diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..5ae2fc9 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,22 @@ +FROM node:24-bookworm-slim + +WORKDIR /app + +RUN npm config set registry https://registry.npmmirror.com/ && \ + yarn config set registry https://registry.npmmirror.com/ + +# Copy the repository contents into the image and install all dependencies +COPY . . + +# The container only runs the backend dev server, so strip Electron-only +# packages before installing to avoid downloading desktop binaries. +RUN node -e "const fs=require('fs');const pkg=JSON.parse(fs.readFileSync('package.json','utf8'));for(const section of ['dependencies','devDependencies']){if(!pkg[section]) continue;for(const name of ['custom-electron-titlebar','electron','electron-builder','electron-rebuild','electronmon']) delete pkg[section][name];}fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2)+'\n');" && \ + yarn install --frozen-lockfile && \ + yarn cache clean + +ENV NODE_ENV=dev +ENV PORT=10588 + +EXPOSE 10588 + +CMD ["yarn", "dev"] \ No newline at end of file diff --git a/data/web/index.html b/data/web/index.html index 73fbb89..383b9b1 100644 --- a/data/web/index.html +++ b/data/web/index.html @@ -5,37 +5,37 @@ Toonflow - - + `),OIs=/enable|requires|diagnostic/,ivn=new RegExp("[_\\p{XID_Start}]\\p{XID_Continue}*","u"),gF="variable.predefined",yIs={tokenPostfix:".wgsl",defaultToken:"invalid",unicode:!0,atoms:fIs,keywords:gIs,reserved:mIs,predeclared_enums:kIs,predeclared_types:vIs,predeclared_type_generators:AIs,predeclared_type_aliases:pIs,predeclared_intrinsics:bIs,operators:wIs,symbols:/[!%&*+\-\.\/:;<=>^|_~,]+/,tokenizer:{root:[[OIs,"keyword","@directive"],[ivn,{cases:{"@atoms":gF,"@keywords":"keyword","@reserved":"invalid","@predeclared_enums":gF,"@predeclared_types":gF,"@predeclared_type_generators":gF,"@predeclared_type_aliases":gF,"@predeclared_intrinsics":gF,"@default":"identifier"}}],{include:"@commentOrSpace"},{include:"@numbers"},[/[{}()\[\]]/,"@brackets"],["@","annotation","@attribute"],[/@symbols/,{cases:{"@operators":"operator","@default":"delimiter"}}],[/./,"invalid"]],commentOrSpace:[[/\s+/,"white"],[/\/\*/,"comment","@blockComment"],[/\/\/.*$/,"comment"]],blockComment:[[/[^\/*]+/,"comment"],[/\/\*/,"comment","@push"],[/\*\//,"comment","@pop"],[/[\/*]/,"comment"]],attribute:[{include:"@commentOrSpace"},[/\w+/,"annotation","@pop"]],directive:[{include:"@commentOrSpace"},[/[()]/,"@brackets"],[/,/,"delimiter"],[ivn,"meta.content"],[/;/,"delimiter","@pop"]],numbers:[[/0[fh]/,"number.float"],[/[1-9][0-9]*[fh]/,"number.float"],[/[0-9]*\.[0-9]+([eE][+-]?[0-9]+)?[fh]?/,"number.float"],[/[0-9]+\.[0-9]*([eE][+-]?[0-9]+)?[fh]?/,"number.float"],[/[0-9]+[eE][+-]?[0-9]+[fh]?/,"number.float"],[/0[xX][0-9a-fA-F]*\.[0-9a-fA-F]+(?:[pP][+-]?[0-9]+[fh]?)?/,"number.hex"],[/0[xX][0-9a-fA-F]+\.[0-9a-fA-F]*(?:[pP][+-]?[0-9]+[fh]?)?/,"number.hex"],[/0[xX][0-9a-fA-F]+[pP][+-]?[0-9]+[fh]?/,"number.hex"],[/0[xX][0-9a-fA-F]+[iu]?/,"number.hex"],[/[1-9][0-9]*[iu]?/,"number"],[/0[iu]?/,"number"]]}},CIs=Object.freeze(Object.defineProperty({__proto__:null,conf:hIs,language:yIs},Symbol.toStringTag,{value:"Module"})),_Is={comments:{blockComment:["\x3C!--","-->"]},brackets:[["<",">"]],autoClosingPairs:[{open:"<",close:">"},{open:"'",close:"'"},{open:'"',close:'"'}],surroundingPairs:[{open:"<",close:">"},{open:"'",close:"'"},{open:'"',close:'"'}],onEnterRules:[{beforeText:new RegExp("<([_:\\w][_:\\w-.\\d]*)([^/>]*(?!/)>)[^<]*$","i"),afterText:/^<\/([_:\w][_:\w-.\d]*)\s*>$/i,action:{indentAction:Rt.IndentAction.IndentOutdent}},{beforeText:new RegExp("<(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$","i"),action:{indentAction:Rt.IndentAction.Indent}}]},SIs={defaultToken:"",tokenPostfix:".xml",ignoreCase:!0,qualifiedName:/(?:[\w\.\-]+:)?[\w\.\-]+/,tokenizer:{root:[[/[^<&]+/,""],{include:"@whitespace"},[/(<)(@qualifiedName)/,[{token:"delimiter"},{token:"tag",next:"@tag"}]],[/(<\/)(@qualifiedName)(\s*)(>)/,[{token:"delimiter"},{token:"tag"},"",{token:"delimiter"}]],[/(<\?)(@qualifiedName)/,[{token:"delimiter"},{token:"metatag",next:"@tag"}]],[/(<\!)(@qualifiedName)/,[{token:"delimiter"},{token:"metatag",next:"@tag"}]],[/<\!\[CDATA\[/,{token:"delimiter.cdata",next:"@cdata"}],[/&\w+;/,"string.escape"]],cdata:[[/[^\]]+/,""],[/\]\]>/,{token:"delimiter.cdata",next:"@pop"}],[/\]/,""]],tag:[[/[ \t\r\n]+/,""],[/(@qualifiedName)(\s*=\s*)("[^"]*"|'[^']*')/,["attribute.name","","attribute.value"]],[/(@qualifiedName)(\s*=\s*)("[^">?\/]*|'[^'>?\/]*)(?=[\?\/]\>)/,["attribute.name","","attribute.value"]],[/(@qualifiedName)(\s*=\s*)("[^">]*|'[^'>]*)/,["attribute.name","","attribute.value"]],[/@qualifiedName/,"attribute.name"],[/\?>/,{token:"delimiter",next:"@pop"}],[/(\/)(>)/,[{token:"tag"},{token:"delimiter",next:"@pop"}]],[/>/,{token:"delimiter",next:"@pop"}]],whitespace:[[/[ \t\r\n]+/,""],[/\x3C!--/,{token:"comment",next:"@comment"}]],comment:[[/[^<\-]+/,"comment.content"],[/-->/,{token:"comment",next:"@pop"}],[/\x3C!--/,"comment.content.invalid"],[/[<\-]/,"comment.content"]]}},LIs=Object.freeze(Object.defineProperty({__proto__:null,conf:_Is,language:SIs},Symbol.toStringTag,{value:"Module"})),xIs={comments:{lineComment:"#"},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],folding:{offSide:!0},onEnterRules:[{beforeText:/:\s*$/,action:{indentAction:Rt.IndentAction.Indent}}]},EIs={tokenPostfix:".yaml",brackets:[{token:"delimiter.bracket",open:"{",close:"}"},{token:"delimiter.square",open:"[",close:"]"}],keywords:["true","True","TRUE","false","False","FALSE","null","Null","Null","~"],numberInteger:/(?:0|[+-]?[0-9]+)/,numberFloat:/(?:0|[+-]?[0-9]+)(?:\.[0-9]+)?(?:e[-+][1-9][0-9]*)?/,numberOctal:/0o[0-7]+/,numberHex:/0x[0-9a-fA-F]+/,numberInfinity:/[+-]?\.(?:inf|Inf|INF)/,numberNaN:/\.(?:nan|Nan|NAN)/,numberDate:/\d{4}-\d\d-\d\d([Tt ]\d\d:\d\d:\d\d(\.\d+)?(( ?[+-]\d\d?(:\d\d)?)|Z)?)?/,escapes:/\\(?:[btnfr\\"']|[0-7][0-7]?|[0-3][0-7]{2})/,tokenizer:{root:[{include:"@whitespace"},{include:"@comment"},[/%[^ ]+.*$/,"meta.directive"],[/---/,"operators.directivesEnd"],[/\.{3}/,"operators.documentEnd"],[/[-?:](?= )/,"operators"],{include:"@anchor"},{include:"@tagHandle"},{include:"@flowCollections"},{include:"@blockStyle"},[/@numberInteger(?![ \t]*\S+)/,"number"],[/@numberFloat(?![ \t]*\S+)/,"number.float"],[/@numberOctal(?![ \t]*\S+)/,"number.octal"],[/@numberHex(?![ \t]*\S+)/,"number.hex"],[/@numberInfinity(?![ \t]*\S+)/,"number.infinity"],[/@numberNaN(?![ \t]*\S+)/,"number.nan"],[/@numberDate(?![ \t]*\S+)/,"number.date"],[/(".*?"|'.*?'|[^#'"]*?)([ \t]*)(:)( |$)/,["type","white","operators","white"]],{include:"@flowScalars"},[/.+?(?=(\s+#|$))/,{cases:{"@keywords":"keyword","@default":"string"}}]],object:[{include:"@whitespace"},{include:"@comment"},[/\}/,"@brackets","@pop"],[/,/,"delimiter.comma"],[/:(?= )/,"operators"],[/(?:".*?"|'.*?'|[^,\{\[]+?)(?=: )/,"type"],{include:"@flowCollections"},{include:"@flowScalars"},{include:"@tagHandle"},{include:"@anchor"},{include:"@flowNumber"},[/[^\},]+/,{cases:{"@keywords":"keyword","@default":"string"}}]],array:[{include:"@whitespace"},{include:"@comment"},[/\]/,"@brackets","@pop"],[/,/,"delimiter.comma"],{include:"@flowCollections"},{include:"@flowScalars"},{include:"@tagHandle"},{include:"@anchor"},{include:"@flowNumber"},[/[^\],]+/,{cases:{"@keywords":"keyword","@default":"string"}}]],multiString:[[/^( +).+$/,"string","@multiStringContinued.$1"]],multiStringContinued:[[/^( *).+$/,{cases:{"$1==$S2":"string","@default":{token:"@rematch",next:"@popall"}}}]],whitespace:[[/[ \t\r\n]+/,"white"]],comment:[[/#.*$/,"comment"]],flowCollections:[[/\[/,"@brackets","@array"],[/\{/,"@brackets","@object"]],flowScalars:[[/"([^"\\]|\\.)*$/,"string.invalid"],[/'([^'\\]|\\.)*$/,"string.invalid"],[/'[^']*'/,"string"],[/"/,"string","@doubleQuotedString"]],doubleQuotedString:[[/[^\\"]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/"/,"string","@pop"]],blockStyle:[[/[>|][0-9]*[+-]?$/,"operators","@multiString"]],flowNumber:[[/@numberInteger(?=[ \t]*[,\]\}])/,"number"],[/@numberFloat(?=[ \t]*[,\]\}])/,"number.float"],[/@numberOctal(?=[ \t]*[,\]\}])/,"number.octal"],[/@numberHex(?=[ \t]*[,\]\}])/,"number.hex"],[/@numberInfinity(?=[ \t]*[,\]\}])/,"number.infinity"],[/@numberNaN(?=[ \t]*[,\]\}])/,"number.nan"],[/@numberDate(?=[ \t]*[,\]\}])/,"number.date"]],tagHandle:[[/\![^ ]*/,"tag"]],anchor:[[/[&*][^ ]+/,"namespace"]]}},TIs=Object.freeze(Object.defineProperty({__proto__:null,conf:xIs,language:EIs},Symbol.toStringTag,{value:"Module"})); +
diff --git a/docker/Dockerfile b/docker/Dockerfile deleted file mode 100644 index 5f90f90..0000000 --- a/docker/Dockerfile +++ /dev/null @@ -1,121 +0,0 @@ -# 构建阶段 -FROM node:24-alpine AS builder - -WORKDIR /app - -# 定义构建参数 -ARG GIT=github -ARG TAG="" -ARG BRANCH="" - -# 安装 git -RUN apk add --no-cache git - -RUN npm config set registry https://registry.npmmirror.com/ && \ - yarn config set registry https://registry.npmmirror.com/ - -# 根据参数选择仓库源,支持 TAG / BRANCH 切换 -# 优先级: TAG > BRANCH > 最新 tag > 默认分支 -RUN if [ "$GIT" = "gitee" ]; then \ - REPO_URL="https://gitee.com/HBAI-Ltd/Toonflow-app.git"; \ - else \ - REPO_URL="https://github.com/HBAI-Ltd/Toonflow-app.git"; \ - fi && \ - echo "Cloning from: $REPO_URL" && \ - git clone "$REPO_URL" . && \ - if [ -n "$TAG" ]; then \ - echo "Checking out specified tag: $TAG" && \ - git checkout "$TAG"; \ - elif [ -n "$BRANCH" ]; then \ - echo "Checking out specified branch: $BRANCH" && \ - git checkout "$BRANCH"; \ - else \ - LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || git tag --sort=-v:refname | head -n 1) && \ - if [ -n "$LATEST_TAG" ]; then \ - echo "Checking out latest tag: $LATEST_TAG" && \ - git checkout "$LATEST_TAG"; \ - else \ - echo "No tags found, using default branch"; \ - fi; \ - fi && \ - echo "Current version:" && git describe --tags --always - -RUN yarn install --frozen-lockfile - -RUN yarn build - -# 生产阶段 -FROM node:24-alpine - -WORKDIR /app - -# 安装 nginx 和 supervisor -RUN apk add --no-cache nginx supervisor && \ - mkdir -p /var/lib/nginx/logs /var/log/nginx && \ - npm config set registry https://registry.npmmirror.com/ && \ - yarn config set registry https://registry.npmmirror.com/ && \ - npm install -g pm2 - -# 复制后端文件 -COPY --from=builder /app/build ./build -COPY --from=builder /app/data/serve ./data/serve -COPY --from=builder /app/package.json ./ -COPY --from=builder /app/yarn.lock ./ - -# 复制静态页面到 nginx 目录 -COPY --from=builder /app/scripts/web /usr/share/nginx/html - -# 只安装生产依赖 -RUN yarn install --frozen-lockfile --production - -# 配置 nginx -RUN cat > /etc/nginx/http.d/default.conf << 'EOF' -server { - listen 80; - location / { - root /usr/share/nginx/html; - index index.html; - try_files $uri $uri/ /index.html; - } -} -EOF - -# 配置 nginx 主配置,日志输出到 stderr/stdout -RUN sed -i 's|error_log /var/log/nginx/error.log warn;|error_log /dev/stderr warn;|g' /etc/nginx/nginx.conf || true && \ - sed -i 's|access_log /var/log/nginx/access.log main;|access_log /dev/stdout main;|g' /etc/nginx/nginx.conf || true - -# 配置 supervisor -RUN cat > /etc/supervisord.conf << 'EOF' -[supervisord] -nodaemon=true -logfile=/var/log/supervisord.log -pidfile=/var/run/supervisord.pid - -[program:nginx] -command=nginx -g "daemon off;" -autostart=true -autorestart=true -stdout_logfile=/dev/stdout -stdout_logfile_maxbytes=0 -stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=0 - -[program:app] -command=pm2-runtime start data/serve/app.js --name app -directory=/app -autostart=true -autorestart=true -stdout_logfile=/dev/stdout -stdout_logfile_maxbytes=0 -stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=0 -environment=NODE_ENV=prod -EOF - -ENV NODE_ENV=prod - -EXPOSE 80 -EXPOSE 10588 - -# 启动时创建必要目录(防止 volume 挂载覆盖) -CMD sh -c "mkdir -p /var/log/nginx /var/lib/nginx/logs && exec supervisord -c /etc/supervisord.conf" diff --git a/docker/Dockerfile.local b/docker/Dockerfile.local deleted file mode 100644 index dcd2856..0000000 --- a/docker/Dockerfile.local +++ /dev/null @@ -1,95 +0,0 @@ -# 本地构建阶段 - 使用本地源码,不从 git 克隆 -FROM node:24-alpine AS builder - -WORKDIR /app - -RUN npm config set registry https://registry.npmmirror.com/ && \ - yarn config set registry https://registry.npmmirror.com/ - -# 复制依赖文件 -COPY package.json yarn.lock ./ - -RUN yarn install --frozen-lockfile - -# 复制源码 -COPY tsconfig.json ./ -COPY src/ ./src/ -COPY scripts/ ./scripts/ - -RUN yarn build - -# 生产阶段 -FROM node:24-alpine - -WORKDIR /app - -# 安装 nginx 和 supervisor -RUN apk add --no-cache nginx supervisor && \ - mkdir -p /var/lib/nginx/logs /var/log/nginx && \ - npm config set registry https://registry.npmmirror.com/ && \ - yarn config set registry https://registry.npmmirror.com/ && \ - npm install -g pm2 - -# 复制后端文件 -COPY --from=builder /app/build ./build -COPY --from=builder /app/data/serve ./data/serve -COPY --from=builder /app/package.json ./ -COPY --from=builder /app/yarn.lock ./ - -# 复制静态页面到 nginx 目录 -COPY --from=builder /app/scripts/web /usr/share/nginx/html - -# 只安装生产依赖 -RUN yarn install --frozen-lockfile --production - -# 配置 nginx -RUN cat > /etc/nginx/http.d/default.conf << 'EOF' -server { - listen 80; - location / { - root /usr/share/nginx/html; - index index.html; - try_files $uri $uri/ /index.html; - } -} -EOF - -# 配置 nginx 主配置,日志输出到 stderr/stdout -RUN sed -i 's|error_log /var/log/nginx/error.log warn;|error_log /dev/stderr warn;|g' /etc/nginx/nginx.conf || true && \ - sed -i 's|access_log /var/log/nginx/access.log main;|access_log /dev/stdout main;|g' /etc/nginx/nginx.conf || true - -# 配置 supervisor -RUN cat > /etc/supervisord.conf << 'EOF' -[supervisord] -nodaemon=true -logfile=/var/log/supervisord.log -pidfile=/var/run/supervisord.pid - -[program:nginx] -command=nginx -g "daemon off;" -autostart=true -autorestart=true -stdout_logfile=/dev/stdout -stdout_logfile_maxbytes=0 -stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=0 - -[program:app] -command=pm2-runtime start data/serve/app.js --name app -directory=/app -autostart=true -autorestart=true -stdout_logfile=/dev/stdout -stdout_logfile_maxbytes=0 -stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=0 -environment=NODE_ENV=prod -EOF - -ENV NODE_ENV=prod - -EXPOSE 80 -EXPOSE 10588 - -# 启动时创建必要目录(防止 volume 挂载覆盖) -CMD sh -c "mkdir -p /var/log/nginx /var/lib/nginx/logs && exec supervisord -c /etc/supervisord.conf" diff --git a/docker/docker-compose.local.yml b/docker/docker-compose.local.yml deleted file mode 100644 index 42923f6..0000000 --- a/docker/docker-compose.local.yml +++ /dev/null @@ -1,24 +0,0 @@ -# 本地打包测试用,使用本地源码构建 -# 用法: docker-compose -f docker/docker-compose.local.yml up -d --build - -services: - toonflow: - build: - context: .. - dockerfile: docker/Dockerfile.local - image: toonflow:local - container_name: toonflow-local - restart: unless-stopped - ports: - - "8080:80" - - "10588:10588" - environment: - - NODE_ENV=prod - volumes: - - ../logs:/var/log - healthcheck: - test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:80/"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 40s diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml deleted file mode 100644 index d8f4b1e..0000000 --- a/docker/docker-compose.yml +++ /dev/null @@ -1,26 +0,0 @@ -services: - toonflow: - build: - context: .. - dockerfile: docker/Dockerfile - args: - GIT: ${GIT:-github} - TAG: ${TAG:-} - BRANCH: ${BRANCH:-} - image: toonflow:${TAG:-latest} - container_name: toonflow - restart: unless-stopped - ports: - - "80" - - "10588:10588" - environment: - - NODE_ENV=prod - volumes: - # 可选: 持久化日志 - - ../logs:/var/log - healthcheck: - test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:80/"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 40s diff --git a/package.json b/package.json index 741b792..fdf1faa 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "dev": "nodemon --inspect --exec tsx src/app.ts", "dev:gui": "electronmon -r tsx scripts/main.ts", "dev:gui-vite": "cross-env VITE_DEV=1 electronmon -r tsx scripts/main.ts", + "start": "cross-env NODE_ENV=prod node data/serve/app.js", "lint": "tsc --noEmit", "build": "cross-env NODE_ENV=prod tsx scripts/build.ts", "pack": "electron-builder --dir", @@ -28,7 +29,6 @@ "dist:win": "yarn build && electron-builder --win", "dist:mac": "yarn build && electron-builder --mac", "dist:linux": "yarn build && electron-builder --linux", - "test": "cross-env NODE_ENV=prod node data/serve/app.js", "docker:build": "docker-compose -f docker/docker-compose.yml up -d --build", "docker:local": "docker-compose -f docker/docker-compose.local.yml up -d --build", "debug:ai": "npx @ai-sdk/devtools", diff --git a/scripts/main.ts b/scripts/main.ts index 80da606..02947ab 100644 --- a/scripts/main.ts +++ b/scripts/main.ts @@ -3,20 +3,62 @@ import path from "path"; import fs from "fs"; import Module from "module"; +declare const __APP_VERSION__: string | undefined; + /** * 将 extraResources 中的 data 目录复制到用户数据目录(跳过已存在的文件,保留用户修改) */ + +function getVersionFromFile(filePath: string): string | null { + try { + if (fs.existsSync(filePath)) { + return fs.readFileSync(filePath, "utf8").trim(); + } + } catch {} + return null; +} + +function writeVersionToFile(filePath: string, version: string): void { + fs.writeFileSync(filePath, version, { encoding: "utf8" }); +} + +function copyDirForce(src: string, dest: string): void { + if (!fs.existsSync(src)) return; + if (fs.existsSync(dest)) { + fs.rmSync(dest, { recursive: true, force: true }); + } + copyDirRecursive(src, dest); +} + function initializeData(): void { const srcDir = path.join(process.resourcesPath, "data"); const destDir = path.join(app.getPath("userData"), "data"); - if (fs.existsSync(destDir)) return; - copyDirRecursive(srcDir, destDir); + const versionFile = path.join(destDir, "version.txt"); + const currentVersion = typeof __APP_VERSION__ !== "undefined" ? __APP_VERSION__ : "0.0.0"; + const userVersion = getVersionFromFile(versionFile); + + // 首次安装或无version.txt,直接全量拷贝 + if (!fs.existsSync(destDir) || !userVersion) { + copyDirRecursive(srcDir, destDir); + writeVersionToFile(versionFile, currentVersion); + return; + } + + // 版本号不同则覆盖 serve 和 web 目录 + if (userVersion !== currentVersion) { + copyDirForce(path.join(srcDir, "serve"), path.join(destDir, "serve")); + copyDirForce(path.join(srcDir, "web"), path.join(destDir, "web")); + writeVersionToFile(versionFile, currentVersion); + } } function copyDirRecursive(src: string, dest: string): void { if (!fs.existsSync(src)) return; if (!fs.existsSync(dest)) fs.mkdirSync(dest, { recursive: true }); for (const entry of fs.readdirSync(src, { withFileTypes: true })) { + // 跳过 oss 文件夹和 db2.sqlite 文件 + if (entry.isDirectory() && entry.name === "oss") continue; + if (!entry.isDirectory() && entry.name === "db2.sqlite") continue; const srcPath = path.join(src, entry.name); const destPath = path.join(dest, entry.name); if (entry.isDirectory()) { @@ -109,7 +151,9 @@ function showLoading(): void { }); loadingWindow.setMenuBarVisibility(false); loadingWindow.removeMenu(); - loadingWindow.on("closed", () => { loadingWindow = null; }); + loadingWindow.on("closed", () => { + loadingWindow = null; + }); void loadingWindow.loadURL(loadingHtml); } @@ -228,7 +272,7 @@ app.whenReady().then(async () => { } else { return { ok: false, error: "缺少url参数" }; } - } + }, }; const handler = handlers[pathname]; const responseData = handler ? handler() : { error: "未知接口" }; diff --git a/src/app.ts b/src/app.ts index 3458637..280cb98 100644 --- a/src/app.ts +++ b/src/app.ts @@ -29,16 +29,24 @@ export default async function startServe(randomPort: Boolean = false) { app.use(express.json({ limit: "100mb" })); app.use(express.urlencoded({ extended: true, limit: "100mb" })); - const rootDir = u.getPath("oss"); - // 确保 uploads 目录存在 + // oss 静态资源 + const rootDir = u.getPath("oss"); if (!fs.existsSync(rootDir)) { fs.mkdirSync(rootDir, { recursive: true }); } console.log("文件目录:", rootDir); - app.use(express.static(rootDir)); + // data/web 静态网站 + const webDir = u.getPath("web"); + if (fs.existsSync(webDir)) { + console.log("静态网站目录:", webDir); + app.use(express.static(webDir)); + } else { + console.warn("静态网站目录不存在:", webDir); + } + app.use(async (req, res, next) => { const setting = await u.db("o_setting").where("key", "tokenKey").select("value").first(); if (!setting) return res.status(444).send({ message: "服务器秘钥未配置,请联系管理员" }); diff --git a/src/types/database.d.ts b/src/types/database.d.ts index 0aca397..2a3bc57 100644 --- a/src/types/database.d.ts +++ b/src/types/database.d.ts @@ -1,4 +1,4 @@ -// @db-hash 6be0a80e9c8f541987a4c1e907736237 +// @db-hash 93b2462070c45c2b449e9a18c4e88763 //该文件由脚本自动生成,请勿手动修改 export interface memories { @@ -178,7 +178,6 @@ export interface o_storyboard { 'sound'?: string | null; 'state'?: string | null; 'title'?: string | null; - 'videoPrompt'?: string | null; } export interface o_tasks { 'describe'?: string | null; @@ -212,7 +211,6 @@ export interface o_video { 'errorReason'?: string | null; 'filePath'?: string | null; 'id'?: number; - 'projectId'?: number | null; 'scriptId'?: number | null; 'state'?: string | null; 'storyboardId'?: number | null;