feat: add login for plugin
This commit is contained in:
parent
74a484718a
commit
cb490470c1
@ -24,6 +24,7 @@
|
||||
"ssh": "ssh -L 6379:localhost:6379 light ",
|
||||
"ssh:sky": "ssh -L 6379:172.21.32.13:6379 sky",
|
||||
"dev:lib": "turbo run dev:lib",
|
||||
"build:lib": "turbo run build",
|
||||
"dev:oss": "turbo run dev:lib --filter=@kevisual/oss"
|
||||
},
|
||||
"keywords": [],
|
||||
@ -37,7 +38,7 @@
|
||||
"dependencies": {
|
||||
"@kevisual/local-app-manager": "0.1.9",
|
||||
"@kevisual/router": "0.0.9",
|
||||
"@kevisual/use-config": "^1.0.9",
|
||||
"@kevisual/use-config": "^1.0.10",
|
||||
"@types/semver": "^7.5.8",
|
||||
"archiver": "^7.0.1",
|
||||
"crypto-js": "^4.2.0",
|
||||
|
348
pnpm-lock.yaml
generated
348
pnpm-lock.yaml
generated
@ -15,13 +15,13 @@ importers:
|
||||
dependencies:
|
||||
'@kevisual/local-app-manager':
|
||||
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':
|
||||
specifier: 0.0.9
|
||||
version: 0.0.9
|
||||
'@kevisual/use-config':
|
||||
specifier: ^1.0.9
|
||||
version: 1.0.9
|
||||
specifier: ^1.0.10
|
||||
version: 1.0.10(dotenv@16.4.7)
|
||||
'@types/semver':
|
||||
specifier: ^7.5.8
|
||||
version: 7.5.8
|
||||
@ -189,16 +189,16 @@ importers:
|
||||
specifier: ^0.0.9
|
||||
version: 0.0.9
|
||||
'@kevisual/use-config':
|
||||
specifier: ^1.0.9
|
||||
version: 1.0.9
|
||||
specifier: ^1.0.10
|
||||
version: 1.0.10(dotenv@16.4.7)
|
||||
ioredis:
|
||||
specifier: ^5.6.0
|
||||
version: 5.6.0
|
||||
nanoid:
|
||||
specifier: ^5.1.2
|
||||
version: 5.1.3
|
||||
specifier: ^5.1.5
|
||||
version: 5.1.5
|
||||
pg:
|
||||
specifier: ^8.13.3
|
||||
specifier: ^8.14.1
|
||||
version: 8.14.1
|
||||
sequelize:
|
||||
specifier: ^6.37.6
|
||||
@ -215,22 +215,22 @@ importers:
|
||||
version: 0.0.6
|
||||
'@rollup/plugin-alias':
|
||||
specifier: ^5.1.1
|
||||
version: 5.1.1(rollup@4.35.0)
|
||||
version: 5.1.1(rollup@4.36.0)
|
||||
'@rollup/plugin-commonjs':
|
||||
specifier: ^28.0.2
|
||||
version: 28.0.3(rollup@4.35.0)
|
||||
specifier: ^28.0.3
|
||||
version: 28.0.3(rollup@4.36.0)
|
||||
'@rollup/plugin-json':
|
||||
specifier: ^6.1.0
|
||||
version: 6.1.0(rollup@4.35.0)
|
||||
version: 6.1.0(rollup@4.36.0)
|
||||
'@rollup/plugin-node-resolve':
|
||||
specifier: ^16.0.0
|
||||
version: 16.0.1(rollup@4.35.0)
|
||||
specifier: ^16.0.1
|
||||
version: 16.0.1(rollup@4.36.0)
|
||||
'@rollup/plugin-replace':
|
||||
specifier: ^6.0.2
|
||||
version: 6.0.2(rollup@4.35.0)
|
||||
version: 6.0.2(rollup@4.36.0)
|
||||
'@rollup/plugin-typescript':
|
||||
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':
|
||||
specifier: ^6.0.3
|
||||
version: 6.0.3
|
||||
@ -247,11 +247,11 @@ importers:
|
||||
specifier: ^4.17.12
|
||||
version: 4.17.12
|
||||
'@types/node':
|
||||
specifier: ^22.13.9
|
||||
version: 22.13.10
|
||||
specifier: ^22.13.11
|
||||
version: 22.13.11
|
||||
'@types/react':
|
||||
specifier: ^19.0.10
|
||||
version: 19.0.10
|
||||
specifier: ^19.0.12
|
||||
version: 19.0.12
|
||||
'@types/uuid':
|
||||
specifier: ^10.0.0
|
||||
version: 10.0.0
|
||||
@ -268,17 +268,17 @@ importers:
|
||||
specifier: latest
|
||||
version: 6.0.1
|
||||
rollup:
|
||||
specifier: ^4.34.9
|
||||
version: 4.35.0
|
||||
specifier: ^4.36.0
|
||||
version: 4.36.0
|
||||
rollup-plugin-copy:
|
||||
specifier: ^3.5.0
|
||||
version: 3.5.0
|
||||
rollup-plugin-dts:
|
||||
specifier: ^6.1.1
|
||||
version: 6.1.1(rollup@4.35.0)(typescript@5.8.2)
|
||||
specifier: ^6.2.1
|
||||
version: 6.2.1(rollup@4.36.0)(typescript@5.8.2)
|
||||
rollup-plugin-esbuild:
|
||||
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:
|
||||
specifier: ^5.9.0
|
||||
version: 5.9.0
|
||||
@ -516,8 +516,10 @@ packages:
|
||||
'@kevisual/types@0.0.6':
|
||||
resolution: {integrity: sha512-7yxe1QmuC5g7lI/1Hm+zXly8if0z+ZqGM1SVOVv2VNRwRAVYBJDc365zWCCfRwE+5YaB2daWTe5zBOU4EkltkQ==}
|
||||
|
||||
'@kevisual/use-config@1.0.9':
|
||||
resolution: {integrity: sha512-lJz98WWL178QUaf/rkM9feMm0aUnYd6ikm3ma/9Zi/K2QNrxbTRAgMGkCggUalAES8IbUvEsg+Q+Y2RaPLxCmw==}
|
||||
'@kevisual/use-config@1.0.10':
|
||||
resolution: {integrity: sha512-fH2B4BnR4+OjR3PzAegF8H9RJpyFZu6BnVDyfvSSZavZMurufkJ949jizoRde+bNAHff/PRcpa5EZg2imZNf1g==}
|
||||
peerDependencies:
|
||||
dotenv: ^16.4.7
|
||||
|
||||
'@ljharb/resumer@0.1.3':
|
||||
resolution: {integrity: sha512-d+tsDgfkj9X5QTriqM4lKesCkMMJC3IrbPKHvayP00ELx2axdXvDfWkqjxrLXIzGcQzmj7VAUT1wopqARTvafw==}
|
||||
@ -624,191 +626,96 @@ packages:
|
||||
rollup:
|
||||
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':
|
||||
resolution: {integrity: sha512-jgrXjjcEwN6XpZXL0HUeOVGfjXhPyxAbbhD0BlXUB+abTOpbPiN5Wb3kOT7yb+uEtATNYF5x5gIfwutmuBA26w==}
|
||||
cpu: [arm]
|
||||
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':
|
||||
resolution: {integrity: sha512-NyfuLvdPdNUfUNeYKUwPwKsE5SXa2J6bCt2LdB/N+AxShnkpiczi3tcLJrm5mA+eqpy0HmaIY9F6XCa32N5yzg==}
|
||||
cpu: [arm64]
|
||||
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':
|
||||
resolution: {integrity: sha512-JQ1Jk5G4bGrD4pWJQzWsD8I1n1mgPXq33+/vP4sk8j/z/C2siRuxZtaUA7yMTf71TCZTZl/4e1bfzwUmFb3+rw==}
|
||||
cpu: [arm64]
|
||||
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':
|
||||
resolution: {integrity: sha512-6c6wMZa1lrtiRsbDziCmjE53YbTkxMYhhnWnSW8R/yqsM7a6mSJ3uAVT0t8Y/DGt7gxUWYuFM4bwWk9XCJrFKA==}
|
||||
cpu: [x64]
|
||||
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':
|
||||
resolution: {integrity: sha512-KXVsijKeJXOl8QzXTsA+sHVDsFOmMCdBRgFmBb+mfEb/7geR7+C8ypAml4fquUt14ZyVXaw2o1FWhqAfOvA4sg==}
|
||||
cpu: [arm64]
|
||||
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':
|
||||
resolution: {integrity: sha512-dVeWq1ebbvByI+ndz4IJcD4a09RJgRYmLccwlQ8bPd4olz3Y213uf1iwvc7ZaxNn2ab7bjc08PrtBgMu6nb4pQ==}
|
||||
cpu: [x64]
|
||||
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':
|
||||
resolution: {integrity: sha512-bvXVU42mOVcF4le6XSjscdXjqx8okv4n5vmwgzcmtvFdifQ5U4dXFYaCB87namDRKlUL9ybVtLQ9ztnawaSzvg==}
|
||||
cpu: [arm]
|
||||
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':
|
||||
resolution: {integrity: sha512-JFIQrDJYrxOnyDQGYkqnNBtjDwTgbasdbUiQvcU8JmGDfValfH1lNpng+4FWlhaVIR4KPkeddYjsVVbmJYvDcg==}
|
||||
cpu: [arm]
|
||||
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':
|
||||
resolution: {integrity: sha512-KqjYVh3oM1bj//5X7k79PSCZ6CvaVzb7Qs7VMWS+SlWB5M8p3FqufLP9VNp4CazJ0CsPDLwVD9r3vX7Ci4J56A==}
|
||||
cpu: [arm64]
|
||||
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':
|
||||
resolution: {integrity: sha512-QiGnhScND+mAAtfHqeT+cB1S9yFnNQ/EwCg5yE3MzoaZZnIV0RV9O5alJAoJKX/sBONVKeZdMfO8QSaWEygMhw==}
|
||||
cpu: [arm64]
|
||||
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':
|
||||
resolution: {integrity: sha512-1ZPyEDWF8phd4FQtTzMh8FQwqzvIjLsl6/84gzUxnMNFBtExBtpL51H67mV9xipuxl1AEAerRBgBwFNpkw8+Lg==}
|
||||
cpu: [loong64]
|
||||
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':
|
||||
resolution: {integrity: sha512-VMPMEIUpPFKpPI9GZMhJrtu8rxnp6mJR3ZzQPykq4xc2GmdHj3Q4cA+7avMyegXy4n1v+Qynr9fR88BmyO74tg==}
|
||||
cpu: [ppc64]
|
||||
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':
|
||||
resolution: {integrity: sha512-ttE6ayb/kHwNRJGYLpuAvB7SMtOeQnVXEIpMtAvx3kepFQeowVED0n1K9nAdraHUPJ5hydEMxBpIR7o4nrm8uA==}
|
||||
cpu: [riscv64]
|
||||
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':
|
||||
resolution: {integrity: sha512-4a5gf2jpS0AIe7uBjxDeUMNcFmaRTbNv7NxI5xOCs4lhzsVyGR/0qBXduPnoWf6dGC365saTiwag8hP1imTgag==}
|
||||
cpu: [s390x]
|
||||
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':
|
||||
resolution: {integrity: sha512-5KtoW8UWmwFKQ96aQL3LlRXX16IMwyzMq/jSSVIIyAANiE1doaQsx/KRyhAvpHlPjPiSU/AYX/8m+lQ9VToxFQ==}
|
||||
cpu: [x64]
|
||||
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':
|
||||
resolution: {integrity: sha512-sycrYZPrv2ag4OCvaN5js+f01eoZ2U+RmT5as8vhxiFz+kxwlHrsxOwKPSA8WyS+Wc6Epid9QeI/IkQ9NkgYyQ==}
|
||||
cpu: [x64]
|
||||
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':
|
||||
resolution: {integrity: sha512-qbqt4N7tokFwwSVlWDsjfoHgviS3n/vZ8LK0h1uLG9TYIRuUTJC88E1xb3LM2iqZ/WTqNQjYrtmtGmrmmawB6A==}
|
||||
cpu: [arm64]
|
||||
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':
|
||||
resolution: {integrity: sha512-t+RY0JuRamIocMuQcfwYSOkmdX9dtkr1PbhKW42AMvaDQa+jOdpUYysroTF/nuPpAaQMWp7ye+ndlmmthieJrQ==}
|
||||
cpu: [ia32]
|
||||
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':
|
||||
resolution: {integrity: sha512-aRXd7tRZkWLqGbChgcMMDEHjOKudo1kChb1Jt1IfR8cY/KIpgNviLeJy5FUb9IpSuQj8dU2fAYNMPW/hLKOSTw==}
|
||||
cpu: [x64]
|
||||
@ -865,15 +772,9 @@ packages:
|
||||
'@types/node-forge@1.3.11':
|
||||
resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==}
|
||||
|
||||
'@types/node@22.13.10':
|
||||
resolution: {integrity: sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==}
|
||||
|
||||
'@types/node@22.13.11':
|
||||
resolution: {integrity: sha512-iEUCUJoU0i3VnrCmgoWCXttklWcvoCIx4jzcP22fioIVSdTmjgoEvmAO/QPw6TcS9k5FrNgn4w7q5lGOd1CT5g==}
|
||||
|
||||
'@types/react@19.0.10':
|
||||
resolution: {integrity: sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g==}
|
||||
|
||||
'@types/react@19.0.12':
|
||||
resolution: {integrity: sha512-V6Ar115dBDrjbtXSrS+/Oruobc+qVbbUxDFC1RSbRqLt5SYvxxyIDrSC85RWml54g+jfNeEMZhEj7wW07ONQhA==}
|
||||
|
||||
@ -1955,11 +1856,6 @@ packages:
|
||||
mz@2.7.0:
|
||||
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:
|
||||
resolution: {integrity: sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==}
|
||||
engines: {node: ^18 || >=20}
|
||||
@ -2302,13 +2198,6 @@ packages:
|
||||
resolution: {integrity: sha512-wI8D5dvYovRMx/YYKtUNt3Yxaw4ORC9xo6Gt9t22kveWz1enG9QrhVlagzwrxSC455xD1dHMKhIJkbsQ7d48BA==}
|
||||
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:
|
||||
resolution: {integrity: sha512-sR3CxYUl7i2CHa0O7bA45mCrgADyAQ0tVtGSqi3yvH28M+eg1+g5d7kQ9hLvEz5dorK3XVsH5L2jwHLQf72DzA==}
|
||||
engines: {node: '>=16'}
|
||||
@ -2323,11 +2212,6 @@ packages:
|
||||
esbuild: '>=0.18.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:
|
||||
resolution: {integrity: sha512-zwATAXNQxUcd40zgtQG0ZafcRK4g004WtEl7kbuhTWPvf07PsfohXl39jVUvPF7jvNAIkKPQ2XrsDlWuxBd++Q==}
|
||||
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
||||
@ -3018,11 +2902,11 @@ snapshots:
|
||||
dependencies:
|
||||
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:
|
||||
'@kevisual/router': 0.0.9
|
||||
'@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
|
||||
|
||||
'@kevisual/router@0.0.9':
|
||||
@ -3036,9 +2920,10 @@ snapshots:
|
||||
|
||||
'@kevisual/types@0.0.6': {}
|
||||
|
||||
'@kevisual/use-config@1.0.9':
|
||||
'@kevisual/use-config@1.0.10(dotenv@16.4.7)':
|
||||
dependencies:
|
||||
'@kevisual/load': 0.0.4
|
||||
dotenv: 16.4.7
|
||||
|
||||
'@ljharb/resumer@0.1.3':
|
||||
dependencies:
|
||||
@ -3114,26 +2999,10 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- 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)':
|
||||
optionalDependencies:
|
||||
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)':
|
||||
dependencies:
|
||||
'@rollup/pluginutils': 5.1.2(rollup@4.36.0)
|
||||
@ -3146,28 +3015,12 @@ snapshots:
|
||||
optionalDependencies:
|
||||
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)':
|
||||
dependencies:
|
||||
'@rollup/pluginutils': 5.1.2(rollup@4.36.0)
|
||||
optionalDependencies:
|
||||
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)':
|
||||
dependencies:
|
||||
'@rollup/pluginutils': 5.1.2(rollup@4.36.0)
|
||||
@ -3178,13 +3031,6 @@ snapshots:
|
||||
optionalDependencies:
|
||||
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)':
|
||||
dependencies:
|
||||
'@rollup/pluginutils': 5.1.2(rollup@4.36.0)
|
||||
@ -3192,15 +3038,6 @@ snapshots:
|
||||
optionalDependencies:
|
||||
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)':
|
||||
dependencies:
|
||||
'@rollup/pluginutils': 5.1.2(rollup@4.36.0)
|
||||
@ -3210,14 +3047,6 @@ snapshots:
|
||||
rollup: 4.36.0
|
||||
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)':
|
||||
dependencies:
|
||||
'@types/estree': 1.0.6
|
||||
@ -3226,117 +3055,60 @@ snapshots:
|
||||
optionalDependencies:
|
||||
rollup: 4.36.0
|
||||
|
||||
'@rollup/rollup-android-arm-eabi@4.35.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-android-arm-eabi@4.36.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-android-arm64@4.35.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-android-arm64@4.36.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-darwin-arm64@4.35.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-darwin-arm64@4.36.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-darwin-x64@4.35.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-darwin-x64@4.36.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-freebsd-arm64@4.35.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-freebsd-arm64@4.36.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-freebsd-x64@4.35.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-freebsd-x64@4.36.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-arm-gnueabihf@4.35.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-arm-gnueabihf@4.36.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-arm-musleabihf@4.35.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-arm-musleabihf@4.36.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-arm64-gnu@4.35.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-arm64-gnu@4.36.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-arm64-musl@4.35.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-arm64-musl@4.36.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-loongarch64-gnu@4.35.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-loongarch64-gnu@4.36.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-powerpc64le-gnu@4.35.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-powerpc64le-gnu@4.36.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-riscv64-gnu@4.35.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-riscv64-gnu@4.36.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-s390x-gnu@4.35.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-s390x-gnu@4.36.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-x64-gnu@4.35.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-x64-gnu@4.36.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-x64-musl@4.35.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-x64-musl@4.36.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-win32-arm64-msvc@4.35.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-win32-arm64-msvc@4.36.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-win32-ia32-msvc@4.35.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-win32-ia32-msvc@4.36.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-win32-x64-msvc@4.35.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-win32-x64-msvc@4.36.0':
|
||||
optional: true
|
||||
|
||||
@ -3394,18 +3166,10 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/node': 22.13.11
|
||||
|
||||
'@types/node@22.13.10':
|
||||
dependencies:
|
||||
undici-types: 6.20.0
|
||||
|
||||
'@types/node@22.13.11':
|
||||
dependencies:
|
||||
undici-types: 6.20.0
|
||||
|
||||
'@types/react@19.0.10':
|
||||
dependencies:
|
||||
csstype: 3.1.3
|
||||
|
||||
'@types/react@19.0.12':
|
||||
dependencies:
|
||||
csstype: 3.1.3
|
||||
@ -4579,8 +4343,6 @@ snapshots:
|
||||
object-assign: 4.1.1
|
||||
thenify-all: 1.6.0
|
||||
|
||||
nanoid@5.1.3: {}
|
||||
|
||||
nanoid@5.1.5: {}
|
||||
|
||||
needle@2.4.0:
|
||||
@ -4969,14 +4731,6 @@ snapshots:
|
||||
globby: 10.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):
|
||||
dependencies:
|
||||
magic-string: 0.30.17
|
||||
@ -4985,17 +4739,6 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@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):
|
||||
dependencies:
|
||||
debug: 4.4.0
|
||||
@ -5007,31 +4750,6 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- 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:
|
||||
dependencies:
|
||||
'@types/estree': 1.0.6
|
||||
|
12
src/app.ts
12
src/app.ts
@ -5,10 +5,18 @@ import * as minioLib from './modules/minio.ts';
|
||||
import * as sequelizeLib from './modules/sequelize.ts';
|
||||
import { useContextKey, useContext } from '@kevisual/use-config/context';
|
||||
import { SimpleRouter } from '@kevisual/router/simple';
|
||||
|
||||
import { OssBase } from '@kevisual/oss/services';
|
||||
useConfig();
|
||||
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 redisPublisher = useContextKey('redisPublisher', () => redisLib.redisPublisher);
|
||||
export const redisSubscriber = useContextKey('redisSubscriber', () => redisLib.redisSubscriber);
|
||||
|
59
src/route.ts
59
src/route.ts
@ -1,8 +1,64 @@
|
||||
import './routes/index.ts';
|
||||
import { app } from './app.ts';
|
||||
import type { App } from '@kevisual/router';
|
||||
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);
|
||||
|
||||
app
|
||||
@ -53,6 +109,7 @@ app
|
||||
if (!tokenUser) {
|
||||
ctx.throw(401, 'No User For authorized');
|
||||
}
|
||||
|
||||
try {
|
||||
const user = await User.findOne({
|
||||
where: {
|
||||
|
78
src/routes/config/check.ts
Normal file
78
src/routes/config/check.ts
Normal 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);
|
45
src/routes/config/config-key.ts
Normal file
45
src/routes/config/config-key.ts
Normal 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);
|
@ -1,3 +1,5 @@
|
||||
import './list.ts';
|
||||
import './upload-config.ts';
|
||||
import './share-config.ts';
|
||||
import './check.ts';
|
||||
import './config-key.ts';
|
||||
|
@ -1,6 +1,9 @@
|
||||
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';
|
||||
|
||||
app
|
||||
.route({
|
||||
path: 'config',
|
||||
@ -31,56 +34,57 @@ app
|
||||
const tokernUser = ctx.state.tokenUser;
|
||||
const tuid = tokernUser.id;
|
||||
const { id, data, ...rest } = ctx.query?.data || {};
|
||||
let config: ConfigModel;
|
||||
if (id) {
|
||||
const config = await ConfigModel.findByPk(id);
|
||||
config = await ConfigModel.findByPk(id);
|
||||
let keyIsChange = false;
|
||||
if (rest?.key) {
|
||||
keyIsChange = rest.key !== config?.key;
|
||||
}
|
||||
if (config && config.uid === tuid) {
|
||||
if (keyIsChange) {
|
||||
const key = rest.key;
|
||||
const keyConfig = await ConfigModel.findOne({
|
||||
where: {
|
||||
key,
|
||||
uid: tuid,
|
||||
},
|
||||
});
|
||||
if (keyConfig && keyConfig.id !== id) {
|
||||
ctx.throw(403, 'key is already exists');
|
||||
}
|
||||
}
|
||||
await config.update({
|
||||
data: {
|
||||
...config.data,
|
||||
...data,
|
||||
},
|
||||
...rest,
|
||||
});
|
||||
if (config.data?.permission?.share === 'public') {
|
||||
await ShareConfigService.expireShareConfig(config.key, tokernUser.username);
|
||||
}
|
||||
ctx.body = config;
|
||||
} else {
|
||||
if (!config || config.uid !== tuid) {
|
||||
ctx.throw(403, 'no permission');
|
||||
}
|
||||
if (keyIsChange) {
|
||||
const key = rest.key;
|
||||
const keyConfig = await ConfigModel.findOne({
|
||||
where: {
|
||||
key,
|
||||
uid: tuid,
|
||||
},
|
||||
});
|
||||
if (keyConfig && keyConfig.id !== id) {
|
||||
ctx.throw(403, 'key is already exists');
|
||||
}
|
||||
}
|
||||
await config.update({
|
||||
data: {
|
||||
...config.data,
|
||||
...data,
|
||||
},
|
||||
...rest,
|
||||
});
|
||||
if (config.data?.permission?.share === 'public') {
|
||||
await ShareConfigService.expireShareConfig(config.key, tokernUser.username);
|
||||
}
|
||||
ctx.body = config;
|
||||
} else if (rest?.key) {
|
||||
// id 不存在,key存在,则属于更新,key不能重复
|
||||
const key = rest.key;
|
||||
const keyConfig = await ConfigModel.findOne({
|
||||
config = await ConfigModel.findOne({
|
||||
where: {
|
||||
key,
|
||||
uid: tuid,
|
||||
},
|
||||
});
|
||||
if (keyConfig) {
|
||||
await keyConfig.update({
|
||||
data: { ...keyConfig.data, ...data },
|
||||
if (config) {
|
||||
await config.update({
|
||||
data: { ...config.data, ...data },
|
||||
...rest,
|
||||
});
|
||||
ctx.body = keyConfig;
|
||||
ctx.body = config;
|
||||
} else {
|
||||
const config = await ConfigModel.create({
|
||||
// 根据key创建一个配置
|
||||
config = await ConfigModel.create({
|
||||
key,
|
||||
...rest,
|
||||
data: data,
|
||||
@ -89,15 +93,33 @@ app
|
||||
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不存在。创建一个新的配置
|
||||
const config = await ConfigModel.create({
|
||||
// id和key不存在。创建一个新的配置, 而且没有id的
|
||||
const newConfig = await ConfigModel.create({
|
||||
...rest,
|
||||
data: data,
|
||||
uid: tuid,
|
||||
});
|
||||
ctx.body = config;
|
||||
ctx.body = newConfig;
|
||||
})
|
||||
.addTo(app);
|
||||
|
||||
@ -154,6 +176,18 @@ app
|
||||
},
|
||||
});
|
||||
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();
|
||||
} else {
|
||||
ctx.throw(403, 'no permission');
|
||||
|
18
src/routes/config/models/default-keys.ts
Normal file
18
src/routes/config/models/default-keys.ts
Normal 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' },
|
||||
},
|
||||
];
|
@ -25,6 +25,7 @@ export class ConfigModel extends Model {
|
||||
declare key: string;
|
||||
declare data: ConfigData; // files
|
||||
declare uid: string;
|
||||
declare hash: string;
|
||||
/**
|
||||
* 获取用户配置
|
||||
* @param key 配置key
|
||||
|
227
src/routes/mark/list.ts
Normal file
227
src/routes/mark/list.ts
Normal 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
319
src/routes/mark/model.ts
Normal 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);
|
||||
};
|
58
src/routes/mark/services/mark.ts
Normal file
58
src/routes/mark/services/mark.ts
Normal 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,
|
||||
};
|
||||
};
|
||||
}
|
@ -12,8 +12,10 @@ export const createCookie = (token: any, ctx: any) => {
|
||||
if (!domain) {
|
||||
return;
|
||||
}
|
||||
if (ctx.res.cookie) {
|
||||
ctx.res.cookie('token', token.token, {
|
||||
const browser = ctx.req.headers['user-agent'];
|
||||
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天
|
||||
domain,
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
@ -87,7 +130,12 @@ app
|
||||
ctx.throw(500, 'Password error');
|
||||
}
|
||||
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);
|
||||
ctx.body = token;
|
||||
})
|
||||
|
@ -1,30 +1,72 @@
|
||||
import { app } from '@/app.ts';
|
||||
import { User } from '@/models/user.ts';
|
||||
import MD5 from 'crypto-js/md5.js';
|
||||
|
||||
import { authCan } from '@kevisual/code-center-module/models';
|
||||
import jsonwebtoken from 'jsonwebtoken';
|
||||
|
||||
// const tokenData: Record<string, string> = {};
|
||||
import { redis } from '@/app.ts';
|
||||
import { createCookie } from './me.ts';
|
||||
import { createCookie, clearCookie } from './me.ts';
|
||||
|
||||
app
|
||||
.route({
|
||||
path: 'user',
|
||||
key: 'webLogin',
|
||||
middleware: ['auth'],
|
||||
middleware: [authCan],
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const tokenUser = ctx.state.tokenUser;
|
||||
const token = ctx.query.token;
|
||||
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) {
|
||||
await setErrorLoginTokenRedis(loginToken);
|
||||
ctx.throw(400, 'loginToken is required');
|
||||
}
|
||||
if (!sign) {
|
||||
await setErrorLoginTokenRedis(loginToken);
|
||||
ctx.throw(400, 'sign is required');
|
||||
}
|
||||
|
||||
if (!randomId) {
|
||||
await setErrorLoginTokenRedis(loginToken);
|
||||
ctx.throw(400, 'randomId is required');
|
||||
}
|
||||
const tokenSecret = 'xiao' + randomId;
|
||||
@ -32,16 +74,19 @@ app
|
||||
try {
|
||||
payload = jsonwebtoken.verify(loginToken, tokenSecret);
|
||||
} catch (e) {
|
||||
await setErrorLoginTokenRedis(loginToken);
|
||||
ctx.throw(400, 'loginToken error');
|
||||
}
|
||||
const { timestamp } = payload;
|
||||
|
||||
const checkSign = MD5(`${tokenSecret}${timestamp}`).toString();
|
||||
if (sign !== checkSign) {
|
||||
await setErrorLoginTokenRedis(loginToken);
|
||||
ctx.throw(400, 'sign error');
|
||||
}
|
||||
const user = await User.findByPk(tokenUser.id);
|
||||
if (!user) {
|
||||
await setErrorLoginTokenRedis(loginToken);
|
||||
ctx.throw(400, 'user not found');
|
||||
}
|
||||
const data = await user.createToken(null, 'plugin', { loginWith: 'cli' });
|
||||
@ -63,11 +108,16 @@ app
|
||||
// const data = tokenData[loginToken];
|
||||
const data = await redis.get(loginToken);
|
||||
if (data) {
|
||||
ctx.body = JSON.parse(data);
|
||||
await redis.expire(loginToken, 3600);
|
||||
const token = JSON.parse(data);
|
||||
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 {
|
||||
ctx.throw(400, 'Checked Failed');
|
||||
}
|
||||
createCookie(data, ctx);
|
||||
})
|
||||
.addTo(app);
|
||||
|
46
src/scripts/sync-mark.ts
Normal file
46
src/scripts/sync-mark.ts
Normal 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
|
Loading…
x
Reference in New Issue
Block a user