From 72cef8d29b25ad5884669f844180c51653238d5d Mon Sep 17 00:00:00 2001 From: jingrow Date: Sat, 15 Nov 2025 04:15:34 +0800 Subject: [PATCH] restructure file serving to use direct filesystem access --- .gitignore | 3 + Caddyfile | 9 +- apps/jingrow/frontend/package-lock.json | 242 +++++++++++++++++- apps/jingrow/frontend/package.json | 2 + apps/jingrow/frontend/vite.config.ts | 19 +- .../jingrow/utils/export_app_package.py | 4 +- apps/jingrow/jingrow/utils/fs.py | 5 +- apps/jingrow/jingrow/utils/path.py | 8 + 8 files changed, 278 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index 3685058..6c609c7 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,9 @@ test/ **/output/ **/__pycache__/ +# 后端文件存储目录 +apps/jingrow/jingrow/public/files/ + *.py[cod] .env diff --git a/Caddyfile b/Caddyfile index 7471497..6e8d11e 100644 --- a/Caddyfile +++ b/Caddyfile @@ -50,7 +50,7 @@ } # 后端 API 反向代理 - handle /jingrow/* { + handle /jingrow* { reverse_proxy localhost:9001 { header_up X-Real-IP {remote_host} header_up X-Forwarded-For {remote_host} @@ -58,6 +58,13 @@ } } + # 文件服务(直接服务文件系统,无需 FastAPI) + handle /files/* { + root * apps/jingrow/jingrow/public + encode gzip zstd + file_server + } + # 前端静态文件 + SPA 路由支持 handle { root * apps/jingrow/frontend/dist diff --git a/apps/jingrow/frontend/package-lock.json b/apps/jingrow/frontend/package-lock.json index 390fdc4..c2b4e25 100644 --- a/apps/jingrow/frontend/package-lock.json +++ b/apps/jingrow/frontend/package-lock.json @@ -75,8 +75,10 @@ "devDependencies": { "@fortawesome/fontawesome-free": "^6.5.2", "@types/node": "^20.0.0", + "@types/serve-static": "^2.2.0", "@vitejs/plugin-vue": "^4.4.0", "sass": "^1.69.0", + "serve-static": "^2.2.0", "typescript": "^5.0.0", "vite": "^4.4.0", "vue-tsc": "^1.8.0" @@ -1635,6 +1637,13 @@ "@types/unist": "*" } }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/katex": { "version": "0.16.7", "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.7.tgz", @@ -1704,6 +1713,17 @@ "integrity": "sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg==", "license": "BSD-3-Clause" }, + "node_modules/@types/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*" + } + }, "node_modules/@types/unist": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", @@ -2068,9 +2088,9 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz", - "integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", + "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", @@ -2455,6 +2475,16 @@ "node": ">=0.4.0" } }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -2513,6 +2543,13 @@ "node": ">= 0.4" } }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true, + "license": "MIT" + }, "node_modules/emoji-regex": { "version": "10.5.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.5.0.tgz", @@ -2546,6 +2583,16 @@ "emojibase": "*" } }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/entities": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", @@ -2641,6 +2688,13 @@ "@esbuild/win32-x64": "0.18.20" } }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true, + "license": "MIT" + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -2659,6 +2713,16 @@ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", "license": "MIT" }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/eventemitter3": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", @@ -2737,6 +2801,16 @@ "node": ">= 6" } }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -2912,6 +2986,33 @@ "node": ">=12.0.0" } }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/immutable": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.3.tgz", @@ -2919,6 +3020,13 @@ "dev": true, "license": "MIT" }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, "node_modules/is-arguments": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", @@ -3391,6 +3499,19 @@ "node": ">= 0.4" } }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/orderedmap": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-2.1.1.tgz", @@ -3409,6 +3530,16 @@ "integrity": "sha512-HUrJFQ/StvgmXRcQ1ftY6VEZUq3jA2t9ncFN4F84J/vN0/FPpQF+8FKXb3l6fLces6q0uOHj6NJn+2xvZnxO6A==", "license": "BSD-3-Clause" }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/path-browserify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", @@ -3859,6 +3990,16 @@ "node": ">=0.10" } }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/raw-loader": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-0.5.1.tgz", @@ -3961,6 +4102,68 @@ "node": ">=10" } }, + "node_modules/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/send/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/send/node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -3993,6 +4196,13 @@ "node": ">= 0.4" } }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true, + "license": "ISC" + }, "node_modules/sortablejs": { "version": "1.15.6", "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.6.tgz", @@ -4014,6 +4224,16 @@ "integrity": "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==", "license": "MIT" }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/tinyexec": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.1.tgz", @@ -4077,6 +4297,16 @@ "node": ">=8.0" } }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, "node_modules/treemate": { "version": "0.3.11", "resolved": "https://registry.npmjs.org/treemate/-/treemate-0.3.11.tgz", @@ -4132,9 +4362,9 @@ } }, "node_modules/unplugin-icons": { - "version": "22.4.2", - "resolved": "https://registry.npmjs.org/unplugin-icons/-/unplugin-icons-22.4.2.tgz", - "integrity": "sha512-Yv15405unO67Chme0Slk0JRA/H2AiAZLK5t7ebt8/ZpTDlBfM4d4En2qD3MX2rzOSkIteQ0syIm3q8MSofeoBA==", + "version": "22.5.0", + "resolved": "https://registry.npmjs.org/unplugin-icons/-/unplugin-icons-22.5.0.tgz", + "integrity": "sha512-MBlMtT5RuMYZy4TZgqUL2OTtOdTUVsS1Mhj6G1pEzMlFJlEnq6mhUfoIt45gBWxHcsOdXJDWLg3pRZ+YmvAVWQ==", "license": "MIT", "dependencies": { "@antfu/install-pkg": "^1.1.0", diff --git a/apps/jingrow/frontend/package.json b/apps/jingrow/frontend/package.json index b636d75..5443d84 100644 --- a/apps/jingrow/frontend/package.json +++ b/apps/jingrow/frontend/package.json @@ -75,8 +75,10 @@ "devDependencies": { "@fortawesome/fontawesome-free": "^6.5.2", "@types/node": "^20.0.0", + "@types/serve-static": "^2.2.0", "@vitejs/plugin-vue": "^4.4.0", "sass": "^1.69.0", + "serve-static": "^2.2.0", "typescript": "^5.0.0", "vite": "^4.4.0", "vue-tsc": "^1.8.0" diff --git a/apps/jingrow/frontend/vite.config.ts b/apps/jingrow/frontend/vite.config.ts index 739e196..7926c2e 100644 --- a/apps/jingrow/frontend/vite.config.ts +++ b/apps/jingrow/frontend/vite.config.ts @@ -5,8 +5,9 @@ import { fileURLToPath, URL } from 'node:url' import Icons from 'unplugin-icons/vite' import IconsResolver from 'unplugin-icons/resolver' import Components from 'unplugin-vue-components/vite' -import fs from 'node:fs' import path from 'node:path' +import fs from 'node:fs' +import { createRequire } from 'node:module' // 统一处理后端 Set-Cookie,移除 Secure 标志,便于在 HTTP 开发环境保存 Cookie const cookieRewriteConfigure = (proxy: any) => { @@ -41,13 +42,16 @@ const currentDir = fileURLToPath(new URL('.', import.meta.url)) const appsDir = path.resolve(currentDir, '..', '..') const APPS_ORDER = loadAppsOrder(appsDir) -export default defineConfig(({ mode }) => { +export default defineConfig(({ mode, command }) => { const env = loadEnv(mode, process.cwd(), '') const BACKEND_URL = env.VITE_BACKEND_SERVER_URL || 'http://localhost:9001' const FRONTEND_HOST = env.VITE_FRONTEND_HOST || '0.0.0.0' const FRONTEND_PORT = Number(env.VITE_FRONTEND_PORT) || 3100 const ALLOWED_HOSTS = (env.VITE_ALLOWED_HOSTS || '').split(',').map((s) => s.trim()).filter(Boolean) + // 开发环境:文件目录路径 + const filesDir = command === 'serve' ? path.resolve(currentDir, '..', 'jingrow', 'public', 'files') : null + return { plugins: [ vue(), @@ -63,7 +67,16 @@ export default defineConfig(({ mode }) => { }), ], }), - ], + // 开发环境:直接服务文件系统 + command === 'serve' && filesDir && { + name: 'serve-files', + configureServer(server) { + const require = createRequire(import.meta.url) + const serve = require('serve-static')(filesDir, { index: false }) + server.middlewares.use('/files', serve) + } + } + ].filter(Boolean), resolve: { alias: { '@': fileURLToPath(new URL('./src', import.meta.url)), diff --git a/apps/jingrow/jingrow/utils/export_app_package.py b/apps/jingrow/jingrow/utils/export_app_package.py index db08c0e..f11f6ed 100644 --- a/apps/jingrow/jingrow/utils/export_app_package.py +++ b/apps/jingrow/jingrow/utils/export_app_package.py @@ -4,6 +4,7 @@ from pathlib import Path import shutil from datetime import datetime +from jingrow.utils.path import get_files_dir def export_app_package_from_local(app_name: str, apps_dir: Path): """从本地应用目录打包应用安装包""" @@ -15,9 +16,8 @@ def export_app_package_from_local(app_name: str, apps_dir: Path): tmp_dir.mkdir(parents=True, exist_ok=True) # files 目录用于存放最终导出文件 - files_dir = root / "apps" / "jingrow" / "frontend" / "public" / "files" - files_dir.mkdir(parents=True, exist_ok=True) + files_dir = get_files_dir() app_source_dir = apps_dir / app_name if not app_source_dir.exists(): return { diff --git a/apps/jingrow/jingrow/utils/fs.py b/apps/jingrow/jingrow/utils/fs.py index 60dbe64..23f2ec0 100644 --- a/apps/jingrow/jingrow/utils/fs.py +++ b/apps/jingrow/jingrow/utils/fs.py @@ -6,6 +6,7 @@ import requests from urllib.parse import urlparse import jingrow from jingrow.config import Config +from jingrow.utils.path import get_files_dir def ensure_dir(path: Path) -> None: """幂等创建目录""" @@ -26,7 +27,7 @@ def atomic_write_json(target: Path, data: dict, indent: int = 2) -> None: @jingrow.whitelist(allow_guest=True) def download_image_to_local(image_url: str, filename: str = None) -> dict: """ - 异步下载图片到本地 public/files 目录 + 异步下载图片到本地文件存储目录(apps/jingrow/jingrow/public/files) Args: image_url (str): 图片URL @@ -61,7 +62,7 @@ def download_image_to_local(image_url: str, filename: str = None) -> dict: filename = "".join(c for c in filename if c.isalnum() or c in "._-") # 构建本地文件路径 - public_dir = Path(__file__).parent.parent.parent.parent.parent / "apps" / "jingrow" / "frontend" / "public" / "files" + public_dir = get_files_dir() ensure_dir(public_dir) local_path = public_dir / filename diff --git a/apps/jingrow/jingrow/utils/path.py b/apps/jingrow/jingrow/utils/path.py index 23fbf87..fdf3434 100644 --- a/apps/jingrow/jingrow/utils/path.py +++ b/apps/jingrow/jingrow/utils/path.py @@ -21,3 +21,11 @@ def get_jingrow_root() -> Path: """返回 jingrow 模块根目录路径:apps/jingrow/jingrow""" return get_root_path() / "apps" / "jingrow" / "jingrow" + +@lru_cache(maxsize=1) +def get_files_dir() -> Path: + """返回文件存储目录:apps/jingrow/jingrow/public/files""" + files_dir = get_jingrow_root() / "public" / "files" + files_dir.mkdir(parents=True, exist_ok=True) + return files_dir +