Revert "fix: resolve chat conversation hanging and stream interruption issues (#1971)"

This reverts commit e68593f22d.
This commit is contained in:
Stijnus
2025-09-07 00:14:13 +02:00
committed by Stijnus
parent e68593f22d
commit 37217a5c7b
61 changed files with 1432 additions and 8811 deletions

View File

@@ -1,86 +0,0 @@
import bcrypt from 'bcryptjs';
import jwt from 'jsonwebtoken';
// Use a secure secret key (in production, this should be an environment variable)
const JWT_SECRET = process.env.JWT_SECRET || 'bolt-multi-user-secret-key-2024-secure';
const SALT_ROUNDS = 10;
export interface JWTPayload {
userId: string;
username: string;
firstName: string;
exp?: number;
}
/**
* Hash a password using bcrypt
*/
export async function hashPassword(password: string): Promise<string> {
return bcrypt.hash(password, SALT_ROUNDS);
}
/**
* Verify a password against a hash
*/
export async function verifyPassword(password: string, hash: string): Promise<boolean> {
return bcrypt.compare(password, hash);
}
/**
* Generate a JWT token
*/
export function generateToken(payload: Omit<JWTPayload, 'exp'>): string {
return jwt.sign(
{
...payload,
exp: Math.floor(Date.now() / 1000) + 7 * 24 * 60 * 60, // 7 days
},
JWT_SECRET,
);
}
/**
* Verify and decode a JWT token
*/
export function verifyToken(token: string): JWTPayload | null {
try {
return jwt.verify(token, JWT_SECRET) as JWTPayload;
} catch {
return null;
}
}
/**
* Generate a secure user ID
*/
export function generateUserId(): string {
return `user_${Date.now()}_${Math.random().toString(36).substring(2, 15)}`;
}
/**
* Validate password strength
*/
export function validatePassword(password: string): { valid: boolean; errors: string[] } {
const errors: string[] = [];
if (password.length < 8) {
errors.push('Password must be at least 8 characters long');
}
if (!/[A-Z]/.test(password)) {
errors.push('Password must contain at least one uppercase letter');
}
if (!/[a-z]/.test(password)) {
errors.push('Password must contain at least one lowercase letter');
}
if (!/[0-9]/.test(password)) {
errors.push('Password must contain at least one number');
}
return {
valid: errors.length === 0,
errors,
};
}

View File

@@ -1,338 +0,0 @@
import fs from 'fs/promises';
import path from 'path';
import { generateUserId, hashPassword } from './crypto';
const USERS_DIR = path.join(process.cwd(), '.users');
const USERS_INDEX_FILE = path.join(USERS_DIR, 'users.json');
const USER_DATA_DIR = path.join(USERS_DIR, 'data');
export interface UserProfile {
id: string;
username: string;
firstName: string;
passwordHash: string;
avatar?: string;
createdAt: string;
lastLogin?: string;
preferences: UserPreferences;
}
export interface UserPreferences {
theme: 'light' | 'dark';
deploySettings: {
netlify?: any;
vercel?: any;
};
githubSettings?: any;
workspaceConfig: any;
}
export interface SecurityLog {
timestamp: string;
userId?: string;
username?: string;
action: 'login' | 'logout' | 'signup' | 'delete' | 'error' | 'failed_login';
details: string;
ip?: string;
}
/**
* Initialize the user storage system
*/
export async function initializeUserStorage(): Promise<void> {
try {
// Create directories if they don't exist
await fs.mkdir(USERS_DIR, { recursive: true });
await fs.mkdir(USER_DATA_DIR, { recursive: true });
// Create users index if it doesn't exist
try {
await fs.access(USERS_INDEX_FILE);
} catch {
await fs.writeFile(USERS_INDEX_FILE, JSON.stringify({ users: [] }, null, 2));
}
} catch (error) {
console.error('Failed to initialize user storage:', error);
throw error;
}
}
/**
* Get all users (without passwords)
*/
export async function getAllUsers(): Promise<Omit<UserProfile, 'passwordHash'>[]> {
try {
await initializeUserStorage();
const data = await fs.readFile(USERS_INDEX_FILE, 'utf-8');
const { users } = JSON.parse(data) as { users: UserProfile[] };
return users.map(({ passwordHash, ...user }) => user);
} catch (error) {
console.error('Failed to get users:', error);
return [];
}
}
/**
* Get a user by username
*/
export async function getUserByUsername(username: string): Promise<UserProfile | null> {
try {
await initializeUserStorage();
const data = await fs.readFile(USERS_INDEX_FILE, 'utf-8');
const { users } = JSON.parse(data) as { users: UserProfile[] };
return users.find((u) => u.username === username) || null;
} catch (error) {
console.error('Failed to get user:', error);
return null;
}
}
/**
* Get a user by ID
*/
export async function getUserById(id: string): Promise<UserProfile | null> {
try {
await initializeUserStorage();
const data = await fs.readFile(USERS_INDEX_FILE, 'utf-8');
const { users } = JSON.parse(data) as { users: UserProfile[] };
return users.find((u) => u.id === id) || null;
} catch (error) {
console.error('Failed to get user:', error);
return null;
}
}
/**
* Create a new user
*/
export async function createUser(
username: string,
password: string,
firstName: string,
avatar?: string,
): Promise<UserProfile | null> {
try {
await initializeUserStorage();
// Check if username already exists
const existingUser = await getUserByUsername(username);
if (existingUser) {
throw new Error('Username already exists');
}
// Create new user
const newUser: UserProfile = {
id: generateUserId(),
username,
firstName,
passwordHash: await hashPassword(password),
avatar,
createdAt: new Date().toISOString(),
preferences: {
theme: 'dark',
deploySettings: {},
workspaceConfig: {},
},
};
// Load existing users
const data = await fs.readFile(USERS_INDEX_FILE, 'utf-8');
const { users } = JSON.parse(data) as { users: UserProfile[] };
// Add new user
users.push(newUser);
// Save updated users
await fs.writeFile(USERS_INDEX_FILE, JSON.stringify({ users }, null, 2));
// Create user data directory
const userDataDir = path.join(USER_DATA_DIR, newUser.id);
await fs.mkdir(userDataDir, { recursive: true });
// Log the signup
await logSecurityEvent({
timestamp: new Date().toISOString(),
userId: newUser.id,
username: newUser.username,
action: 'signup',
details: `User ${newUser.username} created successfully`,
});
return newUser;
} catch (error) {
console.error('Failed to create user:', error);
await logSecurityEvent({
timestamp: new Date().toISOString(),
action: 'error',
details: `Failed to create user ${username}: ${error}`,
});
throw error;
}
}
/**
* Update user profile
*/
export async function updateUser(userId: string, updates: Partial<UserProfile>): Promise<boolean> {
try {
await initializeUserStorage();
const data = await fs.readFile(USERS_INDEX_FILE, 'utf-8');
const { users } = JSON.parse(data) as { users: UserProfile[] };
const userIndex = users.findIndex((u) => u.id === userId);
if (userIndex === -1) {
return false;
}
// Update user (excluding certain fields)
const { id, username, passwordHash, ...safeUpdates } = updates;
users[userIndex] = {
...users[userIndex],
...safeUpdates,
};
// Save updated users
await fs.writeFile(USERS_INDEX_FILE, JSON.stringify({ users }, null, 2));
return true;
} catch (error) {
console.error('Failed to update user:', error);
return false;
}
}
/**
* Update user's last login time
*/
export async function updateLastLogin(userId: string): Promise<void> {
await updateUser(userId, { lastLogin: new Date().toISOString() });
}
/**
* Delete a user
*/
export async function deleteUser(userId: string): Promise<boolean> {
try {
await initializeUserStorage();
const data = await fs.readFile(USERS_INDEX_FILE, 'utf-8');
const { users } = JSON.parse(data) as { users: UserProfile[] };
const userIndex = users.findIndex((u) => u.id === userId);
if (userIndex === -1) {
return false;
}
const deletedUser = users[userIndex];
// Remove user from list
users.splice(userIndex, 1);
// Save updated users
await fs.writeFile(USERS_INDEX_FILE, JSON.stringify({ users }, null, 2));
// Delete user data directory
const userDataDir = path.join(USER_DATA_DIR, userId);
try {
await fs.rm(userDataDir, { recursive: true, force: true });
} catch (error) {
console.warn(`Failed to delete user data directory: ${error}`);
}
// Log the deletion
await logSecurityEvent({
timestamp: new Date().toISOString(),
userId,
username: deletedUser.username,
action: 'delete',
details: `User ${deletedUser.username} deleted`,
});
return true;
} catch (error) {
console.error('Failed to delete user:', error);
return false;
}
}
/**
* Save user-specific data
*/
export async function saveUserData(userId: string, key: string, data: any): Promise<void> {
try {
const userDataDir = path.join(USER_DATA_DIR, userId);
await fs.mkdir(userDataDir, { recursive: true });
const filePath = path.join(userDataDir, `${key}.json`);
await fs.writeFile(filePath, JSON.stringify(data, null, 2));
} catch (error) {
console.error(`Failed to save user data for ${userId}:`, error);
throw error;
}
}
/**
* Load user-specific data
*/
export async function loadUserData(userId: string, key: string): Promise<any | null> {
try {
const filePath = path.join(USER_DATA_DIR, userId, `${key}.json`);
const data = await fs.readFile(filePath, 'utf-8');
return JSON.parse(data);
} catch {
return null;
}
}
/**
* Log security events
*/
export async function logSecurityEvent(event: SecurityLog): Promise<void> {
try {
const logFile = path.join(USERS_DIR, 'security.log');
const logEntry = `${JSON.stringify(event)}\n`;
await fs.appendFile(logFile, logEntry);
} catch (error) {
console.error('Failed to log security event:', error);
}
}
/**
* Get security logs
*/
export async function getSecurityLogs(limit: number = 100): Promise<SecurityLog[]> {
try {
const logFile = path.join(USERS_DIR, 'security.log');
const data = await fs.readFile(logFile, 'utf-8');
const logs = data
.trim()
.split('\n')
.filter((line) => line)
.map((line) => {
try {
return JSON.parse(line) as SecurityLog;
} catch {
return null;
}
})
.filter(Boolean) as SecurityLog[];
return logs.slice(-limit).reverse();
} catch {
return [];
}
}