优化
This commit is contained in:
29
server/lib/__tests__/const.spec.ts
Normal file
29
server/lib/__tests__/const.spec.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { NAME_REGEXP } from '../const';
|
||||
|
||||
describe('NAME_REGEXP', () => {
|
||||
describe('allow', () => {
|
||||
test.each([
|
||||
'test',
|
||||
'test01',
|
||||
'你好世界',
|
||||
'你好world',
|
||||
'最大八个汉字内容',
|
||||
'maxis16charactor',
|
||||
'1234567812345678',
|
||||
])('%s', (input) => {
|
||||
expect(NAME_REGEXP.test(input)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('deny', () => {
|
||||
test.each([
|
||||
'世 界',
|
||||
'你好 world',
|
||||
'超过了八个汉字内容',
|
||||
'overmax16charactor',
|
||||
'12345678123456781',
|
||||
])('%s', (input) => {
|
||||
expect(NAME_REGEXP.test(input)).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
66
server/lib/__tests__/utils.spec.ts
Normal file
66
server/lib/__tests__/utils.spec.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import {
|
||||
checkPathMatch,
|
||||
generateRandomStr,
|
||||
getEmailAddress,
|
||||
isValidStr,
|
||||
sleep,
|
||||
} from '../utils';
|
||||
|
||||
describe('getEmailAddress', () => {
|
||||
test.each([
|
||||
['foo@example.com', 'foo'],
|
||||
['foo.bar@example.com', 'foo.bar'],
|
||||
['foo$bar@example.com', 'foo$bar'],
|
||||
])('%s', (input, output) => {
|
||||
expect(getEmailAddress(input)).toBe(output);
|
||||
});
|
||||
});
|
||||
|
||||
describe('generateRandomStr', () => {
|
||||
test('should generate string with length 10(default)', () => {
|
||||
expect(generateRandomStr()).toHaveLength(10);
|
||||
});
|
||||
|
||||
test('should generate string with manual length', () => {
|
||||
expect(generateRandomStr(4)).toHaveLength(4);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isValidStr', () => {
|
||||
test.each<[any, boolean]>([
|
||||
[false, false],
|
||||
[true, false],
|
||||
[0, false],
|
||||
[1, false],
|
||||
['', false],
|
||||
[{}, false],
|
||||
[[], false],
|
||||
['foo', true],
|
||||
])('%p is %p', (input, output) => {
|
||||
expect(isValidStr(input)).toBe(output);
|
||||
});
|
||||
});
|
||||
|
||||
test('sleep', async () => {
|
||||
const start = new Date().valueOf();
|
||||
await sleep(1000);
|
||||
const end = new Date().valueOf();
|
||||
|
||||
const duration = end - start;
|
||||
expect(duration).toBeGreaterThanOrEqual(1000);
|
||||
expect(duration).toBeLessThan(1050);
|
||||
});
|
||||
|
||||
describe('checkPathMatch', () => {
|
||||
const testList = ['/foo/bar'];
|
||||
|
||||
test.each([
|
||||
['/foo/bar', true],
|
||||
['/foo/bar?query=1', true],
|
||||
['/foo', false],
|
||||
['/foo/baz', false],
|
||||
['/foo/baz?bar=', false],
|
||||
])('%s', (input, output) => {
|
||||
expect(checkPathMatch(testList, input)).toBe(output);
|
||||
});
|
||||
});
|
||||
46
server/lib/const.ts
Normal file
46
server/lib/const.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
export const NAME_REGEXP =
|
||||
/^([0-9a-zA-Z]{1,2}|[\u4e00-\u9eff]|[\u3040-\u309Fー]|[\u30A0-\u30FF]){1,8}$/;
|
||||
|
||||
/**
|
||||
* TODO: 待实现权限相关逻辑
|
||||
*
|
||||
* 标准群组权限
|
||||
* key为权限
|
||||
* value为默认值
|
||||
*/
|
||||
export const BUILTIN_GROUP_PERM = {
|
||||
/**
|
||||
* 查看频道
|
||||
*/
|
||||
displayChannel: true,
|
||||
|
||||
/**
|
||||
* 管理频道
|
||||
*/
|
||||
manageChannel: false,
|
||||
|
||||
/**
|
||||
* 管理角色
|
||||
*/
|
||||
manageRole: false,
|
||||
|
||||
/**
|
||||
* 管理群组
|
||||
*/
|
||||
manageGroup: false,
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
*/
|
||||
sendMessage: true,
|
||||
|
||||
/**
|
||||
* 发送图片
|
||||
*/
|
||||
sendImage: true,
|
||||
};
|
||||
|
||||
/**
|
||||
* 系统用户id
|
||||
*/
|
||||
export const SYSTEM_USERID = '000000000000000000000000';
|
||||
@@ -0,0 +1,9 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`des encrypt D 1`] = `"ihmnn4VBPYE="`;
|
||||
|
||||
exports[`des encrypt bar 1`] = `"p/PIC32MPm4="`;
|
||||
|
||||
exports[`des encrypt foo 1`] = `"NP3+ABhEiY4="`;
|
||||
|
||||
exports[`des encrypt 你 1`] = `"O5kF0LXzjpE="`;
|
||||
17
server/lib/crypto/__tests__/des.spec.ts
Normal file
17
server/lib/crypto/__tests__/des.spec.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { desEncrypt, desDecrypt } from '../des';
|
||||
|
||||
describe('des', () => {
|
||||
const key = '12345678';
|
||||
|
||||
describe('encrypt', () => {
|
||||
test.each([['foo'], ['bar'], ['你'], ['D']])('%s', (input) => {
|
||||
expect(desEncrypt(input, key)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('decrypt', () => {
|
||||
test.each([['foo'], ['bar'], ['你'], ['D']])('%s', (input) => {
|
||||
expect(desDecrypt(desEncrypt(input, key), key)).toBe(input);
|
||||
});
|
||||
});
|
||||
});
|
||||
24
server/lib/crypto/des.ts
Normal file
24
server/lib/crypto/des.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import crypto from 'crypto';
|
||||
import { config } from 'tailchat-server-sdk';
|
||||
|
||||
// DES 加密
|
||||
export function desEncrypt(message: string, key: string = config.secret) {
|
||||
key =
|
||||
key.length >= 8 ? key.slice(0, 8) : key.concat('0'.repeat(8 - key.length));
|
||||
const keyHex = new Buffer(key);
|
||||
const cipher = crypto.createCipheriv('des-cbc', keyHex, keyHex);
|
||||
let c = cipher.update(message, 'utf8', 'base64');
|
||||
c += cipher.final('base64');
|
||||
return c;
|
||||
}
|
||||
|
||||
// DES 解密
|
||||
export function desDecrypt(text: string, key: string = config.secret) {
|
||||
key =
|
||||
key.length >= 8 ? key.slice(0, 8) : key.concat('0'.repeat(8 - key.length));
|
||||
const keyHex = new Buffer(key);
|
||||
const cipher = crypto.createDecipheriv('des-cbc', keyHex, keyHex);
|
||||
let c = cipher.update(text, 'base64', 'utf8');
|
||||
c += cipher.final('utf8');
|
||||
return c;
|
||||
}
|
||||
80
server/lib/utils.ts
Normal file
80
server/lib/utils.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import randomString from 'crypto-random-string';
|
||||
import _ from 'lodash';
|
||||
import urlRegex from 'url-regex';
|
||||
|
||||
/**
|
||||
* 返回电子邮箱的地址
|
||||
* @param email 电子邮箱
|
||||
* @returns 电子邮箱
|
||||
*/
|
||||
export function getEmailAddress(email: string) {
|
||||
return email.split('@')[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成随机字符串
|
||||
* @param length 随机字符串长度
|
||||
*/
|
||||
export function generateRandomStr(length = 10): string {
|
||||
return randomString({ length });
|
||||
}
|
||||
|
||||
export function generateRandomNumStr(length = 6) {
|
||||
return randomString({
|
||||
length,
|
||||
type: 'numeric',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否一个可用的字符串
|
||||
* 定义为有长度的字符串
|
||||
*/
|
||||
export function isValidStr(str: unknown): str is string {
|
||||
return typeof str == 'string' && str !== '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否是一个可用的url
|
||||
*/
|
||||
export function isValidUrl(str: unknown): str is string {
|
||||
return typeof str == 'string' && urlRegex({ exact: true }).test(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测一个地址是否是一个合法的资源地址
|
||||
*/
|
||||
export function isValidStaticAssetsUrl(str: unknown): str is string {
|
||||
if (typeof str !== 'string') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const filename = _.last(str.split('/'));
|
||||
if (filename.indexOf('.') === -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 休眠一定时间
|
||||
*/
|
||||
export function sleep(ms: number): Promise<void> {
|
||||
return new Promise((resolve) =>
|
||||
setTimeout(() => {
|
||||
resolve();
|
||||
}, ms)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查url地址是否匹配
|
||||
*/
|
||||
export function checkPathMatch(urlList: string[], url: string): boolean {
|
||||
const fuzzList = urlList.map((url) => url.replaceAll('/', '.'));
|
||||
const fuzzUrl = url.split('?')[0].replaceAll('/', '.');
|
||||
|
||||
// 考虑到serviceName中间可能会有. 且注册的时候不可能把所有情况都列出来,因此进行模糊处理
|
||||
return fuzzList.includes(fuzzUrl);
|
||||
}
|
||||
Reference in New Issue
Block a user