/* eslint-disable @typescript-eslint/no-explicit-any, no-console, @typescript-eslint/no-non-null-assertion */ import { db } from '@/lib/db'; import { AdminConfig } from './admin.types'; export interface ApiSite { key: string; api: string; name: string; detail?: string; } interface ConfigFileStruct { cache_time?: number; api_site?: { [key: string]: ApiSite; }; custom_category?: { name?: string; type: 'movie' | 'tv'; query: string; }[]; } export const API_CONFIG = { search: { path: '?ac=videolist&wd=', pagePath: '?ac=videolist&wd={query}&pg={page}', headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36', Accept: 'application/json', }, }, detail: { path: '?ac=videolist&ids=', headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36', Accept: 'application/json', }, }, }; // 在模块加载时根据环境决定配置来源 let cachedConfig: AdminConfig; // 从配置文件补充管理员配置 export function refineConfig(adminConfig: AdminConfig): AdminConfig { let fileConfig: ConfigFileStruct; try { fileConfig = JSON.parse(adminConfig.ConfigFile) as ConfigFileStruct; } catch (e) { fileConfig = {} as ConfigFileStruct; } // 合并文件中的源信息 const apiSitesFromFile = Object.entries(fileConfig.api_site || []); const currentApiSites = new Map( (adminConfig.SourceConfig || []).map((s) => [s.key, s]) ); apiSitesFromFile.forEach(([key, site]) => { const existingSource = currentApiSites.get(key); if (existingSource) { // 如果已存在,只覆盖 name、api、detail 和 from existingSource.name = site.name; existingSource.api = site.api; existingSource.detail = site.detail; existingSource.from = 'config'; } else { // 如果不存在,创建新条目 currentApiSites.set(key, { key, name: site.name, api: site.api, detail: site.detail, from: 'config', disabled: false, }); } }); // 检查现有源是否在 fileConfig.api_site 中,如果不在则标记为 custom const apiSitesFromFileKey = new Set(apiSitesFromFile.map(([key]) => key)); currentApiSites.forEach((source) => { if (!apiSitesFromFileKey.has(source.key)) { source.from = 'custom'; } }); // 将 Map 转换回数组 adminConfig.SourceConfig = Array.from(currentApiSites.values()); // 覆盖 CustomCategories const customCategoriesFromFile = fileConfig.custom_category || []; const currentCustomCategories = new Map( (adminConfig.CustomCategories || []).map((c) => [c.query + c.type, c]) ); customCategoriesFromFile.forEach((category) => { const key = category.query + category.type; const existedCategory = currentCustomCategories.get(key); if (existedCategory) { existedCategory.name = category.name; existedCategory.query = category.query; existedCategory.type = category.type; existedCategory.from = 'config'; } else { currentCustomCategories.set(key, { name: category.name, type: category.type, query: category.query, from: 'config', disabled: false, }); } }); // 检查现有 CustomCategories 是否在 fileConfig.custom_category 中,如果不在则标记为 custom const customCategoriesFromFileKeys = new Set( customCategoriesFromFile.map((c) => c.query + c.type) ); currentCustomCategories.forEach((category) => { if (!customCategoriesFromFileKeys.has(category.query + category.type)) { category.from = 'custom'; } }); // 将 Map 转换回数组 adminConfig.CustomCategories = Array.from(currentCustomCategories.values()); return adminConfig; } async function getInitConfig(configFile: string, subConfig: { URL: string; AutoUpdate: boolean; LastCheck: string; } = { URL: "", AutoUpdate: false, LastCheck: "", }): Promise { let cfgFile: ConfigFileStruct; try { cfgFile = JSON.parse(configFile) as ConfigFileStruct; } catch (e) { cfgFile = {} as ConfigFileStruct; } const adminConfig: AdminConfig = { ConfigFile: configFile, ConfigSubscribtion: subConfig, SiteConfig: { SiteName: process.env.NEXT_PUBLIC_SITE_NAME || 'MoonTV', Announcement: process.env.ANNOUNCEMENT || '本网站仅提供影视信息搜索服务,所有内容均来自第三方网站。本站不存储任何视频资源,不对任何内容的准确性、合法性、完整性负责。', SearchDownstreamMaxPage: Number(process.env.NEXT_PUBLIC_SEARCH_MAX_PAGE) || 5, SiteInterfaceCacheTime: cfgFile.cache_time || 7200, DoubanProxyType: process.env.NEXT_PUBLIC_DOUBAN_PROXY_TYPE || 'direct', DoubanProxy: process.env.NEXT_PUBLIC_DOUBAN_PROXY || '', DoubanImageProxyType: process.env.NEXT_PUBLIC_DOUBAN_IMAGE_PROXY_TYPE || 'direct', DoubanImageProxy: process.env.NEXT_PUBLIC_DOUBAN_IMAGE_PROXY || '', DisableYellowFilter: process.env.NEXT_PUBLIC_DISABLE_YELLOW_FILTER === 'true', }, UserConfig: { AllowRegister: process.env.NEXT_PUBLIC_ENABLE_REGISTER === 'true', Users: [], }, SourceConfig: [], CustomCategories: [], }; // 补充用户信息 let userNames: string[] = []; try { userNames = await db.getAllUsers(); } catch (e) { console.error('获取用户列表失败:', e); } const allUsers = userNames.filter((u) => u !== process.env.USERNAME).map((u) => ({ username: u, role: 'user', banned: false, })); allUsers.unshift({ username: process.env.USERNAME!, role: 'owner', banned: false, }); adminConfig.UserConfig.Users = allUsers as any; // 从配置文件中补充源信息 Object.entries(cfgFile.api_site || []).forEach(([key, site]) => { adminConfig.SourceConfig.push({ key: key, name: site.name, api: site.api, detail: site.detail, from: 'config', disabled: false, }); }); // 从配置文件中补充自定义分类信息 cfgFile.custom_category?.forEach((category) => { adminConfig.CustomCategories.push({ name: category.name || category.query, type: category.type, query: category.query, from: 'config', disabled: false, }); }); return adminConfig; } export async function getConfig(): Promise { // 直接使用内存缓存 if (cachedConfig) { return cachedConfig; } // 读 db let adminConfig: AdminConfig | null = null; try { adminConfig = await db.getAdminConfig(); } catch (e) { console.error('获取管理员配置失败:', e); } // db 中无配置,执行一次初始化 if (!adminConfig) { adminConfig = await getInitConfig(""); } adminConfig = configSelfCheck(adminConfig); cachedConfig = adminConfig; db.saveAdminConfig(cachedConfig); return cachedConfig; } export function configSelfCheck(adminConfig: AdminConfig): AdminConfig { // 确保必要的属性存在和初始化 if (!adminConfig.UserConfig) { adminConfig.UserConfig = { AllowRegister: false, Users: [] }; } if (!adminConfig.UserConfig.Users || !Array.isArray(adminConfig.UserConfig.Users)) { adminConfig.UserConfig.Users = []; } if (!adminConfig.SourceConfig || !Array.isArray(adminConfig.SourceConfig)) { adminConfig.SourceConfig = []; } if (!adminConfig.CustomCategories || !Array.isArray(adminConfig.CustomCategories)) { adminConfig.CustomCategories = []; } // 站长变更自检 const ownerUser = process.env.USERNAME; // 去重 const seenUsernames = new Set(); adminConfig.UserConfig.Users = adminConfig.UserConfig.Users.filter((user) => { if (seenUsernames.has(user.username)) { return false; } seenUsernames.add(user.username); return true; }); // 过滤站长 adminConfig.UserConfig.Users = adminConfig.UserConfig.Users.filter((user) => user.username !== ownerUser); // 其他用户不得拥有 owner 权限 adminConfig.UserConfig.Users.forEach((user) => { if (user.role === 'owner') { user.role = 'user'; } }); // 重新添加回站长 adminConfig.UserConfig.Users.unshift({ username: ownerUser!, role: 'owner', banned: false, }); // 采集源去重 const seenSourceKeys = new Set(); adminConfig.SourceConfig = adminConfig.SourceConfig.filter((source) => { if (seenSourceKeys.has(source.key)) { return false; } seenSourceKeys.add(source.key); return true; }); // 自定义分类去重 const seenCustomCategoryKeys = new Set(); adminConfig.CustomCategories = adminConfig.CustomCategories.filter((category) => { if (seenCustomCategoryKeys.has(category.query + category.type)) { return false; } seenCustomCategoryKeys.add(category.query + category.type); return true; }); return adminConfig; } export async function resetConfig() { let originConfig: AdminConfig | null = null; try { originConfig = await db.getAdminConfig(); } catch (e) { console.error('获取管理员配置失败:', e); } if (!originConfig) { originConfig = {} as AdminConfig; } const adminConfig = await getInitConfig(originConfig.ConfigFile, originConfig.ConfigSubscribtion); cachedConfig = adminConfig; await db.saveAdminConfig(adminConfig); return; } export async function getCacheTime(): Promise { const config = await getConfig(); return config.SiteConfig.SiteInterfaceCacheTime || 7200; } export async function getAvailableApiSites(): Promise { const config = await getConfig(); return config.SourceConfig.filter((s) => !s.disabled).map((s) => ({ key: s.key, name: s.name, api: s.api, detail: s.detail, })); } export async function setCachedConfig(config: AdminConfig) { cachedConfig = config; }