diff --git a/bun.lock b/bun.lock index 59990c8..dbc8b73 100644 --- a/bun.lock +++ b/bun.lock @@ -6,20 +6,24 @@ "name": "person-panel", "dependencies": { "dotenv": "17.4.1", - "drizzle-orm": "^0.45.2", + "drizzle-orm": "0.45.2", "drizzle-pkg": "workspace:*", "drizzle-seed": "0.3.1", - "drizzle-zod": "^0.8.3", - "log4js": "^6.9.1", + "drizzle-zod": "0.8.3", + "log4js": "6.9.1", "logger": "workspace:*", + "mime": "^4.1.0", + "multer": "2.1.1", "nuxt": "4.4.2", "pg": "8.20.0", + "ufo": "1.6.3", "vue": "3.5.32", "vue-router": "5.0.4", - "zod": "^4.3.6", + "zod": "4.3.6", }, "devDependencies": { - "@types/pg": "^8.20.0", + "@types/multer": "2.1.0", + "@types/pg": "8.20.0", "drizzle-kit": "0.31.10", "tsx": "4.21.0", "typescript": "6.0.2", @@ -485,14 +489,34 @@ "@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], + "@types/body-parser": ["@types/body-parser@1.19.6", "", { "dependencies": { "@types/connect": "*", "@types/node": "*" } }, "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g=="], + + "@types/connect": ["@types/connect@3.4.38", "", { "dependencies": { "@types/node": "*" } }, "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug=="], + "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], + "@types/express": ["@types/express@5.0.6", "", { "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^5.0.0", "@types/serve-static": "^2" } }, "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA=="], + + "@types/express-serve-static-core": ["@types/express-serve-static-core@5.1.1", "", { "dependencies": { "@types/node": "*", "@types/qs": "*", "@types/range-parser": "*", "@types/send": "*" } }, "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A=="], + + "@types/http-errors": ["@types/http-errors@2.0.5", "", {}, "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg=="], + + "@types/multer": ["@types/multer@2.1.0", "", { "dependencies": { "@types/express": "*" } }, "sha512-zYZb0+nJhOHtPpGDb3vqPjwpdeGlGC157VpkqNQL+UU2qwoacoQ7MpsAmUptI/0Oa127X32JzWDqQVEXp2RcIA=="], + "@types/node": ["@types/node@25.6.0", "", { "dependencies": { "undici-types": "~7.19.0" } }, "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ=="], "@types/pg": ["@types/pg@8.20.0", "", { "dependencies": { "@types/node": "*", "pg-protocol": "*", "pg-types": "^2.2.0" } }, "sha512-bEPFOaMAHTEP1EzpvHTbmwR8UsFyHSKsRisLIHVMXnpNefSbGA1bD6CVy+qKjGSqmZqNqBDV2azOBo8TgkcVow=="], + "@types/qs": ["@types/qs@6.15.0", "", {}, "sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow=="], + + "@types/range-parser": ["@types/range-parser@1.2.7", "", {}, "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ=="], + "@types/resolve": ["@types/resolve@1.20.2", "", {}, "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q=="], + "@types/send": ["@types/send@1.2.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ=="], + + "@types/serve-static": ["@types/serve-static@2.2.0", "", { "dependencies": { "@types/http-errors": "*", "@types/node": "*" } }, "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ=="], + "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], "@unhead/vue": ["@unhead/vue@2.1.13", "", { "dependencies": { "hookable": "^6.0.1", "unhead": "2.1.13" }, "peerDependencies": { "vue": ">=3.5.18" } }, "sha512-HYy0shaHRnLNW9r85gppO8IiGz0ONWVV3zGdlT8CQ0tbTwixznJCIiyqV4BSV1aIF1jJIye0pd1p/k6Eab8Z/A=="], @@ -555,6 +579,8 @@ "anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="], + "append-field": ["append-field@1.0.0", "", {}, "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw=="], + "archiver": ["archiver@7.0.1", "", { "dependencies": { "archiver-utils": "^5.0.2", "async": "^3.2.4", "buffer-crc32": "^1.0.0", "readable-stream": "^4.0.0", "readdir-glob": "^1.1.2", "tar-stream": "^3.0.0", "zip-stream": "^6.0.1" } }, "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ=="], "archiver-utils": ["archiver-utils@5.0.2", "", { "dependencies": { "glob": "^10.0.0", "graceful-fs": "^4.2.0", "is-stream": "^2.0.1", "lazystream": "^1.0.0", "lodash": "^4.17.15", "normalize-path": "^3.0.0", "readable-stream": "^4.0.0" } }, "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA=="], @@ -611,6 +637,8 @@ "bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="], + "busboy": ["busboy@1.6.0", "", { "dependencies": { "streamsearch": "^1.1.0" } }, "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA=="], + "c12": ["c12@3.3.4", "", { "dependencies": { "chokidar": "^5.0.0", "confbox": "^0.2.4", "defu": "^6.1.6", "dotenv": "^17.3.1", "exsolve": "^1.0.8", "giget": "^3.2.0", "jiti": "^2.6.1", "ohash": "^2.0.11", "pathe": "^2.0.3", "perfect-debounce": "^2.1.0", "pkg-types": "^2.3.0", "rc9": "^3.0.1" }, "peerDependencies": { "magicast": "*" }, "optionalPeers": ["magicast"] }, "sha512-cM0ApFQSBXuourJejzwv/AuPRvAxordTyParRVcHjjtXirtkzM0uK2L9TTn9s0cXZbG7E55jCivRQzoxYmRAlA=="], "cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="], @@ -641,6 +669,8 @@ "compress-commons": ["compress-commons@6.0.2", "", { "dependencies": { "crc-32": "^1.2.0", "crc32-stream": "^6.0.0", "is-stream": "^2.0.1", "normalize-path": "^3.0.0", "readable-stream": "^4.0.0" } }, "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg=="], + "concat-stream": ["concat-stream@2.0.0", "", { "dependencies": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.0.2", "typedarray": "^0.0.6" } }, "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A=="], + "confbox": ["confbox@0.2.4", "", {}, "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ=="], "consola": ["consola@3.4.2", "", {}, "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA=="], @@ -977,6 +1007,8 @@ "mdn-data": ["mdn-data@2.27.1", "", {}, "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ=="], + "media-typer": ["media-typer@0.3.0", "", {}, "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="], + "merge-stream": ["merge-stream@2.0.0", "", {}, "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="], "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], @@ -985,9 +1017,9 @@ "mime": ["mime@4.1.0", "", { "bin": { "mime": "bin/cli.js" } }, "sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw=="], - "mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], + "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], - "mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="], + "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], "mimic-fn": ["mimic-fn@4.0.0", "", {}, "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw=="], @@ -1007,6 +1039,8 @@ "muggle-string": ["muggle-string@0.4.1", "", {}, "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ=="], + "multer": ["multer@2.1.1", "", { "dependencies": { "append-field": "^1.0.0", "busboy": "^1.6.0", "concat-stream": "^2.0.0", "type-is": "^1.6.18" } }, "sha512-mo+QTzKlx8R7E5ylSXxWzGoXoZbOsRMpyitcht8By2KHvMbf3tjwosZ/Mu/XYU6UuJ3VZnODIrak5ZrPiPyB6A=="], + "mysql2": ["mysql2@3.22.0", "", { "dependencies": { "aws-ssl-profiles": "^1.1.2", "denque": "^2.1.0", "generate-function": "^2.3.1", "iconv-lite": "^0.7.2", "long": "^5.3.2", "lru.min": "^1.1.4", "named-placeholders": "^1.1.6", "sql-escaper": "^1.3.3" }, "peerDependencies": { "@types/node": ">= 8" } }, "sha512-4jaJYBObj7FhD3lnZhqX1yDMuZN4mQNz+IolDySDXT7fbozMBpeGQNcuWXKUqo4ahkAEfkjUHPjnwuDI0/6VKw=="], "named-placeholders": ["named-placeholders@1.1.6", "", { "dependencies": { "lru.min": "^1.1.0" } }, "sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w=="], @@ -1193,7 +1227,7 @@ "rc9": ["rc9@3.0.1", "", { "dependencies": { "defu": "^6.1.6", "destr": "^2.0.5" } }, "sha512-gMDyleLWVE+i6Sgtc0QbbY6pEKqYs97NGi6isHQPqYlLemPoO8dxQ3uGi0f4NiP98c+jMW6cG1Kx9dDwfvqARQ=="], - "readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="], + "readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], "readdir-glob": ["readdir-glob@1.1.3", "", { "dependencies": { "minimatch": "^5.1.0" } }, "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA=="], @@ -1285,6 +1319,8 @@ "streamroller": ["streamroller@3.1.5", "", { "dependencies": { "date-format": "^4.0.14", "debug": "^4.3.4", "fs-extra": "^8.1.0" } }, "sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw=="], + "streamsearch": ["streamsearch@1.1.0", "", {}, "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg=="], + "streamx": ["streamx@2.25.0", "", { "dependencies": { "events-universal": "^1.0.0", "fast-fifo": "^1.3.2", "text-decoder": "^1.1.0" } }, "sha512-0nQuG6jf1w+wddNEEXCF4nTg3LtufWINB5eFEN+5TNZW7KWJp6x87+JFL43vaAUPyCfH1wID+mNVyW6OHtFamg=="], "string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], @@ -1345,8 +1381,12 @@ "type-fest": ["type-fest@5.5.0", "", { "dependencies": { "tagged-tag": "^1.0.0" } }, "sha512-PlBfpQwiUvGViBNX84Yxwjsdhd1TUlXr6zjX7eoirtCPIr08NAmxwa+fcYBTeRQxHo9YC9wwF3m9i700sHma8g=="], + "type-is": ["type-is@1.6.18", "", { "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" } }, "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g=="], + "type-level-regexp": ["type-level-regexp@0.1.17", "", {}, "sha512-wTk4DH3cxwk196uGLK/E9pE45aLfeKJacKmcEgEOA/q5dnPGNxXt0cfYdFxb57L+sEpf1oJH4Dnx/pnRcku9jg=="], + "typedarray": ["typedarray@0.0.6", "", {}, "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="], + "typescript": ["typescript@6.0.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ=="], "ufo": ["ufo@1.6.3", "", {}, "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q=="], @@ -1497,12 +1537,20 @@ "anymatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], + "archiver/readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="], + "archiver-utils/glob": ["glob@10.5.0", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="], "archiver-utils/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], + "archiver-utils/readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="], + "compress-commons/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], + "compress-commons/readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="], + + "crc32-stream/readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="], + "cross-fetch/node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="], "cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], @@ -1533,6 +1581,8 @@ "rollup-plugin-visualizer/open": ["open@11.0.0", "", { "dependencies": { "default-browser": "^5.4.0", "define-lazy-prop": "^3.0.0", "is-in-ssh": "^1.0.0", "is-inside-container": "^1.0.0", "powershell-utils": "^0.1.0", "wsl-utils": "^0.3.0" } }, "sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw=="], + "send/mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="], + "source-map-support/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], "string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], @@ -1571,6 +1621,8 @@ "youch/cookie-es": ["cookie-es@3.1.1", "", {}, "sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg=="], + "zip-stream/readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="], + "@esbuild-kit/core-utils/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.18.20", "", { "os": "android", "cpu": "arm" }, "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw=="], "@esbuild-kit/core-utils/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.18.20", "", { "os": "android", "cpu": "arm64" }, "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ=="], @@ -1689,6 +1741,8 @@ "rollup-plugin-visualizer/open/wsl-utils": ["wsl-utils@0.3.1", "", { "dependencies": { "is-wsl": "^3.1.0", "powershell-utils": "^0.1.0" } }, "sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg=="], + "send/mime-types/mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], + "string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], "tsx/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.7", "", { "os": "aix", "cpu": "ppc64" }, "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg=="], diff --git a/package.json b/package.json index a19dff2..28edd05 100644 --- a/package.json +++ b/package.json @@ -20,20 +20,24 @@ }, "dependencies": { "dotenv": "17.4.1", - "drizzle-orm": "^0.45.2", + "drizzle-orm": "0.45.2", "drizzle-pkg": "workspace:*", "drizzle-seed": "0.3.1", - "drizzle-zod": "^0.8.3", - "log4js": "^6.9.1", + "drizzle-zod": "0.8.3", + "log4js": "6.9.1", "logger": "workspace:*", + "mime": "4.1.0", + "multer": "2.1.1", "nuxt": "4.4.2", "pg": "8.20.0", + "ufo": "1.6.3", "vue": "3.5.32", "vue-router": "5.0.4", - "zod": "^4.3.6" + "zod": "4.3.6" }, "devDependencies": { - "@types/pg": "^8.20.0", + "@types/multer": "2.1.0", + "@types/pg": "8.20.0", "drizzle-kit": "0.31.10", "tsx": "4.21.0", "typescript": "6.0.2" diff --git a/packages/drizzle-pkg/env.ts b/packages/drizzle-pkg/env.ts index 6f3da84..92a311f 100644 --- a/packages/drizzle-pkg/env.ts +++ b/packages/drizzle-pkg/env.ts @@ -1,4 +1,4 @@ import { config } from 'dotenv'; -config({ path: '../../.env.local' }); +config({ path: '../../.env' }); diff --git a/packages/drizzle-pkg/lib/schema/schema.ts b/packages/drizzle-pkg/lib/schema/auth.ts similarity index 80% rename from packages/drizzle-pkg/lib/schema/schema.ts rename to packages/drizzle-pkg/lib/schema/auth.ts index 0f15b51..0532ada 100644 --- a/packages/drizzle-pkg/lib/schema/schema.ts +++ b/packages/drizzle-pkg/lib/schema/auth.ts @@ -1,6 +1,6 @@ import { integer, pgTable, varchar } from "drizzle-orm/pg-core"; -export const usersTable = pgTable("users_table", { +export const usersTable = pgTable("users", { id: integer().primaryKey().generatedAlwaysAsIdentity(), name: varchar().notNull(), age: integer().notNull(), diff --git a/packages/drizzle-pkg/seed.ts b/packages/drizzle-pkg/seed.ts index 188e1cd..6ed7f28 100644 --- a/packages/drizzle-pkg/seed.ts +++ b/packages/drizzle-pkg/seed.ts @@ -1,6 +1,6 @@ import './env'; import { seed } from "drizzle-seed"; -import { usersTable } from "./lib/schema/schema"; +import { usersTable } from "./lib/schema/auth"; import { dbGlobal } from "./lib/db"; async function main() { diff --git a/server/api/[...slug].ts b/server/api/[...slug].ts new file mode 100644 index 0000000..b243782 --- /dev/null +++ b/server/api/[...slug].ts @@ -0,0 +1,12 @@ +import type { H3Event } from "h3"; + +const handler = eventHandler(async (event: H3Event) => { + event.node.res.statusCode = 404 + return { + code: 0, + message: "该接口不存在" + } +}); + +export type HealthCheckData = Awaited>; +export default handler; diff --git a/server/api/file/upload.post.ts b/server/api/file/upload.post.ts new file mode 100644 index 0000000..24bb12a --- /dev/null +++ b/server/api/file/upload.post.ts @@ -0,0 +1,86 @@ +import multer from 'multer'; +import fs from 'node:fs'; +import path from 'node:path'; +import { callNodeListener } from 'h3'; + +// 类型定义 +interface IFile { + name: string; + url: string; // 前端可直接访问的 URL + path: string; // 服务器存储路径 + mimeType: string; + size: number; +} + +export default defineWrappedResponseHandler(async (event) => { + try { + // 存储目录 + const uploadDir = path.join(process.cwd(), 'public/assets'); + + // 自动创建目录 + if (!fs.existsSync(uploadDir)) { + fs.mkdirSync(uploadDir, { recursive: true }); + } + + // 配置存储 + const storage = multer.diskStorage({ + destination: uploadDir, + filename: (req, file, cb) => { + // 生成唯一文件名:时间戳 + 原始文件名(安全处理) + const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1e9); + // 获取文件后缀 + const ext = path.extname(file.originalname).toLowerCase(); + // 干净的文件名(只保留字母数字) + const baseName = path.basename(file.originalname, ext).replace(/[^a-z0-9]/gi, '-'); + // 最终文件名(带后缀) + const filename = `${uniqueSuffix}-${baseName}${ext}`; + cb(null, filename); + }, + }); + + // 上传配置 + const upload = multer({ + storage, + limits: { + fileSize: 5 * 1024 * 1024, // 5MB 限制 + }, + fileFilter: (req, file, cb) => { + const allowedTypes = ['image/png', 'image/jpeg', 'image/jpg', 'image/webp']; + if (allowedTypes.includes(file.mimetype)) { + cb(null, true); + } else { + cb(new Error('只支持 PNG/JPG/WebP 格式图片')); + } + }, + }); + + // 执行上传(最多 10 个文件) + await callNodeListener( + // @ts-expect-error Nuxt 类型兼容 + upload.array('file', 10), + event.node.req, + event.node.res + ); + + // 获取上传后的文件 + // @ts-expect-error + const uploadedFiles = event.node.req.files || []; + + // 格式化返回数据 + const result: IFile[] = uploadedFiles.map((file: any) => ({ + name: file.originalname, + url: `/public/assets/${file.filename}`, // ✅ 前端可直接访问 + mimeType: file.mimetype, + size: file.size, + })); + + return result; + + } catch (err: any) { + console.error('上传失败:', err); + return createError({ + statusCode: 400, + statusMessage: err.message || '上传失败', + }); + } +}); \ No newline at end of file diff --git a/server/api/health.get.ts b/server/api/health.get.ts deleted file mode 100644 index 7362f8e..0000000 --- a/server/api/health.get.ts +++ /dev/null @@ -1,7 +0,0 @@ -export default defineEventHandler(() => { - return { - status: 'ok', - timestamp: new Date().toISOString(), - uptime: Math.floor(process.uptime()), - } -}) diff --git a/server/api/hello.get.ts b/server/api/hello.get.ts new file mode 100644 index 0000000..a59dc93 --- /dev/null +++ b/server/api/hello.get.ts @@ -0,0 +1,14 @@ + +export default defineWrappedResponseHandler((event) => { + return { + hello: "aa", + users: [ + { + id: 1, + name: "aaa", + email: "aaa", + age: 23, + } + ] + } +}) \ No newline at end of file diff --git a/server/api/hello.ts b/server/api/hello.ts deleted file mode 100644 index 146a58e..0000000 --- a/server/api/hello.ts +++ /dev/null @@ -1,16 +0,0 @@ -import log4js from "logger"; -import { getUsers } from "#server/service/auth"; - -const logger = log4js.getLogger("APP") - -export default defineWrappedResponseHandler({ - auth: true, - authType: 'token', -}, async (event) => { - logger.info("hello: world"); - const users = await getUsers(1) - return { - hello: 'world', - users: users, - } -}) \ No newline at end of file diff --git a/server/api/pic/random.get.ts b/server/api/pic/random.get.ts new file mode 100644 index 0000000..d3d406b --- /dev/null +++ b/server/api/pic/random.get.ts @@ -0,0 +1,56 @@ +import type { H3Event } from "h3"; +import fs from "fs/promises"; +import { resolve } from "node:path"; + +const handler = eventHandler(async (event: H3Event) => { + const query = getQuery(event); + + if (Reflect.has(query, "auto")) { + try { + return await $fetch("https://api.miaomc.cn/image/get", { method: "get", mode: "cors" }) + } catch (error) {} + try { + return await sendRedirect( + event, + encodeURI("https://api.r10086.com/樱道随机图片api接口.php?图片系列=动漫综合1"), + 302 + ); + } catch (error) {} + return "error" + } + if (Reflect.has(query, "miaomc")) { + // return await $fetch("https://api.miaomc.cn/image/get", { method: "get", mode: "cors" }) + event.node.res.statusCode = 302; + event.node.res.setHeader("location", "https://api.miaomc.cn/image/get"); + return; + } + if (Reflect.has(query, "r10086")) { + // return `选择 + // + // `; + return await sendRedirect( + event, + encodeURI("https://api.r10086.com/樱道随机图片api接口.php?图片系列=动漫综合1"), + 302 + ); + } + if (Reflect.has(query, "favicon")) { + const avatarPath = resolve("public", "favicon.ico") + event.node.res.setHeader("Content-Type", "image/jpeg"); + return fs.readFile(avatarPath) //fs.createReadStream(avatarPath); + } + return `选择 + +

选择图片路径

+
    +
  1. auto(同时支持以下两种方式)
  2. +
  3. api.miaomc.cn
  4. +
  5. r10086
  6. +
  7. favicon
  8. +
+ + `; +}); + +export type ReturnData = Awaited>; +export default handler; diff --git a/server/middleware/00.public.ts b/server/middleware/00.public.ts new file mode 100644 index 0000000..88a6eb4 --- /dev/null +++ b/server/middleware/00.public.ts @@ -0,0 +1,88 @@ +import { resolve, join, sep, extname } from "node:path"; +import { promises as fsp } from "node:fs"; +import { + decodePath, + withLeadingSlash, + withoutTrailingSlash, + parseURL, +} from "ufo"; +import type { H3Event } from "h3"; +import mime from "mime"; + +const METHODS = new Set(["HEAD", "GET"]); +const SAFE_BASE_DIR = resolve("public"); + +// 缓存配置 +const CACHE_CONTROL = "public, max-age=31536000, immutable"; +const NOT_MODIFIED = 304; +const FORBIDDEN = 403; +const NOT_FOUND = 404; +const SERVER_ERROR = 500; + +export default eventHandler(async (event: H3Event) => { + if (!event.path.startsWith("/public")) return; + + const { req, res } = event.node; + const method = req.method; + + if (method && !METHODS.has(method)) return; + + try { + // 安全解析路径 + const url = event.path.replace(/^\/public/, ""); + const pathname = decodePath( + withLeadingSlash(withoutTrailingSlash(parseURL(url).pathname)) + ); + + const targetPath = join(SAFE_BASE_DIR, pathname); + const resolvedPath = resolve(targetPath); + + // 安全校验 + if (!resolvedPath.startsWith(SAFE_BASE_DIR + sep)) { + res.statusCode = FORBIDDEN; + return "Forbidden"; + } + + const stat = await fsp.stat(resolvedPath); + if (!stat.isFile()) { + res.statusCode = NOT_FOUND; + return "Not Found"; + } + + // ====================== 缓存逻辑 ====================== + const mtime = stat.mtime.toUTCString(); + const etag = `"${stat.mtime.getTime().toString(16)}-${stat.size.toString(16)}"`; + + const contentType = mime.getType(resolvedPath) || "application/octet-stream"; + + // 设置缓存头 + res.setHeader("Cache-Control", CACHE_CONTROL); + res.setHeader("ETag", etag); + res.setHeader("Last-Modified", mtime); + res.setHeader("Content-Type", contentType); + res.setHeader("Content-Length", stat.size); + + // 禁用 keep-alive + res.setHeader("Connection", "close"); + + // 304 协商缓存 + const ifNoneMatch = req.headers["if-none-match"]; + const ifModifiedSince = req.headers["if-modified-since"]; + if (ifNoneMatch === etag || (ifModifiedSince && ifModifiedSince === mtime)) { + res.statusCode = NOT_MODIFIED; + return ""; + } + // ====================================================== + + if (method === "HEAD") return ""; + return fsp.readFile(resolvedPath); + + } catch (err: any) { + if (err.code === "ENOENT") { + res.statusCode = NOT_FOUND; + return "Not Found"; + } + res.statusCode = SERVER_ERROR; + return "Server Error"; + } +}); \ No newline at end of file diff --git a/server/utils/handler.ts b/server/utils/handler.ts index cef2844..55d98cf 100644 --- a/server/utils/handler.ts +++ b/server/utils/handler.ts @@ -1,7 +1,10 @@ interface IConfig { - auth?: boolean; - authType?: 'token' | 'basic'; + +} + +const defaultConfig: IConfig = { + } export const defineWrappedResponseHandler = ( @@ -12,15 +15,13 @@ export const defineWrappedResponseHandler = ( if (!handler) { throw new Error('handler or config is required'); } - const config = typeof handlerOrConfig === 'object' ? handlerOrConfig : {}; + const config = Object.assign({ ...defaultConfig }, typeof handlerOrConfig === 'object' ? handlerOrConfig : {}); + return defineEventHandler(async (event) => { try { - // do something before the route handler const response = await handler(event) - // do something after the route handler - return { response } + return response } catch (err) { - // Error handling return { err } } })