fix: 修复proxy_service和account_service中不当的顶层import

- proxy_service.py: 将requests和WhitelistManager改为函数内延迟import,
  避免启动时加载不需要的依赖;移除未使用的get_exit_ip_via_proxy导入
- account_service.py: 移除未使用的func、joinedload、AuditLog、
  user_has_permission顶层导入
This commit is contained in:
yml2213
2026-06-23 08:35:29 +08:00
parent 2ab6724543
commit dd56de9bd4
40 changed files with 2006 additions and 936 deletions
+23
View File
@@ -0,0 +1,23 @@
import { useCallback, useMemo } from 'react';
import { getUser, type AuthUser } from '../store/auth';
export function usePermissions(userOverride?: AuthUser | null) {
const user = userOverride === undefined ? getUser() : userOverride;
const permissions = useMemo(() => user?.permissions ?? [], [user]);
const permissionSet = useMemo(() => new Set(permissions), [permissions]);
const can = useCallback((permission: string) => permissionSet.has(permission), [permissionSet]);
const canAny = useCallback(
(items: string[]) => items.some((permission) => permissionSet.has(permission)),
[permissionSet],
);
return {
user,
can,
canAny,
permissions,
};
}
+102
View File
@@ -0,0 +1,102 @@
import { useCallback, useEffect, useRef, useState } from 'react';
export interface RealtimeLog {
level: string;
message: string;
}
interface ConnectOptions {
clear?: boolean;
onClose?: () => void;
onError?: () => void;
onResult?: () => void;
}
function toWebSocketUrl(pathOrUrl: string): string {
if (pathOrUrl.startsWith('ws://') || pathOrUrl.startsWith('wss://')) {
return pathOrUrl;
}
const protocol = window.location.protocol === 'https:' ? 'wss' : 'ws';
const path = pathOrUrl.startsWith('/') ? pathOrUrl : `/${pathOrUrl}`;
return `${protocol}://${window.location.host}${path}`;
}
export function useWebSocketLogs() {
const [logs, setLogs] = useState<RealtimeLog[]>([]);
const [connected, setConnected] = useState(false);
const wsRef = useRef<WebSocket | null>(null);
const callbacksRef = useRef<ConnectOptions>({});
const suppressCloseRef = useRef(false);
const clearLogs = useCallback(() => {
setLogs([]);
}, []);
const close = useCallback((notify = false) => {
if (!wsRef.current) {
setConnected(false);
return;
}
suppressCloseRef.current = !notify;
wsRef.current.close();
wsRef.current = null;
setConnected(false);
}, []);
const connect = useCallback((pathOrUrl: string, options: ConnectOptions = {}) => {
close(false);
suppressCloseRef.current = false;
callbacksRef.current = options;
if (options.clear ?? true) {
setLogs([]);
}
const ws = new WebSocket(toWebSocketUrl(pathOrUrl));
wsRef.current = ws;
setConnected(true);
ws.onmessage = (event) => {
try {
const msg = JSON.parse(event.data) as RealtimeLog;
if (msg.level === 'heartbeat') return;
if (msg.level === 'result') {
callbacksRef.current.onResult?.();
return;
}
setLogs((prev) => [...prev, msg]);
} catch {
setLogs((prev) => [...prev, { level: 'info', message: String(event.data) }]);
}
};
ws.onclose = () => {
const isCurrent = wsRef.current === ws;
const shouldNotify = isCurrent && !suppressCloseRef.current;
if (isCurrent) {
wsRef.current = null;
setConnected(false);
suppressCloseRef.current = false;
}
if (shouldNotify) {
callbacksRef.current.onClose?.();
}
};
ws.onerror = () => {
setConnected(false);
callbacksRef.current.onError?.();
};
}, [close]);
useEffect(() => () => {
close(false);
}, [close]);
return {
logs,
connected,
clearLogs,
connect,
close,
};
}