@maiyunnet/kebab 2.0.2 → 2.0.4
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/index.js +1 -1
- package/lib/sql.js +1 -3
- package/lib/text.js +5 -1
- package/package.json +1 -1
- package/tsconfig.json +1 -1
- package/index.ts +0 -33
- package/lib/buffer.ts +0 -152
- package/lib/captcha.ts +0 -63
- package/lib/consistent.ts +0 -219
- package/lib/core.ts +0 -880
- package/lib/crypto.ts +0 -384
- package/lib/db.ts +0 -719
- package/lib/dns.ts +0 -405
- package/lib/fs.ts +0 -527
- package/lib/jwt.ts +0 -276
- package/lib/kv.ts +0 -1489
- package/lib/lan.ts +0 -87
- package/lib/net/formdata.ts +0 -166
- package/lib/net/request.ts +0 -150
- package/lib/net/response.ts +0 -59
- package/lib/net.ts +0 -662
- package/lib/s3.ts +0 -235
- package/lib/scan.ts +0 -364
- package/lib/session.ts +0 -230
- package/lib/sql.ts +0 -1151
- package/lib/ssh/sftp.ts +0 -508
- package/lib/ssh/shell.ts +0 -123
- package/lib/ssh.ts +0 -191
- package/lib/text.ts +0 -615
- package/lib/time.ts +0 -254
- package/lib/ws.ts +0 -523
- package/lib/zip.ts +0 -447
- package/lib/zlib.ts +0 -350
- package/main.ts +0 -27
- package/sys/child.ts +0 -678
- package/sys/cmd.ts +0 -225
- package/sys/ctr.ts +0 -904
- package/sys/master.ts +0 -355
- package/sys/mod.ts +0 -1871
- package/sys/route.ts +0 -1113
- package/types/index.d.ts +0 -283
- package/www/example/ctr/main.ts +0 -9
- package/www/example/ctr/middle.ts +0 -26
- package/www/example/ctr/test.ts +0 -3218
- package/www/example/mod/test.ts +0 -47
- package/www/example/mod/testdata.ts +0 -30
- package/www/example/ws/mproxy.ts +0 -16
- package/www/example/ws/rproxy.ts +0 -14
- package/www/example/ws/test.ts +0 -36
package/lib/s3.ts
DELETED
|
@@ -1,235 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Project: Kebab, User: Tang Rukun, JianSuoQiYue
|
|
3
|
-
* Date: 2024-2-18 18:32:45
|
|
4
|
-
* Last: 2024-2-18 18:32:47, 2024-3-16 16:42:27, 2024-5-31 21:36:26, 2024-7-8 00:28:42, 2024-7-19 11:32:43, 2025-6-10 21:45:34
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
// --- 库和定义 ---
|
|
8
|
-
import * as s3 from '@aws-sdk/client-s3';
|
|
9
|
-
import * as ls from '@aws-sdk/lib-storage';
|
|
10
|
-
import * as stream from 'stream';
|
|
11
|
-
import * as sCtr from '~/sys/ctr';
|
|
12
|
-
import * as lCore from '~/lib/core';
|
|
13
|
-
import * as lText from '~/lib/text';
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* s3 文档:https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/s3/
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
/** --- 服务商定义 --- */
|
|
20
|
-
export enum ESERVICE {
|
|
21
|
-
'AMAZON',
|
|
22
|
-
'TENCENT',
|
|
23
|
-
'ALIBABA',
|
|
24
|
-
'CF'
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/** --- 选项 --- */
|
|
28
|
-
export interface IOptions {
|
|
29
|
-
/** --- 服务商 ---- */
|
|
30
|
-
'service': ESERVICE;
|
|
31
|
-
/** --- cf r2 使用 --- */
|
|
32
|
-
'account'?: string;
|
|
33
|
-
/** --- 密钥键 --- */
|
|
34
|
-
'secretId'?: string;
|
|
35
|
-
/** --- 密钥值 --- */
|
|
36
|
-
'secretKey'?: string;
|
|
37
|
-
/** --- 区域 --- */
|
|
38
|
-
'region'?: string;
|
|
39
|
-
/** --- 预定义 bucket --- */
|
|
40
|
-
'bucket'?: string;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export class S3 {
|
|
44
|
-
|
|
45
|
-
private readonly _link;
|
|
46
|
-
|
|
47
|
-
/** --- bucket 名 --- */
|
|
48
|
-
private _bucket: string = '';
|
|
49
|
-
|
|
50
|
-
private readonly _ctr: sCtr.Ctr;
|
|
51
|
-
|
|
52
|
-
public constructor(ctr: sCtr.Ctr, opt: IOptions) {
|
|
53
|
-
this._ctr = ctr;
|
|
54
|
-
const config = ctr.getPrototype('_config');
|
|
55
|
-
opt.account ??= config.s3?.[ESERVICE[opt.service]]?.account ?? '';
|
|
56
|
-
opt.secretId ??= config.s3?.[ESERVICE[opt.service]]?.sid ?? '';
|
|
57
|
-
opt.secretKey ??= config.s3?.[ESERVICE[opt.service]]?.skey ?? '';
|
|
58
|
-
opt.region ??= config.s3?.[ESERVICE[opt.service]]?.region ?? '';
|
|
59
|
-
opt.bucket ??= config.s3?.[ESERVICE[opt.service]]?.bucket ?? '';
|
|
60
|
-
this._bucket = opt.bucket;
|
|
61
|
-
let endpoint: string | undefined;
|
|
62
|
-
switch (opt.service) {
|
|
63
|
-
case ESERVICE.TENCENT: {
|
|
64
|
-
endpoint = `https://cos.${opt.region}.myqcloud.com`;
|
|
65
|
-
break;
|
|
66
|
-
}
|
|
67
|
-
case ESERVICE.ALIBABA: {
|
|
68
|
-
endpoint = `https://oss-${opt.region}.aliyuncs.com`;
|
|
69
|
-
break;
|
|
70
|
-
}
|
|
71
|
-
case ESERVICE.CF: {
|
|
72
|
-
endpoint = `https://${opt.account}.r2.cloudflarestorage.com`;
|
|
73
|
-
break;
|
|
74
|
-
}
|
|
75
|
-
default: {
|
|
76
|
-
endpoint = undefined;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
this._link = new s3.S3Client({
|
|
80
|
-
'region': opt.region,
|
|
81
|
-
'credentials': {
|
|
82
|
-
'accessKeyId': opt.secretId,
|
|
83
|
-
'secretAccessKey': opt.secretKey
|
|
84
|
-
},
|
|
85
|
-
'endpoint': endpoint
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* --- 修改预定义 bucket ---
|
|
91
|
-
* @param bucket bucket 名
|
|
92
|
-
*/
|
|
93
|
-
public setBucket(bucket: string): void {
|
|
94
|
-
this._bucket = bucket;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* --- 上传对象(可传流且也可无需设置 length) --
|
|
99
|
-
* @param key 对象路径
|
|
100
|
-
* @param content 内容
|
|
101
|
-
* @param length 设置 contentLength,如果是流模式则需要设置此项,也可以设置为对象参数
|
|
102
|
-
* @param bucket bucket 名
|
|
103
|
-
*/
|
|
104
|
-
public async putObject(
|
|
105
|
-
key: string, content: string | Buffer | stream.Readable, length?: number | {
|
|
106
|
-
'length'?: number;
|
|
107
|
-
'type'?: string;
|
|
108
|
-
'disposition'?: string;
|
|
109
|
-
'bucket'?: string;
|
|
110
|
-
}, bucket?: string
|
|
111
|
-
): Promise<s3.CompleteMultipartUploadCommandOutput | false> {
|
|
112
|
-
try {
|
|
113
|
-
/** --- content type --- */
|
|
114
|
-
let type: string | undefined = undefined;
|
|
115
|
-
/** --- content disposition --- */
|
|
116
|
-
let disposition: string | undefined = undefined;
|
|
117
|
-
if (typeof length !== 'number') {
|
|
118
|
-
type = length?.type;
|
|
119
|
-
disposition = length?.disposition;
|
|
120
|
-
bucket = length?.bucket;
|
|
121
|
-
length = length?.length;
|
|
122
|
-
}
|
|
123
|
-
const upload = new ls.Upload({
|
|
124
|
-
'client': this._link,
|
|
125
|
-
'params': {
|
|
126
|
-
'Bucket': bucket ?? this._bucket,
|
|
127
|
-
'Key': key,
|
|
128
|
-
'Body': content,
|
|
129
|
-
'ContentLength': length,
|
|
130
|
-
'ContentType': type,
|
|
131
|
-
'ContentDisposition': disposition
|
|
132
|
-
}
|
|
133
|
-
});
|
|
134
|
-
const res = await upload.done();
|
|
135
|
-
return (res.Location && res.Bucket && res.Key) ? res : false;
|
|
136
|
-
}
|
|
137
|
-
catch (e: any) {
|
|
138
|
-
await lCore.log(this._ctr, '[putObject, s3] ' + lText.stringifyJson(e.message ?? '').slice(1, -1).replace(/"/g, '""'), '-error');
|
|
139
|
-
return false;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* --- 获取对象流,可通过流获取 buffer 或 text ---
|
|
145
|
-
* @param key 对象路径
|
|
146
|
-
* @param bucket bucket 名
|
|
147
|
-
*/
|
|
148
|
-
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
149
|
-
public async getObject(key: string, bucket?: string) {
|
|
150
|
-
try {
|
|
151
|
-
const go = new s3.GetObjectCommand({
|
|
152
|
-
'Bucket': bucket ?? this._bucket,
|
|
153
|
-
'Key': key
|
|
154
|
-
});
|
|
155
|
-
const r = await this._link.send(go);
|
|
156
|
-
return r.Body;
|
|
157
|
-
}
|
|
158
|
-
catch {
|
|
159
|
-
// await lCore.log(this._ctr, '[getObject, s3] ' + lText.stringifyJson(e.message ?? '').slice(1, -1).replace(/"/g, '""'), '-error');
|
|
160
|
-
return false;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* --- 删除对象 ---
|
|
166
|
-
* @param key 对象路径
|
|
167
|
-
* @param bucket bucket 名
|
|
168
|
-
*/
|
|
169
|
-
public async deleteObject(key: string, bucket?: string): Promise<boolean> {
|
|
170
|
-
try {
|
|
171
|
-
const doc = new s3.DeleteObjectCommand({
|
|
172
|
-
'Bucket': bucket ?? this._bucket,
|
|
173
|
-
'Key': key
|
|
174
|
-
});
|
|
175
|
-
await this._link.send(doc);
|
|
176
|
-
return true;
|
|
177
|
-
}
|
|
178
|
-
catch {
|
|
179
|
-
// await lCore.log(this._ctr, '[deleteObject, s3] ' + lText.stringifyJson(e.message ?? '').slice(1, -1).replace(/"/g, '""'), '-error');
|
|
180
|
-
return false;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* --- 批量删除对象 ---
|
|
186
|
-
* @param keys 批量对象路径
|
|
187
|
-
* @param bucket bucket 名
|
|
188
|
-
*/
|
|
189
|
-
public async deleteObjects(keys: string[], bucket?: string): Promise<boolean> {
|
|
190
|
-
try {
|
|
191
|
-
const doc = new s3.DeleteObjectsCommand({
|
|
192
|
-
'Bucket': bucket ?? this._bucket,
|
|
193
|
-
'Delete': {
|
|
194
|
-
'Objects': keys.map((key) => ({ 'Key': key }))
|
|
195
|
-
}
|
|
196
|
-
});
|
|
197
|
-
await this._link.send(doc);
|
|
198
|
-
return true;
|
|
199
|
-
}
|
|
200
|
-
catch (e: any) {
|
|
201
|
-
await lCore.log(this._ctr, '[deleteObjects, s3] ' + lText.stringifyJson(e.message ?? '').slice(1, -1).replace(/"/g, '""'), '-error');
|
|
202
|
-
return false;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* --- 检测对象是否存在 ---
|
|
208
|
-
* @param key 对象路径
|
|
209
|
-
* @param bucket bucket 名
|
|
210
|
-
*/
|
|
211
|
-
public async headObject(key: string, bucket?: string): Promise<s3.HeadObjectCommandOutput | false> {
|
|
212
|
-
try {
|
|
213
|
-
const ho = new s3.HeadObjectCommand({
|
|
214
|
-
'Bucket': bucket ?? this._bucket,
|
|
215
|
-
'Key': key
|
|
216
|
-
});
|
|
217
|
-
return await this._link.send(ho);
|
|
218
|
-
}
|
|
219
|
-
catch (e: any) {
|
|
220
|
-
if (e.$metadata?.httpStatusCode !== 404) {
|
|
221
|
-
await lCore.log(this._ctr, '[headObject, s3] ' + lText.stringifyJson(e.message ?? '').slice(1, -1).replace(/"/g, '""'), '-error');
|
|
222
|
-
}
|
|
223
|
-
return false;
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* --- 创建一个对象存储对象 ---
|
|
231
|
-
* @param opt 选项
|
|
232
|
-
*/
|
|
233
|
-
export function get(ctr: sCtr.Ctr, opt: IOptions): S3 {
|
|
234
|
-
return new S3(ctr, opt);
|
|
235
|
-
}
|
package/lib/scan.ts
DELETED
|
@@ -1,364 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Project: Kebab, User: JianSuoQiYue
|
|
3
|
-
* Date: 2022-09-24 15:23:25
|
|
4
|
-
* Last: 2022-09-24 15:23:25, 2022-9-26 12:37:01, 2022-12-29 00:11:16
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
/*
|
|
8
|
-
CREATE TABLE IF NOT EXISTS `scan` (
|
|
9
|
-
`id` bigint NOT NULL AUTO_INCREMENT,
|
|
10
|
-
`token` char (32) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
|
|
11
|
-
`data` text NOT NULL,
|
|
12
|
-
`time_update` bigint NOT NULL,
|
|
13
|
-
`time_add` bigint NOT NULL,
|
|
14
|
-
`time_exp` bigint NOT NULL,
|
|
15
|
-
PRIMARY KEY (`id`),
|
|
16
|
-
UNIQUE KEY `token` (`token`) USING btree
|
|
17
|
-
KEY `time_update` (`time_update`),
|
|
18
|
-
KEY `time_exp` (`time_exp`)
|
|
19
|
-
) ENGINE = InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
|
|
20
|
-
*/
|
|
21
|
-
import * as lCore from '~/lib/core';
|
|
22
|
-
import * as lDb from '~/lib/db';
|
|
23
|
-
import * as lKv from '~/lib/kv';
|
|
24
|
-
import * as lSql from '~/lib/sql';
|
|
25
|
-
import * as lTime from '~/lib/time';
|
|
26
|
-
import * as lText from '~/lib/text';
|
|
27
|
-
import * as sCtr from '~/sys/ctr';
|
|
28
|
-
|
|
29
|
-
/** --- Scan 设置的选项 --- */
|
|
30
|
-
export interface IOptions {
|
|
31
|
-
'ttl'?: number;
|
|
32
|
-
'sqlPre'?: sCtr.Ctr | string;
|
|
33
|
-
'name'?: string;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/** --- scanned 函数的选项 --- */
|
|
37
|
-
export interface IStaticOptions {
|
|
38
|
-
'sqlPre'?: sCtr.Ctr | string;
|
|
39
|
-
'name'?: string;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export class Scan {
|
|
43
|
-
|
|
44
|
-
private readonly _link: lDb.Pool | lKv.Pool;
|
|
45
|
-
|
|
46
|
-
private readonly _sql: lSql.Sql | null = null;
|
|
47
|
-
|
|
48
|
-
/** --- 表名或者 kv 里 key 的前缀 --- */
|
|
49
|
-
private readonly _name: string = 'scan';
|
|
50
|
-
|
|
51
|
-
private _token: string | null = null;
|
|
52
|
-
|
|
53
|
-
/** --- 有效期,默认 5 分钟 --- */
|
|
54
|
-
private _ttl = 60 * 5;
|
|
55
|
-
|
|
56
|
-
public constructor(link: lDb.Pool | lKv.Pool, token?: string, opt: IOptions = {}) {
|
|
57
|
-
if (opt.ttl !== undefined) {
|
|
58
|
-
this._ttl = opt.ttl;
|
|
59
|
-
}
|
|
60
|
-
if (opt.name) {
|
|
61
|
-
this._name = opt.name;
|
|
62
|
-
}
|
|
63
|
-
this._link = link;
|
|
64
|
-
if (link instanceof lDb.Pool) {
|
|
65
|
-
this._sql = lSql.get(opt.sqlPre);
|
|
66
|
-
}
|
|
67
|
-
if (token) {
|
|
68
|
-
this._token = token;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/** --- 二维码剩余有效时间 --- */
|
|
73
|
-
private _timeLeft: number | null = null;
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* --- 生成二维码处的轮询,检查是否被扫码、被录入数据 ---
|
|
77
|
-
* @returns -3 系统错误 -2 token 不存在或已过期 -1 无操作, 0 已扫码, 其他返回为存的数据并结束轮询
|
|
78
|
-
*/
|
|
79
|
-
public async poll(): Promise<any> {
|
|
80
|
-
if (!this._token) {
|
|
81
|
-
return -3;
|
|
82
|
-
}
|
|
83
|
-
const time = lTime.stamp();
|
|
84
|
-
if (this._link instanceof lDb.Pool) {
|
|
85
|
-
// --- Db ---
|
|
86
|
-
this._sql!.select('*', this._name).where([
|
|
87
|
-
{ 'token': this._token },
|
|
88
|
-
['time_exp', '>', time]
|
|
89
|
-
]);
|
|
90
|
-
const r = await this._link.query(this._sql!.getSql(), this._sql!.getData());
|
|
91
|
-
if (r.error) {
|
|
92
|
-
// --- 出错 ---
|
|
93
|
-
return -3;
|
|
94
|
-
}
|
|
95
|
-
const data = r.rows?.[0];
|
|
96
|
-
if (!data) {
|
|
97
|
-
// --- 不存在或过期 ---
|
|
98
|
-
return -2;
|
|
99
|
-
}
|
|
100
|
-
// --- 存在,判断是否被扫码,以及是否被写入数据 ---
|
|
101
|
-
this._timeLeft = data['time_exp'] - time;
|
|
102
|
-
if (data['data'] !== '') {
|
|
103
|
-
// --- 已经写入数据了,删除数据库条目并返回写入的数据内容 ---
|
|
104
|
-
this._sql!.delete(this._name).where({
|
|
105
|
-
'id': data['id']
|
|
106
|
-
});
|
|
107
|
-
const r = lText.parseJson(data['data']);
|
|
108
|
-
return r === false ? -3 : r;
|
|
109
|
-
}
|
|
110
|
-
else if (data['time_update'] > 0) {
|
|
111
|
-
// --- 已被扫描 ---
|
|
112
|
-
return 0;
|
|
113
|
-
}
|
|
114
|
-
else {
|
|
115
|
-
// --- 未扫描 ---
|
|
116
|
-
return -1;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
else {
|
|
120
|
-
// --- Kv ---
|
|
121
|
-
const data = await this._link.getJson('scan-' + this._name + '_' + this._token);
|
|
122
|
-
if (data === null) {
|
|
123
|
-
// --- 不存在或过期 ---
|
|
124
|
-
return -2;
|
|
125
|
-
}
|
|
126
|
-
const ttl = await this._link.ttl('scan-' + this._name + '_' + this._token);
|
|
127
|
-
if (ttl === null) {
|
|
128
|
-
return -3;
|
|
129
|
-
}
|
|
130
|
-
this._timeLeft = ttl;
|
|
131
|
-
if (data['data'] !== null) {
|
|
132
|
-
// --- 已经写入数据了,删除数据库条目并返回写入的数据内容 ---
|
|
133
|
-
await this._link.del('scan-' + this._name + '_' + this._token);
|
|
134
|
-
return data;
|
|
135
|
-
}
|
|
136
|
-
else if (data['time_update'] > 0) {
|
|
137
|
-
// --- 已被扫描 ---
|
|
138
|
-
return 0;
|
|
139
|
-
}
|
|
140
|
-
else {
|
|
141
|
-
// --- 未扫描 ---
|
|
142
|
-
return -1;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* --- 创建 token,直接应用到本类 ---
|
|
149
|
-
*/
|
|
150
|
-
public async createToken(): Promise<boolean> {
|
|
151
|
-
await this._gc();
|
|
152
|
-
const time = lTime.stamp();
|
|
153
|
-
let count = 0;
|
|
154
|
-
while (true) {
|
|
155
|
-
if (count === 5) {
|
|
156
|
-
return false;
|
|
157
|
-
}
|
|
158
|
-
this._token = lCore.random(32, lCore.RANDOM_LUN);
|
|
159
|
-
if (this._link instanceof lDb.Pool) {
|
|
160
|
-
// --- Db ---
|
|
161
|
-
this._sql!.insert(this._name).values({
|
|
162
|
-
'token': this._token,
|
|
163
|
-
'data': '',
|
|
164
|
-
'time_update': '0',
|
|
165
|
-
'time_add': time,
|
|
166
|
-
'time_exp': time + this._ttl
|
|
167
|
-
});
|
|
168
|
-
const r = await this._link.execute(this._sql!.getSql(), this._sql!.getData());
|
|
169
|
-
if (r.error) {
|
|
170
|
-
if (r.error.errno !== 1062) {
|
|
171
|
-
return false;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
else {
|
|
175
|
-
break;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
else {
|
|
179
|
-
// --- Kv ---
|
|
180
|
-
if (await this._link.set('scan-' + this._name + '_' + this._token, {
|
|
181
|
-
'time_update': 0,
|
|
182
|
-
'data': null
|
|
183
|
-
}, this._ttl, 'nx')) {
|
|
184
|
-
break;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
++count;
|
|
188
|
-
}
|
|
189
|
-
return true;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* --- 获取当前 token ---
|
|
194
|
-
*/
|
|
195
|
-
public getToken(): string | null {
|
|
196
|
-
return this._token;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* --- 设置有效期,设置后的新 token 被创建有效 ---
|
|
201
|
-
* @param ttl
|
|
202
|
-
*/
|
|
203
|
-
public setTTL(ttl: number): void {
|
|
204
|
-
this._ttl = ttl;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* --- 获取设置的有效期 ---
|
|
209
|
-
*/
|
|
210
|
-
public getTTL(): number {
|
|
211
|
-
return this._ttl;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* --- 获取当前 token 可扫剩余有效期 ---
|
|
216
|
-
*/
|
|
217
|
-
public getTimeLeft(): number | null {
|
|
218
|
-
return this._timeLeft;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* --- 根据情况清空 Db 状态下的 scan 表垃圾数据 ---
|
|
223
|
-
*/
|
|
224
|
-
private async _gc(): Promise<void> {
|
|
225
|
-
if (this._link instanceof lKv.Pool) {
|
|
226
|
-
return;
|
|
227
|
-
}
|
|
228
|
-
if (lCore.rand(0, 10) !== 5) {
|
|
229
|
-
return;
|
|
230
|
-
}
|
|
231
|
-
this._sql!.delete(this._name).where([
|
|
232
|
-
['time_exp', '<', lTime.stamp()]
|
|
233
|
-
]);
|
|
234
|
-
await this._link.execute(this._sql!.getSql(), this._sql!.getData());
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* -- 创建 Scan 对象 ---
|
|
241
|
-
* @param link
|
|
242
|
-
* @param token Token
|
|
243
|
-
* @param opt
|
|
244
|
-
*/
|
|
245
|
-
export async function get(link: lDb.Pool | lKv.Pool, token?: string, opt: IOptions = {}): Promise<Scan> {
|
|
246
|
-
const scan = new Scan(link, token, opt);
|
|
247
|
-
if (!token) {
|
|
248
|
-
await scan.createToken();
|
|
249
|
-
}
|
|
250
|
-
return scan;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* --- 对 token 执行访问操作,通常用户扫码后展示的网页所调用,代表已扫码 ---
|
|
255
|
-
* @param link
|
|
256
|
-
* @patam token 必填
|
|
257
|
-
* @param opt
|
|
258
|
-
*/
|
|
259
|
-
export async function scanned(
|
|
260
|
-
link: lDb.Pool | lKv.Pool,
|
|
261
|
-
token: string,
|
|
262
|
-
opt: IStaticOptions = {}
|
|
263
|
-
): Promise<boolean> {
|
|
264
|
-
const time = lTime.stamp();
|
|
265
|
-
const name = opt.name ?? 'scan';
|
|
266
|
-
if (link instanceof lDb.Pool) {
|
|
267
|
-
// --- Db ---
|
|
268
|
-
const sql = lSql.get(opt.sqlPre);
|
|
269
|
-
sql.update(name, {
|
|
270
|
-
'time_update': time
|
|
271
|
-
}).where([
|
|
272
|
-
{
|
|
273
|
-
'token': token,
|
|
274
|
-
'time_update': '0'
|
|
275
|
-
},
|
|
276
|
-
['time_exp', '>', time]
|
|
277
|
-
]);
|
|
278
|
-
const r = await link.execute(sql.getSql(), sql.getData());
|
|
279
|
-
if (r.error) {
|
|
280
|
-
return false;
|
|
281
|
-
}
|
|
282
|
-
if (r.packet?.affectedRows && r.packet.affectedRows > 0) {
|
|
283
|
-
return true;
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
else {
|
|
287
|
-
// --- Kv ---
|
|
288
|
-
const ldata = await link.getJson('scan-' + name + '_' + token);
|
|
289
|
-
if (ldata === null) {
|
|
290
|
-
return false;
|
|
291
|
-
}
|
|
292
|
-
if (ldata['time_update'] > 0) {
|
|
293
|
-
// --- 已经被扫码过了 ---
|
|
294
|
-
return false;
|
|
295
|
-
}
|
|
296
|
-
ldata['time_update'] = time;
|
|
297
|
-
const ttl = await link.ttl('scan-' + name + '_' + token);
|
|
298
|
-
if (ttl === null) {
|
|
299
|
-
return false;
|
|
300
|
-
}
|
|
301
|
-
return link.set('scan-' + name + '_' + token, ldata, ttl + 1, 'xx');
|
|
302
|
-
}
|
|
303
|
-
return false;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
/**
|
|
307
|
-
* --- 将数据写入 token,通常在客户的逻辑下去写,服务器会 poll 到 ---
|
|
308
|
-
* @param link
|
|
309
|
-
* @param token
|
|
310
|
-
* @param data
|
|
311
|
-
* @param opt
|
|
312
|
-
*/
|
|
313
|
-
export async function setData(
|
|
314
|
-
link: lDb.Pool | lKv.Pool,
|
|
315
|
-
token: string,
|
|
316
|
-
data: Record<string, any> | string | number,
|
|
317
|
-
opt: IStaticOptions = {}
|
|
318
|
-
): Promise<boolean> {
|
|
319
|
-
if (typeof data === 'number' && Number.isInteger(data)) {
|
|
320
|
-
if (data >= -3 && data <= 1) {
|
|
321
|
-
return false;
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
const time = lTime.stamp();
|
|
325
|
-
const name = opt.name ?? 'scan';
|
|
326
|
-
if (link instanceof lDb.Pool) {
|
|
327
|
-
// --- Db ---
|
|
328
|
-
const sql = lSql.get(opt.sqlPre);
|
|
329
|
-
sql.update(name, {
|
|
330
|
-
'data': lText.stringifyJson(data)
|
|
331
|
-
}).where([
|
|
332
|
-
{
|
|
333
|
-
'token': token,
|
|
334
|
-
},
|
|
335
|
-
['time_update', '>', '0'],
|
|
336
|
-
['time_exp', '>', time]
|
|
337
|
-
]);
|
|
338
|
-
const r = await link.execute(sql.getSql(), sql.getData());
|
|
339
|
-
if (r.error) {
|
|
340
|
-
return false;
|
|
341
|
-
}
|
|
342
|
-
if (r.packet?.affectedRows && r.packet?.affectedRows > 0) {
|
|
343
|
-
return true;
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
else {
|
|
347
|
-
// --- Kv ---
|
|
348
|
-
const ldata = await link.getJson('scan-' + name + '_' + token);
|
|
349
|
-
if (ldata === null) {
|
|
350
|
-
return false;
|
|
351
|
-
}
|
|
352
|
-
if (ldata['time_update'] === 0) {
|
|
353
|
-
// --- 还未被扫码,无法操作 ---
|
|
354
|
-
return false;
|
|
355
|
-
}
|
|
356
|
-
const ttl = await link.ttl('scan-' + name + '_' + token);
|
|
357
|
-
if (ttl === null) {
|
|
358
|
-
return false;
|
|
359
|
-
}
|
|
360
|
-
ldata['data'] = data;
|
|
361
|
-
return link.set('scan-' + name + '_' + token, ldata, ttl + 1, 'xx');
|
|
362
|
-
}
|
|
363
|
-
return false;
|
|
364
|
-
}
|