Remove config module dependency from frontend

- Delete unused useWebsockets.ts hook
- Hardcode API URLs instead of using config module:
  - useClient.ts: default '/api'
  - useAuth.ts: '/api/auth' and '/api'
  - usePasskeys.ts: '/api/auth'
  - useImageLoader.ts: '/api'
  - useFiles.ts: '/api'
  - file-types.ts: '/api'
- Remove useWebsockets export from hooks/index.ts
This commit is contained in:
2026-02-20 21:52:07 +00:00
parent ba51ee0320
commit 15ab29e23f
8 changed files with 13 additions and 127 deletions
+4 -3
View File
@@ -1,5 +1,6 @@
import { useClient, getHeaders } from 'hooks/useClient';
import { config } from 'config';
const API_URL = '/api';
export type DirEntry = {
name: string;
@@ -79,7 +80,7 @@ export const useFiles = (root: string = 'home') => {
triggerBlobDownload(blob, isDir && !name.endsWith('.zip') ? `${name}.zip` : name);
} else {
const headers = getHeaders();
const url = `${config.API_URL}/${withRoot('/file-browser/download').replace(/^\//, '')}`;
const url = `${API_URL}/${withRoot('/file-browser/download').replace(/^\//, '')}`;
const res = await fetch(url, {
method: 'POST',
headers: { ...headers, 'Content-Type': 'application/json' },
@@ -98,7 +99,7 @@ export const useFiles = (root: string = 'home') => {
formData.append('file', file, name);
}
const authHeaders = getHeaders();
const url = withRoot(`${config.API_URL}/file-browser/upload?path=${encodeURIComponent(path)}`);
const url = withRoot(`${API_URL}/file-browser/upload?path=${encodeURIComponent(path)}`);
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
+4 -3
View File
@@ -1,5 +1,6 @@
import { getHeaders } from 'hooks/useClient';
import { config } from 'config';
const API_URL = '/api';
export type FileType = 'markdown' | 'audio' | 'video' | 'image' | 'pdf' | 'code' | 'archive' | 'text';
@@ -143,14 +144,14 @@ export function getRawUrl(filePath: string, root?: string): string {
const headers = getHeaders();
const token = headers['Authorization']?.replace('Bearer ', '') ?? '';
const rootParam = root && root !== 'home' ? `&root=${encodeURIComponent(root)}` : '';
return `${config.API_URL}/file-browser/raw?path=${encodeURIComponent(filePath)}&token=${encodeURIComponent(token)}${rootParam}`;
return `${API_URL}/file-browser/raw?path=${encodeURIComponent(filePath)}&token=${encodeURIComponent(token)}${rootParam}`;
}
export function getTranscodeUrl(filePath: string, root?: string, t = '0'): string {
const headers = getHeaders();
const token = headers['Authorization']?.replace('Bearer ', '') ?? '';
const rootParam = root && root !== 'home' ? `&root=${encodeURIComponent(root)}` : '';
return `${config.API_URL}/file-browser/transcode?path=${encodeURIComponent(filePath)}&t=${encodeURIComponent(t)}&token=${encodeURIComponent(token)}${rootParam}`;
return `${API_URL}/file-browser/transcode?path=${encodeURIComponent(filePath)}&t=${encodeURIComponent(t)}&token=${encodeURIComponent(token)}${rootParam}`;
}
export function getArchiveBaseName(name: string): string {
-1
View File
@@ -12,7 +12,6 @@ export { usePopover } from './usePopover';
export { useQueryState } from './useQueryState';
export { useTimeout } from './useTimeout';
export { useTimer } from './useTimer';
export { useWebsockets } from './useWebsockets';
export { useChatWebSocket } from './useChatWebSocket';
export { useDataControl } from './useDataControl';
export { useCustomSorter } from './useCustomSorter';
+1 -2
View File
@@ -3,13 +3,12 @@ import { useQuery, useQueryClient } from '@tanstack/react-query';
import { useClient } from 'hooks/useClient';
import { useGlobal } from 'hooks/useGlobal';
import { usePasskeys } from './usePasskeys';
import { config } from 'config';
type UseAuthProps = { authUrl?: string; apiUrl?: string } | undefined;
export type UserWithToken = User & { token: string };
export const useAuth = (props: UseAuthProps = {}) => {
const { authUrl = config.AUTH_URL, apiUrl = config.API_URL } = props;
const { authUrl = '/api/auth', apiUrl = '/api' } = props;
const queryClient = useQueryClient();
const [user, setUser, refreshUser] = useGlobal<UserWithToken | null>('CURRENT_USER', null);
const authClient = useClient(authUrl);
@@ -1,6 +1,5 @@
import { useQueryClient } from '@tanstack/react-query';
import { useClient } from 'hooks/useClient';
import { config } from 'config';
import { startRegistration, startAuthentication } from '@simplewebauthn/browser';
import type {
PublicKeyCredentialCreationOptionsJSON,
@@ -16,7 +15,7 @@ type PasskeyUser = {
export const usePasskeys = () => {
const queryClient = useQueryClient();
const authClient = useClient(config.AUTH_URL);
const authClient = useClient('/api/auth');
const createPasskeyCredentials = async (user: PasskeyUser) => {
// Get registration options from server
+2 -3
View File
@@ -1,8 +1,7 @@
import { config } from 'config';
import { useGlobal } from './useGlobal';
let theToken: string | null = null;
export const createClient = (baseUrl: string = config.API_URL) => {
export const createClient = (baseUrl: string = '/api') => {
const lsToken =
window.officerBearerToken ||
document.body.dataset['officerBearerToken'] ||
@@ -28,7 +27,7 @@ export const createClient = (baseUrl: string = config.API_URL) => {
};
};
export const useClient = (baseUrl: string = config.API_URL) => {
export const useClient = (baseUrl: string = '/api') => {
const [apiError, setApiError] = useGlobal<ErrorDetails | null>('API_ERROR', null);
useClient.config.onError = (error) => {
setApiError(error);
+1 -2
View File
@@ -1,8 +1,7 @@
import { useEffect, useRef } from 'react';
import { useClient } from 'hooks/useClient';
import { config } from 'config';
export const useImageLoader = (baseUrl: string = config.FILES_URL) => {
export const useImageLoader = (baseUrl: string = '/api') => {
const listContainer = useRef<HTMLDivElement>(null);
const filesClient = useClient(baseUrl);
-111
View File
@@ -1,111 +0,0 @@
import { useState, useEffect, useRef, useCallback } from 'react';
import { config } from 'config';
import type { WebSocketMessage } from './types';
type MessageHandler = (data: WebSocketMessage) => void;
export const useWebsockets = (onMessage?: MessageHandler) => {
const [isOnline, setOnline] = useState(false);
const [userCount, setUserCount] = useState(0);
const socketRef = useRef<WebSocket | null>(null);
const retryRef = useRef(0);
const retryTimeoutRef = useRef<number | null>(null);
const isCleaningUpRef = useRef(false);
const connectSocket = useCallback(() => {
if (isCleaningUpRef.current) return;
if (socketRef.current && socketRef.current.readyState === WebSocket.OPEN) return;
const socket = new WebSocket(config.WS_URL);
socketRef.current = socket;
socket.addEventListener('open', () => {
setOnline(true);
retryRef.current = 0;
console.log('🟢 Connected to', config.WS_URL);
});
socket.addEventListener('message', (event) => {
try {
let data: WebSocketMessage;
if (event.data instanceof ArrayBuffer) {
const text = new TextDecoder().decode(event.data);
data = JSON.parse(text) as WebSocketMessage;
} else if (typeof event.data === 'string') {
data = JSON.parse(event.data) as WebSocketMessage;
} else {
console.warn('⚠️ Unknown WS message type:', typeof event.data);
return;
}
if (
data &&
typeof data === 'object' &&
'type' in data &&
data.type === 'users' &&
'count' in data &&
typeof data.count === 'number'
) {
console.log('👥 Users online:', data.count);
setUserCount(data.count);
return;
}
onMessage?.(data);
} catch (err) {
console.warn('⚠️ Failed to parse WS message:', event.data, err);
}
});
socket.addEventListener('close', () => {
if (isCleaningUpRef.current) return;
setOnline(false);
console.log('🔴 Disconnected');
const retry = (retryRef.current ?? 0) + 1;
retryRef.current = retry;
const delay = Math.min(5000, 300 * retry);
retryTimeoutRef.current = window.setTimeout(connectSocket, delay);
});
socket.addEventListener('error', (err) => {
console.error('⚠️ WebSocket error', err);
socket.close();
});
}, [onMessage]);
const send = useCallback((data: string | Record<string, unknown>) => {
const socket = socketRef.current;
if (socket && socket.readyState === WebSocket.OPEN) {
socket.send(typeof data === 'string' ? data : JSON.stringify(data));
} else {
console.warn('Cannot send message, socket not ready');
}
}, []);
useEffect(() => {
isCleaningUpRef.current = false;
connectSocket();
return () => {
isCleaningUpRef.current = true;
if (retryTimeoutRef.current !== null) {
clearTimeout(retryTimeoutRef.current);
retryTimeoutRef.current = null;
}
if (socketRef.current) {
socketRef.current.close();
socketRef.current = null;
}
};
}, [connectSocket]);
return { isOnline, userCount, send };
};
export type UseWebsocketsReturn = ReturnType<typeof useWebsockets>;