From f61978c3b82e4fb8baa7b6bb9538dca51cc1a19b Mon Sep 17 00:00:00 2001 From: shinya Date: Mon, 14 Jul 2025 10:11:25 +0800 Subject: [PATCH 1/8] fix: d1 --- src/app/layout.tsx | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/app/layout.tsx b/src/app/layout.tsx index efd21f5..0b32fd2 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -13,10 +13,14 @@ const inter = Inter({ subsets: ['latin'] }); // 动态生成 metadata,支持配置更新后的标题变化 export async function generateMetadata(): Promise { - const config = await getConfig(); + let siteName = process.env.NEXT_PUBLIC_SITE_NAME; + if (process.env.NEXT_PUBLIC_STORAGE_TYPE === 'd1') { + const config = await getConfig(); + siteName = config.SiteConfig.SiteName; + } return { - title: config.SiteConfig.SiteName, + title: siteName, description: '影视聚合', manifest: '/manifest.json', }; @@ -31,15 +35,26 @@ export default async function RootLayout({ }: { children: React.ReactNode; }) { - const config = await getConfig(); - const siteName = config.SiteConfig.SiteName; - const announcement = config.SiteConfig.Announcement; + let siteName = process.env.NEXT_PUBLIC_SITE_NAME || 'MoonTV'; + let announcement = + process.env.ANNOUNCEMENT || + '本网站仅提供影视信息搜索服务,所有内容均来自第三方网站。本站不存储任何视频资源,不对任何内容的准确性、合法性、完整性负责。'; + let enableRegister = process.env.NEXT_PUBLIC_ENABLE_REGISTER === 'true'; + let aggregateSearchResult = + process.env.NEXT_PUBLIC_AGGREGATE_SEARCH_RESULT !== 'false'; + if (process.env.NEXT_PUBLIC_STORAGE_TYPE !== 'd1') { + const config = await getConfig(); + siteName = config.SiteConfig.SiteName; + announcement = config.SiteConfig.Announcement; + enableRegister = config.UserConfig.AllowRegister; + aggregateSearchResult = config.SiteConfig.SearchResultDefaultAggregate; + } // 将运行时配置注入到全局 window 对象,供客户端在运行时读取 const runtimeConfig = { STORAGE_TYPE: process.env.NEXT_PUBLIC_STORAGE_TYPE || 'localstorage', - ENABLE_REGISTER: config.UserConfig.AllowRegister, - AGGREGATE_SEARCH_RESULT: config.SiteConfig.SearchResultDefaultAggregate, + ENABLE_REGISTER: enableRegister, + AGGREGATE_SEARCH_RESULT: aggregateSearchResult, }; return ( From 8afbc0e0afa786e286ec3649f7f5bb2c583dd302 Mon Sep 17 00:00:00 2001 From: shinya Date: Mon, 14 Jul 2025 10:13:32 +0800 Subject: [PATCH 2/8] fix: fix --- src/app/layout.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 0b32fd2..2149a8e 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -14,7 +14,7 @@ const inter = Inter({ subsets: ['latin'] }); // 动态生成 metadata,支持配置更新后的标题变化 export async function generateMetadata(): Promise { let siteName = process.env.NEXT_PUBLIC_SITE_NAME; - if (process.env.NEXT_PUBLIC_STORAGE_TYPE === 'd1') { + if (process.env.NEXT_PUBLIC_STORAGE_TYPE !== 'd1') { const config = await getConfig(); siteName = config.SiteConfig.SiteName; } From dee31005e7be71a6b112991a015454b159874ecf Mon Sep 17 00:00:00 2001 From: shinya Date: Mon, 14 Jul 2025 10:30:14 +0800 Subject: [PATCH 3/8] fix: init sql --- src/lib/d1.db.ts | 168 ++++++++++++++++++++++++---------------------- src/middleware.ts | 2 +- 2 files changed, 90 insertions(+), 80 deletions(-) diff --git a/src/lib/d1.db.ts b/src/lib/d1.db.ts index ca2ecf7..40faf78 100644 --- a/src/lib/d1.db.ts +++ b/src/lib/d1.db.ts @@ -37,83 +37,6 @@ interface D1ExecResult { duration: number; } -// 数据库初始化 SQL -const INIT_SQL = ` - CREATE TABLE IF NOT EXISTS users ( - username TEXT PRIMARY KEY, - password TEXT NOT NULL, - created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')) - ); - - CREATE TABLE IF NOT EXISTS play_records ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - username TEXT NOT NULL, - key TEXT NOT NULL, - title TEXT NOT NULL, - source_name TEXT NOT NULL, - cover TEXT NOT NULL, - year TEXT NOT NULL, - index_episode INTEGER NOT NULL, - total_episodes INTEGER NOT NULL, - play_time INTEGER NOT NULL, - total_time INTEGER NOT NULL, - save_time INTEGER NOT NULL, - search_title TEXT, - UNIQUE(username, key) - ); - - CREATE TABLE IF NOT EXISTS favorites ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - username TEXT NOT NULL, - key TEXT NOT NULL, - title TEXT NOT NULL, - source_name TEXT NOT NULL, - cover TEXT NOT NULL, - year TEXT NOT NULL, - total_episodes INTEGER NOT NULL, - save_time INTEGER NOT NULL, - UNIQUE(username, key) - ); - - CREATE TABLE IF NOT EXISTS search_history ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - username TEXT NOT NULL, - keyword TEXT NOT NULL, - created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')), - UNIQUE(username, keyword) - ); - - CREATE TABLE IF NOT EXISTS admin_config ( - id INTEGER PRIMARY KEY DEFAULT 1, - config TEXT NOT NULL, - updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')) - ); - - -- 基本索引 - CREATE INDEX IF NOT EXISTS idx_play_records_username ON play_records(username); - CREATE INDEX IF NOT EXISTS idx_favorites_username ON favorites(username); - CREATE INDEX IF NOT EXISTS idx_search_history_username ON search_history(username); - - -- 复合索引优化查询性能 - -- 播放记录:用户名+键值的复合索引,用于快速查找特定记录 - CREATE INDEX IF NOT EXISTS idx_play_records_username_key ON play_records(username, key); - -- 播放记录:用户名+保存时间的复合索引,用于按时间排序的查询 - CREATE INDEX IF NOT EXISTS idx_play_records_username_save_time ON play_records(username, save_time DESC); - - -- 收藏:用户名+键值的复合索引,用于快速查找特定收藏 - CREATE INDEX IF NOT EXISTS idx_favorites_username_key ON favorites(username, key); - -- 收藏:用户名+保存时间的复合索引,用于按时间排序的查询 - CREATE INDEX IF NOT EXISTS idx_favorites_username_save_time ON favorites(username, save_time DESC); - - -- 搜索历史:用户名+关键词的复合索引,用于快速查找/删除特定搜索记录 - CREATE INDEX IF NOT EXISTS idx_search_history_username_keyword ON search_history(username, keyword); - -- 搜索历史:用户名+创建时间的复合索引,用于按时间排序的查询 - CREATE INDEX IF NOT EXISTS idx_search_history_username_created_at ON search_history(username, created_at DESC); - - -- 搜索历史清理查询的优化索引 - CREATE INDEX IF NOT EXISTS idx_search_history_username_id_created_at ON search_history(username, id, created_at DESC); -`; - // 获取全局D1数据库实例 function getD1Database(): D1Database { return (process.env as any).DB as D1Database; @@ -136,9 +59,96 @@ export class D1Storage implements IStorage { private async initDatabase() { try { - if (this.db) { - await this.db.exec(INIT_SQL); + if (!this.db) { + throw new Error('D1 database instance not available'); } + + console.log('Executing D1 database initialization SQL...'); + + // 将初始化SQL分解为单独的语句 + const statements = [ + `CREATE TABLE IF NOT EXISTS users ( + username TEXT PRIMARY KEY, + password TEXT NOT NULL, + created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')) + )`, + + `CREATE TABLE IF NOT EXISTS play_records ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + username TEXT NOT NULL, + key TEXT NOT NULL, + title TEXT NOT NULL, + source_name TEXT NOT NULL, + cover TEXT NOT NULL, + year TEXT NOT NULL, + index_episode INTEGER NOT NULL, + total_episodes INTEGER NOT NULL, + play_time INTEGER NOT NULL, + total_time INTEGER NOT NULL, + save_time INTEGER NOT NULL, + search_title TEXT, + UNIQUE(username, key) + )`, + + `CREATE TABLE IF NOT EXISTS favorites ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + username TEXT NOT NULL, + key TEXT NOT NULL, + title TEXT NOT NULL, + source_name TEXT NOT NULL, + cover TEXT NOT NULL, + year TEXT NOT NULL, + total_episodes INTEGER NOT NULL, + save_time INTEGER NOT NULL, + UNIQUE(username, key) + )`, + + `CREATE TABLE IF NOT EXISTS search_history ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + username TEXT NOT NULL, + keyword TEXT NOT NULL, + created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')), + UNIQUE(username, keyword) + )`, + + `CREATE TABLE IF NOT EXISTS admin_config ( + id INTEGER PRIMARY KEY DEFAULT 1, + config TEXT NOT NULL, + updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')) + )`, + + // 基本索引 + `CREATE INDEX IF NOT EXISTS idx_play_records_username ON play_records(username)`, + `CREATE INDEX IF NOT EXISTS idx_favorites_username ON favorites(username)`, + `CREATE INDEX IF NOT EXISTS idx_search_history_username ON search_history(username)`, + + // 复合索引 + `CREATE INDEX IF NOT EXISTS idx_play_records_username_key ON play_records(username, key)`, + `CREATE INDEX IF NOT EXISTS idx_play_records_username_save_time ON play_records(username, save_time DESC)`, + `CREATE INDEX IF NOT EXISTS idx_favorites_username_key ON favorites(username, key)`, + `CREATE INDEX IF NOT EXISTS idx_favorites_username_save_time ON favorites(username, save_time DESC)`, + `CREATE INDEX IF NOT EXISTS idx_search_history_username_keyword ON search_history(username, keyword)`, + `CREATE INDEX IF NOT EXISTS idx_search_history_username_created_at ON search_history(username, created_at DESC)`, + `CREATE INDEX IF NOT EXISTS idx_search_history_username_id_created_at ON search_history(username, id, created_at DESC)`, + ]; + + // 逐个执行每个SQL语句 + for (let i = 0; i < statements.length; i++) { + const statement = statements[i]; + try { + console.log( + `Executing SQL statement ${i + 1}/${statements.length}:`, + statement.substring(0, 50) + '...' + ); + await this.db.prepare(statement).run(); + } catch (err) { + console.error(`Failed to execute statement ${i + 1}:`, statement); + console.error('Error:', err); + throw err; + } + } + + console.log('D1 database initialization completed successfully'); } catch (err) { console.error('Failed to initialize D1 database:', err); throw err; diff --git a/src/middleware.ts b/src/middleware.ts index 9e96101..c8a8679 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -128,6 +128,6 @@ function shouldSkipAuth(pathname: string): boolean { // 配置middleware匹配规则 export const config = { matcher: [ - '/((?!_next/static|_next/image|favicon.ico|api/detail|api/search|api/image-proxy|api/douban).*)', + '/((?!_next/static|_next/image|favicon.ico|api/detail|api/search|api/image-proxy|api/douban|api/cron|api/server-config).*)', ], }; From 7ee0edb41973b7bd6e5906204b553a5d2ffcdc9f Mon Sep 17 00:00:00 2001 From: shinya Date: Mon, 14 Jul 2025 12:31:52 +0800 Subject: [PATCH 4/8] feat: make getconfig sync --- src/lib/config.ts | 218 +++++++++++++++++++++++----------------------- 1 file changed, 108 insertions(+), 110 deletions(-) diff --git a/src/lib/config.ts b/src/lib/config.ts index 3094831..5636fc3 100644 --- a/src/lib/config.ts +++ b/src/lib/config.ts @@ -68,128 +68,126 @@ async function initConfig() { if (storageType !== 'localstorage') { // 数据库存储,读取并补全管理员配置 const storage = getStorage(); - (async () => { - try { - // 尝试从数据库获取管理员配置 - let adminConfig: AdminConfig | null = null; - if (storage && typeof (storage as any).getAdminConfig === 'function') { - adminConfig = await (storage as any).getAdminConfig(); + + try { + // 尝试从数据库获取管理员配置 + let adminConfig: AdminConfig | null = null; + if (storage && typeof (storage as any).getAdminConfig === 'function') { + adminConfig = await (storage as any).getAdminConfig(); + } + + // 获取所有用户名,用于补全 Users + let userNames: string[] = []; + if (storage && typeof (storage as any).getAllUsers === 'function') { + try { + userNames = await (storage as any).getAllUsers(); + } catch (e) { + console.error('获取用户列表失败:', e); } + } - // 获取所有用户名,用于补全 Users - let userNames: string[] = []; - if (storage && typeof (storage as any).getAllUsers === 'function') { - try { - userNames = await (storage as any).getAllUsers(); - } catch (e) { - console.error('获取用户列表失败:', e); - } - } + // 从文件中获取源信息,用于补全源 + const apiSiteEntries = Object.entries(fileConfig.api_site); - // 从文件中获取源信息,用于补全源 - const apiSiteEntries = Object.entries(fileConfig.api_site); - - if (adminConfig) { - // 补全 SourceConfig - const existed = new Set( - (adminConfig.SourceConfig || []).map((s) => s.key) - ); - apiSiteEntries.forEach(([key, site]) => { - if (!existed.has(key)) { - adminConfig!.SourceConfig.push({ - key, - name: site.name, - api: site.api, - detail: site.detail, - from: 'config', - disabled: false, - }); - } - }); - - // 检查现有源是否在 fileConfig.api_site 中,如果不在则标记为 custom - const apiSiteKeys = new Set(apiSiteEntries.map(([key]) => key)); - adminConfig.SourceConfig.forEach((source) => { - if (!apiSiteKeys.has(source.key)) { - source.from = 'custom'; - } - }); - - const existedUsers = new Set( - (adminConfig.UserConfig.Users || []).map((u) => u.username) - ); - userNames.forEach((uname) => { - if (!existedUsers.has(uname)) { - adminConfig!.UserConfig.Users.push({ - username: uname, - role: 'user', - }); - } - }); - // 站长 - const ownerUser = process.env.USERNAME; - if (ownerUser) { - adminConfig!.UserConfig.Users = - adminConfig!.UserConfig.Users.filter( - (u) => u.username !== ownerUser - ); - adminConfig!.UserConfig.Users.unshift({ - username: ownerUser, - role: 'owner', - }); - } - } else { - // 数据库中没有配置,创建新的管理员配置 - let allUsers = userNames.map((uname) => ({ - username: uname, - role: 'user', - })); - const ownerUser = process.env.USERNAME; - if (ownerUser) { - allUsers = allUsers.filter((u) => u.username !== ownerUser); - allUsers.unshift({ - username: ownerUser, - role: 'owner', - }); - } - adminConfig = { - SiteConfig: { - SiteName: process.env.SITE_NAME || 'MoonTV', - Announcement: - process.env.ANNOUNCEMENT || - '本网站仅提供影视信息搜索服务,所有内容均来自第三方网站。本站不存储任何视频资源,不对任何内容的准确性、合法性、完整性负责。', - SearchDownstreamMaxPage: - Number(process.env.NEXT_PUBLIC_SEARCH_MAX_PAGE) || 5, - SiteInterfaceCacheTime: fileConfig.cache_time || 7200, - SearchResultDefaultAggregate: - process.env.NEXT_PUBLIC_AGGREGATE_SEARCH_RESULT !== 'false', - }, - UserConfig: { - AllowRegister: process.env.NEXT_PUBLIC_ENABLE_REGISTER === 'true', - Users: allUsers as any, - }, - SourceConfig: apiSiteEntries.map(([key, site]) => ({ + if (adminConfig) { + // 补全 SourceConfig + const existed = new Set( + (adminConfig.SourceConfig || []).map((s) => s.key) + ); + apiSiteEntries.forEach(([key, site]) => { + if (!existed.has(key)) { + adminConfig!.SourceConfig.push({ key, name: site.name, api: site.api, detail: site.detail, from: 'config', disabled: false, - })), - }; - } + }); + } + }); - // 写回数据库(更新/创建) - if (storage && typeof (storage as any).setAdminConfig === 'function') { - await (storage as any).setAdminConfig(adminConfig); - } + // 检查现有源是否在 fileConfig.api_site 中,如果不在则标记为 custom + const apiSiteKeys = new Set(apiSiteEntries.map(([key]) => key)); + adminConfig.SourceConfig.forEach((source) => { + if (!apiSiteKeys.has(source.key)) { + source.from = 'custom'; + } + }); - // 更新缓存 - cachedConfig = adminConfig; - } catch (err) { - console.error('加载管理员配置失败:', err); + const existedUsers = new Set( + (adminConfig.UserConfig.Users || []).map((u) => u.username) + ); + userNames.forEach((uname) => { + if (!existedUsers.has(uname)) { + adminConfig!.UserConfig.Users.push({ + username: uname, + role: 'user', + }); + } + }); + // 站长 + const ownerUser = process.env.USERNAME; + if (ownerUser) { + adminConfig!.UserConfig.Users = adminConfig!.UserConfig.Users.filter( + (u) => u.username !== ownerUser + ); + adminConfig!.UserConfig.Users.unshift({ + username: ownerUser, + role: 'owner', + }); + } + } else { + // 数据库中没有配置,创建新的管理员配置 + let allUsers = userNames.map((uname) => ({ + username: uname, + role: 'user', + })); + const ownerUser = process.env.USERNAME; + if (ownerUser) { + allUsers = allUsers.filter((u) => u.username !== ownerUser); + allUsers.unshift({ + username: ownerUser, + role: 'owner', + }); + } + adminConfig = { + SiteConfig: { + SiteName: process.env.SITE_NAME || 'MoonTV', + Announcement: + process.env.ANNOUNCEMENT || + '本网站仅提供影视信息搜索服务,所有内容均来自第三方网站。本站不存储任何视频资源,不对任何内容的准确性、合法性、完整性负责。', + SearchDownstreamMaxPage: + Number(process.env.NEXT_PUBLIC_SEARCH_MAX_PAGE) || 5, + SiteInterfaceCacheTime: fileConfig.cache_time || 7200, + SearchResultDefaultAggregate: + process.env.NEXT_PUBLIC_AGGREGATE_SEARCH_RESULT !== 'false', + }, + UserConfig: { + AllowRegister: process.env.NEXT_PUBLIC_ENABLE_REGISTER === 'true', + Users: allUsers as any, + }, + SourceConfig: apiSiteEntries.map(([key, site]) => ({ + key, + name: site.name, + api: site.api, + detail: site.detail, + from: 'config', + disabled: false, + })), + }; } - })(); + + // 写回数据库(更新/创建) + if (storage && typeof (storage as any).setAdminConfig === 'function') { + await (storage as any).setAdminConfig(adminConfig); + } + + // 更新缓存 + cachedConfig = adminConfig; + } catch (err) { + console.error('加载管理员配置失败:', err); + } } else { // 本地存储直接使用文件配置 cachedConfig = { From 590ef4dd6e7882223e80b0fdf2378e1b06fe91a4 Mon Sep 17 00:00:00 2001 From: shinya Date: Mon, 14 Jul 2025 12:33:34 +0800 Subject: [PATCH 5/8] feat: delete d1 init --- D1初始化.md | 75 ++++++++++++++++++++++++++++++++++ src/lib/d1.db.ts | 103 ----------------------------------------------- 2 files changed, 75 insertions(+), 103 deletions(-) create mode 100644 D1初始化.md diff --git a/D1初始化.md b/D1初始化.md new file mode 100644 index 0000000..f6dc790 --- /dev/null +++ b/D1初始化.md @@ -0,0 +1,75 @@ +```sql +CREATE TABLE IF NOT EXISTS users ( + username TEXT PRIMARY KEY, + password TEXT NOT NULL, + created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')) + ); + + CREATE TABLE IF NOT EXISTS play_records ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + username TEXT NOT NULL, + key TEXT NOT NULL, + title TEXT NOT NULL, + source_name TEXT NOT NULL, + cover TEXT NOT NULL, + year TEXT NOT NULL, + index_episode INTEGER NOT NULL, + total_episodes INTEGER NOT NULL, + play_time INTEGER NOT NULL, + total_time INTEGER NOT NULL, + save_time INTEGER NOT NULL, + search_title TEXT, + UNIQUE(username, key) + ); + + CREATE TABLE IF NOT EXISTS favorites ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + username TEXT NOT NULL, + key TEXT NOT NULL, + title TEXT NOT NULL, + source_name TEXT NOT NULL, + cover TEXT NOT NULL, + year TEXT NOT NULL, + total_episodes INTEGER NOT NULL, + save_time INTEGER NOT NULL, + UNIQUE(username, key) + ); + + CREATE TABLE IF NOT EXISTS search_history ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + username TEXT NOT NULL, + keyword TEXT NOT NULL, + created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')), + UNIQUE(username, keyword) + ); + + CREATE TABLE IF NOT EXISTS admin_config ( + id INTEGER PRIMARY KEY DEFAULT 1, + config TEXT NOT NULL, + updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')) + ); + + -- 基本索引 + CREATE INDEX IF NOT EXISTS idx_play_records_username ON play_records(username); + CREATE INDEX IF NOT EXISTS idx_favorites_username ON favorites(username); + CREATE INDEX IF NOT EXISTS idx_search_history_username ON search_history(username); + + -- 复合索引优化查询性能 + -- 播放记录:用户名+键值的复合索引,用于快速查找特定记录 + CREATE INDEX IF NOT EXISTS idx_play_records_username_key ON play_records(username, key); + -- 播放记录:用户名+保存时间的复合索引,用于按时间排序的查询 + CREATE INDEX IF NOT EXISTS idx_play_records_username_save_time ON play_records(username, save_time DESC); + + -- 收藏:用户名+键值的复合索引,用于快速查找特定收藏 + CREATE INDEX IF NOT EXISTS idx_favorites_username_key ON favorites(username, key); + -- 收藏:用户名+保存时间的复合索引,用于按时间排序的查询 + CREATE INDEX IF NOT EXISTS idx_favorites_username_save_time ON favorites(username, save_time DESC); + + -- 搜索历史:用户名+关键词的复合索引,用于快速查找/删除特定搜索记录 + CREATE INDEX IF NOT EXISTS idx_search_history_username_keyword ON search_history(username, keyword); + -- 搜索历史:用户名+创建时间的复合索引,用于按时间排序的查询 + CREATE INDEX IF NOT EXISTS idx_search_history_username_created_at ON search_history(username, created_at DESC); + + -- 搜索历史清理查询的优化索引 + CREATE INDEX IF NOT EXISTS idx_search_history_username_id_created_at ON search_history(username, id, created_at DESC); +``` diff --git a/src/lib/d1.db.ts b/src/lib/d1.db.ts index 40faf78..87f487a 100644 --- a/src/lib/d1.db.ts +++ b/src/lib/d1.db.ts @@ -44,117 +44,14 @@ function getD1Database(): D1Database { export class D1Storage implements IStorage { private db: D1Database | null = null; - private initialized = false; private async getDatabase(): Promise { if (!this.db) { this.db = getD1Database(); - if (!this.initialized) { - await this.initDatabase(); - this.initialized = true; - } } return this.db; } - private async initDatabase() { - try { - if (!this.db) { - throw new Error('D1 database instance not available'); - } - - console.log('Executing D1 database initialization SQL...'); - - // 将初始化SQL分解为单独的语句 - const statements = [ - `CREATE TABLE IF NOT EXISTS users ( - username TEXT PRIMARY KEY, - password TEXT NOT NULL, - created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')) - )`, - - `CREATE TABLE IF NOT EXISTS play_records ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - username TEXT NOT NULL, - key TEXT NOT NULL, - title TEXT NOT NULL, - source_name TEXT NOT NULL, - cover TEXT NOT NULL, - year TEXT NOT NULL, - index_episode INTEGER NOT NULL, - total_episodes INTEGER NOT NULL, - play_time INTEGER NOT NULL, - total_time INTEGER NOT NULL, - save_time INTEGER NOT NULL, - search_title TEXT, - UNIQUE(username, key) - )`, - - `CREATE TABLE IF NOT EXISTS favorites ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - username TEXT NOT NULL, - key TEXT NOT NULL, - title TEXT NOT NULL, - source_name TEXT NOT NULL, - cover TEXT NOT NULL, - year TEXT NOT NULL, - total_episodes INTEGER NOT NULL, - save_time INTEGER NOT NULL, - UNIQUE(username, key) - )`, - - `CREATE TABLE IF NOT EXISTS search_history ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - username TEXT NOT NULL, - keyword TEXT NOT NULL, - created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')), - UNIQUE(username, keyword) - )`, - - `CREATE TABLE IF NOT EXISTS admin_config ( - id INTEGER PRIMARY KEY DEFAULT 1, - config TEXT NOT NULL, - updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')) - )`, - - // 基本索引 - `CREATE INDEX IF NOT EXISTS idx_play_records_username ON play_records(username)`, - `CREATE INDEX IF NOT EXISTS idx_favorites_username ON favorites(username)`, - `CREATE INDEX IF NOT EXISTS idx_search_history_username ON search_history(username)`, - - // 复合索引 - `CREATE INDEX IF NOT EXISTS idx_play_records_username_key ON play_records(username, key)`, - `CREATE INDEX IF NOT EXISTS idx_play_records_username_save_time ON play_records(username, save_time DESC)`, - `CREATE INDEX IF NOT EXISTS idx_favorites_username_key ON favorites(username, key)`, - `CREATE INDEX IF NOT EXISTS idx_favorites_username_save_time ON favorites(username, save_time DESC)`, - `CREATE INDEX IF NOT EXISTS idx_search_history_username_keyword ON search_history(username, keyword)`, - `CREATE INDEX IF NOT EXISTS idx_search_history_username_created_at ON search_history(username, created_at DESC)`, - `CREATE INDEX IF NOT EXISTS idx_search_history_username_id_created_at ON search_history(username, id, created_at DESC)`, - ]; - - // 逐个执行每个SQL语句 - for (let i = 0; i < statements.length; i++) { - const statement = statements[i]; - try { - console.log( - `Executing SQL statement ${i + 1}/${statements.length}:`, - statement.substring(0, 50) + '...' - ); - await this.db.prepare(statement).run(); - } catch (err) { - console.error(`Failed to execute statement ${i + 1}:`, statement); - console.error('Error:', err); - throw err; - } - } - - console.log('D1 database initialization completed successfully'); - } catch (err) { - console.error('Failed to initialize D1 database:', err); - throw err; - } - } - // 播放记录相关 async getPlayRecord( userName: string, From d28cd0dd840acf4ce5e7fb30cc504d414d0f5cdd Mon Sep 17 00:00:00 2001 From: shinya Date: Mon, 14 Jul 2025 12:53:13 +0800 Subject: [PATCH 6/8] fix: getConfig --- src/lib/config.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/lib/config.ts b/src/lib/config.ts index 5636fc3..3d04241 100644 --- a/src/lib/config.ts +++ b/src/lib/config.ts @@ -219,7 +219,23 @@ async function initConfig() { } export async function getConfig(): Promise { - await initConfig(); + const storageType = process.env.NEXT_PUBLIC_STORAGE_TYPE || 'localstorage'; + if (process.env.DOCKER_ENV === 'true' || storageType === 'localstorage') { + await initConfig(); + return cachedConfig; + } + // 非 docker 环境且 DB 存储,直接读 db 配置 + const storage = getStorage(); + let adminConfig: AdminConfig | null = null; + if (storage && typeof (storage as any).getAdminConfig === 'function') { + adminConfig = await (storage as any).getAdminConfig(); + } + if (adminConfig) { + cachedConfig = adminConfig; + } else { + // DB 无配置,执行一次初始化 + await initConfig(); + } return cachedConfig; } From f14e774b3b8ade84d157aa9585203e67694ccea2 Mon Sep 17 00:00:00 2001 From: shinya Date: Mon, 14 Jul 2025 12:57:16 +0800 Subject: [PATCH 7/8] feat: disable d1 change config --- src/app/admin/page.tsx | 80 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 70 insertions(+), 10 deletions(-) diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx index bc9859f..8e4f490 100644 --- a/src/app/admin/page.tsx +++ b/src/app/admin/page.tsx @@ -126,6 +126,11 @@ const UserConfig = ({ config, role, refreshConfig }: UserConfigProps) => { // 当前登录用户名 const currentUsername = getAuthInfoFromBrowserCookie()?.username || null; + // 检测存储类型是否为 d1 + const isD1Storage = + typeof window !== 'undefined' && + (window as any).RUNTIME_CONFIG?.STORAGE_TYPE === 'd1'; + useEffect(() => { if (config?.UserConfig) { setUserSettings({ @@ -285,18 +290,29 @@ const UserConfig = ({ config, role, refreshConfig }: UserConfigProps) => { 注册设置
-