/* eslint-disable no-console, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */ import { AdminConfig } from './admin.types'; import { KvrocksStorage } from './kvrocks.db'; import { RedisStorage } from './redis.db'; import { Favorite, IStorage, PlayRecord, SkipConfig, DanmakuFilterConfig } from './types'; import { UpstashRedisStorage } from './upstash.db'; // storage type 常量: 'localstorage' | 'redis' | 'upstash',默认 'localstorage' const STORAGE_TYPE = (process.env.NEXT_PUBLIC_STORAGE_TYPE as | 'localstorage' | 'redis' | 'upstash' | 'kvrocks' | undefined) || 'localstorage'; // 创建存储实例 function createStorage(): IStorage { switch (STORAGE_TYPE) { case 'redis': return new RedisStorage(); case 'upstash': return new UpstashRedisStorage(); case 'kvrocks': return new KvrocksStorage(); case 'localstorage': default: return null as unknown as IStorage; } } // 单例存储实例 let storageInstance: IStorage | null = null; export function getStorage(): IStorage { if (!storageInstance) { storageInstance = createStorage(); } return storageInstance; } // 工具函数:生成存储key export function generateStorageKey(source: string, id: string): string { return `${source}+${id}`; } // 导出便捷方法 export class DbManager { private storage: IStorage; constructor() { this.storage = getStorage(); } // 播放记录相关方法 async getPlayRecord( userName: string, source: string, id: string ): Promise { const key = generateStorageKey(source, id); return this.storage.getPlayRecord(userName, key); } async savePlayRecord( userName: string, source: string, id: string, record: PlayRecord ): Promise { const key = generateStorageKey(source, id); await this.storage.setPlayRecord(userName, key, record); } async getAllPlayRecords(userName: string): Promise<{ [key: string]: PlayRecord; }> { return this.storage.getAllPlayRecords(userName); } async deletePlayRecord( userName: string, source: string, id: string ): Promise { const key = generateStorageKey(source, id); await this.storage.deletePlayRecord(userName, key); } // 收藏相关方法 async getFavorite( userName: string, source: string, id: string ): Promise { const key = generateStorageKey(source, id); return this.storage.getFavorite(userName, key); } async saveFavorite( userName: string, source: string, id: string, favorite: Favorite ): Promise { const key = generateStorageKey(source, id); await this.storage.setFavorite(userName, key, favorite); } async getAllFavorites( userName: string ): Promise<{ [key: string]: Favorite }> { return this.storage.getAllFavorites(userName); } async deleteFavorite( userName: string, source: string, id: string ): Promise { const key = generateStorageKey(source, id); await this.storage.deleteFavorite(userName, key); } async isFavorited( userName: string, source: string, id: string ): Promise { const favorite = await this.getFavorite(userName, source, id); return favorite !== null; } // ---------- 用户相关(旧版本,保持兼容) ---------- async registerUser(userName: string, password: string): Promise { await this.storage.registerUser(userName, password); } async verifyUser(userName: string, password: string): Promise { return this.storage.verifyUser(userName, password); } // 检查用户是否已存在 async checkUserExist(userName: string): Promise { return this.storage.checkUserExist(userName); } async changePassword(userName: string, newPassword: string): Promise { await this.storage.changePassword(userName, newPassword); } async deleteUser(userName: string): Promise { await this.storage.deleteUser(userName); } // ---------- 用户相关(新版本) ---------- async createUserV2( userName: string, password: string, role: 'owner' | 'admin' | 'user' = 'user', tags?: string[], oidcSub?: string, enabledApis?: string[] ): Promise { if (typeof (this.storage as any).createUserV2 === 'function') { await (this.storage as any).createUserV2(userName, password, role, tags, oidcSub, enabledApis); } } async verifyUserV2(userName: string, password: string): Promise { if (typeof (this.storage as any).verifyUserV2 === 'function') { return (this.storage as any).verifyUserV2(userName, password); } return false; } async getUserInfoV2(userName: string): Promise<{ role: 'owner' | 'admin' | 'user'; banned: boolean; tags?: string[]; oidcSub?: string; enabledApis?: string[]; created_at: number; playrecord_migrated?: boolean; } | null> { if (typeof (this.storage as any).getUserInfoV2 === 'function') { return (this.storage as any).getUserInfoV2(userName); } return null; } async updateUserInfoV2( userName: string, updates: { role?: 'owner' | 'admin' | 'user'; banned?: boolean; tags?: string[]; oidcSub?: string; enabledApis?: string[]; } ): Promise { if (typeof (this.storage as any).updateUserInfoV2 === 'function') { await (this.storage as any).updateUserInfoV2(userName, updates); } } async changePasswordV2(userName: string, newPassword: string): Promise { if (typeof (this.storage as any).changePasswordV2 === 'function') { await (this.storage as any).changePasswordV2(userName, newPassword); } } async checkUserExistV2(userName: string): Promise { if (typeof (this.storage as any).checkUserExistV2 === 'function') { return (this.storage as any).checkUserExistV2(userName); } return false; } async getUserByOidcSub(oidcSub: string): Promise { if (typeof (this.storage as any).getUserByOidcSub === 'function') { return (this.storage as any).getUserByOidcSub(oidcSub); } return null; } async getUserListV2( offset: number = 0, limit: number = 20, ownerUsername?: string ): Promise<{ users: Array<{ username: string; role: 'owner' | 'admin' | 'user'; banned: boolean; tags?: string[]; oidcSub?: string; enabledApis?: string[]; created_at: number; }>; total: number; }> { if (typeof (this.storage as any).getUserListV2 === 'function') { return (this.storage as any).getUserListV2(offset, limit, ownerUsername); } return { users: [], total: 0 }; } async deleteUserV2(userName: string): Promise { if (typeof (this.storage as any).deleteUserV2 === 'function') { await (this.storage as any).deleteUserV2(userName); } } async getUsersByTag(tagName: string): Promise { if (typeof (this.storage as any).getUsersByTag === 'function') { return (this.storage as any).getUsersByTag(tagName); } return []; } // ---------- 播放记录迁移 ---------- async migratePlayRecords(userName: string): Promise { if (typeof (this.storage as any).migratePlayRecords === 'function') { await (this.storage as any).migratePlayRecords(userName); } } // ---------- 收藏迁移 ---------- async migrateFavorites(userName: string): Promise { if (typeof (this.storage as any).migrateFavorites === 'function') { await (this.storage as any).migrateFavorites(userName); } } // ---------- 数据迁移 ---------- async migrateUsersFromConfig(adminConfig: AdminConfig): Promise { if (typeof (this.storage as any).createUserV2 !== 'function') { throw new Error('当前存储类型不支持新版用户存储'); } const users = adminConfig.UserConfig.Users; if (!users || users.length === 0) { return; } console.log(`开始迁移 ${users.length} 个用户...`); for (const user of users) { try { // 跳过环境变量中的站长(站长使用环境变量认证,不需要迁移) if (user.username === process.env.USERNAME) { console.log(`跳过站长 ${user.username} 的迁移`); continue; } // 检查用户是否已经迁移 const exists = await this.checkUserExistV2(user.username); if (exists) { console.log(`用户 ${user.username} 已存在,跳过迁移`); continue; } // 获取密码 let password = ''; // 如果是OIDC用户,生成随机密码(OIDC用户不需要密码登录) if ((user as any).oidcSub) { password = crypto.randomUUID(); console.log(`用户 ${user.username} (OIDC用户) 使用随机密码迁移`); } // 尝试从旧的存储中获取密码 else { try { if ((this.storage as any).client) { const storedPassword = await (this.storage as any).client.get(`u:${user.username}:pwd`); if (storedPassword) { password = storedPassword; console.log(`用户 ${user.username} 使用旧密码迁移`); } else { // 没有旧密码,使用默认密码 password = 'defaultPassword123'; console.log(`用户 ${user.username} 没有旧密码,使用默认密码`); } } else { password = 'defaultPassword123'; } } catch (err) { console.error(`获取用户 ${user.username} 的密码失败,使用默认密码`, err); password = 'defaultPassword123'; } } // 将站长角色转换为普通角色 const migratedRole = user.role === 'owner' ? 'user' : user.role; if (user.role === 'owner') { console.log(`用户 ${user.username} 的角色从 owner 转换为 user`); } // 创建新用户 await this.createUserV2( user.username, password, migratedRole, user.tags, (user as any).oidcSub, user.enabledApis ); // 如果用户被封禁,更新状态 if (user.banned) { await this.updateUserInfoV2(user.username, { banned: true }); } console.log(`用户 ${user.username} 迁移成功`); } catch (err) { console.error(`迁移用户 ${user.username} 失败:`, err); } } console.log('用户迁移完成'); } // ---------- 搜索历史 ---------- async getSearchHistory(userName: string): Promise { return this.storage.getSearchHistory(userName); } async addSearchHistory(userName: string, keyword: string): Promise { await this.storage.addSearchHistory(userName, keyword); } async deleteSearchHistory(userName: string, keyword?: string): Promise { await this.storage.deleteSearchHistory(userName, keyword); } // 获取全部用户名 async getAllUsers(): Promise { if (typeof (this.storage as any).getAllUsers === 'function') { return (this.storage as any).getAllUsers(); } return []; } // ---------- 管理员配置 ---------- async getAdminConfig(): Promise { if (typeof (this.storage as any).getAdminConfig === 'function') { return (this.storage as any).getAdminConfig(); } return null; } async saveAdminConfig(config: AdminConfig): Promise { if (typeof (this.storage as any).setAdminConfig === 'function') { await (this.storage as any).setAdminConfig(config); } } // ---------- 跳过片头片尾配置 ---------- async getSkipConfig( userName: string, source: string, id: string ): Promise { if (typeof (this.storage as any).getSkipConfig === 'function') { return (this.storage as any).getSkipConfig(userName, source, id); } return null; } async setSkipConfig( userName: string, source: string, id: string, config: SkipConfig ): Promise { if (typeof (this.storage as any).setSkipConfig === 'function') { await (this.storage as any).setSkipConfig(userName, source, id, config); } } async deleteSkipConfig( userName: string, source: string, id: string ): Promise { if (typeof (this.storage as any).deleteSkipConfig === 'function') { await (this.storage as any).deleteSkipConfig(userName, source, id); } } async getAllSkipConfigs( userName: string ): Promise<{ [key: string]: SkipConfig }> { if (typeof (this.storage as any).getAllSkipConfigs === 'function') { return (this.storage as any).getAllSkipConfigs(userName); } return {}; } // ---------- 弹幕过滤配置 ---------- async getDanmakuFilterConfig(userName: string): Promise { if (typeof (this.storage as any).getDanmakuFilterConfig === 'function') { return (this.storage as any).getDanmakuFilterConfig(userName); } return null; } async setDanmakuFilterConfig( userName: string, config: DanmakuFilterConfig ): Promise { if (typeof (this.storage as any).setDanmakuFilterConfig === 'function') { await (this.storage as any).setDanmakuFilterConfig(userName, config); } } async deleteDanmakuFilterConfig(userName: string): Promise { if (typeof (this.storage as any).deleteDanmakuFilterConfig === 'function') { await (this.storage as any).deleteDanmakuFilterConfig(userName); } } // ---------- 数据清理 ---------- async clearAllData(): Promise { if (typeof (this.storage as any).clearAllData === 'function') { await (this.storage as any).clearAllData(); } else { throw new Error('存储类型不支持清空数据操作'); } } // ---------- 通用键值存储 ---------- async getGlobalValue(key: string): Promise { if (typeof (this.storage as any).getGlobalValue === 'function') { return (this.storage as any).getGlobalValue(key); } return null; } async setGlobalValue(key: string, value: string): Promise { if (typeof (this.storage as any).setGlobalValue === 'function') { await (this.storage as any).setGlobalValue(key, value); } } async deleteGlobalValue(key: string): Promise { if (typeof (this.storage as any).deleteGlobalValue === 'function') { await (this.storage as any).deleteGlobalValue(key); } } } // 导出默认实例 export const db = new DbManager();