diff --git a/package.json b/package.json index 4c0665d..3c5c978 100644 --- a/package.json +++ b/package.json @@ -32,15 +32,17 @@ "license": "UNLICENSED", "dependencies": { "@kevisual/auth": "1.0.5", - "@kevisual/local-app-manager": "0.1.8", + "@kevisual/local-app-manager": "0.1.9", "@kevisual/router": "^0.0.6", "@kevisual/use-config": "^1.0.7", "@types/semver": "^7.5.8", "archiver": "^7.0.1", + "crypto-js": "^4.2.0", "dayjs": "^1.11.13", "formidable": "^3.5.2", "ioredis": "^5.5.0", "json5": "^2.2.3", + "jsonwebtoken": "^9.0.2", "lodash-es": "^4.17.21", "minio": "^8.0.4", "nanoid": "^5.1.2", @@ -67,6 +69,7 @@ "@types/archiver": "^6.0.3", "@types/crypto-js": "^4.2.2", "@types/formidable": "^3.4.5", + "@types/jsonwebtoken": "^9.0.9", "@types/lodash-es": "^4.17.12", "@types/node": "^22.13.5", "@types/react": "^19.0.10", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a764685..e9fc5d6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,8 +17,8 @@ importers: specifier: 1.0.5 version: 1.0.5 '@kevisual/local-app-manager': - specifier: 0.1.8 - version: 0.1.8(@kevisual/router@0.0.6)(@kevisual/types@0.0.6)(@kevisual/use-config@1.0.7)(pm2@5.4.3) + specifier: 0.1.9 + version: 0.1.9(@kevisual/router@0.0.6)(@kevisual/types@0.0.6)(@kevisual/use-config@1.0.7)(pm2@5.4.3) '@kevisual/router': specifier: ^0.0.6 version: 0.0.6 @@ -31,6 +31,9 @@ importers: archiver: specifier: ^7.0.1 version: 7.0.1 + crypto-js: + specifier: ^4.2.0 + version: 4.2.0 dayjs: specifier: ^1.11.13 version: 1.11.13 @@ -43,6 +46,9 @@ importers: json5: specifier: ^2.2.3 version: 2.2.3 + jsonwebtoken: + specifier: ^9.0.2 + version: 9.0.2 lodash-es: specifier: ^4.17.21 version: 4.17.21 @@ -116,6 +122,9 @@ importers: '@types/formidable': specifier: ^3.4.5 version: 3.4.5 + '@types/jsonwebtoken': + specifier: ^9.0.9 + version: 9.0.9 '@types/lodash-es': specifier: ^4.17.12 version: 4.17.12 @@ -339,8 +348,8 @@ packages: '@kevisual/auth@1.0.5': resolution: {integrity: sha512-GwsLj7unKXi7lmMiIIgdig4LwwLiDJnOy15HHZR5gMbyK6s5/uJiMY5RXPB2+onGzTNDqFo/hXjsD2wkerHPVg==} - '@kevisual/local-app-manager@0.1.8': - resolution: {integrity: sha512-3dRstV+g1zZox7NvkXPpQHmil0rU89bXM15msmbmWUB450FIh1WaaQ7pWT6cQKhTuPO+duvSSAXtbbL7A1TqvQ==} + '@kevisual/local-app-manager@0.1.9': + resolution: {integrity: sha512-oppYUjvpPg/7gBw1h3dX63fhxB8MvGc74yGgIM4JifvSuOYLcZ9oZyauYV+5lQVh2tjJImOuUmRHnVXu1FWKDQ==} peerDependencies: '@kevisual/router': ^0.0.6-alpha-2 '@kevisual/types': ^0.0.1 @@ -599,6 +608,9 @@ packages: '@types/glob@7.2.0': resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} + '@types/jsonwebtoken@9.0.9': + resolution: {integrity: sha512-uoe+GxEuHbvy12OUQct2X9JenKM3qAscquYymuQN4fMWG9DBQtykrQEFcAbVACF7qaLw9BePSodUL0kquqBJpQ==} + '@types/lodash-es@4.17.12': resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==} @@ -776,6 +788,9 @@ packages: resolution: {integrity: sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==} engines: {node: '>=8.0.0'} + buffer-equal-constant-time@1.0.1: + resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} @@ -877,6 +892,9 @@ packages: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} + crypto-js@4.2.0: + resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} + csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} @@ -983,6 +1001,9 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + ecdsa-sig-formatter@1.0.11: + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -1491,6 +1512,16 @@ packages: jsonfile@6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + jsonwebtoken@9.0.2: + resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==} + engines: {node: '>=12', npm: '>=6'} + + jwa@1.4.1: + resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==} + + jws@3.2.2: + resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==} + lazy@1.0.11: resolution: {integrity: sha512-Y+CjUfLmIpoUCCRl0ub4smrYtGGr5AOa2AKOaWelGHOGz33X/Y/KizefGqbkwfz44+cnq/+9habclf8vOmu2LA==} engines: {node: '>=0.2.0'} @@ -1505,9 +1536,30 @@ packages: lodash.defaults@4.2.0: resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} + lodash.includes@4.3.0: + resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} + lodash.isarguments@3.1.0: resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==} + lodash.isboolean@3.0.3: + resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} + + lodash.isinteger@4.0.4: + resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} + + lodash.isnumber@3.0.3: + resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} + + lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + + lodash.isstring@4.0.1: + resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} + + lodash.once@4.1.1: + resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} + lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} @@ -2510,7 +2562,7 @@ snapshots: '@kevisual/auth@1.0.5': {} - '@kevisual/local-app-manager@0.1.8(@kevisual/router@0.0.6)(@kevisual/types@0.0.6)(@kevisual/use-config@1.0.7)(pm2@5.4.3)': + '@kevisual/local-app-manager@0.1.9(@kevisual/router@0.0.6)(@kevisual/types@0.0.6)(@kevisual/use-config@1.0.7)(pm2@5.4.3)': dependencies: '@kevisual/router': 0.0.6 '@kevisual/types': 0.0.6 @@ -2753,6 +2805,11 @@ snapshots: '@types/minimatch': 5.1.2 '@types/node': 22.13.5 + '@types/jsonwebtoken@9.0.9': + dependencies: + '@types/ms': 0.7.34 + '@types/node': 22.13.5 + '@types/lodash-es@4.17.12': dependencies: '@types/lodash': 4.17.7 @@ -2932,6 +2989,8 @@ snapshots: buffer-crc32@1.0.0: {} + buffer-equal-constant-time@1.0.1: {} + buffer-from@1.1.2: {} buffer@6.0.3: @@ -3045,6 +3104,8 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + crypto-js@4.2.0: {} + csstype@3.1.3: {} culvert@0.1.2: {} @@ -3153,6 +3214,10 @@ snapshots: eastasianwidth@0.2.0: {} + ecdsa-sig-formatter@1.0.11: + dependencies: + safe-buffer: 5.2.1 + emoji-regex@8.0.0: {} emoji-regex@9.2.2: {} @@ -3750,6 +3815,30 @@ snapshots: optionalDependencies: graceful-fs: 4.2.11 + jsonwebtoken@9.0.2: + dependencies: + jws: 3.2.2 + lodash.includes: 4.3.0 + lodash.isboolean: 3.0.3 + lodash.isinteger: 4.0.4 + lodash.isnumber: 3.0.3 + lodash.isplainobject: 4.0.6 + lodash.isstring: 4.0.1 + lodash.once: 4.1.1 + ms: 2.1.3 + semver: 7.7.1 + + jwa@1.4.1: + dependencies: + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: 5.2.1 + + jws@3.2.2: + dependencies: + jwa: 1.4.1 + safe-buffer: 5.2.1 + lazy@1.0.11: {} lazystream@1.0.1: @@ -3760,8 +3849,22 @@ snapshots: lodash.defaults@4.2.0: {} + lodash.includes@4.3.0: {} + lodash.isarguments@3.1.0: {} + lodash.isboolean@3.0.3: {} + + lodash.isinteger@4.0.4: {} + + lodash.isnumber@3.0.3: {} + + lodash.isplainobject@4.0.6: {} + + lodash.isstring@4.0.1: {} + + lodash.once@4.1.1: {} + lodash@4.17.21: {} lru-cache@10.4.3: {} diff --git a/src/routes/user/index.ts b/src/routes/user/index.ts index 2657df8..33618e9 100644 --- a/src/routes/user/index.ts +++ b/src/routes/user/index.ts @@ -5,4 +5,6 @@ import './me.ts'; import './update.ts' -import './init.ts' \ No newline at end of file +import './init.ts' + +import './web-login.ts' \ No newline at end of file diff --git a/src/routes/user/me.ts b/src/routes/user/me.ts index 1bf7a25..84bc48b 100644 --- a/src/routes/user/me.ts +++ b/src/routes/user/me.ts @@ -2,7 +2,14 @@ import { app } from '@/app.ts'; import { Org } from '@/models/org.ts'; import { User } from '@/models/user.ts'; import { domain } from '@/modules/domain.ts'; - +const createCookie = (token: any, ctx: any) => { + ctx.res.cookie('token', token.token, { + maxAge: token.expireTime, + domain, + sameSite: 'lax', + httpOnly: true, + }); +}; app .route({ path: 'user', @@ -46,12 +53,7 @@ app ctx.throw(500, 'Password error'); } const token = await user.createToken(null, loginType); - ctx.res.cookie('token', token.token, { - maxAge: token.expireTime, - domain, - sameSite: 'lax', - httpOnly: true, - }); + createCookie(token, ctx); ctx.body = token; }) .addTo(app); @@ -135,10 +137,12 @@ app } if (user.type === 'user') { const token = await user.createToken(null, loginType); + createCookie(token, ctx); ctx.body = token; return; } else if (user.type === 'org' && tokenUser.uid) { const token = await user.createToken(tokenUser.uid, loginType); + createCookie(token, ctx); ctx.body = token; return; } @@ -155,6 +159,7 @@ app } if (type === 'user') { const token = await me.createToken(null, loginType); + createCookie(token, ctx); ctx.body = token; return; } @@ -165,6 +170,7 @@ app if (orgUser.type === 'user') { // 想转换的type===org, 但实际上这个用户名是一个用户, 比如org调用switchOrg root const token = await orgUser.createToken(null, loginType); + createCookie(token, ctx); ctx.body = token; return; } @@ -175,6 +181,7 @@ app ctx.throw('Permission denied'); } const token = await orgUser.createToken(me.id, loginType); + createCookie(token, ctx); ctx.body = token; }) .addTo(app); diff --git a/src/routes/user/web-login.ts b/src/routes/user/web-login.ts new file mode 100644 index 0000000..aa8bb84 --- /dev/null +++ b/src/routes/user/web-login.ts @@ -0,0 +1,83 @@ +import { app } from '@/app.ts'; +import { User } from '@/models/user.ts'; +import { log } from 'console'; +import MD5 from 'crypto-js/md5.js'; + +import jsonwebtoken from 'jsonwebtoken'; + +const tokenData: Record = {}; +app + .route({ + path: 'user', + key: 'webLogin', + middleware: ['auth'], + }) + .define(async (ctx) => { + const tokenUser = ctx.state.tokenUser; + const { loginToken, sign, randomId } = ctx.query || {}; + if (!loginToken) { + ctx.throw(400, 'loginToken is required'); + } + if (!sign) { + ctx.throw(400, 'sign is required'); + } + + if (!randomId) { + ctx.throw(400, 'randomId is required'); + } + const tokenSecret = 'xiao' + randomId; + let payload: any = {}; + try { + payload = jsonwebtoken.verify(loginToken, tokenSecret); + } catch (e) { + ctx.throw(400, 'loginToken error'); + } + const { timestamp } = payload; + + const checkSign = MD5(`${tokenSecret}${timestamp}`).toString(); + if (sign !== checkSign) { + ctx.throw(400, 'sign error'); + } + const user = await User.findByPk(tokenUser.id); + console.log('tokenUser', tokenUser) + if (!user) { + ctx.throw(400, 'user not found'); + } + const token = await user.createToken(null, 'plugin'); + const data = jsonwebtoken.sign( + { + userToken: token, + user: { + username: user.username, + id: user.id, + }, + }, + tokenSecret, + { + expiresIn: '1h', + }, + ); + + // ctx.body = data; + tokenData[loginToken] = data; + }) + .addTo(app); + +app + .route({ + path: 'user', + key: 'checkLoginStatus', + }) + .define(async (ctx) => { + const { loginToken } = ctx.query; + if (!loginToken) { + ctx.throw(400, 'loginToken is required'); + } + const data = tokenData[loginToken]; + if (data) { + ctx.body = data; + } else { + ctx.throw(400, 'Checked Failed'); + } + }) + .addTo(app); diff --git a/src/scripts/user.ts b/src/scripts/user.ts new file mode 100644 index 0000000..9ad002a --- /dev/null +++ b/src/scripts/user.ts @@ -0,0 +1,12 @@ +import { User } from '../models/user.ts'; + +const uid = '9b7b521d-082e-4a39-8410-9569c07e3e72'; + +User.findByPk(uid).then((user) => { + console.log(user); +}); +User.findAll().then((users) => { + for (let user of users) { + console.log(user.id, user.username); + } +});