"feat: 新增 kevisual 同步配置,升级相关依赖"
This commit is contained in:
parent
1869164d3d
commit
552653c8f7
32
assistant/kevisual.json
Normal file
32
assistant/kevisual.json
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"$schema": "https://kevisual.xiongxiao.me/root/ai/kevisual/tools/kevisual-sync/schema.json?v=2",
|
||||
"metadata": {
|
||||
"share": "public"
|
||||
},
|
||||
"checkDir": {
|
||||
"src/query/query-login": {
|
||||
"url": "https://kevisual.xiongxiao.me/root/ai/code/registry/query/query-login",
|
||||
"enabled": true
|
||||
},
|
||||
"src/query/query-ai": {
|
||||
"url": "https://kevisual.xiongxiao.me/root/ai/code/registry/query/query-ai",
|
||||
"enabled": true
|
||||
}
|
||||
},
|
||||
"syncDirectory": [
|
||||
{
|
||||
"files": [
|
||||
"src/query/query-login/**/*",
|
||||
"src/query/query-ai/**/*"
|
||||
],
|
||||
"ignore": [],
|
||||
"registry": "https://kevisual.xiongxiao.me/root/ai/code/registry",
|
||||
"replace": {
|
||||
"src/": ""
|
||||
}
|
||||
}
|
||||
],
|
||||
"sync": {
|
||||
"src/query/index.ts": "https://kevisual.xiongxiao.me/root/ai/code/registry/query/index.ts"
|
||||
}
|
||||
}
|
@ -41,11 +41,11 @@
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@kevisual/ai-center": "^0.0.4",
|
||||
"@kevisual/ai": "^0.0.5",
|
||||
"@kevisual/load": "^0.0.6",
|
||||
"@kevisual/local-app-manager": "^0.1.20",
|
||||
"@kevisual/logger": "^0.0.4",
|
||||
"@kevisual/query": "0.0.18",
|
||||
"@kevisual/query": "0.0.20",
|
||||
"@kevisual/query-login": "0.0.6",
|
||||
"@kevisual/router": "^0.0.21",
|
||||
"@kevisual/task-command": "^0.0.7",
|
||||
@ -62,7 +62,7 @@
|
||||
"dayjs": "^1.11.13",
|
||||
"dotenv": "^16.5.0",
|
||||
"get-port": "^7.1.0",
|
||||
"inquirer": "^12.6.1",
|
||||
"inquirer": "^12.6.3",
|
||||
"lodash-es": "^4.17.21",
|
||||
"nanoid": "^5.1.5",
|
||||
"send": "^1.2.0",
|
||||
@ -77,6 +77,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"eventemitter3": "^5.0.1",
|
||||
"lowdb": "^7.0.1",
|
||||
"pm2": "^6.0.6"
|
||||
}
|
||||
}
|
225
assistant/pnpm-lock.yaml
generated
225
assistant/pnpm-lock.yaml
generated
@ -11,13 +11,16 @@ importers:
|
||||
eventemitter3:
|
||||
specifier: ^5.0.1
|
||||
version: 5.0.1
|
||||
lowdb:
|
||||
specifier: ^7.0.1
|
||||
version: 7.0.1
|
||||
pm2:
|
||||
specifier: ^6.0.6
|
||||
version: 6.0.6(supports-color@10.0.0)
|
||||
devDependencies:
|
||||
'@kevisual/ai-center':
|
||||
specifier: ^0.0.4
|
||||
version: 0.0.4
|
||||
'@kevisual/ai':
|
||||
specifier: ^0.0.5
|
||||
version: 0.0.5
|
||||
'@kevisual/load':
|
||||
specifier: ^0.0.6
|
||||
version: 0.0.6
|
||||
@ -28,11 +31,11 @@ importers:
|
||||
specifier: ^0.0.4
|
||||
version: 0.0.4
|
||||
'@kevisual/query':
|
||||
specifier: 0.0.18
|
||||
version: 0.0.18(@kevisual/ws@8.0.0)
|
||||
specifier: 0.0.20
|
||||
version: 0.0.20(@kevisual/ws@8.0.0)
|
||||
'@kevisual/query-login':
|
||||
specifier: 0.0.6
|
||||
version: 0.0.6(@kevisual/query@0.0.18(@kevisual/ws@8.0.0))(rollup@4.40.0)(tslib@2.8.1)(typescript@5.8.3)
|
||||
version: 0.0.6(@kevisual/query@0.0.20(@kevisual/ws@8.0.0))(rollup@4.40.0)(tslib@2.8.1)(typescript@5.8.3)
|
||||
'@kevisual/router':
|
||||
specifier: ^0.0.21
|
||||
version: 0.0.21
|
||||
@ -79,8 +82,8 @@ importers:
|
||||
specifier: ^7.1.0
|
||||
version: 7.1.0
|
||||
inquirer:
|
||||
specifier: ^12.6.1
|
||||
version: 12.6.1(@types/node@22.15.21)
|
||||
specifier: ^12.6.3
|
||||
version: 12.6.3(@types/node@22.15.21)
|
||||
lodash-es:
|
||||
specifier: ^4.17.21
|
||||
version: 4.17.21
|
||||
@ -126,7 +129,7 @@ importers:
|
||||
version: 16.5.0
|
||||
inquirer:
|
||||
specifier: ^12.6.0
|
||||
version: 12.6.1(@types/node@22.15.21)
|
||||
version: 12.6.3(@types/node@22.15.21)
|
||||
lodash-es:
|
||||
specifier: ^4.17.21
|
||||
version: 4.17.21
|
||||
@ -144,8 +147,8 @@ packages:
|
||||
resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@inquirer/checkbox@4.1.6':
|
||||
resolution: {integrity: sha512-62u896rWCtKKE43soodq5e/QcRsA22I+7/4Ov7LESWnKRO6BVo2A1DFLDmXL9e28TB0CfHc3YtkbPm7iwajqkg==}
|
||||
'@inquirer/checkbox@4.1.8':
|
||||
resolution: {integrity: sha512-d/QAsnwuHX2OPolxvYcgSj7A9DO9H6gVOy2DvBTx+P2LH2iRTo/RSGV3iwCzW024nP9hw98KIuDmdyhZQj1UQg==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
@ -153,8 +156,8 @@ packages:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@inquirer/confirm@5.1.10':
|
||||
resolution: {integrity: sha512-FxbQ9giWxUWKUk2O5XZ6PduVnH2CZ/fmMKMBkH71MHJvWr7WL5AHKevhzF1L5uYWB2P548o1RzVxrNd3dpmk6g==}
|
||||
'@inquirer/confirm@5.1.12':
|
||||
resolution: {integrity: sha512-dpq+ielV9/bqgXRUbNH//KsY6WEw9DrGPmipkpmgC1Y46cwuBTNx7PXFWTjc3MQ+urcc0QxoVHcMI0FW4Ok0hg==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
@ -162,8 +165,8 @@ packages:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@inquirer/core@10.1.11':
|
||||
resolution: {integrity: sha512-BXwI/MCqdtAhzNQlBEFE7CEflhPkl/BqvAuV/aK6lW3DClIfYVDWPP/kXuXHtBWC7/EEbNqd/1BGq2BGBBnuxw==}
|
||||
'@inquirer/core@10.1.13':
|
||||
resolution: {integrity: sha512-1viSxebkYN2nJULlzCxES6G9/stgHSepZ9LqqfdIGPHj5OHhiBUXVS0a6R0bEC2A+VL4D9w6QB66ebCr6HGllA==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
@ -171,8 +174,8 @@ packages:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@inquirer/editor@4.2.11':
|
||||
resolution: {integrity: sha512-YoZr0lBnnLFPpfPSNsQ8IZyKxU47zPyVi9NLjCWtna52//M/xuL0PGPAxHxxYhdOhnvY2oBafoM+BI5w/JK7jw==}
|
||||
'@inquirer/editor@4.2.13':
|
||||
resolution: {integrity: sha512-WbicD9SUQt/K8O5Vyk9iC2ojq5RHoCLK6itpp2fHsWe44VxxcA9z3GTWlvjSTGmMQpZr+lbVmrxdHcumJoLbMA==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
@ -180,8 +183,8 @@ packages:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@inquirer/expand@4.0.13':
|
||||
resolution: {integrity: sha512-HgYNWuZLHX6q5y4hqKhwyytqAghmx35xikOGY3TcgNiElqXGPas24+UzNPOwGUZa5Dn32y25xJqVeUcGlTv+QQ==}
|
||||
'@inquirer/expand@4.0.15':
|
||||
resolution: {integrity: sha512-4Y+pbr/U9Qcvf+N/goHzPEXiHH8680lM3Dr3Y9h9FFw4gHS+zVpbj8LfbKWIb/jayIB4aSO4pWiBTrBYWkvi5A==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
@ -189,12 +192,12 @@ packages:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@inquirer/figures@1.0.11':
|
||||
resolution: {integrity: sha512-eOg92lvrn/aRUqbxRyvpEWnrvRuTYRifixHkYVpJiygTgVSBIHDqLh0SrMQXkafvULg3ck11V7xvR+zcgvpHFw==}
|
||||
'@inquirer/figures@1.0.12':
|
||||
resolution: {integrity: sha512-MJttijd8rMFcKJC8NYmprWr6hD3r9Gd9qUC0XwPNwoEPWSMVJwA2MlXxF+nhZZNMY+HXsWa+o7KY2emWYIn0jQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
'@inquirer/input@4.1.10':
|
||||
resolution: {integrity: sha512-kV3BVne3wJ+j6reYQUZi/UN9NZGZLxgc/tfyjeK3mrx1QI7RXPxGp21IUTv+iVHcbP4ytZALF8vCHoxyNSC6qg==}
|
||||
'@inquirer/input@4.1.12':
|
||||
resolution: {integrity: sha512-xJ6PFZpDjC+tC1P8ImGprgcsrzQRsUh9aH3IZixm1lAZFK49UGHxM3ltFfuInN2kPYNfyoPRh+tU4ftsjPLKqQ==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
@ -202,8 +205,8 @@ packages:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@inquirer/number@3.0.13':
|
||||
resolution: {integrity: sha512-IrLezcg/GWKS8zpKDvnJ/YTflNJdG0qSFlUM/zNFsdi4UKW/CO+gaJpbMgQ20Q58vNKDJbEzC6IebdkprwL6ew==}
|
||||
'@inquirer/number@3.0.15':
|
||||
resolution: {integrity: sha512-xWg+iYfqdhRiM55MvqiTCleHzszpoigUpN5+t1OMcRkJrUrw7va3AzXaxvS+Ak7Gny0j2mFSTv2JJj8sMtbV2g==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
@ -211,8 +214,8 @@ packages:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@inquirer/password@4.0.13':
|
||||
resolution: {integrity: sha512-NN0S/SmdhakqOTJhDwOpeBEEr8VdcYsjmZHDb0rblSh2FcbXQOr+2IApP7JG4WE3sxIdKytDn4ed3XYwtHxmJQ==}
|
||||
'@inquirer/password@4.0.15':
|
||||
resolution: {integrity: sha512-75CT2p43DGEnfGTaqFpbDC2p2EEMrq0S+IRrf9iJvYreMy5mAWj087+mdKyLHapUEPLjN10mNvABpGbk8Wdraw==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
@ -220,8 +223,8 @@ packages:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@inquirer/prompts@7.5.1':
|
||||
resolution: {integrity: sha512-5AOrZPf2/GxZ+SDRZ5WFplCA2TAQgK3OYrXCYmJL5NaTu4ECcoWFlfUZuw7Es++6Njv7iu/8vpYJhuzxUH76Vg==}
|
||||
'@inquirer/prompts@7.5.3':
|
||||
resolution: {integrity: sha512-8YL0WiV7J86hVAxrh3fE5mDCzcTDe1670unmJRz6ArDgN+DBK1a0+rbnNWp4DUB5rPMwqD5ZP6YHl9KK1mbZRg==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
@ -229,8 +232,8 @@ packages:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@inquirer/rawlist@4.1.1':
|
||||
resolution: {integrity: sha512-VBUC0jPN2oaOq8+krwpo/mf3n/UryDUkKog3zi+oIi8/e5hykvdntgHUB9nhDM78RubiyR1ldIOfm5ue+2DeaQ==}
|
||||
'@inquirer/rawlist@4.1.3':
|
||||
resolution: {integrity: sha512-7XrV//6kwYumNDSsvJIPeAqa8+p7GJh7H5kRuxirct2cgOcSWwwNGoXDRgpNFbY/MG2vQ4ccIWCi8+IXXyFMZA==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
@ -238,8 +241,8 @@ packages:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@inquirer/search@3.0.13':
|
||||
resolution: {integrity: sha512-9g89d2c5Izok/Gw/U7KPC3f9kfe5rA1AJ24xxNZG0st+vWekSk7tB9oE+dJv5JXd0ZSijomvW0KPMoBd8qbN4g==}
|
||||
'@inquirer/search@3.0.15':
|
||||
resolution: {integrity: sha512-YBMwPxYBrADqyvP4nNItpwkBnGGglAvCLVW8u4pRmmvOsHUtCAUIMbUrLX5B3tFL1/WsLGdQ2HNzkqswMs5Uaw==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
@ -247,8 +250,8 @@ packages:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@inquirer/select@4.2.1':
|
||||
resolution: {integrity: sha512-gt1Kd5XZm+/ddemcT3m23IP8aD8rC9drRckWoP/1f7OL46Yy2FGi8DSmNjEjQKtPl6SV96Kmjbl6p713KXJ/Jg==}
|
||||
'@inquirer/select@4.2.3':
|
||||
resolution: {integrity: sha512-OAGhXU0Cvh0PhLz9xTF/kx6g6x+sP+PcyTiLvCrewI99P3BBeexD+VbuwkNDvqGkk3y2h5ZiWLeRP7BFlhkUDg==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
@ -256,8 +259,8 @@ packages:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@inquirer/type@3.0.6':
|
||||
resolution: {integrity: sha512-/mKVCtVpyBu3IDarv0G+59KC4stsD5mDsGpYh+GKs1NZT88Jh52+cuoA1AtLk2Q0r/quNl+1cSUyLRHBFeD0XA==}
|
||||
'@inquirer/type@3.0.7':
|
||||
resolution: {integrity: sha512-PfunHQcjwnju84L+ycmcMKB/pTPIngjUJvfnRhKY6FKPuYXlM4aQCb/nIdTFR6BEhMjFvngzvng/vBAJMZpLSA==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
@ -268,8 +271,8 @@ packages:
|
||||
'@jridgewell/sourcemap-codec@1.5.0':
|
||||
resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
|
||||
|
||||
'@kevisual/ai-center@0.0.4':
|
||||
resolution: {integrity: sha512-pZw8sH7G0JjknHBHERV71INvmgyDS2JWyI2kb1a9FFHfRKg4TCv+RJIE505vKoAPjUd6+h6FHVHXvPXBdeTdxg==}
|
||||
'@kevisual/ai@0.0.5':
|
||||
resolution: {integrity: sha512-yEwwFIWJz2Enj1Uo3wDH9Aa0lf42ACAZdMFCXkqI44NyA/5C59tm1Ro4Bm46Z2bgT5IMtkIDfTxJHMvVtosJ7w==}
|
||||
|
||||
'@kevisual/cache@0.0.2':
|
||||
resolution: {integrity: sha512-2Cl5KF2Gi27uLfhO6CdTMFnRzx9vYnqevAo7d9ab3rOaqTgF8tLeAXglXyRbaWW3WUbHU2XaOb4r98uUsqIQQw==}
|
||||
@ -280,9 +283,6 @@ packages:
|
||||
'@kevisual/local-app-manager@0.1.20':
|
||||
resolution: {integrity: sha512-PQf1wL9vQazTYvxhTqnhno3ZYccFGmgjkPEqKUY9IUpc2vP7srX73TGn/3e7S5dysO1urzgif4dciGe0yPbxAg==}
|
||||
|
||||
'@kevisual/logger@0.0.3':
|
||||
resolution: {integrity: sha512-8emqxg+ab62WAK6VY4FQqetXPSSVKFAjGctD1NDbdnxt7YWuI/PyuDltCpsVz+uvWpV1dO5OKZOoHU7ow59Omw==}
|
||||
|
||||
'@kevisual/logger@0.0.4':
|
||||
resolution: {integrity: sha512-+fpr92eokSxoGOW1SIRl/27lPuO+zyY+feR5o2Q4YCNlAdt2x64NwC/w8r/3NEC5QenLgd4K0azyKTI2mHbARw==}
|
||||
|
||||
@ -291,8 +291,8 @@ packages:
|
||||
peerDependencies:
|
||||
'@kevisual/query': ^0.0.17
|
||||
|
||||
'@kevisual/query@0.0.18':
|
||||
resolution: {integrity: sha512-I2vHTu0I6AyD9PJyr+vxyp9jIJ6rd2EZqLVHTv/+zrVKVc2SS76Tg7aGNkmAFqqLSCB8kLLsmMGtSJU1Qb8VVg==}
|
||||
'@kevisual/query@0.0.20':
|
||||
resolution: {integrity: sha512-IpkQd1Pz50namxDNytLJpyR2CRLJWKTkmk68Fiw6CEkTXQDkdyBh2kIOIf7+Geu8quai/J9io3KjWzt17AeYdg==}
|
||||
|
||||
'@kevisual/router@0.0.21':
|
||||
resolution: {integrity: sha512-XKTxbNO924cT18UOAGplWErZ+hMze8Y53F2jYCk18v4jsdsvjRho5uXXjJb6HSVsuITMtQR4R3rG0IcM3jkDKQ==}
|
||||
@ -930,8 +930,8 @@ packages:
|
||||
ini@1.3.8:
|
||||
resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
|
||||
|
||||
inquirer@12.6.1:
|
||||
resolution: {integrity: sha512-MGFnzHVS3l3oM3cy+LWkyR7UUtVEn3D5U41CZbEY34szToWoJAvaVtCTz1mxsEzZFk/HXWyCArn0HDgloTXMDw==}
|
||||
inquirer@12.6.3:
|
||||
resolution: {integrity: sha512-eX9beYAjr1MqYsIjx1vAheXsRk1jbZRvHLcBu5nA9wX0rXR1IfCZLnVLp4Ym4mrhqmh7AuANwcdtgQ291fZDfQ==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
@ -998,6 +998,10 @@ packages:
|
||||
lodash@4.17.21:
|
||||
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
|
||||
|
||||
lowdb@7.0.1:
|
||||
resolution: {integrity: sha512-neJAj8GwF0e8EpycYIDFqEPcx9Qz4GUho20jWFR7YiFeXzF1YMLdxB36PypcTSPMA+4+LvgyMacYhlr18Zlymw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
lru-cache@6.0.0:
|
||||
resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
|
||||
engines: {node: '>=10'}
|
||||
@ -1064,6 +1068,7 @@ packages:
|
||||
node-domexception@1.0.0:
|
||||
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
|
||||
engines: {node: '>=10.5.0'}
|
||||
deprecated: Use your platform's native DOMException instead
|
||||
|
||||
node-fetch@2.7.0:
|
||||
resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
|
||||
@ -1296,6 +1301,10 @@ packages:
|
||||
resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
steno@4.0.2:
|
||||
resolution: {integrity: sha512-yhPIQXjrlt1xv7dyPQg2P17URmXbuM5pdGkpiMB3RenprfiBlvK415Lctfe0eshk90oA7/tNq7WEiMK8RSP39A==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
string-width@4.2.3:
|
||||
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
|
||||
engines: {node: '>=8'}
|
||||
@ -1419,27 +1428,27 @@ snapshots:
|
||||
'@babel/helper-validator-identifier@7.25.9':
|
||||
optional: true
|
||||
|
||||
'@inquirer/checkbox@4.1.6(@types/node@22.15.21)':
|
||||
'@inquirer/checkbox@4.1.8(@types/node@22.15.21)':
|
||||
dependencies:
|
||||
'@inquirer/core': 10.1.11(@types/node@22.15.21)
|
||||
'@inquirer/figures': 1.0.11
|
||||
'@inquirer/type': 3.0.6(@types/node@22.15.21)
|
||||
'@inquirer/core': 10.1.13(@types/node@22.15.21)
|
||||
'@inquirer/figures': 1.0.12
|
||||
'@inquirer/type': 3.0.7(@types/node@22.15.21)
|
||||
ansi-escapes: 4.3.2
|
||||
yoctocolors-cjs: 2.1.2
|
||||
optionalDependencies:
|
||||
'@types/node': 22.15.21
|
||||
|
||||
'@inquirer/confirm@5.1.10(@types/node@22.15.21)':
|
||||
'@inquirer/confirm@5.1.12(@types/node@22.15.21)':
|
||||
dependencies:
|
||||
'@inquirer/core': 10.1.11(@types/node@22.15.21)
|
||||
'@inquirer/type': 3.0.6(@types/node@22.15.21)
|
||||
'@inquirer/core': 10.1.13(@types/node@22.15.21)
|
||||
'@inquirer/type': 3.0.7(@types/node@22.15.21)
|
||||
optionalDependencies:
|
||||
'@types/node': 22.15.21
|
||||
|
||||
'@inquirer/core@10.1.11(@types/node@22.15.21)':
|
||||
'@inquirer/core@10.1.13(@types/node@22.15.21)':
|
||||
dependencies:
|
||||
'@inquirer/figures': 1.0.11
|
||||
'@inquirer/type': 3.0.6(@types/node@22.15.21)
|
||||
'@inquirer/figures': 1.0.12
|
||||
'@inquirer/type': 3.0.7(@types/node@22.15.21)
|
||||
ansi-escapes: 4.3.2
|
||||
cli-width: 4.1.0
|
||||
mute-stream: 2.0.0
|
||||
@ -1449,97 +1458,97 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@types/node': 22.15.21
|
||||
|
||||
'@inquirer/editor@4.2.11(@types/node@22.15.21)':
|
||||
'@inquirer/editor@4.2.13(@types/node@22.15.21)':
|
||||
dependencies:
|
||||
'@inquirer/core': 10.1.11(@types/node@22.15.21)
|
||||
'@inquirer/type': 3.0.6(@types/node@22.15.21)
|
||||
'@inquirer/core': 10.1.13(@types/node@22.15.21)
|
||||
'@inquirer/type': 3.0.7(@types/node@22.15.21)
|
||||
external-editor: 3.1.0
|
||||
optionalDependencies:
|
||||
'@types/node': 22.15.21
|
||||
|
||||
'@inquirer/expand@4.0.13(@types/node@22.15.21)':
|
||||
'@inquirer/expand@4.0.15(@types/node@22.15.21)':
|
||||
dependencies:
|
||||
'@inquirer/core': 10.1.11(@types/node@22.15.21)
|
||||
'@inquirer/type': 3.0.6(@types/node@22.15.21)
|
||||
'@inquirer/core': 10.1.13(@types/node@22.15.21)
|
||||
'@inquirer/type': 3.0.7(@types/node@22.15.21)
|
||||
yoctocolors-cjs: 2.1.2
|
||||
optionalDependencies:
|
||||
'@types/node': 22.15.21
|
||||
|
||||
'@inquirer/figures@1.0.11': {}
|
||||
'@inquirer/figures@1.0.12': {}
|
||||
|
||||
'@inquirer/input@4.1.10(@types/node@22.15.21)':
|
||||
'@inquirer/input@4.1.12(@types/node@22.15.21)':
|
||||
dependencies:
|
||||
'@inquirer/core': 10.1.11(@types/node@22.15.21)
|
||||
'@inquirer/type': 3.0.6(@types/node@22.15.21)
|
||||
'@inquirer/core': 10.1.13(@types/node@22.15.21)
|
||||
'@inquirer/type': 3.0.7(@types/node@22.15.21)
|
||||
optionalDependencies:
|
||||
'@types/node': 22.15.21
|
||||
|
||||
'@inquirer/number@3.0.13(@types/node@22.15.21)':
|
||||
'@inquirer/number@3.0.15(@types/node@22.15.21)':
|
||||
dependencies:
|
||||
'@inquirer/core': 10.1.11(@types/node@22.15.21)
|
||||
'@inquirer/type': 3.0.6(@types/node@22.15.21)
|
||||
'@inquirer/core': 10.1.13(@types/node@22.15.21)
|
||||
'@inquirer/type': 3.0.7(@types/node@22.15.21)
|
||||
optionalDependencies:
|
||||
'@types/node': 22.15.21
|
||||
|
||||
'@inquirer/password@4.0.13(@types/node@22.15.21)':
|
||||
'@inquirer/password@4.0.15(@types/node@22.15.21)':
|
||||
dependencies:
|
||||
'@inquirer/core': 10.1.11(@types/node@22.15.21)
|
||||
'@inquirer/type': 3.0.6(@types/node@22.15.21)
|
||||
'@inquirer/core': 10.1.13(@types/node@22.15.21)
|
||||
'@inquirer/type': 3.0.7(@types/node@22.15.21)
|
||||
ansi-escapes: 4.3.2
|
||||
optionalDependencies:
|
||||
'@types/node': 22.15.21
|
||||
|
||||
'@inquirer/prompts@7.5.1(@types/node@22.15.21)':
|
||||
'@inquirer/prompts@7.5.3(@types/node@22.15.21)':
|
||||
dependencies:
|
||||
'@inquirer/checkbox': 4.1.6(@types/node@22.15.21)
|
||||
'@inquirer/confirm': 5.1.10(@types/node@22.15.21)
|
||||
'@inquirer/editor': 4.2.11(@types/node@22.15.21)
|
||||
'@inquirer/expand': 4.0.13(@types/node@22.15.21)
|
||||
'@inquirer/input': 4.1.10(@types/node@22.15.21)
|
||||
'@inquirer/number': 3.0.13(@types/node@22.15.21)
|
||||
'@inquirer/password': 4.0.13(@types/node@22.15.21)
|
||||
'@inquirer/rawlist': 4.1.1(@types/node@22.15.21)
|
||||
'@inquirer/search': 3.0.13(@types/node@22.15.21)
|
||||
'@inquirer/select': 4.2.1(@types/node@22.15.21)
|
||||
'@inquirer/checkbox': 4.1.8(@types/node@22.15.21)
|
||||
'@inquirer/confirm': 5.1.12(@types/node@22.15.21)
|
||||
'@inquirer/editor': 4.2.13(@types/node@22.15.21)
|
||||
'@inquirer/expand': 4.0.15(@types/node@22.15.21)
|
||||
'@inquirer/input': 4.1.12(@types/node@22.15.21)
|
||||
'@inquirer/number': 3.0.15(@types/node@22.15.21)
|
||||
'@inquirer/password': 4.0.15(@types/node@22.15.21)
|
||||
'@inquirer/rawlist': 4.1.3(@types/node@22.15.21)
|
||||
'@inquirer/search': 3.0.15(@types/node@22.15.21)
|
||||
'@inquirer/select': 4.2.3(@types/node@22.15.21)
|
||||
optionalDependencies:
|
||||
'@types/node': 22.15.21
|
||||
|
||||
'@inquirer/rawlist@4.1.1(@types/node@22.15.21)':
|
||||
'@inquirer/rawlist@4.1.3(@types/node@22.15.21)':
|
||||
dependencies:
|
||||
'@inquirer/core': 10.1.11(@types/node@22.15.21)
|
||||
'@inquirer/type': 3.0.6(@types/node@22.15.21)
|
||||
'@inquirer/core': 10.1.13(@types/node@22.15.21)
|
||||
'@inquirer/type': 3.0.7(@types/node@22.15.21)
|
||||
yoctocolors-cjs: 2.1.2
|
||||
optionalDependencies:
|
||||
'@types/node': 22.15.21
|
||||
|
||||
'@inquirer/search@3.0.13(@types/node@22.15.21)':
|
||||
'@inquirer/search@3.0.15(@types/node@22.15.21)':
|
||||
dependencies:
|
||||
'@inquirer/core': 10.1.11(@types/node@22.15.21)
|
||||
'@inquirer/figures': 1.0.11
|
||||
'@inquirer/type': 3.0.6(@types/node@22.15.21)
|
||||
'@inquirer/core': 10.1.13(@types/node@22.15.21)
|
||||
'@inquirer/figures': 1.0.12
|
||||
'@inquirer/type': 3.0.7(@types/node@22.15.21)
|
||||
yoctocolors-cjs: 2.1.2
|
||||
optionalDependencies:
|
||||
'@types/node': 22.15.21
|
||||
|
||||
'@inquirer/select@4.2.1(@types/node@22.15.21)':
|
||||
'@inquirer/select@4.2.3(@types/node@22.15.21)':
|
||||
dependencies:
|
||||
'@inquirer/core': 10.1.11(@types/node@22.15.21)
|
||||
'@inquirer/figures': 1.0.11
|
||||
'@inquirer/type': 3.0.6(@types/node@22.15.21)
|
||||
'@inquirer/core': 10.1.13(@types/node@22.15.21)
|
||||
'@inquirer/figures': 1.0.12
|
||||
'@inquirer/type': 3.0.7(@types/node@22.15.21)
|
||||
ansi-escapes: 4.3.2
|
||||
yoctocolors-cjs: 2.1.2
|
||||
optionalDependencies:
|
||||
'@types/node': 22.15.21
|
||||
|
||||
'@inquirer/type@3.0.6(@types/node@22.15.21)':
|
||||
'@inquirer/type@3.0.7(@types/node@22.15.21)':
|
||||
optionalDependencies:
|
||||
'@types/node': 22.15.21
|
||||
|
||||
'@jridgewell/sourcemap-codec@1.5.0': {}
|
||||
|
||||
'@kevisual/ai-center@0.0.4':
|
||||
'@kevisual/ai@0.0.5':
|
||||
dependencies:
|
||||
'@kevisual/logger': 0.0.3
|
||||
'@kevisual/logger': 0.0.4
|
||||
|
||||
'@kevisual/cache@0.0.2(rollup@4.40.0)(tslib@2.8.1)(typescript@5.8.3)':
|
||||
dependencies:
|
||||
@ -1565,21 +1574,19 @@ snapshots:
|
||||
- supports-color
|
||||
- utf-8-validate
|
||||
|
||||
'@kevisual/logger@0.0.3': {}
|
||||
|
||||
'@kevisual/logger@0.0.4': {}
|
||||
|
||||
'@kevisual/query-login@0.0.6(@kevisual/query@0.0.18(@kevisual/ws@8.0.0))(rollup@4.40.0)(tslib@2.8.1)(typescript@5.8.3)':
|
||||
'@kevisual/query-login@0.0.6(@kevisual/query@0.0.20(@kevisual/ws@8.0.0))(rollup@4.40.0)(tslib@2.8.1)(typescript@5.8.3)':
|
||||
dependencies:
|
||||
'@kevisual/cache': 0.0.2(rollup@4.40.0)(tslib@2.8.1)(typescript@5.8.3)
|
||||
'@kevisual/query': 0.0.18(@kevisual/ws@8.0.0)
|
||||
'@kevisual/query': 0.0.20(@kevisual/ws@8.0.0)
|
||||
dotenv: 16.5.0
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
- tslib
|
||||
- typescript
|
||||
|
||||
'@kevisual/query@0.0.18(@kevisual/ws@8.0.0)':
|
||||
'@kevisual/query@0.0.20(@kevisual/ws@8.0.0)':
|
||||
dependencies:
|
||||
openai: 4.99.0(@kevisual/ws@8.0.0)
|
||||
transitivePeerDependencies:
|
||||
@ -2165,11 +2172,11 @@ snapshots:
|
||||
|
||||
ini@1.3.8: {}
|
||||
|
||||
inquirer@12.6.1(@types/node@22.15.21):
|
||||
inquirer@12.6.3(@types/node@22.15.21):
|
||||
dependencies:
|
||||
'@inquirer/core': 10.1.11(@types/node@22.15.21)
|
||||
'@inquirer/prompts': 7.5.1(@types/node@22.15.21)
|
||||
'@inquirer/type': 3.0.6(@types/node@22.15.21)
|
||||
'@inquirer/core': 10.1.13(@types/node@22.15.21)
|
||||
'@inquirer/prompts': 7.5.3(@types/node@22.15.21)
|
||||
'@inquirer/type': 3.0.7(@types/node@22.15.21)
|
||||
ansi-escapes: 4.3.2
|
||||
mute-stream: 2.0.0
|
||||
run-async: 3.0.0
|
||||
@ -2231,6 +2238,10 @@ snapshots:
|
||||
|
||||
lodash@4.17.21: {}
|
||||
|
||||
lowdb@7.0.1:
|
||||
dependencies:
|
||||
steno: 4.0.2
|
||||
|
||||
lru-cache@6.0.0:
|
||||
dependencies:
|
||||
yallist: 4.0.0
|
||||
@ -2582,6 +2593,8 @@ snapshots:
|
||||
|
||||
statuses@2.0.1: {}
|
||||
|
||||
steno@4.0.2: {}
|
||||
|
||||
string-width@4.2.3:
|
||||
dependencies:
|
||||
emoji-regex: 8.0.0
|
||||
|
@ -3,6 +3,7 @@ import { HttpsPem } from '@/module/assistant/https/sign.ts';
|
||||
// import { AssistantConfig } from '@/module/assistant/index.ts';
|
||||
import { AssistantInit, parseHomeArg } from '@/services/init/index.ts';
|
||||
import { configDir as HomeConfigDir } from '@/module/assistant/config/index.ts';
|
||||
import { useContextKey } from '@kevisual/use-config/context';
|
||||
|
||||
const manualParse = parseHomeArg(HomeConfigDir);
|
||||
const _configDir = manualParse.configDir;
|
||||
@ -13,7 +14,8 @@ export const assistantConfig = new AssistantInit({
|
||||
});
|
||||
|
||||
const httpsPem = new HttpsPem(assistantConfig);
|
||||
export const app = new App({
|
||||
export const app = useContextKey('app', () => {
|
||||
return new App({
|
||||
serverOptions: {
|
||||
path: '/client/router',
|
||||
httpType: 'https',
|
||||
@ -21,4 +23,4 @@ export const app = new App({
|
||||
httpsKey: httpsPem.key,
|
||||
},
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -1,4 +1,5 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
/**
|
||||
* 检查文件是否存在
|
||||
@ -18,6 +19,22 @@ export const checkFileExists = (filePath: string, checkIsFile = false) => {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 检查文件目录是否存在,如果不存在则创建
|
||||
* @param filePath 文件路径
|
||||
* @param create
|
||||
* @returns
|
||||
*/
|
||||
export const checkFileDir = (filePath: string, create = true) => {
|
||||
const dirPath = path.dirname(filePath);
|
||||
|
||||
const exist = checkFileExists(dirPath);
|
||||
if (create && !exist) {
|
||||
fs.mkdirSync(dirPath, { recursive: true });
|
||||
}
|
||||
return exist;
|
||||
};
|
||||
|
||||
export const createDir = (dirPath: string) => {
|
||||
if (!checkFileExists(dirPath)) {
|
||||
fs.mkdirSync(dirPath, { recursive: true });
|
||||
|
47
assistant/src/module/assistant/storage/index.ts
Normal file
47
assistant/src/module/assistant/storage/index.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import path from 'node:path';
|
||||
import fs from 'node:fs';
|
||||
import { checkFileDir, checkFileExists } from '../file/index.js';
|
||||
type SimpleObject = {
|
||||
[key: string]: any;
|
||||
};
|
||||
type StorageOptions = {
|
||||
dir?: string;
|
||||
};
|
||||
export class StorageCore {
|
||||
storagePath: string;
|
||||
constructor(opts?: StorageOptions) {
|
||||
this.storagePath = opts?.dir || process.cwd();
|
||||
}
|
||||
async getData<T = SimpleObject>(key: string = 'data.json', fileDirectory?: string): Promise<T | null> {
|
||||
const storageFilePath = path.join(this.storagePath, fileDirectory, key);
|
||||
if (!checkFileExists(storageFilePath)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
const data = fs.readFileSync(storageFilePath, 'utf-8');
|
||||
return JSON.parse(data) as T;
|
||||
} catch (error) {
|
||||
console.error(`Error reading data from ${storageFilePath}:`, error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
async setData<T = SimpleObject>(data: T, key: string = 'data.json', fileDirectory?: string): Promise<void> {
|
||||
const storageFilePath = path.join(this.storagePath, fileDirectory, key);
|
||||
try {
|
||||
checkFileDir(storageFilePath, true);
|
||||
fs.writeFileSync(storageFilePath, JSON.stringify(data, null, 2), 'utf-8');
|
||||
} catch (error) {
|
||||
console.error(`Error writing data to ${storageFilePath}:`, error);
|
||||
}
|
||||
}
|
||||
async deleteData(key: string = 'data.json', fileDirectory?: string): Promise<void> {
|
||||
const storageFilePath = path.join(this.storagePath, fileDirectory, key);
|
||||
try {
|
||||
if (checkFileExists(storageFilePath)) {
|
||||
fs.unlinkSync(storageFilePath);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error deleting data from ${storageFilePath}:`, error);
|
||||
}
|
||||
}
|
||||
}
|
7
assistant/src/query/index.ts
Normal file
7
assistant/src/query/index.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { Query } from '@kevisual/query';
|
||||
|
||||
export const query = new Query();
|
||||
|
||||
export const clientQuery = new Query({ url: '/client/router' });
|
||||
|
||||
export { QueryUtil } from '@kevisual/router/define';
|
42
assistant/src/query/query-ai/defines/ai.ts
Normal file
42
assistant/src/query/query-ai/defines/ai.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import { QueryUtil } from '@/query/index.ts';
|
||||
|
||||
type Message = {
|
||||
role?: 'user' | 'assistant' | 'system' | 'tool';
|
||||
content?: string;
|
||||
name?: string;
|
||||
};
|
||||
export type PostChat = {
|
||||
messages?: Message[];
|
||||
model?: string;
|
||||
group?: string;
|
||||
user?: string;
|
||||
};
|
||||
|
||||
export type ChatDataOpts = {
|
||||
id?: string;
|
||||
title?: string;
|
||||
messages?: any[];
|
||||
data?: any;
|
||||
type?: 'temp' | 'keep' | string;
|
||||
};
|
||||
export type ChatOpts = {
|
||||
username: string;
|
||||
model: string;
|
||||
/**
|
||||
* 获取完整消息回复
|
||||
*/
|
||||
getFull?: boolean;
|
||||
group: string;
|
||||
/**
|
||||
* openai的参数
|
||||
*/
|
||||
options?: any;
|
||||
};
|
||||
|
||||
export const appDefine = QueryUtil.create({
|
||||
chat: {
|
||||
path: 'ai',
|
||||
key: 'chat',
|
||||
description: '与 AI 进行对话, 调用 GPT 的AI 服务,生成结果,并返回。',
|
||||
},
|
||||
});
|
101
assistant/src/query/query-ai/query-ai.ts
Normal file
101
assistant/src/query/query-ai/query-ai.ts
Normal file
@ -0,0 +1,101 @@
|
||||
import { appDefine } from './defines/ai.ts';
|
||||
import { PostChat, ChatOpts, ChatDataOpts } from './defines/ai.ts';
|
||||
|
||||
import { BaseQuery, DataOpts, Query } from '@kevisual/query/query';
|
||||
|
||||
export { appDefine };
|
||||
|
||||
export class QueryApp<T extends Query = Query> extends BaseQuery<T, typeof appDefine> {
|
||||
constructor(opts?: { query: T }) {
|
||||
super({
|
||||
...opts,
|
||||
query: opts?.query!,
|
||||
queryDefine: appDefine,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 与 AI 进行对话, 调用 GPT 的AI 服务,生成结果,并返回。
|
||||
* @param data
|
||||
* @param opts
|
||||
* @returns
|
||||
*/
|
||||
postChat(data: PostChat, opts?: DataOpts) {
|
||||
return this.chain('chat').post(data, opts);
|
||||
}
|
||||
/**
|
||||
* 获取模型列表
|
||||
* @param opts
|
||||
* @returns
|
||||
*/
|
||||
getModelList(data?: { usernames?: string[] }, opts?: DataOpts) {
|
||||
return this.query.post(
|
||||
{
|
||||
path: 'ai',
|
||||
key: 'get-model-list',
|
||||
data,
|
||||
},
|
||||
opts,
|
||||
);
|
||||
}
|
||||
/**
|
||||
* 聊天对话模型
|
||||
* @param data
|
||||
* @param chatOpts
|
||||
* @param opts
|
||||
* @returns
|
||||
*/
|
||||
chat(data: ChatDataOpts, chatOpts: ChatOpts, opts?: DataOpts) {
|
||||
const { username, model, group, getFull = true } = chatOpts;
|
||||
if (!username || !model || !group) {
|
||||
throw new Error('username, model, group is required');
|
||||
}
|
||||
return this.query.post(
|
||||
{
|
||||
path: 'ai',
|
||||
key: 'chat',
|
||||
...chatOpts,
|
||||
getFull,
|
||||
data,
|
||||
},
|
||||
opts,
|
||||
);
|
||||
}
|
||||
clearConfigCache(opts?: DataOpts) {
|
||||
return this.query.post(
|
||||
{
|
||||
path: 'ai',
|
||||
key: 'clear-cache',
|
||||
},
|
||||
opts,
|
||||
);
|
||||
}
|
||||
/**
|
||||
* 获取聊天使用情况
|
||||
* @param opts
|
||||
* @returns
|
||||
*/
|
||||
getChatUsage(opts?: DataOpts) {
|
||||
return this.query.post(
|
||||
{
|
||||
path: 'ai',
|
||||
key: 'get-chat-usage',
|
||||
},
|
||||
opts,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除当前用户模型自己的统计
|
||||
* @param opts
|
||||
* @returns
|
||||
*/
|
||||
clearSelfUsage(opts?: DataOpts) {
|
||||
return this.query.post(
|
||||
{
|
||||
path: 'ai',
|
||||
key: 'clear-chat-limit',
|
||||
},
|
||||
opts,
|
||||
);
|
||||
}
|
||||
}
|
204
assistant/src/query/query-login/login-cache.ts
Normal file
204
assistant/src/query/query-login/login-cache.ts
Normal file
@ -0,0 +1,204 @@
|
||||
export interface Cache {
|
||||
/**
|
||||
* @update 获取缓存
|
||||
*/
|
||||
get(key: string): Promise<any>;
|
||||
/**
|
||||
* @update 设置缓存
|
||||
*/
|
||||
set(key: string, value: any): Promise<any>;
|
||||
/**
|
||||
* @update 删除缓存
|
||||
*/
|
||||
del(): Promise<void>;
|
||||
/**
|
||||
* 初始化
|
||||
*/
|
||||
init?: () => Promise<any>;
|
||||
}
|
||||
type User = {
|
||||
avatar?: string;
|
||||
description?: string;
|
||||
id?: string;
|
||||
needChangePassword?: boolean;
|
||||
orgs?: string[];
|
||||
type?: string;
|
||||
username?: string;
|
||||
};
|
||||
|
||||
export type CacheLoginUser = {
|
||||
user?: User;
|
||||
id?: string;
|
||||
accessToken?: string;
|
||||
refreshToken?: string;
|
||||
};
|
||||
type CacheLogin = {
|
||||
loginUsers: CacheLoginUser[];
|
||||
} & CacheLoginUser;
|
||||
|
||||
export type CacheStore<T = Cache> = {
|
||||
name: string;
|
||||
/**
|
||||
* 缓存数据
|
||||
* @important 需要先调用init
|
||||
*/
|
||||
cacheData: CacheLogin;
|
||||
/**
|
||||
* 实际操作的cache, 需要先调用init
|
||||
*/
|
||||
cache: T;
|
||||
|
||||
/**
|
||||
* 设置当前用户
|
||||
*/
|
||||
setLoginUser(user: CacheLoginUser): Promise<void>;
|
||||
/**
|
||||
* 获取当前用户
|
||||
*/
|
||||
getCurrentUser(): Promise<User>;
|
||||
/**
|
||||
* 获取当前用户列表
|
||||
*/
|
||||
getCurrentUserList(): Promise<CacheLoginUser[]>;
|
||||
/**
|
||||
* 获取缓存的refreshToken
|
||||
*/
|
||||
getRefreshToken(): Promise<string>;
|
||||
/**
|
||||
* 获取缓存的accessToken
|
||||
*/
|
||||
getAccessToken(): Promise<string>;
|
||||
/**
|
||||
* 清除当前用户
|
||||
*/
|
||||
clearCurrentUser(): Promise<void>;
|
||||
/**
|
||||
* 清除所有用户
|
||||
*/
|
||||
clearAll(): Promise<void>;
|
||||
|
||||
getValue(): Promise<CacheLogin>;
|
||||
setValue(value: CacheLogin): Promise<CacheLogin>;
|
||||
delValue(): Promise<void>;
|
||||
init(): Promise<any>;
|
||||
};
|
||||
|
||||
export type LoginCacheStoreOpts = {
|
||||
name: string;
|
||||
cache: Cache;
|
||||
};
|
||||
export class LoginCacheStore implements CacheStore<any> {
|
||||
cache: Cache;
|
||||
name: string;
|
||||
cacheData: CacheLogin;
|
||||
constructor(opts: LoginCacheStoreOpts) {
|
||||
if (!opts.cache) {
|
||||
throw new Error('cache is required');
|
||||
}
|
||||
// @ts-ignore
|
||||
this.cache = opts.cache;
|
||||
this.cacheData = {
|
||||
loginUsers: [],
|
||||
user: undefined,
|
||||
id: undefined,
|
||||
accessToken: undefined,
|
||||
refreshToken: undefined,
|
||||
};
|
||||
this.name = opts.name;
|
||||
}
|
||||
/**
|
||||
* 设置缓存
|
||||
* @param key
|
||||
* @param value
|
||||
* @returns
|
||||
*/
|
||||
async setValue(value: CacheLogin) {
|
||||
await this.cache.set(this.name, value);
|
||||
this.cacheData = value;
|
||||
return value;
|
||||
}
|
||||
/**
|
||||
* 删除缓存
|
||||
*/
|
||||
async delValue() {
|
||||
await this.cache.del();
|
||||
}
|
||||
getValue(): Promise<CacheLogin> {
|
||||
return this.cache.get(this.name);
|
||||
}
|
||||
/**
|
||||
* 初始化,设置默认值
|
||||
*/
|
||||
async init() {
|
||||
const defaultData = {
|
||||
loginUsers: [],
|
||||
user: null,
|
||||
id: null,
|
||||
accessToken: null,
|
||||
refreshToken: null,
|
||||
};
|
||||
if (this.cache.init) {
|
||||
try {
|
||||
const cacheData = await this.cache.init();
|
||||
this.cacheData = cacheData || defaultData;
|
||||
} catch (error) {
|
||||
console.log('cacheInit error', error);
|
||||
}
|
||||
} else {
|
||||
this.cacheData = (await this.getValue()) || defaultData;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 设置当前用户
|
||||
* @param user
|
||||
*/
|
||||
async setLoginUser(user: CacheLoginUser) {
|
||||
const has = this.cacheData.loginUsers.find((u) => u.id === user.id);
|
||||
if (has) {
|
||||
this.cacheData.loginUsers = this.cacheData?.loginUsers?.filter((u) => u?.id && u.id !== user.id);
|
||||
}
|
||||
this.cacheData.loginUsers.push(user);
|
||||
this.cacheData.user = user.user;
|
||||
this.cacheData.id = user.id;
|
||||
this.cacheData.accessToken = user.accessToken;
|
||||
this.cacheData.refreshToken = user.refreshToken;
|
||||
await this.setValue(this.cacheData);
|
||||
}
|
||||
|
||||
getCurrentUser(): Promise<CacheLoginUser> {
|
||||
const cacheData = this.cacheData;
|
||||
return Promise.resolve(cacheData.user!);
|
||||
}
|
||||
getCurrentUserList(): Promise<CacheLoginUser[]> {
|
||||
return Promise.resolve(this.cacheData.loginUsers.filter((u) => u?.id));
|
||||
}
|
||||
getRefreshToken(): Promise<string> {
|
||||
const cacheData = this.cacheData;
|
||||
return Promise.resolve(cacheData.refreshToken || '');
|
||||
}
|
||||
getAccessToken(): Promise<string> {
|
||||
const cacheData = this.cacheData;
|
||||
return Promise.resolve(cacheData.accessToken || '');
|
||||
}
|
||||
|
||||
async clearCurrentUser() {
|
||||
const user = await this.getCurrentUser();
|
||||
const has = this.cacheData.loginUsers.find((u) => u.id === user.id);
|
||||
if (has) {
|
||||
this.cacheData.loginUsers = this.cacheData?.loginUsers?.filter((u) => u?.id && u.id !== user.id);
|
||||
}
|
||||
this.cacheData.user = undefined;
|
||||
this.cacheData.id = undefined;
|
||||
this.cacheData.accessToken = undefined;
|
||||
this.cacheData.refreshToken = undefined;
|
||||
await this.setValue(this.cacheData);
|
||||
}
|
||||
async clearAll() {
|
||||
this.cacheData.loginUsers = [];
|
||||
this.cacheData.user = undefined;
|
||||
this.cacheData.id = undefined;
|
||||
this.cacheData.accessToken = undefined;
|
||||
this.cacheData.refreshToken = undefined;
|
||||
await this.setValue(this.cacheData);
|
||||
}
|
||||
}
|
132
assistant/src/query/query-login/login-node-cache.ts
Normal file
132
assistant/src/query/query-login/login-node-cache.ts
Normal file
@ -0,0 +1,132 @@
|
||||
import { Cache } from './login-cache.ts';
|
||||
import { homedir } from 'node:os';
|
||||
import { join, dirname } from 'node:path';
|
||||
import fs from 'node:fs';
|
||||
import { readFileSync, writeFileSync, accessSync } from 'node:fs';
|
||||
import { readFile, writeFile, unlink, mkdir } from 'node:fs/promises';
|
||||
export const fileExists = async (
|
||||
filePath: string,
|
||||
{ createIfNotExists = true, isFile = true, isDir = false }: { createIfNotExists?: boolean; isFile?: boolean; isDir?: boolean } = {},
|
||||
) => {
|
||||
try {
|
||||
accessSync(filePath, fs.constants.F_OK);
|
||||
return true;
|
||||
} catch (error) {
|
||||
if (createIfNotExists && isDir) {
|
||||
await mkdir(filePath, { recursive: true });
|
||||
return true;
|
||||
} else if (createIfNotExists && isFile) {
|
||||
await mkdir(dirname(filePath), { recursive: true });
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
export const readConfigFile = (filePath: string) => {
|
||||
try {
|
||||
const data = readFileSync(filePath, 'utf-8');
|
||||
const jsonData = JSON.parse(data);
|
||||
return jsonData;
|
||||
} catch (error) {
|
||||
return {};
|
||||
}
|
||||
};
|
||||
export const writeConfigFile = (filePath: string, data: any) => {
|
||||
writeFileSync(filePath, JSON.stringify(data, null, 2));
|
||||
};
|
||||
export const getHostName = () => {
|
||||
const configDir = join(homedir(), '.config', 'envision');
|
||||
const configFile = join(configDir, 'config.json');
|
||||
const config = readConfigFile(configFile);
|
||||
const baseURL = config.baseURL || 'https://kevisual.cn';
|
||||
const hostname = new URL(baseURL).hostname;
|
||||
return hostname;
|
||||
};
|
||||
export class StorageNode implements Storage {
|
||||
cacheData: any;
|
||||
filePath: string;
|
||||
constructor() {
|
||||
this.cacheData = {};
|
||||
const configDir = join(homedir(), '.config', 'envision');
|
||||
const hostname = getHostName();
|
||||
this.filePath = join(configDir, 'config', `${hostname}-storage.json`);
|
||||
fileExists(this.filePath, { isFile: true });
|
||||
}
|
||||
async loadCache() {
|
||||
const filePath = this.filePath;
|
||||
try {
|
||||
const data = await readConfigFile(filePath);
|
||||
this.cacheData = data;
|
||||
} catch (error) {
|
||||
this.cacheData = {};
|
||||
await writeFile(filePath, JSON.stringify(this.cacheData, null, 2));
|
||||
}
|
||||
}
|
||||
get length() {
|
||||
return Object.keys(this.cacheData).length;
|
||||
}
|
||||
getItem(key: string) {
|
||||
return this.cacheData[key];
|
||||
}
|
||||
setItem(key: string, value: any) {
|
||||
this.cacheData[key] = value;
|
||||
writeFile(this.filePath, JSON.stringify(this.cacheData, null, 2));
|
||||
}
|
||||
removeItem(key: string) {
|
||||
delete this.cacheData[key];
|
||||
writeFile(this.filePath, JSON.stringify(this.cacheData, null, 2));
|
||||
}
|
||||
clear() {
|
||||
this.cacheData = {};
|
||||
writeFile(this.filePath, JSON.stringify(this.cacheData, null, 2));
|
||||
}
|
||||
key(index: number) {
|
||||
return Object.keys(this.cacheData)[index];
|
||||
}
|
||||
}
|
||||
export class LoginNodeCache implements Cache {
|
||||
filepath: string;
|
||||
|
||||
constructor(filepath?: string) {
|
||||
this.filepath = filepath || join(homedir(), '.config', 'envision', 'config', `${getHostName()}-login.json`);
|
||||
fileExists(this.filepath, { isFile: true });
|
||||
}
|
||||
async get(_key: string) {
|
||||
try {
|
||||
const filePath = this.filepath;
|
||||
const data = readConfigFile(filePath);
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.log('get error', error);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
async set(_key: string, value: any) {
|
||||
try {
|
||||
const data = readConfigFile(this.filepath);
|
||||
const newData = { ...data, ...value };
|
||||
writeConfigFile(this.filepath, newData);
|
||||
} catch (error) {
|
||||
console.log('set error', error);
|
||||
}
|
||||
}
|
||||
async del() {
|
||||
await unlink(this.filepath);
|
||||
}
|
||||
async loadCache(filePath: string) {
|
||||
try {
|
||||
const data = await readFile(filePath, 'utf-8');
|
||||
const jsonData = JSON.parse(data);
|
||||
return jsonData;
|
||||
} catch (error) {
|
||||
// console.log('loadCache error', error);
|
||||
console.log('create new cache file:', filePath);
|
||||
const defaultData = { loginUsers: [] };
|
||||
writeConfigFile(filePath, defaultData);
|
||||
return defaultData;
|
||||
}
|
||||
}
|
||||
async init() {
|
||||
return await this.loadCache(this.filepath);
|
||||
}
|
||||
}
|
12
assistant/src/query/query-login/query-login-browser.ts
Normal file
12
assistant/src/query/query-login/query-login-browser.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { QueryLogin, QueryLoginOpts } from './query-login.ts';
|
||||
import { MyCache } from '@kevisual/cache';
|
||||
type QueryLoginNodeOptsWithoutCache = Omit<QueryLoginOpts, 'cache'>;
|
||||
|
||||
export class QueryLoginBrowser extends QueryLogin {
|
||||
constructor(opts: QueryLoginNodeOptsWithoutCache) {
|
||||
super({
|
||||
...opts,
|
||||
cache: new MyCache('login'),
|
||||
});
|
||||
}
|
||||
}
|
14
assistant/src/query/query-login/query-login-node.ts
Normal file
14
assistant/src/query/query-login/query-login-node.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { QueryLogin, QueryLoginOpts } from './query-login.ts';
|
||||
import { LoginNodeCache, StorageNode } from './login-node-cache.ts';
|
||||
type QueryLoginNodeOptsWithoutCache = Omit<QueryLoginOpts, 'cache'>;
|
||||
export const storage = new StorageNode();
|
||||
await storage.loadCache();
|
||||
export class QueryLoginNode extends QueryLogin {
|
||||
constructor(opts: QueryLoginNodeOptsWithoutCache) {
|
||||
super({
|
||||
...opts,
|
||||
storage,
|
||||
cache: new LoginNodeCache(),
|
||||
});
|
||||
}
|
||||
}
|
434
assistant/src/query/query-login/query-login.ts
Normal file
434
assistant/src/query/query-login/query-login.ts
Normal file
@ -0,0 +1,434 @@
|
||||
import { Query, BaseQuery } from '@kevisual/query';
|
||||
import type { Result, DataOpts } from '@kevisual/query/query';
|
||||
import { setBaseResponse } from '@kevisual/query/query';
|
||||
import { LoginCacheStore, CacheStore } from './login-cache.ts';
|
||||
import { Cache } from './login-cache.ts';
|
||||
|
||||
export type QueryLoginOpts = {
|
||||
query?: Query;
|
||||
isBrowser?: boolean;
|
||||
onLoad?: () => void;
|
||||
storage?: Storage;
|
||||
cache: Cache;
|
||||
};
|
||||
export type QueryLoginData = {
|
||||
username?: string;
|
||||
password: string;
|
||||
email?: string;
|
||||
};
|
||||
export type QueryLoginResult = {
|
||||
accessToken: string;
|
||||
refreshToken: string;
|
||||
};
|
||||
|
||||
export class QueryLogin extends BaseQuery {
|
||||
/**
|
||||
* query login cache, 非实际操作, 一个cache的包裹模块
|
||||
*/
|
||||
cacheStore: CacheStore;
|
||||
isBrowser: boolean;
|
||||
load?: boolean;
|
||||
storage: Storage;
|
||||
onLoad?: () => void;
|
||||
|
||||
constructor(opts?: QueryLoginOpts) {
|
||||
super({
|
||||
query: opts?.query || new Query(),
|
||||
});
|
||||
this.cacheStore = new LoginCacheStore({ name: 'login', cache: opts?.cache! });
|
||||
this.isBrowser = opts?.isBrowser ?? true;
|
||||
this.init();
|
||||
this.onLoad = opts?.onLoad;
|
||||
this.storage = opts?.storage || localStorage;
|
||||
}
|
||||
setQuery(query: Query) {
|
||||
this.query = query;
|
||||
}
|
||||
private async init() {
|
||||
await this.cacheStore.init();
|
||||
this.load = true;
|
||||
this.onLoad?.();
|
||||
}
|
||||
async post<T = any>(data: any, opts?: DataOpts) {
|
||||
try {
|
||||
return this.query.post<T>({ path: 'user', ...data }, opts);
|
||||
} catch (error) {
|
||||
console.log('error', error);
|
||||
return {
|
||||
code: 400,
|
||||
} as any;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 登录,
|
||||
* @param data
|
||||
* @returns
|
||||
*/
|
||||
async login(data: QueryLoginData) {
|
||||
const res = await this.post<QueryLoginResult>({ key: 'login', ...data });
|
||||
if (res.code === 200) {
|
||||
const { accessToken, refreshToken } = res?.data || {};
|
||||
this.storage.setItem('token', accessToken || '');
|
||||
await this.beforeSetLoginUser({ accessToken, refreshToken });
|
||||
}
|
||||
return res;
|
||||
}
|
||||
/**
|
||||
* 手机号登录
|
||||
* @param data
|
||||
* @returns
|
||||
*/
|
||||
async loginByCode(data: { phone: string; code: string }) {
|
||||
const res = await this.post<QueryLoginResult>({ path: 'sms', key: 'login', data });
|
||||
if (res.code === 200) {
|
||||
const { accessToken, refreshToken } = res?.data || {};
|
||||
this.storage.setItem('token', accessToken || '');
|
||||
await this.beforeSetLoginUser({ accessToken, refreshToken });
|
||||
}
|
||||
return res;
|
||||
}
|
||||
/**
|
||||
* 设置token
|
||||
* @param token
|
||||
*/
|
||||
async setLoginToken(token: { accessToken: string; refreshToken: string }) {
|
||||
const { accessToken, refreshToken } = token;
|
||||
this.storage.setItem('token', accessToken || '');
|
||||
await this.beforeSetLoginUser({ accessToken, refreshToken });
|
||||
}
|
||||
async loginByWechat(data: { code: string }) {
|
||||
const res = await this.post<QueryLoginResult>({ path: 'wx', key: 'open-login', code: data.code });
|
||||
if (res.code === 200) {
|
||||
const { accessToken, refreshToken } = res?.data || {};
|
||||
this.storage.setItem('token', accessToken || '');
|
||||
await this.beforeSetLoginUser({ accessToken, refreshToken });
|
||||
}
|
||||
return res;
|
||||
}
|
||||
/**
|
||||
* 检测微信登录,登陆成功后,调用onSuccess,否则调用onError
|
||||
* @param param0
|
||||
*/
|
||||
async checkWechat({ onSuccess, onError }: { onSuccess?: (res: QueryLoginResult) => void; onError?: (res: any) => void }) {
|
||||
const url = new URL(window.location.href);
|
||||
const code = url.searchParams.get('code');
|
||||
const state = url.searchParams.get('state');
|
||||
if (code && state) {
|
||||
const res = await this.loginByWechat({ code });
|
||||
if (res.code === 200) {
|
||||
onSuccess?.(res.data);
|
||||
} else {
|
||||
onError?.(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 登陆成功,需要获取用户信息进行缓存
|
||||
* @param param0
|
||||
*/
|
||||
async beforeSetLoginUser({ accessToken, refreshToken, check401 }: { accessToken?: string; refreshToken?: string; check401?: boolean }) {
|
||||
if (accessToken && refreshToken) {
|
||||
const resUser = await this.getMe(accessToken, check401);
|
||||
if (resUser.code === 200) {
|
||||
const user = resUser.data;
|
||||
if (user) {
|
||||
this.cacheStore.setLoginUser({
|
||||
user,
|
||||
id: user.id,
|
||||
accessToken,
|
||||
refreshToken,
|
||||
});
|
||||
} else {
|
||||
console.error('登录失败');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 刷新token
|
||||
* @param refreshToken
|
||||
* @returns
|
||||
*/
|
||||
async queryRefreshToken(refreshToken?: string) {
|
||||
const _refreshToken = refreshToken || this.cacheStore.getRefreshToken();
|
||||
let data = { refreshToken: _refreshToken };
|
||||
if (!_refreshToken) {
|
||||
await this.cacheStore.clearCurrentUser();
|
||||
return {
|
||||
code: 401,
|
||||
message: '请先登录',
|
||||
data: {} as any,
|
||||
};
|
||||
}
|
||||
return this.post(
|
||||
{ key: 'refreshToken', data },
|
||||
{
|
||||
afterResponse: async (response, ctx) => {
|
||||
setBaseResponse(response);
|
||||
return response as any;
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
/**
|
||||
* 检查401错误,并刷新token, 如果refreshToken存在,则刷新token, 否则返回401
|
||||
* 拦截请求,请使用run401Action, 不要直接使用 afterCheck401ToRefreshToken
|
||||
* @param response
|
||||
* @param ctx
|
||||
* @param refetch
|
||||
* @returns
|
||||
*/
|
||||
async afterCheck401ToRefreshToken(response: Result, ctx?: { req?: any; res?: any; fetch?: any }, refetch?: boolean) {
|
||||
const that = this;
|
||||
if (response?.code === 401) {
|
||||
const hasRefreshToken = await that.cacheStore.getRefreshToken();
|
||||
if (hasRefreshToken) {
|
||||
const res = await that.queryRefreshToken(hasRefreshToken);
|
||||
if (res.code === 200) {
|
||||
const { accessToken, refreshToken } = res?.data || {};
|
||||
that.storage.setItem('token', accessToken || '');
|
||||
await that.beforeSetLoginUser({ accessToken, refreshToken, check401: false });
|
||||
if (refetch && ctx && ctx.req && ctx.req.url && ctx.fetch) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 1500));
|
||||
const url = ctx.req?.url;
|
||||
const body = ctx.req?.body;
|
||||
const headers = ctx.req?.headers;
|
||||
const res = await ctx.fetch(url, {
|
||||
method: 'POST',
|
||||
body: body,
|
||||
headers: { ...headers, Authorization: `Bearer ${accessToken}` },
|
||||
});
|
||||
setBaseResponse(res);
|
||||
return res;
|
||||
}
|
||||
} else {
|
||||
that.storage.removeItem('token');
|
||||
await that.cacheStore.clearCurrentUser();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
return response as any;
|
||||
}
|
||||
/**
|
||||
* 一个简单的401处理, 如果401,则刷新token, 如果refreshToken不存在,则返回401
|
||||
* refetch 是否重新请求, 会有bug,无限循环,按需要使用
|
||||
* TODO:
|
||||
* @param response
|
||||
* @param ctx
|
||||
* @param opts
|
||||
* @returns
|
||||
*/
|
||||
async run401Action(
|
||||
response: Result,
|
||||
ctx?: { req?: any; res?: any; fetch?: any },
|
||||
opts?: {
|
||||
/**
|
||||
* 是否重新请求, 会有bug,无限循环,按需要使用
|
||||
*/
|
||||
refetch?: boolean;
|
||||
/**
|
||||
* check之后的回调
|
||||
*/
|
||||
afterCheck?: (res: Result) => any;
|
||||
/**
|
||||
* 401处理后, 还是401, 则回调
|
||||
*/
|
||||
afterAlso401?: (res: Result) => any;
|
||||
},
|
||||
) {
|
||||
const that = this;
|
||||
const refetch = opts?.refetch ?? false;
|
||||
if (response?.code === 401) {
|
||||
if (that.query.stop === true) {
|
||||
return { code: 500, success: false, message: 'refresh token loading...' };
|
||||
}
|
||||
that.query.stop = true;
|
||||
const res = await that.afterCheck401ToRefreshToken(response, ctx, refetch);
|
||||
that.query.stop = false;
|
||||
opts?.afterCheck?.(res);
|
||||
if (res.code === 401) {
|
||||
opts?.afterAlso401?.(res);
|
||||
}
|
||||
return res;
|
||||
} else {
|
||||
return response as any;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 获取用户信息
|
||||
* @param token
|
||||
* @returns
|
||||
*/
|
||||
async getMe(token?: string, check401: boolean = true) {
|
||||
const _token = token || this.storage.getItem('token');
|
||||
const that = this;
|
||||
return that.post(
|
||||
{ key: 'me' },
|
||||
{
|
||||
beforeRequest: async (config) => {
|
||||
if (config.headers) {
|
||||
config.headers['Authorization'] = `Bearer ${_token}`;
|
||||
}
|
||||
if (!_token) {
|
||||
return false;
|
||||
}
|
||||
return config;
|
||||
},
|
||||
afterResponse: async (response, ctx) => {
|
||||
if (response?.code === 401 && check401 && !token) {
|
||||
return await that.afterCheck401ToRefreshToken(response, ctx);
|
||||
}
|
||||
return response as any;
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
/**
|
||||
* 检查本地用户,如果本地用户存在,则返回本地用户,否则返回null
|
||||
* @returns
|
||||
*/
|
||||
async checkLocalUser() {
|
||||
const user = await this.cacheStore.getCurrentUser();
|
||||
if (user) {
|
||||
return user;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* 检查本地token是否存在,简单的判断是否已经属于登陆状态
|
||||
* @returns
|
||||
*/
|
||||
async checkLocalToken() {
|
||||
const token = this.storage.getItem('token');
|
||||
return !!token;
|
||||
}
|
||||
/**
|
||||
* 检查本地用户列表
|
||||
* @returns
|
||||
*/
|
||||
async getToken() {
|
||||
const token = this.storage.getItem('token');
|
||||
return token || '';
|
||||
}
|
||||
async beforeRequest(opts: any = {}) {
|
||||
const token = this.storage.getItem('token');
|
||||
if (token) {
|
||||
opts.headers = { ...opts.headers, Authorization: `Bearer ${token}` };
|
||||
}
|
||||
return opts;
|
||||
}
|
||||
/**
|
||||
* 请求更新,切换用户, 使用switchUser
|
||||
* @param username
|
||||
* @returns
|
||||
*/
|
||||
private async postSwitchUser(username: string) {
|
||||
return this.post({ key: 'switchCheck', data: { username } });
|
||||
}
|
||||
/**
|
||||
* 切换用户
|
||||
* @param username
|
||||
* @returns
|
||||
*/
|
||||
async switchUser(username: string) {
|
||||
const localUserList = await this.cacheStore.getCurrentUserList();
|
||||
const user = localUserList.find((userItem) => userItem.user!.username === username);
|
||||
if (user) {
|
||||
this.storage.setItem('token', user.accessToken || '');
|
||||
await this.beforeSetLoginUser({ accessToken: user.accessToken, refreshToken: user.refreshToken });
|
||||
return {
|
||||
code: 200,
|
||||
data: {
|
||||
accessToken: user.accessToken,
|
||||
refreshToken: user.refreshToken,
|
||||
},
|
||||
success: true,
|
||||
message: '切换用户成功',
|
||||
};
|
||||
}
|
||||
const res = await this.postSwitchUser(username);
|
||||
|
||||
if (res.code === 200) {
|
||||
const { accessToken, refreshToken } = res?.data || {};
|
||||
this.storage.setItem('token', accessToken || '');
|
||||
await this.beforeSetLoginUser({ accessToken, refreshToken });
|
||||
}
|
||||
return res;
|
||||
}
|
||||
/**
|
||||
* 退出登陆,去掉token, 并删除缓存
|
||||
* @returns
|
||||
*/
|
||||
async logout() {
|
||||
this.storage.removeItem('token');
|
||||
const users = await this.cacheStore.getCurrentUserList();
|
||||
const tokens = users
|
||||
.map((user) => {
|
||||
return user?.accessToken;
|
||||
})
|
||||
.filter(Boolean);
|
||||
this.cacheStore.delValue();
|
||||
return this.post<Result>({ key: 'logout', data: { tokens } });
|
||||
}
|
||||
/**
|
||||
* 检查用户名的组,这个用户是否存在
|
||||
* @param username
|
||||
* @returns
|
||||
*/
|
||||
async hasUser(username: string) {
|
||||
const that = this;
|
||||
return this.post<Result>(
|
||||
{
|
||||
path: 'org',
|
||||
key: 'hasUser',
|
||||
data: {
|
||||
username,
|
||||
},
|
||||
},
|
||||
{
|
||||
afterResponse: async (response, ctx) => {
|
||||
if (response?.code === 401) {
|
||||
const res = await that.afterCheck401ToRefreshToken(response, ctx, true);
|
||||
return res;
|
||||
}
|
||||
return response as any;
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
/**
|
||||
* 检查登录状态
|
||||
* @param token
|
||||
* @returns
|
||||
*/
|
||||
async checkLoginStatus(token: string) {
|
||||
const res = await this.post({
|
||||
path: 'user',
|
||||
key: 'checkLoginStatus',
|
||||
loginToken: token,
|
||||
});
|
||||
if (res.code === 200) {
|
||||
const accessToken = res.data?.accessToken;
|
||||
this.storage.setItem('token', accessToken || '');
|
||||
await this.beforeSetLoginUser({ accessToken, refreshToken: res.data?.refreshToken });
|
||||
return res;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* 使用web登录,创建url地址, 需要MD5和jsonwebtoken
|
||||
*/
|
||||
loginWithWeb(baseURL: string, { MD5, jsonwebtoken }: { MD5: any; jsonwebtoken: any }) {
|
||||
const randomId = Math.random().toString(36).substring(2, 15);
|
||||
const timestamp = Date.now();
|
||||
const tokenSecret = 'xiao' + randomId;
|
||||
const sign = MD5(`${tokenSecret}${timestamp}`).toString();
|
||||
const token = jsonwebtoken.sign({ randomId, timestamp, sign }, tokenSecret, {
|
||||
// 10分钟过期
|
||||
expiresIn: 60 * 10, // 10分钟
|
||||
});
|
||||
const url = `${baseURL}/api/router?path=user&key=webLogin&p&loginToken=${token}&sign=${sign}&randomId=${randomId}`;
|
||||
return { url, token, tokenSecret };
|
||||
}
|
||||
}
|
35
assistant/src/routes/ai/index.ts
Normal file
35
assistant/src/routes/ai/index.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import { useContextKey } from '@kevisual/use-config/context';
|
||||
// import { appDefine } from '@/query/query-ai/defines/ai.ts';
|
||||
import { ProviderManager } from '@kevisual/ai';
|
||||
import { App } from '@kevisual/router';
|
||||
import { assistantConfig } from '@/app.ts';
|
||||
const app = useContextKey<App>('app');
|
||||
|
||||
app
|
||||
.route({
|
||||
path: 'ai',
|
||||
key: 'chat',
|
||||
middleware: ['auth'],
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const { messages = [], username, group, question, chatOpts = {} } = ctx.query;
|
||||
// if (!username || !model || !group) {
|
||||
// return ctx.throw(4001, 'username, model, group is required');
|
||||
// }
|
||||
if (messages.length === 0 && question) {
|
||||
messages.push({
|
||||
role: 'user',
|
||||
content: question,
|
||||
});
|
||||
}
|
||||
const as = assistantConfig.getCacheAssistantConfig();
|
||||
const { provider, apiKey, model } = as.ai || {};
|
||||
const pm = new ProviderManager({
|
||||
provider: provider,
|
||||
apiKey: apiKey,
|
||||
model: model,
|
||||
});
|
||||
const result = await pm.provider.chat(messages, chatOpts);
|
||||
ctx.body = result;
|
||||
})
|
||||
.addTo(app);
|
@ -2,6 +2,8 @@ import { Query } from '@kevisual/query';
|
||||
import { app, assistantConfig } from '../app.ts';
|
||||
import './config/index.ts';
|
||||
import './shop-install/index.ts';
|
||||
import './ai/index.ts';
|
||||
|
||||
import os from 'node:os';
|
||||
|
||||
app
|
||||
|
655
pnpm-lock.yaml
generated
655
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user