From 530dfac665bbb06ff59a32b64e987615bcda76d0 Mon Sep 17 00:00:00 2001 From: LIlGG <1103069291@qq.com> Date: Thu, 25 Sep 2025 15:24:13 +0800 Subject: [PATCH] pref: change the database type to SQLite --- .env.example | 7 - .gitignore | 2 + Dockerfile | 95 ++--- README.md | 5 +- app/lib/.server/llm/chat-stream-text.ts | 2 +- app/lib/.server/llm/tools/index.ts | 18 +- app/lib/.server/prisma.ts | 8 +- docker-compose-dev.yaml | 24 +- docker-compose-prod.yaml | 27 +- docker-entrypoint.sh | 345 ++++++++++++------ logto/docker-compose.yaml | 2 +- package.json | 32 +- pnpm-lock.yaml | 169 ++++++++- .../migration.sql | 76 ++-- prisma/migrations/migration_lock.toml | 2 +- prisma/schema.prisma | 10 +- 16 files changed, 513 insertions(+), 311 deletions(-) rename prisma/migrations/{20250922030707_init => 20250925035842_init}/migration.sql (65%) diff --git a/.env.example b/.env.example index e86d4ae..a0e3161 100644 --- a/.env.example +++ b/.env.example @@ -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" - diff --git a/.gitignore b/.gitignore index 3b810e1..ea0db88 100644 --- a/.gitignore +++ b/.gitignore @@ -52,3 +52,5 @@ mock # storage /public/uploads + +/data/ diff --git a/Dockerfile b/Dockerfile index e8096f4..b012e32 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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 diff --git a/README.md b/README.md index e5c29be..7bec42f 100644 --- a/README.md +++ b/README.md @@ -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 ``` diff --git a/app/lib/.server/llm/chat-stream-text.ts b/app/lib/.server/llm/chat-stream-text.ts index 727f6a4..fe43fbd 100644 --- a/app/lib/.server/llm/chat-stream-text.ts +++ b/app/lib/.server/llm/chat-stream-text.ts @@ -80,7 +80,7 @@ ${Object.entries(context) return _streamText({ model, - tools, + tools: tools(), system: systemPrompt, maxOutputTokens: maxTokens || MAX_TOKENS, messages: convertToModelMessages(messages), diff --git a/app/lib/.server/llm/tools/index.ts b/app/lib/.server/llm/tools/index.ts index 4d7c76e..b13a2ad 100644 --- a/app/lib/.server/llm/tools/index.ts +++ b/app/lib/.server/llm/tools/index.ts @@ -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 = {}; -export { serperTool, weatherTool }; + if (process.env.SERPER_API_KEY) { + tools.serper = serperTool; + } + + if (process.env.WEATHER_API_KEY) { + tools.weather = weatherTool; + } + + return tools; +}; diff --git a/app/lib/.server/prisma.ts b/app/lib/.server/prisma.ts index c18441f..ca20e64 100644 --- a/app/lib/.server/prisma.ts +++ b/app/lib/.server/prisma.ts @@ -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; diff --git a/docker-compose-dev.yaml b/docker-compose-dev.yaml index 7775ac6..3cfccb9 100644 --- a/docker-compose-dev.yaml +++ b/docker-compose-dev.yaml @@ -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: diff --git a/docker-compose-prod.yaml b/docker-compose-prod.yaml index 2efb5ad..890b4bd 100644 --- a/docker-compose-prod.yaml +++ b/docker-compose-prod.yaml @@ -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: diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index e5051e2..4e02ee3 100644 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -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 - fi +cleanup() { + log_info "正在清理资源..." - 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') + for temp_file in "${TEMP_FILES[@]}"; do + if [[ -f "$temp_file" ]]; then + rm -f "$temp_file" + log_debug "已删除临时文件: $temp_file" + fi + done - 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) +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 -until pnpm prisma db push --accept-data-loss 2>$ERROR_LOG || pnpm prisma migrate deploy 2>$ERROR_LOG; do - RETRY_COUNT=$((RETRY_COUNT + 1)) +log_debug() { + if [[ ${CURRENT_LOG_LEVEL} -le ${LOG_LEVEL_DEBUG} ]]; then + echo "🔍 [$(date '+%Y-%m-%d %H:%M:%S')] [DEBUG] $*" + fi +} - echo "❌ 数据库连接尝试 $RETRY_COUNT 失败,错误信息:" - cat $ERROR_LOG +log_info() { + if [[ ${CURRENT_LOG_LEVEL} -le ${LOG_LEVEL_INFO} ]]; then + echo "ℹ️ [$(date '+%Y-%m-%d %H:%M:%S')] [INFO] $*" + fi +} - 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 "❌ 数据库无法连接" +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 - echo "⚠️ pg_isready 命令不可用,无法进行连接测试" + 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 + if ! handle_db_migration; then + log_error "数据库迁移失败,退出启动流程" + exit 1 fi -else - if [ -s "$MIGRATE_ERROR_LOG" ]; then - echo "⚠️ 数据库迁移过程中出现警告或错误:" - cat $MIGRATE_ERROR_LOG - else - echo "🆕 数据库迁移已成功应用" - fi -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 + log_info "执行命令: $*" + exec "$@" +} -echo "🎉 启动应用服务..." - -# 执行传入的命令 -echo "🚀 执行命令: $@" -exec "$@" +main "$@" diff --git a/logto/docker-compose.yaml b/logto/docker-compose.yaml index 6813e20..3190cfd 100644 --- a/logto/docker-compose.yaml +++ b/logto/docker-compose.yaml @@ -38,4 +38,4 @@ networks: volumes: logto-connectors: - postgres-data: \ No newline at end of file + postgres-data: diff --git a/package.json b/package.json index e4c803c..18cabba 100644 --- a/package.json +++ b/package.json @@ -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": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c5e6159..8c30dd1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -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: {} diff --git a/prisma/migrations/20250922030707_init/migration.sql b/prisma/migrations/20250925035842_init/migration.sql similarity index 65% rename from prisma/migrations/20250922030707_init/migration.sql rename to prisma/migrations/20250925035842_init/migration.sql index 6381905..498144e 100644 --- a/prisma/migrations/20250922030707_init/migration.sql +++ b/prisma/migrations/20250925035842_init/migration.sql @@ -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; diff --git a/prisma/migrations/migration_lock.toml b/prisma/migrations/migration_lock.toml index 044d57c..2a5a444 100644 --- a/prisma/migrations/migration_lock.toml +++ b/prisma/migrations/migration_lock.toml @@ -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" diff --git a/prisma/schema.prisma b/prisma/schema.prisma index a6e32e5..a5bb795 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -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) // 创建时间