From b06665788f2f152d5b5500c97810bf305ee1f759 Mon Sep 17 00:00:00 2001 From: katelya Date: Thu, 4 Sep 2025 21:25:45 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=AE=8C=E5=96=84=E6=88=90=E4=BA=BA?= =?UTF-8?q?=E5=86=85=E5=AE=B9=E8=BF=87=E6=BB=A4=E5=8A=9F=E8=83=BD=E7=9A=84?= =?UTF-8?q?=E9=83=A8=E7=BD=B2=E5=85=BC=E5=AE=B9=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 为用户设置API添加Edge Runtime配置确保部署兼容性 - 完善所有存储后端的用户设置方法实现 - 为D1数据库添加user_settings表迁移脚本 - 修复TypeScript类型错误和构建兼容性 - 所有25个API路由现在都正确配置了Edge Runtime - 确保Docker、Cloudflare Pages等各平台部署正常运行 --- D1_MIGRATION.md | 12 ++++++ public/sw.js | 2 +- src/app/api/user/settings/route.ts | 3 ++ src/lib/d1.db.ts | 61 +++++++++++++++++++++++++++++- src/lib/kvrocks.db.ts | 44 ++++++++++++++++++++- src/lib/upstash.db.ts | 42 ++++++++++++++++++++ 6 files changed, 161 insertions(+), 3 deletions(-) diff --git a/D1_MIGRATION.md b/D1_MIGRATION.md index a199f84..eddbc66 100644 --- a/D1_MIGRATION.md +++ b/D1_MIGRATION.md @@ -26,6 +26,18 @@ CREATE TABLE IF NOT EXISTS skip_configs ( CREATE INDEX IF NOT EXISTS idx_skip_configs_username ON skip_configs(username); CREATE INDEX IF NOT EXISTS idx_skip_configs_username_key ON skip_configs(username, key); CREATE INDEX IF NOT EXISTS idx_skip_configs_username_updated_time ON skip_configs(username, updated_time DESC); + +-- 创建用户设置表(成人内容过滤功能) +CREATE TABLE IF NOT EXISTS user_settings ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + username TEXT NOT NULL UNIQUE, + settings TEXT NOT NULL, + updated_time INTEGER NOT NULL +); + +-- 为用户设置添加索引以优化查询性能 +CREATE INDEX IF NOT EXISTS idx_user_settings_username ON user_settings(username); +CREATE INDEX IF NOT EXISTS idx_user_settings_updated_time ON user_settings(updated_time DESC); ``` ## 🚀 执行迁移的方法 diff --git a/public/sw.js b/public/sw.js index 8ecbe10..d0a2e09 100644 --- a/public/sw.js +++ b/public/sw.js @@ -1 +1 @@ -if(!self.define){let e,s={};const n=(n,t)=>(n=new URL(n+".js",t).href,s[n]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=n,e.onload=s,document.head.appendChild(e)}else e=n,importScripts(n),s()}).then(()=>{let e=s[n];if(!e)throw new Error(`Module ${n} didn’t register its module`);return e}));self.define=(t,a)=>{const c=e||("document"in self?document.currentScript.src:"")||location.href;if(s[c])return;let i={};const r=e=>n(e,c),o={module:{uri:c},exports:i,require:r};s[c]=Promise.all(t.map(e=>o[e]||r(e))).then(e=>(a(...e),i))}}define(["./workbox-e9849328"],function(e){"use strict";importScripts(),self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"/_next/app-build-manifest.json",revision:"7db9e4ef70cbcab3780d6c680dd662a2"},{url:"/_next/static/_tNEn3OI_gHK8Eg73JHHK/_buildManifest.js",revision:"046380ae5bc74b46b6d5eac3eed65355"},{url:"/_next/static/_tNEn3OI_gHK8Eg73JHHK/_ssgManifest.js",revision:"b6652df95db52feb4daf4eca35380933"},{url:"/_next/static/chunks/110-9df7e37d43792a8e.js",revision:"_tNEn3OI_gHK8Eg73JHHK"},{url:"/_next/static/chunks/154-de4a84fd5b2e0100.js",revision:"_tNEn3OI_gHK8Eg73JHHK"},{url:"/_next/static/chunks/29-0844689411ca7d55.js",revision:"_tNEn3OI_gHK8Eg73JHHK"},{url:"/_next/static/chunks/459-6bec40a8423cc309.js",revision:"_tNEn3OI_gHK8Eg73JHHK"},{url:"/_next/static/chunks/51b697cb-f464f3017ac1ea30.js",revision:"_tNEn3OI_gHK8Eg73JHHK"},{url:"/_next/static/chunks/682-d1dca8d17a3a8e6f.js",revision:"_tNEn3OI_gHK8Eg73JHHK"},{url:"/_next/static/chunks/900-fb094d8873768e88.js",revision:"_tNEn3OI_gHK8Eg73JHHK"},{url:"/_next/static/chunks/967-217cdcb80ae3beeb.js",revision:"_tNEn3OI_gHK8Eg73JHHK"},{url:"/_next/static/chunks/998-568996670b543597.js",revision:"_tNEn3OI_gHK8Eg73JHHK"},{url:"/_next/static/chunks/app/_not-found/page-ac328df06cf68f14.js",revision:"_tNEn3OI_gHK8Eg73JHHK"},{url:"/_next/static/chunks/app/admin/page-d05d4621a6953d54.js",revision:"_tNEn3OI_gHK8Eg73JHHK"},{url:"/_next/static/chunks/app/config/page-11f6321397ad65b1.js",revision:"_tNEn3OI_gHK8Eg73JHHK"},{url:"/_next/static/chunks/app/douban/page-2d0023184aa37aff.js",revision:"_tNEn3OI_gHK8Eg73JHHK"},{url:"/_next/static/chunks/app/layout-bd0bfbfdb401e15f.js",revision:"_tNEn3OI_gHK8Eg73JHHK"},{url:"/_next/static/chunks/app/login/page-1638e1d936c78592.js",revision:"_tNEn3OI_gHK8Eg73JHHK"},{url:"/_next/static/chunks/app/page-6a58e37ab3250691.js",revision:"_tNEn3OI_gHK8Eg73JHHK"},{url:"/_next/static/chunks/app/play/page-586b6c5a6381cf6d.js",revision:"_tNEn3OI_gHK8Eg73JHHK"},{url:"/_next/static/chunks/app/search/page-63fe30b91e0539a7.js",revision:"_tNEn3OI_gHK8Eg73JHHK"},{url:"/_next/static/chunks/app/tvbox/page-3a990d4dba7ad091.js",revision:"_tNEn3OI_gHK8Eg73JHHK"},{url:"/_next/static/chunks/app/warning/page-11cba4cf9332a238.js",revision:"_tNEn3OI_gHK8Eg73JHHK"},{url:"/_next/static/chunks/c72274ce-06682d6fc8197e6d.js",revision:"_tNEn3OI_gHK8Eg73JHHK"},{url:"/_next/static/chunks/da9543df-bf6da1a431d8604f.js",revision:"_tNEn3OI_gHK8Eg73JHHK"},{url:"/_next/static/chunks/framework-6e06c675866dc992.js",revision:"_tNEn3OI_gHK8Eg73JHHK"},{url:"/_next/static/chunks/main-459a10fe41fde25d.js",revision:"_tNEn3OI_gHK8Eg73JHHK"},{url:"/_next/static/chunks/main-app-dbd320e104e1a5dc.js",revision:"_tNEn3OI_gHK8Eg73JHHK"},{url:"/_next/static/chunks/pages/_app-792b631a362c29e1.js",revision:"_tNEn3OI_gHK8Eg73JHHK"},{url:"/_next/static/chunks/pages/_error-9fde6601392a2a99.js",revision:"_tNEn3OI_gHK8Eg73JHHK"},{url:"/_next/static/chunks/polyfills-42372ed130431b0a.js",revision:"846118c33b2c0e922d7b3a7676f81f6f"},{url:"/_next/static/chunks/webpack-17170f1d90853b2d.js",revision:"_tNEn3OI_gHK8Eg73JHHK"},{url:"/_next/static/css/00661c2d88d90da0.css",revision:"00661c2d88d90da0"},{url:"/_next/static/css/23100062f5d4aac0.css",revision:"23100062f5d4aac0"},{url:"/_next/static/css/275ed64cc4367444.css",revision:"275ed64cc4367444"},{url:"/_next/static/media/26a46d62cd723877-s.woff2",revision:"befd9c0fdfa3d8a645d5f95717ed6420"},{url:"/_next/static/media/55c55f0601d81cf3-s.woff2",revision:"43828e14271c77b87e3ed582dbff9f74"},{url:"/_next/static/media/581909926a08bbc8-s.woff2",revision:"f0b86e7c24f455280b8df606b89af891"},{url:"/_next/static/media/8e9860b6e62d6359-s.woff2",revision:"01ba6c2a184b8cba08b0d57167664d75"},{url:"/_next/static/media/97e0cb1ae144a2a9-s.woff2",revision:"e360c61c5bd8d90639fd4503c829c2dc"},{url:"/_next/static/media/df0a9ae256c0569c-s.woff2",revision:"d54db44de5ccb18886ece2fda72bdfe0"},{url:"/_next/static/media/e4af272ccee01ff0-s.p.woff2",revision:"65850a373e258f1c897a2b3d75eb74de"},{url:"/favicon.ico",revision:"c5de6e56c5664adda146825f75ea6ecf"},{url:"/icons/icon-192x192.png",revision:"4a56c090828a1ad254c903c7aec0389d"},{url:"/icons/icon-256x256.png",revision:"f6409eb1a001f754121e3a8281c0319c"},{url:"/icons/icon-384x384.png",revision:"f6efc3e357b9ffdf4e0d8c14b2ed0ac1"},{url:"/icons/icon-512x512.png",revision:"9c008cbbeb6a576fe07bb1284a83f4d2"},{url:"/logo.png",revision:"40de611b143c47c6291c7bdad2c959ca"},{url:"/manifest.json",revision:"7bd3dabc1cfbfe40f09577efca223d31"},{url:"/robots.txt",revision:"e2b2cd8514443456bc6fb9d77b3b1f3e"},{url:"/screenshot1.png",revision:"10572bfcea54dc93ac4c5f7c9057fc98"},{url:"/screenshot2.png",revision:"f815a8990973a221899976867365c239"},{url:"/screenshot3.png",revision:"49709e96345dfeeab1d8083821d4b44e"},{url:"/screenshot4.png",revision:"a76c751e41e37556048a487e4f8b8b1c"},{url:"/wechat.jpg",revision:"d0f601311802667cd6ca5a37dc69bfa7"}],{ignoreURLParametersMatching:[]}),e.cleanupOutdatedCaches(),e.registerRoute("/",new e.NetworkFirst({cacheName:"start-url",plugins:[{cacheWillUpdate:async({request:e,response:s,event:n,state:t})=>s&&"opaqueredirect"===s.type?new Response(s.body,{status:200,statusText:"OK",headers:s.headers}):s}]}),"GET"),e.registerRoute(/^https:\/\/fonts\.(?:gstatic)\.com\/.*/i,new e.CacheFirst({cacheName:"google-fonts-webfonts",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:31536e3})]}),"GET"),e.registerRoute(/^https:\/\/fonts\.(?:googleapis)\.com\/.*/i,new e.StaleWhileRevalidate({cacheName:"google-fonts-stylesheets",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:604800})]}),"GET"),e.registerRoute(/\.(?:eot|otf|ttc|ttf|woff|woff2|font.css)$/i,new e.StaleWhileRevalidate({cacheName:"static-font-assets",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:604800})]}),"GET"),e.registerRoute(/\.(?:jpg|jpeg|gif|png|svg|ico|webp)$/i,new e.StaleWhileRevalidate({cacheName:"static-image-assets",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/_next\/image\?url=.+$/i,new e.StaleWhileRevalidate({cacheName:"next-image",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:mp3|wav|ogg)$/i,new e.CacheFirst({cacheName:"static-audio-assets",plugins:[new e.RangeRequestsPlugin,new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:mp4)$/i,new e.CacheFirst({cacheName:"static-video-assets",plugins:[new e.RangeRequestsPlugin,new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:js)$/i,new e.StaleWhileRevalidate({cacheName:"static-js-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:css|less)$/i,new e.StaleWhileRevalidate({cacheName:"static-style-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/_next\/data\/.+\/.+\.json$/i,new e.StaleWhileRevalidate({cacheName:"next-data",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:json|xml|csv)$/i,new e.NetworkFirst({cacheName:"static-data-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:e})=>{if(!(self.origin===e.origin))return!1;const s=e.pathname;return!s.startsWith("/api/auth/")&&!!s.startsWith("/api/")},new e.NetworkFirst({cacheName:"apis",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:16,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:e})=>{if(!(self.origin===e.origin))return!1;return!e.pathname.startsWith("/api/")},new e.NetworkFirst({cacheName:"others",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:e})=>!(self.origin===e.origin),new e.NetworkFirst({cacheName:"cross-origin",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:3600})]}),"GET")}); +if(!self.define){let e,s={};const c=(c,n)=>(c=new URL(c+".js",n).href,s[c]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=c,e.onload=s,document.head.appendChild(e)}else e=c,importScripts(c),s()}).then(()=>{let e=s[c];if(!e)throw new Error(`Module ${c} didn’t register its module`);return e}));self.define=(n,t)=>{const i=e||("document"in self?document.currentScript.src:"")||location.href;if(s[i])return;let a={};const r=e=>c(e,i),o={module:{uri:i},exports:a,require:r};s[i]=Promise.all(n.map(e=>o[e]||r(e))).then(e=>(t(...e),a))}}define(["./workbox-e9849328"],function(e){"use strict";importScripts(),self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"/_next/app-build-manifest.json",revision:"dc1d5ac623300622e02724e27ecf2e18"},{url:"/_next/static/Cc11st-UPy6zUFRhlXlF0/_buildManifest.js",revision:"046380ae5bc74b46b6d5eac3eed65355"},{url:"/_next/static/Cc11st-UPy6zUFRhlXlF0/_ssgManifest.js",revision:"b6652df95db52feb4daf4eca35380933"},{url:"/_next/static/chunks/110-5af0f210f33cfa1d.js",revision:"Cc11st-UPy6zUFRhlXlF0"},{url:"/_next/static/chunks/154-211e189482cc0258.js",revision:"Cc11st-UPy6zUFRhlXlF0"},{url:"/_next/static/chunks/29-2acace5e289d422b.js",revision:"Cc11st-UPy6zUFRhlXlF0"},{url:"/_next/static/chunks/459-0617417b9c423d5b.js",revision:"Cc11st-UPy6zUFRhlXlF0"},{url:"/_next/static/chunks/51b697cb-24a59f0c53e2e105.js",revision:"Cc11st-UPy6zUFRhlXlF0"},{url:"/_next/static/chunks/682-e64c4c7808d3e611.js",revision:"Cc11st-UPy6zUFRhlXlF0"},{url:"/_next/static/chunks/900-c7c9e505cc903ead.js",revision:"Cc11st-UPy6zUFRhlXlF0"},{url:"/_next/static/chunks/967-ac2d9567775f072e.js",revision:"Cc11st-UPy6zUFRhlXlF0"},{url:"/_next/static/chunks/998-f22ebd15e7bac0f0.js",revision:"Cc11st-UPy6zUFRhlXlF0"},{url:"/_next/static/chunks/app/_not-found/page-4dc7d52fd5d943cc.js",revision:"Cc11st-UPy6zUFRhlXlF0"},{url:"/_next/static/chunks/app/admin/page-03c708c7e6227f30.js",revision:"Cc11st-UPy6zUFRhlXlF0"},{url:"/_next/static/chunks/app/config/page-578e0487b53650a4.js",revision:"Cc11st-UPy6zUFRhlXlF0"},{url:"/_next/static/chunks/app/douban/page-23af0908f4f16f4d.js",revision:"Cc11st-UPy6zUFRhlXlF0"},{url:"/_next/static/chunks/app/layout-41b89aac912b9c2b.js",revision:"Cc11st-UPy6zUFRhlXlF0"},{url:"/_next/static/chunks/app/login/page-bbafa236e79f06c3.js",revision:"Cc11st-UPy6zUFRhlXlF0"},{url:"/_next/static/chunks/app/page-8f189c83607ce0f9.js",revision:"Cc11st-UPy6zUFRhlXlF0"},{url:"/_next/static/chunks/app/play/page-546c607d23bbe9b6.js",revision:"Cc11st-UPy6zUFRhlXlF0"},{url:"/_next/static/chunks/app/search/page-015039692c653292.js",revision:"Cc11st-UPy6zUFRhlXlF0"},{url:"/_next/static/chunks/app/settings/page-ef076e30e268b7f3.js",revision:"Cc11st-UPy6zUFRhlXlF0"},{url:"/_next/static/chunks/app/tvbox/page-443d4dd8e3c842b3.js",revision:"Cc11st-UPy6zUFRhlXlF0"},{url:"/_next/static/chunks/app/warning/page-11cba4cf9332a238.js",revision:"Cc11st-UPy6zUFRhlXlF0"},{url:"/_next/static/chunks/c72274ce-06682d6fc8197e6d.js",revision:"Cc11st-UPy6zUFRhlXlF0"},{url:"/_next/static/chunks/da9543df-bf6da1a431d8604f.js",revision:"Cc11st-UPy6zUFRhlXlF0"},{url:"/_next/static/chunks/framework-6e06c675866dc992.js",revision:"Cc11st-UPy6zUFRhlXlF0"},{url:"/_next/static/chunks/main-6c3022dbb9d044ff.js",revision:"Cc11st-UPy6zUFRhlXlF0"},{url:"/_next/static/chunks/main-app-dbd320e104e1a5dc.js",revision:"Cc11st-UPy6zUFRhlXlF0"},{url:"/_next/static/chunks/pages/_app-792b631a362c29e1.js",revision:"Cc11st-UPy6zUFRhlXlF0"},{url:"/_next/static/chunks/pages/_error-9fde6601392a2a99.js",revision:"Cc11st-UPy6zUFRhlXlF0"},{url:"/_next/static/chunks/polyfills-42372ed130431b0a.js",revision:"846118c33b2c0e922d7b3a7676f81f6f"},{url:"/_next/static/chunks/webpack-17170f1d90853b2d.js",revision:"Cc11st-UPy6zUFRhlXlF0"},{url:"/_next/static/css/23100062f5d4aac0.css",revision:"23100062f5d4aac0"},{url:"/_next/static/css/275ed64cc4367444.css",revision:"275ed64cc4367444"},{url:"/_next/static/css/71c2bf6dfbfb4376.css",revision:"71c2bf6dfbfb4376"},{url:"/_next/static/media/26a46d62cd723877-s.woff2",revision:"befd9c0fdfa3d8a645d5f95717ed6420"},{url:"/_next/static/media/55c55f0601d81cf3-s.woff2",revision:"43828e14271c77b87e3ed582dbff9f74"},{url:"/_next/static/media/581909926a08bbc8-s.woff2",revision:"f0b86e7c24f455280b8df606b89af891"},{url:"/_next/static/media/8e9860b6e62d6359-s.woff2",revision:"01ba6c2a184b8cba08b0d57167664d75"},{url:"/_next/static/media/97e0cb1ae144a2a9-s.woff2",revision:"e360c61c5bd8d90639fd4503c829c2dc"},{url:"/_next/static/media/df0a9ae256c0569c-s.woff2",revision:"d54db44de5ccb18886ece2fda72bdfe0"},{url:"/_next/static/media/e4af272ccee01ff0-s.p.woff2",revision:"65850a373e258f1c897a2b3d75eb74de"},{url:"/favicon.ico",revision:"c5de6e56c5664adda146825f75ea6ecf"},{url:"/icons/icon-192x192.png",revision:"4a56c090828a1ad254c903c7aec0389d"},{url:"/icons/icon-256x256.png",revision:"f6409eb1a001f754121e3a8281c0319c"},{url:"/icons/icon-384x384.png",revision:"f6efc3e357b9ffdf4e0d8c14b2ed0ac1"},{url:"/icons/icon-512x512.png",revision:"9c008cbbeb6a576fe07bb1284a83f4d2"},{url:"/logo.png",revision:"40de611b143c47c6291c7bdad2c959ca"},{url:"/manifest.json",revision:"7bd3dabc1cfbfe40f09577efca223d31"},{url:"/robots.txt",revision:"e2b2cd8514443456bc6fb9d77b3b1f3e"},{url:"/screenshot1.png",revision:"10572bfcea54dc93ac4c5f7c9057fc98"},{url:"/screenshot2.png",revision:"f815a8990973a221899976867365c239"},{url:"/screenshot3.png",revision:"49709e96345dfeeab1d8083821d4b44e"},{url:"/screenshot4.png",revision:"a76c751e41e37556048a487e4f8b8b1c"},{url:"/wechat.jpg",revision:"d0f601311802667cd6ca5a37dc69bfa7"}],{ignoreURLParametersMatching:[]}),e.cleanupOutdatedCaches(),e.registerRoute("/",new e.NetworkFirst({cacheName:"start-url",plugins:[{cacheWillUpdate:async({request:e,response:s,event:c,state:n})=>s&&"opaqueredirect"===s.type?new Response(s.body,{status:200,statusText:"OK",headers:s.headers}):s}]}),"GET"),e.registerRoute(/^https:\/\/fonts\.(?:gstatic)\.com\/.*/i,new e.CacheFirst({cacheName:"google-fonts-webfonts",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:31536e3})]}),"GET"),e.registerRoute(/^https:\/\/fonts\.(?:googleapis)\.com\/.*/i,new e.StaleWhileRevalidate({cacheName:"google-fonts-stylesheets",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:604800})]}),"GET"),e.registerRoute(/\.(?:eot|otf|ttc|ttf|woff|woff2|font.css)$/i,new e.StaleWhileRevalidate({cacheName:"static-font-assets",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:604800})]}),"GET"),e.registerRoute(/\.(?:jpg|jpeg|gif|png|svg|ico|webp)$/i,new e.StaleWhileRevalidate({cacheName:"static-image-assets",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/_next\/image\?url=.+$/i,new e.StaleWhileRevalidate({cacheName:"next-image",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:mp3|wav|ogg)$/i,new e.CacheFirst({cacheName:"static-audio-assets",plugins:[new e.RangeRequestsPlugin,new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:mp4)$/i,new e.CacheFirst({cacheName:"static-video-assets",plugins:[new e.RangeRequestsPlugin,new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:js)$/i,new e.StaleWhileRevalidate({cacheName:"static-js-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:css|less)$/i,new e.StaleWhileRevalidate({cacheName:"static-style-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/_next\/data\/.+\/.+\.json$/i,new e.StaleWhileRevalidate({cacheName:"next-data",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:json|xml|csv)$/i,new e.NetworkFirst({cacheName:"static-data-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:e})=>{if(!(self.origin===e.origin))return!1;const s=e.pathname;return!s.startsWith("/api/auth/")&&!!s.startsWith("/api/")},new e.NetworkFirst({cacheName:"apis",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:16,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:e})=>{if(!(self.origin===e.origin))return!1;return!e.pathname.startsWith("/api/")},new e.NetworkFirst({cacheName:"others",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:e})=>!(self.origin===e.origin),new e.NetworkFirst({cacheName:"cross-origin",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:3600})]}),"GET")}); diff --git a/src/app/api/user/settings/route.ts b/src/app/api/user/settings/route.ts index 9ab9a44..33dffe3 100644 --- a/src/app/api/user/settings/route.ts +++ b/src/app/api/user/settings/route.ts @@ -4,6 +4,9 @@ import { NextRequest, NextResponse } from 'next/server'; import { getStorage } from '@/lib/db'; import { UserSettings } from '@/lib/types'; +// 设置运行时为 Edge Runtime,确保部署兼容性 +export const runtime = 'edge'; + // 获取用户设置 export async function GET(_request: NextRequest) { try { diff --git a/src/lib/d1.db.ts b/src/lib/d1.db.ts index 84e9ee0..10d57c1 100644 --- a/src/lib/d1.db.ts +++ b/src/lib/d1.db.ts @@ -1,7 +1,7 @@ /* eslint-disable no-console, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */ import { AdminConfig } from './admin.types'; -import { EpisodeSkipConfig, Favorite, IStorage, PlayRecord } from './types'; +import { EpisodeSkipConfig, Favorite, IStorage, PlayRecord, UserSettings } from './types'; // 搜索历史最大条数 const SEARCH_HISTORY_LIMIT = 20; @@ -573,4 +573,63 @@ export class D1Storage implements IStorage { throw err; } } + + // ---------- 用户设置 ---------- + async getUserSettings(userName: string): Promise { + try { + const db = await this.getDatabase(); + const row = await db + .prepare('SELECT settings FROM user_settings WHERE username = ?') + .bind(userName) + .first(); + + if (row && row.settings) { + return JSON.parse(row.settings as string) as UserSettings; + } + return null; + } catch (err) { + console.error('Failed to get user settings:', err); + throw err; + } + } + + async setUserSettings( + userName: string, + settings: UserSettings + ): Promise { + try { + const db = await this.getDatabase(); + await db + .prepare(` + INSERT OR REPLACE INTO user_settings (username, settings, updated_time) + VALUES (?, ?, ?) + `) + .bind(userName, JSON.stringify(settings), Date.now()) + .run(); + } catch (err) { + console.error('Failed to set user settings:', err); + throw err; + } + } + + async updateUserSettings( + userName: string, + settings: Partial + ): Promise { + const current = await this.getUserSettings(userName); + const defaultSettings: UserSettings = { + filter_adult_content: true, + theme: 'auto', + language: 'zh-CN', + auto_play: false, + video_quality: 'auto' + }; + const updated: UserSettings = { + ...defaultSettings, + ...current, + ...settings, + filter_adult_content: settings.filter_adult_content ?? current?.filter_adult_content ?? true + }; + await this.setUserSettings(userName, updated); + } } diff --git a/src/lib/kvrocks.db.ts b/src/lib/kvrocks.db.ts index d5a3f3f..5482b73 100644 --- a/src/lib/kvrocks.db.ts +++ b/src/lib/kvrocks.db.ts @@ -3,7 +3,7 @@ import { createClient, RedisClientType } from 'redis'; import { AdminConfig } from './admin.types'; -import { EpisodeSkipConfig, Favorite, IStorage, PlayRecord } from './types'; +import { EpisodeSkipConfig, Favorite, IStorage, PlayRecord, UserSettings } from './types'; // 搜索历史最大条数 const SEARCH_HISTORY_LIMIT = 20; @@ -337,6 +337,48 @@ export class KvrocksStorage implements IStorage { this.client.set(this.adminConfigKey(), JSON.stringify(config)) ); } + + // ---------- 用户设置 ---------- + private userSettingsKey(userName: string) { + return `u:${userName}:settings`; + } + + async getUserSettings(userName: string): Promise { + const val = await withRetry(() => + this.client.get(this.userSettingsKey(userName)) + ); + return val ? (JSON.parse(val) as UserSettings) : null; + } + + async setUserSettings( + userName: string, + settings: UserSettings + ): Promise { + await withRetry(() => + this.client.set(this.userSettingsKey(userName), JSON.stringify(settings)) + ); + } + + async updateUserSettings( + userName: string, + settings: Partial + ): Promise { + const current = await this.getUserSettings(userName); + const defaultSettings: UserSettings = { + filter_adult_content: true, + theme: 'auto', + language: 'zh-CN', + auto_play: false, + video_quality: 'auto' + }; + const updated: UserSettings = { + ...defaultSettings, + ...current, + ...settings, + filter_adult_content: settings.filter_adult_content ?? current?.filter_adult_content ?? true + }; + await this.setUserSettings(userName, updated); + } } // Kvrocks客户端单例 diff --git a/src/lib/upstash.db.ts b/src/lib/upstash.db.ts index f4ef3ce..c578feb 100644 --- a/src/lib/upstash.db.ts +++ b/src/lib/upstash.db.ts @@ -329,6 +329,48 @@ export class UpstashRedisStorage implements IStorage { await this.client.srem(this.skipConfigsKey(userName), key); }); } + + // ---------- 用户设置 ---------- + private userSettingsKey(userName: string) { + return `u:${userName}:settings`; + } + + async getUserSettings(userName: string): Promise { + const val = await withRetry(() => + this.client.get(this.userSettingsKey(userName)) + ); + return val ? (val as UserSettings) : null; + } + + async setUserSettings( + userName: string, + settings: UserSettings + ): Promise { + await withRetry(() => + this.client.set(this.userSettingsKey(userName), settings) + ); + } + + async updateUserSettings( + userName: string, + settings: Partial + ): Promise { + const current = await this.getUserSettings(userName); + const defaultSettings: UserSettings = { + filter_adult_content: true, + theme: 'auto', + language: 'zh-CN', + auto_play: false, + video_quality: 'auto' + }; + const updated: UserSettings = { + ...defaultSettings, + ...current, + ...settings, + filter_adult_content: settings.filter_adult_content ?? current?.filter_adult_content ?? true + }; + await this.setUserSettings(userName, updated); + } } // 单例 Upstash Redis 客户端