@jayfong/x-server 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +154 -0
- package/README.md +51 -0
- package/lib/cli/api_generator.d.ts +44 -0
- package/lib/cli/api_generator.js +339 -0
- package/lib/cli/build_util.d.ts +9 -0
- package/lib/cli/build_util.js +141 -0
- package/lib/cli/cli.d.ts +2 -0
- package/lib/cli/cli.js +216 -0
- package/lib/cli/deploy_util.d.ts +10 -0
- package/lib/cli/deploy_util.js +51 -0
- package/lib/cli/env_util.d.ts +29 -0
- package/lib/cli/env_util.js +139 -0
- package/lib/cli/register.d.ts +0 -0
- package/lib/cli/register.js +5 -0
- package/lib/cli/template_util.d.ts +3 -0
- package/lib/cli/template_util.js +18 -0
- package/lib/cli/templates/handlers.ts +3 -0
- package/lib/cli/templates/hooks.ts +2 -0
- package/lib/cli/templates/models.ts +22 -0
- package/lib/cli/templates/routes.ts +26 -0
- package/lib/cli/templates/tasks.ts +2 -0
- package/lib/core/define_bus.d.ts +38 -0
- package/lib/core/define_bus.js +15 -0
- package/lib/core/define_handler.d.ts +16 -0
- package/lib/core/define_handler.js +41 -0
- package/lib/core/define_hook.d.ts +2 -0
- package/lib/core/define_hook.js +8 -0
- package/lib/core/define_server.d.ts +3 -0
- package/lib/core/define_server.js +10 -0
- package/lib/core/define_task.d.ts +2 -0
- package/lib/core/define_task.js +27 -0
- package/lib/core/handler.d.ts +10 -0
- package/lib/core/handler.js +76 -0
- package/lib/core/http_error.d.ts +2 -0
- package/lib/core/http_error.js +8 -0
- package/lib/core/http_method.d.ts +3 -0
- package/lib/core/http_method.js +10 -0
- package/lib/core/server.d.ts +16 -0
- package/lib/core/server.js +137 -0
- package/lib/core/types.d.ts +123 -0
- package/lib/core/types.js +2 -0
- package/lib/index.d.ts +22 -0
- package/lib/index.js +41 -0
- package/lib/plugins/base.d.ts +4 -0
- package/lib/plugins/base.js +2 -0
- package/lib/plugins/cors.d.ts +21 -0
- package/lib/plugins/cors.js +39 -0
- package/lib/plugins/file_parser.d.ts +13 -0
- package/lib/plugins/file_parser.js +19 -0
- package/lib/plugins/ws_parser.d.ts +13 -0
- package/lib/plugins/ws_parser.js +19 -0
- package/lib/plugins/xml_parser.d.ts +19 -0
- package/lib/plugins/xml_parser.js +48 -0
- package/lib/services/base.d.ts +4 -0
- package/lib/services/base.js +2 -0
- package/lib/services/cache.d.ts +119 -0
- package/lib/services/cache.js +196 -0
- package/lib/services/captcha.d.ts +33 -0
- package/lib/services/captcha.js +34 -0
- package/lib/services/dispose.d.ts +17 -0
- package/lib/services/dispose.js +24 -0
- package/lib/services/jwt.d.ts +21 -0
- package/lib/services/jwt.js +50 -0
- package/lib/services/redis.d.ts +10 -0
- package/lib/services/redis.js +14 -0
- package/lib/x.d.ts +5 -0
- package/lib/x.js +11 -0
- package/package.json +86 -0
package/lib/index.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
// @index(['./**/*.ts', '!**/*.test.ts', '!./cli/**'], f => `export * from '${f.path}'`)
|
|
18
|
+
__exportStar(require("./core/define_bus"), exports);
|
|
19
|
+
__exportStar(require("./core/define_handler"), exports);
|
|
20
|
+
__exportStar(require("./core/define_hook"), exports);
|
|
21
|
+
__exportStar(require("./core/define_server"), exports);
|
|
22
|
+
__exportStar(require("./core/define_task"), exports);
|
|
23
|
+
__exportStar(require("./core/handler"), exports);
|
|
24
|
+
__exportStar(require("./core/http_error"), exports);
|
|
25
|
+
__exportStar(require("./core/server"), exports);
|
|
26
|
+
__exportStar(require("./core/types"), exports);
|
|
27
|
+
__exportStar(require("./plugins/base"), exports);
|
|
28
|
+
__exportStar(require("./plugins/cors"), exports);
|
|
29
|
+
__exportStar(require("./plugins/file_parser"), exports);
|
|
30
|
+
__exportStar(require("./plugins/ws_parser"), exports);
|
|
31
|
+
__exportStar(require("./plugins/xml_parser"), exports);
|
|
32
|
+
__exportStar(require("./services/base"), exports);
|
|
33
|
+
__exportStar(require("./services/cache"), exports);
|
|
34
|
+
__exportStar(require("./services/captcha"), exports);
|
|
35
|
+
__exportStar(require("./services/dispose"), exports);
|
|
36
|
+
__exportStar(require("./services/jwt"), exports);
|
|
37
|
+
__exportStar(require("./services/redis"), exports);
|
|
38
|
+
__exportStar(require("./x"), exports);
|
|
39
|
+
// @endindex
|
|
40
|
+
// @ts-ignore
|
|
41
|
+
__exportStar(require(".x/models"), exports);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { BasePlugin } from './base';
|
|
2
|
+
import { FastifyInstance } from 'fastify';
|
|
3
|
+
import { MsValue } from 'vtils/date';
|
|
4
|
+
export interface CorsPluginOptions {
|
|
5
|
+
/**
|
|
6
|
+
* 允许的来源,支持位于开头的 * 通配符
|
|
7
|
+
*/
|
|
8
|
+
allow: string[];
|
|
9
|
+
/**
|
|
10
|
+
* 预检请求缓存时长
|
|
11
|
+
*/
|
|
12
|
+
ttl: MsValue;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* CORS 支持插件
|
|
16
|
+
*/
|
|
17
|
+
export declare class CorsPlugin implements BasePlugin {
|
|
18
|
+
private options;
|
|
19
|
+
constructor(options: CorsPluginOptions);
|
|
20
|
+
register(fastify: FastifyInstance): void;
|
|
21
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.CorsPlugin = void 0;
|
|
7
|
+
const fastify_cors_1 = __importDefault(require("fastify-cors"));
|
|
8
|
+
const vtils_1 = require("vtils");
|
|
9
|
+
const date_1 = require("vtils/date");
|
|
10
|
+
/**
|
|
11
|
+
* CORS 支持插件
|
|
12
|
+
*/
|
|
13
|
+
class CorsPlugin {
|
|
14
|
+
constructor(options) {
|
|
15
|
+
this.options = options;
|
|
16
|
+
}
|
|
17
|
+
register(fastify) {
|
|
18
|
+
const allows = this.options.allow.map(item => ({
|
|
19
|
+
type: item.startsWith('*.') ? 'endsWith' : 'equal',
|
|
20
|
+
domain: item.startsWith('*.') ? item.replace('*.', '.') : item,
|
|
21
|
+
}));
|
|
22
|
+
const check = (0, vtils_1.memoize)((origin) => {
|
|
23
|
+
let i = origin.indexOf(':');
|
|
24
|
+
let j = origin.indexOf(':', i + 1);
|
|
25
|
+
i += 3;
|
|
26
|
+
j = j === -1 ? origin.length : j;
|
|
27
|
+
const domain = origin.substring(i, j);
|
|
28
|
+
const pass = allows.some(item => item.type === 'equal'
|
|
29
|
+
? domain === item.domain
|
|
30
|
+
: domain.endsWith(item.domain));
|
|
31
|
+
return pass;
|
|
32
|
+
});
|
|
33
|
+
fastify.register(fastify_cors_1.default, {
|
|
34
|
+
origin: (origin, cb) => cb(null, origin ? check(origin) : true),
|
|
35
|
+
maxAge: (0, date_1.ms)(this.options.ttl, true),
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
exports.CorsPlugin = CorsPlugin;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { FastifyMultipartOptions } from 'fastify-multipart';
|
|
2
|
+
import { BasePlugin } from './base';
|
|
3
|
+
import { FastifyInstance } from 'fastify';
|
|
4
|
+
export interface FileParserPluginOptions extends FastifyMultipartOptions {
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* file 解析器插件
|
|
8
|
+
*/
|
|
9
|
+
export declare class FileParserPlugin implements BasePlugin {
|
|
10
|
+
private options?;
|
|
11
|
+
constructor(options?: FileParserPluginOptions | undefined);
|
|
12
|
+
register(fastify: FastifyInstance): void;
|
|
13
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.FileParserPlugin = void 0;
|
|
7
|
+
const fastify_multipart_1 = __importDefault(require("fastify-multipart"));
|
|
8
|
+
/**
|
|
9
|
+
* file 解析器插件
|
|
10
|
+
*/
|
|
11
|
+
class FileParserPlugin {
|
|
12
|
+
constructor(options) {
|
|
13
|
+
this.options = options;
|
|
14
|
+
}
|
|
15
|
+
register(fastify) {
|
|
16
|
+
fastify.register(fastify_multipart_1.default, this.options);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
exports.FileParserPlugin = FileParserPlugin;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { WebsocketPluginOptions } from 'fastify-websocket';
|
|
2
|
+
import { BasePlugin } from './base';
|
|
3
|
+
import { FastifyInstance } from 'fastify';
|
|
4
|
+
export interface WsParserPluginOptions extends WebsocketPluginOptions {
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* websocket 解析器插件
|
|
8
|
+
*/
|
|
9
|
+
export declare class WsParserPlugin implements BasePlugin {
|
|
10
|
+
private options?;
|
|
11
|
+
constructor(options?: WsParserPluginOptions | undefined);
|
|
12
|
+
register(fastify: FastifyInstance): void;
|
|
13
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.WsParserPlugin = void 0;
|
|
7
|
+
const fastify_websocket_1 = __importDefault(require("fastify-websocket"));
|
|
8
|
+
/**
|
|
9
|
+
* websocket 解析器插件
|
|
10
|
+
*/
|
|
11
|
+
class WsParserPlugin {
|
|
12
|
+
constructor(options) {
|
|
13
|
+
this.options = options;
|
|
14
|
+
}
|
|
15
|
+
register(fastify) {
|
|
16
|
+
fastify.register(fastify_websocket_1.default, this.options);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
exports.WsParserPlugin = WsParserPlugin;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import fxp from 'fast-xml-parser';
|
|
2
|
+
import { BasePlugin } from './base';
|
|
3
|
+
import { FastifyInstance } from 'fastify';
|
|
4
|
+
export interface XmlParserPluginOptions extends Partial<fxp.X2jOptions> {
|
|
5
|
+
/**
|
|
6
|
+
* 要处理的 `Content-Type`
|
|
7
|
+
*/
|
|
8
|
+
contentType?: string | RegExp | string[];
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* XML 解析器插件
|
|
12
|
+
*
|
|
13
|
+
* 底层解析器是 [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser)
|
|
14
|
+
*/
|
|
15
|
+
export declare class XmlParserPlugin implements BasePlugin {
|
|
16
|
+
private options?;
|
|
17
|
+
constructor(options?: XmlParserPluginOptions | undefined);
|
|
18
|
+
register(fastify: FastifyInstance): void;
|
|
19
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.XmlParserPlugin = void 0;
|
|
7
|
+
const fast_xml_parser_1 = __importDefault(require("fast-xml-parser"));
|
|
8
|
+
/**
|
|
9
|
+
* XML 解析器插件
|
|
10
|
+
*
|
|
11
|
+
* 底层解析器是 [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser)
|
|
12
|
+
*/
|
|
13
|
+
class XmlParserPlugin {
|
|
14
|
+
constructor(options) {
|
|
15
|
+
this.options = options;
|
|
16
|
+
}
|
|
17
|
+
register(fastify) {
|
|
18
|
+
const { contentType = ['text/xml', 'application/xml'], ...parseOptions } = this.options || {};
|
|
19
|
+
fastify.addContentTypeParser(contentType, (req, payload, done) => {
|
|
20
|
+
let body = '';
|
|
21
|
+
const handleError = (err) => done(err);
|
|
22
|
+
const handleData = (data) => (body += data);
|
|
23
|
+
const handleEnd = () => {
|
|
24
|
+
let data;
|
|
25
|
+
let err;
|
|
26
|
+
try {
|
|
27
|
+
data = fast_xml_parser_1.default.parse(body, parseOptions);
|
|
28
|
+
}
|
|
29
|
+
catch (_err) {
|
|
30
|
+
err = _err;
|
|
31
|
+
}
|
|
32
|
+
payload.removeListener('error', handleError);
|
|
33
|
+
payload.removeListener('data', handleData);
|
|
34
|
+
payload.removeListener('end', handleEnd);
|
|
35
|
+
if (err) {
|
|
36
|
+
done(err);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
done(null, data);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
payload.on('error', handleError);
|
|
43
|
+
payload.on('data', handleData);
|
|
44
|
+
payload.on('end', handleEnd);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
exports.XmlParserPlugin = XmlParserPlugin;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { AsyncOrSync, IsEmptyArray, NonEmptyArray } from 'vtils/types';
|
|
2
|
+
import { BaseService } from './base';
|
|
3
|
+
import { MsValue } from 'vtils/date';
|
|
4
|
+
export declare type CacheTTL = MsValue;
|
|
5
|
+
export interface CacheOptions {
|
|
6
|
+
/** 默认过期时间 */
|
|
7
|
+
defaultTTL: CacheTTL;
|
|
8
|
+
/** 存储键前缀 */
|
|
9
|
+
prefix: string;
|
|
10
|
+
}
|
|
11
|
+
declare type Data = {
|
|
12
|
+
[K in string]: (...args: any[]) => any;
|
|
13
|
+
};
|
|
14
|
+
declare type Key<T extends Data> = keyof T;
|
|
15
|
+
declare type KeyPath<T extends Data> = {
|
|
16
|
+
[K in keyof T]: T[K] extends (...args: infer X) => any ? IsEmptyArray<X> extends true ? K : [K, ...X] : never;
|
|
17
|
+
}[keyof T];
|
|
18
|
+
declare type KeyPathForNumberValue<T extends Data> = {
|
|
19
|
+
[K in keyof T]: T[K] extends (...args: infer X) => number ? IsEmptyArray<X> extends true ? K : [K, ...X] : never;
|
|
20
|
+
}[keyof T];
|
|
21
|
+
declare type Value<T extends Data, P extends KeyPath<T>> = ReturnType<T[P extends any[] ? P[0] : P]>;
|
|
22
|
+
export declare class CacheService<TData extends Data = Data, TKey extends Key<TData> = any, TKeyPath extends KeyPath<TData> = any, TKeyPathForNumberValue extends KeyPathForNumberValue<TData> = any> implements BaseService {
|
|
23
|
+
options: CacheOptions;
|
|
24
|
+
serviceName: string;
|
|
25
|
+
private prefix;
|
|
26
|
+
constructor(options: CacheOptions);
|
|
27
|
+
private toRedisKey;
|
|
28
|
+
/**
|
|
29
|
+
* 设置缓存内容。对象类内容尽量避免使用,以免造成问题。
|
|
30
|
+
*
|
|
31
|
+
* @param key 键
|
|
32
|
+
* @param value 值
|
|
33
|
+
* @param ttl 缓存时间(单位:时)
|
|
34
|
+
* @returns 返回设置的缓存内容
|
|
35
|
+
*/
|
|
36
|
+
set<K extends TKeyPath, V extends Value<TData, K>>(key: K, value: V, ttl?: CacheTTL | ((data: V, defaultTTL: CacheTTL) => CacheTTL)): Promise<V>;
|
|
37
|
+
/**
|
|
38
|
+
* 存储值然后返回其对应的键,该键 URL 友好。
|
|
39
|
+
*
|
|
40
|
+
* @param value 值
|
|
41
|
+
* @param ttl 存活时间
|
|
42
|
+
*/
|
|
43
|
+
save<T>(value: T | ((key: string) => AsyncOrSync<T>), ttl?: CacheTTL): Promise<string>;
|
|
44
|
+
/**
|
|
45
|
+
* 获取缓存内容。
|
|
46
|
+
*
|
|
47
|
+
* @param key 键
|
|
48
|
+
* @returns 返回获取到的缓存内容
|
|
49
|
+
*/
|
|
50
|
+
get<K extends TKeyPath, V extends Value<TData, K>>(key: K): Promise<V | null>;
|
|
51
|
+
/**
|
|
52
|
+
* 获取多个缓存内容。
|
|
53
|
+
*
|
|
54
|
+
* @param keys 键
|
|
55
|
+
* @returns 返回获取到的缓存内容
|
|
56
|
+
*/
|
|
57
|
+
getMany<K extends TKeyPath, V extends Value<TData, K>>(...keys: K[]): Promise<Array<V | null>>;
|
|
58
|
+
/**
|
|
59
|
+
* 缓存内容。
|
|
60
|
+
*
|
|
61
|
+
* @param key 键
|
|
62
|
+
* @param action 获取缓存内容的操作
|
|
63
|
+
* @param ttl 缓存时间(单位:时)
|
|
64
|
+
* @returns 返回获取到的内容
|
|
65
|
+
*/
|
|
66
|
+
remember<K extends TKeyPath, V extends Value<TData, K>>(key: K, action: () => V | Promise<V>, ttl?: CacheTTL | ((data: V, defaultTTL: CacheTTL) => CacheTTL)): Promise<V>;
|
|
67
|
+
/**
|
|
68
|
+
* 自增。
|
|
69
|
+
*
|
|
70
|
+
* @param key 键
|
|
71
|
+
* @param increment 增量
|
|
72
|
+
*/
|
|
73
|
+
increase<K extends TKeyPathForNumberValue>(key: K, increment?: number): Promise<number>;
|
|
74
|
+
/**
|
|
75
|
+
* 自减。
|
|
76
|
+
*
|
|
77
|
+
* @param key 键
|
|
78
|
+
* @param decrement 减量
|
|
79
|
+
*/
|
|
80
|
+
decrease<K extends TKeyPathForNumberValue>(key: K, decrement?: number): Promise<number>;
|
|
81
|
+
/**
|
|
82
|
+
* 新增元素。
|
|
83
|
+
*
|
|
84
|
+
* @param key 键
|
|
85
|
+
* @param values 元素列表
|
|
86
|
+
*/
|
|
87
|
+
push<K extends TKeyPath, V extends Value<TData, K>>(key: K, ...values: V[]): Promise<any>;
|
|
88
|
+
/**
|
|
89
|
+
* 从头部弹出元素并返回。
|
|
90
|
+
*
|
|
91
|
+
* @param key 键
|
|
92
|
+
* @param count 弹出元素个数
|
|
93
|
+
*/
|
|
94
|
+
shift<K extends TKeyPath, V extends Value<TData, K>>(key: K, count?: number): Promise<V[]>;
|
|
95
|
+
/**
|
|
96
|
+
* 删除缓存。
|
|
97
|
+
*
|
|
98
|
+
* @param keys 键列表
|
|
99
|
+
*/
|
|
100
|
+
remove<K extends TKeyPath>(...keys: K[]): Promise<any>;
|
|
101
|
+
/**
|
|
102
|
+
* 清空全部缓存。
|
|
103
|
+
*/
|
|
104
|
+
clearAll(): Promise<number>;
|
|
105
|
+
/**
|
|
106
|
+
* 清空指定前缀的缓存。
|
|
107
|
+
*/
|
|
108
|
+
clear<K extends TKey>(...prefixes: NonEmptyArray<K>): Promise<number>;
|
|
109
|
+
/**
|
|
110
|
+
* 派生出一个指定类型的缓存服务。
|
|
111
|
+
*/
|
|
112
|
+
fork<TNextData extends Data>(options?: Partial<CacheOptions>): CacheService<TNextData, Key<TNextData>, KeyPath<TNextData>, KeyPathForNumberValue<TNextData>>;
|
|
113
|
+
}
|
|
114
|
+
declare module '../x' {
|
|
115
|
+
interface X {
|
|
116
|
+
cache: CacheService;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
export {};
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.CacheService = void 0;
|
|
7
|
+
const cuid_1 = __importDefault(require("cuid"));
|
|
8
|
+
const date_1 = require("vtils/date");
|
|
9
|
+
const x_1 = require("../x");
|
|
10
|
+
class CacheService {
|
|
11
|
+
constructor(options) {
|
|
12
|
+
this.options = options;
|
|
13
|
+
this.serviceName = 'cache';
|
|
14
|
+
this.prefix = `${options.prefix}:`;
|
|
15
|
+
}
|
|
16
|
+
toRedisKey(key) {
|
|
17
|
+
return `${this.prefix}${Array.isArray(key) ? key.join('_') : String(key)}`;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* 设置缓存内容。对象类内容尽量避免使用,以免造成问题。
|
|
21
|
+
*
|
|
22
|
+
* @param key 键
|
|
23
|
+
* @param value 值
|
|
24
|
+
* @param ttl 缓存时间(单位:时)
|
|
25
|
+
* @returns 返回设置的缓存内容
|
|
26
|
+
*/
|
|
27
|
+
async set(key, value, ttl = this.options
|
|
28
|
+
.defaultTTL) {
|
|
29
|
+
const redisKey = this.toRedisKey(key);
|
|
30
|
+
const redisValue = JSON.stringify(value);
|
|
31
|
+
const redisTtl = typeof ttl === 'function' ? ttl(value, this.options.defaultTTL) : ttl;
|
|
32
|
+
await x_1.x.redis.set(redisKey, redisValue,
|
|
33
|
+
// 毫秒
|
|
34
|
+
'PX', (0, date_1.ms)(redisTtl));
|
|
35
|
+
return value;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* 存储值然后返回其对应的键,该键 URL 友好。
|
|
39
|
+
*
|
|
40
|
+
* @param value 值
|
|
41
|
+
* @param ttl 存活时间
|
|
42
|
+
*/
|
|
43
|
+
async save(value, ttl = this.options.defaultTTL) {
|
|
44
|
+
const key = `X__${(0, cuid_1.default)()}`;
|
|
45
|
+
await this.set(key, typeof value === 'function' ? await value(key) : value, ttl);
|
|
46
|
+
return key;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* 获取缓存内容。
|
|
50
|
+
*
|
|
51
|
+
* @param key 键
|
|
52
|
+
* @returns 返回获取到的缓存内容
|
|
53
|
+
*/
|
|
54
|
+
async get(key) {
|
|
55
|
+
const redisKey = this.toRedisKey(key);
|
|
56
|
+
const gotValue = await x_1.x.redis.get(redisKey);
|
|
57
|
+
if (gotValue) {
|
|
58
|
+
try {
|
|
59
|
+
return JSON.parse(gotValue);
|
|
60
|
+
}
|
|
61
|
+
catch { }
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* 获取多个缓存内容。
|
|
67
|
+
*
|
|
68
|
+
* @param keys 键
|
|
69
|
+
* @returns 返回获取到的缓存内容
|
|
70
|
+
*/
|
|
71
|
+
async getMany(...keys) {
|
|
72
|
+
const redisKeys = keys.map(key => this.toRedisKey(key));
|
|
73
|
+
if (!redisKeys.length)
|
|
74
|
+
return [];
|
|
75
|
+
const gotValues = await x_1.x.redis.mget(...redisKeys);
|
|
76
|
+
return gotValues.map(value => {
|
|
77
|
+
if (value == null)
|
|
78
|
+
return null;
|
|
79
|
+
try {
|
|
80
|
+
return JSON.parse(value);
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* 缓存内容。
|
|
89
|
+
*
|
|
90
|
+
* @param key 键
|
|
91
|
+
* @param action 获取缓存内容的操作
|
|
92
|
+
* @param ttl 缓存时间(单位:时)
|
|
93
|
+
* @returns 返回获取到的内容
|
|
94
|
+
*/
|
|
95
|
+
async remember(key, action, ttl = this.options
|
|
96
|
+
.defaultTTL) {
|
|
97
|
+
let value = await this.get(key);
|
|
98
|
+
if (value === null) {
|
|
99
|
+
value = await action();
|
|
100
|
+
await this.set(key, value, ttl);
|
|
101
|
+
}
|
|
102
|
+
return value;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* 自增。
|
|
106
|
+
*
|
|
107
|
+
* @param key 键
|
|
108
|
+
* @param increment 增量
|
|
109
|
+
*/
|
|
110
|
+
async increase(key, increment = 1) {
|
|
111
|
+
const redisKey = this.toRedisKey(key);
|
|
112
|
+
return x_1.x.redis.incrby(redisKey, increment);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* 自减。
|
|
116
|
+
*
|
|
117
|
+
* @param key 键
|
|
118
|
+
* @param decrement 减量
|
|
119
|
+
*/
|
|
120
|
+
async decrease(key, decrement = 1) {
|
|
121
|
+
return this.increase(key, -decrement);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* 新增元素。
|
|
125
|
+
*
|
|
126
|
+
* @param key 键
|
|
127
|
+
* @param values 元素列表
|
|
128
|
+
*/
|
|
129
|
+
async push(key, ...values) {
|
|
130
|
+
const redisKey = this.toRedisKey(key);
|
|
131
|
+
return x_1.x.redis.rpush(redisKey, ...values.map(value => JSON.stringify(value)));
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* 从头部弹出元素并返回。
|
|
135
|
+
*
|
|
136
|
+
* @param key 键
|
|
137
|
+
* @param count 弹出元素个数
|
|
138
|
+
*/
|
|
139
|
+
async shift(key, count = 1) {
|
|
140
|
+
const redisKey = this.toRedisKey(key);
|
|
141
|
+
const res = [];
|
|
142
|
+
for (let i = 0; i < count; i++) {
|
|
143
|
+
const item = await x_1.x.redis.lpop(redisKey);
|
|
144
|
+
if (item) {
|
|
145
|
+
res.push(JSON.parse(item));
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return res;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* 删除缓存。
|
|
155
|
+
*
|
|
156
|
+
* @param keys 键列表
|
|
157
|
+
*/
|
|
158
|
+
async remove(...keys) {
|
|
159
|
+
return Promise.all(keys.map(async (key) => {
|
|
160
|
+
const redisKey = this.toRedisKey(key);
|
|
161
|
+
return x_1.x.redis.del(redisKey);
|
|
162
|
+
}));
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* 清空全部缓存。
|
|
166
|
+
*/
|
|
167
|
+
async clearAll() {
|
|
168
|
+
const keys = await x_1.x.redis.keys(`${this.prefix}*`);
|
|
169
|
+
if (keys.length) {
|
|
170
|
+
return x_1.x.redis.del(...keys);
|
|
171
|
+
}
|
|
172
|
+
return 0;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* 清空指定前缀的缓存。
|
|
176
|
+
*/
|
|
177
|
+
async clear(...prefixes) {
|
|
178
|
+
const keys = (await Promise.all(prefixes.map(prefix => x_1.x.redis.keys(`${this.toRedisKey(prefix)}*`)))).flat();
|
|
179
|
+
if (keys.length) {
|
|
180
|
+
return x_1.x.redis.del(...keys);
|
|
181
|
+
}
|
|
182
|
+
return 0;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* 派生出一个指定类型的缓存服务。
|
|
186
|
+
*/
|
|
187
|
+
fork(options) {
|
|
188
|
+
return options
|
|
189
|
+
? new CacheService({
|
|
190
|
+
...this.options,
|
|
191
|
+
...options,
|
|
192
|
+
})
|
|
193
|
+
: this;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
exports.CacheService = CacheService;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { BaseService } from './base';
|
|
2
|
+
import type { MsValue } from 'vtils/date';
|
|
3
|
+
export interface CaptchaOptions {
|
|
4
|
+
/**
|
|
5
|
+
* 验证码长度
|
|
6
|
+
*/
|
|
7
|
+
size: number;
|
|
8
|
+
/**
|
|
9
|
+
* 验证码存活时长
|
|
10
|
+
*/
|
|
11
|
+
ttl: MsValue;
|
|
12
|
+
}
|
|
13
|
+
export interface CaptchaGenerateResult {
|
|
14
|
+
text: string;
|
|
15
|
+
svg: string;
|
|
16
|
+
token: string;
|
|
17
|
+
}
|
|
18
|
+
export interface CaptchaVerifyOptions {
|
|
19
|
+
text: string;
|
|
20
|
+
token: string;
|
|
21
|
+
}
|
|
22
|
+
export declare class CaptchaService implements BaseService {
|
|
23
|
+
private options;
|
|
24
|
+
serviceName: string;
|
|
25
|
+
constructor(options: CaptchaOptions);
|
|
26
|
+
generate(): Promise<CaptchaGenerateResult>;
|
|
27
|
+
verify(options: CaptchaVerifyOptions): Promise<boolean>;
|
|
28
|
+
}
|
|
29
|
+
declare module '../x' {
|
|
30
|
+
interface X {
|
|
31
|
+
captcha: CaptchaService;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.CaptchaService = void 0;
|
|
7
|
+
const svg_captcha_1 = __importDefault(require("svg-captcha"));
|
|
8
|
+
const x_1 = require("../x");
|
|
9
|
+
class CaptchaService {
|
|
10
|
+
constructor(options) {
|
|
11
|
+
this.options = options;
|
|
12
|
+
this.serviceName = 'captcha';
|
|
13
|
+
}
|
|
14
|
+
async generate() {
|
|
15
|
+
const res = svg_captcha_1.default.create({
|
|
16
|
+
size: this.options.size,
|
|
17
|
+
});
|
|
18
|
+
const token = await x_1.x.cache.save(res.text, this.options.ttl);
|
|
19
|
+
return {
|
|
20
|
+
text: res.text,
|
|
21
|
+
svg: res.data,
|
|
22
|
+
token: token,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
async verify(options) {
|
|
26
|
+
if (options.text?.length !== this.options.size) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
const expectedText = await x_1.x.cache.get(options.token);
|
|
30
|
+
await x_1.x.cache.remove(options.token);
|
|
31
|
+
return options.text === expectedText;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.CaptchaService = CaptchaService;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { BaseService } from './base';
|
|
2
|
+
export interface DisposeOptions {
|
|
3
|
+
disposeOnExit?: boolean;
|
|
4
|
+
}
|
|
5
|
+
export declare class DisposeService implements BaseService {
|
|
6
|
+
private options?;
|
|
7
|
+
serviceName: string;
|
|
8
|
+
constructor(options?: DisposeOptions | undefined);
|
|
9
|
+
private disposes;
|
|
10
|
+
add(fn: () => any): void;
|
|
11
|
+
dispose(): Promise<any[]>;
|
|
12
|
+
}
|
|
13
|
+
declare module '../x' {
|
|
14
|
+
interface X {
|
|
15
|
+
dispose: DisposeService;
|
|
16
|
+
}
|
|
17
|
+
}
|