feat: 引入drizzle、sqlite
torbox-admin / deploy (push) Has been cancelled

This commit is contained in:
2026-10-06 07:35:00 -07:00
parent 2022ebc6b3
commit c0101fb346
12 changed files with 551 additions and 19 deletions
+1
View File
@@ -12,3 +12,4 @@ next-env.d.ts
AGENTS.md
CLAUDE.md
README.md
data
+4
View File
@@ -16,3 +16,7 @@ LOG_LEVEL=
LOG_DIR=
# 日志文件保留天数,默认 14
LOG_RETAIN_DAYS=
# SQLite 数据库文件路径,默认 ./data/torbox-admin.db
# Docker 部署时请挂载该目录,否则容器重建后数据丢失
DB_FILE_NAME=
+7 -5
View File
@@ -14,7 +14,7 @@ env:
IMAGE_NAME: torbox-admin
# 与 central-platform-frontend(22786)同机部署,端口不能相同
HOST_PORT: 22787
# 服务器上的部署目录:.env(密钥与配置)、logs/(日志文件)都在这里,不进镜像也不进仓库
# 服务器上的部署目录:.env(密钥与配置)、logs/(日志文件)、data/(SQLite 数据库)都在这里,不进镜像也不进仓库
DEPLOY_DIR: /app/torbox-admin
DOCKER_BUILDKIT: "0"
@@ -60,9 +60,9 @@ jobs:
exit 1
fi
done
# 容器内以 node 用户(uid 1000)运行,需要能读 .env、写日志目录
mkdir -p /data/logs
chown 1000:1000 /data/logs /data/.env
# 容器内以 node 用户(uid 1000)运行,需要能读 .env、写日志与数据库目录
mkdir -p /data/logs /data/data
chown 1000:1000 /data/logs /data/data /data/.env
chmod 600 /data/.env
'
@@ -89,7 +89,7 @@ jobs:
docker rm $IMAGE_NAME || true
fi
# 配置:.env 只读挂载,由应用启动时加载;日志:写入宿主机目录,容器重建不丢
# 配置:.env 只读挂载,由应用启动时加载;日志与数据库:写入宿主机目录,容器重建不丢
# 控制台输出同时保留(docker logs),限制大小避免无限增长
docker run -d \
--name $IMAGE_NAME \
@@ -98,8 +98,10 @@ jobs:
-e HOSTNAME=127.0.0.1 \
-e TZ=Asia/Shanghai \
-e LOG_DIR=/app/logs \
-e DB_FILE_NAME=/app/data/torbox-admin.db \
-v $DEPLOY_DIR/.env:/app/.env:ro \
-v $DEPLOY_DIR/logs:/app/logs \
-v $DEPLOY_DIR/data:/app/data \
--log-opt max-size=10m \
--log-opt max-file=3 \
--restart unless-stopped \
+3 -1
View File
@@ -48,4 +48,6 @@ next-env.d.ts
AGENTS.md
CLAUDE.md
.vscode
.vscode
# sqlite
/data/
+3
View File
@@ -29,6 +29,9 @@ COPY --from=builder --chown=node:node /app/.next/standalone ./
COPY --from=builder --chown=node:node /app/.next/static ./.next/static
COPY --from=builder --chown=node:node /app/public ./public
# SQLite 数据库目录:生产环境通过 DB_FILE_NAME 指向挂载卷;预先创建并授权,未挂载时也能启动
RUN mkdir -p /app/data && chown node:node /app/data
USER node
EXPOSE 22787
+11
View File
@@ -0,0 +1,11 @@
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "sqlite",
schema: "./src/server/db/schema.ts",
out: "./drizzle",
dbCredentials: {
// 与 src/server/env.ts 的 getDbFileName 保持一致:drizzle-kit 不经过 Next.js,需自行读取
url: process.env.DB_FILE_NAME?.trim() || "./data/torbox-admin.db"
}
});
+12
View File
@@ -2,6 +2,7 @@ import { defineConfig, globalIgnores } from "eslint/config";
import nextVitals from "eslint-config-next/core-web-vitals";
import nextTs from "eslint-config-next/typescript";
import tanstackQuery from "@tanstack/eslint-plugin-query";
import drizzle from "eslint-plugin-drizzle";
const eslintConfig = defineConfig([
...nextVitals,
@@ -16,6 +17,17 @@ const eslintConfig = defineConfig([
}
},
{
// 该插件仅提供传统配置,flat config 下手动注册;限定 db 对象避免误报其他 update/delete 方法
plugins: {
drizzle
},
rules: {
"drizzle/enforce-delete-with-where": ["error", { drizzleObjectName: ["db"] }],
"drizzle/enforce-update-with-where": ["error", { drizzleObjectName: ["db"] }]
}
},
globalIgnores([".next/**", "out/**", "build/**", "next-env.d.ts"])
]);
+8 -2
View File
@@ -6,7 +6,10 @@
"dev": "next dev -p 3200",
"build": "next build",
"start": "next start -p 3200",
"lint": "eslint"
"lint": "eslint",
"db:generate": "drizzle-kit generate",
"db:migrate": "drizzle-kit migrate",
"db:studio": "drizzle-kit studio"
},
"dependencies": {
"@base-ui/react": "^1.8.0",
@@ -18,6 +21,7 @@
"axios": "^1.20.0",
"class-variance-authority": "^0.7.1",
"cn": "^0.3.2",
"drizzle-orm": "1.0.0-rc.4",
"fast-xml-parser": "^5.11.2",
"lucide-react": "^1.47.0",
"motion": "^14.0.0",
@@ -33,11 +37,13 @@
"@tailwindcss/postcss": "^4",
"@tanstack/eslint-plugin-query": "^5.103.2",
"@tanstack/react-query-devtools": "^5.103.2",
"@types/node": "^20",
"@types/node": "^22.20.5",
"@types/react": "^19",
"@types/react-dom": "^19",
"drizzle-kit": "1.0.0-rc.4",
"eslint": "^9",
"eslint-config-next": "16.3.5",
"eslint-plugin-drizzle": "1.0.0-rc.4",
"shadcn": "^4.21.0",
"tailwindcss": "^4",
"typescript": "^5.9.3"
+476 -10
View File
@@ -35,6 +35,9 @@ importers:
cn:
specifier: ^0.3.2
version: 0.3.2
drizzle-orm:
specifier: 1.0.0-rc.4
version: 1.0.0-rc.4(zod@3.25.76)
fast-xml-parser:
specifier: ^5.11.2
version: 5.11.2
@@ -46,7 +49,7 @@ importers:
version: 14.0.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
next:
specifier: 16.3.5
version: 16.3.5(@babel/core@7.29.7)(@types/node@20.19.43)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
version: 16.3.5(@babel/core@7.29.7)(@types/node@22.20.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
pino:
specifier: ^10.4.0
version: 10.4.0
@@ -76,20 +79,26 @@ importers:
specifier: ^5.103.2
version: 5.103.2(@tanstack/react-query@5.103.2(react@19.2.8))(@types/react@19.3.0)(csstype@3.2.3)(react@19.2.8)
'@types/node':
specifier: ^20
version: 20.19.43
specifier: ^22.20.5
version: 22.20.5
'@types/react':
specifier: ^19
version: 19.3.0
'@types/react-dom':
specifier: ^19
version: 19.3.0(@types/react@19.3.0)
drizzle-kit:
specifier: 1.0.0-rc.4
version: 1.0.0-rc.4
eslint:
specifier: ^9
version: 9.39.5(jiti@2.7.0)
eslint-config-next:
specifier: 16.3.5
version: 16.3.5(@typescript-eslint/parser@8.70.1(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3)
eslint-plugin-drizzle:
specifier: 1.0.0-rc.4
version: 1.0.0-rc.4(eslint@9.39.5(jiti@2.7.0))
shadcn:
specifier: ^4.21.0
version: 4.21.0(typescript@5.9.3)
@@ -278,9 +287,168 @@ packages:
'@dotenvx/primitives@0.8.0':
resolution: {integrity: sha512-VYJy0uhFm9zTJ1TxBaW/pA8bjbOM/OttaNMwZ1RHG4JKyRG7DhSdiqD1ipQoAyoD22olUtxbP78W9xY3Wd11bg==}
'@drizzle-team/brocli@0.12.1':
resolution: {integrity: sha512-yHwomvolVafvUXhy5TdiJVkuNCxXn+bKOs2aWiWEQh/5YaV97Oa/abJz3dmx7lxgIUsUlS1Jl4mEfvJPo3YsVw==}
'@emnapi/runtime@1.11.3':
resolution: {integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==}
'@esbuild/aix-ppc64@0.25.12':
resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [aix]
'@esbuild/android-arm64@0.25.12':
resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [android]
'@esbuild/android-arm@0.25.12':
resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==}
engines: {node: '>=18'}
cpu: [arm]
os: [android]
'@esbuild/android-x64@0.25.12':
resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==}
engines: {node: '>=18'}
cpu: [x64]
os: [android]
'@esbuild/darwin-arm64@0.25.12':
resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [darwin]
'@esbuild/darwin-x64@0.25.12':
resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==}
engines: {node: '>=18'}
cpu: [x64]
os: [darwin]
'@esbuild/freebsd-arm64@0.25.12':
resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [freebsd]
'@esbuild/freebsd-x64@0.25.12':
resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [freebsd]
'@esbuild/linux-arm64@0.25.12':
resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==}
engines: {node: '>=18'}
cpu: [arm64]
os: [linux]
'@esbuild/linux-arm@0.25.12':
resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==}
engines: {node: '>=18'}
cpu: [arm]
os: [linux]
'@esbuild/linux-ia32@0.25.12':
resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==}
engines: {node: '>=18'}
cpu: [ia32]
os: [linux]
'@esbuild/linux-loong64@0.25.12':
resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==}
engines: {node: '>=18'}
cpu: [loong64]
os: [linux]
'@esbuild/linux-mips64el@0.25.12':
resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==}
engines: {node: '>=18'}
cpu: [mips64el]
os: [linux]
'@esbuild/linux-ppc64@0.25.12':
resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [linux]
'@esbuild/linux-riscv64@0.25.12':
resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==}
engines: {node: '>=18'}
cpu: [riscv64]
os: [linux]
'@esbuild/linux-s390x@0.25.12':
resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==}
engines: {node: '>=18'}
cpu: [s390x]
os: [linux]
'@esbuild/linux-x64@0.25.12':
resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==}
engines: {node: '>=18'}
cpu: [x64]
os: [linux]
'@esbuild/netbsd-arm64@0.25.12':
resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [netbsd]
'@esbuild/netbsd-x64@0.25.12':
resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [netbsd]
'@esbuild/openbsd-arm64@0.25.12':
resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openbsd]
'@esbuild/openbsd-x64@0.25.12':
resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==}
engines: {node: '>=18'}
cpu: [x64]
os: [openbsd]
'@esbuild/openharmony-arm64@0.25.12':
resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openharmony]
'@esbuild/sunos-x64@0.25.12':
resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==}
engines: {node: '>=18'}
cpu: [x64]
os: [sunos]
'@esbuild/win32-arm64@0.25.12':
resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [win32]
'@esbuild/win32-ia32@0.25.12':
resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==}
engines: {node: '>=18'}
cpu: [ia32]
os: [win32]
'@esbuild/win32-x64@0.25.12':
resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==}
engines: {node: '>=18'}
cpu: [x64]
os: [win32]
'@eslint-community/eslint-utils@4.10.1':
resolution: {integrity: sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -559,6 +727,10 @@ packages:
'@jridgewell/trace-mapping@0.3.31':
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
'@js-temporal/polyfill@0.5.1':
resolution: {integrity: sha512-hloP58zRVCRSpgDxmqCWJNlizAlUgJFqG2ypq79DCvyv9tHjRYMDOcPFjzfl/A1/YxDvRCZz8wvZvmapQnKwFQ==}
engines: {node: '>=12'}
'@kobalte/core@0.13.14':
resolution: {integrity: sha512-SBVB1lnvYbYt2x3t7fG0lo5fGLEbHQ6PII3NDPva8vD5e2v53On84ZVQiuIk0XI/55R4GQhden7Yem+R/XR8uQ==}
peerDependencies:
@@ -885,8 +1057,8 @@ packages:
'@types/json5@0.0.29':
resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
'@types/node@20.19.43':
resolution: {integrity: sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==}
'@types/node@22.20.5':
resolution: {integrity: sha512-U2+DNr+wSjpsTS/wZGYHq7GcwfuSmKiKvoPvK22zwTlRhU91yOniN4qRR5KhIjvif7ysw/dz/hKmfDH0Ris4aA==}
'@types/react-dom@19.3.0':
resolution: {integrity: sha512-ZI7bU42mZXXKHn/qNLEw2IrbiINU7X5+vfgdixBHkCNpYWXjKgfQ/P+uyGb5CjOLB9UcnTeg3rylQtV2hym44Q==}
@@ -1465,6 +1637,156 @@ packages:
resolution: {integrity: sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==}
engines: {node: '>=12'}
drizzle-kit@1.0.0-rc.4:
resolution: {integrity: sha512-KZCpjRyu+oYHLj/UJogfFlOkWhHVkaEI2EOT1U3NDVXUzLoTyPjqwFxwOrlQxsY6jzRyxcz4EacqboGfhEeYrA==}
hasBin: true
drizzle-orm@1.0.0-rc.4:
resolution: {integrity: sha512-BT+pf+qoiYHqltoA88Jmf6ilGMXPlpfE0hEJKc2adRtMCAl25Swk/t5gXcWxZNAwdtf3F5gCd2FpeOyP/pT0Hw==}
peerDependencies:
'@aws-sdk/client-rds-data': '>=3'
'@cloudflare/workers-types': '>=4'
'@effect/sql-d1': '>=4.0.0-beta.83 || >=4.0.0'
'@effect/sql-libsql': '>=4.0.0-beta.83 || >=4.0.0'
'@effect/sql-mysql2': '>=4.0.0-beta.83 || >=4.0.0'
'@effect/sql-pg': '>=4.0.0-beta.83 || >=4.0.0'
'@effect/sql-pglite': '>=4.0.0-beta.83 || >=4.0.0'
'@effect/sql-sqlite-bun': '>=4.0.0-beta.83 || >=4.0.0'
'@effect/sql-sqlite-do': '>=4.0.0-beta.83 || >=4.0.0'
'@effect/sql-sqlite-node': '>=4.0.0-beta.83 || >=4.0.0'
'@effect/sql-sqlite-wasm': '>=4.0.0-beta.83 || >=4.0.0'
'@electric-sql/pglite': '>=0.2.0'
'@libsql/client': '>=0.10.0'
'@libsql/client-wasm': '>=0.10.0'
'@neondatabase/serverless': '>=0.10.0'
'@op-engineering/op-sqlite': '>=2'
'@opentelemetry/api': ^1.4.1
'@planetscale/database': '>=1.13'
'@sinclair/typebox': '>=0.34.8'
'@sqlitecloud/drivers': '>=1.0.653'
'@tidbcloud/serverless': '*'
'@tursodatabase/database': '>=0.6.0-pre.28 || >=0.6.0'
'@tursodatabase/database-common': '>=0.6.0-pre.28 || >=0.6.0'
'@tursodatabase/database-wasm': '>=0.6.0-pre.28 || >=0.6.0'
'@tursodatabase/serverless': '>=1.1.3'
'@tursodatabase/sync': '>=0.6.0-pre.28 || >=0.6.0'
'@types/better-sqlite3': '*'
'@types/mssql': ^9.1.4
'@types/pg': '*'
'@types/sql.js': '*'
'@upstash/redis': '>=1.34.7'
'@vercel/postgres': '>=0.8.0'
'@xata.io/client': '*'
arktype: '>=2.0.0'
better-sqlite3: '>=9.3.0'
bun-types: '*'
effect: '>=4.0.0-beta.83 || >=4.0.0'
expo-sqlite: '>=14.0.0'
mssql: ^11.0.1
mysql2: '>=2'
pg: '>=8'
postgres: '>=3'
sql.js: '>=1'
sqlite3: '>=5'
typebox: '>=1.0.0'
valibot: '>=1.0.0-beta.7'
zod: ^3.25.0 || ^4.0.0
peerDependenciesMeta:
'@aws-sdk/client-rds-data':
optional: true
'@cloudflare/workers-types':
optional: true
'@effect/sql-d1':
optional: true
'@effect/sql-libsql':
optional: true
'@effect/sql-mysql2':
optional: true
'@effect/sql-pg':
optional: true
'@effect/sql-pglite':
optional: true
'@effect/sql-sqlite-bun':
optional: true
'@effect/sql-sqlite-do':
optional: true
'@effect/sql-sqlite-node':
optional: true
'@effect/sql-sqlite-wasm':
optional: true
'@electric-sql/pglite':
optional: true
'@libsql/client':
optional: true
'@libsql/client-wasm':
optional: true
'@neondatabase/serverless':
optional: true
'@op-engineering/op-sqlite':
optional: true
'@opentelemetry/api':
optional: true
'@planetscale/database':
optional: true
'@sinclair/typebox':
optional: true
'@sqlitecloud/drivers':
optional: true
'@tidbcloud/serverless':
optional: true
'@tursodatabase/database':
optional: true
'@tursodatabase/database-common':
optional: true
'@tursodatabase/database-wasm':
optional: true
'@tursodatabase/serverless':
optional: true
'@tursodatabase/sync':
optional: true
'@types/better-sqlite3':
optional: true
'@types/mssql':
optional: true
'@types/pg':
optional: true
'@types/sql.js':
optional: true
'@upstash/redis':
optional: true
'@vercel/postgres':
optional: true
'@xata.io/client':
optional: true
arktype:
optional: true
better-sqlite3:
optional: true
bun-types:
optional: true
effect:
optional: true
expo-sqlite:
optional: true
mssql:
optional: true
mysql2:
optional: true
pg:
optional: true
postgres:
optional: true
sql.js:
optional: true
sqlite3:
optional: true
typebox:
optional: true
valibot:
optional: true
zod:
optional: true
dunder-proto@1.0.1:
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
engines: {node: '>= 0.4'}
@@ -1542,6 +1864,11 @@ packages:
resolution: {integrity: sha512-yPDz7wqpg1/mmHLmS3tcfTfbw5f1eryXvyghYBffGdERwe+mV7ZcWzTR8LR17Kvqt3qfPurjlonmnq3MKXIOXw==}
engines: {node: '>= 0.4'}
esbuild@0.25.12:
resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==}
engines: {node: '>=18'}
hasBin: true
escalade@3.2.0:
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
engines: {node: '>=6'}
@@ -1593,6 +1920,11 @@ packages:
eslint-import-resolver-webpack:
optional: true
eslint-plugin-drizzle@1.0.0-rc.4:
resolution: {integrity: sha512-bXxOn9HBWI3Ico5zFshPHMyN6DZUA9LOvFAgugIges2rLLIX3t470L9adHxiyqtkymnELr7YWhCXaeA8F4s/CA==}
peerDependencies:
eslint: '>=8.0.0'
eslint-plugin-import@2.32.0:
resolution: {integrity: sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==}
engines: {node: '>=4'}
@@ -2230,6 +2562,9 @@ packages:
resolution: {integrity: sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==}
hasBin: true
jsbi@4.3.2:
resolution: {integrity: sha512-9fqMSQbhJykSeii05nxKl4m6Eqn2P6rOlYiS+C5Dr/HPIU/7yZxu5qzbs40tgaFORiw2Amd0mirjxatXYMkIew==}
jsesc@3.1.0:
resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
engines: {node: '>=6'}
@@ -3636,11 +3971,91 @@ snapshots:
'@dotenvx/primitives@0.8.0': {}
'@drizzle-team/brocli@0.12.1': {}
'@emnapi/runtime@1.11.3':
dependencies:
tslib: 2.8.1
optional: true
'@esbuild/aix-ppc64@0.25.12':
optional: true
'@esbuild/android-arm64@0.25.12':
optional: true
'@esbuild/android-arm@0.25.12':
optional: true
'@esbuild/android-x64@0.25.12':
optional: true
'@esbuild/darwin-arm64@0.25.12':
optional: true
'@esbuild/darwin-x64@0.25.12':
optional: true
'@esbuild/freebsd-arm64@0.25.12':
optional: true
'@esbuild/freebsd-x64@0.25.12':
optional: true
'@esbuild/linux-arm64@0.25.12':
optional: true
'@esbuild/linux-arm@0.25.12':
optional: true
'@esbuild/linux-ia32@0.25.12':
optional: true
'@esbuild/linux-loong64@0.25.12':
optional: true
'@esbuild/linux-mips64el@0.25.12':
optional: true
'@esbuild/linux-ppc64@0.25.12':
optional: true
'@esbuild/linux-riscv64@0.25.12':
optional: true
'@esbuild/linux-s390x@0.25.12':
optional: true
'@esbuild/linux-x64@0.25.12':
optional: true
'@esbuild/netbsd-arm64@0.25.12':
optional: true
'@esbuild/netbsd-x64@0.25.12':
optional: true
'@esbuild/openbsd-arm64@0.25.12':
optional: true
'@esbuild/openbsd-x64@0.25.12':
optional: true
'@esbuild/openharmony-arm64@0.25.12':
optional: true
'@esbuild/sunos-x64@0.25.12':
optional: true
'@esbuild/win32-arm64@0.25.12':
optional: true
'@esbuild/win32-ia32@0.25.12':
optional: true
'@esbuild/win32-x64@0.25.12':
optional: true
'@eslint-community/eslint-utils@4.10.1(eslint@9.39.5(jiti@2.7.0))':
dependencies:
eslint: 9.39.5(jiti@2.7.0)
@@ -3876,6 +4291,10 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.6.0
'@js-temporal/polyfill@0.5.1':
dependencies:
jsbi: 4.3.2
'@kobalte/core@0.13.14(solid-js@1.9.15)':
dependencies:
'@floating-ui/dom': 1.8.0
@@ -4193,7 +4612,7 @@ snapshots:
'@types/json5@0.0.29': {}
'@types/node@20.19.43':
'@types/node@22.20.5':
dependencies:
undici-types: 6.21.0
@@ -4763,6 +5182,18 @@ snapshots:
dotenv@17.4.2: {}
drizzle-kit@1.0.0-rc.4:
dependencies:
'@drizzle-team/brocli': 0.12.1
'@js-temporal/polyfill': 0.5.1
esbuild: 0.25.12
get-tsconfig: 4.14.3
jiti: 2.7.0
drizzle-orm@1.0.0-rc.4(zod@3.25.76):
optionalDependencies:
zod: 3.25.76
dunder-proto@1.0.1:
dependencies:
call-bind-apply-helpers: 1.0.2
@@ -4910,6 +5341,35 @@ snapshots:
is-date-object: 1.1.0
is-symbol: 1.1.1
esbuild@0.25.12:
optionalDependencies:
'@esbuild/aix-ppc64': 0.25.12
'@esbuild/android-arm': 0.25.12
'@esbuild/android-arm64': 0.25.12
'@esbuild/android-x64': 0.25.12
'@esbuild/darwin-arm64': 0.25.12
'@esbuild/darwin-x64': 0.25.12
'@esbuild/freebsd-arm64': 0.25.12
'@esbuild/freebsd-x64': 0.25.12
'@esbuild/linux-arm': 0.25.12
'@esbuild/linux-arm64': 0.25.12
'@esbuild/linux-ia32': 0.25.12
'@esbuild/linux-loong64': 0.25.12
'@esbuild/linux-mips64el': 0.25.12
'@esbuild/linux-ppc64': 0.25.12
'@esbuild/linux-riscv64': 0.25.12
'@esbuild/linux-s390x': 0.25.12
'@esbuild/linux-x64': 0.25.12
'@esbuild/netbsd-arm64': 0.25.12
'@esbuild/netbsd-x64': 0.25.12
'@esbuild/openbsd-arm64': 0.25.12
'@esbuild/openbsd-x64': 0.25.12
'@esbuild/openharmony-arm64': 0.25.12
'@esbuild/sunos-x64': 0.25.12
'@esbuild/win32-arm64': 0.25.12
'@esbuild/win32-ia32': 0.25.12
'@esbuild/win32-x64': 0.25.12
escalade@3.2.0: {}
escape-html@1.0.3: {}
@@ -4968,6 +5428,10 @@ snapshots:
transitivePeerDependencies:
- supports-color
eslint-plugin-drizzle@1.0.0-rc.4(eslint@9.39.5(jiti@2.7.0)):
dependencies:
eslint: 9.39.5(jiti@2.7.0)
eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.70.1(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3))(eslint-import-resolver-typescript@3.5.2)(eslint@9.39.5(jiti@2.7.0)):
dependencies:
'@rtsao/scc': 1.1.0
@@ -5702,6 +6166,8 @@ snapshots:
dependencies:
argparse: 2.0.1
jsbi@4.3.2: {}
jsesc@3.1.0: {}
json-buffer@3.0.1: {}
@@ -5914,7 +6380,7 @@ snapshots:
dependencies:
content-type: 2.1.0
next@16.3.5(@babel/core@7.29.7)(@types/node@20.19.43)(react-dom@19.2.8(react@19.2.8))(react@19.2.8):
next@16.3.5(@babel/core@7.29.7)(@types/node@22.20.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8):
dependencies:
'@next/env': 16.3.5
'@swc/helpers': 0.5.23
@@ -5933,7 +6399,7 @@ snapshots:
'@next/swc-linux-x64-musl': 16.3.5
'@next/swc-win32-arm64-msvc': 16.3.5
'@next/swc-win32-x64-msvc': 16.3.5
sharp: 0.35.4(@types/node@20.19.43)
sharp: 0.35.4(@types/node@22.20.5)
transitivePeerDependencies:
- '@babel/core'
- '@types/node'
@@ -6441,7 +6907,7 @@ snapshots:
- supports-color
- typescript
sharp@0.35.4(@types/node@20.19.43):
sharp@0.35.4(@types/node@22.20.5):
dependencies:
'@img/colour': 1.1.0
detect-libc: 2.1.2
@@ -6472,7 +6938,7 @@ snapshots:
'@img/sharp-win32-arm64': 0.35.4
'@img/sharp-win32-ia32': 0.35.4
'@img/sharp-win32-x64': 0.35.4
'@types/node': 20.19.43
'@types/node': 22.20.5
optional: true
shebang-command@2.0.0:
+16
View File
@@ -0,0 +1,16 @@
// SQLite 连接(Node 内置 node:sqlite,无需原生依赖)
import { mkdirSync } from "node:fs";
import { dirname } from "node:path";
import { drizzle } from "drizzle-orm/node-sqlite";
import { getDbFileName } from "@/server/env";
const createDb = () => {
const file = getDbFileName();
mkdirSync(dirname(file), { recursive: true });
return drizzle({ connection: { path: file } });
};
// 开发模式热更新会重复执行模块,缓存到 globalThis 避免重复打开数据库
const globalForDb = globalThis as unknown as { db?: ReturnType<typeof createDb> };
export const db = (globalForDb.db ??= createDb());
+4
View File
@@ -0,0 +1,4 @@
// 数据库表结构(drizzle-kit 以此生成迁移)
// 使用 `import { sqliteTable, integer, text } from "drizzle-orm/sqlite-core"` 定义表后,
// 运行 `pnpm db:generate` 生成迁移,`pnpm db:migrate` 应用
export {};
+6 -1
View File
@@ -8,7 +8,8 @@ type ServerEnvName =
| "JACKETT_API_KEY"
| "LOG_LEVEL"
| "LOG_DIR"
| "LOG_RETAIN_DAYS";
| "LOG_RETAIN_DAYS"
| "DB_FILE_NAME";
/** 读取环境变量,空白视为未配置 */
const readEnv = (name: ServerEnvName) => {
@@ -83,6 +84,10 @@ export const getLogRetainDays = () => {
return Number.isInteger(days) && days > 0 ? days : 14;
};
/** SQLite 数据库文件路径(DB_FILE_NAME),默认 ./data/torbox-admin.db;需与 drizzle.config.ts 保持一致 */
export const getDbFileName = () =>
readEnv("DB_FILE_NAME")?.trim() || "./data/torbox-admin.db";
/** 实际配置的密钥值,供日志脱敏使用;演示模式的固定密钥不是机密,不在其中 */
export const getSecretValues = () =>
[