Skip to content

Commit a0fbd11

Browse files
committed
witf
1 parent d34fe16 commit a0fbd11

File tree

12 files changed

+696
-259
lines changed

12 files changed

+696
-259
lines changed

.svelte-kit/ambient.d.ts

Lines changed: 260 additions & 196 deletions
Large diffs are not rendered by default.

.svelte-kit/generated/server/internal.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

server/database/notevault.db

20 KB
Binary file not shown.

src/lib/api.ts

Lines changed: 65 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,43 @@ type RequestInit = {
1010
credentials?: RequestCredentials;
1111
};
1212

13-
const API_BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:3001/api';
13+
const API_BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:12001/api';
1414

1515
class ApiClient {
16+
private cache = new Map<string, { data: any; timestamp: number }>();
17+
private readonly CACHE_TTL = 5000; // 5 seconds cache
18+
1619
private getAuthHeaders(): HeadersInit {
1720
if (!browser) return {};
1821

1922
const token = localStorage.getItem('auth_token');
2023
return token ? { Authorization: `Bearer ${token}` } : {};
2124
}
2225

26+
private getCacheKey(endpoint: string, options: RequestInit = {}): string {
27+
return `${options.method || 'GET'}:${endpoint}`;
28+
}
29+
30+
private isValidCache(timestamp: number): boolean {
31+
return Date.now() - timestamp < this.CACHE_TTL;
32+
}
33+
2334
private async request<T>(
2435
endpoint: string,
25-
options: RequestInit = {}
36+
options: RequestInit = {},
37+
retryCount = 0
2638
): Promise<T> {
39+
const cacheKey = this.getCacheKey(endpoint, options);
40+
const method = options.method || 'GET';
41+
42+
// Only cache GET requests
43+
if (method === 'GET') {
44+
const cached = this.cache.get(cacheKey);
45+
if (cached && this.isValidCache(cached.timestamp)) {
46+
return cached.data;
47+
}
48+
}
49+
2750
const url = `${API_BASE_URL}${endpoint}`;
2851

2952
const config: RequestInit = {
@@ -39,11 +62,35 @@ class ApiClient {
3962
const response = await fetch(url, config);
4063

4164
if (!response.ok) {
65+
// Handle rate limiting with exponential backoff
66+
if (response.status === 429 && retryCount < 3) {
67+
const retryAfter = response.headers.get('Retry-After');
68+
const delay = retryAfter ? parseInt(retryAfter) * 1000 : Math.pow(2, retryCount) * 1000;
69+
70+
console.warn(`Rate limited. Retrying after ${delay}ms (attempt ${retryCount + 1}/3)`);
71+
72+
await new Promise(resolve => setTimeout(resolve, delay));
73+
return this.request<T>(endpoint, options, retryCount + 1);
74+
}
75+
4276
const error = await response.json().catch(() => ({ error: 'Network error' }));
77+
78+
// Provide user-friendly error messages for rate limiting
79+
if (response.status === 429) {
80+
throw new Error('Too many requests. Please wait a moment and try again.');
81+
}
82+
4383
throw new Error(error.error || `HTTP ${response.status}`);
4484
}
4585

46-
return await response.json();
86+
const data = await response.json();
87+
88+
// Cache GET requests
89+
if (method === 'GET') {
90+
this.cache.set(cacheKey, { data, timestamp: Date.now() });
91+
}
92+
93+
return data;
4794
} catch (error) {
4895
console.error('API request failed:', error);
4996
throw error;
@@ -149,8 +196,20 @@ class ApiClient {
149196
}
150197

151198
// Note endpoints
152-
async getWorkspaceNotes(workspaceId: string) {
153-
return this.request(`/notes/workspace/${workspaceId}`);
199+
async getWorkspaceNotes(workspaceId: string, params?: {
200+
limit?: number;
201+
offset?: number;
202+
sortBy?: string;
203+
sortOrder?: 'asc' | 'desc';
204+
}) {
205+
const query = new URLSearchParams();
206+
if (params?.limit) query.set('limit', params.limit.toString());
207+
if (params?.offset) query.set('offset', params.offset.toString());
208+
if (params?.sortBy) query.set('sortBy', params.sortBy);
209+
if (params?.sortOrder) query.set('sortOrder', params.sortOrder);
210+
211+
const url = `/notes/workspace/${workspaceId}${query.toString() ? '?' + query.toString() : ''}`;
212+
return this.request(url);
154213
}
155214

156215
async getNote(id: string) {
@@ -167,6 +226,7 @@ class ApiClient {
167226
color: string;
168227
tags?: string[];
169228
isPublic?: boolean;
229+
collectionId?: string;
170230
}) {
171231
return this.request('/notes', {
172232
method: 'POST',

src/lib/components/ChatMembersModal.svelte

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@
6969
}
7070
7171
$: filteredUsers = $onlineUsers.filter(user =>
72-
user.displayName.toLowerCase().includes(searchQuery.toLowerCase()) ||
72+
(user.displayName || user.username || '')?.toLowerCase().includes(searchQuery.toLowerCase()) ||
7373
user.username?.toLowerCase().includes(searchQuery.toLowerCase())
7474
);
7575
</script>
@@ -111,7 +111,7 @@
111111
<div class="relative">
112112
<img
113113
src={user.avatar}
114-
alt={user.displayName}
114+
alt={user.displayName || user.username}
115115
class="w-10 h-10 rounded-full"
116116
/>
117117
<div class="absolute -bottom-1 -right-1 w-3 h-3 bg-green-500 border-2 border-dark-900 rounded-full"></div>
@@ -120,7 +120,7 @@
120120
<div class="flex-1 min-w-0">
121121
<div class="flex items-center space-x-2">
122122
<span class="font-medium text-white truncate">
123-
{user.displayName}
123+
{user.displayName || user.username}
124124
</span>
125125
{#if getRoleIcon(user.role)}
126126
<svelte:component

src/lib/components/Sidebar.svelte

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@
4141
}
4242
return $page.url.pathname.startsWith(href);
4343
}
44+
45+
// Reactive statement to debug active states
46+
$: console.log('Current page path:', $page.url.pathname);
4447
4548
function handleLogout() {
4649
authStore.logout();
@@ -93,7 +96,7 @@
9396
{#each navigation as item}
9497
<a
9598
href={item.href}
96-
class={isActive(item.href) ? 'sidebar-item-active' : 'sidebar-item-inactive'}
99+
class="flex items-center px-3 py-2 text-sm font-medium rounded-lg transition-colors {isActive(item.href) ? 'bg-primary-600 text-white' : 'text-dark-300 hover:bg-dark-800 hover:text-white'}"
97100
>
98101
<div class="flex items-center justify-between w-full">
99102
<div class="flex items-center">
@@ -133,7 +136,7 @@
133136
{#each adminNavigation as item}
134137
<a
135138
href={item.href}
136-
class={isActive(item.href) ? 'sidebar-item-active' : 'sidebar-item-inactive'}
139+
class="flex items-center px-3 py-2 text-sm font-medium rounded-lg transition-colors {isActive(item.href) ? 'bg-primary-600 text-white' : 'text-dark-300 hover:bg-dark-800 hover:text-white'}"
137140
>
138141
<svelte:component this={item.icon} class="w-5 h-5 mr-3" />
139142
{item.name}

src/lib/stores/chat.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,15 @@ const getCurrentUserId = (): string | null => {
2525

2626
export const chatStore = {
2727
connect: () => {
28-
if (!browser || socket?.connected) return;
28+
if (!browser) return;
29+
30+
// If there's already a connected socket, don't create a new one
31+
if (socket && socket.connected) return;
2932

3033
// Use the same base URL as the API but without the /api path for WebSocket
3134
const wsUrl = import.meta.env.VITE_API_URL
3235
? import.meta.env.VITE_API_URL.replace('/api', '')
33-
: 'http://localhost:3001';
36+
: 'http://localhost:12001';
3437

3538
socket = io(wsUrl, {
3639
transports: ['websocket', 'polling']

src/lib/stores/workspaces.ts

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,16 @@ export const workspaceStore = {
5151
}
5252
},
5353

54-
loadWorkspaceNotes: async (workspaceId: string) => {
54+
loadWorkspaceNotes: async (workspaceId: string, params?: {
55+
limit?: number;
56+
offset?: number;
57+
sortBy?: string;
58+
sortOrder?: 'asc' | 'desc';
59+
append?: boolean;
60+
}) => {
5561
try {
56-
const notesData = await api.getWorkspaceNotes(workspaceId);
62+
const { append = false, ...queryParams } = params || {};
63+
const notesData = await api.getWorkspaceNotes(workspaceId, queryParams);
5764
const formattedNotes: Note[] = notesData.map((note: any) => ({
5865
id: note.id,
5966
title: note.title,
@@ -69,10 +76,20 @@ export const workspaceStore = {
6976
updatedAt: new Date(note.updatedAt),
7077
isPublic: note.isPublic
7178
}));
72-
workspaceNotes.set(formattedNotes);
79+
80+
if (append) {
81+
workspaceNotes.update(notes => [...notes, ...formattedNotes]);
82+
} else {
83+
workspaceNotes.set(formattedNotes);
84+
}
85+
86+
return formattedNotes;
7387
} catch (error) {
7488
console.error('Failed to load workspace notes:', error);
75-
workspaceNotes.set([]);
89+
if (!params?.append) {
90+
workspaceNotes.set([]);
91+
}
92+
return [];
7693
}
7794
},
7895

@@ -210,6 +227,7 @@ export const workspaceStore = {
210227
color: string;
211228
tags?: string[];
212229
isPublic?: boolean;
230+
collectionId?: string;
213231
}) => {
214232
try {
215233
const newNoteData = await api.createNote({

src/routes/+layout.svelte

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import { authStore, isAuthenticated, isLoading } from '$lib/stores/auth';
77
import { showCreateWorkspaceModal } from '$lib/stores/modals';
88
import { goto } from '$app/navigation';
9+
import { navigating } from '$app/stores';
10+
import { chatStore } from '$lib/stores/chat';
911
import Sidebar from '$lib/components/Sidebar.svelte';
1012
import CommandPalette from '$lib/components/CommandPalette.svelte';
1113
import CreateWorkspaceModal from '$lib/components/CreateWorkspaceModal.svelte';
@@ -26,8 +28,18 @@
2628
let rightPanelVisible = false;
2729
let focusModeActive = false;
2830
31+
let initialized = false;
32+
2933
onMount(async () => {
30-
authStore.checkAuth();
34+
if (!initialized) {
35+
initialized = true;
36+
await authStore.checkAuth();
37+
38+
// Initialize chat connection only once after auth is checked
39+
if (browser) {
40+
chatStore.connect();
41+
}
42+
}
3143
3244
if (browser) {
3345
// Initialize PWA functionality
@@ -291,7 +303,7 @@
291303
}
292304
293305
$: {
294-
if (!$isLoading && !$isAuthenticated && !publicRoutes.includes($page.url.pathname)) {
306+
if (initialized && !$isLoading && !$isAuthenticated && !publicRoutes.includes($page.url.pathname)) {
295307
goto('/login');
296308
}
297309
}

src/routes/+page.svelte

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
2525
onMount(async () => {
2626
loadWorkspacesWithLoading();
27-
chatStore.connect();
2827
2928
// Load real data
3029
await loadDashboardData();

0 commit comments

Comments
 (0)