feat: switch panel database to sqlite

This commit is contained in:
JetSprow
2026-04-30 09:18:05 +10:00
parent 513d821414
commit 153e3954c6
21 changed files with 516 additions and 277 deletions

View File

@@ -2,9 +2,8 @@
APP_PORT="3000"
SITE_NAME="J-Board"
# PostgreSQL for local tools; Docker Compose overrides host to db
POSTGRES_PASSWORD="jboard123"
DATABASE_URL="postgresql://jboard:jboard123@localhost:5432/jboard"
# SQLite for local tools and Docker
DATABASE_URL="file:./storage/jboard.db"
# NextAuth
# Use the public domain you will reverse proxy to this panel, for example https://panel.example.com

View File

@@ -1,9 +1,9 @@
FROM node:20-alpine AS base
RUN apk add --no-cache postgresql-client
# --- deps: install production + dev dependencies ---
FROM base AS deps
WORKDIR /app
RUN apk add --no-cache python3 make g++
COPY package.json package-lock.json ./
RUN npm ci
@@ -32,6 +32,7 @@ ENV NEXT_TELEMETRY_DISABLED=1
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
RUN mkdir -p /app/storage && chown -R nextjs:nodejs /app/storage
# Next.js standalone output
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
@@ -43,7 +44,11 @@ COPY --from=builder /app/prisma ./prisma
COPY --from=builder /app/prisma.config.ts ./prisma.config.ts
COPY --from=builder /app/node_modules/.prisma ./node_modules/.prisma
COPY --from=builder /app/node_modules/@prisma/client ./node_modules/@prisma/client
COPY --from=builder /app/node_modules/@prisma/adapter-pg ./node_modules/@prisma/adapter-pg
COPY --from=builder /app/node_modules/@prisma/adapter-better-sqlite3 ./node_modules/@prisma/adapter-better-sqlite3
COPY --from=builder /app/node_modules/@prisma/driver-adapter-utils ./node_modules/@prisma/driver-adapter-utils
COPY --from=builder /app/node_modules/better-sqlite3 ./node_modules/better-sqlite3
COPY --from=builder /app/node_modules/bindings ./node_modules/bindings
COPY --from=builder /app/node_modules/file-uri-to-path ./node_modules/file-uri-to-path
USER nextjs
EXPOSE 3000

View File

@@ -7,13 +7,13 @@ services:
ports:
- "${APP_PORT:-3000}:3000"
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
volumes:
- sqlite_data:/app/storage
env_file: .env
environment:
- DATABASE_URL=postgresql://jboard:${POSTGRES_PASSWORD:-jboard123}@db:5432/jboard
- DATABASE_URL=file:/app/storage/jboard.db
- REDIS_URL=redis://redis:6379
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:3000/api/public/app-info || exit 1"]
@@ -28,33 +28,14 @@ services:
build:
context: .
target: init
depends_on:
db:
condition: service_healthy
volumes:
- sqlite_data:/app/storage
env_file: .env
environment:
- DATABASE_URL=postgresql://jboard:${POSTGRES_PASSWORD:-jboard123}@db:5432/jboard
- DATABASE_URL=file:/app/storage/jboard.db
profiles:
- setup
db:
image: postgres:16-alpine
restart: unless-stopped
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
POSTGRES_DB: jboard
POSTGRES_USER: jboard
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-jboard123}
# 仅暴露给内部网络,生产环境不要对外开放
# ports:
# - "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U jboard"]
interval: 5s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
restart: unless-stopped
@@ -70,5 +51,5 @@ services:
retries: 5
volumes:
postgres_data:
sqlite_data:
redis_data:

601
package-lock.json generated
View File

@@ -10,9 +10,10 @@
"dependencies": {
"@base-ui/react": "^1.4.1",
"@marsidev/react-turnstile": "^1.5.0",
"@prisma/adapter-pg": "^7.7.0",
"@prisma/adapter-better-sqlite3": "^7.7.0",
"@prisma/client": "^7.7.0",
"bcryptjs": "^3.0.3",
"better-sqlite3": "^12.5.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"date-fns": "^4.1.0",
@@ -23,7 +24,6 @@
"next-auth": "^4.24.14",
"next-themes": "^0.4.6",
"nodemailer": "^7.0.13",
"pg": "^8.20.0",
"qrcode.react": "^4.2.0",
"react": "19.2.4",
"react-dom": "19.2.4",
@@ -38,6 +38,7 @@
},
"devDependencies": {
"@tailwindcss/postcss": "^4",
"@types/better-sqlite3": "^7.6.13",
"@types/node": "^20",
"@types/nodemailer": "^8.0.0",
"@types/react": "^19",
@@ -2487,16 +2488,14 @@
"url": "https://github.com/sponsors/panva"
}
},
"node_modules/@prisma/adapter-pg": {
"node_modules/@prisma/adapter-better-sqlite3": {
"version": "7.7.0",
"resolved": "https://registry.npmjs.org/@prisma/adapter-pg/-/adapter-pg-7.7.0.tgz",
"integrity": "sha512-q33Ta8sKbgzEpAy0lx45tAq//yMv0qcb+8nj+TCA3P4wiAY+OBFEFk/NDkZncAfHaNJeGo5WJpJdpbL+ijYx8g==",
"resolved": "https://registry.npmjs.org/@prisma/adapter-better-sqlite3/-/adapter-better-sqlite3-7.7.0.tgz",
"integrity": "sha512-K6V9AHEKBBC/zo5gXb3PO5A0dYv/fSpCRMnbKY9lpB7B+02MdLSVVP6JtMpLD+NcPDQxKvKACJO7n+zZH4hA5g==",
"license": "Apache-2.0",
"dependencies": {
"@prisma/driver-adapter-utils": "7.7.0",
"@types/pg": "^8.16.0",
"pg": "^8.16.3",
"postgres-array": "3.0.4"
"better-sqlite3": "^12.6.0"
}
},
"node_modules/@prisma/client": {
@@ -3307,6 +3306,16 @@
"tslib": "^2.4.0"
}
},
"node_modules/@types/better-sqlite3": {
"version": "7.6.13",
"resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.13.tgz",
"integrity": "sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/d3-array": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz",
@@ -3437,6 +3446,7 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.39.tgz",
"integrity": "sha512-orrrD74MBUyK8jOAD/r0+lfa1I2MO6I+vAkmAWzMYbCcgrN4lCrmK52gRFQq/JRxfYPfonkr4b0jcY7Olqdqbw==",
"license": "MIT",
"peer": true,
"dependencies": {
"undici-types": "~6.21.0"
}
@@ -3451,17 +3461,6 @@
"@types/node": "*"
}
},
"node_modules/@types/pg": {
"version": "8.20.0",
"resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.20.0.tgz",
"integrity": "sha512-bEPFOaMAHTEP1EzpvHTbmwR8UsFyHSKsRisLIHVMXnpNefSbGA1bD6CVy+qKjGSqmZqNqBDV2azOBo8TgkcVow==",
"license": "MIT",
"dependencies": {
"@types/node": "*",
"pg-protocol": "*",
"pg-types": "^2.2.0"
}
},
"node_modules/@types/react": {
"version": "19.2.14",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz",
@@ -4484,6 +4483,26 @@
"dev": true,
"license": "MIT"
},
"node_modules/base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT"
},
"node_modules/baseline-browser-mapping": {
"version": "2.10.20",
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.20.tgz",
@@ -4512,6 +4531,40 @@
"devOptional": true,
"license": "MIT"
},
"node_modules/better-sqlite3": {
"version": "12.9.0",
"resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-12.9.0.tgz",
"integrity": "sha512-wqUv4Gm3toFpHDQmaKD4QhZm3g1DjUBI0yzS4UBl6lElUmXFYdTQmmEDpAFa5o8FiFiymURypEnfVHzILKaxqQ==",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"bindings": "^1.5.0",
"prebuild-install": "^7.1.1"
},
"engines": {
"node": "20.x || 22.x || 23.x || 24.x || 25.x"
}
},
"node_modules/bindings": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
"license": "MIT",
"dependencies": {
"file-uri-to-path": "1.0.0"
}
},
"node_modules/bl": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
"license": "MIT",
"dependencies": {
"buffer": "^5.5.0",
"inherits": "^2.0.4",
"readable-stream": "^3.4.0"
}
},
"node_modules/body-parser": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz",
@@ -4593,6 +4646,30 @@
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
}
},
"node_modules/buffer": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT",
"dependencies": {
"base64-js": "^1.3.1",
"ieee754": "^1.1.13"
}
},
"node_modules/bundle-name": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz",
@@ -4832,6 +4909,12 @@
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/chownr": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
"license": "ISC"
},
"node_modules/citty": {
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz",
@@ -5404,6 +5487,21 @@
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/decompress-response": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
"integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
"license": "MIT",
"dependencies": {
"mimic-response": "^3.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/dedent": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz",
@@ -5418,6 +5516,15 @@
}
}
},
"node_modules/deep-extend": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
"license": "MIT",
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/deep-is": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
@@ -5565,7 +5672,6 @@
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
"devOptional": true,
"license": "Apache-2.0",
"engines": {
"node": ">=8"
@@ -5698,6 +5804,15 @@
"node": ">= 0.8"
}
},
"node_modules/end-of-stream": {
"version": "1.4.5",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
"integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
"license": "MIT",
"dependencies": {
"once": "^1.4.0"
}
},
"node_modules/enhanced-resolve": {
"version": "5.20.1",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz",
@@ -6481,6 +6596,15 @@
"url": "https://github.com/sindresorhus/execa?sponsor=1"
}
},
"node_modules/expand-template": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
"integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==",
"license": "(MIT OR WTFPL)",
"engines": {
"node": ">=6"
}
},
"node_modules/express": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
@@ -6729,6 +6853,12 @@
"node": ">=16.0.0"
}
},
"node_modules/file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
"license": "MIT"
},
"node_modules/fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
@@ -6863,6 +6993,12 @@
"node": ">= 0.8"
}
},
"node_modules/fs-constants": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
"license": "MIT"
},
"node_modules/fs-extra": {
"version": "11.3.4",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.4.tgz",
@@ -7109,6 +7245,12 @@
"giget": "dist/cli.mjs"
}
},
"node_modules/github-from-package": {
"version": "0.0.0",
"resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
"integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==",
"license": "MIT"
},
"node_modules/glob-parent": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
@@ -7437,6 +7579,26 @@
"url": "https://opencollective.com/express"
}
},
"node_modules/ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "BSD-3-Clause"
},
"node_modules/ignore": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@@ -7488,6 +7650,12 @@
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"license": "ISC"
},
"node_modules/ini": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
"license": "ISC"
},
"node_modules/inline-style-parser": {
"version": "0.2.7",
"resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz",
@@ -9517,6 +9685,18 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/mimic-response": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
"integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
"license": "MIT",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/minimatch": {
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
@@ -9539,6 +9719,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/mkdirp-classic": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
"license": "MIT"
},
"node_modules/mmdb-lib": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/mmdb-lib/-/mmdb-lib-3.0.2.tgz",
@@ -9673,6 +9859,12 @@
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/napi-build-utils": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz",
"integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==",
"license": "MIT"
},
"node_modules/napi-postinstall": {
"version": "0.3.4",
"resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz",
@@ -9828,6 +10020,30 @@
"node": "^10 || ^12 || >=14"
}
},
"node_modules/node-abi": {
"version": "3.89.0",
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.89.0.tgz",
"integrity": "sha512-6u9UwL0HlAl21+agMN3YAMXcKByMqwGx+pq+P76vii5f7hTPtKDp08/H9py6DY+cfDw7kQNTGEj/rly3IgbNQA==",
"license": "MIT",
"dependencies": {
"semver": "^7.3.5"
},
"engines": {
"node": ">=10"
}
},
"node_modules/node-abi/node_modules/semver": {
"version": "7.7.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/node-domexception": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
@@ -10448,105 +10664,6 @@
"devOptional": true,
"license": "MIT"
},
"node_modules/pg": {
"version": "8.20.0",
"resolved": "https://registry.npmjs.org/pg/-/pg-8.20.0.tgz",
"integrity": "sha512-ldhMxz2r8fl/6QkXnBD3CR9/xg694oT6DZQ2s6c/RI28OjtSOpxnPrUCGOBJ46RCUxcWdx3p6kw/xnDHjKvaRA==",
"license": "MIT",
"peer": true,
"dependencies": {
"pg-connection-string": "^2.12.0",
"pg-pool": "^3.13.0",
"pg-protocol": "^1.13.0",
"pg-types": "2.2.0",
"pgpass": "1.0.5"
},
"engines": {
"node": ">= 16.0.0"
},
"optionalDependencies": {
"pg-cloudflare": "^1.3.0"
},
"peerDependencies": {
"pg-native": ">=3.0.1"
},
"peerDependenciesMeta": {
"pg-native": {
"optional": true
}
}
},
"node_modules/pg-cloudflare": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.3.0.tgz",
"integrity": "sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==",
"license": "MIT",
"optional": true
},
"node_modules/pg-connection-string": {
"version": "2.12.0",
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.12.0.tgz",
"integrity": "sha512-U7qg+bpswf3Cs5xLzRqbXbQl85ng0mfSV/J0nnA31MCLgvEaAo7CIhmeyrmJpOr7o+zm0rXK+hNnT5l9RHkCkQ==",
"license": "MIT"
},
"node_modules/pg-int8": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
"license": "ISC",
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/pg-pool": {
"version": "3.13.0",
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.13.0.tgz",
"integrity": "sha512-gB+R+Xud1gLFuRD/QgOIgGOBE2KCQPaPwkzBBGC9oG69pHTkhQeIuejVIk3/cnDyX39av2AxomQiyPT13WKHQA==",
"license": "MIT",
"peerDependencies": {
"pg": ">=8.0"
}
},
"node_modules/pg-protocol": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.13.0.tgz",
"integrity": "sha512-zzdvXfS6v89r6v7OcFCHfHlyG/wvry1ALxZo4LqgUoy7W9xhBDMaqOuMiF3qEV45VqsN6rdlcehHrfDtlCPc8w==",
"license": "MIT"
},
"node_modules/pg-types": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
"integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
"license": "MIT",
"dependencies": {
"pg-int8": "1.0.1",
"postgres-array": "~2.0.0",
"postgres-bytea": "~1.0.0",
"postgres-date": "~1.0.4",
"postgres-interval": "^1.1.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/pg-types/node_modules/postgres-array": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
"integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
"license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/pgpass": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
"integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
"license": "MIT",
"dependencies": {
"split2": "^4.1.0"
}
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@@ -10651,45 +10768,6 @@
"url": "https://github.com/sponsors/porsager"
}
},
"node_modules/postgres-array": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-3.0.4.tgz",
"integrity": "sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ==",
"license": "MIT",
"engines": {
"node": ">=12"
}
},
"node_modules/postgres-bytea": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz",
"integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/postgres-date": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
"integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/postgres-interval": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
"integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
"license": "MIT",
"dependencies": {
"xtend": "^4.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/powershell-utils": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/powershell-utils/-/powershell-utils-0.1.0.tgz",
@@ -10725,6 +10803,33 @@
"preact": ">=10"
}
},
"node_modules/prebuild-install": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz",
"integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==",
"deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.",
"license": "MIT",
"dependencies": {
"detect-libc": "^2.0.0",
"expand-template": "^2.0.3",
"github-from-package": "0.0.0",
"minimist": "^1.2.3",
"mkdirp-classic": "^0.5.3",
"napi-build-utils": "^2.0.0",
"node-abi": "^3.3.0",
"pump": "^3.0.0",
"rc": "^1.2.7",
"simple-get": "^4.0.0",
"tar-fs": "^2.0.0",
"tunnel-agent": "^0.6.0"
},
"bin": {
"prebuild-install": "bin.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/prelude-ls": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@@ -10867,6 +10972,16 @@
"node": ">= 0.10"
}
},
"node_modules/pump": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz",
"integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==",
"license": "MIT",
"dependencies": {
"end-of-stream": "^1.1.0",
"once": "^1.3.1"
}
},
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@@ -10962,6 +11077,30 @@
"node": ">= 0.10"
}
},
"node_modules/rc": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
"license": "(BSD-2-Clause OR MIT OR Apache-2.0)",
"dependencies": {
"deep-extend": "^0.6.0",
"ini": "~1.3.0",
"minimist": "^1.2.0",
"strip-json-comments": "~2.0.1"
},
"bin": {
"rc": "cli.js"
}
},
"node_modules/rc/node_modules/strip-json-comments": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
"integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/rc9": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz",
@@ -11054,6 +11193,20 @@
}
}
},
"node_modules/readable-stream": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"license": "MIT",
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/readdirp": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
@@ -11428,6 +11581,26 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT"
},
"node_modules/safe-push-apply": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz",
@@ -11855,6 +12028,51 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/simple-concat": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT"
},
"node_modules/simple-get": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz",
"integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT",
"dependencies": {
"decompress-response": "^6.0.0",
"once": "^1.3.1",
"simple-concat": "^1.0.0"
}
},
"node_modules/sisteransi": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
@@ -11899,15 +12117,6 @@
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/split2": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
"license": "ISC",
"engines": {
"node": ">= 10.x"
}
},
"node_modules/sqlstring": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz",
@@ -11979,6 +12188,15 @@
"integrity": "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==",
"license": "MIT"
},
"node_modules/string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"license": "MIT",
"dependencies": {
"safe-buffer": "~5.2.0"
}
},
"node_modules/string-width": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
@@ -12305,6 +12523,34 @@
"url": "https://opencollective.com/webpack"
}
},
"node_modules/tar-fs": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz",
"integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==",
"license": "MIT",
"dependencies": {
"chownr": "^1.1.1",
"mkdirp-classic": "^0.5.2",
"pump": "^3.0.0",
"tar-stream": "^2.1.4"
}
},
"node_modules/tar-stream": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
"integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
"license": "MIT",
"dependencies": {
"bl": "^4.0.3",
"end-of-stream": "^1.4.1",
"fs-constants": "^1.0.0",
"inherits": "^2.0.3",
"readable-stream": "^3.1.1"
},
"engines": {
"node": ">=6"
}
},
"node_modules/tiny-invariant": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
@@ -12525,6 +12771,18 @@
"fsevents": "~2.3.3"
}
},
"node_modules/tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
"integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
"license": "Apache-2.0",
"dependencies": {
"safe-buffer": "^5.0.1"
},
"engines": {
"node": "*"
}
},
"node_modules/tw-animate-css": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.4.0.tgz",
@@ -13233,15 +13491,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
"license": "MIT",
"engines": {
"node": ">=0.4"
}
},
"node_modules/y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",

View File

@@ -13,9 +13,10 @@
"dependencies": {
"@base-ui/react": "^1.4.1",
"@marsidev/react-turnstile": "^1.5.0",
"@prisma/adapter-pg": "^7.7.0",
"@prisma/adapter-better-sqlite3": "^7.7.0",
"@prisma/client": "^7.7.0",
"bcryptjs": "^3.0.3",
"better-sqlite3": "^12.5.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"date-fns": "^4.1.0",
@@ -26,7 +27,6 @@
"next-auth": "^4.24.14",
"next-themes": "^0.4.6",
"nodemailer": "^7.0.13",
"pg": "^8.20.0",
"qrcode.react": "^4.2.0",
"react": "19.2.4",
"react-dom": "19.2.4",
@@ -41,6 +41,7 @@
},
"devDependencies": {
"@tailwindcss/postcss": "^4",
"@types/better-sqlite3": "^7.6.13",
"@types/node": "^20",
"@types/nodemailer": "^8.0.0",
"@types/react": "^19",

View File

@@ -3,7 +3,7 @@ generator client {
}
datasource db {
provider = "postgresql"
provider = "sqlite"
}
enum Role {
@@ -247,28 +247,28 @@ model SubscriptionPlan {
totalTrafficGb Int?
allowRenewal Boolean @default(false)
allowTrafficTopup Boolean @default(false)
renewalPrice Decimal? @db.Decimal(10, 2)
renewalPrice Decimal?
renewalPricingMode String @default("FIXED_DURATION")
renewalDurationDays Int?
renewalMinDays Int?
renewalMaxDays Int?
renewalTrafficGb Int?
topupPricingMode String @default("PER_GB")
topupPricePerGb Decimal? @db.Decimal(10, 2)
topupFixedPrice Decimal? @db.Decimal(10, 2)
topupPricePerGb Decimal?
topupFixedPrice Decimal?
minTopupGb Int?
maxTopupGb Int?
streamingServiceId String?
categoryId String?
pricingMode PlanPricingMode @default(TRAFFIC_SLIDER)
fixedTrafficGb Int?
fixedPrice Decimal? @db.Decimal(10, 2)
fixedPrice Decimal?
// STREAMING: fixed price per slot
price Decimal? @db.Decimal(10, 2)
price Decimal?
// PROXY: linked to a node, price per GB, slider purchase
nodeId String?
inboundId String?
pricePerGb Decimal? @db.Decimal(10, 2)
pricePerGb Decimal?
minTrafficGb Int? @default(10)
maxTrafficGb Int? @default(1000)
createdAt DateTime @default(now())
@@ -509,8 +509,8 @@ model OrderItem {
selectedInboundId String?
trafficGb Int?
quantity Int @default(1)
unitAmount Decimal @db.Decimal(10, 2)
amount Decimal @db.Decimal(10, 2)
unitAmount Decimal
amount Decimal
createdAt DateTime @default(now())
order Order @relation(fields: [orderId], references: [id], onDelete: Cascade)
@@ -551,9 +551,9 @@ model Order {
selectedInboundId String?
targetSubscriptionId String?
subscriptionId String? @unique
amount Decimal @db.Decimal(10, 2)
subtotalAmount Decimal @default(0) @db.Decimal(10, 2)
discountAmount Decimal @default(0) @db.Decimal(10, 2)
amount Decimal
subtotalAmount Decimal @default(0)
discountAmount Decimal @default(0)
couponId String?
couponCode String?
promotionName String?
@@ -621,9 +621,9 @@ model Coupon {
name String
description String?
discountType CouponDiscountType @default(AMOUNT_OFF)
discountValue Decimal @db.Decimal(10, 2)
thresholdAmount Decimal? @db.Decimal(10, 2)
maxDiscountAmount Decimal? @db.Decimal(10, 2)
discountValue Decimal
thresholdAmount Decimal?
maxDiscountAmount Decimal?
totalLimit Int?
perUserLimit Int?
isPublic Boolean @default(true)
@@ -660,8 +660,8 @@ model CouponGrant {
model PromotionRule {
id String @id @default(cuid())
name String
thresholdAmount Decimal @db.Decimal(10, 2)
discountAmount Decimal @db.Decimal(10, 2)
thresholdAmount Decimal
discountAmount Decimal
isActive Boolean @default(true)
startsAt DateTime?
endsAt DateTime?
@@ -677,7 +677,7 @@ model InviteRewardLedger {
inviterId String
inviteeId String
orderId String
rewardAmount Decimal @default(0) @db.Decimal(10, 2)
rewardAmount Decimal @default(0)
couponCode String?
status InviteRewardStatus @default(ISSUED)
createdAt DateTime @default(now())
@@ -785,7 +785,7 @@ model AppConfig {
nodeAccessUniqueTargetWarning Int @default(80)
nodeAccessUniqueTargetSuspend Int @default(160)
inviteRewardCouponId String?
inviteRewardRate Decimal @default(0) @db.Decimal(5, 2)
inviteRewardRate Decimal @default(0)
inviteRewardEnabled Boolean @default(false)
turnstileSiteKey String?
turnstileSecretKey String?

View File

@@ -1,9 +1,11 @@
import "dotenv/config";
import { PrismaClient } from "@prisma/client";
import { PrismaPg } from "@prisma/adapter-pg";
import { PrismaBetterSqlite3 } from "@prisma/adapter-better-sqlite3";
import bcrypt from "bcryptjs";
const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL });
const adapter = new PrismaBetterSqlite3({
url: process.env.DATABASE_URL || "file:./storage/jboard.db",
});
const prisma = new PrismaClient({ adapter });
function envValue(key: string, fallback: string) {

View File

@@ -36,8 +36,8 @@ export async function getAnnouncements(
...(q
? {
OR: [
{ title: { contains: q, mode: "insensitive" as const } },
{ body: { contains: q, mode: "insensitive" as const } },
{ title: { contains: q } },
{ body: { contains: q } },
],
}
: {}),

View File

@@ -14,11 +14,11 @@ export async function getAuditLogs(
...(q
? {
OR: [
{ action: { contains: q, mode: "insensitive" as const } },
{ targetType: { contains: q, mode: "insensitive" as const } },
{ targetLabel: { contains: q, mode: "insensitive" as const } },
{ actorEmail: { contains: q, mode: "insensitive" as const } },
{ message: { contains: q, mode: "insensitive" as const } },
{ action: { contains: q } },
{ targetType: { contains: q } },
{ targetLabel: { contains: q } },
{ actorEmail: { contains: q } },
{ message: { contains: q } },
],
}
: {}),

View File

@@ -41,8 +41,8 @@ export async function getNodeServers(
...(q
? {
OR: [
{ name: { contains: q, mode: "insensitive" as const } },
{ panelUrl: { contains: q, mode: "insensitive" as const } },
{ name: { contains: q } },
{ panelUrl: { contains: q } },
],
}
: {}),

View File

@@ -30,10 +30,10 @@ export async function getAdminOrders(
...(q
? {
OR: [
{ user: { email: { contains: q, mode: "insensitive" as const } } },
{ user: { name: { contains: q, mode: "insensitive" as const } } },
{ plan: { name: { contains: q, mode: "insensitive" as const } } },
{ tradeNo: { contains: q, mode: "insensitive" as const } },
{ user: { email: { contains: q } } },
{ user: { name: { contains: q } } },
{ plan: { name: { contains: q } } },
{ tradeNo: { contains: q } },
],
}
: {}),

View File

@@ -36,8 +36,8 @@ export async function getAdminPlans(
...(q
? {
OR: [
{ name: { contains: q, mode: "insensitive" as const } },
{ description: { contains: q, mode: "insensitive" as const } },
{ name: { contains: q } },
{ description: { contains: q } },
],
}
: {}),

View File

@@ -26,8 +26,8 @@ export async function getStreamingServices(
...(q
? {
OR: [
{ name: { contains: q, mode: "insensitive" as const } },
{ description: { contains: q, mode: "insensitive" as const } },
{ name: { contains: q } },
{ description: { contains: q } },
],
}
: {}),

View File

@@ -40,8 +40,8 @@ async function searchRelatedIds(q: string) {
prisma.user.findMany({
where: {
OR: [
{ email: { contains: q, mode: "insensitive" } },
{ name: { contains: q, mode: "insensitive" } },
{ email: { contains: q } },
{ name: { contains: q } },
],
},
select: { id: true },
@@ -51,9 +51,9 @@ async function searchRelatedIds(q: string) {
where: {
OR: [
{ id: q },
{ user: { email: { contains: q, mode: "insensitive" } } },
{ user: { name: { contains: q, mode: "insensitive" } } },
{ plan: { name: { contains: q, mode: "insensitive" } } },
{ user: { email: { contains: q } } },
{ user: { name: { contains: q } } },
{ plan: { name: { contains: q } } },
],
},
select: { id: true },
@@ -87,8 +87,8 @@ export async function getSubscriptionRiskEvents(
{ id: q },
{ userId: q },
{ subscriptionId: q },
{ ip: { contains: q, mode: "insensitive" as const } },
{ message: { contains: q, mode: "insensitive" as const } },
{ ip: { contains: q } },
{ message: { contains: q } },
...(userIds.length > 0 ? [{ userId: { in: userIds } }] : []),
...(subscriptionIds.length > 0 ? [{ subscriptionId: { in: subscriptionIds } }] : []),
],

View File

@@ -40,9 +40,9 @@ export async function getAdminSubscriptions(
...(q
? {
OR: [
{ user: { email: { contains: q, mode: "insensitive" as const } } },
{ user: { name: { contains: q, mode: "insensitive" as const } } },
{ plan: { name: { contains: q, mode: "insensitive" as const } } },
{ user: { email: { contains: q } } },
{ user: { name: { contains: q } } },
{ plan: { name: { contains: q } } },
],
}
: {}),

View File

@@ -49,9 +49,9 @@ export async function getAdminSupportTickets(
...(q
? {
OR: [
{ subject: { contains: q, mode: "insensitive" as const } },
{ category: { contains: q, mode: "insensitive" as const } },
{ user: { email: { contains: q, mode: "insensitive" as const } } },
{ subject: { contains: q } },
{ category: { contains: q } },
{ user: { email: { contains: q } } },
],
}
: {}),

View File

@@ -32,9 +32,9 @@ export async function getAdminTaskRuns(
...(q
? {
OR: [
{ title: { contains: q, mode: "insensitive" as const } },
{ errorMessage: { contains: q, mode: "insensitive" as const } },
{ targetType: { contains: q, mode: "insensitive" as const } },
{ title: { contains: q } },
{ errorMessage: { contains: q } },
{ targetType: { contains: q } },
],
}
: {}),

View File

@@ -44,9 +44,9 @@ export async function getTrafficClients(
...(q
? {
OR: [
{ user: { email: { contains: q, mode: "insensitive" as const } } },
{ inbound: { server: { name: { contains: q, mode: "insensitive" as const } } } },
{ email: { contains: q, mode: "insensitive" as const } },
{ user: { email: { contains: q } } },
{ inbound: { server: { name: { contains: q } } } },
{ email: { contains: q } },
],
}
: {}),

View File

@@ -35,10 +35,10 @@ export async function getAdminUsers(
...(q
? {
OR: [
{ email: { contains: q, mode: "insensitive" as const } },
{ name: { contains: q, mode: "insensitive" as const } },
{ inviteCode: { contains: q, mode: "insensitive" as const } },
{ invitedBy: { email: { contains: q, mode: "insensitive" as const } } },
{ email: { contains: q } },
{ name: { contains: q } },
{ inviteCode: { contains: q } },
{ invitedBy: { email: { contains: q } } },
],
}
: {}),

View File

@@ -14,11 +14,11 @@ export async function GET(req: Request) {
where: q
? {
OR: [
{ action: { contains: q, mode: "insensitive" } },
{ targetType: { contains: q, mode: "insensitive" } },
{ targetLabel: { contains: q, mode: "insensitive" } },
{ actorEmail: { contains: q, mode: "insensitive" } },
{ message: { contains: q, mode: "insensitive" } },
{ action: { contains: q } },
{ targetType: { contains: q } },
{ targetLabel: { contains: q } },
{ actorEmail: { contains: q } },
{ message: { contains: q } },
],
}
: undefined,

View File

@@ -1,10 +1,12 @@
import { PrismaClient, type Prisma } from "@prisma/client";
import { PrismaPg } from "@prisma/adapter-pg";
import { PrismaBetterSqlite3 } from "@prisma/adapter-better-sqlite3";
const globalForPrisma = globalThis as unknown as { prisma: PrismaClient };
function createClient() {
const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL });
const adapter = new PrismaBetterSqlite3({
url: process.env.DATABASE_URL || "file:./storage/jboard.db",
});
return new PrismaClient({ adapter });
}