fix(backend+frontend): WebSocket auth, Yjs persistence fix, keep docs in memory (Issues #2, #4, #5)

This commit is contained in:
2026-06-29 23:33:58 +02:00
parent 2e9dfdfec0
commit cf62a777d2
6 changed files with 99 additions and 159 deletions
+1
View File
@@ -86,6 +86,7 @@ const CADEditor: React.FC<CADEditorProps> = ({ projectId, token, onNavigateBack
userName: user?.name || 'Gast',
userColor: '#3498db',
enabled: true,
token: token || undefined,
});
// Project
+6 -3
View File
@@ -27,6 +27,7 @@ export class WebSocketProvider {
private reconnectDelay = 1000;
private maxReconnectDelay = 30000;
private shouldReconnect = true;
private authToken: string | undefined;
constructor(opts: WebSocketProviderOptions) {
this.url = opts.url;
@@ -36,15 +37,17 @@ export class WebSocketProvider {
this.onSync = opts.onSync;
}
connect(): void {
connect(token?: string): void {
if (this.ws && (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING)) {
return;
}
this.authToken = token;
this.shouldReconnect = true;
this.setStatus('connecting');
const fullUrl = `${this.url}/ws/collab/${encodeURIComponent(this.docName)}`;
const tokenParam = token ? `?token=${encodeURIComponent(token)}` : '';
const fullUrl = `${this.url}/ws/collab/${encodeURIComponent(this.docName)}${tokenParam}`;
try {
this.ws = new WebSocket(fullUrl);
} catch (err) {
@@ -130,7 +133,7 @@ export class WebSocketProvider {
if (this.reconnectTimer) return;
this.reconnectTimer = setTimeout(() => {
this.reconnectTimer = null;
this.connect();
this.connect(this.authToken);
}, this.reconnectDelay);
this.reconnectDelay = Math.min(this.reconnectDelay * 2, this.maxReconnectDelay);
}
+4 -3
View File
@@ -16,6 +16,7 @@ export interface UseYjsBindingOptions {
userName: string;
userColor: string;
enabled?: boolean;
token?: string;
}
export interface UseYjsBindingResult {
@@ -44,7 +45,7 @@ export interface UseYjsBindingResult {
const DEFAULT_WS_URL = `wss://${window.location.host}`;
export function useYjsBinding(opts: UseYjsBindingOptions): UseYjsBindingResult {
const { docName, wsUrl = DEFAULT_WS_URL, userId, userName, userColor, enabled = true } = opts;
const { docName, wsUrl = DEFAULT_WS_URL, userId, userName, userColor, enabled = true, token } = opts;
const yjsDocRef = useRef<YjsDocument | null>(null);
const providerRef = useRef<WebSocketProvider | null>(null);
@@ -92,7 +93,7 @@ export function useYjsBinding(opts: UseYjsBindingOptions): UseYjsBindingResult {
const unbindLocal = provider.bindLocalUpdates();
// Connect
provider.connect();
provider.connect(token);
syncState();
// Cleanup
@@ -107,7 +108,7 @@ export function useYjsBinding(opts: UseYjsBindingOptions): UseYjsBindingResult {
providerRef.current = null;
awarenessRef.current = null;
};
}, [docName, wsUrl, userId, userName, userColor, enabled]);
}, [docName, wsUrl, userId, userName, userColor, enabled, token]);
// Mutators
const setElement = useCallback((el: CADElement) => {