pref: change the database type to SQLite

This commit is contained in:
LIlGG
2025-09-25 15:24:13 +08:00
parent 1f4fb103e9
commit 530dfac665
16 changed files with 513 additions and 311 deletions

View File

@@ -149,10 +149,3 @@ OPERATING_ENV=production
STORAGE_DIR=/public/uploads
# 附件上传的最大大小
MAX_UPLOAD_SIZE_MB=5
POSTGRES_PASSWORD=p0stgr3s
# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings
DATABASE_URL="postgresql://upage:${POSTGRES_PASSWORD}@localhost:5433/upage?schema=upage_schema"

2
.gitignore vendored
View File

@@ -52,3 +52,5 @@ mock
# storage
/public/uploads
/data/

View File

@@ -1,78 +1,53 @@
FROM node:20.18.0-alpine AS builder
# ---- build stage ----
FROM node:20.18.0-alpine AS build
WORKDIR /app
RUN apk add --no-cache python3 make g++ bash
COPY package.json pnpm-lock.yaml ./
ENV HUSKY=0
RUN npm install -g pnpm && pnpm install
# Use pnpm
RUN corepack enable && corepack prepare pnpm@9.4.0 --activate
# Install deps efficiently
COPY package.json pnpm-lock.yaml* ./
RUN pnpm fetch
# Copy source and build
COPY . .
# install with dev deps (needed to build)
RUN pnpm install --offline --frozen-lockfile
RUN NODE_OPTIONS="--max-old-space-size=4096" pnpm run build
FROM node:20.18.0-alpine AS deps
WORKDIR /app
RUN apk add --no-cache python3 make g++ bash
COPY package.json pnpm-lock.yaml ./
ENV HUSKY=0
RUN npm install -g pnpm && pnpm install --prod
COPY prisma ./prisma
# Generate prisma client
RUN pnpm prisma generate
FROM node:20.18.0-alpine AS upage-ai-production
# Build the Remix app (SSR + client)
RUN NODE_OPTIONS=--max-old-space-size=4096 pnpm run build
# Keep only production deps for runtime
RUN pnpm prune --prod --ignore-scripts
# ---- runtime stage ----
FROM node:20.18.0-alpine AS runtime
WORKDIR /app
RUN apk add --no-cache bash
ENV NODE_ENV=production
ENV LOGTO_ENABLE_DEV=false
ENV PORT=3000
ENV HOST=0.0.0.0
RUN npm install -g pnpm
# Use pnpm
RUN corepack enable && corepack prepare pnpm@9.4.0 --activate
ARG LOG_LEVEL=debug
ARG PORT=3000
ARG LLM_DEFAULT_PROVIDER
ARG LLM_DEFAULT_MODEL
ARG LLM_ENABLED_PROVIDERS
ARG DEFAULT_NUM_CTX
ARG OLLAMA_API_BASE_URL
ARG TOGETHER_API_BASE_URL
ARG LOGTO_ENDPOINT
ARG LOGTO_APP_ID
ARG LOGTO_BASE_URL
# Install bash
RUN apk add --no-cache bash \
&& rm -rf /var/lib/apt/lists/*
ENV NODE_ENV=production \
PORT=${PORT} \
LOG_LEVEL=${LOG_LEVEL} \
DEFAULT_NUM_CTX=${DEFAULT_NUM_CTX} \
LLM_DEFAULT_PROVIDER=${LLM_DEFAULT_PROVIDER} \
LLM_DEFAULT_MODEL=${LLM_DEFAULT_MODEL} \
LLM_ENABLED_PROVIDERS=${LLM_ENABLED_PROVIDERS} \
OLLAMA_API_BASE_URL=${OLLAMA_API_BASE_URL} \
TOGETHER_API_BASE_URL=${TOGETHER_API_BASE_URL} \
LOGTO_ENDPOINT=${LOGTO_ENDPOINT} \
LOGTO_APP_ID=${LOGTO_APP_ID} \
LOGTO_BASE_URL=${LOGTO_BASE_URL} \
RUNNING_IN_DOCKER=true \
STORAGE_DIR=/app/storage
COPY --from=builder /app/build ./build
COPY --from=builder /app/public ./public
COPY --from=builder /app/server.mjs ./
COPY --from=deps /app/node_modules ./node_modules
COPY --from=deps /app/prisma ./prisma
COPY package.json ./
# Copy only what we need to run
COPY --from=build /app/build /app/build
COPY --from=build /app/node_modules /app/node_modules
COPY --from=build /app/public ./public
COPY --from=build /app/package.json /app/package.json
COPY --from=build /app/server.mjs ./
COPY --from=build /app/prisma ./prisma
COPY docker-entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-entrypoint.sh

View File

@@ -9,7 +9,7 @@
------------------------------
UPage 是一款基于人工智能的可视化网页构建平台,支持多种 AI 提供商集成,快速实现定制化网页。
UPage 是一款基于人工智能的可视化网页构建平台,支持多种 AI 提供商集成,基于自然语言快速实现定制化网页。
------------------------------
@@ -30,7 +30,8 @@ docker run -d \
-e OPENAI_LIKE_API_KEY=your-openai-like-api-key \
-e LLM_DEFAULT_MODEL=your-default-model \
-e LLM_MINOR_MODEL=your-minor-model \
-v ./data:/app/data \
-v ./logs:/app/logs \
-v ./storage:/app/storage \
ghcr.io/halo-dev/upage:latest
ghcr.io/halo-dev/upage
```

View File

@@ -80,7 +80,7 @@ ${Object.entries(context)
return _streamText({
model,
tools,
tools: tools(),
system: systemPrompt,
maxOutputTokens: maxTokens || MAX_TOKENS,
messages: convertToModelMessages(messages),

View File

@@ -1,9 +1,17 @@
import type { Tool, ToolSet } from 'ai';
import { serperTool } from './serper';
import { weatherTool } from './weather';
export const tools = {
serper: serperTool,
weather: weatherTool,
};
export const tools: () => ToolSet = () => {
const tools: Record<string, Tool> = {};
export { serperTool, weatherTool };
if (process.env.SERPER_API_KEY) {
tools.serper = serperTool;
}
if (process.env.WEATHER_API_KEY) {
tools.weather = weatherTool;
}
return tools;
};

View File

@@ -1,3 +1,4 @@
import { PrismaBetterSQLite3 } from '@prisma/adapter-better-sqlite3';
import { PrismaClient } from '@prisma/client';
// 创建PrismaClient实例
@@ -7,13 +8,18 @@ declare global {
var __db: PrismaClient | undefined;
}
const adapter = new PrismaBetterSQLite3({
url: 'file:data/upage.db',
});
// 在开发环境中使用全局变量,避免热重载时创建多个实例
if (process.env.NODE_ENV === 'production') {
prisma = new PrismaClient();
prisma = new PrismaClient({ adapter });
} else {
if (!global.__db) {
global.__db = new PrismaClient({
log: ['query', 'error', 'warn'],
adapter,
});
}
prisma = global.__db;

View File

@@ -1,16 +1,13 @@
version: "3.9"
services:
upage:
depends_on:
postgres:
condition: service_healthy
image: upage-ai:dev
image: upage-ai:production
restart: unless-stopped
ports:
- "${PORT:-3000}:3000"
environment:
- OPERATING_ENV=${OPERATING_ENV:-production}
- NODE_ENV=${NODE_ENV:-production}
- DATABASE_URL=postgres://upage:${POSTGRES_PASSWORD}@postgres:5432/upage?schema=upage_schema
- GROQ_API_KEY=${GROQ_API_KEY}
- HuggingFace_API_KEY=${HuggingFace_API_KEY}
- OPENAI_API_KEY=${OPENAI_API_KEY}
@@ -48,23 +45,6 @@ services:
- USAGE_LOG_FILE=true
- STORAGE_DIR=/app/storage
- MAX_UPLOAD_SIZE_MB=${MAX_UPLOAD_SIZE_MB:-5}
networks:
upage_network:
postgres:
image: postgres:17-alpine
environment:
POSTGRES_USER: upage
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
healthcheck:
test: ["CMD-SHELL", "pg_isready"]
interval: 10s
timeout: 5s
retries: 5
networks:
upage_network:
networks:
upage_network:
volumes:
upage-db:

View File

@@ -1,17 +1,13 @@
version: "3.9"
services:
upage:
depends_on:
postgres:
condition: service_healthy
image: ghcr.io/halo-dev/upage:latest
image: upage-ai:production
restart: unless-stopped
ports:
- "${PORT:-3000}:3000"
environment:
- OPERATING_ENV=${OPERATING_ENV:-production}
- NODE_ENV=${NODE_ENV:-production}
- DATABASE_URL=postgres://upage:${POSTGRES_PASSWORD}@postgres:5432/upage?schema=upage_schema
- GROQ_API_KEY=${GROQ_API_KEY}
- HuggingFace_API_KEY=${HuggingFace_API_KEY}
- OPENAI_API_KEY=${OPENAI_API_KEY}
@@ -49,28 +45,9 @@ services:
- USAGE_LOG_FILE=${USAGE_LOG_FILE:-true}
- MAX_UPLOAD_SIZE_MB=${MAX_UPLOAD_SIZE_MB:-5}
volumes:
- ./data:/app/data
- ./logs:/app/logs
- ./storage:/app/storage
networks:
upage_network:
postgres:
image: postgres:17-alpine
user: postgres
environment:
POSTGRES_USER: upage
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
healthcheck:
test: ["CMD-SHELL", "pg_isready"]
interval: 10s
timeout: 5s
retries: 5
volumes:
- upage-db:/var/lib/postgresql/data
networks:
upage_network:
networks:
upage_network:
volumes:
upage-db:

View File

@@ -1,131 +1,260 @@
#!/bin/bash
set -e
set -eo pipefail
echo "🚀 启动 UPage 应用..."
SCRIPT_NAME=$(basename "$0")
START_TIME=$(date +%s)
MAX_DB_RETRIES=10
DB_RETRY_INTERVAL=5
TEMP_FILES=()
print_db_info() {
if [ -z "$DATABASE_URL" ]; then
echo "⚠️ 警告DATABASE_URL 环境变量未设置!"
return
cleanup() {
log_info "正在清理资源..."
for temp_file in "${TEMP_FILES[@]}"; do
if [[ -f "$temp_file" ]]; then
rm -f "$temp_file"
log_debug "已删除临时文件: $temp_file"
fi
done
DB_USER=$(echo $DATABASE_URL | sed -n 's/.*:\/\/\([^:]*\):.*/\1/p')
DB_HOST=$(echo $DATABASE_URL | sed -n 's/.*@\([^:]*\):.*/\1/p')
DB_PORT=$(echo $DATABASE_URL | sed -n 's/.*:\([0-9]*\)\/.*/\1/p')
DB_NAME=$(echo $DATABASE_URL | sed -n 's/.*\/\([^?]*\).*/\1/p')
DB_SCHEMA=$(echo $DATABASE_URL | sed -n 's/.*schema=\([^&]*\).*/\1/p')
echo "📊 数据库连接信息:"
echo " - 用户: $DB_USER"
echo " - 主机: $DB_HOST"
echo " - 端口: $DB_PORT"
echo " - 数据库: $DB_NAME"
echo " - Schema: $DB_SCHEMA"
log_info "清理完成"
}
echo "🖥️ 系统环境信息:"
echo " - NODE_ENV: $NODE_ENV"
echo " - 当前用户: $(whoami)"
echo " - 工作目录: $(pwd)"
handle_exit() {
log_info "接收到退出信号,正在退出..."
cleanup
exit 0
}
print_db_info
trap handle_exit SIGTERM SIGINT
trap cleanup EXIT
echo "⏳ 等待数据库连接..."
MAX_RETRIES=5
RETRY_COUNT=0
LOG_LEVEL_DEBUG=0
LOG_LEVEL_INFO=1
LOG_LEVEL_WARN=2
LOG_LEVEL_ERROR=3
CURRENT_LOG_LEVEL=${LOG_LEVEL_INFO}
ERROR_LOG=$(mktemp)
until pnpm prisma db push --accept-data-loss 2>$ERROR_LOG || pnpm prisma migrate deploy 2>$ERROR_LOG; do
RETRY_COUNT=$((RETRY_COUNT + 1))
echo "❌ 数据库连接尝试 $RETRY_COUNT 失败,错误信息:"
cat $ERROR_LOG
if [ $RETRY_COUNT -ge $MAX_RETRIES ]; then
echo "❌ 数据库连接失败!已达到最大重试次数。"
echo "🔍 最后一次错误详情:"
cat $ERROR_LOG
echo "🔍 尝试使用 pg_isready 检查数据库连接:"
if command -v pg_isready >/dev/null 2>&1; then
pg_isready -h $DB_HOST -p $DB_PORT -U $DB_USER && echo "✅ 数据库可以连接" || echo "❌ 数据库无法连接"
else
echo "⚠️ pg_isready 命令不可用,无法进行连接测试"
if [[ "$LOG_LEVEL" == "debug" ]]; then
CURRENT_LOG_LEVEL=${LOG_LEVEL_DEBUG}
elif [[ "$LOG_LEVEL" == "info" ]]; then
CURRENT_LOG_LEVEL=${LOG_LEVEL_INFO}
elif [[ "$LOG_LEVEL" == "warn" ]]; then
CURRENT_LOG_LEVEL=${LOG_LEVEL_WARN}
elif [[ "$LOG_LEVEL" == "error" ]]; then
CURRENT_LOG_LEVEL=${LOG_LEVEL_ERROR}
fi
log_debug() {
if [[ ${CURRENT_LOG_LEVEL} -le ${LOG_LEVEL_DEBUG} ]]; then
echo "🔍 [$(date '+%Y-%m-%d %H:%M:%S')] [DEBUG] $*"
fi
}
log_info() {
if [[ ${CURRENT_LOG_LEVEL} -le ${LOG_LEVEL_INFO} ]]; then
echo " [$(date '+%Y-%m-%d %H:%M:%S')] [INFO] $*"
fi
}
log_warn() {
if [[ ${CURRENT_LOG_LEVEL} -le ${LOG_LEVEL_WARN} ]]; then
echo "⚠️ [$(date '+%Y-%m-%d %H:%M:%S')] [WARN] $*"
fi
}
log_error() {
if [[ ${CURRENT_LOG_LEVEL} -le ${LOG_LEVEL_ERROR} ]]; then
echo "❌ [$(date '+%Y-%m-%d %H:%M:%S')] [ERROR] $*"
fi
}
log_success() {
if [[ ${CURRENT_LOG_LEVEL} -le ${LOG_LEVEL_INFO} ]]; then
echo "✅ [$(date '+%Y-%m-%d %H:%M:%S')] [SUCCESS] $*"
fi
}
# 创建安全的临时文件
create_temp_file() {
local temp_file
temp_file=$(mktemp)
TEMP_FILES+=("$temp_file")
echo "$temp_file"
}
safe_db_url() {
# 直接返回固定的数据库路径
echo "sqlite://data/upage.db"
}
# 设置数据库信息
extract_db_info() {
DB_FILE="data/upage.db"
return 0
}
# 检查数据库连接
check_db_connection() {
log_info "检查 SQLite 数据库..."
if ! extract_db_info; then
return 1
fi
log_info "SQLite 数据库文件路径: $DB_FILE"
# 如果数据库文件已存在,检查是否可读写
if [[ -f "$DB_FILE" && ! -w "$DB_FILE" ]]; then
log_error "SQLite 数据库文件存在但不可写: $DB_FILE"
return 1
fi
# 验证 Prisma 配置
local output_file
output_file=$(create_temp_file)
if pnpm prisma validate --schema=./prisma/schema.prisma > "$output_file" 2>&1; then
log_success "Prisma 配置验证成功"
else
log_warn "Prisma 配置验证警告,但将继续执行:"
cat "$output_file"
fi
log_success "SQLite 数据库检查通过"
return 0
}
# 等待数据库就绪
wait_for_db() {
log_info "准备 SQLite 数据库..."
if check_db_connection; then
log_success "SQLite 数据库就绪"
return 0
else
log_error "SQLite 数据库检查失败"
return 1
fi
}
# 处理数据库迁移
handle_db_migration() {
log_info "处理数据库迁移..."
# 1. 尝试直接应用迁移
local migrate_output
migrate_output=$(create_temp_file)
log_info "尝试应用数据库迁移..."
if pnpm prisma migrate deploy --schema=./prisma/schema.prisma > "$migrate_output" 2>&1; then
log_success "数据库迁移成功应用"
return 0
fi
if grep -q "P3005" "$migrate_output" || grep -q "数据库架构不为空" "$migrate_output"; then
log_warn "检测到已存在的数据库结构,需要应用 baseline..."
local migration_dirs
migration_dirs=$(find prisma/migrations -maxdepth 1 -type d | grep -v "^prisma/migrations$" | sort)
if [[ -z "$migration_dirs" ]]; then
log_warn "未找到迁移,跳过 baseline 处理"
else
log_info "找到以下迁移:"
for migration_dir in $migration_dirs; do
local migration_name
migration_name=$(basename "$migration_dir")
log_info " - $migration_name"
log_info "将迁移 $migration_name 标记为已应用..."
local resolve_output
resolve_output=$(create_temp_file)
if ! pnpm prisma migrate resolve --applied "$migration_name" > "$resolve_output" 2>&1; then
log_warn "标记迁移 $migration_name 时出错:"
cat "$resolve_output"
fi
done
log_success "Baseline 应用成功"
log_info "检查并应用新的迁移..."
local deploy_output
deploy_output=$(create_temp_file)
if ! pnpm prisma migrate deploy > "$deploy_output" 2>&1; then
log_warn "应用新迁移时出错:"
cat "$deploy_output"
log_info "尝试使用 db push 作为备选方案..."
local push_output
push_output=$(create_temp_file)
if ! pnpm prisma db push --accept-data-loss --skip-generate > "$push_output" 2>&1; then
log_error "使用 db push 也失败了:"
cat "$push_output"
return 1
else
log_success "使用 db push 成功应用架构"
fi
else
log_success "新迁移应用成功"
fi
fi
else
log_warn "迁移失败,错误信息:"
cat "$migrate_output"
log_info "尝试使用 db push 作为备选方案..."
local push_output
push_output=$(create_temp_file)
if ! pnpm prisma db push --accept-data-loss --skip-generate > "$push_output" 2>&1; then
log_error "使用 db push 也失败了:"
cat "$push_output"
return 1
else
log_success "使用 db push 成功应用架构"
fi
fi
return 0
}
main() {
log_info "🚀 启动 UPage 应用..."
log_info "系统环境信息:"
log_info " - NODE_ENV: $NODE_ENV"
log_info " - 当前用户: $(whoami)"
log_info " - 工作目录: $(pwd)"
if extract_db_info; then
log_info "SQLite 数据库信息:"
log_info " - 数据库文件: $DB_FILE"
fi
if ! wait_for_db; then
log_error "数据库连接失败,退出启动流程"
exit 1
fi
echo "⏳ 数据库尚未就绪,等待 5 秒后重试... (${RETRY_COUNT}/${MAX_RETRIES})"
sleep 5
done
rm -f $ERROR_LOG
echo "✅ 数据库连接成功"
echo "🔍 检查数据库迁移状态..."
MIGRATE_ERROR_LOG=$(mktemp)
if pnpm prisma migrate deploy 2>$MIGRATE_ERROR_LOG | grep -q "P3005"; then
echo "📊 检测到已存在的数据库结构需要应用baseline..."
echo "📑 获取所有迁移..."
MIGRATION_DIRS=$(find prisma/migrations -maxdepth 1 -type d | grep -v "^prisma/migrations$" | sort)
if [ -z "$MIGRATION_DIRS" ]; then
echo "⚠️ 未找到迁移跳过baseline处理"
else
echo "🔄 找到以下迁移:"
for MIGRATION_DIR in $MIGRATION_DIRS; do
MIGRATION_NAME=$(basename "$MIGRATION_DIR")
echo " - $MIGRATION_NAME"
echo "🔖 将迁移 $MIGRATION_NAME 标记为已应用..."
RESOLVE_ERROR_LOG=$(mktemp)
if ! pnpm prisma migrate resolve --applied "$MIGRATION_NAME" 2>$RESOLVE_ERROR_LOG; then
echo "⚠️ 标记迁移 $MIGRATION_NAME 时出错:"
cat $RESOLVE_ERROR_LOG
fi
rm -f $RESOLVE_ERROR_LOG
done
echo "✅ Baseline应用成功"
echo "🔄 检查并应用新的迁移..."
DEPLOY_ERROR_LOG=$(mktemp)
if ! pnpm prisma migrate deploy 2>$DEPLOY_ERROR_LOG; then
echo "⚠️ 应用新迁移时出错:"
cat $DEPLOY_ERROR_LOG
fi
rm -f $DEPLOY_ERROR_LOG
fi
else
if [ -s "$MIGRATE_ERROR_LOG" ]; then
echo "⚠️ 数据库迁移过程中出现警告或错误:"
cat $MIGRATE_ERROR_LOG
else
echo "🆕 数据库迁移已成功应用"
fi
if ! handle_db_migration; then
log_error "数据库迁移失败,退出启动流程"
exit 1
fi
rm -f $MIGRATE_ERROR_LOG
local end_time
end_time=$(date +%s)
local duration=$((end_time - START_TIME))
echo "✅ 数据库迁移完成"
log_success "初始化完成,耗时 ${duration}"
log_info "启动应用服务..."
echo "🔧 生成 Prisma Client..."
GENERATE_ERROR_LOG=$(mktemp)
if ! pnpm prisma generate 2>$GENERATE_ERROR_LOG; then
echo "⚠️ 生成 Prisma Client 时出错:"
cat $GENERATE_ERROR_LOG
else
echo "✅ Prisma Client 生成完成"
fi
rm -f $GENERATE_ERROR_LOG
echo "🎉 启动应用服务..."
# 执行传入的命令
echo "🚀 执行命令: $@"
log_info "执行命令: $*"
exec "$@"
}
main "$@"

View File

@@ -14,8 +14,9 @@
"check:stage": "biome check --write --staged --no-errors-on-unmatched",
"clean": "node scripts/clean.js",
"setup": "prisma generate && prisma migrate deploy",
"docker:build": "docker build -t upage-ai:dev -t upage-ai:latest --target upage-ai-production .",
"docker:run": "docker compose -f docker-compose-dev.yaml up",
"docker:build": "docker build -t upage-ai:production -t upage-ai:latest --target runtime .",
"docker:dev:run": "docker compose -f docker-compose-dev.yaml up",
"docker:prod:run": "docker compose -f docker-compose-prod.yaml up",
"prepare": "husky || true",
"test": "vitest --run",
"test:watch": "vitest",
@@ -41,6 +42,8 @@
"@nanostores/react": "^1.0.0",
"@octokit/rest": "^22.0.0",
"@openrouter/ai-sdk-provider": "^1.2.0",
"@prisma/adapter-better-sqlite3": "^6.16.2",
"@prisma/client": "^6.16.2",
"@radix-ui/react-checkbox": "^1.3.3",
"@radix-ui/react-collapsible": "^1.1.12",
"@radix-ui/react-context-menu": "^2.2.16",
@@ -64,6 +67,7 @@
"classnames": "^2.5.1",
"compression": "^1.8.1",
"cors": "^2.8.5",
"cross-env": "^10.0.0",
"date-fns": "^4.1.0",
"diff": "^8.0.2",
"express": "^4.21.2",
@@ -79,6 +83,8 @@
"nanostores": "^1.0.1",
"ollama-ai-provider-v2": "^1.3.1",
"path-browserify": "^1.0.1",
"prettier": "^3.6.2",
"prisma": "^6.16.2",
"qrcode": "1.5.1",
"react": "^18.3.1",
"react-chartjs-2": "^5.3.0",
@@ -95,20 +101,19 @@
"remix-utils": "^9.0.0",
"shiki": "^3.13.0",
"sonner": "^2.0.7",
"unified": "^11.0.5",
"unist-util-visit": "^5.0.0",
"winston": "^3.17.0",
"winston-daily-rotate-file": "^5.0.0",
"zod": "^4.1.11",
"unified": "^11.0.5",
"prisma": "^6.16.2"
"zod": "^4.1.11"
},
"devDependencies": {
"dotenv": "^17.2.2",
"@octokit/types": "^15.0.0",
"@biomejs/biome": "2.2.4",
"@iconify/json": "^2.2.387",
"@iconify/types": "^2.0.0",
"@prisma/client": "^6.16.2",
"@octokit/types": "^15.0.0",
"@remix-run/dev": "^2.17.1",
"@remix-run/serve": "^2.17.1",
"@testing-library/jest-dom": "^6.8.0",
"@testing-library/react": "^16.3.0",
"@types/compression": "^1.8.1",
@@ -118,24 +123,21 @@
"@types/path-browserify": "^1.0.3",
"@types/react": "^18.3.1",
"@types/react-dom": "^18.3.1",
"@unocss/reset": "^66.5.2",
"@vitejs/plugin-react": "^5.0.3",
"concurrently": "^9.2.1",
"cross-env": "^10.0.0",
"crypto-browserify": "^3.12.1",
"dotenv": "^17.2.2",
"fast-glob": "^3.3.3",
"husky": "9.1.7",
"node-fetch": "^3.3.2",
"pnpm": "^10.17.1",
"sass-embedded": "^1.93.1",
"typescript": "^5.9.2",
"unocss": "^66.5.2",
"@unocss/reset": "^66.5.2",
"vite": "^5.4.19",
"vite-tsconfig-paths": "^5.1.4",
"vitest": "^3.2.4",
"@remix-run/dev": "^2.17.1",
"@remix-run/serve": "^2.17.1",
"node-fetch": "^3.3.2",
"prettier": "^3.6.2"
"vitest": "^3.2.4"
},
"packageManager": "pnpm@9.4.0",
"engines": {

169
pnpm-lock.yaml generated
View File

@@ -65,6 +65,12 @@ importers:
'@openrouter/ai-sdk-provider':
specifier: ^1.2.0
version: 1.2.0(ai@5.0.49(zod@4.1.11))(zod@4.1.11)
'@prisma/adapter-better-sqlite3':
specifier: ^6.16.2
version: 6.16.2
'@prisma/client':
specifier: ^6.16.2
version: 6.16.2(prisma@6.16.2(typescript@5.9.2))(typescript@5.9.2)
'@radix-ui/react-checkbox':
specifier: ^1.3.3
version: 1.3.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -134,6 +140,9 @@ importers:
cors:
specifier: ^2.8.5
version: 2.8.5
cross-env:
specifier: ^10.0.0
version: 10.0.0
date-fns:
specifier: ^4.1.0
version: 4.1.0
@@ -179,6 +188,9 @@ importers:
path-browserify:
specifier: ^1.0.1
version: 1.0.1
prettier:
specifier: ^3.6.2
version: 3.6.2
prisma:
specifier: ^6.16.2
version: 6.16.2(typescript@5.9.2)
@@ -258,9 +270,6 @@ importers:
'@octokit/types':
specifier: ^15.0.0
version: 15.0.0
'@prisma/client':
specifier: ^6.16.2
version: 6.16.2(prisma@6.16.2(typescript@5.9.2))(typescript@5.9.2)
'@remix-run/dev':
specifier: ^2.17.1
version: 2.17.1(@remix-run/react@2.17.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2))(@remix-run/serve@2.17.1(typescript@5.9.2))(@types/node@22.15.21)(sass-embedded@1.93.1)(sass@1.93.1)(typescript@5.9.2)(vite@5.4.19(@types/node@22.15.21)(sass-embedded@1.93.1)(sass@1.93.1))
@@ -303,9 +312,6 @@ importers:
concurrently:
specifier: ^9.2.1
version: 9.2.1
cross-env:
specifier: ^10.0.0
version: 10.0.0
crypto-browserify:
specifier: ^3.12.1
version: 3.12.1
@@ -324,9 +330,6 @@ importers:
pnpm:
specifier: ^10.17.1
version: 10.17.1
prettier:
specifier: ^3.6.2
version: 3.6.2
sass-embedded:
specifier: ^1.93.1
version: 1.93.1
@@ -1382,6 +1385,9 @@ packages:
'@polka/url@1.0.0-next.29':
resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
'@prisma/adapter-better-sqlite3@6.16.2':
resolution: {integrity: sha512-bCYiAveZAYZJTLsykJDZLXSpZ+0zu7KgoSl5VFlxR72S0WqCaImbtvFHxGDpwxhC9gO7skqZI7hQXDHSHDM0ig==}
'@prisma/client@6.16.2':
resolution: {integrity: sha512-E00PxBcalMfYO/TWnXobBVUai6eW/g5OsifWQsQDzJYm7yaY+IRLo7ZLsaefi0QkTpxfuhFcQ/w180i6kX3iJw==}
engines: {node: '>=18.18'}
@@ -1400,6 +1406,9 @@ packages:
'@prisma/debug@6.16.2':
resolution: {integrity: sha512-bo4/gA/HVV6u8YK2uY6glhNsJ7r+k/i5iQ9ny/3q5bt9ijCj7WMPUwfTKPvtEgLP+/r26Z686ly11hhcLiQ8zA==}
'@prisma/driver-adapter-utils@6.16.2':
resolution: {integrity: sha512-DMgfafnG0zPd+QoAQOC0Trn1xlb0fVAfQi2MpkpzSf641KiVkVPkJRXDSbcTbxGxO2HRdd0vI9U6LlesWad4XA==}
'@prisma/engines-version@6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43':
resolution: {integrity: sha512-ThvlDaKIVrnrv97ujNFDYiQbeMQpLa0O86HFA2mNoip4mtFqM7U5GSz2ie1i2xByZtvPztJlNRgPsXGeM/kqAA==}
@@ -2612,10 +2621,16 @@ packages:
before-after-hook@4.0.0:
resolution: {integrity: sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==}
better-sqlite3@11.10.0:
resolution: {integrity: sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ==}
binary-extensions@2.3.0:
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
engines: {node: '>=8'}
bindings@1.5.0:
resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}
bl@4.1.0:
resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
@@ -3001,6 +3016,10 @@ packages:
decode-named-character-reference@1.1.0:
resolution: {integrity: sha512-Wy+JTSbFThEOXQIR2L6mxJvEs+veIzpmqD7ynWxMXGpnk3smkHQOp6forLdHsKpAMW9iJpaBBIxz285t1n1C3w==}
decompress-response@6.0.0:
resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==}
engines: {node: '>=10'}
dedent@1.6.0:
resolution: {integrity: sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==}
peerDependencies:
@@ -3013,6 +3032,10 @@ packages:
resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==}
engines: {node: '>=6'}
deep-extend@0.6.0:
resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
engines: {node: '>=4.0.0'}
deep-object-diff@1.1.9:
resolution: {integrity: sha512-Rn+RuwkmkDwCi2/oXOFS9Gsr5lJZu/yTGpK7wAaAIE75CC+LCGEZHpY6VQJa/RoJcrmaA/docWJZvYohlNkWPA==}
@@ -3061,6 +3084,10 @@ packages:
engines: {node: '>=0.10'}
hasBin: true
detect-libc@2.1.1:
resolution: {integrity: sha512-ecqj/sy1jcK1uWrwpR67UhYrIFQ+5WlGxth34WquCbamhFA6hkkwiu37o6J5xCHdo1oixJRfVRw+ywV+Hq/0Aw==}
engines: {node: '>=8'}
detect-node-es@1.1.0:
resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==}
@@ -3261,6 +3288,10 @@ packages:
resolution: {integrity: sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw==}
engines: {node: '>=6'}
expand-template@2.0.3:
resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==}
engines: {node: '>=6'}
expect-type@1.2.1:
resolution: {integrity: sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==}
engines: {node: '>=12.0.0'}
@@ -3333,6 +3364,9 @@ packages:
resolution: {integrity: sha512-ek5xNX2YBYlXhiUXui3D/BXa3LdqPmoLJ7rqEx2bKJ7EAUEfmXgW0Das7Dc6Nr9MvqaOnIqiPV0mZk/r/UpNAg==}
engines: {node: '>=20'}
file-uri-to-path@1.0.0:
resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==}
fill-range@7.1.1:
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
engines: {node: '>=8'}
@@ -3448,6 +3482,9 @@ packages:
resolution: {integrity: sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==}
hasBin: true
github-from-package@0.0.0:
resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==}
glob-parent@5.1.2:
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
engines: {node: '>= 6'}
@@ -3628,6 +3665,9 @@ packages:
inherits@2.0.4:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
ini@1.3.8:
resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
inline-style-parser@0.1.1:
resolution: {integrity: sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==}
@@ -4238,6 +4278,10 @@ packages:
resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
engines: {node: '>=6'}
mimic-response@3.1.0:
resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
engines: {node: '>=10'}
min-indent@1.0.1:
resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
engines: {node: '>=4'}
@@ -4337,6 +4381,9 @@ packages:
resolution: {integrity: sha512-kNZ9xnoJYKg/AfxjrVL4SS0fKX++4awQReGqWnwTRHxeHGZ1FJFVgTqr/eMrNQdp0Tz7M7tG/TDaX8QfHDwVCw==}
engines: {node: ^20.0.0 || >=22.0.0}
napi-build-utils@2.0.0:
resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==}
negotiator@0.6.3:
resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==}
engines: {node: '>= 0.6'}
@@ -4345,6 +4392,10 @@ packages:
resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==}
engines: {node: '>= 0.6'}
node-abi@3.77.0:
resolution: {integrity: sha512-DSmt0OEcLoK4i3NuscSbGjOf3bqiDEutejqENSplMSFA/gmB8mkED9G4pKWnPl7MDU4rSHebKPHeitpDfyH0cQ==}
engines: {node: '>=10'}
node-addon-api@7.1.1:
resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
@@ -4686,6 +4737,11 @@ packages:
resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
engines: {node: ^10 || ^12 || >=14}
prebuild-install@7.1.3:
resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==}
engines: {node: '>=10'}
hasBin: true
prettier@2.8.8:
resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==}
engines: {node: '>=10.13.0'}
@@ -4807,6 +4863,10 @@ packages:
rc9@2.1.2:
resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==}
rc@1.2.8:
resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==}
hasBin: true
react-chartjs-2@5.3.0:
resolution: {integrity: sha512-UfZZFnDsERI3c3CZGxzvNJd02SHjaSJ8kgW1djn65H1KK8rehwTjyrRKOG3VTMG8wtHZ5rgAO5oTHtHi9GCCmw==}
peerDependencies:
@@ -5293,6 +5353,12 @@ packages:
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
engines: {node: '>=14'}
simple-concat@1.0.1:
resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==}
simple-get@4.0.1:
resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==}
simple-swizzle@0.2.2:
resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==}
@@ -5407,6 +5473,10 @@ packages:
resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==}
engines: {node: '>=8'}
strip-json-comments@2.0.1:
resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==}
engines: {node: '>=0.10.0'}
strip-literal@3.0.0:
resolution: {integrity: sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==}
@@ -5568,6 +5638,9 @@ packages:
tslib@2.8.1:
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
tunnel-agent@0.6.0:
resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
turbo-stream@2.4.1:
resolution: {integrity: sha512-v8kOJXpG3WoTN/+at8vK7erSzo6nW6CIaeOvNOkHQVDajfz1ZVeSxCbc6tOH4hrGZW7VUCV0TOXd8CPzYnYkrw==}
@@ -7136,6 +7209,11 @@ snapshots:
'@polka/url@1.0.0-next.29': {}
'@prisma/adapter-better-sqlite3@6.16.2':
dependencies:
'@prisma/driver-adapter-utils': 6.16.2
better-sqlite3: 11.10.0
'@prisma/client@6.16.2(prisma@6.16.2(typescript@5.9.2))(typescript@5.9.2)':
optionalDependencies:
prisma: 6.16.2(typescript@5.9.2)
@@ -7152,6 +7230,10 @@ snapshots:
'@prisma/debug@6.16.2': {}
'@prisma/driver-adapter-utils@6.16.2':
dependencies:
'@prisma/debug': 6.16.2
'@prisma/engines-version@6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43': {}
'@prisma/engines@6.16.2':
@@ -8562,8 +8644,17 @@ snapshots:
before-after-hook@4.0.0: {}
better-sqlite3@11.10.0:
dependencies:
bindings: 1.5.0
prebuild-install: 7.1.3
binary-extensions@2.3.0: {}
bindings@1.5.0:
dependencies:
file-uri-to-path: 1.0.0
bl@4.1.0:
dependencies:
buffer: 5.7.1
@@ -9023,10 +9114,16 @@ snapshots:
dependencies:
character-entities: 2.0.2
decompress-response@6.0.0:
dependencies:
mimic-response: 3.1.0
dedent@1.6.0: {}
deep-eql@5.0.2: {}
deep-extend@0.6.0: {}
deep-object-diff@1.1.9: {}
deepmerge-ts@7.1.5: {}
@@ -9063,6 +9160,8 @@ snapshots:
detect-libc@1.0.3:
optional: true
detect-libc@2.1.1: {}
detect-node-es@1.1.0: {}
devlop@1.1.0:
@@ -9299,6 +9398,8 @@ snapshots:
exit-hook@2.2.1: {}
expand-template@2.0.3: {}
expect-type@1.2.1: {}
express-rate-limit@8.1.0(express@4.21.2):
@@ -9404,6 +9505,8 @@ snapshots:
transitivePeerDependencies:
- supports-color
file-uri-to-path@1.0.0: {}
fill-range@7.1.1:
dependencies:
to-regex-range: 5.0.1
@@ -9522,6 +9625,8 @@ snapshots:
nypm: 0.6.2
pathe: 2.0.3
github-from-package@0.0.0: {}
glob-parent@5.1.2:
dependencies:
is-glob: 4.0.3
@@ -9785,6 +9890,8 @@ snapshots:
inherits@2.0.4: {}
ini@1.3.8: {}
inline-style-parser@0.1.1: {}
inline-style-parser@0.2.4: {}
@@ -10768,6 +10875,8 @@ snapshots:
mimic-fn@2.1.0: {}
mimic-response@3.1.0: {}
min-indent@1.0.1: {}
minimalistic-assert@1.0.1: {}
@@ -10850,10 +10959,16 @@ snapshots:
nanostores@1.0.1: {}
napi-build-utils@2.0.0: {}
negotiator@0.6.3: {}
negotiator@0.6.4: {}
node-abi@3.77.0:
dependencies:
semver: 7.7.2
node-addon-api@7.1.1:
optional: true
@@ -11201,6 +11316,21 @@ snapshots:
picocolors: 1.1.1
source-map-js: 1.2.1
prebuild-install@7.1.3:
dependencies:
detect-libc: 2.1.1
expand-template: 2.0.3
github-from-package: 0.0.0
minimist: 1.2.8
mkdirp-classic: 0.5.3
napi-build-utils: 2.0.0
node-abi: 3.77.0
pump: 3.0.2
rc: 1.2.8
simple-get: 4.0.1
tar-fs: 2.1.4
tunnel-agent: 0.6.0
prettier@2.8.8: {}
prettier@3.6.2: {}
@@ -11327,6 +11457,13 @@ snapshots:
defu: 6.1.4
destr: 2.0.5
rc@1.2.8:
dependencies:
deep-extend: 0.6.0
ini: 1.3.8
minimist: 1.2.8
strip-json-comments: 2.0.1
react-chartjs-2@5.3.0(chart.js@4.5.0)(react@18.3.1):
dependencies:
chart.js: 4.5.0
@@ -11866,6 +12003,14 @@ snapshots:
signal-exit@4.1.0: {}
simple-concat@1.0.1: {}
simple-get@4.0.1:
dependencies:
decompress-response: 6.0.0
once: 1.4.0
simple-concat: 1.0.1
simple-swizzle@0.2.2:
dependencies:
is-arrayish: 0.3.2
@@ -11974,6 +12119,8 @@ snapshots:
dependencies:
min-indent: 1.0.1
strip-json-comments@2.0.1: {}
strip-literal@3.0.0:
dependencies:
js-tokens: 9.0.1
@@ -12131,6 +12278,10 @@ snapshots:
tslib@2.8.1: {}
tunnel-agent@0.6.0:
dependencies:
safe-buffer: 5.2.1
turbo-stream@2.4.1: {}
type-fest@4.41.0: {}

View File

@@ -1,6 +1,6 @@
-- CreateTable
CREATE TABLE "ChatUsage" (
"id" TEXT NOT NULL,
"id" TEXT NOT NULL PRIMARY KEY,
"userId" TEXT NOT NULL,
"chatId" TEXT NOT NULL,
"messageId" TEXT NOT NULL,
@@ -9,35 +9,31 @@ CREATE TABLE "ChatUsage" (
"cachedTokens" INTEGER NOT NULL DEFAULT 0,
"reasoningTokens" INTEGER NOT NULL DEFAULT 0,
"totalTokens" INTEGER NOT NULL DEFAULT 0,
"calledAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"calledAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"status" TEXT NOT NULL,
"modelName" TEXT,
"prompt" TEXT,
"metadata" JSONB,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "ChatUsage_pkey" PRIMARY KEY ("id")
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL
);
-- CreateTable
CREATE TABLE "Chat" (
"id" TEXT NOT NULL,
"id" TEXT NOT NULL PRIMARY KEY,
"urlId" TEXT,
"userId" TEXT NOT NULL,
"description" TEXT,
"timestamp" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"timestamp" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"version" INTEGER NOT NULL DEFAULT 1,
"metadata" JSONB,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "Chat_pkey" PRIMARY KEY ("id")
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL
);
-- CreateTable
CREATE TABLE "Message" (
"id" TEXT NOT NULL,
"id" TEXT NOT NULL PRIMARY KEY,
"chatId" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"role" TEXT NOT NULL,
@@ -48,26 +44,24 @@ CREATE TABLE "Message" (
"metadata" JSONB,
"parts" JSONB,
"version" INTEGER NOT NULL DEFAULT 1,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "Message_pkey" PRIMARY KEY ("id")
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
CONSTRAINT "Message_chatId_fkey" FOREIGN KEY ("chatId") REFERENCES "Chat" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);
-- CreateTable
CREATE TABLE "Page" (
"id" TEXT NOT NULL,
"id" TEXT NOT NULL PRIMARY KEY,
"messageId" TEXT NOT NULL,
"pages" JSONB NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "Page_pkey" PRIMARY KEY ("id")
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
CONSTRAINT "Page_messageId_fkey" FOREIGN KEY ("messageId") REFERENCES "Message" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);
-- CreateTable
CREATE TABLE "Section" (
"id" TEXT NOT NULL,
"id" TEXT NOT NULL PRIMARY KEY,
"messageId" TEXT NOT NULL,
"type" TEXT NOT NULL DEFAULT 'section',
"action" TEXT NOT NULL DEFAULT 'add',
@@ -77,15 +71,14 @@ CREATE TABLE "Section" (
"domId" TEXT NOT NULL,
"rootDomId" TEXT,
"sort" INTEGER NOT NULL DEFAULT 0,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "Section_pkey" PRIMARY KEY ("id")
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
CONSTRAINT "Section_messageId_fkey" FOREIGN KEY ("messageId") REFERENCES "Message" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);
-- CreateTable
CREATE TABLE "Deployment" (
"id" TEXT NOT NULL,
"id" TEXT NOT NULL PRIMARY KEY,
"userId" TEXT NOT NULL,
"chatId" TEXT NOT NULL,
"platform" TEXT NOT NULL,
@@ -93,24 +86,21 @@ CREATE TABLE "Deployment" (
"url" TEXT NOT NULL,
"status" TEXT NOT NULL,
"metadata" JSONB,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "Deployment_pkey" PRIMARY KEY ("id")
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
CONSTRAINT "Deployment_chatId_fkey" FOREIGN KEY ("chatId") REFERENCES "Chat" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);
-- CreateTable
CREATE TABLE "UserSetting" (
"id" TEXT NOT NULL,
"id" TEXT NOT NULL PRIMARY KEY,
"userId" TEXT NOT NULL,
"category" TEXT NOT NULL,
"key" TEXT NOT NULL,
"value" TEXT NOT NULL,
"isSecret" BOOLEAN NOT NULL DEFAULT false,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "UserSetting_pkey" PRIMARY KEY ("id")
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL
);
-- CreateIndex
@@ -187,15 +177,3 @@ CREATE INDEX "UserSetting_isSecret_idx" ON "UserSetting"("isSecret");
-- CreateIndex
CREATE UNIQUE INDEX "UserSetting_userId_category_key_key" ON "UserSetting"("userId", "category", "key");
-- AddForeignKey
ALTER TABLE "Message" ADD CONSTRAINT "Message_chatId_fkey" FOREIGN KEY ("chatId") REFERENCES "Chat"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Page" ADD CONSTRAINT "Page_messageId_fkey" FOREIGN KEY ("messageId") REFERENCES "Message"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Section" ADD CONSTRAINT "Section_messageId_fkey" FOREIGN KEY ("messageId") REFERENCES "Message"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Deployment" ADD CONSTRAINT "Deployment_chatId_fkey" FOREIGN KEY ("chatId") REFERENCES "Chat"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@@ -1,3 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (e.g., Git)
provider = "postgresql"
provider = "sqlite"

View File

@@ -3,8 +3,8 @@ generator client {
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
provider = "sqlite"
url = "file:../data/upage.db"
}
// 聊天使用记录,每个 message 对应多条。
@@ -87,7 +87,7 @@ model Message {
// 'user' 或 'assistant'
role String
// 消息内容
content String @db.Text
content String
// 版本 ID
revisionId String?
// 注释
@@ -147,7 +147,7 @@ model Section {
// 页面名称
pageName String @default("")
// Section 内容 (HTML/CSS)
content String @db.Text
content String
// DOM ID
domId String
// 根 DOM ID用于标识父级容器
@@ -209,7 +209,7 @@ model UserSetting {
// 设置键名
key String
// 设置值
value String @db.Text
value String
// 是否为敏感信息(如 API 密钥)
isSecret Boolean @default(false)
// 创建时间