commit 1b5468995f1d2653111b757a277b762d3d01cba4 Author: 扫地僧 <357099073@qq.com> Date: Sat Aug 22 22:51:21 2026 +0800 first commit diff --git a/backend/.gitignore b/backend/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/backend/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/backend/.vscode/extensions.json b/backend/.vscode/extensions.json new file mode 100644 index 0000000..a7cea0b --- /dev/null +++ b/backend/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["Vue.volar"] +} diff --git a/backend/README.md b/backend/README.md new file mode 100644 index 0000000..33895ab --- /dev/null +++ b/backend/README.md @@ -0,0 +1,5 @@ +# Vue 3 + TypeScript + Vite + +This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 ` + + diff --git a/backend/package-lock.json b/backend/package-lock.json new file mode 100644 index 0000000..139cf50 --- /dev/null +++ b/backend/package-lock.json @@ -0,0 +1,1163 @@ +{ + "name": "backend", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "backend", + "version": "0.0.0", + "dependencies": { + "vue": "^3.5.40" + }, + "devDependencies": { + "@types/node": "^24.13.3", + "@vitejs/plugin-vue": "^6.0.8", + "@vue/tsconfig": "^0.9.1", + "typescript": "~6.0.2", + "vite": "^8.2.0", + "vue-tsc": "^3.3.8" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.8", + "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.29.8.tgz", + "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@oxc-project/types": { + "version": "0.146.0", + "resolved": "https://registry.npmmirror.com/@oxc-project/types/-/types-0.146.0.tgz", + "integrity": "sha512-XC0QsnnhVe7sLIWmYmdPw7x5P0h4W8vUU3Nv1ySgWXtvCz8NizoAEpGXA0sOYoJQV2Rl13LgURAHQ5cI5ILCSA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@rolldown/binding-android-arm-eabi": { + "version": "1.2.5", + "resolved": "https://registry.npmmirror.com/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.5.tgz", + "integrity": "sha512-DLe/i+l8ynIBY7XEQ191TeZvCoowIGa18R+dIV30GW7DiOtp74i/xX8hs8GUjW5ARV7VZuie3d6AumSmCwbeRA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.5", + "resolved": "https://registry.npmmirror.com/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.5.tgz", + "integrity": "sha512-zXcwKlQApYAOELHd8PwKDFkagYF9Wy4e0RJ+0qnzl9Pjnpj75TEG8ufv40p2J7kCEfwZAsNiuzRIyNNMWT38ig==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.2.5", + "resolved": "https://registry.npmmirror.com/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.5.tgz", + "integrity": "sha512-dK4QakI42nzWgJT5sm4y4y/O//D4OxM75/cH28RLV+nzIN9AY+YsbuUVrUTjlLjXR6vpyxFbSsbmNuJ6BP9sww==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.2.5", + "resolved": "https://registry.npmmirror.com/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.5.tgz", + "integrity": "sha512-fqSALaUu1Wjd1nK2uW2kJDWdLCc8lx1IcY+MTY26Aurfdx19anlzhqXOgCFbBFQnlFDTn4TC1/7Nz4Bl2mLP3A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.2.5", + "resolved": "https://registry.npmmirror.com/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.5.tgz", + "integrity": "sha512-/vCnNxlkxs9tKxNDcyWUePpJ/PgTzxIaVhoM5SmG8UV+GR/IcPam4VYxi7GIMo7PSDuNqlJqvprqii9NqqVCMw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.2.5", + "resolved": "https://registry.npmmirror.com/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.5.tgz", + "integrity": "sha512-abk0NLA519LxRCszmbE0jYKuQ9YPocOXTiOXOo6Yr+YAT95VH+PtqYAjOJvGKt3viEd/x4qzabAlwd5bHOOARg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.2.5", + "resolved": "https://registry.npmmirror.com/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.5.tgz", + "integrity": "sha512-Y7eALiJ8lr0M2HH103Js+g7V34wf6snlpZLAsHI90uLhr3PVlNsbFVAXJC9d/V6BnPyKtpSwI+NcB/RLxsQxuA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.2.5", + "resolved": "https://registry.npmmirror.com/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.5.tgz", + "integrity": "sha512-xMvZgnbZg4YVnR/AX2b3oOPDTFYJvUVaJg5FedA/LuvexAtXibZQej4cnTkw3rjsJ/ggUROB64TdtETiim+FYA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.2.5", + "resolved": "https://registry.npmmirror.com/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.5.tgz", + "integrity": "sha512-GRjeqTUDHTo5GwntsLaAMcBahG3nlpjftXWZLN73HiYQlhwEowvarFgQnRnQZtIp4keXX7quXFbG38uPZBa2EA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.2.5", + "resolved": "https://registry.npmmirror.com/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.5.tgz", + "integrity": "sha512-vLNTR45F2Uwc8AufkNXPmB4VliaXs+FvcheEogIzOXzO4l+LzieXF5A/TWxLy5HtqpsRCHUfd0lPVrrdgXdLHQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.2.5", + "resolved": "https://registry.npmmirror.com/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.5.tgz", + "integrity": "sha512-Mgj59/HTuYeK9Gz2MA+mBWKnHsAgkBSec15ZMb1st3oIfFbX7gCjOae7GydHhzcyQi9Z/7M1QuN9bR3oFqF0jQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.2.5", + "resolved": "https://registry.npmmirror.com/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.5.tgz", + "integrity": "sha512-mY8AP0/ichsbhAxGnLa3d3+MwV0EfgrPND2bplI3Ym8T6R2pJ0N87bvrKVwNXmdy3jnr6eQBecdqx/HMknBmpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.2.5", + "resolved": "https://registry.npmmirror.com/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.5.tgz", + "integrity": "sha512-8SLssA2oweAxyRgDp789ACfRb/3P+zNRJpzZxSizxF9m8NUDQ4+3xjo8ttjhVGGw6Qxb70oZiEtIjaKikCO7Yw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.5", + "resolved": "https://registry.npmmirror.com/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.5.tgz", + "integrity": "sha512-vGbruD5zquhoc8D9SViXgN2FBJtNdTyQ4DtG+SWiEGlJiAzoKcZ2xp+xuXCffhubVdt0NJlTZqkeRuERy7g8Cw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.2.5", + "resolved": "https://registry.npmmirror.com/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.5.tgz", + "integrity": "sha512-e/SXpgISz+IoqVcSSI0rx/d/he8zqLex+/rCWpnHpmVfmPIUjag9H6P7zotf0gJHwPUhQxZ/mF8tr6acebT9yw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.13.3", + "resolved": "https://registry.npmmirror.com/@types/node/-/node-24.13.3.tgz", + "integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/@vitejs/plugin-vue": { + "version": "6.0.8", + "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-6.0.8.tgz", + "integrity": "sha512-0ZjgOg7oO6farnNGup7yvoM/YXZV84OZxHAwtflItNa/6zzQyVb5LNxyea3FEKEX2XlagIKzrlH7wwxkKgtiew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@volar/language-core": { + "version": "2.4.28", + "resolved": "https://registry.npmmirror.com/@volar/language-core/-/language-core-2.4.28.tgz", + "integrity": "sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/source-map": "2.4.28" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.28", + "resolved": "https://registry.npmmirror.com/@volar/source-map/-/source-map-2.4.28.tgz", + "integrity": "sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@volar/typescript": { + "version": "2.4.28", + "resolved": "https://registry.npmmirror.com/@volar/typescript/-/typescript-2.4.28.tgz", + "integrity": "sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.28", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.41", + "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.41.tgz", + "integrity": "sha512-q0Xtv/F9w2YO/7htQhtiL+Ev2WCJbe5N2hc+XfgyKkEKqWpSxknmT8QOuGdEKNdjPq0c3F7rNpFkTo3Kfrm7pg==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@vue/shared": "3.5.41", + "entities": "^7.0.1", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.41", + "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.41.tgz", + "integrity": "sha512-oKacVfNglLvGjnS6BXOlGL7EyG2h8X03pqXCjzotRZUaXGjbrTJUnVAQjrCqUnS+lyu31nwQjZY/d817GmCnfw==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.41", + "@vue/shared": "3.5.41" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.41", + "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.41.tgz", + "integrity": "sha512-XJhip7R2wy6vX3knCxdZN4KracFaZUef58s1KYewqluedHIJaPIVfXoYT7MF1F8nCvv6k8bWWxDC8opMkg1VTQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@vue/compiler-core": "3.5.41", + "@vue/compiler-dom": "3.5.41", + "@vue/compiler-ssr": "3.5.41", + "@vue/shared": "3.5.41", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.19", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.41", + "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.41.tgz", + "integrity": "sha512-U3v5OejKEGqOI0Wy0+Sz7hGuIFZHA4LSXzrNM3IMIeDyJEBBfTpX26n3SDgToRpP2bLc9FfI2j/kSgcJ8Emq5A==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.41", + "@vue/shared": "3.5.41" + } + }, + "node_modules/@vue/language-core": { + "version": "3.3.11", + "resolved": "https://registry.npmmirror.com/@vue/language-core/-/language-core-3.3.11.tgz", + "integrity": "sha512-QJmpliwAVpC/OxubIByPAhNzsQPRc8/gxlN2qnVzVfIMjMDz/9RnXRFoetjz5yEgXVXyp4LqhXq3V53PjmNzFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.28", + "@vue/compiler-dom": "^3.5.0", + "@vue/shared": "^3.5.0", + "alien-signals": "^3.2.1", + "muggle-string": "^0.4.1", + "path-browserify": "^1.0.1", + "picomatch": "^4.0.4" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.41", + "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.41.tgz", + "integrity": "sha512-rznsqKM0np0x18EjzF8x88MpEhdNsffbvFbckLL5+oUKz1BxAImEmO7J1ArRYSyo6aQaVoBDp7jEkT91OOxydA==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.41" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.41", + "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.41.tgz", + "integrity": "sha512-Vcry58hiAKwGen9Z1jUZE0feFsNArPCMOImYI8el48A9Idf6DuQYD0U05zZIF2Iad1hGhPSvcbBbAOhNr55fhg==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.41", + "@vue/shared": "3.5.41" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.41", + "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.41.tgz", + "integrity": "sha512-3vVBahVBS9+U6cmXBLyb8nE6/yYo4J/CGI9eVFs3KiMc0YHuudwKyShTD65jtJy/L9PUUxNAFu4cj4LiJ0UFbw==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.41", + "@vue/runtime-core": "3.5.41", + "@vue/shared": "3.5.41", + "csstype": "^3.2.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.41", + "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.41.tgz", + "integrity": "sha512-n6hx/pNFfbD6SuyeuMVkvqox8bwf/ET9JlA/kAz/imw8sw++wkqKe2mHX5KutjPpbKE4Z56yTHszoOjGMI9igQ==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.41", + "@vue/runtime-dom": "3.5.41", + "@vue/shared": "3.5.41" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.41", + "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.41.tgz", + "integrity": "sha512-IOnwSCma8j+9xJT6b8H0dEYidC80NsYmNMlZxRsukYcSoGaDBohog5hDxzeUXdFeGWFA++vWvxqOmrr96VlqMA==", + "license": "MIT" + }, + "node_modules/@vue/tsconfig": { + "version": "0.9.1", + "resolved": "https://registry.npmmirror.com/@vue/tsconfig/-/tsconfig-0.9.1.tgz", + "integrity": "sha512-buvjm+9NzLCJL29KY1j1991YYJ5e6275OiK+G4jtmfIb+z4POywbdm0wXusT9adVWqe0xqg70TbI7+mRx4uU9w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "typescript": ">= 5.8", + "vue": "^3.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "vue": { + "optional": true + } + } + }, + "node_modules/alien-signals": { + "version": "3.2.1", + "resolved": "https://registry.npmmirror.com/alien-signals/-/alien-signals-3.2.1.tgz", + "integrity": "sha512-I8FjmltrfnDFoZedi5CG8DghVYNhzb/Ijluz7tCSJH0xpd0484Kowhbb1XDYOxfJpU1p5wnM2X54dA+IfGyD1g==", + "dev": true, + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmmirror.com/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmmirror.com/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmmirror.com/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmmirror.com/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmmirror.com/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmmirror.com/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmmirror.com/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmmirror.com/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmmirror.com/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmmirror.com/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmmirror.com/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmmirror.com/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmmirror.com/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.18", + "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.26", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.26.tgz", + "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.17", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rolldown": { + "version": "1.2.5", + "resolved": "https://registry.npmmirror.com/rolldown/-/rolldown-1.2.5.tgz", + "integrity": "sha512-VD2IE5PUG4Oj8zz2VGykiYd5wbnjdIiSsNQb8Qu5B+noEp+A78mu2iVvpp27g8es14Tk9rofNs5Tku9iQCS4fA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.146.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm-eabi": "1.2.5", + "@rolldown/binding-android-arm64": "1.2.5", + "@rolldown/binding-darwin-arm64": "1.2.5", + "@rolldown/binding-darwin-x64": "1.2.5", + "@rolldown/binding-freebsd-x64": "1.2.5", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.5", + "@rolldown/binding-linux-arm64-gnu": "1.2.5", + "@rolldown/binding-linux-arm64-musl": "1.2.5", + "@rolldown/binding-linux-ppc64-gnu": "1.2.5", + "@rolldown/binding-linux-s390x-gnu": "1.2.5", + "@rolldown/binding-linux-x64-gnu": "1.2.5", + "@rolldown/binding-linux-x64-musl": "1.2.5", + "@rolldown/binding-openharmony-arm64": "1.2.5", + "@rolldown/binding-win32-arm64-msvc": "1.2.5", + "@rolldown/binding-win32-x64-msvc": "1.2.5" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmmirror.com/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "8.2.2", + "resolved": "https://registry.npmmirror.com/vite/-/vite-8.2.2.tgz", + "integrity": "sha512-cFKLV/PRgAUlIRm5WjMjJ86jrftzpqcgH+Us+DS8mI3CDNiH30Whrz8uHL3+MOLPAgqbMBAqWdAHAphOAM+z/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.33.0", + "picomatch": "^4.0.5", + "postcss": "^8.5.26", + "rolldown": "~1.2.4", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.4.0 || ^0.5.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vscode-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/vue": { + "version": "3.5.41", + "resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.41.tgz", + "integrity": "sha512-2laE0p+aK+/AOPG/XL/WepOs/GlK755LJ1XECi9kDUrz1FKNw8rb2Xzlw9JS1rqEV55nb0ttsKxVlTCcd+R5cg==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.41", + "@vue/compiler-sfc": "3.5.41", + "@vue/runtime-dom": "3.5.41", + "@vue/server-renderer": "3.5.41", + "@vue/shared": "3.5.41" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-tsc": { + "version": "3.3.11", + "resolved": "https://registry.npmmirror.com/vue-tsc/-/vue-tsc-3.3.11.tgz", + "integrity": "sha512-gOb0B9rtU2+f1dszwPqSH5kAieIF9ReeLhD3kSRNHv5WZZUQz/JdVXW0RTdqhNTMlQkqKzrTTviqKr/4FYZraQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/typescript": "2.4.28", + "@vue/language-core": "3.3.11" + }, + "bin": { + "vue-tsc": "bin/vue-tsc.js" + }, + "peerDependencies": { + "typescript": ">=5.0.0" + } + } + } +} diff --git a/backend/package.json b/backend/package.json new file mode 100644 index 0000000..f5b2ef5 --- /dev/null +++ b/backend/package.json @@ -0,0 +1,22 @@ +{ + "name": "backend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vue-tsc -b && vite build", + "preview": "vite preview" + }, + "dependencies": { + "vue": "^3.5.40" + }, + "devDependencies": { + "@types/node": "^24.13.3", + "@vitejs/plugin-vue": "^6.0.8", + "@vue/tsconfig": "^0.9.1", + "typescript": "~6.0.2", + "vite": "^8.2.0", + "vue-tsc": "^3.3.8" + } +} diff --git a/backend/public/favicon.svg b/backend/public/favicon.svg new file mode 100644 index 0000000..6893eb1 --- /dev/null +++ b/backend/public/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/backend/public/icons.svg b/backend/public/icons.svg new file mode 100644 index 0000000..e952219 --- /dev/null +++ b/backend/public/icons.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/backend/src/App.vue b/backend/src/App.vue new file mode 100644 index 0000000..388b05e --- /dev/null +++ b/backend/src/App.vue @@ -0,0 +1,7 @@ + + + diff --git a/backend/src/assets/hero.png b/backend/src/assets/hero.png new file mode 100644 index 0000000..02251f4 Binary files /dev/null and b/backend/src/assets/hero.png differ diff --git a/backend/src/assets/vite.svg b/backend/src/assets/vite.svg new file mode 100644 index 0000000..5101b67 --- /dev/null +++ b/backend/src/assets/vite.svg @@ -0,0 +1 @@ +Vite diff --git a/backend/src/assets/vue.svg b/backend/src/assets/vue.svg new file mode 100644 index 0000000..770e9d3 --- /dev/null +++ b/backend/src/assets/vue.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/backend/src/components/HelloWorld.vue b/backend/src/components/HelloWorld.vue new file mode 100644 index 0000000..c232865 --- /dev/null +++ b/backend/src/components/HelloWorld.vue @@ -0,0 +1,95 @@ + + + diff --git a/backend/src/main.ts b/backend/src/main.ts new file mode 100644 index 0000000..2425c0f --- /dev/null +++ b/backend/src/main.ts @@ -0,0 +1,5 @@ +import { createApp } from 'vue' +import './style.css' +import App from './App.vue' + +createApp(App).mount('#app') diff --git a/backend/src/style.css b/backend/src/style.css new file mode 100644 index 0000000..527d4fb --- /dev/null +++ b/backend/src/style.css @@ -0,0 +1,296 @@ +:root { + --text: #6b6375; + --text-h: #08060d; + --bg: #fff; + --border: #e5e4e7; + --code-bg: #f4f3ec; + --accent: #aa3bff; + --accent-bg: rgba(170, 59, 255, 0.1); + --accent-border: rgba(170, 59, 255, 0.5); + --social-bg: rgba(244, 243, 236, 0.5); + --shadow: + rgba(0, 0, 0, 0.1) 0 10px 15px -3px, rgba(0, 0, 0, 0.05) 0 4px 6px -2px; + + --sans: system-ui, 'Segoe UI', Roboto, sans-serif; + --heading: system-ui, 'Segoe UI', Roboto, sans-serif; + --mono: ui-monospace, Consolas, monospace; + + font: 18px/145% var(--sans); + letter-spacing: 0.18px; + color-scheme: light dark; + color: var(--text); + background: var(--bg); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + + @media (max-width: 1024px) { + font-size: 16px; + } +} + +@media (prefers-color-scheme: dark) { + :root { + --text: #9ca3af; + --text-h: #f3f4f6; + --bg: #16171d; + --border: #2e303a; + --code-bg: #1f2028; + --accent: #c084fc; + --accent-bg: rgba(192, 132, 252, 0.15); + --accent-border: rgba(192, 132, 252, 0.5); + --social-bg: rgba(47, 48, 58, 0.5); + --shadow: + rgba(0, 0, 0, 0.4) 0 10px 15px -3px, rgba(0, 0, 0, 0.25) 0 4px 6px -2px; + } + + #social .button-icon { + filter: invert(1) brightness(2); + } +} + +body { + margin: 0; +} + +h1, +h2 { + font-family: var(--heading); + font-weight: 500; + color: var(--text-h); +} + +h1 { + font-size: 56px; + letter-spacing: -1.68px; + margin: 32px 0; + @media (max-width: 1024px) { + font-size: 36px; + margin: 20px 0; + } +} +h2 { + font-size: 24px; + line-height: 118%; + letter-spacing: -0.24px; + margin: 0 0 8px; + @media (max-width: 1024px) { + font-size: 20px; + } +} +p { + margin: 0; +} + +code, +.counter { + font-family: var(--mono); + display: inline-flex; + border-radius: 4px; + color: var(--text-h); +} + +code { + font-size: 15px; + line-height: 135%; + padding: 4px 8px; + background: var(--code-bg); +} + +.counter { + font-size: 16px; + padding: 5px 10px; + border-radius: 5px; + color: var(--accent); + background: var(--accent-bg); + border: 2px solid transparent; + transition: border-color 0.3s; + margin-bottom: 24px; + + &:hover { + border-color: var(--accent-border); + } + &:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 2px; + } +} + +.hero { + position: relative; + + .base, + .framework, + .vite { + inset-inline: 0; + margin: 0 auto; + } + + .base { + width: 170px; + position: relative; + z-index: 0; + } + + .framework, + .vite { + position: absolute; + } + + .framework { + z-index: 1; + top: 34px; + height: 28px; + transform: perspective(2000px) rotateZ(300deg) rotateX(44deg) rotateY(39deg) + scale(1.4); + } + + .vite { + z-index: 0; + top: 107px; + height: 26px; + width: auto; + transform: perspective(2000px) rotateZ(300deg) rotateX(40deg) rotateY(39deg) + scale(0.8); + } +} + +#app { + width: 1126px; + max-width: 100%; + margin: 0 auto; + text-align: center; + border-inline: 1px solid var(--border); + min-height: 100svh; + display: flex; + flex-direction: column; + box-sizing: border-box; +} + +#center { + display: flex; + flex-direction: column; + gap: 25px; + place-content: center; + place-items: center; + flex-grow: 1; + + @media (max-width: 1024px) { + padding: 32px 20px 24px; + gap: 18px; + } +} + +#next-steps { + display: flex; + border-top: 1px solid var(--border); + text-align: left; + + & > div { + flex: 1 1 0; + padding: 32px; + @media (max-width: 1024px) { + padding: 24px 20px; + } + } + + .icon { + margin-bottom: 16px; + width: 22px; + height: 22px; + } + + @media (max-width: 1024px) { + flex-direction: column; + text-align: center; + } +} + +#docs { + border-right: 1px solid var(--border); + + @media (max-width: 1024px) { + border-right: none; + border-bottom: 1px solid var(--border); + } +} + +#next-steps ul { + list-style: none; + padding: 0; + display: flex; + gap: 8px; + margin: 32px 0 0; + + .logo { + height: 18px; + } + + a { + color: var(--text-h); + font-size: 16px; + border-radius: 6px; + background: var(--social-bg); + display: flex; + padding: 6px 12px; + align-items: center; + gap: 8px; + text-decoration: none; + transition: box-shadow 0.3s; + + &:hover { + box-shadow: var(--shadow); + } + .button-icon { + height: 18px; + width: 18px; + } + } + + @media (max-width: 1024px) { + margin-top: 20px; + flex-wrap: wrap; + justify-content: center; + + li { + flex: 1 1 calc(50% - 8px); + } + + a { + width: 100%; + justify-content: center; + box-sizing: border-box; + } + } +} + +#spacer { + height: 88px; + border-top: 1px solid var(--border); + @media (max-width: 1024px) { + height: 48px; + } +} + +.ticks { + position: relative; + width: 100%; + + &::before, + &::after { + content: ''; + position: absolute; + top: -4.5px; + border: 5px solid transparent; + } + + &::before { + left: 0; + border-left-color: var(--border); + } + &::after { + right: 0; + border-right-color: var(--border); + } +} diff --git a/backend/tsconfig.app.json b/backend/tsconfig.app.json new file mode 100644 index 0000000..d72aa75 --- /dev/null +++ b/backend/tsconfig.app.json @@ -0,0 +1,15 @@ +{ + "extends": "@vue/tsconfig/tsconfig.dom.json", + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "types": ["vite/client"], + "allowArbitraryExtensions": true, + + /* Linting */ + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"] +} diff --git a/backend/tsconfig.json b/backend/tsconfig.json new file mode 100644 index 0000000..1ffef60 --- /dev/null +++ b/backend/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/backend/tsconfig.node.json b/backend/tsconfig.node.json new file mode 100644 index 0000000..8455dcb --- /dev/null +++ b/backend/tsconfig.node.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "es2023", + "lib": ["ES2023"], + "types": ["node"], + "skipLibCheck": true, + + /* Bundler mode */ + "module": "nodenext", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["vite.config.ts"] +} diff --git a/backend/vite.config.ts b/backend/vite.config.ts new file mode 100644 index 0000000..bbcf80c --- /dev/null +++ b/backend/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [vue()], +}) diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..a1bc364 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,555 @@ +# 文件存储系统 (FileStorageSystem) + +一个类似七牛云的文件存储系统,提供用户注册、项目隔离存储、API文件操作等功能。 + +## 功能概述 + +### 1. 用户系统 +- **用户注册/登录**:支持邮箱/手机号注册,JWT认证 +- **API密钥管理**:每个用户可生成多个API Key,用于程序化访问 +- **用户信息管理**:个人资料、密码修改、账户设置 +- **角色权限**:基于角色的访问控制(RBAC),灵活配置权限 +- **账户安全**:登录失败锁定、密码强度要求 + +### 2. 项目管理 +- **项目创建**:用户可创建多个独立项目(如项目A、项目B) +- **项目隔离**:每个项目拥有独立的存储空间和文件命名空间 +- **项目配额**:可设置每个项目的存储容量限制 +- **项目成员**(可选):支持项目级别的权限管理 + +### 3. 文件管理 +- **文件上传**:支持单文件/批量上传,断点续传 +- **文件下载**:直接下载或生成临时下载链接 +- **文件删除**:软删除/硬删除,回收站机制 +- **文件元数据**:文件名、大小、类型、上传时间、MD5校验 +- **文件目录**:支持虚拟目录结构组织文件 +- **文件访问控制**:公开/私有文件设置,访问密码保护 +- **临时链接**:生成带过期时间的临时访问URL +- **文件预览**:图片、视频、文档在线预览(可选) + +### 4. API接口 +- **RESTful API**:标准REST风格接口 +- **认证方式**:API Key + Secret签名认证 +- **SDK支持**:提供Go/Python/JavaScript SDK(后期) + +### 5. 存储引擎 +- **本地存储**:文件存储在本地文件系统 +- **元数据存储**:SQLite数据库存储文件元数据、用户信息等 +- **文件去重**:基于MD5的文件去重,节省存储空间 +- **存储统计**:项目/用户级别的存储使用统计 +- **流量统计**:记录上传/下载流量,便于计费分析 +- **自定义域名**:支持绑定自定义域名访问文件(可选) + +### 6. 安全特性 +- **API认证**:HMAC-SHA256签名认证 +- **访问控制**:项目级别的文件访问隔离 +- **文件限制**:可配置的文件大小、类型限制 + +### 7. 系统管理 +- **系统设置**:站点名称、站点描述、Logo配置、备案信息等 +- **注册开关**:控制是否开放用户注册功能 +- **站点开关**:站点维护模式,临时关闭站点访问 +- **操作日志**:记录用户关键操作(登录、文件操作、项目管理等) +- **系统日志**:记录系统运行日志(错误、警告、调试信息) +- **存储配置**:默认存储配额、文件大小限制、允许的文件类型 +- **邮件配置**:SMTP设置,用于发送通知邮件(可选) +- **用户管理**:用户列表、封禁/解封、角色分配、密码重置 +- **角色管理**:创建/编辑/删除角色,配置角色权限 +- **权限管理**:查看系统权限点,按模块分组 +- **API限流**:请求频率限制,防止滥用 +- **Webhook回调**:文件上传/删除等事件的HTTP回调通知 +- **数据备份**:数据库和文件的手动/自动备份 + +## 技术架构 + +### 后端技术栈 +- **语言**:Go 1.21+ +- **Web框架**:Gin (高性能HTTP框架) +- **数据库**:SQLite (嵌入式数据库) +- **ORM**:GORM (Go ORM库) +- **配置管理**:Viper +- **日志**:Zap (高性能日志库) +- **认证**:JWT + API Key签名 +- **限流**:golang.org/x/time/rate (令牌桶限流) + +### 项目结构 +``` +filestoragesystem/ +├── docs/ # 文档目录 +├── go/ # Go后端代码 +│ ├── cmd/ # 主程序入口 +│ ├── internal/ # 内部包 +│ │ ├── config/ # 配置管理 +│ │ ├── controller/ # 控制器层 +│ │ │ ├── admin.go # 系统管理控制器 +│ │ │ ├── auth.go # 认证控制器 +│ │ │ ├── file.go # 文件控制器 +│ │ │ ├── project.go # 项目控制器 +│ │ │ ├── role.go # 角色权限控制器 +│ │ │ ├── stats.go # 统计控制器 +│ │ │ ├── user.go # 用户管理控制器 +│ │ │ └── webhook.go # Webhook控制器 +│ │ ├── middleware/ # 中间件 +│ │ │ ├── auth.go # 认证中间件(JWT/API Key) +│ │ │ ├── permission.go # 权限检查中间件 +│ │ │ ├── logger.go # 日志中间件 +│ │ │ ├── cors.go # 跨域中间件 +│ │ │ ├── rate_limit.go # 限流中间件 +│ │ │ └── site.go # 站点状态中间件 +│ │ ├── model/ # 数据模型 +│ │ ├── repository/ # 数据访问层 +│ │ ├── service/ # 业务逻辑层 +│ │ └── utils/ # 工具函数 +│ ├── pkg/ # 公共包 +│ ├── storage/ # 文件存储目录 +│ ├── go.mod # Go模块文件 +│ └── main.go # 程序入口 +├── backend/ # 前端项目(现有) +└── README.md # 项目说明 +``` + +## 数据库设计 + +### 用户表 (users) +```sql +CREATE TABLE users ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + username VARCHAR(50) UNIQUE NOT NULL, + email VARCHAR(100) UNIQUE NOT NULL, + password_hash VARCHAR(255) NOT NULL, + role_id INTEGER DEFAULT 1, -- 关联角色表 + status INTEGER DEFAULT 1, -- 1:正常 0:禁用 + storage_used BIGINT DEFAULT 0, -- 已用存储空间(字节) + storage_limit BIGINT DEFAULT 10737418240, -- 存储限制(默认10GB) + last_login_at DATETIME, + last_login_ip VARCHAR(50), + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (role_id) REFERENCES roles(id) +); +``` + +### 角色表 (roles) +```sql +CREATE TABLE roles ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name VARCHAR(50) UNIQUE NOT NULL, -- 角色名称 + code VARCHAR(50) UNIQUE NOT NULL, -- 角色编码:super_admin/admin/user + description VARCHAR(255), -- 角色描述 + is_system INTEGER DEFAULT 0, -- 是否系统内置角色(不可删除) + status INTEGER DEFAULT 1, -- 1:启用 0:禁用 + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP +); + +-- 默认角色 +INSERT INTO roles (name, code, description, is_system) VALUES +('超级管理员', 'super_admin', '系统超级管理员,拥有所有权限', 1), +('管理员', 'admin', '系统管理员,可管理用户和内容', 1), +('普通用户', 'user', '普通用户,管理自己的项目和文件', 1); +``` + +### 权限表 (permissions) +```sql +CREATE TABLE permissions ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name VARCHAR(50) NOT NULL, -- 权限名称 + code VARCHAR(100) UNIQUE NOT NULL, -- 权限编码 + module VARCHAR(50) NOT NULL, -- 所属模块:user/project/file/system + description VARCHAR(255), -- 权限描述 + created_at DATETIME DEFAULT CURRENT_TIMESTAMP +); + +-- 默认权限 +INSERT INTO permissions (name, code, module, description) VALUES +-- 用户管理 +('查看用户列表', 'user:list', 'user', '查看所有用户列表'), +('查看用户详情', 'user:detail', 'user', '查看用户详细信息'), +('创建用户', 'user:create', 'user', '创建新用户'), +('编辑用户', 'user:edit', 'user', '编辑用户信息'), +('删除用户', 'user:delete', 'user', '删除用户'), +('禁用用户', 'user:disable', 'user', '禁用/启用用户'), +('分配角色', 'user:assign_role', 'user', '修改用户角色'), +('重置密码', 'user:reset_password', 'user', '重置用户密码'), +-- 项目管理 +('查看项目列表', 'project:list', 'project', '查看项目列表'), +('创建项目', 'project:create', 'project', '创建新项目'), +('编辑项目', 'project:edit', 'project', '编辑项目信息'), +('删除项目', 'project:delete', 'project', '删除项目'), +-- 文件管理 +('上传文件', 'file:upload', 'file', '上传文件'), +('下载文件', 'file:download', 'file', '下载文件'), +('删除文件', 'file:delete', 'file', '删除文件'), +('查看文件列表', 'file:list', 'file', '查看文件列表'), +-- 系统管理 +('查看系统设置', 'system:settings:view', 'system', '查看系统设置'), +('修改系统设置', 'system:settings:edit', 'system', '修改系统设置'), +('查看操作日志', 'system:logs:operation', 'system', '查看操作日志'), +('查看系统日志', 'system:logs:system', 'system', '查看系统日志'), +('清理日志', 'system:logs:cleanup', 'system', '清理历史日志'); +``` + +### 角色权限关联表 (role_permissions) +```sql +CREATE TABLE role_permissions ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + role_id INTEGER NOT NULL, + permission_id INTEGER NOT NULL, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (role_id) REFERENCES roles(id) ON DELETE CASCADE, + FOREIGN KEY (permission_id) REFERENCES permissions(id) ON DELETE CASCADE, + UNIQUE(role_id, permission_id) +); + +-- 超级管理员拥有所有权限 +INSERT INTO role_permissions (role_id, permission_id) +SELECT 1, id FROM permissions; + +-- 管理员权限 +INSERT INTO role_permissions (role_id, permission_id) +SELECT 2, id FROM permissions WHERE code NOT IN ('user:delete', 'user:assign_role', 'system:settings:edit'); + +-- 普通用户权限 +INSERT INTO role_permissions (role_id, permission_id) +SELECT 3, id FROM permissions WHERE code IN ('project:list', 'project:create', 'project:edit', 'project:delete', 'file:upload', 'file:download', 'file:delete', 'file:list'); +``` + +### API密钥表 (api_keys) +```sql +CREATE TABLE api_keys ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL, + access_key VARCHAR(32) UNIQUE NOT NULL, + secret_key VARCHAR(64) NOT NULL, + name VARCHAR(50), -- 密钥名称 + status INTEGER DEFAULT 1, + last_used_at DATETIME, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (user_id) REFERENCES users(id) +); +``` + +### 项目表 (projects) +```sql +CREATE TABLE projects ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL, + name VARCHAR(100) NOT NULL, + description TEXT, + storage_used BIGINT DEFAULT 0, + storage_limit BIGINT DEFAULT 5368709120, -- 默认5GB + status INTEGER DEFAULT 1, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (user_id) REFERENCES users(id), + UNIQUE(user_id, name) +); +``` + +### 文件表 (files) +```sql +CREATE TABLE files ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + project_id INTEGER NOT NULL, + user_id INTEGER NOT NULL, + filename VARCHAR(255) NOT NULL, -- 原始文件名 + stored_path VARCHAR(500) NOT NULL, -- 存储路径 + size BIGINT NOT NULL, + mime_type VARCHAR(100), + md5 VARCHAR(32), + storage_type INTEGER DEFAULT 1, -- 1:本地存储 + visibility INTEGER DEFAULT 1, -- 1:私有 2:公开 + download_count BIGINT DEFAULT 0, -- 下载次数 + status INTEGER DEFAULT 1, -- 1:正常 0:已删除 + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, + deleted_at DATETIME, -- 软删除时间 + FOREIGN KEY (project_id) REFERENCES projects(id), + FOREIGN KEY (user_id) REFERENCES users(id) +); +``` + +### 临时链接表 (temp_links) +```sql +CREATE TABLE temp_links ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + file_id INTEGER NOT NULL, + user_id INTEGER NOT NULL, + token VARCHAR(64) UNIQUE NOT NULL, -- 访问令牌 + password VARCHAR(50), -- 访问密码(可选) + max_count INTEGER DEFAULT 0, -- 最大访问次数,0为不限 + used_count INTEGER DEFAULT 0, -- 已访问次数 + expires_at DATETIME NOT NULL, -- 过期时间 + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (file_id) REFERENCES files(id), + FOREIGN KEY (user_id) REFERENCES users(id) +); +``` + +### 流量统计表 (traffic_logs) +```sql +CREATE TABLE traffic_logs ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL, + project_id INTEGER, + file_id INTEGER, + type INTEGER NOT NULL, -- 1:上传 2:下载 + size BIGINT NOT NULL, -- 流量大小(字节) + ip VARCHAR(50), + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (user_id) REFERENCES users(id) +); + +CREATE INDEX idx_traffic_logs_user ON traffic_logs(user_id); +CREATE INDEX idx_traffic_logs_created ON traffic_logs(created_at); +``` + +### Webhook配置表 (webhooks) +```sql +CREATE TABLE webhooks ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL, + project_id INTEGER, -- 为空则为用户级别 + url VARCHAR(500) NOT NULL, + secret VARCHAR(64), -- 签名密钥 + events VARCHAR(255) NOT NULL, -- 触发事件:file.upload,file.delete等 + status INTEGER DEFAULT 1, -- 1:启用 0:禁用 + last_triggered_at DATETIME, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (user_id) REFERENCES users(id) +); +``` + +### 系统设置表 (system_settings) +```sql +CREATE TABLE system_settings ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + key VARCHAR(100) UNIQUE NOT NULL, -- 设置键名 + value TEXT, -- 设置值 + description VARCHAR(255), -- 设置描述 + group_name VARCHAR(50) DEFAULT 'general', -- 设置分组 + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP +); + +-- 默认设置数据 +INSERT INTO system_settings (key, value, description, group_name) VALUES +('site_name', '文件存储系统', '站点名称', 'site'), +('site_description', '高效、安全的文件存储服务', '站点描述', 'site'), +('site_logo', '/assets/logo.png', '站点Logo路径', 'site'), +('site_icp', '', 'ICP备案号', 'site'), +('site_copyright', '© 2024 FileStorageSystem', '版权信息', 'site'), +('register_enabled', 'true', '是否开放注册', 'feature'), +('site_enabled', 'true', '站点是否启用', 'feature'), +('site_maintenance_msg', '系统维护中,请稍后再访问', '维护提示信息', 'feature'), +('default_storage_limit', '10737418240', '默认用户存储配额(字节)', 'storage'), +('default_project_limit', '5368709120', '默认项目存储配额(字节)', 'storage'), +('max_file_size', '104857600', '单文件最大限制(字节)', 'storage'), +('allowed_file_types', '*', '允许的文件类型(*表示所有)', 'storage'), +('api_rate_limit', '100', 'API请求频率限制(次/分钟)', 'api'), +('temp_link_max_age', '3600', '临时链接默认有效期(秒)', 'api'), +('webhook_enabled', 'true', '是否启用Webhook回调', 'api'); +``` + +### 操作日志表 (operation_logs) +```sql +CREATE TABLE operation_logs ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER, -- 操作用户ID,系统操作为NULL + username VARCHAR(50), -- 用户名,便于查询 + action VARCHAR(50) NOT NULL, -- 操作类型:login/logout/upload/download/delete/create/update + resource_type VARCHAR(50), -- 资源类型:user/project/file/apikey + resource_id INTEGER, -- 资源ID + resource_name VARCHAR(255), -- 资源名称 + ip VARCHAR(50), -- 操作IP + user_agent VARCHAR(500), -- 用户代理 + request_path VARCHAR(500), -- 请求路径 + request_method VARCHAR(10), -- 请求方法 + status INTEGER DEFAULT 1, -- 1:成功 0:失败 + error_message TEXT, -- 错误信息 + extra_data TEXT, -- 额外数据(JSON) + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (user_id) REFERENCES users(id) +); + +CREATE INDEX idx_operation_logs_user ON operation_logs(user_id); +CREATE INDEX idx_operation_logs_action ON operation_logs(action); +CREATE INDEX idx_operation_logs_created ON operation_logs(created_at); +``` + +### 系统日志表 (system_logs) +```sql +CREATE TABLE system_logs ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + level VARCHAR(10) NOT NULL, -- 日志级别:debug/info/warn/error/fatal + module VARCHAR(50), -- 模块名称:server/database/storage/auth + message TEXT NOT NULL, -- 日志内容 + stack_trace TEXT, -- 堆栈信息 + request_id VARCHAR(50), -- 请求ID,用于链路追踪 + ip VARCHAR(50), -- 相关IP + extra_data TEXT, -- 额外数据(JSON) + created_at DATETIME DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX idx_system_logs_level ON system_logs(level); +CREATE INDEX idx_system_logs_module ON system_logs(module); +CREATE INDEX idx_system_logs_created ON system_logs(created_at); +``` + +## API设计 + +### 认证相关 +- `POST /api/v1/auth/register` - 用户注册 +- `POST /api/v1/auth/login` - 用户登录 +- `POST /api/v1/auth/apikeys` - 生成API密钥 +- `GET /api/v1/auth/apikeys` - 获取API密钥列表 +- `DELETE /api/v1/auth/apikeys/:id` - 删除API密钥 + +### 项目管理 +- `POST /api/v1/projects` - 创建项目 +- `GET /api/v1/projects` - 获取项目列表 +- `GET /api/v1/projects/:id` - 获取项目详情 +- `PUT /api/v1/projects/:id` - 更新项目信息 +- `DELETE /api/v1/projects/:id` - 删除项目 + +### 文件操作 (需要API Key认证) +- `POST /api/v1/files/upload` - 上传文件 +- `GET /api/v1/files/:id` - 获取文件信息 +- `GET /api/v1/files/:id/download` - 下载文件 +- `DELETE /api/v1/files/:id` - 删除文件 +- `GET /api/v1/files` - 获取文件列表 +- `POST /api/v1/files/:id/temp-link` - 生成临时访问链接 +- `GET /api/v1/files/temp/:token` - 通过临时链接访问文件 + +### 用户管理 (需要管理员权限) +- `GET /api/v1/admin/users` - 获取用户列表 +- `GET /api/v1/admin/users/:id` - 获取用户详情 +- `POST /api/v1/admin/users` - 创建用户 +- `PUT /api/v1/admin/users/:id` - 编辑用户信息 +- `DELETE /api/v1/admin/users/:id` - 删除用户 +- `PUT /api/v1/admin/users/:id/status` - 启用/禁用用户 +- `PUT /api/v1/admin/users/:id/role` - 修改用户角色 +- `POST /api/v1/admin/users/:id/reset-password` - 重置用户密码 + +### 角色权限管理 (需要超级管理员权限) +- `GET /api/v1/admin/roles` - 获取角色列表 +- `GET /api/v1/admin/roles/:id` - 获取角色详情(含权限) +- `POST /api/v1/admin/roles` - 创建角色 +- `PUT /api/v1/admin/roles/:id` - 编辑角色 +- `DELETE /api/v1/admin/roles/:id` - 删除角色(系统角色不可删) +- `PUT /api/v1/admin/roles/:id/permissions` - 配置角色权限 +- `GET /api/v1/admin/permissions` - 获取权限列表(按模块分组) + +### 存储统计 +- `GET /api/v1/stats/user` - 用户存储统计 +- `GET /api/v1/stats/project/:id` - 项目存储统计 +- `GET /api/v1/stats/traffic` - 流量统计 + +### 系统管理 (需要管理员权限) +- `GET /api/v1/admin/settings` - 获取系统设置 +- `PUT /api/v1/admin/settings` - 更新系统设置 +- `GET /api/v1/admin/settings/:group` - 获取分组设置 +- `PUT /api/v1/admin/settings/:group` - 更新分组设置 + +### 日志管理 (需要管理员权限) +- `GET /api/v1/admin/logs/operation` - 获取操作日志列表 +- `GET /api/v1/admin/logs/operation/:id` - 获取操作日志详情 +- `GET /api/v1/admin/logs/system` - 获取系统日志列表 +- `GET /api/v1/admin/logs/system/:id` - 获取系统日志详情 +- `DELETE /api/v1/admin/logs/cleanup` - 清理历史日志 + +### 站点状态 (公开接口) +- `GET /api/v1/status` - 获取站点状态(维护模式、注册开关等) + +## 开发计划 + +### 阶段一:基础框架 +1. 初始化Go项目,配置依赖 +2. 实现基础路由和中间件 +3. SQLite数据库连接和迁移 +4. 用户注册/登录功能 + +### 阶段二:核心功能 +1. API密钥管理 +2. 项目CRUD操作 +3. 文件上传功能 +4. 文件下载功能 + +### 阶段三:系统管理 +1. 系统设置管理(站点信息、注册开关、站点开关) +2. 操作日志记录和查询 +3. 系统日志记录和查询 +4. 角色权限管理(RBAC) +5. 用户管理(列表、封禁、角色分配、密码重置) +6. 权限检查中间件 + +### 阶段四:完善功能 +1. 文件删除和回收站 +2. 存储统计和流量统计 +3. 临时访问链接 +4. API限流 +5. 错误处理和日志 +6. 配置文件支持 + +### 阶段五:优化和测试 +1. 单元测试 +2. 性能优化 +3. API文档生成 +4. 部署脚本 + +## 快速开始 + +### 环境要求 +- Go 1.21+ +- SQLite 3 + +### 安装和运行 +```bash +# 进入Go后端目录 +cd go + +# 安装依赖 +go mod tidy + +# 运行程序 +go run main.go +``` + +### 配置文件 +```yaml +# config.yaml +server: + port: 8080 + mode: debug # debug/release + +database: + driver: sqlite + source: ./storage/data.db + +storage: + root: ./storage/files + max_size: 100MB # 单文件最大限制 + +admin: + username: admin + password: admin123 # 首次运行后请修改 + email: admin@example.com + role: super_admin # 默认角色 + +jwt: + secret: your-secret-key # 请修改为随机密钥 + expire: 24h # Token过期时间 +``` + +## 注意事项 + +1. **安全性**:生产环境需要修改JWT密钥,启用HTTPS +2. **性能**:大量文件建议使用对象存储服务(如MinIO、阿里云OSS) +3. **备份**:定期备份SQLite数据库和文件存储目录 +4. **监控**:建议添加Prometheus监控指标 +5. **日志管理**:定期清理过期日志,避免数据库过大 +6. **站点维护**:开启维护模式时,确保管理员仍可访问后台 +7. **权限控制**:管理员账户需在配置文件中预设,避免权限滥用 + +## 许可证 + +MIT License \ No newline at end of file