feat: implement hybrid database architecture and frontend encryption

- Add PostgreSQL + SQLite hybrid database support with automatic switching
- Implement frontend AES-GCM + RSA-OAEP encryption for sensitive data
- Add comprehensive DatabaseInterface with all required methods
- Fix compilation issues with interface consistency
- Update all database method signatures to use DatabaseInterface
- Add missing UpdateTraderInitialBalance method to PostgreSQL implementation
- Integrate RSA public key distribution via /api/config endpoint
- Add frontend crypto service with proper error handling
- Support graceful degradation between encrypted and plaintext transmission
- Add directory creation for RSA keys and PEM parsing fixes
- Test both SQLite and PostgreSQL modes successfully

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: tinkle-community <tinklefund@gmail.com>
This commit is contained in:
icy
2025-11-06 01:50:06 +08:00
parent aecc5e58a1
commit 65053518d6
104 changed files with 16864 additions and 4152 deletions

View File

@@ -1,62 +1,86 @@
import React, { createContext, useContext, useState, useEffect } from 'react';
import { getSystemConfig } from '../lib/config';
import React, { createContext, useContext, useState, useEffect } from 'react'
import { getSystemConfig } from '../lib/config'
interface User {
id: string;
email: string;
id: string
email: string
}
interface AuthContextType {
user: User | null;
token: string | null;
login: (email: string, password: string) => Promise<{ success: boolean; message?: string; userID?: string; requiresOTP?: boolean }>;
register: (email: string, password: string, betaCode?: string) => Promise<{ success: boolean; message?: string; userID?: string; otpSecret?: string; qrCodeURL?: string }>;
verifyOTP: (userID: string, otpCode: string) => Promise<{ success: boolean; message?: string }>;
completeRegistration: (userID: string, otpCode: string) => Promise<{ success: boolean; message?: string }>;
logout: () => void;
isLoading: boolean;
user: User | null
token: string | null
login: (
email: string,
password: string
) => Promise<{
success: boolean
message?: string
userID?: string
requiresOTP?: boolean
}>
register: (
email: string,
password: string,
betaCode?: string
) => Promise<{
success: boolean
message?: string
userID?: string
otpSecret?: string
qrCodeURL?: string
}>
verifyOTP: (
userID: string,
otpCode: string
) => Promise<{ success: boolean; message?: string }>
completeRegistration: (
userID: string,
otpCode: string
) => Promise<{ success: boolean; message?: string }>
logout: () => void
isLoading: boolean
}
const AuthContext = createContext<AuthContextType | undefined>(undefined);
const AuthContext = createContext<AuthContextType | undefined>(undefined)
export function AuthProvider({ children }: { children: React.ReactNode }) {
const [user, setUser] = useState<User | null>(null);
const [token, setToken] = useState<string | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [user, setUser] = useState<User | null>(null)
const [token, setToken] = useState<string | null>(null)
const [isLoading, setIsLoading] = useState(true)
useEffect(() => {
// 先检查是否为管理员模式(使用带缓存的系统配置获取)
getSystemConfig()
.then(data => {
.then((data) => {
if (data.admin_mode) {
// 管理员模式下模拟admin用户
setUser({ id: 'admin', email: 'admin@localhost' });
setToken('admin-mode');
setUser({ id: 'admin', email: 'admin@localhost' })
setToken('admin-mode')
} else {
// 非管理员模式检查本地存储中是否有token
const savedToken = localStorage.getItem('auth_token');
const savedUser = localStorage.getItem('auth_user');
const savedToken = localStorage.getItem('auth_token')
const savedUser = localStorage.getItem('auth_user')
if (savedToken && savedUser) {
setToken(savedToken);
setUser(JSON.parse(savedUser));
setToken(savedToken)
setUser(JSON.parse(savedUser))
}
}
setIsLoading(false);
setIsLoading(false)
})
.catch(err => {
console.error('Failed to fetch system config:', err);
.catch((err) => {
console.error('Failed to fetch system config:', err)
// 发生错误时,继续检查本地存储
const savedToken = localStorage.getItem('auth_token');
const savedUser = localStorage.getItem('auth_user');
const savedToken = localStorage.getItem('auth_token')
const savedUser = localStorage.getItem('auth_user')
if (savedToken && savedUser) {
setToken(savedToken);
setUser(JSON.parse(savedUser));
setToken(savedToken)
setUser(JSON.parse(savedUser))
}
setIsLoading(false);
});
}, []);
setIsLoading(false)
})
}, [])
const login = async (email: string, password: string) => {
try {
@@ -66,9 +90,9 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
'Content-Type': 'application/json',
},
body: JSON.stringify({ email, password }),
});
})
const data = await response.json();
const data = await response.json()
if (response.ok) {
if (data.requires_otp) {
@@ -77,23 +101,31 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
userID: data.user_id,
requiresOTP: true,
message: data.message,
};
}
}
} else {
return { success: false, message: data.error };
return { success: false, message: data.error }
}
} catch (error) {
return { success: false, message: '登录失败,请重试' };
return { success: false, message: '登录失败,请重试' }
}
return { success: false, message: '未知错误' };
};
return { success: false, message: '未知错误' }
}
const register = async (email: string, password: string, betaCode?: string) => {
const register = async (
email: string,
password: string,
betaCode?: string
) => {
try {
const requestBody: { email: string; password: string; beta_code?: string } = { email, password };
const requestBody: {
email: string
password: string
beta_code?: string
} = { email, password }
if (betaCode) {
requestBody.beta_code = betaCode;
requestBody.beta_code = betaCode
}
const response = await fetch('/api/register', {
@@ -102,9 +134,9 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
'Content-Type': 'application/json',
},
body: JSON.stringify(requestBody),
});
})
const data = await response.json();
const data = await response.json()
if (response.ok) {
return {
@@ -113,14 +145,14 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
otpSecret: data.otp_secret,
qrCodeURL: data.qr_code_url,
message: data.message,
};
}
} else {
return { success: false, message: data.error };
return { success: false, message: data.error }
}
} catch (error) {
return { success: false, message: '注册失败,请重试' };
return { success: false, message: '注册失败,请重试' }
}
};
}
const verifyOTP = async (userID: string, otpCode: string) => {
try {
@@ -130,30 +162,30 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
'Content-Type': 'application/json',
},
body: JSON.stringify({ user_id: userID, otp_code: otpCode }),
});
})
const data = await response.json();
const data = await response.json()
if (response.ok) {
// 登录成功保存token和用户信息
const userInfo = { id: data.user_id, email: data.email };
setToken(data.token);
setUser(userInfo);
localStorage.setItem('auth_token', data.token);
localStorage.setItem('auth_user', JSON.stringify(userInfo));
// 跳转到首页
window.history.pushState({}, '', '/');
window.dispatchEvent(new PopStateEvent('popstate'));
return { success: true, message: data.message };
const userInfo = { id: data.user_id, email: data.email }
setToken(data.token)
setUser(userInfo)
localStorage.setItem('auth_token', data.token)
localStorage.setItem('auth_user', JSON.stringify(userInfo))
// 跳转到配置页面
window.history.pushState({}, '', '/traders')
window.dispatchEvent(new PopStateEvent('popstate'))
return { success: true, message: data.message }
} else {
return { success: false, message: data.error };
return { success: false, message: data.error }
}
} catch (error) {
return { success: false, message: 'OTP验证失败请重试' };
return { success: false, message: 'OTP验证失败请重试' }
}
};
}
const completeRegistration = async (userID: string, otpCode: string) => {
try {
@@ -163,37 +195,37 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
'Content-Type': 'application/json',
},
body: JSON.stringify({ user_id: userID, otp_code: otpCode }),
});
})
const data = await response.json();
const data = await response.json()
if (response.ok) {
// 注册完成,自动登录
const userInfo = { id: data.user_id, email: data.email };
setToken(data.token);
setUser(userInfo);
localStorage.setItem('auth_token', data.token);
localStorage.setItem('auth_user', JSON.stringify(userInfo));
// 跳转到首页
window.history.pushState({}, '', '/');
window.dispatchEvent(new PopStateEvent('popstate'));
return { success: true, message: data.message };
const userInfo = { id: data.user_id, email: data.email }
setToken(data.token)
setUser(userInfo)
localStorage.setItem('auth_token', data.token)
localStorage.setItem('auth_user', JSON.stringify(userInfo))
// 跳转到配置页面
window.history.pushState({}, '', '/traders')
window.dispatchEvent(new PopStateEvent('popstate'))
return { success: true, message: data.message }
} else {
return { success: false, message: data.error };
return { success: false, message: data.error }
}
} catch (error) {
return { success: false, message: '注册完成失败,请重试' };
return { success: false, message: '注册完成失败,请重试' }
}
};
}
const logout = () => {
setUser(null);
setToken(null);
localStorage.removeItem('auth_token');
localStorage.removeItem('auth_user');
};
setUser(null)
setToken(null)
localStorage.removeItem('auth_token')
localStorage.removeItem('auth_user')
}
return (
<AuthContext.Provider
@@ -210,13 +242,13 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
>
{children}
</AuthContext.Provider>
);
)
}
export function useAuth() {
const context = useContext(AuthContext);
const context = useContext(AuthContext)
if (context === undefined) {
throw new Error('useAuth must be used within an AuthProvider');
throw new Error('useAuth must be used within an AuthProvider')
}
return context;
}
return context
}