feat: add CNB login functionality and user management
- Introduced `cnb-login` route to handle user login via CNB token. - Created `CnbServices` class for managing CNB user interactions. - Added `findByCnbId` method in the User model to retrieve users by CNB ID. - Updated error handling to provide more structured error messages. - Enhanced user creation logic to handle CNB users. - Added tests for the new CNB login functionality.
This commit is contained in:
@@ -66,12 +66,13 @@
|
||||
"devDependencies": {
|
||||
"@aws-sdk/client-s3": "^3.994.0",
|
||||
"@kevisual/api": "^0.0.52",
|
||||
"@kevisual/cnb": "^0.0.28",
|
||||
"@kevisual/context": "^0.0.8",
|
||||
"@kevisual/local-app-manager": "0.1.32",
|
||||
"@kevisual/logger": "^0.0.4",
|
||||
"@kevisual/oss": "0.0.19",
|
||||
"@kevisual/permission": "^0.0.4",
|
||||
"@kevisual/router": "0.0.80",
|
||||
"@kevisual/router": "0.0.83",
|
||||
"@kevisual/types": "^0.0.12",
|
||||
"@kevisual/use-config": "^1.0.30",
|
||||
"@types/archiver": "^7.0.0",
|
||||
@@ -100,7 +101,7 @@
|
||||
"inflight": "latest",
|
||||
"picomatch": "^4.0.2"
|
||||
},
|
||||
"packageManager": "pnpm@10.30.0",
|
||||
"packageManager": "pnpm@10.30.1",
|
||||
"workspaces": [
|
||||
"wxmsg"
|
||||
]
|
||||
|
||||
226
pnpm-lock.yaml
generated
226
pnpm-lock.yaml
generated
@@ -67,6 +67,9 @@ importers:
|
||||
'@kevisual/api':
|
||||
specifier: ^0.0.52
|
||||
version: 0.0.52(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
'@kevisual/cnb':
|
||||
specifier: ^0.0.28
|
||||
version: 0.0.28(dotenv@17.3.1)(ioredis@5.9.3)
|
||||
'@kevisual/context':
|
||||
specifier: ^0.0.8
|
||||
version: 0.0.8
|
||||
@@ -83,8 +86,8 @@ importers:
|
||||
specifier: ^0.0.4
|
||||
version: 0.0.4
|
||||
'@kevisual/router':
|
||||
specifier: 0.0.80
|
||||
version: 0.0.80
|
||||
specifier: 0.0.83
|
||||
version: 0.0.83
|
||||
'@kevisual/types':
|
||||
specifier: ^0.0.12
|
||||
version: 0.0.12
|
||||
@@ -667,6 +670,9 @@ packages:
|
||||
'@kevisual/auth@2.0.3':
|
||||
resolution: {integrity: sha512-4xpijaIhlCTr/DlJaV/gmkCQeg45EO1yxWpRvUX+1jCdVbuxSR0wZrF0SD9oybnjmKWMKDNPLsXyduFjMGcItA==}
|
||||
|
||||
'@kevisual/cnb@0.0.28':
|
||||
resolution: {integrity: sha512-mv45B68D/lliPBUXEnxbofV+Ds/KSYXuGzzG7S8yEekwp31PwRjecP2dyE0Mxe+DlhFmsSYN09liVUHcCXDbOg==}
|
||||
|
||||
'@kevisual/context@0.0.4':
|
||||
resolution: {integrity: sha512-HJeLeZQLU+7tCluSfOyvkgKLs0HjCZrdJlZgEgKRSa8XTwZfMAUt6J7qZTbrZAHBlPtX68EPu/PI8JMCeu3WAQ==}
|
||||
|
||||
@@ -712,6 +718,9 @@ packages:
|
||||
'@kevisual/router@0.0.80':
|
||||
resolution: {integrity: sha512-rVwi6Yf411bnNm2x94lMm+s4Csw0Yb7u/aj+VJJ59iouAYhjLuL7Rs1EcARhnQf47cegBJi6zozfGHgLsLHN2w==}
|
||||
|
||||
'@kevisual/router@0.0.83':
|
||||
resolution: {integrity: sha512-CVazzM1rXVyvU7QcMQr0/EuqacRNEGalThDDLGQcvKEVHyduJ9yWddn6kezgWFCpNlPKhzSCKkIFuZVixNVxDQ==}
|
||||
|
||||
'@kevisual/types@0.0.12':
|
||||
resolution: {integrity: sha512-zJXH2dosir3jVrQ6QG4i0+iLQeT9gJ3H+cKXs8ReWboxBSYzUZO78XssVeVrFPsJ33iaAqo4q3DWbSS1dWGn7Q==}
|
||||
|
||||
@@ -724,6 +733,10 @@ packages:
|
||||
resolution: {integrity: sha512-jlFxSlXUEz93cFW+UYT5BXv/rFVgiMQnIfqRYZ0gj1hSP8PMGRqMqUoHSLfKvfRRS4jseLSvTTeEKSQpZJtURg==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
|
||||
'@kevisual/ws@8.19.0':
|
||||
resolution: {integrity: sha512-jLsL80wBBKkrJZrfk3SQpJ9JA/zREdlUROj7eCkmzqduAWKSI0wVcXuCKf+mLFCHB0Q0Tkh2rgzjSlurt3JQgw==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
|
||||
'@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3':
|
||||
resolution: {integrity: sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==}
|
||||
cpu: [arm64]
|
||||
@@ -1192,6 +1205,10 @@ packages:
|
||||
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
|
||||
engines: {node: '>= 8.10.0'}
|
||||
|
||||
chokidar@5.0.0:
|
||||
resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==}
|
||||
engines: {node: '>= 20.19.0'}
|
||||
|
||||
chownr@1.1.4:
|
||||
resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==}
|
||||
|
||||
@@ -1217,6 +1234,9 @@ packages:
|
||||
resolution: {integrity: sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==}
|
||||
engines: {node: '>= 14'}
|
||||
|
||||
cookie-es@1.2.2:
|
||||
resolution: {integrity: sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==}
|
||||
|
||||
core-util-is@1.0.3:
|
||||
resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
|
||||
|
||||
@@ -1240,6 +1260,9 @@ packages:
|
||||
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
crossws@0.3.5:
|
||||
resolution: {integrity: sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==}
|
||||
|
||||
crypto-js@4.2.0:
|
||||
resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==}
|
||||
|
||||
@@ -1293,6 +1316,9 @@ packages:
|
||||
resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
|
||||
engines: {node: '>=4.0.0'}
|
||||
|
||||
defu@6.1.4:
|
||||
resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==}
|
||||
|
||||
degenerator@5.0.1:
|
||||
resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==}
|
||||
engines: {node: '>= 14'}
|
||||
@@ -1305,6 +1331,9 @@ packages:
|
||||
resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
destr@2.0.5:
|
||||
resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==}
|
||||
|
||||
detect-libc@2.1.2:
|
||||
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -1603,6 +1632,9 @@ packages:
|
||||
graceful-fs@4.2.11:
|
||||
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
|
||||
|
||||
h3@1.15.5:
|
||||
resolution: {integrity: sha512-xEyq3rSl+dhGX2Lm0+eFQIAzlDN6Fs0EcC4f7BNUmzaRX/PTzeuM+Tr2lHB8FoXggsQIeXLj8EDVgs5ywxyxmg==}
|
||||
|
||||
has-flag@4.0.0:
|
||||
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -1648,6 +1680,9 @@ packages:
|
||||
resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==}
|
||||
engines: {node: '>= 12'}
|
||||
|
||||
iron-webcrypto@1.2.1:
|
||||
resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==}
|
||||
|
||||
is-binary-path@2.1.0:
|
||||
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -1748,6 +1783,10 @@ packages:
|
||||
lru-cache@10.4.3:
|
||||
resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
|
||||
|
||||
lru-cache@11.2.6:
|
||||
resolution: {integrity: sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==}
|
||||
engines: {node: 20 || >=22}
|
||||
|
||||
lru-cache@6.0.0:
|
||||
resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -1835,14 +1874,23 @@ packages:
|
||||
node-abort-controller@3.1.1:
|
||||
resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==}
|
||||
|
||||
node-fetch-native@1.6.7:
|
||||
resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==}
|
||||
|
||||
node-gyp-build-optional-packages@5.2.2:
|
||||
resolution: {integrity: sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==}
|
||||
hasBin: true
|
||||
|
||||
node-mock-http@1.0.4:
|
||||
resolution: {integrity: sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ==}
|
||||
|
||||
normalize-path@3.0.0:
|
||||
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
ofetch@1.5.1:
|
||||
resolution: {integrity: sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==}
|
||||
|
||||
on-finished@2.4.1:
|
||||
resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
|
||||
engines: {node: '>= 0.8'}
|
||||
@@ -2000,6 +2048,9 @@ packages:
|
||||
queue-tick@1.0.1:
|
||||
resolution: {integrity: sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==}
|
||||
|
||||
radix3@1.1.2:
|
||||
resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==}
|
||||
|
||||
range-parser@1.2.1:
|
||||
resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
|
||||
engines: {node: '>= 0.6'}
|
||||
@@ -2039,6 +2090,10 @@ packages:
|
||||
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
|
||||
engines: {node: '>=8.10.0'}
|
||||
|
||||
readdirp@5.0.0:
|
||||
resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==}
|
||||
engines: {node: '>= 20.19.0'}
|
||||
|
||||
redis-errors@1.2.0:
|
||||
resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==}
|
||||
engines: {node: '>=4'}
|
||||
@@ -2254,6 +2309,12 @@ packages:
|
||||
tx2@1.0.5:
|
||||
resolution: {integrity: sha512-sJ24w0y03Md/bxzK4FU8J8JveYYUbSs2FViLJ2D/8bytSiyPRbuE3DyL/9UKYXTZlV3yXq0L8GLlhobTnekCVg==}
|
||||
|
||||
ufo@1.6.3:
|
||||
resolution: {integrity: sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==}
|
||||
|
||||
uncrypto@0.1.3:
|
||||
resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==}
|
||||
|
||||
undici-types@7.16.0:
|
||||
resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==}
|
||||
|
||||
@@ -2264,6 +2325,68 @@ packages:
|
||||
resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
|
||||
unstorage@1.17.4:
|
||||
resolution: {integrity: sha512-fHK0yNg38tBiJKp/Vgsq4j0JEsCmgqH58HAn707S7zGkArbZsVr/CwINoi+nh3h98BRCwKvx1K3Xg9u3VV83sw==}
|
||||
peerDependencies:
|
||||
'@azure/app-configuration': ^1.8.0
|
||||
'@azure/cosmos': ^4.2.0
|
||||
'@azure/data-tables': ^13.3.0
|
||||
'@azure/identity': ^4.6.0
|
||||
'@azure/keyvault-secrets': ^4.9.0
|
||||
'@azure/storage-blob': ^12.26.0
|
||||
'@capacitor/preferences': ^6 || ^7 || ^8
|
||||
'@deno/kv': '>=0.9.0'
|
||||
'@netlify/blobs': ^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0
|
||||
'@planetscale/database': ^1.19.0
|
||||
'@upstash/redis': ^1.34.3
|
||||
'@vercel/blob': '>=0.27.1'
|
||||
'@vercel/functions': ^2.2.12 || ^3.0.0
|
||||
'@vercel/kv': ^1 || ^2 || ^3
|
||||
aws4fetch: ^1.0.20
|
||||
db0: '>=0.2.1'
|
||||
idb-keyval: ^6.2.1
|
||||
ioredis: ^5.4.2
|
||||
uploadthing: ^7.4.4
|
||||
peerDependenciesMeta:
|
||||
'@azure/app-configuration':
|
||||
optional: true
|
||||
'@azure/cosmos':
|
||||
optional: true
|
||||
'@azure/data-tables':
|
||||
optional: true
|
||||
'@azure/identity':
|
||||
optional: true
|
||||
'@azure/keyvault-secrets':
|
||||
optional: true
|
||||
'@azure/storage-blob':
|
||||
optional: true
|
||||
'@capacitor/preferences':
|
||||
optional: true
|
||||
'@deno/kv':
|
||||
optional: true
|
||||
'@netlify/blobs':
|
||||
optional: true
|
||||
'@planetscale/database':
|
||||
optional: true
|
||||
'@upstash/redis':
|
||||
optional: true
|
||||
'@vercel/blob':
|
||||
optional: true
|
||||
'@vercel/functions':
|
||||
optional: true
|
||||
'@vercel/kv':
|
||||
optional: true
|
||||
aws4fetch:
|
||||
optional: true
|
||||
db0:
|
||||
optional: true
|
||||
idb-keyval:
|
||||
optional: true
|
||||
ioredis:
|
||||
optional: true
|
||||
uploadthing:
|
||||
optional: true
|
||||
|
||||
util-deprecate@1.0.2:
|
||||
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
||||
|
||||
@@ -3034,6 +3157,38 @@ snapshots:
|
||||
|
||||
'@kevisual/auth@2.0.3': {}
|
||||
|
||||
'@kevisual/cnb@0.0.28(dotenv@17.3.1)(ioredis@5.9.3)':
|
||||
dependencies:
|
||||
'@kevisual/query': 0.0.49
|
||||
'@kevisual/router': 0.0.80
|
||||
'@kevisual/use-config': 1.0.30(dotenv@17.3.1)
|
||||
es-toolkit: 1.44.0
|
||||
nanoid: 5.1.6
|
||||
unstorage: 1.17.4(ioredis@5.9.3)
|
||||
ws: '@kevisual/ws@8.19.0'
|
||||
zod: 4.3.6
|
||||
transitivePeerDependencies:
|
||||
- '@azure/app-configuration'
|
||||
- '@azure/cosmos'
|
||||
- '@azure/data-tables'
|
||||
- '@azure/identity'
|
||||
- '@azure/keyvault-secrets'
|
||||
- '@azure/storage-blob'
|
||||
- '@capacitor/preferences'
|
||||
- '@deno/kv'
|
||||
- '@netlify/blobs'
|
||||
- '@planetscale/database'
|
||||
- '@upstash/redis'
|
||||
- '@vercel/blob'
|
||||
- '@vercel/functions'
|
||||
- '@vercel/kv'
|
||||
- aws4fetch
|
||||
- db0
|
||||
- dotenv
|
||||
- idb-keyval
|
||||
- ioredis
|
||||
- uploadthing
|
||||
|
||||
'@kevisual/context@0.0.4': {}
|
||||
|
||||
'@kevisual/context@0.0.6': {}
|
||||
@@ -3080,6 +3235,10 @@ snapshots:
|
||||
dependencies:
|
||||
es-toolkit: 1.44.0
|
||||
|
||||
'@kevisual/router@0.0.83':
|
||||
dependencies:
|
||||
es-toolkit: 1.44.0
|
||||
|
||||
'@kevisual/types@0.0.12': {}
|
||||
|
||||
'@kevisual/use-config@1.0.30(dotenv@17.3.1)':
|
||||
@@ -3089,6 +3248,8 @@ snapshots:
|
||||
|
||||
'@kevisual/ws@8.0.0': {}
|
||||
|
||||
'@kevisual/ws@8.19.0': {}
|
||||
|
||||
'@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3':
|
||||
optional: true
|
||||
|
||||
@@ -3731,6 +3892,10 @@ snapshots:
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.3
|
||||
|
||||
chokidar@5.0.0:
|
||||
dependencies:
|
||||
readdirp: 5.0.0
|
||||
|
||||
chownr@1.1.4:
|
||||
optional: true
|
||||
|
||||
@@ -3756,6 +3921,8 @@ snapshots:
|
||||
normalize-path: 3.0.0
|
||||
readable-stream: 4.5.2
|
||||
|
||||
cookie-es@1.2.2: {}
|
||||
|
||||
core-util-is@1.0.3: {}
|
||||
|
||||
crc-32@1.2.2: {}
|
||||
@@ -3777,6 +3944,10 @@ snapshots:
|
||||
shebang-command: 2.0.0
|
||||
which: 2.0.2
|
||||
|
||||
crossws@0.3.5:
|
||||
dependencies:
|
||||
uncrypto: 0.1.3
|
||||
|
||||
crypto-js@4.2.0: {}
|
||||
|
||||
culvert@0.1.2: {}
|
||||
@@ -3809,6 +3980,8 @@ snapshots:
|
||||
deep-extend@0.6.0:
|
||||
optional: true
|
||||
|
||||
defu@6.1.4: {}
|
||||
|
||||
degenerator@5.0.1:
|
||||
dependencies:
|
||||
ast-types: 0.13.4
|
||||
@@ -3819,6 +3992,8 @@ snapshots:
|
||||
|
||||
depd@2.0.0: {}
|
||||
|
||||
destr@2.0.5: {}
|
||||
|
||||
detect-libc@2.1.2:
|
||||
optional: true
|
||||
|
||||
@@ -4053,6 +4228,18 @@ snapshots:
|
||||
|
||||
graceful-fs@4.2.11: {}
|
||||
|
||||
h3@1.15.5:
|
||||
dependencies:
|
||||
cookie-es: 1.2.2
|
||||
crossws: 0.3.5
|
||||
defu: 6.1.4
|
||||
destr: 2.0.5
|
||||
iron-webcrypto: 1.2.1
|
||||
node-mock-http: 1.0.4
|
||||
radix3: 1.1.2
|
||||
ufo: 1.6.3
|
||||
uncrypto: 0.1.3
|
||||
|
||||
has-flag@4.0.0: {}
|
||||
|
||||
hasown@2.0.2:
|
||||
@@ -4124,6 +4311,8 @@ snapshots:
|
||||
jsbn: 1.1.0
|
||||
sprintf-js: 1.1.3
|
||||
|
||||
iron-webcrypto@1.2.1: {}
|
||||
|
||||
is-binary-path@2.1.0:
|
||||
dependencies:
|
||||
binary-extensions: 2.3.0
|
||||
@@ -4226,6 +4415,8 @@ snapshots:
|
||||
|
||||
lru-cache@10.4.3: {}
|
||||
|
||||
lru-cache@11.2.6: {}
|
||||
|
||||
lru-cache@6.0.0:
|
||||
dependencies:
|
||||
yallist: 4.0.0
|
||||
@@ -4305,13 +4496,23 @@ snapshots:
|
||||
|
||||
node-abort-controller@3.1.1: {}
|
||||
|
||||
node-fetch-native@1.6.7: {}
|
||||
|
||||
node-gyp-build-optional-packages@5.2.2:
|
||||
dependencies:
|
||||
detect-libc: 2.1.2
|
||||
optional: true
|
||||
|
||||
node-mock-http@1.0.4: {}
|
||||
|
||||
normalize-path@3.0.0: {}
|
||||
|
||||
ofetch@1.5.1:
|
||||
dependencies:
|
||||
destr: 2.0.5
|
||||
node-fetch-native: 1.6.7
|
||||
ufo: 1.6.3
|
||||
|
||||
on-finished@2.4.1:
|
||||
dependencies:
|
||||
ee-first: 1.1.1
|
||||
@@ -4537,6 +4738,8 @@ snapshots:
|
||||
|
||||
queue-tick@1.0.1: {}
|
||||
|
||||
radix3@1.1.2: {}
|
||||
|
||||
range-parser@1.2.1: {}
|
||||
|
||||
rc@1.2.8:
|
||||
@@ -4591,6 +4794,8 @@ snapshots:
|
||||
dependencies:
|
||||
picomatch: 4.0.2
|
||||
|
||||
readdirp@5.0.0: {}
|
||||
|
||||
redis-errors@1.2.0: {}
|
||||
|
||||
redis-parser@3.0.0:
|
||||
@@ -4813,12 +5018,29 @@ snapshots:
|
||||
json-stringify-safe: 5.0.1
|
||||
optional: true
|
||||
|
||||
ufo@1.6.3: {}
|
||||
|
||||
uncrypto@0.1.3: {}
|
||||
|
||||
undici-types@7.16.0: {}
|
||||
|
||||
undici-types@7.18.2: {}
|
||||
|
||||
universalify@2.0.1: {}
|
||||
|
||||
unstorage@1.17.4(ioredis@5.9.3):
|
||||
dependencies:
|
||||
anymatch: 3.1.3
|
||||
chokidar: 5.0.0
|
||||
destr: 2.0.5
|
||||
h3: 1.15.5
|
||||
lru-cache: 11.2.6
|
||||
node-fetch-native: 1.6.7
|
||||
ofetch: 1.5.1
|
||||
ufo: 1.6.3
|
||||
optionalDependencies:
|
||||
ioredis: 5.9.3
|
||||
|
||||
util-deprecate@1.0.2: {}
|
||||
|
||||
uuid@11.1.0: {}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { cryptPwd } from '../oauth/salt.ts';
|
||||
import { OauthUser } from '../oauth/oauth.ts';
|
||||
import { db } from '../../modules/db.ts';
|
||||
import { Org } from './org.ts';
|
||||
|
||||
import { UserSecret } from './user-secret.ts';
|
||||
import { cfUser, cfOrgs, cfUserSecrets } from '../../db/drizzle/schema.ts';
|
||||
import { eq, sql, InferSelectModel, InferInsertModel } from 'drizzle-orm';
|
||||
|
||||
@@ -17,6 +17,7 @@ export type UserData = {
|
||||
wxUnionId?: string;
|
||||
phone?: string;
|
||||
canChangeUsername?: boolean;
|
||||
cnbId?: string;
|
||||
};
|
||||
|
||||
export enum UserTypes {
|
||||
@@ -95,7 +96,6 @@ export class User {
|
||||
* @returns
|
||||
*/
|
||||
static async verifyToken(token: string) {
|
||||
const { UserSecret } = await import('./user-secret.ts');
|
||||
return await UserSecret.verifyToken(token);
|
||||
}
|
||||
/**
|
||||
@@ -108,7 +108,6 @@ export class User {
|
||||
return { accessToken: token.accessToken, refreshToken: token.refreshToken, token: token.accessToken };
|
||||
}
|
||||
static async getOauthUser(token: string) {
|
||||
const { UserSecret } = await import('./user-secret.ts');
|
||||
return await UserSecret.verifyToken(token);
|
||||
}
|
||||
/**
|
||||
@@ -126,7 +125,6 @@ export class User {
|
||||
* @returns
|
||||
*/
|
||||
static async getUserByToken(token: string) {
|
||||
const { UserSecret } = await import('./user-secret.ts');
|
||||
const oauthUser = await UserSecret.verifyToken(token);
|
||||
if (!oauthUser) {
|
||||
throw new CustomError('Token is invalid. get UserByToken');
|
||||
@@ -176,6 +174,20 @@ export class User {
|
||||
return users.length > 0 ? new User(users[0]) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 CNB ID 查找用户
|
||||
* @param cnbId
|
||||
* @returns
|
||||
*/
|
||||
static async findByCnbId(cnbId: string): Promise<User | null> {
|
||||
const users = await db
|
||||
.select()
|
||||
.from(usersTable)
|
||||
.where(sql`${usersTable.data}->>'cnbId' = ${cnbId}`)
|
||||
.limit(1);
|
||||
return users.length > 0 ? new User(users[0]) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据条件查找一个用户
|
||||
*/
|
||||
@@ -193,7 +205,7 @@ export class User {
|
||||
const users = await query.limit(1);
|
||||
return users.length > 0 ? new User(users[0]) : null;
|
||||
}
|
||||
static findByunionid(){
|
||||
static findByunionid() {
|
||||
|
||||
}
|
||||
|
||||
@@ -345,7 +357,7 @@ export class User {
|
||||
if (this.tokenUser && this.tokenUser.uid) {
|
||||
id = this.tokenUser.uid;
|
||||
} else {
|
||||
throw new CustomError(400, 'Permission denied');
|
||||
throw new CustomError('Permission denied', { code: 400 });
|
||||
}
|
||||
}
|
||||
const cache = await redis.get(`user:${id}:orgs`);
|
||||
|
||||
@@ -147,6 +147,7 @@ export class RedisTokenStore implements Store<OauthUser> {
|
||||
// 计算过期时间,根据opts.expire 和 opts.loginType
|
||||
// 如果expire存在,则使用expire,否则使用opts.loginType 进行计算;
|
||||
let expire = opts?.expire;
|
||||
const day = 24 * 60 * 60; // 一天的秒数
|
||||
if (!expire) {
|
||||
switch (opts.loginType) {
|
||||
case 'day':
|
||||
@@ -170,7 +171,8 @@ export class RedisTokenStore implements Store<OauthUser> {
|
||||
|
||||
await this.set(accessToken, JSON.stringify(value), expire);
|
||||
await this.set(userPrefix + ':token:' + accessToken, accessToken, expire);
|
||||
let refreshTokenExpiresIn = Math.min(expire * 7, 60 * 60 * 24 * 30, 60 * 60 * 24 * 365); // 最大为一年
|
||||
// refreshToken的过期时间比accessToken多2天,确保在accessToken过期后,refreshToken仍然有效
|
||||
let refreshTokenExpiresIn = expire + 2 * day;
|
||||
if (refreshToken) {
|
||||
// 小于7天, 则设置为7天
|
||||
if (refreshTokenExpiresIn < 60 * 60 * 24 * 7) {
|
||||
|
||||
@@ -18,14 +18,14 @@ export class ShareConfigService {
|
||||
shareCacheConfig = JSON.parse(shareCacheConfigString);
|
||||
} catch (e) {
|
||||
await redis.set(`config:share:${username}:${key}`, '', 'EX', 0); // 删除缓存
|
||||
throw new CustomError(400, 'config parse error');
|
||||
throw new CustomError(400, { message: 'config parse error' });
|
||||
}
|
||||
const owner = username;
|
||||
if (shareCacheConfig) {
|
||||
const permission = new UserPermission({ permission: (shareCacheConfig?.data as any)?.permission, owner });
|
||||
const result = permission.checkPermissionSuccess(options);
|
||||
if (!result.success) {
|
||||
throw new CustomError(403, 'no permission');
|
||||
throw new CustomError(403, { message: 'no permission' });
|
||||
}
|
||||
return shareCacheConfig;
|
||||
}
|
||||
@@ -35,7 +35,7 @@ export class ShareConfigService {
|
||||
.limit(1);
|
||||
const user = users[0];
|
||||
if (!user) {
|
||||
throw new CustomError(404, 'user not found');
|
||||
throw new CustomError(404, { message: 'user not found' });
|
||||
}
|
||||
const configs = await db.select()
|
||||
.from(schema.kvConfig)
|
||||
@@ -43,12 +43,12 @@ export class ShareConfigService {
|
||||
.limit(1);
|
||||
const config = configs[0];
|
||||
if (!config) {
|
||||
throw new CustomError(404, 'config not found');
|
||||
throw new CustomError(404, { message: 'config not found' });
|
||||
}
|
||||
const permission = new UserPermission({ permission: (config?.data as any)?.permission, owner });
|
||||
const result = permission.checkPermissionSuccess(options);
|
||||
if (!result.success) {
|
||||
throw new CustomError(403, 'no permission');
|
||||
throw new CustomError(403, { message: 'no permission' });
|
||||
}
|
||||
await redis.set(`config:share:${username}:${key}`, JSON.stringify(config), 'EX', 60 * 60 * 24 * 7); // 7天
|
||||
return config;
|
||||
|
||||
@@ -9,18 +9,18 @@ import { eq } from 'drizzle-orm';
|
||||
|
||||
export const checkUsername = (username: string) => {
|
||||
if (username.length > 30) {
|
||||
throw new CustomError(400, '用户名不能过长');
|
||||
throw new CustomError(400, { message: '用户名不能过长' });
|
||||
}
|
||||
if (!/^[a-zA-Z0-9_@]+$/.test(username)) {
|
||||
throw new CustomError(400, '用户名包含非法字符');
|
||||
throw new CustomError(400, { message: '用户名包含非法字符' });
|
||||
}
|
||||
if (username.includes(' ')) {
|
||||
throw new CustomError(400, '用户名不能包含空格');
|
||||
throw new CustomError(400, { message: '用户名不能包含空格' });
|
||||
}
|
||||
};
|
||||
export const checkUsernameShort = (username: string) => {
|
||||
if (username.length <= 3) {
|
||||
throw new CustomError(400, '用户名不能过短');
|
||||
throw new CustomError(400, { message: '用户名不能过短' });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -31,13 +31,13 @@ export const toChangeName = async (opts: { id: string; newName: string; admin?:
|
||||
}
|
||||
const user = await User.findByPk(id);
|
||||
if (!user) {
|
||||
ctx.throw(404, 'User not found');
|
||||
ctx.throw(404, { message: 'User not found' });
|
||||
}
|
||||
const oldName = user.username;
|
||||
checkUsername(newName);
|
||||
const findUserByUsername = await User.findOne({ username: newName });
|
||||
if (findUserByUsername) {
|
||||
ctx.throw(400, 'Username already exists');
|
||||
ctx.throw(400, { message: 'Username already exists' });
|
||||
}
|
||||
user.username = newName;
|
||||
const data = user.data || {};
|
||||
@@ -65,7 +65,7 @@ export const toChangeName = async (opts: { id: string; newName: string; admin?:
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('迁移文件数据失败', error);
|
||||
ctx.throw(500, 'Failed to change username');
|
||||
ctx.throw(500, { message: 'Failed to change username' });
|
||||
}
|
||||
return user;
|
||||
}
|
||||
@@ -79,13 +79,13 @@ app
|
||||
const { id, newName } = ctx.query.data || {};
|
||||
try {
|
||||
if (!id || !newName) {
|
||||
ctx.throw(400, '参数错误');
|
||||
ctx.throw(400, { message: '参数错误' });
|
||||
}
|
||||
const user = await toChangeName({ id, newName, admin: true, ctx });
|
||||
ctx.body = await user?.getInfo?.();
|
||||
} catch (error) {
|
||||
console.error('changeName error', error);
|
||||
ctx.throw(500, 'Failed to change username');
|
||||
ctx.throw(500, { message: 'Failed to change username' });
|
||||
}
|
||||
})
|
||||
.addTo(app);
|
||||
@@ -99,7 +99,7 @@ app
|
||||
.define(async (ctx) => {
|
||||
const { username } = ctx.query.data || {};
|
||||
if (!username) {
|
||||
ctx.throw(400, 'Username is required');
|
||||
ctx.throw(400, { message: 'Username is required' });
|
||||
}
|
||||
checkUsername(username);
|
||||
const user = await User.findOne({ username });
|
||||
@@ -121,7 +121,7 @@ app
|
||||
const { id, password } = ctx.query.data || {};
|
||||
const user = await User.findByPk(id);
|
||||
if (!user) {
|
||||
ctx.throw(404, 'User not found');
|
||||
ctx.throw(404, { message: 'User not found' });
|
||||
}
|
||||
let pwd = password || nanoid(6);
|
||||
user.createPassword(pwd);
|
||||
@@ -149,7 +149,7 @@ app
|
||||
checkUsername(username);
|
||||
const findUserByUsername = await User.findOne({ username });
|
||||
if (findUserByUsername) {
|
||||
ctx.throw(400, 'Username already exists');
|
||||
ctx.throw(400, { message: 'Username already exists' });
|
||||
}
|
||||
let pwd = password || nanoid(6);
|
||||
const user = await User.createUser(username, pwd, description);
|
||||
@@ -172,7 +172,7 @@ app
|
||||
const { id } = ctx.query.data || {};
|
||||
const user = await User.findByPk(id);
|
||||
if (!user) {
|
||||
ctx.throw(404, 'User not found');
|
||||
ctx.throw(404, { message: 'User not found' });
|
||||
}
|
||||
await db.delete(schema.cfUser).where(eq(schema.cfUser.id, user.id));
|
||||
backupUserA(user.username, user.id);
|
||||
|
||||
30
src/routes/user/cnb-login.ts
Normal file
30
src/routes/user/cnb-login.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { app, redis } from "@/app.ts";
|
||||
import z from "zod";
|
||||
import { CnbServices } from "./modules/cnb-services.ts";
|
||||
import { createCookie } from "./me.ts";
|
||||
app
|
||||
.route({
|
||||
path: 'user',
|
||||
key: 'cnb-login',
|
||||
description: 'cnb登陆, 根据 CNB_TOKEN 获取用户信息',
|
||||
metadata: {
|
||||
args: {
|
||||
data: z.object({
|
||||
cnbToken: z.string().describe('cnb token'),
|
||||
}),
|
||||
}
|
||||
}
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const { cnbToken } = ctx.query.data || {};
|
||||
if (!cnbToken) {
|
||||
ctx.throw(400, 'CNB Token is required');
|
||||
}
|
||||
const cnb = new CnbServices(cnbToken);
|
||||
const token = await cnb.login();
|
||||
if (!token) {
|
||||
ctx.throw(500, '登陆失败');
|
||||
}
|
||||
createCookie(token, ctx);
|
||||
ctx.body = token;
|
||||
}).addTo(app);
|
||||
@@ -16,3 +16,5 @@ import './admin/user.ts';
|
||||
import './secret-key/list.ts';
|
||||
|
||||
import './wx-login.ts'
|
||||
|
||||
import './cnb-login.ts';
|
||||
@@ -35,15 +35,15 @@ app
|
||||
const tokenUser = ctx.state.tokenUser;
|
||||
const { id, username, password, description } = ctx.query.data || {};
|
||||
if (!id) {
|
||||
throw new CustomError(400, 'id is required');
|
||||
throw new CustomError(400, { message: 'id is required' });
|
||||
}
|
||||
const user = await User.findByPk(id);
|
||||
if (user.id !== tokenUser.id) {
|
||||
throw new CustomError(403, 'Permission denied');
|
||||
throw new CustomError(403, { message: 'Permission denied' });
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
throw new CustomError(500, 'user not found');
|
||||
throw new CustomError(500, { message: 'user not found' });
|
||||
}
|
||||
if (username) {
|
||||
user.username = username;
|
||||
@@ -73,12 +73,12 @@ app
|
||||
.define(async (ctx) => {
|
||||
const { username, password, description } = ctx.query.data || {};
|
||||
if (!username) {
|
||||
throw new CustomError(400, 'username is required');
|
||||
throw new CustomError(400, { message: 'username is required' });
|
||||
}
|
||||
checkUsername(username);
|
||||
const findUserByUsername = await User.findOne({ username });
|
||||
if (findUserByUsername) {
|
||||
throw new CustomError(400, 'username already exists');
|
||||
throw new CustomError(400, { message: 'username already exists' });
|
||||
}
|
||||
const pwd = password || nanoid(6);
|
||||
const user = await User.createUser(username, pwd, description);
|
||||
|
||||
@@ -1,19 +1,24 @@
|
||||
import { app } from '@/app.ts';
|
||||
import { Org } from '@/models/org.ts';
|
||||
import { User } from '@/models/user.ts';
|
||||
import { proxyDomain as domain } from '@/modules/domain.ts';
|
||||
import { logger } from '@/modules/logger.ts';
|
||||
import z from 'zod';
|
||||
/**
|
||||
* 当配置了domain后,创建cookie,当get请求地址的时候,会自动带上cookie
|
||||
* @param token
|
||||
* @param ctx
|
||||
* @returns
|
||||
*/
|
||||
export const createCookie = (token: any, ctx: any) => {
|
||||
export const createCookie = (token: { accessToken?: string; token?: string }, ctx: any) => {
|
||||
if (!domain) {
|
||||
return;
|
||||
}
|
||||
if (!ctx?.req) {
|
||||
logger.debug('登陆用户没有请求对象,不需要创建cookie');
|
||||
return
|
||||
}
|
||||
//TODO, 获取访问的 hostname, 如果访问的和 domain 的不一致,也创建cookie
|
||||
const browser = ctx.req.headers['user-agent'];
|
||||
const browser = ctx?.req?.headers['user-agent'];
|
||||
const isBrowser = browser.includes('Mozilla'); // 浏览器
|
||||
if (isBrowser && ctx.res.cookie) {
|
||||
ctx.res.cookie('token', token.accessToken || token?.token, {
|
||||
@@ -351,6 +356,14 @@ app
|
||||
.route({
|
||||
path: 'user',
|
||||
key: 'refreshToken',
|
||||
description: '根据refreshToken刷新token',
|
||||
metadata: {
|
||||
args: {
|
||||
data: z.object({
|
||||
refreshToken: z.string().describe('刷新token'),
|
||||
}),
|
||||
}
|
||||
}
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const { refreshToken } = ctx.query.data || {};
|
||||
|
||||
37
src/routes/user/modules/cnb-services.ts
Normal file
37
src/routes/user/modules/cnb-services.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { CNB } from '@kevisual/cnb'
|
||||
import { UserModel } from '../../../auth/index.ts';
|
||||
import { CustomError } from '@kevisual/router';
|
||||
|
||||
export class CnbServices {
|
||||
cnb: CNB;
|
||||
constructor(token?: string) {
|
||||
this.cnb = new CNB({
|
||||
token,
|
||||
});
|
||||
}
|
||||
|
||||
async login(): Promise<ReturnType<typeof UserModel.prototype.createToken>> {
|
||||
const cnbUserRes = await this.cnb.user.getUser();
|
||||
if (cnbUserRes.code !== 200) {
|
||||
throw new CustomError('CNB Token is invalid');
|
||||
}
|
||||
const cnbUser = cnbUserRes?.data;
|
||||
const cnbUserId = cnbUser?.id
|
||||
if (!cnbUserId) {
|
||||
throw new CustomError('CNB User ID is missing');
|
||||
}
|
||||
let user = await UserModel.findByCnbId(cnbUserId);
|
||||
if (!user) {
|
||||
const username = '@cnb-' + cnbUser.username;
|
||||
// 如果用户不存在,创建一个新用户
|
||||
user = await UserModel.createUser(username, cnbUserId);
|
||||
user.data = {
|
||||
...user.data,
|
||||
cnbId: cnbUserId,
|
||||
}
|
||||
await user.save();
|
||||
}
|
||||
const token = await user.createToken();
|
||||
return token;
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,7 @@ export class WxServices {
|
||||
const token = await fetchToken(code, type);
|
||||
console.log('login token', token);
|
||||
if (!token.unionid) {
|
||||
throw new CustomError(400, 'code is invalid, wxdata can not be found');
|
||||
throw new CustomError(400, { message: 'code is invalid, wxdata can not be found' });
|
||||
}
|
||||
this.wxToken = token;
|
||||
const unionid = token.unionid;
|
||||
@@ -180,7 +180,7 @@ export class WxServices {
|
||||
async getUserInfo() {
|
||||
try {
|
||||
if (!this.wxToken) {
|
||||
throw new CustomError(400, 'wxToken is not set');
|
||||
throw new CustomError(400, { message: 'wxToken is not set' });
|
||||
}
|
||||
const openid = this.wxToken.openid;
|
||||
const access_token = this.wxToken.access_token;
|
||||
|
||||
@@ -45,7 +45,7 @@ export const fetchToken = async (code: string, type: 'open' | 'mp' = 'open'): Pr
|
||||
appSecret = wx.appSecret;
|
||||
}
|
||||
if (!appId || !appSecret) {
|
||||
throw new CustomError(500, 'appId or appSecret is not set');
|
||||
throw new CustomError(500, { message: 'appId or appSecret is not set' });
|
||||
}
|
||||
console.log('fetchToken===', appId, appSecret, code);
|
||||
const wxUrl = `https://api.weixin.qq.com/sns/oauth2/access_token?appid=${appId}&secret=${appSecret}&code=${code}&grant_type=authorization_code`;
|
||||
|
||||
@@ -6,12 +6,21 @@ import jsonwebtoken from 'jsonwebtoken';
|
||||
|
||||
import { redis } from '@/app.ts';
|
||||
import { createCookie, clearCookie } from './me.ts';
|
||||
import z from 'zod';
|
||||
|
||||
app
|
||||
.route({
|
||||
path: 'user',
|
||||
key: 'webLogin',
|
||||
description: 'web登录接口,配合插件使用',
|
||||
middleware: [authCan],
|
||||
metadata: {
|
||||
args: {
|
||||
loginToken: z.string().describe('web登录令牌,服务端生成,客户端保持一致'),
|
||||
sign: z.string().describe('签名,服务端生成,客户端保持一致'),
|
||||
randomId: z.string().describe('随机字符串,服务端和客户端保持一致'),
|
||||
}
|
||||
}
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const tokenUser = ctx.state.tokenUser;
|
||||
@@ -97,6 +106,7 @@ app
|
||||
.route({
|
||||
path: 'user',
|
||||
key: 'checkLoginStatus',
|
||||
description: '循环检查登陆状态',
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const { loginToken } = ctx.query;
|
||||
|
||||
22
src/test/cnb-login.ts
Normal file
22
src/test/cnb-login.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { app, showMore, cnbToken } from './common.ts';
|
||||
|
||||
const res = await app.run({
|
||||
path: 'user',
|
||||
key: 'cnb-login',
|
||||
payload: {
|
||||
data: {
|
||||
cnbToken
|
||||
}
|
||||
}
|
||||
})
|
||||
console.log(showMore(res));
|
||||
|
||||
const token = res.data.token;
|
||||
const me = await app.run({
|
||||
path: 'user',
|
||||
key: 'me',
|
||||
payload: {
|
||||
token
|
||||
}
|
||||
})
|
||||
console.log(showMore(me));
|
||||
@@ -13,6 +13,7 @@ export {
|
||||
export const config = useConfig();
|
||||
|
||||
export const token = config.KEVISUAL_TOKEN || '';
|
||||
export const cnbToken = config.CNB_TOKEN || '';
|
||||
|
||||
export const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
export const showRes = (res, ...args) => {
|
||||
|
||||
Reference in New Issue
Block a user