diff --git a/app.vue b/app.vue index 2d4d510..41b018f 100644 --- a/app.vue +++ b/app.vue @@ -18,7 +18,7 @@ import "/style/default.scss"; useHead({ title: '杪 Blog', meta: [ - { name: 'description', content: '杪儿的博客' } + { name: 'description', content: '杪儿的博客', lang: 'zh-CN' } ], }) diff --git a/components/icons/copy.vue b/components/icons/copy.vue new file mode 100644 index 0000000..1fa5699 --- /dev/null +++ b/components/icons/copy.vue @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/components/icons/upload.vue b/components/icons/upload.vue new file mode 100644 index 0000000..a7274bc --- /dev/null +++ b/components/icons/upload.vue @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/package.json b/package.json index 2d27d18..cd323a1 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "node-fetch": "^3.3.2", "nuxt": "^3.15.1", "pinia": "^2.3.0", + "qrcode": "^1.5.4", "unplugin-auto-import": "^19.0.0", "unplugin-vue-components": "^28.0.0", "vue": "^3.4.21", @@ -44,6 +45,7 @@ "@types/better-sqlite3": "^7.6.12", "@types/jsonwebtoken": "^9.0.7", "@types/markdown-it": "^14.1.2", + "@types/qrcode": "^1.5.5", "element-plus": "^2.9.3", "sass": "^1.83.1", "vite-plugin-mkcert": "^1.17.6" diff --git a/pages/index.vue b/pages/index.vue index 94fe573..5b5c147 100644 --- a/pages/index.vue +++ b/pages/index.vue @@ -91,8 +91,12 @@ const PageContent = reactive([ }, { name: 'MiaoBlog', - description: '施工中,不过不要抱有太大期待', + description: '施工中,不过最好不要抱有太大期待,毕竟还是以实现功能为目标去做的', links: [ + { + name: "gitea", + url: "https://nj.miaospring.top:3001/539943419/miaoBlog.git" + }, { name: "github", url: "https://github.com/Zioywishing/miaoBlog" @@ -134,6 +138,16 @@ const PageContent = reactive([ } ] }, + { + name: '喵喵图床', + description: '其实就是一个很简陋的图床,上传,下载,没了', + nuxtLinks: [ + { + name: 'link', + to: `/tools/imgBed` + } + ] + }, ] }, { subTitle: '学习资料', diff --git a/pages/tools/imgBed.vue b/pages/tools/imgBed.vue new file mode 100644 index 0000000..9d9559d --- /dev/null +++ b/pages/tools/imgBed.vue @@ -0,0 +1,196 @@ + + + + + + + + 拖拽 图片 至此或单击此处选择文件 + + + + + + + 过期时间(小时) + + + + + 点击上传图片 + + + + + + + + + + 下载 + + 分享 + + + + + + + + + {{ shareDialogData.url }} + + + + + + + + + + + + \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2a5b23f..19389f6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -50,6 +50,9 @@ importers: pinia: specifier: ^2.3.0 version: 2.3.0(typescript@5.7.3)(vue@3.5.13(typescript@5.7.3)) + qrcode: + specifier: ^1.5.4 + version: 1.5.4 unplugin-auto-import: specifier: ^19.0.0 version: 19.0.0(@nuxt/kit@3.15.1(magicast@0.3.5)(rollup@4.30.1))(@vueuse/core@9.13.0(vue@3.5.13(typescript@5.7.3)))(rollup@4.30.1) @@ -105,6 +108,9 @@ importers: '@types/markdown-it': specifier: ^14.1.2 version: 14.1.2 + '@types/qrcode': + specifier: ^1.5.5 + version: 1.5.5 element-plus: specifier: ^2.9.3 version: 2.9.3(vue@3.5.13(typescript@5.7.3)) @@ -1172,6 +1178,9 @@ packages: '@types/parse-path@7.0.3': resolution: {integrity: sha512-LriObC2+KYZD3FzCrgWGv/qufdUy4eXrxcLgQMfYXgPbLIecKIsVBaQgUPmxSSLcjmYbDTQbMgr6qr6l/eb7Bg==} + '@types/qrcode@1.5.5': + resolution: {integrity: sha512-CdfBi/e3Qk+3Z/fXYShipBT13OJ2fDO2Q2w5CIP5anLTLIndQG9z6P1cnm+8zCWSpm5dnxMFd/uREtb0EXuQzg==} + '@types/resolve@1.20.2': resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} @@ -1466,6 +1475,10 @@ packages: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} + camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + caniuse-api@3.0.0: resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} @@ -1505,6 +1518,9 @@ packages: resolution: {integrity: sha512-5mOlNS0mhX0707P2I0aZ2V/cmHUEO/fL7VFLqszkhUsxt7RwnmrInf/eEQKlf5GzvYeHIjT+Ov1HRfNmymlG0w==} engines: {node: '>=18'} + cliui@6.0.0: + resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} + cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} @@ -1699,6 +1715,10 @@ packages: supports-color: optional: true + decamelize@1.2.0: + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} + engines: {node: '>=0.10.0'} + decompress-response@6.0.0: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} engines: {node: '>=10'} @@ -1768,6 +1788,9 @@ packages: resolution: {integrity: sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==} engines: {node: '>=0.3.1'} + dijkstrajs@1.0.3: + resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==} + dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} @@ -1932,6 +1955,10 @@ packages: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + flatted@3.3.2: resolution: {integrity: sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==} @@ -2310,6 +2337,10 @@ packages: resolution: {integrity: sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==} engines: {node: '>=14'} + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + lodash-es@4.17.21: resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} @@ -2647,6 +2678,18 @@ packages: peerDependencies: typescript: ^5.x + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} @@ -2675,6 +2718,10 @@ packages: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + path-is-absolute@1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} @@ -2734,6 +2781,10 @@ packages: resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} engines: {node: '>=4'} + pngjs@5.0.0: + resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==} + engines: {node: '>=10.13.0'} + postcss-calc@10.1.0: resolution: {integrity: sha512-uQ/LDGsf3mgsSUEXmAt3VsCSHR3aKqtEIkmB+4PhzYwRYOW5MZs/GhCCFpsOtJJkP6EC6uGipbrnaTjqaJZcJw==} engines: {node: ^18.12 || ^20.9 || >=22.0} @@ -2944,6 +2995,11 @@ packages: resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} engines: {node: '>=6'} + qrcode@1.5.4: + resolution: {integrity: sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==} + engines: {node: '>=10.13.0'} + hasBin: true + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -3005,6 +3061,9 @@ packages: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} + require-main-filename@2.0.0: + resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} + resolve-from@5.0.0: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} @@ -3087,6 +3146,9 @@ packages: resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==} engines: {node: '>= 0.8.0'} + set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} @@ -3682,6 +3744,9 @@ packages: whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + which-module@2.0.1: + resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -3692,6 +3757,10 @@ packages: engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} hasBin: true + wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -3715,6 +3784,9 @@ packages: utf-8-validate: optional: true + y18n@4.0.3: + resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} + y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -3737,10 +3809,18 @@ packages: engines: {node: '>= 14'} hasBin: true + yargs-parser@18.1.3: + resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} + engines: {node: '>=6'} + yargs-parser@21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} + yargs@15.4.1: + resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} + engines: {node: '>=8'} + yargs@17.7.2: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} @@ -4939,6 +5019,10 @@ snapshots: '@types/parse-path@7.0.3': {} + '@types/qrcode@1.5.5': + dependencies: + '@types/node': 22.10.5 + '@types/resolve@1.20.2': {} '@types/web-bluetooth@0.0.16': {} @@ -5335,6 +5419,8 @@ snapshots: cac@6.7.14: {} + camelcase@5.3.1: {} + caniuse-api@3.0.0: dependencies: browserslist: 4.24.4 @@ -5383,6 +5469,12 @@ snapshots: is-wsl: 3.1.0 is64bit: 2.0.0 + cliui@6.0.0: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + cliui@8.0.1: dependencies: string-width: 4.2.3 @@ -5566,6 +5658,8 @@ snapshots: optionalDependencies: supports-color: 9.4.0 + decamelize@1.2.0: {} + decompress-response@6.0.0: dependencies: mimic-response: 3.1.0 @@ -5607,6 +5701,8 @@ snapshots: diff@7.0.0: {} + dijkstrajs@1.0.3: {} + dom-serializer@2.0.0: dependencies: domelementtype: 2.3.0 @@ -5827,6 +5923,11 @@ snapshots: dependencies: to-regex-range: 5.0.1 + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + flatted@3.3.2: {} follow-redirects@1.15.9(debug@4.4.0): @@ -6213,6 +6314,10 @@ snapshots: mlly: 1.7.4 pkg-types: 1.3.0 + locate-path@5.0.0: + dependencies: + p-locate: 4.1.0 + lodash-es@4.17.21: {} lodash-unified@1.0.3(@types/lodash-es@4.17.12)(lodash-es@4.17.21)(lodash@4.17.21): @@ -6703,6 +6808,16 @@ snapshots: transitivePeerDependencies: - encoding + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + + p-locate@4.1.0: + dependencies: + p-limit: 2.3.0 + + p-try@2.2.0: {} + package-json-from-dist@1.0.1: {} package-manager-detector@0.2.8: {} @@ -6731,6 +6846,8 @@ snapshots: parseurl@1.3.3: {} + path-exists@4.0.0: {} + path-is-absolute@1.0.1: {} path-key@3.1.1: {} @@ -6776,6 +6893,8 @@ snapshots: pluralize@8.0.0: {} + pngjs@5.0.0: {} + postcss-calc@10.1.0(postcss@8.4.49): dependencies: postcss: 8.4.49 @@ -6980,6 +7099,12 @@ snapshots: punycode.js@2.3.1: {} + qrcode@1.5.4: + dependencies: + dijkstrajs: 1.0.3 + pngjs: 5.0.0 + yargs: 15.4.1 + queue-microtask@1.2.3: {} queue-tick@1.0.1: {} @@ -7048,6 +7173,8 @@ snapshots: require-from-string@2.0.2: {} + require-main-filename@2.0.0: {} + resolve-from@5.0.0: {} resolve@1.22.10: @@ -7157,6 +7284,8 @@ snapshots: transitivePeerDependencies: - supports-color + set-blocking@2.0.0: {} + setprototypeof@1.2.0: {} shebang-command@2.0.0: @@ -7732,6 +7861,8 @@ snapshots: tr46: 0.0.3 webidl-conversions: 3.0.1 + which-module@2.0.1: {} + which@2.0.2: dependencies: isexe: 2.0.0 @@ -7740,6 +7871,12 @@ snapshots: dependencies: isexe: 2.0.0 + wrap-ansi@6.2.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 @@ -7756,6 +7893,8 @@ snapshots: ws@8.18.0: {} + y18n@4.0.3: {} + y18n@5.0.8: {} yallist@3.1.1: {} @@ -7768,8 +7907,27 @@ snapshots: yaml@2.7.0: {} + yargs-parser@18.1.3: + dependencies: + camelcase: 5.3.1 + decamelize: 1.2.0 + yargs-parser@21.1.1: {} + yargs@15.4.1: + dependencies: + cliui: 6.0.0 + decamelize: 1.2.0 + find-up: 4.1.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + require-main-filename: 2.0.0 + set-blocking: 2.0.0 + string-width: 4.2.3 + which-module: 2.0.1 + y18n: 4.0.3 + yargs-parser: 18.1.3 + yargs@17.7.2: dependencies: cliui: 8.0.1 diff --git a/server/api/posts/getPostContent.ts b/server/api/posts/getPostContent.ts index b8a090b..d9bbdef 100644 --- a/server/api/posts/getPostContent.ts +++ b/server/api/posts/getPostContent.ts @@ -1,5 +1,4 @@ // @ts-ignore -import testData from "~/public/testData/test.md" import DB from 'better-sqlite3'; import { postItem } from "~/types/post"; diff --git a/server/api/tools/imgBed/download/[hash].ts b/server/api/tools/imgBed/download/[hash].ts new file mode 100644 index 0000000..cd744a9 --- /dev/null +++ b/server/api/tools/imgBed/download/[hash].ts @@ -0,0 +1,39 @@ +// @ts-ignore +import DB from 'better-sqlite3'; +import { defineEventHandler, getRouterParam } from 'h3'; +import fs from 'fs'; +import { sendStream } from 'h3'; + +export default defineEventHandler(async (event) => { + try { + const hash = getRouterParam(event, 'hash'); + if (!hash) { + return { code: 400, msg: 'Invalid hash parameter' }; + } + const db = new DB('./userData/db/imgBed.db'); + // 从数据库中查询文件信息 + const fileInfo = db.prepare(` + SELECT name, expire FROM imgBed WHERE hash =? + `).get(hash) as { name: string, expire: number }; + db.close(); + + if (!fileInfo) { + return { code: 404, msg: 'File not found' }; + } + + const fileName = fileInfo.name; + const filePath = `./userData/imgBed/${hash}`; + // 检查文件是否存在 + if (!fs.existsSync(filePath)) { + return { code: 404, msg: 'File not found on disk' }; + } + const fileStream = fs.createReadStream(filePath); + + setResponseHeader(event, 'Content-Disposition', `attachment; filename="${fileName}"`); + // 发送文件流 + return sendStream(event, fileStream); + } catch (error) { + console.error(error); + return { code: 500, msg: 'Internal Server Error' }; + } +}); \ No newline at end of file diff --git a/server/api/tools/imgBed/upload.ts b/server/api/tools/imgBed/upload.ts new file mode 100644 index 0000000..e2de398 --- /dev/null +++ b/server/api/tools/imgBed/upload.ts @@ -0,0 +1,39 @@ +// @ts-ignore +import DB from 'better-sqlite3'; +import { defineEventHandler, readBody } from 'h3'; +import crypto from 'crypto'; +import fs from 'fs'; + +export default defineEventHandler(async (event) => { + try { + const data = await readFormData(event) + const file = data.get('file') as File + const expire = data.get('expire') as string + console.log({ + file: file.name, + expire, + }) + const fileBuffer = Buffer.from(await file.arrayBuffer()); + // 计算文件的 MD5 哈希值 + const hash = crypto.createHash('md5').update(fileBuffer).digest('hex'); + const fileName = file.name; + const filePath = `./userData/imgBed/${hash}`; + // 将文件写入磁盘 + await fs.promises.writeFile(filePath, fileBuffer); + // 获取文件大小 + const fileSize = fileBuffer.length; + + const db = new DB('./userData/db/imgBed.db'); + // 插入数据到数据库 + db.prepare(` + INSERT INTO imgBed (name, hash, size, expire) + VALUES (?,?,?,?) + `).run([fileName, hash, fileSize, expire]); + db.close(); + + return { code: 200, hash, fileName, fileSize, expire: Number(expire) + Date.now() }; + } catch (error) { + console.error(error); + return { code: 500, msg: 'Internal Server Error' }; + } +}); \ No newline at end of file diff --git a/server/middleware/auth.ts b/server/middleware/auth.ts index 810cc93..0258cfa 100644 --- a/server/middleware/auth.ts +++ b/server/middleware/auth.ts @@ -7,12 +7,10 @@ const pathNeedAuth = serverConfig.auth.api export default defineEventHandler(async (event) => { const path = getRequestURL(event).pathname - // console.log(path) if (pathNeedAuth.has(path)) { const key = getKey() // @ts-ignore const authHeader = event.headers.get('authorization'); - // console.log(authHeader) if (!authHeader || !authHeader.startsWith('Bearer ')) { throw createError({ statusCode: 401, statusMessage: 'Unauthorized' }) } @@ -20,7 +18,6 @@ export default defineEventHandler(async (event) => { try { const decoded = jwt.verify(token, key) as { userId: number, password: string, username: string, exp: number } if (decoded.exp > Date.now()) { - // console.log('token expired') throw createError({ statusCode: 401, statusMessage: 'Token invalid or expired', data: { err: 'token expired', token } }) } // console.log(decoded) diff --git a/server/utils/initUserData.ts b/server/utils/initUserData.ts index 0b59e65..8c91e74 100644 --- a/server/utils/initUserData.ts +++ b/server/utils/initUserData.ts @@ -1,5 +1,6 @@ import mkfileIfNotExist from "./mkfileIfNotExist" -import db from 'better-sqlite3' +import DB from 'better-sqlite3' +import fs from 'fs' let checked = false @@ -14,6 +15,7 @@ function checkUserData() { const dirList = [ './userData/', './userData/db/', + './userData/imgBed/', ] dirList.forEach(mkdirIfNotExist) @@ -24,9 +26,14 @@ function checkUserData() { } function createDB() { - const userDB = new db('./userData/db/user.db'); - const postsDB = new db('./userData/db/posts.db'); + createUserDB(); + createPostsDB(); + createImgBedDB(); +} + +const createUserDB = () => { + const userDB = new DB('./userData/db/user.db'); userDB.exec(` CREATE TABLE IF NOT EXISTS user ( id INTEGER PRIMARY KEY AUTOINCREMENT, @@ -36,7 +43,11 @@ function createDB() { lastLoginTime INTEGER NOT NULL ); `); + userDB.close(); +} +const createPostsDB = () => { + const postsDB = new DB('./userData/db/posts.db'); postsDB.exec(` CREATE TABLE IF NOT EXISTS posts ( id INTEGER PRIMARY KEY AUTOINCREMENT, @@ -49,14 +60,12 @@ function createDB() { FOREIGN KEY(tag_id) REFERENCES tags(id) ); `); - postsDB.exec(` CREATE TABLE IF NOT EXISTS postContent ( id INTEGER PRIMARY KEY AUTOINCREMENT, content TEXT NOT NULL ); `); - postsDB.exec(` CREATE TABLE IF NOT EXISTS tags ( id INTEGER PRIMARY KEY AUTOINCREMENT, @@ -77,9 +86,56 @@ function createDB() { tag15 TEXT ); `); - postsDB.close(); - userDB.close(); } +const createImgBedDB = () => { + const imgBedDB = new DB('./userData/db/imgBed.db'); + imgBedDB.exec(` + CREATE TABLE IF NOT EXISTS imgBed ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + hash TEXT NOT NULL, + size INTEGER NOT NULL, + expire INTEGER NOT NULL + ); + `); + imgBedDB.close(); + setInterval(cleanExpiredFiles_imgBed, 1000 * 30 * 60 ); + // cleanExpiredFiles_imgBed() +} + +const cleanExpiredFiles_imgBed = async () => { + try { + const db = new DB('./userData/db/imgBed.db'); + const currentTime = Date.now(); + // 从数据库中查询过期文件的哈希 + const expiredFiles = db.prepare(` + SELECT hash FROM imgBed WHERE expire + `).all(currentTime) as { + hash: string; + }[]; + db.close(); + + for (const file of expiredFiles) { + const hash = file.hash; + const filePath = useRuntimeConfig().public.userDataPath + `/imgBed/${hash}`; + // 检查文件是否存在 + if (fs.existsSync(filePath)) { + // 删除文件 + fs.unlinkSync(filePath); + } + } + + // 从数据库中删除过期文件的记录 + const db2 = new DB('./userData/db/imgBed.db'); + db2.prepare(` + DELETE FROM imgBed WHERE expire + `).run(currentTime); + db2.close(); + } catch (error) { + console.error('Error cleaning expired files:', error); + } +}; + export default checkUserData \ No newline at end of file