feat: add login for plugin

This commit is contained in:
xion 2025-03-23 16:48:19 +08:00
parent 74a484718a
commit cb490470c1
18 changed files with 1079 additions and 369 deletions

View File

@ -24,6 +24,7 @@
"ssh": "ssh -L 6379:localhost:6379 light ", "ssh": "ssh -L 6379:localhost:6379 light ",
"ssh:sky": "ssh -L 6379:172.21.32.13:6379 sky", "ssh:sky": "ssh -L 6379:172.21.32.13:6379 sky",
"dev:lib": "turbo run dev:lib", "dev:lib": "turbo run dev:lib",
"build:lib": "turbo run build",
"dev:oss": "turbo run dev:lib --filter=@kevisual/oss" "dev:oss": "turbo run dev:lib --filter=@kevisual/oss"
}, },
"keywords": [], "keywords": [],
@ -37,7 +38,7 @@
"dependencies": { "dependencies": {
"@kevisual/local-app-manager": "0.1.9", "@kevisual/local-app-manager": "0.1.9",
"@kevisual/router": "0.0.9", "@kevisual/router": "0.0.9",
"@kevisual/use-config": "^1.0.9", "@kevisual/use-config": "^1.0.10",
"@types/semver": "^7.5.8", "@types/semver": "^7.5.8",
"archiver": "^7.0.1", "archiver": "^7.0.1",
"crypto-js": "^4.2.0", "crypto-js": "^4.2.0",

348
pnpm-lock.yaml generated
View File

@ -15,13 +15,13 @@ importers:
dependencies: dependencies:
'@kevisual/local-app-manager': '@kevisual/local-app-manager':
specifier: 0.1.9 specifier: 0.1.9
version: 0.1.9(@kevisual/router@0.0.9)(@kevisual/types@0.0.6)(@kevisual/use-config@1.0.9)(pm2@6.0.5) version: 0.1.9(@kevisual/router@0.0.9)(@kevisual/types@0.0.6)(@kevisual/use-config@1.0.10(dotenv@16.4.7))(pm2@6.0.5)
'@kevisual/router': '@kevisual/router':
specifier: 0.0.9 specifier: 0.0.9
version: 0.0.9 version: 0.0.9
'@kevisual/use-config': '@kevisual/use-config':
specifier: ^1.0.9 specifier: ^1.0.10
version: 1.0.9 version: 1.0.10(dotenv@16.4.7)
'@types/semver': '@types/semver':
specifier: ^7.5.8 specifier: ^7.5.8
version: 7.5.8 version: 7.5.8
@ -189,16 +189,16 @@ importers:
specifier: ^0.0.9 specifier: ^0.0.9
version: 0.0.9 version: 0.0.9
'@kevisual/use-config': '@kevisual/use-config':
specifier: ^1.0.9 specifier: ^1.0.10
version: 1.0.9 version: 1.0.10(dotenv@16.4.7)
ioredis: ioredis:
specifier: ^5.6.0 specifier: ^5.6.0
version: 5.6.0 version: 5.6.0
nanoid: nanoid:
specifier: ^5.1.2 specifier: ^5.1.5
version: 5.1.3 version: 5.1.5
pg: pg:
specifier: ^8.13.3 specifier: ^8.14.1
version: 8.14.1 version: 8.14.1
sequelize: sequelize:
specifier: ^6.37.6 specifier: ^6.37.6
@ -215,22 +215,22 @@ importers:
version: 0.0.6 version: 0.0.6
'@rollup/plugin-alias': '@rollup/plugin-alias':
specifier: ^5.1.1 specifier: ^5.1.1
version: 5.1.1(rollup@4.35.0) version: 5.1.1(rollup@4.36.0)
'@rollup/plugin-commonjs': '@rollup/plugin-commonjs':
specifier: ^28.0.2 specifier: ^28.0.3
version: 28.0.3(rollup@4.35.0) version: 28.0.3(rollup@4.36.0)
'@rollup/plugin-json': '@rollup/plugin-json':
specifier: ^6.1.0 specifier: ^6.1.0
version: 6.1.0(rollup@4.35.0) version: 6.1.0(rollup@4.36.0)
'@rollup/plugin-node-resolve': '@rollup/plugin-node-resolve':
specifier: ^16.0.0 specifier: ^16.0.1
version: 16.0.1(rollup@4.35.0) version: 16.0.1(rollup@4.36.0)
'@rollup/plugin-replace': '@rollup/plugin-replace':
specifier: ^6.0.2 specifier: ^6.0.2
version: 6.0.2(rollup@4.35.0) version: 6.0.2(rollup@4.36.0)
'@rollup/plugin-typescript': '@rollup/plugin-typescript':
specifier: ^12.1.2 specifier: ^12.1.2
version: 12.1.2(rollup@4.35.0)(tslib@2.8.1)(typescript@5.8.2) version: 12.1.2(rollup@4.36.0)(tslib@2.8.1)(typescript@5.8.2)
'@types/archiver': '@types/archiver':
specifier: ^6.0.3 specifier: ^6.0.3
version: 6.0.3 version: 6.0.3
@ -247,11 +247,11 @@ importers:
specifier: ^4.17.12 specifier: ^4.17.12
version: 4.17.12 version: 4.17.12
'@types/node': '@types/node':
specifier: ^22.13.9 specifier: ^22.13.11
version: 22.13.10 version: 22.13.11
'@types/react': '@types/react':
specifier: ^19.0.10 specifier: ^19.0.12
version: 19.0.10 version: 19.0.12
'@types/uuid': '@types/uuid':
specifier: ^10.0.0 specifier: ^10.0.0
version: 10.0.0 version: 10.0.0
@ -268,17 +268,17 @@ importers:
specifier: latest specifier: latest
version: 6.0.1 version: 6.0.1
rollup: rollup:
specifier: ^4.34.9 specifier: ^4.36.0
version: 4.35.0 version: 4.36.0
rollup-plugin-copy: rollup-plugin-copy:
specifier: ^3.5.0 specifier: ^3.5.0
version: 3.5.0 version: 3.5.0
rollup-plugin-dts: rollup-plugin-dts:
specifier: ^6.1.1 specifier: ^6.2.1
version: 6.1.1(rollup@4.35.0)(typescript@5.8.2) version: 6.2.1(rollup@4.36.0)(typescript@5.8.2)
rollup-plugin-esbuild: rollup-plugin-esbuild:
specifier: ^6.2.1 specifier: ^6.2.1
version: 6.2.1(esbuild@0.25.0)(rollup@4.35.0) version: 6.2.1(esbuild@0.25.0)(rollup@4.36.0)
tape: tape:
specifier: ^5.9.0 specifier: ^5.9.0
version: 5.9.0 version: 5.9.0
@ -516,8 +516,10 @@ packages:
'@kevisual/types@0.0.6': '@kevisual/types@0.0.6':
resolution: {integrity: sha512-7yxe1QmuC5g7lI/1Hm+zXly8if0z+ZqGM1SVOVv2VNRwRAVYBJDc365zWCCfRwE+5YaB2daWTe5zBOU4EkltkQ==} resolution: {integrity: sha512-7yxe1QmuC5g7lI/1Hm+zXly8if0z+ZqGM1SVOVv2VNRwRAVYBJDc365zWCCfRwE+5YaB2daWTe5zBOU4EkltkQ==}
'@kevisual/use-config@1.0.9': '@kevisual/use-config@1.0.10':
resolution: {integrity: sha512-lJz98WWL178QUaf/rkM9feMm0aUnYd6ikm3ma/9Zi/K2QNrxbTRAgMGkCggUalAES8IbUvEsg+Q+Y2RaPLxCmw==} resolution: {integrity: sha512-fH2B4BnR4+OjR3PzAegF8H9RJpyFZu6BnVDyfvSSZavZMurufkJ949jizoRde+bNAHff/PRcpa5EZg2imZNf1g==}
peerDependencies:
dotenv: ^16.4.7
'@ljharb/resumer@0.1.3': '@ljharb/resumer@0.1.3':
resolution: {integrity: sha512-d+tsDgfkj9X5QTriqM4lKesCkMMJC3IrbPKHvayP00ELx2axdXvDfWkqjxrLXIzGcQzmj7VAUT1wopqARTvafw==} resolution: {integrity: sha512-d+tsDgfkj9X5QTriqM4lKesCkMMJC3IrbPKHvayP00ELx2axdXvDfWkqjxrLXIzGcQzmj7VAUT1wopqARTvafw==}
@ -624,191 +626,96 @@ packages:
rollup: rollup:
optional: true optional: true
'@rollup/rollup-android-arm-eabi@4.35.0':
resolution: {integrity: sha512-uYQ2WfPaqz5QtVgMxfN6NpLD+no0MYHDBywl7itPYd3K5TjjSghNKmX8ic9S8NU8w81NVhJv/XojcHptRly7qQ==}
cpu: [arm]
os: [android]
'@rollup/rollup-android-arm-eabi@4.36.0': '@rollup/rollup-android-arm-eabi@4.36.0':
resolution: {integrity: sha512-jgrXjjcEwN6XpZXL0HUeOVGfjXhPyxAbbhD0BlXUB+abTOpbPiN5Wb3kOT7yb+uEtATNYF5x5gIfwutmuBA26w==} resolution: {integrity: sha512-jgrXjjcEwN6XpZXL0HUeOVGfjXhPyxAbbhD0BlXUB+abTOpbPiN5Wb3kOT7yb+uEtATNYF5x5gIfwutmuBA26w==}
cpu: [arm] cpu: [arm]
os: [android] os: [android]
'@rollup/rollup-android-arm64@4.35.0':
resolution: {integrity: sha512-FtKddj9XZudurLhdJnBl9fl6BwCJ3ky8riCXjEw3/UIbjmIY58ppWwPEvU3fNu+W7FUsAsB1CdH+7EQE6CXAPA==}
cpu: [arm64]
os: [android]
'@rollup/rollup-android-arm64@4.36.0': '@rollup/rollup-android-arm64@4.36.0':
resolution: {integrity: sha512-NyfuLvdPdNUfUNeYKUwPwKsE5SXa2J6bCt2LdB/N+AxShnkpiczi3tcLJrm5mA+eqpy0HmaIY9F6XCa32N5yzg==} resolution: {integrity: sha512-NyfuLvdPdNUfUNeYKUwPwKsE5SXa2J6bCt2LdB/N+AxShnkpiczi3tcLJrm5mA+eqpy0HmaIY9F6XCa32N5yzg==}
cpu: [arm64] cpu: [arm64]
os: [android] os: [android]
'@rollup/rollup-darwin-arm64@4.35.0':
resolution: {integrity: sha512-Uk+GjOJR6CY844/q6r5DR/6lkPFOw0hjfOIzVx22THJXMxktXG6CbejseJFznU8vHcEBLpiXKY3/6xc+cBm65Q==}
cpu: [arm64]
os: [darwin]
'@rollup/rollup-darwin-arm64@4.36.0': '@rollup/rollup-darwin-arm64@4.36.0':
resolution: {integrity: sha512-JQ1Jk5G4bGrD4pWJQzWsD8I1n1mgPXq33+/vP4sk8j/z/C2siRuxZtaUA7yMTf71TCZTZl/4e1bfzwUmFb3+rw==} resolution: {integrity: sha512-JQ1Jk5G4bGrD4pWJQzWsD8I1n1mgPXq33+/vP4sk8j/z/C2siRuxZtaUA7yMTf71TCZTZl/4e1bfzwUmFb3+rw==}
cpu: [arm64] cpu: [arm64]
os: [darwin] os: [darwin]
'@rollup/rollup-darwin-x64@4.35.0':
resolution: {integrity: sha512-3IrHjfAS6Vkp+5bISNQnPogRAW5GAV1n+bNCrDwXmfMHbPl5EhTmWtfmwlJxFRUCBZ+tZ/OxDyU08aF6NI/N5Q==}
cpu: [x64]
os: [darwin]
'@rollup/rollup-darwin-x64@4.36.0': '@rollup/rollup-darwin-x64@4.36.0':
resolution: {integrity: sha512-6c6wMZa1lrtiRsbDziCmjE53YbTkxMYhhnWnSW8R/yqsM7a6mSJ3uAVT0t8Y/DGt7gxUWYuFM4bwWk9XCJrFKA==} resolution: {integrity: sha512-6c6wMZa1lrtiRsbDziCmjE53YbTkxMYhhnWnSW8R/yqsM7a6mSJ3uAVT0t8Y/DGt7gxUWYuFM4bwWk9XCJrFKA==}
cpu: [x64] cpu: [x64]
os: [darwin] os: [darwin]
'@rollup/rollup-freebsd-arm64@4.35.0':
resolution: {integrity: sha512-sxjoD/6F9cDLSELuLNnY0fOrM9WA0KrM0vWm57XhrIMf5FGiN8D0l7fn+bpUeBSU7dCgPV2oX4zHAsAXyHFGcQ==}
cpu: [arm64]
os: [freebsd]
'@rollup/rollup-freebsd-arm64@4.36.0': '@rollup/rollup-freebsd-arm64@4.36.0':
resolution: {integrity: sha512-KXVsijKeJXOl8QzXTsA+sHVDsFOmMCdBRgFmBb+mfEb/7geR7+C8ypAml4fquUt14ZyVXaw2o1FWhqAfOvA4sg==} resolution: {integrity: sha512-KXVsijKeJXOl8QzXTsA+sHVDsFOmMCdBRgFmBb+mfEb/7geR7+C8ypAml4fquUt14ZyVXaw2o1FWhqAfOvA4sg==}
cpu: [arm64] cpu: [arm64]
os: [freebsd] os: [freebsd]
'@rollup/rollup-freebsd-x64@4.35.0':
resolution: {integrity: sha512-2mpHCeRuD1u/2kruUiHSsnjWtHjqVbzhBkNVQ1aVD63CcexKVcQGwJ2g5VphOd84GvxfSvnnlEyBtQCE5hxVVw==}
cpu: [x64]
os: [freebsd]
'@rollup/rollup-freebsd-x64@4.36.0': '@rollup/rollup-freebsd-x64@4.36.0':
resolution: {integrity: sha512-dVeWq1ebbvByI+ndz4IJcD4a09RJgRYmLccwlQ8bPd4olz3Y213uf1iwvc7ZaxNn2ab7bjc08PrtBgMu6nb4pQ==} resolution: {integrity: sha512-dVeWq1ebbvByI+ndz4IJcD4a09RJgRYmLccwlQ8bPd4olz3Y213uf1iwvc7ZaxNn2ab7bjc08PrtBgMu6nb4pQ==}
cpu: [x64] cpu: [x64]
os: [freebsd] os: [freebsd]
'@rollup/rollup-linux-arm-gnueabihf@4.35.0':
resolution: {integrity: sha512-mrA0v3QMy6ZSvEuLs0dMxcO2LnaCONs1Z73GUDBHWbY8tFFocM6yl7YyMu7rz4zS81NDSqhrUuolyZXGi8TEqg==}
cpu: [arm]
os: [linux]
'@rollup/rollup-linux-arm-gnueabihf@4.36.0': '@rollup/rollup-linux-arm-gnueabihf@4.36.0':
resolution: {integrity: sha512-bvXVU42mOVcF4le6XSjscdXjqx8okv4n5vmwgzcmtvFdifQ5U4dXFYaCB87namDRKlUL9ybVtLQ9ztnawaSzvg==} resolution: {integrity: sha512-bvXVU42mOVcF4le6XSjscdXjqx8okv4n5vmwgzcmtvFdifQ5U4dXFYaCB87namDRKlUL9ybVtLQ9ztnawaSzvg==}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
'@rollup/rollup-linux-arm-musleabihf@4.35.0':
resolution: {integrity: sha512-DnYhhzcvTAKNexIql8pFajr0PiDGrIsBYPRvCKlA5ixSS3uwo/CWNZxB09jhIapEIg945KOzcYEAGGSmTSpk7A==}
cpu: [arm]
os: [linux]
'@rollup/rollup-linux-arm-musleabihf@4.36.0': '@rollup/rollup-linux-arm-musleabihf@4.36.0':
resolution: {integrity: sha512-JFIQrDJYrxOnyDQGYkqnNBtjDwTgbasdbUiQvcU8JmGDfValfH1lNpng+4FWlhaVIR4KPkeddYjsVVbmJYvDcg==} resolution: {integrity: sha512-JFIQrDJYrxOnyDQGYkqnNBtjDwTgbasdbUiQvcU8JmGDfValfH1lNpng+4FWlhaVIR4KPkeddYjsVVbmJYvDcg==}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
'@rollup/rollup-linux-arm64-gnu@4.35.0':
resolution: {integrity: sha512-uagpnH2M2g2b5iLsCTZ35CL1FgyuzzJQ8L9VtlJ+FckBXroTwNOaD0z0/UF+k5K3aNQjbm8LIVpxykUOQt1m/A==}
cpu: [arm64]
os: [linux]
'@rollup/rollup-linux-arm64-gnu@4.36.0': '@rollup/rollup-linux-arm64-gnu@4.36.0':
resolution: {integrity: sha512-KqjYVh3oM1bj//5X7k79PSCZ6CvaVzb7Qs7VMWS+SlWB5M8p3FqufLP9VNp4CazJ0CsPDLwVD9r3vX7Ci4J56A==} resolution: {integrity: sha512-KqjYVh3oM1bj//5X7k79PSCZ6CvaVzb7Qs7VMWS+SlWB5M8p3FqufLP9VNp4CazJ0CsPDLwVD9r3vX7Ci4J56A==}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
'@rollup/rollup-linux-arm64-musl@4.35.0':
resolution: {integrity: sha512-XQxVOCd6VJeHQA/7YcqyV0/88N6ysSVzRjJ9I9UA/xXpEsjvAgDTgH3wQYz5bmr7SPtVK2TsP2fQ2N9L4ukoUg==}
cpu: [arm64]
os: [linux]
'@rollup/rollup-linux-arm64-musl@4.36.0': '@rollup/rollup-linux-arm64-musl@4.36.0':
resolution: {integrity: sha512-QiGnhScND+mAAtfHqeT+cB1S9yFnNQ/EwCg5yE3MzoaZZnIV0RV9O5alJAoJKX/sBONVKeZdMfO8QSaWEygMhw==} resolution: {integrity: sha512-QiGnhScND+mAAtfHqeT+cB1S9yFnNQ/EwCg5yE3MzoaZZnIV0RV9O5alJAoJKX/sBONVKeZdMfO8QSaWEygMhw==}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
'@rollup/rollup-linux-loongarch64-gnu@4.35.0':
resolution: {integrity: sha512-5pMT5PzfgwcXEwOaSrqVsz/LvjDZt+vQ8RT/70yhPU06PTuq8WaHhfT1LW+cdD7mW6i/J5/XIkX/1tCAkh1W6g==}
cpu: [loong64]
os: [linux]
'@rollup/rollup-linux-loongarch64-gnu@4.36.0': '@rollup/rollup-linux-loongarch64-gnu@4.36.0':
resolution: {integrity: sha512-1ZPyEDWF8phd4FQtTzMh8FQwqzvIjLsl6/84gzUxnMNFBtExBtpL51H67mV9xipuxl1AEAerRBgBwFNpkw8+Lg==} resolution: {integrity: sha512-1ZPyEDWF8phd4FQtTzMh8FQwqzvIjLsl6/84gzUxnMNFBtExBtpL51H67mV9xipuxl1AEAerRBgBwFNpkw8+Lg==}
cpu: [loong64] cpu: [loong64]
os: [linux] os: [linux]
'@rollup/rollup-linux-powerpc64le-gnu@4.35.0':
resolution: {integrity: sha512-c+zkcvbhbXF98f4CtEIP1EBA/lCic5xB0lToneZYvMeKu5Kamq3O8gqrxiYYLzlZH6E3Aq+TSW86E4ay8iD8EA==}
cpu: [ppc64]
os: [linux]
'@rollup/rollup-linux-powerpc64le-gnu@4.36.0': '@rollup/rollup-linux-powerpc64le-gnu@4.36.0':
resolution: {integrity: sha512-VMPMEIUpPFKpPI9GZMhJrtu8rxnp6mJR3ZzQPykq4xc2GmdHj3Q4cA+7avMyegXy4n1v+Qynr9fR88BmyO74tg==} resolution: {integrity: sha512-VMPMEIUpPFKpPI9GZMhJrtu8rxnp6mJR3ZzQPykq4xc2GmdHj3Q4cA+7avMyegXy4n1v+Qynr9fR88BmyO74tg==}
cpu: [ppc64] cpu: [ppc64]
os: [linux] os: [linux]
'@rollup/rollup-linux-riscv64-gnu@4.35.0':
resolution: {integrity: sha512-s91fuAHdOwH/Tad2tzTtPX7UZyytHIRR6V4+2IGlV0Cej5rkG0R61SX4l4y9sh0JBibMiploZx3oHKPnQBKe4g==}
cpu: [riscv64]
os: [linux]
'@rollup/rollup-linux-riscv64-gnu@4.36.0': '@rollup/rollup-linux-riscv64-gnu@4.36.0':
resolution: {integrity: sha512-ttE6ayb/kHwNRJGYLpuAvB7SMtOeQnVXEIpMtAvx3kepFQeowVED0n1K9nAdraHUPJ5hydEMxBpIR7o4nrm8uA==} resolution: {integrity: sha512-ttE6ayb/kHwNRJGYLpuAvB7SMtOeQnVXEIpMtAvx3kepFQeowVED0n1K9nAdraHUPJ5hydEMxBpIR7o4nrm8uA==}
cpu: [riscv64] cpu: [riscv64]
os: [linux] os: [linux]
'@rollup/rollup-linux-s390x-gnu@4.35.0':
resolution: {integrity: sha512-hQRkPQPLYJZYGP+Hj4fR9dDBMIM7zrzJDWFEMPdTnTy95Ljnv0/4w/ixFw3pTBMEuuEuoqtBINYND4M7ujcuQw==}
cpu: [s390x]
os: [linux]
'@rollup/rollup-linux-s390x-gnu@4.36.0': '@rollup/rollup-linux-s390x-gnu@4.36.0':
resolution: {integrity: sha512-4a5gf2jpS0AIe7uBjxDeUMNcFmaRTbNv7NxI5xOCs4lhzsVyGR/0qBXduPnoWf6dGC365saTiwag8hP1imTgag==} resolution: {integrity: sha512-4a5gf2jpS0AIe7uBjxDeUMNcFmaRTbNv7NxI5xOCs4lhzsVyGR/0qBXduPnoWf6dGC365saTiwag8hP1imTgag==}
cpu: [s390x] cpu: [s390x]
os: [linux] os: [linux]
'@rollup/rollup-linux-x64-gnu@4.35.0':
resolution: {integrity: sha512-Pim1T8rXOri+0HmV4CdKSGrqcBWX0d1HoPnQ0uw0bdp1aP5SdQVNBy8LjYncvnLgu3fnnCt17xjWGd4cqh8/hA==}
cpu: [x64]
os: [linux]
'@rollup/rollup-linux-x64-gnu@4.36.0': '@rollup/rollup-linux-x64-gnu@4.36.0':
resolution: {integrity: sha512-5KtoW8UWmwFKQ96aQL3LlRXX16IMwyzMq/jSSVIIyAANiE1doaQsx/KRyhAvpHlPjPiSU/AYX/8m+lQ9VToxFQ==} resolution: {integrity: sha512-5KtoW8UWmwFKQ96aQL3LlRXX16IMwyzMq/jSSVIIyAANiE1doaQsx/KRyhAvpHlPjPiSU/AYX/8m+lQ9VToxFQ==}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
'@rollup/rollup-linux-x64-musl@4.35.0':
resolution: {integrity: sha512-QysqXzYiDvQWfUiTm8XmJNO2zm9yC9P/2Gkrwg2dH9cxotQzunBHYr6jk4SujCTqnfGxduOmQcI7c2ryuW8XVg==}
cpu: [x64]
os: [linux]
'@rollup/rollup-linux-x64-musl@4.36.0': '@rollup/rollup-linux-x64-musl@4.36.0':
resolution: {integrity: sha512-sycrYZPrv2ag4OCvaN5js+f01eoZ2U+RmT5as8vhxiFz+kxwlHrsxOwKPSA8WyS+Wc6Epid9QeI/IkQ9NkgYyQ==} resolution: {integrity: sha512-sycrYZPrv2ag4OCvaN5js+f01eoZ2U+RmT5as8vhxiFz+kxwlHrsxOwKPSA8WyS+Wc6Epid9QeI/IkQ9NkgYyQ==}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
'@rollup/rollup-win32-arm64-msvc@4.35.0':
resolution: {integrity: sha512-OUOlGqPkVJCdJETKOCEf1mw848ZyJ5w50/rZ/3IBQVdLfR5jk/6Sr5m3iO2tdPgwo0x7VcncYuOvMhBWZq8ayg==}
cpu: [arm64]
os: [win32]
'@rollup/rollup-win32-arm64-msvc@4.36.0': '@rollup/rollup-win32-arm64-msvc@4.36.0':
resolution: {integrity: sha512-qbqt4N7tokFwwSVlWDsjfoHgviS3n/vZ8LK0h1uLG9TYIRuUTJC88E1xb3LM2iqZ/WTqNQjYrtmtGmrmmawB6A==} resolution: {integrity: sha512-qbqt4N7tokFwwSVlWDsjfoHgviS3n/vZ8LK0h1uLG9TYIRuUTJC88E1xb3LM2iqZ/WTqNQjYrtmtGmrmmawB6A==}
cpu: [arm64] cpu: [arm64]
os: [win32] os: [win32]
'@rollup/rollup-win32-ia32-msvc@4.35.0':
resolution: {integrity: sha512-2/lsgejMrtwQe44glq7AFFHLfJBPafpsTa6JvP2NGef/ifOa4KBoglVf7AKN7EV9o32evBPRqfg96fEHzWo5kw==}
cpu: [ia32]
os: [win32]
'@rollup/rollup-win32-ia32-msvc@4.36.0': '@rollup/rollup-win32-ia32-msvc@4.36.0':
resolution: {integrity: sha512-t+RY0JuRamIocMuQcfwYSOkmdX9dtkr1PbhKW42AMvaDQa+jOdpUYysroTF/nuPpAaQMWp7ye+ndlmmthieJrQ==} resolution: {integrity: sha512-t+RY0JuRamIocMuQcfwYSOkmdX9dtkr1PbhKW42AMvaDQa+jOdpUYysroTF/nuPpAaQMWp7ye+ndlmmthieJrQ==}
cpu: [ia32] cpu: [ia32]
os: [win32] os: [win32]
'@rollup/rollup-win32-x64-msvc@4.35.0':
resolution: {integrity: sha512-PIQeY5XDkrOysbQblSW7v3l1MDZzkTEzAfTPkj5VAu3FW8fS4ynyLg2sINp0fp3SjZ8xkRYpLqoKcYqAkhU1dw==}
cpu: [x64]
os: [win32]
'@rollup/rollup-win32-x64-msvc@4.36.0': '@rollup/rollup-win32-x64-msvc@4.36.0':
resolution: {integrity: sha512-aRXd7tRZkWLqGbChgcMMDEHjOKudo1kChb1Jt1IfR8cY/KIpgNviLeJy5FUb9IpSuQj8dU2fAYNMPW/hLKOSTw==} resolution: {integrity: sha512-aRXd7tRZkWLqGbChgcMMDEHjOKudo1kChb1Jt1IfR8cY/KIpgNviLeJy5FUb9IpSuQj8dU2fAYNMPW/hLKOSTw==}
cpu: [x64] cpu: [x64]
@ -865,15 +772,9 @@ packages:
'@types/node-forge@1.3.11': '@types/node-forge@1.3.11':
resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==} resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==}
'@types/node@22.13.10':
resolution: {integrity: sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==}
'@types/node@22.13.11': '@types/node@22.13.11':
resolution: {integrity: sha512-iEUCUJoU0i3VnrCmgoWCXttklWcvoCIx4jzcP22fioIVSdTmjgoEvmAO/QPw6TcS9k5FrNgn4w7q5lGOd1CT5g==} resolution: {integrity: sha512-iEUCUJoU0i3VnrCmgoWCXttklWcvoCIx4jzcP22fioIVSdTmjgoEvmAO/QPw6TcS9k5FrNgn4w7q5lGOd1CT5g==}
'@types/react@19.0.10':
resolution: {integrity: sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g==}
'@types/react@19.0.12': '@types/react@19.0.12':
resolution: {integrity: sha512-V6Ar115dBDrjbtXSrS+/Oruobc+qVbbUxDFC1RSbRqLt5SYvxxyIDrSC85RWml54g+jfNeEMZhEj7wW07ONQhA==} resolution: {integrity: sha512-V6Ar115dBDrjbtXSrS+/Oruobc+qVbbUxDFC1RSbRqLt5SYvxxyIDrSC85RWml54g+jfNeEMZhEj7wW07ONQhA==}
@ -1955,11 +1856,6 @@ packages:
mz@2.7.0: mz@2.7.0:
resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
nanoid@5.1.3:
resolution: {integrity: sha512-zAbEOEr7u2CbxwoMRlz/pNSpRP0FdAU4pRaYunCdEezWohXFs+a0Xw7RfkKaezMsmSM1vttcLthJtwRnVtOfHQ==}
engines: {node: ^18 || >=20}
hasBin: true
nanoid@5.1.5: nanoid@5.1.5:
resolution: {integrity: sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==} resolution: {integrity: sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==}
engines: {node: ^18 || >=20} engines: {node: ^18 || >=20}
@ -2302,13 +2198,6 @@ packages:
resolution: {integrity: sha512-wI8D5dvYovRMx/YYKtUNt3Yxaw4ORC9xo6Gt9t22kveWz1enG9QrhVlagzwrxSC455xD1dHMKhIJkbsQ7d48BA==} resolution: {integrity: sha512-wI8D5dvYovRMx/YYKtUNt3Yxaw4ORC9xo6Gt9t22kveWz1enG9QrhVlagzwrxSC455xD1dHMKhIJkbsQ7d48BA==}
engines: {node: '>=8.3'} engines: {node: '>=8.3'}
rollup-plugin-dts@6.1.1:
resolution: {integrity: sha512-aSHRcJ6KG2IHIioYlvAOcEq6U99sVtqDDKVhnwt70rW6tsz3tv5OSjEiWcgzfsHdLyGXZ/3b/7b/+Za3Y6r1XA==}
engines: {node: '>=16'}
peerDependencies:
rollup: ^3.29.4 || ^4
typescript: ^4.5 || ^5.0
rollup-plugin-dts@6.2.1: rollup-plugin-dts@6.2.1:
resolution: {integrity: sha512-sR3CxYUl7i2CHa0O7bA45mCrgADyAQ0tVtGSqi3yvH28M+eg1+g5d7kQ9hLvEz5dorK3XVsH5L2jwHLQf72DzA==} resolution: {integrity: sha512-sR3CxYUl7i2CHa0O7bA45mCrgADyAQ0tVtGSqi3yvH28M+eg1+g5d7kQ9hLvEz5dorK3XVsH5L2jwHLQf72DzA==}
engines: {node: '>=16'} engines: {node: '>=16'}
@ -2323,11 +2212,6 @@ packages:
esbuild: '>=0.18.0' esbuild: '>=0.18.0'
rollup: ^1.20.0 || ^2.0.0 || ^3.0.0 || ^4.0.0 rollup: ^1.20.0 || ^2.0.0 || ^3.0.0 || ^4.0.0
rollup@4.35.0:
resolution: {integrity: sha512-kg6oI4g+vc41vePJyO6dHt/yl0Rz3Thv0kJeVQ3D1kS3E5XSuKbPc29G4IpT/Kv1KQwgHVcN+HtyS+HYLNSvQg==}
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true
rollup@4.36.0: rollup@4.36.0:
resolution: {integrity: sha512-zwATAXNQxUcd40zgtQG0ZafcRK4g004WtEl7kbuhTWPvf07PsfohXl39jVUvPF7jvNAIkKPQ2XrsDlWuxBd++Q==} resolution: {integrity: sha512-zwATAXNQxUcd40zgtQG0ZafcRK4g004WtEl7kbuhTWPvf07PsfohXl39jVUvPF7jvNAIkKPQ2XrsDlWuxBd++Q==}
engines: {node: '>=18.0.0', npm: '>=8.0.0'} engines: {node: '>=18.0.0', npm: '>=8.0.0'}
@ -3018,11 +2902,11 @@ snapshots:
dependencies: dependencies:
eventemitter3: 5.0.1 eventemitter3: 5.0.1
'@kevisual/local-app-manager@0.1.9(@kevisual/router@0.0.9)(@kevisual/types@0.0.6)(@kevisual/use-config@1.0.9)(pm2@6.0.5)': '@kevisual/local-app-manager@0.1.9(@kevisual/router@0.0.9)(@kevisual/types@0.0.6)(@kevisual/use-config@1.0.10(dotenv@16.4.7))(pm2@6.0.5)':
dependencies: dependencies:
'@kevisual/router': 0.0.9 '@kevisual/router': 0.0.9
'@kevisual/types': 0.0.6 '@kevisual/types': 0.0.6
'@kevisual/use-config': 1.0.9 '@kevisual/use-config': 1.0.10(dotenv@16.4.7)
pm2: 6.0.5 pm2: 6.0.5
'@kevisual/router@0.0.9': '@kevisual/router@0.0.9':
@ -3036,9 +2920,10 @@ snapshots:
'@kevisual/types@0.0.6': {} '@kevisual/types@0.0.6': {}
'@kevisual/use-config@1.0.9': '@kevisual/use-config@1.0.10(dotenv@16.4.7)':
dependencies: dependencies:
'@kevisual/load': 0.0.4 '@kevisual/load': 0.0.4
dotenv: 16.4.7
'@ljharb/resumer@0.1.3': '@ljharb/resumer@0.1.3':
dependencies: dependencies:
@ -3114,26 +2999,10 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@rollup/plugin-alias@5.1.1(rollup@4.35.0)':
optionalDependencies:
rollup: 4.35.0
'@rollup/plugin-alias@5.1.1(rollup@4.36.0)': '@rollup/plugin-alias@5.1.1(rollup@4.36.0)':
optionalDependencies: optionalDependencies:
rollup: 4.36.0 rollup: 4.36.0
'@rollup/plugin-commonjs@28.0.3(rollup@4.35.0)':
dependencies:
'@rollup/pluginutils': 5.1.2(rollup@4.35.0)
commondir: 1.0.1
estree-walker: 2.0.2
fdir: 6.3.0(picomatch@4.0.2)
is-reference: 1.2.1
magic-string: 0.30.11
picomatch: 4.0.2
optionalDependencies:
rollup: 4.35.0
'@rollup/plugin-commonjs@28.0.3(rollup@4.36.0)': '@rollup/plugin-commonjs@28.0.3(rollup@4.36.0)':
dependencies: dependencies:
'@rollup/pluginutils': 5.1.2(rollup@4.36.0) '@rollup/pluginutils': 5.1.2(rollup@4.36.0)
@ -3146,28 +3015,12 @@ snapshots:
optionalDependencies: optionalDependencies:
rollup: 4.36.0 rollup: 4.36.0
'@rollup/plugin-json@6.1.0(rollup@4.35.0)':
dependencies:
'@rollup/pluginutils': 5.1.2(rollup@4.35.0)
optionalDependencies:
rollup: 4.35.0
'@rollup/plugin-json@6.1.0(rollup@4.36.0)': '@rollup/plugin-json@6.1.0(rollup@4.36.0)':
dependencies: dependencies:
'@rollup/pluginutils': 5.1.2(rollup@4.36.0) '@rollup/pluginutils': 5.1.2(rollup@4.36.0)
optionalDependencies: optionalDependencies:
rollup: 4.36.0 rollup: 4.36.0
'@rollup/plugin-node-resolve@16.0.1(rollup@4.35.0)':
dependencies:
'@rollup/pluginutils': 5.1.2(rollup@4.35.0)
'@types/resolve': 1.20.2
deepmerge: 4.3.1
is-module: 1.0.0
resolve: 1.22.8
optionalDependencies:
rollup: 4.35.0
'@rollup/plugin-node-resolve@16.0.1(rollup@4.36.0)': '@rollup/plugin-node-resolve@16.0.1(rollup@4.36.0)':
dependencies: dependencies:
'@rollup/pluginutils': 5.1.2(rollup@4.36.0) '@rollup/pluginutils': 5.1.2(rollup@4.36.0)
@ -3178,13 +3031,6 @@ snapshots:
optionalDependencies: optionalDependencies:
rollup: 4.36.0 rollup: 4.36.0
'@rollup/plugin-replace@6.0.2(rollup@4.35.0)':
dependencies:
'@rollup/pluginutils': 5.1.2(rollup@4.35.0)
magic-string: 0.30.11
optionalDependencies:
rollup: 4.35.0
'@rollup/plugin-replace@6.0.2(rollup@4.36.0)': '@rollup/plugin-replace@6.0.2(rollup@4.36.0)':
dependencies: dependencies:
'@rollup/pluginutils': 5.1.2(rollup@4.36.0) '@rollup/pluginutils': 5.1.2(rollup@4.36.0)
@ -3192,15 +3038,6 @@ snapshots:
optionalDependencies: optionalDependencies:
rollup: 4.36.0 rollup: 4.36.0
'@rollup/plugin-typescript@12.1.2(rollup@4.35.0)(tslib@2.8.1)(typescript@5.8.2)':
dependencies:
'@rollup/pluginutils': 5.1.2(rollup@4.35.0)
resolve: 1.22.8
typescript: 5.8.2
optionalDependencies:
rollup: 4.35.0
tslib: 2.8.1
'@rollup/plugin-typescript@12.1.2(rollup@4.36.0)(tslib@2.8.1)(typescript@5.8.2)': '@rollup/plugin-typescript@12.1.2(rollup@4.36.0)(tslib@2.8.1)(typescript@5.8.2)':
dependencies: dependencies:
'@rollup/pluginutils': 5.1.2(rollup@4.36.0) '@rollup/pluginutils': 5.1.2(rollup@4.36.0)
@ -3210,14 +3047,6 @@ snapshots:
rollup: 4.36.0 rollup: 4.36.0
tslib: 2.8.1 tslib: 2.8.1
'@rollup/pluginutils@5.1.2(rollup@4.35.0)':
dependencies:
'@types/estree': 1.0.6
estree-walker: 2.0.2
picomatch: 4.0.2
optionalDependencies:
rollup: 4.35.0
'@rollup/pluginutils@5.1.2(rollup@4.36.0)': '@rollup/pluginutils@5.1.2(rollup@4.36.0)':
dependencies: dependencies:
'@types/estree': 1.0.6 '@types/estree': 1.0.6
@ -3226,117 +3055,60 @@ snapshots:
optionalDependencies: optionalDependencies:
rollup: 4.36.0 rollup: 4.36.0
'@rollup/rollup-android-arm-eabi@4.35.0':
optional: true
'@rollup/rollup-android-arm-eabi@4.36.0': '@rollup/rollup-android-arm-eabi@4.36.0':
optional: true optional: true
'@rollup/rollup-android-arm64@4.35.0':
optional: true
'@rollup/rollup-android-arm64@4.36.0': '@rollup/rollup-android-arm64@4.36.0':
optional: true optional: true
'@rollup/rollup-darwin-arm64@4.35.0':
optional: true
'@rollup/rollup-darwin-arm64@4.36.0': '@rollup/rollup-darwin-arm64@4.36.0':
optional: true optional: true
'@rollup/rollup-darwin-x64@4.35.0':
optional: true
'@rollup/rollup-darwin-x64@4.36.0': '@rollup/rollup-darwin-x64@4.36.0':
optional: true optional: true
'@rollup/rollup-freebsd-arm64@4.35.0':
optional: true
'@rollup/rollup-freebsd-arm64@4.36.0': '@rollup/rollup-freebsd-arm64@4.36.0':
optional: true optional: true
'@rollup/rollup-freebsd-x64@4.35.0':
optional: true
'@rollup/rollup-freebsd-x64@4.36.0': '@rollup/rollup-freebsd-x64@4.36.0':
optional: true optional: true
'@rollup/rollup-linux-arm-gnueabihf@4.35.0':
optional: true
'@rollup/rollup-linux-arm-gnueabihf@4.36.0': '@rollup/rollup-linux-arm-gnueabihf@4.36.0':
optional: true optional: true
'@rollup/rollup-linux-arm-musleabihf@4.35.0':
optional: true
'@rollup/rollup-linux-arm-musleabihf@4.36.0': '@rollup/rollup-linux-arm-musleabihf@4.36.0':
optional: true optional: true
'@rollup/rollup-linux-arm64-gnu@4.35.0':
optional: true
'@rollup/rollup-linux-arm64-gnu@4.36.0': '@rollup/rollup-linux-arm64-gnu@4.36.0':
optional: true optional: true
'@rollup/rollup-linux-arm64-musl@4.35.0':
optional: true
'@rollup/rollup-linux-arm64-musl@4.36.0': '@rollup/rollup-linux-arm64-musl@4.36.0':
optional: true optional: true
'@rollup/rollup-linux-loongarch64-gnu@4.35.0':
optional: true
'@rollup/rollup-linux-loongarch64-gnu@4.36.0': '@rollup/rollup-linux-loongarch64-gnu@4.36.0':
optional: true optional: true
'@rollup/rollup-linux-powerpc64le-gnu@4.35.0':
optional: true
'@rollup/rollup-linux-powerpc64le-gnu@4.36.0': '@rollup/rollup-linux-powerpc64le-gnu@4.36.0':
optional: true optional: true
'@rollup/rollup-linux-riscv64-gnu@4.35.0':
optional: true
'@rollup/rollup-linux-riscv64-gnu@4.36.0': '@rollup/rollup-linux-riscv64-gnu@4.36.0':
optional: true optional: true
'@rollup/rollup-linux-s390x-gnu@4.35.0':
optional: true
'@rollup/rollup-linux-s390x-gnu@4.36.0': '@rollup/rollup-linux-s390x-gnu@4.36.0':
optional: true optional: true
'@rollup/rollup-linux-x64-gnu@4.35.0':
optional: true
'@rollup/rollup-linux-x64-gnu@4.36.0': '@rollup/rollup-linux-x64-gnu@4.36.0':
optional: true optional: true
'@rollup/rollup-linux-x64-musl@4.35.0':
optional: true
'@rollup/rollup-linux-x64-musl@4.36.0': '@rollup/rollup-linux-x64-musl@4.36.0':
optional: true optional: true
'@rollup/rollup-win32-arm64-msvc@4.35.0':
optional: true
'@rollup/rollup-win32-arm64-msvc@4.36.0': '@rollup/rollup-win32-arm64-msvc@4.36.0':
optional: true optional: true
'@rollup/rollup-win32-ia32-msvc@4.35.0':
optional: true
'@rollup/rollup-win32-ia32-msvc@4.36.0': '@rollup/rollup-win32-ia32-msvc@4.36.0':
optional: true optional: true
'@rollup/rollup-win32-x64-msvc@4.35.0':
optional: true
'@rollup/rollup-win32-x64-msvc@4.36.0': '@rollup/rollup-win32-x64-msvc@4.36.0':
optional: true optional: true
@ -3394,18 +3166,10 @@ snapshots:
dependencies: dependencies:
'@types/node': 22.13.11 '@types/node': 22.13.11
'@types/node@22.13.10':
dependencies:
undici-types: 6.20.0
'@types/node@22.13.11': '@types/node@22.13.11':
dependencies: dependencies:
undici-types: 6.20.0 undici-types: 6.20.0
'@types/react@19.0.10':
dependencies:
csstype: 3.1.3
'@types/react@19.0.12': '@types/react@19.0.12':
dependencies: dependencies:
csstype: 3.1.3 csstype: 3.1.3
@ -4579,8 +4343,6 @@ snapshots:
object-assign: 4.1.1 object-assign: 4.1.1
thenify-all: 1.6.0 thenify-all: 1.6.0
nanoid@5.1.3: {}
nanoid@5.1.5: {} nanoid@5.1.5: {}
needle@2.4.0: needle@2.4.0:
@ -4969,14 +4731,6 @@ snapshots:
globby: 10.0.1 globby: 10.0.1
is-plain-object: 3.0.1 is-plain-object: 3.0.1
rollup-plugin-dts@6.1.1(rollup@4.35.0)(typescript@5.8.2):
dependencies:
magic-string: 0.30.11
rollup: 4.35.0
typescript: 5.8.2
optionalDependencies:
'@babel/code-frame': 7.26.2
rollup-plugin-dts@6.2.1(rollup@4.36.0)(typescript@5.8.2): rollup-plugin-dts@6.2.1(rollup@4.36.0)(typescript@5.8.2):
dependencies: dependencies:
magic-string: 0.30.17 magic-string: 0.30.17
@ -4985,17 +4739,6 @@ snapshots:
optionalDependencies: optionalDependencies:
'@babel/code-frame': 7.26.2 '@babel/code-frame': 7.26.2
rollup-plugin-esbuild@6.2.1(esbuild@0.25.0)(rollup@4.35.0):
dependencies:
debug: 4.4.0
es-module-lexer: 1.6.0
esbuild: 0.25.0
get-tsconfig: 4.10.0
rollup: 4.35.0
unplugin-utils: 0.2.4
transitivePeerDependencies:
- supports-color
rollup-plugin-esbuild@6.2.1(esbuild@0.25.0)(rollup@4.36.0): rollup-plugin-esbuild@6.2.1(esbuild@0.25.0)(rollup@4.36.0):
dependencies: dependencies:
debug: 4.4.0 debug: 4.4.0
@ -5007,31 +4750,6 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
rollup@4.35.0:
dependencies:
'@types/estree': 1.0.6
optionalDependencies:
'@rollup/rollup-android-arm-eabi': 4.35.0
'@rollup/rollup-android-arm64': 4.35.0
'@rollup/rollup-darwin-arm64': 4.35.0
'@rollup/rollup-darwin-x64': 4.35.0
'@rollup/rollup-freebsd-arm64': 4.35.0
'@rollup/rollup-freebsd-x64': 4.35.0
'@rollup/rollup-linux-arm-gnueabihf': 4.35.0
'@rollup/rollup-linux-arm-musleabihf': 4.35.0
'@rollup/rollup-linux-arm64-gnu': 4.35.0
'@rollup/rollup-linux-arm64-musl': 4.35.0
'@rollup/rollup-linux-loongarch64-gnu': 4.35.0
'@rollup/rollup-linux-powerpc64le-gnu': 4.35.0
'@rollup/rollup-linux-riscv64-gnu': 4.35.0
'@rollup/rollup-linux-s390x-gnu': 4.35.0
'@rollup/rollup-linux-x64-gnu': 4.35.0
'@rollup/rollup-linux-x64-musl': 4.35.0
'@rollup/rollup-win32-arm64-msvc': 4.35.0
'@rollup/rollup-win32-ia32-msvc': 4.35.0
'@rollup/rollup-win32-x64-msvc': 4.35.0
fsevents: 2.3.3
rollup@4.36.0: rollup@4.36.0:
dependencies: dependencies:
'@types/estree': 1.0.6 '@types/estree': 1.0.6

View File

@ -5,10 +5,18 @@ import * as minioLib from './modules/minio.ts';
import * as sequelizeLib from './modules/sequelize.ts'; import * as sequelizeLib from './modules/sequelize.ts';
import { useContextKey, useContext } from '@kevisual/use-config/context'; import { useContextKey, useContext } from '@kevisual/use-config/context';
import { SimpleRouter } from '@kevisual/router/simple'; import { SimpleRouter } from '@kevisual/router/simple';
import { OssBase } from '@kevisual/oss/services';
useConfig(); useConfig();
export const router = useContextKey('router', () => new SimpleRouter()); export const router = useContextKey('router', () => new SimpleRouter());
export const oss = useContextKey(
'oss',
() =>
new OssBase({
client: minioLib.minioClient,
bucketName: minioLib.bucketName,
prefix: '',
}),
);
export const redis = useContextKey('redis', () => redisLib.redis); export const redis = useContextKey('redis', () => redisLib.redis);
export const redisPublisher = useContextKey('redisPublisher', () => redisLib.redisPublisher); export const redisPublisher = useContextKey('redisPublisher', () => redisLib.redisPublisher);
export const redisSubscriber = useContextKey('redisSubscriber', () => redisLib.redisSubscriber); export const redisSubscriber = useContextKey('redisSubscriber', () => redisLib.redisSubscriber);

View File

@ -1,8 +1,64 @@
import './routes/index.ts'; import './routes/index.ts';
import { app } from './app.ts'; import { app } from './app.ts';
import type { App } from '@kevisual/router';
import { User } from './models/user.ts'; import { User } from './models/user.ts';
import { addAuth } from '@kevisual/code-center-module/models'; // import { addAuth } from '@kevisual/code-center-module/models';
// addAuth(app);
import { createCookie, getSomeInfoFromReq } from './routes/user/me.ts';
/**
* auth中间件, token
* id: auth user成功
* id: auth-can user成功
*
* @param app
*/
export const addAuth = (app: App) => {
app
.route({
path: 'auth',
id: 'auth',
})
.define(async (ctx) => {
const token = ctx.query.token;
if (!token) {
app.throw(401, 'Token is required');
}
const user = await User.getOauthUser(token);
if (!user) {
app.throw(401, 'Token is invalid');
}
const someInfo = getSomeInfoFromReq(ctx);
if (someInfo.isBrowser && !ctx.req?.cookies?.['token']) {
createCookie({ accessToken: token }, ctx);
}
ctx.state.tokenUser = user;
})
.addTo(app);
app
.route({
path: 'auth',
key: 'can',
id: 'auth-can',
})
.define(async (ctx) => {
if (ctx.query?.token) {
const token = ctx.query.token;
const user = await User.getOauthUser(token);
if (token) {
ctx.state.tokenUser = user;
const someInfo = getSomeInfoFromReq(ctx);
if (someInfo.isBrowser && !ctx.req?.cookies?.['token']) {
createCookie({ accessToken: token }, ctx);
}
} else {
ctx.state.tokenUser = null;
}
}
})
.addTo(app);
};
addAuth(app); addAuth(app);
app app
@ -53,6 +109,7 @@ app
if (!tokenUser) { if (!tokenUser) {
ctx.throw(401, 'No User For authorized'); ctx.throw(401, 'No User For authorized');
} }
try { try {
const user = await User.findOne({ const user = await User.findOne({
where: { where: {

View File

@ -0,0 +1,78 @@
import { app } from '@/app.ts';
import { ConfigModel } from './models/model.ts';
import { oss } from '@/app.ts';
import { ConfigOssService } from '@kevisual/oss/services';
import { Op } from 'sequelize';
app
.route({
path: 'config',
key: 'detect',
middleware: ['auth'],
})
.define(async (ctx) => {
const tokenUser = ctx.state.tokenUser;
const owner = tokenUser.username;
const configOss = ConfigOssService.fromBase({
oss,
opts: {
owner,
},
});
const { list, keys, keyEtagMap } = await configOss.getList();
const configList = await ConfigModel.findAll({
where: {
key: {
[Op.in]: keys,
},
uid: tokenUser.id,
},
});
const needUpdateList = list.filter((item) => {
const key = item.key;
const hash = keyEtagMap.get(key);
const config = configList.find((item) => item.key === key);
if (!config) {
return true;
}
return config?.hash !== hash;
});
const keyDataMap = await configOss.getObjectList(needUpdateList.map((item) => item.key));
const updateList = [];
for (const [key, json] of keyDataMap.entries()) {
const keyETag = keyEtagMap.get(key);
const configData = keyDataMap.get(key);
if (keyETag && configData) {
const [config, created] = await ConfigModel.findOrCreate({
where: {
key,
uid: tokenUser.id,
},
defaults: {
key,
title: key,
description: `${key}:${keyETag} 同步而来`,
uid: tokenUser.id,
hash: keyETag,
data: configData,
},
});
if (!created) {
await config.update(
{
hash: keyETag,
data: json,
},
{
fields: ['hash', 'data'],
},
);
}
updateList.push(config);
}
}
ctx.body = {
updateList,
};
})
.addTo(app);

View File

@ -0,0 +1,45 @@
import { app } from '@/app.ts';
import { ConfigModel } from './models/model.ts';
import { ShareConfigService } from './services/share.ts';
import { oss } from '@/app.ts';
import { ConfigOssService } from '@kevisual/oss/services';
import { User } from '@/models/user.ts';
import { defaultKeys } from './models/default-keys.ts';
app
.route({
path: 'config',
key: 'defaultConfig',
middleware: ['auth'],
})
.define(async (ctx) => {
const tokenUser = ctx.state.tokenUser;
const { configKey } = ctx.query;
if (!configKey) {
ctx.throw(400, 'configKey is required');
}
const user = new User();
user.setTokenUser(tokenUser);
const isAdmin = await user.hasUser('admin');
const usersConfig = ['upload.json', 'workspace.json', 'ai.json'];
const adminConfig = ['vip.json'];
const configs = [...usersConfig, ...(isAdmin ? adminConfig : [])];
if (!configs.includes(configKey)) {
ctx.throw(400, 'configKey is invalid');
}
const defaultConfig = defaultKeys.find((item) => item.key === configKey);
const [config, created] = await ConfigModel.findOrCreate({
where: {
key: configKey,
uid: tokenUser.id,
},
defaults: {
key: configKey,
uid: tokenUser.id,
data: defaultConfig?.data,
},
});
ctx.body = config;
})
.addTo(app);

View File

@ -1,3 +1,5 @@
import './list.ts'; import './list.ts';
import './upload-config.ts'; import './upload-config.ts';
import './share-config.ts'; import './share-config.ts';
import './check.ts';
import './config-key.ts';

View File

@ -1,6 +1,9 @@
import { app } from '@/app.ts'; import { app } from '@/app.ts';
import { ConfigModel } from './models/model.ts'; import { ConfigModel } from './models/model.ts';
import { ShareConfigService } from './services/share.ts'; import { ShareConfigService } from './services/share.ts';
import { oss } from '@/app.ts';
import { ConfigOssService } from '@kevisual/oss/services';
app app
.route({ .route({
path: 'config', path: 'config',
@ -31,13 +34,16 @@ app
const tokernUser = ctx.state.tokenUser; const tokernUser = ctx.state.tokenUser;
const tuid = tokernUser.id; const tuid = tokernUser.id;
const { id, data, ...rest } = ctx.query?.data || {}; const { id, data, ...rest } = ctx.query?.data || {};
let config: ConfigModel;
if (id) { if (id) {
const config = await ConfigModel.findByPk(id); config = await ConfigModel.findByPk(id);
let keyIsChange = false; let keyIsChange = false;
if (rest?.key) { if (rest?.key) {
keyIsChange = rest.key !== config?.key; keyIsChange = rest.key !== config?.key;
} }
if (config && config.uid === tuid) { if (!config || config.uid !== tuid) {
ctx.throw(403, 'no permission');
}
if (keyIsChange) { if (keyIsChange) {
const key = rest.key; const key = rest.key;
const keyConfig = await ConfigModel.findOne({ const keyConfig = await ConfigModel.findOne({
@ -61,26 +67,24 @@ app
await ShareConfigService.expireShareConfig(config.key, tokernUser.username); await ShareConfigService.expireShareConfig(config.key, tokernUser.username);
} }
ctx.body = config; ctx.body = config;
} else {
ctx.throw(403, 'no permission');
}
} else if (rest?.key) { } else if (rest?.key) {
// id 不存在key存在则属于更新key不能重复 // id 不存在key存在则属于更新key不能重复
const key = rest.key; const key = rest.key;
const keyConfig = await ConfigModel.findOne({ config = await ConfigModel.findOne({
where: { where: {
key, key,
uid: tuid, uid: tuid,
}, },
}); });
if (keyConfig) { if (config) {
await keyConfig.update({ await config.update({
data: { ...keyConfig.data, ...data }, data: { ...config.data, ...data },
...rest, ...rest,
}); });
ctx.body = keyConfig; ctx.body = config;
} else { } else {
const config = await ConfigModel.create({ // 根据key创建一个配置
config = await ConfigModel.create({
key, key,
...rest, ...rest,
data: data, data: data,
@ -89,15 +93,33 @@ app
ctx.body = config; ctx.body = config;
} }
} }
if (id || rest?.key) return; const key = config?.key;
const ossConfig = ConfigOssService.fromBase({
oss,
opts: {
owner: tokernUser.username,
},
});
if (ossConfig.isEndWithJson(key)) {
const data = config.data;
const hash = ossConfig.hash(data);
if (config.hash !== hash) {
config.hash = hash;
await config.save({
fields: ['hash'],
});
await ossConfig.putJsonObject(key, data);
}
}
if (config) return;
// id和key不存在。创建一个新的配置 // id和key不存在。创建一个新的配置, 而且没有id的
const config = await ConfigModel.create({ const newConfig = await ConfigModel.create({
...rest, ...rest,
data: data, data: data,
uid: tuid, uid: tuid,
}); });
ctx.body = config; ctx.body = newConfig;
}) })
.addTo(app); .addTo(app);
@ -154,6 +176,18 @@ app
}, },
}); });
if (config && config.uid === tuid) { if (config && config.uid === tuid) {
const key = config.key;
const ossConfig = ConfigOssService.fromBase({
oss,
opts: {
owner: tokernUser.username,
},
});
if (ossConfig.isEndWithJson(key)) {
try {
await ossConfig.deleteObject(key);
} catch (e) {}
}
await config.destroy(); await config.destroy();
} else { } else {
ctx.throw(403, 'no permission'); ctx.throw(403, 'no permission');

View File

@ -0,0 +1,18 @@
export const defaultKeys = [
{
key: 'upload.json',
data: { key: 'upload', version: '1.0.0' },
},
{
key: 'workspace.json',
data: { key: 'workspace', version: '1.0.0' },
},
{
key: 'ai.json',
data: { key: 'ai', version: '1.0.0' },
},
{
key: 'vip.json',
data: { key: 'vip', version: '1.0.0' },
},
];

View File

@ -25,6 +25,7 @@ export class ConfigModel extends Model {
declare key: string; declare key: string;
declare data: ConfigData; // files declare data: ConfigData; // files
declare uid: string; declare uid: string;
declare hash: string;
/** /**
* *
* @param key key * @param key key

227
src/routes/mark/list.ts Normal file
View File

@ -0,0 +1,227 @@
import { app } from '@/app.ts';
import { MarkModel } from './model.ts';
import { MarkServices } from './services/mark.ts';
import dayjs from 'dayjs';
app
.route({
path: 'mark',
key: 'list',
description: 'mark list.',
middleware: ['auth'],
})
.define(async (ctx) => {
const tokenUser = ctx.state.tokenUser;
ctx.body = await MarkServices.getList({
uid: tokenUser.id,
query: ctx.query,
queryType: 'simple',
});
})
.addTo(app);
app
.route({
path: 'mark',
key: 'getVersion',
middleware: ['auth'],
})
.define(async (ctx) => {
const tokenUser = ctx.state.tokenUser;
const { id } = ctx.query;
if (id) {
const markModel = await MarkModel.findByPk(id);
if (!markModel) {
ctx.throw(404, 'mark not found');
}
if (markModel.uid !== tokenUser.id) {
ctx.throw(403, 'no permission');
}
ctx.body = {
version: Number(markModel.version),
updatedAt: markModel.updatedAt,
createdAt: markModel.createdAt,
id: markModel.id,
};
} else {
const [markModel, created] = await MarkModel.findOrCreate({
where: {
uid: tokenUser.id,
puid: tokenUser.uid,
title: dayjs().format('YYYY-MM-DD'),
},
defaults: {
title: dayjs().format('YYYY-MM-DD'),
uid: tokenUser.id,
markType: 'wallnote',
tags: ['daily'],
},
});
ctx.body = {
version: Number(markModel.version),
updatedAt: markModel.updatedAt,
createdAt: markModel.createdAt,
id: markModel.id,
created: created,
};
}
})
.addTo(app);
app
.route({
path: 'mark',
key: 'get',
middleware: ['auth'],
})
.define(async (ctx) => {
const tokenUser = ctx.state.tokenUser;
const { id } = ctx.query;
if (id) {
const markModel = await MarkModel.findByPk(id);
if (!markModel) {
ctx.throw(404, 'mark not found');
}
if (markModel.uid !== tokenUser.id) {
ctx.throw(403, 'no permission');
}
ctx.body = markModel;
} else {
// id 不存在获取当天的title为 日期的一条数据
const [markModel, created] = await MarkModel.findOrCreate({
where: {
uid: tokenUser.id,
puid: tokenUser.uid,
title: dayjs().format('YYYY-MM-DD'),
},
defaults: {
title: dayjs().format('YYYY-MM-DD'),
uid: tokenUser.id,
markType: 'wallnote',
tags: ['daily'],
uname: tokenUser.username,
puid: tokenUser.uid,
version: 1,
},
});
ctx.body = markModel;
}
})
.addTo(app);
app
.route({
path: 'mark',
key: 'update',
middleware: ['auth'],
})
.define(async (ctx) => {
const tokenUser = ctx.state.tokenUser;
const { id, ...data } = ctx.query.data || {};
let markModel: MarkModel;
if (id) {
markModel = await MarkModel.findByPk(id);
if (!markModel) {
ctx.throw(404, 'mark not found');
}
if (markModel.uid !== tokenUser.id) {
ctx.throw(403, 'no permission');
}
const version = Number(markModel.version) + 1;
await markModel.update({ ...markModel.data, ...data, version });
} else {
markModel = await MarkModel.create({
...data,
uname: tokenUser.username,
uid: tokenUser.id,
puid: tokenUser.uid,
});
}
ctx.body = markModel;
})
.addTo(app);
app
.route({
path: 'mark',
key: 'updateNode',
middleware: ['auth'],
})
.define(async (ctx) => {
const tokenUser = ctx.state.tokenUser;
const operate = ctx.query.operate || 'update';
const { id, node } = ctx.query.data || {};
const markModel = await MarkModel.findByPk(id);
if (!markModel) {
ctx.throw(404, 'mark not found');
}
if (markModel.uid !== tokenUser.id) {
ctx.throw(403, 'no permission');
}
await MarkModel.updateJsonNode(id, node, { operate });
ctx.body = markModel;
})
.addTo(app);
app
.route({
path: 'mark',
key: 'updateNodes',
middleware: ['auth'],
})
.define(async (ctx) => {
const tokenUser = ctx.state.tokenUser;
const { id, nodeOperateList } = ctx.query.data || {};
const markModel = await MarkModel.findByPk(id);
if (!markModel) {
ctx.throw(404, 'mark not found');
}
if (markModel.uid !== tokenUser.id) {
ctx.throw(403, 'no permission');
}
if (!nodeOperateList || !Array.isArray(nodeOperateList) || nodeOperateList.length === 0) {
ctx.throw(400, 'nodeOperateList is required');
}
if (nodeOperateList.some((node) => !node.node)) {
ctx.throw(400, 'nodeOperateList node is required');
}
const newmark = await MarkModel.updateJsonNodes(id, nodeOperateList);
ctx.body = newmark;
})
.addTo(app);
app
.route({
path: 'mark',
key: 'delete',
middleware: ['auth'],
})
.define(async (ctx) => {
const tokenUser = ctx.state.tokenUser;
const { id } = ctx.query;
const markModel = await MarkModel.findByPk(id);
if (!markModel) {
ctx.throw(404, 'mark not found');
}
if (markModel.uid !== tokenUser.id) {
ctx.throw(403, 'no permission');
}
await markModel.destroy();
ctx.body = markModel;
})
.addTo(app);
app
.route({ path: 'mark', key: 'getMenu', description: '获取菜单', middleware: ['auth'] })
.define(async (ctx) => {
const tokenUser = ctx.state.tokenUser;
const { rows, count } = await MarkModel.findAndCountAll({
where: {
uid: tokenUser.id,
},
attributes: ['id', 'title', 'summary', 'tags', 'thumbnail', 'link', 'createdAt', 'updatedAt'],
});
ctx.body = {
list: rows,
total: count,
};
})
.addTo(app);

319
src/routes/mark/model.ts Normal file
View File

@ -0,0 +1,319 @@
import { useContextKey } from '@kevisual/use-config/context';
import { nanoid, customAlphabet } from 'nanoid';
import { DataTypes, Model, ModelAttributes } from 'sequelize';
import type { Sequelize } from 'sequelize';
export const random = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ');
export type Mark = Partial<InstanceType<typeof MarkModel>>;
export type MarkData = {
md?: string; // markdown
mdList?: string[]; // markdown list
type?: string; // 类型 markdown | json | html | image | video | audio | code | link | file
data?: any;
push?: boolean; // 是否推送到elasticsearch
pushTime?: Date; // 推送时间
summary?: string; // 摘要
nodes?: MarkDataNode[]; // 节点
[key: string]: any;
};
export type MarkFile = {
id: string;
name: string;
url: string;
size: number;
type: 'self' | 'data' | 'generate'; // generate为生成文件
query: string; // 'data.nodes[id].content';
hash: string;
fileKey: string; // 文件的名称, 唯一
};
export type MarkDataNode = {
id?: string;
[key: string]: any;
};
export type MarkConfig = {
[key: string]: any;
};
export type MarkAuth = {
[key: string]: any;
};
/**
*
* auth
* config
*
*/
export class MarkModel extends Model {
declare id: string;
declare title: string; // 标题可以ai生成
declare description: string; // 描述可以ai生成
declare cover: string; // 封面可以ai生成
declare thumbnail: string; // 缩略图
declare markType: string; // markdown | json | html | image | video | audio | code | link | file
declare link: string; // 访问链接
declare tags: string[]; // 标签
declare summary: string; // 摘要, description的简化版
declare data: MarkData; // 数据
declare uid: string; // 操作用户的id
declare puid: string; // 父级用户的id, 真实用户
declare config: MarkConfig; // mark属于一定不会暴露的内容。
declare fileList: MarkFile[]; // 文件管理
declare uname: string; // 用户的名称, 或者着别名
declare createdAt: Date;
declare updatedAt: Date;
declare version: number;
/**
* data中的node的节点node的id
* @param param0
*/
static async updateJsonNode(id: string, node: MarkDataNode, opts?: { operate?: 'update' | 'delete'; Model?: any; sequelize?: Sequelize }) {
const sequelize = opts?.sequelize || (await useContextKey('sequelize'));
const transaction = await sequelize.transaction(); // 开启事务
const operate = opts.operate || 'update';
const isUpdate = operate === 'update';
const Model = opts.Model || MarkModel;
try {
// 1. 获取当前的 JSONB 字段值(加锁)
const mark = await Model.findByPk(id, {
transaction,
lock: transaction.LOCK.UPDATE, // 加锁,防止其他事务同时修改
});
if (!mark) {
throw new Error('Mark not found');
}
// 2. 修改特定的数组元素
const data = mark.data as MarkData;
const items = data.nodes;
if (!node.id) {
node.id = random(12);
}
// 找到要更新的元素
const itemIndex = items.findIndex((item) => item.id === node.id);
if (itemIndex === -1) {
isUpdate && items.push(node);
} else {
if (isUpdate) {
items[itemIndex] = node;
} else {
items.splice(itemIndex, 1);
}
}
const version = Number(mark.version) + 1;
// 4. 更新 JSONB 字段
const result = await mark.update(
{
data: {
...data,
nodes: items,
},
version,
},
{ transaction },
);
await transaction.commit();
return result;
} catch (error) {
await transaction.rollback();
throw error;
}
}
static async updateJsonNodes(id: string, nodes: { node: MarkDataNode; operate?: 'update' | 'delete' }[], opts?: { Model?: any; sequelize?: Sequelize }) {
const sequelize = opts?.sequelize || (await useContextKey('sequelize'));
const transaction = await sequelize.transaction(); // 开启事务
const Model = opts?.Model || MarkModel;
try {
const mark = await Model.findByPk(id, {
transaction,
lock: transaction.LOCK.UPDATE, // 加锁,防止其他事务同时修改
});
if (!mark) {
throw new Error('Mark not found');
}
const data = mark.data as MarkData;
const _nodes = data.nodes || [];
// 过滤不在nodes中的节点
const blankNodes = nodes.filter((node) => !_nodes.find((n) => n.id === node.node.id)).map((node) => node.node);
// 更新或删除节点
const newNodes = _nodes
.map((node) => {
const nodeOperate = nodes.find((n) => n.node.id === node.id);
if (nodeOperate) {
if (nodeOperate.operate === 'delete') {
return null;
}
return nodeOperate.node;
}
return node;
})
.filter((node) => node !== null);
const version = Number(mark.version) + 1;
const result = await mark.update(
{
data: {
...data,
nodes: [...blankNodes, ...newNodes],
},
version,
},
{ transaction },
);
await transaction.commit();
return result;
} catch (error) {
await transaction.rollback();
throw error;
}
}
static async updateData(id: string, data: MarkData, opts: { Model?: any; sequelize?: Sequelize }) {
const sequelize = opts.sequelize || (await useContextKey('sequelize'));
const transaction = await sequelize.transaction(); // 开启事务
const Model = opts.Model || MarkModel;
const mark = await Model.findByPk(id, {
transaction,
lock: transaction.LOCK.UPDATE, // 加锁,防止其他事务同时修改
});
if (!mark) {
throw new Error('Mark not found');
}
const version = Number(mark.version) + 1;
const result = await mark.update(
{
...mark.data,
...data,
data: {
...mark.data,
...data,
},
version,
},
{ transaction },
);
await transaction.commit();
return result;
}
static async createNew(data: any, opts: { Model?: any; sequelize?: Sequelize }) {
const sequelize = opts.sequelize || (await useContextKey('sequelize'));
const transaction = await sequelize.transaction(); // 开启事务
const Model = opts.Model || MarkModel;
const result = await Model.create({ ...data, version: 1 }, { transaction });
await transaction.commit();
return result;
}
}
export type MarkInitOpts<T = any> = {
tableName: string;
sequelize?: Sequelize;
callInit?: (attribute: ModelAttributes) => ModelAttributes;
Model?: T;
};
export type Opts = {
sync?: boolean;
alter?: boolean;
logging?: boolean;
force?: boolean;
};
export const MarkMInit = async <T = any>(opts: MarkInitOpts<T>, sync?: Opts) => {
const sequelize = await useContextKey('sequelize');
opts.sequelize = opts.sequelize || sequelize;
const { callInit, Model, ...optsRest } = opts;
const modelAttribute = {
id: {
type: DataTypes.UUID,
primaryKey: true,
defaultValue: DataTypes.UUIDV4,
comment: 'id',
},
title: {
type: DataTypes.TEXT,
defaultValue: '',
},
markType: {
type: DataTypes.TEXT,
defaultValue: 'md', // markdown | json | html | image | video | audio | code | link | file
comment: '类型',
},
description: {
type: DataTypes.TEXT,
defaultValue: '',
},
cover: {
type: DataTypes.TEXT,
defaultValue: '',
comment: '封面',
},
thumbnail: {
type: DataTypes.TEXT,
defaultValue: '',
comment: '缩略图',
},
link: {
type: DataTypes.TEXT,
defaultValue: '',
comment: '链接',
},
tags: {
type: DataTypes.JSONB,
defaultValue: [],
},
summary: {
type: DataTypes.TEXT,
defaultValue: '',
comment: '摘要',
},
config: {
type: DataTypes.JSONB,
defaultValue: {},
},
data: {
type: DataTypes.JSONB,
defaultValue: {},
},
fileList: {
type: DataTypes.JSONB,
defaultValue: [],
},
uname: {
type: DataTypes.STRING,
defaultValue: '',
comment: '用户的名称, 更新后的用户的名称',
},
version: {
type: DataTypes.INTEGER, // 更新刷新版本,多人协作
defaultValue: 1,
},
uid: {
type: DataTypes.UUID,
allowNull: true,
},
puid: {
type: DataTypes.UUID,
allowNull: true,
},
};
const InitModel = Model || MarkModel;
// @ts-ignore
InitModel.init(callInit ? callInit(modelAttribute) : modelAttribute, {
sequelize,
paranoid: true,
...optsRest,
});
console.log('MarkModel init', optsRest);
if (sync && sync.sync) {
const { sync: _, ...rest } = sync;
console.log('MarkModel sync', rest);
MarkModel.sync({ alter: true, logging: false, ...rest }).catch((e) => {
console.error('MarkModel sync', e);
});
}
};
export const markModelInit = MarkMInit;
export const syncMarkModel = async (sync?: Opts) => {
const sequelize = await useContextKey('sequelize');
await MarkMInit({ sequelize, tableName: 'micro_mark' }, sync);
};

View File

@ -0,0 +1,58 @@
import { FindAttributeOptions, Op } from 'sequelize';
import { MarkModel } from '../model.ts';
export class MarkServices {
static getList = async (opts: {
/** 查询用户的 */
uid?: string;
query?: {
page?: number;
pageSize?: number;
search?: string;
markType?: string;
sort?: string;
};
/**
*
* simple: 简单查询
*/
queryType?: string;
}) => {
const { uid, query } = opts;
const { page = 1, pageSize = 999, search, sort = 'DESC' } = query;
const searchWhere = search
? {
[Op.or]: [{ title: { [Op.like]: `%${search}%` } }, { summary: { [Op.like]: `%${search}%` } }],
}
: {};
if (opts.query?.markType) {
searchWhere['markType'] = opts.query.markType;
}
const attributes: FindAttributeOptions = {
exclude: [],
};
const queryType = opts.queryType || 'simple';
if (queryType === 'simple') {
// attributes.include = ['id', 'title', 'link', 'summary', 'thumbnail', 'markType', 'tags', 'uid', 'share', 'uname'];
attributes.exclude = ['data', 'config', 'cover', 'description'];
}
const { rows, count } = await MarkModel.findAndCountAll({
where: {
uid: uid,
...searchWhere,
},
order: [['updatedAt', sort]],
attributes: attributes,
limit: pageSize,
offset: (page - 1) * pageSize,
});
return {
pagination: {
current: page,
pageSize,
total: count,
},
list: rows,
};
};
}

View File

@ -12,8 +12,10 @@ export const createCookie = (token: any, ctx: any) => {
if (!domain) { if (!domain) {
return; return;
} }
if (ctx.res.cookie) { const browser = ctx.req.headers['user-agent'];
ctx.res.cookie('token', token.token, { const isBrowser = browser.includes('Mozilla'); // 浏览器
if (isBrowser && ctx.res.cookie) {
ctx.res.cookie('token', token.accessToken || token?.token, {
maxAge: 7 * 24 * 60 * 60 * 1000, // 过期时间, 设置7天 maxAge: 7 * 24 * 60 * 60 * 1000, // 过期时间, 设置7天
domain, domain,
sameSite: 'lax', sameSite: 'lax',
@ -21,7 +23,48 @@ export const createCookie = (token: any, ctx: any) => {
}); });
} }
}; };
const clearCookie = (ctx: any) => { export type ReqHeaders = {
host: string;
'x-forwarded-for': string;
'x-real-ip': string;
'sec-ch-ua': string; // 浏览器
'sec-ch-ua-mobile': string; // 移动设备
'sec-ch-ua-platform': string; // 平台
'sec-ch-ua-arch': string; // 架构
'sec-ch-ua-bitness': string; // 位数
'sec-ch-ua-full-version': string; // 完整版本
'sec-ch-ua-full-version-list': string; // 完整版本列表
'sec-fetch-dest': string; // 目标
'sec-fetch-mode': string; // 模式
'sec-fetch-site': string; // 站点
'sec-fetch-user': string; // 用户
'upgrade-insecure-requests': string; // 升级不安全请求
'user-agent': string; // 用户代理
accept: string; // 接受
'accept-language': string; // 接受语言
'accept-encoding': string; // 接受编码
'cache-control': string; // 缓存控制
pragma: string; // 预先
expires: string; // 过期
connection: string; // 连接
cookie: string; // 饼干
};
export const getSomeInfoFromReq = (ctx: any) => {
const headers = ctx.req.headers as ReqHeaders;
const userAgent = headers['user-agent'];
const host = headers['host'];
const ip = headers['x-forwarded-for'] || ctx.req.connection.remoteAddress;
console.log('req headers', headers);
return {
'user-agent': userAgent,
browser: userAgent,
isBrowser: userAgent.includes('Mozilla'),
host,
ip,
headers,
};
};
export const clearCookie = (ctx: any) => {
if (!domain) { if (!domain) {
return; return;
} }
@ -87,7 +130,12 @@ app
ctx.throw(500, 'Password error'); ctx.throw(500, 'Password error');
} }
user.expireOrgs(); user.expireOrgs();
const token = await user.createToken(null, loginType); const someInfo = getSomeInfoFromReq(ctx);
const token = await user.createToken(null, loginType, {
ip: someInfo.ip,
browser: someInfo['user-agent'],
host: someInfo.host,
});
createCookie(token, ctx); createCookie(token, ctx);
ctx.body = token; ctx.body = token;
}) })

View File

@ -1,30 +1,72 @@
import { app } from '@/app.ts'; import { app } from '@/app.ts';
import { User } from '@/models/user.ts'; import { User } from '@/models/user.ts';
import MD5 from 'crypto-js/md5.js'; import MD5 from 'crypto-js/md5.js';
import { authCan } from '@kevisual/code-center-module/models';
import jsonwebtoken from 'jsonwebtoken'; import jsonwebtoken from 'jsonwebtoken';
// const tokenData: Record<string, string> = {};
import { redis } from '@/app.ts'; import { redis } from '@/app.ts';
import { createCookie } from './me.ts'; import { createCookie, clearCookie } from './me.ts';
app app
.route({ .route({
path: 'user', path: 'user',
key: 'webLogin', key: 'webLogin',
middleware: ['auth'], middleware: [authCan],
}) })
.define(async (ctx) => { .define(async (ctx) => {
const tokenUser = ctx.state.tokenUser; const tokenUser = ctx.state.tokenUser;
const token = ctx.query.token;
const { loginToken, sign, randomId } = ctx.query || {}; const { loginToken, sign, randomId } = ctx.query || {};
const setErrorLoginTokenRedis = async (loginToken: string) => {
await redis.set(loginToken, JSON.stringify({}), 'EX', 2 * 60); // 2分钟
};
if (!tokenUser) {
if (token) {
console.log('web-login, token', ' run clearCookie', token, tokenUser);
// clearCookie(ctx);
} else {
// const message = 'token is expired, please login in web page. ';
}
try {
ctx.res.setHeader('Content-Type', 'text/html');
const createRedirectHtml = () => {
const reqUrl = ctx.req.url;
return `
<html lang="zh-CN">
<body>
<h1>login with web page</h1>
<a href="${reqUrl}">${reqUrl}</a>
<script>
const redirect = new URL('${reqUrl}', window.location.origin);
const encodeRedirect = encodeURIComponent(redirect.toString());
const toPage = new URL('/user/login/?user-check=true&redirect='+encodeRedirect, window.location.origin);
setTimeout(() => {
window.location.href = toPage.toString();
}, 1000);
</script>
</body>
</html>
`;
};
ctx.res.end(createRedirectHtml());
} catch (e) {
await setErrorLoginTokenRedis(loginToken);
ctx.throw(400, 'token is expired and redirect error');
}
return;
}
if (!loginToken) { if (!loginToken) {
await setErrorLoginTokenRedis(loginToken);
ctx.throw(400, 'loginToken is required'); ctx.throw(400, 'loginToken is required');
} }
if (!sign) { if (!sign) {
await setErrorLoginTokenRedis(loginToken);
ctx.throw(400, 'sign is required'); ctx.throw(400, 'sign is required');
} }
if (!randomId) { if (!randomId) {
await setErrorLoginTokenRedis(loginToken);
ctx.throw(400, 'randomId is required'); ctx.throw(400, 'randomId is required');
} }
const tokenSecret = 'xiao' + randomId; const tokenSecret = 'xiao' + randomId;
@ -32,16 +74,19 @@ app
try { try {
payload = jsonwebtoken.verify(loginToken, tokenSecret); payload = jsonwebtoken.verify(loginToken, tokenSecret);
} catch (e) { } catch (e) {
await setErrorLoginTokenRedis(loginToken);
ctx.throw(400, 'loginToken error'); ctx.throw(400, 'loginToken error');
} }
const { timestamp } = payload; const { timestamp } = payload;
const checkSign = MD5(`${tokenSecret}${timestamp}`).toString(); const checkSign = MD5(`${tokenSecret}${timestamp}`).toString();
if (sign !== checkSign) { if (sign !== checkSign) {
await setErrorLoginTokenRedis(loginToken);
ctx.throw(400, 'sign error'); ctx.throw(400, 'sign error');
} }
const user = await User.findByPk(tokenUser.id); const user = await User.findByPk(tokenUser.id);
if (!user) { if (!user) {
await setErrorLoginTokenRedis(loginToken);
ctx.throw(400, 'user not found'); ctx.throw(400, 'user not found');
} }
const data = await user.createToken(null, 'plugin', { loginWith: 'cli' }); const data = await user.createToken(null, 'plugin', { loginWith: 'cli' });
@ -63,11 +108,16 @@ app
// const data = tokenData[loginToken]; // const data = tokenData[loginToken];
const data = await redis.get(loginToken); const data = await redis.get(loginToken);
if (data) { if (data) {
ctx.body = JSON.parse(data); const token = JSON.parse(data);
await redis.expire(loginToken, 3600); if (token.accessToken) {
ctx.body = token;
createCookie(token, ctx);
} else {
ctx.throw(500, 'Checked error Failed, login failed, please login again');
}
await redis.expire(loginToken, 2 * 60); // 2分钟
} else { } else {
ctx.throw(400, 'Checked Failed'); ctx.throw(400, 'Checked Failed');
} }
createCookie(data, ctx);
}) })
.addTo(app); .addTo(app);

46
src/scripts/sync-mark.ts Normal file
View File

@ -0,0 +1,46 @@
import { useContextKey } from '@kevisual/use-config/context';
import { sequelize } from '../modules/sequelize.ts';
import { MarkModel, syncMarkModel } from '../routes/mark/model.ts';
export const sequelize2 = useContextKey('sequelize', () => sequelize);
const main = async () => {
// 把所有markmodel的表的source字段的类型改为jsonb
// const marks = await MarkModel.findAll();
// const mark = marks[0];
// for (const mark of marks) {
// if (mark.source) {
// try {
// await MarkModel.update({ source: {} }, { where: { id: mark.id } });
// } catch (e) {
// console.error('update source error:', e);
// }
// }
// }
console.log('update source success');
// await MarkModel.sync({ alter: true, logging: true }).catch((e) => {
// console.error('MarkModel.sync error:', e);
// });
await syncMarkModel({ alter: true, logging: true, sync: true });
};
main();
const sql = `ALTER TABLE "micro_mark" ALTER COLUMN "source" DROP NOT NULL;ALTER TABLE "micro_mark" ALTER COLUMN "source" SET DEFAULT '{}';ALTER TABLE "micro_mark" ALTER COLUMN "source" TYPE JSONB ; COMMENT ON COLUMN "micro_mark"."source" IS '需要的数据的来源,作为一个备注使用。';`;
// sequelize
/**
*
*/
const runSql = async () => {
sequelize
.query(sql)
.then(() => {
console.log('update source success');
})
.catch((e) => {
console.error('update source error:', e);
});
};
// runSql();

@ -1 +1 @@
Subproject commit 5563ded0a19d60c34cce6272e272c6405bcab304 Subproject commit 68332c9c8dd74751ee3b1a6944984ee02313f929

@ -1 +1 @@
Subproject commit bc6df19c9c5365b7950929ebe1be9fbb7224c670 Subproject commit f8e2f74d5e4ac68e2a78db66b25246074f2675bd