Revert "fix: resolve chat conversation hanging and stream interruption issues (#1971)"
This reverts commit e68593f22d.
This commit is contained in:
@@ -1,300 +0,0 @@
|
||||
import { atom, map } from 'nanostores';
|
||||
import type { UserProfile } from '~/lib/utils/fileUserStorage';
|
||||
import Cookies from 'js-cookie';
|
||||
|
||||
export interface AuthState {
|
||||
isAuthenticated: boolean;
|
||||
user: Omit<UserProfile, 'passwordHash'> | null;
|
||||
token: string | null;
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
// Authentication state store
|
||||
export const authStore = map<AuthState>({
|
||||
isAuthenticated: false,
|
||||
user: null,
|
||||
token: null,
|
||||
loading: true,
|
||||
});
|
||||
|
||||
// Remember me preference
|
||||
export const rememberMeStore = atom<boolean>(false);
|
||||
|
||||
// Session timeout tracking
|
||||
let sessionTimeout: NodeJS.Timeout | null = null;
|
||||
const SESSION_TIMEOUT = 7 * 24 * 60 * 60 * 1000; // 7 days
|
||||
|
||||
/**
|
||||
* Initialize auth from stored token
|
||||
*/
|
||||
export async function initializeAuth(): Promise<void> {
|
||||
if (typeof window === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
authStore.setKey('loading', true);
|
||||
|
||||
try {
|
||||
const token = Cookies.get('auth_token');
|
||||
|
||||
if (token) {
|
||||
// Verify token with backend
|
||||
const response = await fetch('/api/auth/verify', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const data = (await response.json()) as { user: Omit<UserProfile, 'passwordHash'> };
|
||||
setAuthState({
|
||||
isAuthenticated: true,
|
||||
user: data.user,
|
||||
token,
|
||||
loading: false,
|
||||
});
|
||||
startSessionTimer();
|
||||
} else {
|
||||
// Token is invalid, clear it
|
||||
clearAuth();
|
||||
}
|
||||
} else {
|
||||
authStore.setKey('loading', false);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize auth:', error);
|
||||
authStore.setKey('loading', false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set authentication state
|
||||
*/
|
||||
export function setAuthState(state: AuthState): void {
|
||||
authStore.set(state);
|
||||
|
||||
if (state.token) {
|
||||
// Store token in cookie
|
||||
const cookieOptions = rememberMeStore.get()
|
||||
? { expires: 7 } // 7 days
|
||||
: undefined; // Session cookie
|
||||
|
||||
Cookies.set('auth_token', state.token, cookieOptions);
|
||||
|
||||
// Store user preferences in localStorage
|
||||
if (state.user) {
|
||||
localStorage.setItem(`bolt_user_${state.user.id}`, JSON.stringify(state.user.preferences || {}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Login user
|
||||
*/
|
||||
export async function login(
|
||||
username: string,
|
||||
password: string,
|
||||
rememberMe: boolean = false,
|
||||
): Promise<{ success: boolean; error?: string }> {
|
||||
try {
|
||||
const response = await fetch('/api/auth/login', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ username, password }),
|
||||
});
|
||||
|
||||
const data = (await response.json()) as {
|
||||
success?: boolean;
|
||||
error?: string;
|
||||
user?: Omit<UserProfile, 'passwordHash'>;
|
||||
token?: string;
|
||||
};
|
||||
|
||||
if (response.ok) {
|
||||
rememberMeStore.set(rememberMe);
|
||||
setAuthState({
|
||||
isAuthenticated: true,
|
||||
user: data.user || null,
|
||||
token: data.token || null,
|
||||
loading: false,
|
||||
});
|
||||
startSessionTimer();
|
||||
|
||||
return { success: true };
|
||||
} else {
|
||||
return { success: false, error: data.error || 'Login failed' };
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Login error:', error);
|
||||
return { success: false, error: 'Network error' };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Signup new user
|
||||
*/
|
||||
export async function signup(
|
||||
username: string,
|
||||
password: string,
|
||||
firstName: string,
|
||||
avatar?: string,
|
||||
): Promise<{ success: boolean; error?: string }> {
|
||||
try {
|
||||
const response = await fetch('/api/auth/signup', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ username, password, firstName, avatar }),
|
||||
});
|
||||
|
||||
const data = (await response.json()) as {
|
||||
success?: boolean;
|
||||
error?: string;
|
||||
user?: Omit<UserProfile, 'passwordHash'>;
|
||||
token?: string;
|
||||
};
|
||||
|
||||
if (response.ok) {
|
||||
setAuthState({
|
||||
isAuthenticated: true,
|
||||
user: data.user || null,
|
||||
token: data.token || null,
|
||||
loading: false,
|
||||
});
|
||||
startSessionTimer();
|
||||
|
||||
return { success: true };
|
||||
} else {
|
||||
return { success: false, error: data.error || 'Signup failed' };
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Signup error:', error);
|
||||
return { success: false, error: 'Network error' };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logout user
|
||||
*/
|
||||
export async function logout(): Promise<void> {
|
||||
const state = authStore.get();
|
||||
|
||||
if (state.token) {
|
||||
try {
|
||||
await fetch('/api/auth/logout', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${state.token}`,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Logout error:', error);
|
||||
}
|
||||
}
|
||||
|
||||
clearAuth();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear authentication state
|
||||
*/
|
||||
function clearAuth(): void {
|
||||
authStore.set({
|
||||
isAuthenticated: false,
|
||||
user: null,
|
||||
token: null,
|
||||
loading: false,
|
||||
});
|
||||
|
||||
Cookies.remove('auth_token');
|
||||
stopSessionTimer();
|
||||
|
||||
// Clear user-specific localStorage
|
||||
const currentUser = authStore.get().user;
|
||||
|
||||
if (currentUser?.id) {
|
||||
// Keep preferences but clear sensitive data
|
||||
const prefs = localStorage.getItem(`bolt_user_${currentUser.id}`);
|
||||
|
||||
if (prefs) {
|
||||
try {
|
||||
const parsed = JSON.parse(prefs);
|
||||
delete parsed.deploySettings;
|
||||
delete parsed.githubSettings;
|
||||
localStorage.setItem(`bolt_user_${currentUser.id}`, JSON.stringify(parsed));
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start session timer
|
||||
*/
|
||||
function startSessionTimer(): void {
|
||||
stopSessionTimer();
|
||||
|
||||
if (!rememberMeStore.get()) {
|
||||
sessionTimeout = setTimeout(() => {
|
||||
logout();
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
window.location.href = '/auth';
|
||||
}
|
||||
}, SESSION_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop session timer
|
||||
*/
|
||||
function stopSessionTimer(): void {
|
||||
if (sessionTimeout) {
|
||||
clearTimeout(sessionTimeout);
|
||||
sessionTimeout = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update user profile
|
||||
*/
|
||||
export async function updateProfile(
|
||||
updates: Partial<Omit<UserProfile, 'passwordHash' | 'id' | 'username'>>,
|
||||
): Promise<boolean> {
|
||||
const state = authStore.get();
|
||||
|
||||
if (!state.token || !state.user) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/users/profile', {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${state.token}`,
|
||||
},
|
||||
body: JSON.stringify(updates),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const updatedUser = (await response.json()) as Omit<UserProfile, 'passwordHash'>;
|
||||
authStore.setKey('user', updatedUser);
|
||||
|
||||
return true;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to update profile:', error);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initialize auth on load
|
||||
if (typeof window !== 'undefined') {
|
||||
initializeAuth();
|
||||
}
|
||||
@@ -223,13 +223,10 @@ export class WorkbenchStore {
|
||||
}
|
||||
|
||||
async saveFile(filePath: string) {
|
||||
console.log(`[WorkbenchStore] saveFile called for: ${filePath}`);
|
||||
|
||||
const documents = this.#editorStore.documents.get();
|
||||
const document = documents[filePath];
|
||||
|
||||
if (document === undefined) {
|
||||
console.warn(`[WorkbenchStore] No document found for: ${filePath}`);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -239,39 +236,21 @@ export class WorkbenchStore {
|
||||
* This is a more complex feature that would be implemented in a future update
|
||||
*/
|
||||
|
||||
try {
|
||||
console.log(`[WorkbenchStore] Saving to file system: ${filePath}`);
|
||||
await this.#filesStore.saveFile(filePath, document.value);
|
||||
console.log(`[WorkbenchStore] File saved successfully: ${filePath}`);
|
||||
await this.#filesStore.saveFile(filePath, document.value);
|
||||
|
||||
const newUnsavedFiles = new Set(this.unsavedFiles.get());
|
||||
const wasUnsaved = newUnsavedFiles.has(filePath);
|
||||
newUnsavedFiles.delete(filePath);
|
||||
const newUnsavedFiles = new Set(this.unsavedFiles.get());
|
||||
newUnsavedFiles.delete(filePath);
|
||||
|
||||
console.log(`[WorkbenchStore] Updating unsaved files:`, {
|
||||
filePath,
|
||||
wasUnsaved,
|
||||
previousCount: this.unsavedFiles.get().size,
|
||||
newCount: newUnsavedFiles.size,
|
||||
remainingFiles: Array.from(newUnsavedFiles),
|
||||
});
|
||||
|
||||
this.unsavedFiles.set(newUnsavedFiles);
|
||||
} catch (error) {
|
||||
console.error(`[WorkbenchStore] Failed to save file ${filePath}:`, error);
|
||||
throw error;
|
||||
}
|
||||
this.unsavedFiles.set(newUnsavedFiles);
|
||||
}
|
||||
|
||||
async saveCurrentDocument() {
|
||||
const currentDocument = this.currentDocument.get();
|
||||
|
||||
if (currentDocument === undefined) {
|
||||
console.warn('[WorkbenchStore] No current document to save');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`[WorkbenchStore] Saving current document: ${currentDocument.filePath}`);
|
||||
await this.saveFile(currentDocument.filePath);
|
||||
}
|
||||
|
||||
@@ -293,14 +272,9 @@ export class WorkbenchStore {
|
||||
}
|
||||
|
||||
async saveAllFiles() {
|
||||
const filesToSave = Array.from(this.unsavedFiles.get());
|
||||
console.log(`[WorkbenchStore] saveAllFiles called for ${filesToSave.length} files:`, filesToSave);
|
||||
|
||||
for (const filePath of filesToSave) {
|
||||
for (const filePath of this.unsavedFiles.get()) {
|
||||
await this.saveFile(filePath);
|
||||
}
|
||||
|
||||
console.log('[WorkbenchStore] saveAllFiles complete. Remaining unsaved:', Array.from(this.unsavedFiles.get()));
|
||||
}
|
||||
|
||||
getFileModifcations() {
|
||||
|
||||
Reference in New Issue
Block a user