temp
This commit is contained in:
@@ -8,7 +8,14 @@ import { OauthUser } from '../oauth/oauth.ts';
|
||||
export const redis = useContextKey<Redis>('redis');
|
||||
|
||||
const UserSecretStatus = ['active', 'inactive', 'expired'] as const;
|
||||
|
||||
const randomString = (length: number) => {
|
||||
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||
let result = '';
|
||||
for (let i = 0; i < length; i++) {
|
||||
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
||||
}
|
||||
return result;
|
||||
};
|
||||
type Data = {
|
||||
[key: string]: any;
|
||||
/**
|
||||
@@ -45,10 +52,12 @@ export class UserSecret extends Model {
|
||||
if (!oauth.isSecretKey(token)) {
|
||||
return await oauth.verifyToken(token);
|
||||
}
|
||||
// const secretToken = await oauth.verifyToken(token);
|
||||
// if (secretToken) {
|
||||
// return secretToken;
|
||||
// }
|
||||
const secretToken = await oauth.verifyToken(token);
|
||||
if (secretToken) {
|
||||
console.log('verifyToken: verified as normal token');
|
||||
return secretToken;
|
||||
}
|
||||
console.log('verifyToken: try to verify as secret key');
|
||||
const userSecret = await UserSecret.findOne({
|
||||
where: { token },
|
||||
});
|
||||
@@ -66,7 +75,7 @@ export class UserSecret extends Model {
|
||||
if (!oauthUser) {
|
||||
return null; // 如果没有找到对应的oauth用户,则返回null
|
||||
}
|
||||
// await oauth.saveSecretKey(oauthUser, userSecret.token);
|
||||
await oauth.saveSecretKey(oauthUser, userSecret.token);
|
||||
// 存储到oauth中的token store中
|
||||
return oauthUser;
|
||||
}
|
||||
@@ -74,10 +83,10 @@ export class UserSecret extends Model {
|
||||
* owner 组织用户的 oauthUser
|
||||
* @returns
|
||||
*/
|
||||
async getOauthUser() {
|
||||
async getOauthUser(opts?: { wx?: boolean }) {
|
||||
const user = await User.findOne({
|
||||
where: { id: this.userId },
|
||||
attributes: ['id', 'username', 'type', 'owner'],
|
||||
attributes: ['id', 'username', 'type', 'owner', 'data'],
|
||||
});
|
||||
let org: User = null;
|
||||
if (!user) {
|
||||
@@ -117,6 +126,44 @@ export class UserSecret extends Model {
|
||||
const expiredTime = new Date(this.expiredTime);
|
||||
return now > expiredTime.getTime(); // 如果当前时间大于过期时间,则认为已过期
|
||||
}
|
||||
/**
|
||||
* 检查是否过期,如果过期则更新状态为expired
|
||||
*
|
||||
* @returns
|
||||
*/
|
||||
async checkOnUse() {
|
||||
if (!this.expiredTime) {
|
||||
return {
|
||||
code: 200
|
||||
}
|
||||
}
|
||||
try {
|
||||
|
||||
const now = Date.now();
|
||||
const expiredTime = new Date(this.expiredTime);
|
||||
const isExpired = now > expiredTime.getTime(); // 如果当前时间大于过期时间,则认为已过期
|
||||
if (isExpired) {
|
||||
this.status = 'active';
|
||||
const expireTime = UserSecret.getExpiredTime();
|
||||
this.expiredTime = expireTime;
|
||||
await this.save()
|
||||
}
|
||||
if (this.status !== 'active') {
|
||||
this.status = 'active';
|
||||
await this.save()
|
||||
}
|
||||
return {
|
||||
code: 200
|
||||
};
|
||||
}
|
||||
catch (e) {
|
||||
console.error('checkExpiredAndUpdate error', this.id, this.title);
|
||||
return {
|
||||
code: 500,
|
||||
message: 'checkExpiredAndUpdate error'
|
||||
}
|
||||
}
|
||||
}
|
||||
async createNewToken() {
|
||||
if (this.token) {
|
||||
await oauth.delToken(this.token);
|
||||
@@ -134,8 +181,21 @@ export class UserSecret extends Model {
|
||||
}
|
||||
return token;
|
||||
}
|
||||
static async createSecret(tokenUser: { id: string; uid?: string }, expireDay = 365) {
|
||||
const expireTime = expireDay * 24 * 60 * 60 * 1000; // 转换为毫秒
|
||||
/**
|
||||
* 根据 unionid 生成redis的key
|
||||
* `wxmp:unionid:token:${unionid}`
|
||||
* @param unionid
|
||||
* @returns
|
||||
*/
|
||||
static wxRedisKey(unionid: string) {
|
||||
return `wxmp:unionid:token:${unionid}`;
|
||||
}
|
||||
static getExpiredTime(expireDays?: number) {
|
||||
const defaultExpireDays = expireDays || 365;
|
||||
const expireTime = defaultExpireDays * 24 * 60 * 60 * 1000;
|
||||
return new Date(Date.now() + expireTime)
|
||||
}
|
||||
static async createSecret(tokenUser: { id: string; uid?: string, title?: string }, expireDays = 365) {
|
||||
const token = await UserSecret.createToken();
|
||||
let userId = tokenUser.id;
|
||||
let orgId: string = null;
|
||||
@@ -147,11 +207,13 @@ export class UserSecret extends Model {
|
||||
userId,
|
||||
orgId,
|
||||
token,
|
||||
expiredTime: new Date(Date.now() + expireTime),
|
||||
title: tokenUser.title || randomString(6),
|
||||
expiredTime: UserSecret.getExpiredTime(expireDays),
|
||||
});
|
||||
|
||||
return userSecret;
|
||||
}
|
||||
|
||||
async getPermission(opts: { id: string; uid?: string }) {
|
||||
const { id, uid } = opts;
|
||||
let userId: string = id;
|
||||
|
||||
@@ -2,6 +2,7 @@ import { User, UserInit, UserServices } from '../auth/models/index.ts';
|
||||
import { UserSecretInit, UserSecret } from '../auth/models/index.ts';
|
||||
import { OrgInit } from '../auth/models/index.ts';
|
||||
export { User, UserInit, UserServices, UserSecret };
|
||||
import { useContextKey } from '@kevisual/context';
|
||||
const init = async () => {
|
||||
await OrgInit(null, null, {
|
||||
alter: true,
|
||||
@@ -21,5 +22,7 @@ const init = async () => {
|
||||
}).catch((e) => {
|
||||
console.error('UserSecret sync', e);
|
||||
});
|
||||
console.log('Models synced');
|
||||
useContextKey('models-synced', true);
|
||||
};
|
||||
init();
|
||||
|
||||
@@ -68,16 +68,7 @@ export class WxServices {
|
||||
},
|
||||
});
|
||||
// @ts-ignore
|
||||
if (type === 'open' && user && user.data.wxOpenid !== token.openid) {
|
||||
user.data = {
|
||||
...user.data,
|
||||
// @ts-ignore
|
||||
wxOpenid: token.openid,
|
||||
};
|
||||
user = await user.update({ data: user.data });
|
||||
console.log('mp-user login openid update=============', token.openid, token.unionid);
|
||||
// @ts-ignore
|
||||
} else if (type === 'mp' && user && user.data.wxmpOpenid !== token.openid) {
|
||||
if (type === 'mp' && user && user.data.wxmpOpenid !== token.openid) {
|
||||
user.data = {
|
||||
...user.data,
|
||||
// @ts-ignore
|
||||
@@ -94,7 +85,7 @@ export class WxServices {
|
||||
canChangeUsername: true,
|
||||
};
|
||||
user.data = data;
|
||||
if ((type = 'mp')) {
|
||||
if (type === 'mp') {
|
||||
// @ts-ignore
|
||||
data.wxmpOpenid = token.openid;
|
||||
} else {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Op } from 'sequelize';
|
||||
import { User, UserSecret } from '@/models/user.ts';
|
||||
import { app } from '@/app.ts';
|
||||
|
||||
import { redis } from '@/app.ts';
|
||||
app
|
||||
.route({
|
||||
path: 'secret',
|
||||
@@ -10,7 +10,7 @@ app
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const tokenUser = ctx.state.tokenUser;
|
||||
const { page = 1, pageSize = 100, search, sort = 'DESC', orgId } = ctx.query;
|
||||
const { page = 1, pageSize = 100, search, sort = 'DESC', orgId, showToken = false } = ctx.query;
|
||||
const searchWhere: Record<string, any> = search
|
||||
? {
|
||||
[Op.or]: [{ title: { [Op.like]: `%${search}%` } }, { description: { [Op.like]: `%${search}%` } }],
|
||||
@@ -18,7 +18,10 @@ app
|
||||
: {};
|
||||
if (orgId) {
|
||||
searchWhere.orgId = orgId;
|
||||
} else {
|
||||
searchWhere.orgId = null;
|
||||
}
|
||||
const excludeFields = showToken ? [] : ['token'];
|
||||
const { rows: secrets, count } = await UserSecret.findAndCountAll({
|
||||
where: {
|
||||
userId: tokenUser.userId,
|
||||
@@ -27,7 +30,7 @@ app
|
||||
offset: (page - 1) * pageSize,
|
||||
limit: pageSize,
|
||||
attributes: {
|
||||
exclude: ['token'], // Exclude sensitive token field
|
||||
exclude: excludeFields, // Exclude sensitive token field
|
||||
},
|
||||
order: [['updatedAt', sort]],
|
||||
});
|
||||
@@ -166,3 +169,52 @@ app
|
||||
ctx.body = secret;
|
||||
})
|
||||
.addTo(app);
|
||||
|
||||
app.route({
|
||||
path: 'secret',
|
||||
key: 'wxnotify',
|
||||
description: '为了微信去缓存需要的数据, unionid是公众号下的用户的unionid',
|
||||
}).define(async (ctx) => {
|
||||
const { openid, unionid } = ctx.query;
|
||||
if (!openid && !unionid) {
|
||||
// ctx.throw(400, '需要提供 openid 或者 unionid 参数');
|
||||
ctx.throw(400, '需要提供 unionid 参数');
|
||||
}
|
||||
// 最少20为的openid
|
||||
if (unionid.length < 20) {
|
||||
ctx.throw(400, 'unionid 是必填的');
|
||||
}
|
||||
const redisKey = UserSecret.wxRedisKey(unionid);
|
||||
const token = await redis.get(redisKey);
|
||||
if (token) {
|
||||
ctx.body = 'success'
|
||||
return;
|
||||
}
|
||||
const user = await User.findOne({
|
||||
where: {
|
||||
data: {
|
||||
wxUnionId: unionid
|
||||
}
|
||||
}
|
||||
})
|
||||
if (!user) {
|
||||
ctx.throw(404, '请关注公众号《人生可视化助手》后再操作');
|
||||
return
|
||||
}
|
||||
let secretKey = await UserSecret.findOne({
|
||||
where: {
|
||||
userId: user.id,
|
||||
title: 'wxmp-notify-token'
|
||||
}
|
||||
});
|
||||
if (!secretKey) {
|
||||
secretKey = await UserSecret.createSecret({ id: user.id, title: 'wxmp-notify-token' });
|
||||
}
|
||||
const check = await secretKey.checkOnUse();
|
||||
if (check.code !== 200) {
|
||||
ctx.throw(check.code, check.message);
|
||||
}
|
||||
await redis.set(redisKey, secretKey.token, 'EX', 30 * 24 * 60 * 60); // 30天过期
|
||||
ctx.body = 'success'
|
||||
|
||||
}).addTo(app);
|
||||
33
src/test/common.ts
Normal file
33
src/test/common.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { app } from '@/app.ts';
|
||||
import '@/route.ts';
|
||||
import { useConfig, useContextKey } from '@kevisual/context';
|
||||
import { Query } from '@kevisual/query';
|
||||
import util from 'node:util';
|
||||
export {
|
||||
app,
|
||||
useContextKey
|
||||
}
|
||||
export const config = useConfig();
|
||||
|
||||
export const token = config.TOKEN || '';
|
||||
|
||||
export const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
export const showRes = (res, ...args) => {
|
||||
if (res.code === 200) {
|
||||
if (args.length === 0) {
|
||||
console.log(res.code, util.inspect(res.body, { depth: 6, colors: true }));
|
||||
return;
|
||||
}
|
||||
console.log(res.code, ...args);
|
||||
} else {
|
||||
console.error(res.code, res.message, ...args);
|
||||
}
|
||||
}
|
||||
|
||||
export const exit = (code = 0) => {
|
||||
process.exit(code);
|
||||
}
|
||||
|
||||
export const query = new Query({
|
||||
url: 'https://kevisual.cn/api/router'
|
||||
})
|
||||
45
src/test/secret-key.ts
Normal file
45
src/test/secret-key.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { app, token, showRes, sleep, useContextKey, exit, query } from './common.ts'
|
||||
|
||||
// await sleep(4000)
|
||||
// const token2 = 'sk_6m3gjpkpny2ma9r96ei3bzck3kpg7b7g4oajghw7gmqoqk0vlh3swgxy85e0wnpt'
|
||||
// await useContextKey('models-synced');
|
||||
|
||||
// const res = await app.call({
|
||||
// path: 'secret',
|
||||
// key: 'list',
|
||||
// payload: {
|
||||
// token,
|
||||
// showToken: true
|
||||
// }
|
||||
// })
|
||||
// showRes(res)
|
||||
|
||||
|
||||
// const userRes = await app.call({
|
||||
// path: 'user',
|
||||
// key: 'me',
|
||||
// payload: {
|
||||
// token: token2,
|
||||
// }
|
||||
// })
|
||||
// showRes(userRes)
|
||||
|
||||
// const openid = 'omcvy7AHC6bAA0QM4x9_bE0fGD1g'
|
||||
// const res = await app.call({
|
||||
// path: 'secret',
|
||||
// key: 'wxnotify',
|
||||
// payload: {
|
||||
// openid
|
||||
// }
|
||||
// });
|
||||
// showRes(res)
|
||||
|
||||
const res = await query.post({
|
||||
path: 'secret',
|
||||
key: 'wxnotify',
|
||||
payload: {
|
||||
openid: 'omcvy7M5CBAIB8TWDw6gNDHeHGeE'
|
||||
}
|
||||
})
|
||||
showRes(res)
|
||||
exit(0);
|
||||
Reference in New Issue
Block a user