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": "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
348
pnpm-lock.yaml
generated
@ -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
|
||||||
|
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 * 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);
|
||||||
|
59
src/route.ts
59
src/route.ts
@ -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: {
|
||||||
|
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 './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';
|
||||||
|
@ -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');
|
||||||
|
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 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
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) {
|
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;
|
||||||
})
|
})
|
||||||
|
@ -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
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