@waline/vercel 1.36.4 → 1.37.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/__tests__/xss.spec.js +8 -19
- package/development.js +1 -0
- package/index.js +4 -3
- package/package.json +17 -18
- package/src/config/adapter.js +9 -11
- package/src/config/config.js +4 -12
- package/src/config/extend.js +0 -6
- package/src/config/middleware.js +2 -5
- package/src/controller/article.js +5 -12
- package/src/controller/comment.js +33 -53
- package/src/controller/db.js +3 -9
- package/src/controller/oauth.js +8 -8
- package/src/controller/rest.js +1 -1
- package/src/controller/user/password.js +3 -12
- package/src/controller/user.js +18 -45
- package/src/controller/verification.js +3 -2
- package/src/extend/think.js +33 -23
- package/src/logic/base.js +31 -47
- package/src/logic/comment.js +7 -4
- package/src/logic/db.js +1 -1
- package/src/logic/token.js +2 -2
- package/src/middleware/dashboard.js +1 -0
- package/src/middleware/plugin.js +1 -1
- package/src/service/akismet.js +10 -3
- package/src/service/avatar.js +2 -4
- package/src/service/markdown/highlight.js +1 -1
- package/src/service/markdown/mathCommon.js +2 -8
- package/src/service/markdown/mathjax.js +1 -2
- package/src/service/markdown/utils.js +5 -5
- package/src/service/markdown/xss.js +5 -10
- package/src/service/notify.js +111 -135
- package/src/service/storage/base.js +2 -7
- package/src/service/storage/cloudbase.js +9 -7
- package/src/service/storage/github.js +39 -42
- package/src/service/storage/leancloud.js +41 -54
- package/src/service/storage/mongodb.js +11 -8
- package/src/service/storage/mysql.js +7 -13
- package/src/service/storage/postgresql.js +7 -11
- package/src/service/storage/deta.js +0 -310
|
@@ -1,310 +0,0 @@
|
|
|
1
|
-
const { performance } = require('perf_hooks');
|
|
2
|
-
|
|
3
|
-
const { Deta } = require('deta');
|
|
4
|
-
|
|
5
|
-
const Base = require('./base.js');
|
|
6
|
-
|
|
7
|
-
module.exports = class extends Base {
|
|
8
|
-
constructor(tableName) {
|
|
9
|
-
super(tableName);
|
|
10
|
-
const deta = Deta(process.env.DETA_PROJECT_KEY);
|
|
11
|
-
|
|
12
|
-
this.instance = deta.Base(tableName);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
complex(obj, keys) {
|
|
16
|
-
const result = new Array(keys.reduce((a, b) => a * obj[b].length, 1));
|
|
17
|
-
|
|
18
|
-
for (let i = 0; i < result.length; i++) {
|
|
19
|
-
result[i] = { ...obj };
|
|
20
|
-
for (let n = 0; n < keys.length; n++) {
|
|
21
|
-
const divisor = keys
|
|
22
|
-
.slice(n + 1)
|
|
23
|
-
.reduce((a, b) => a * obj[b].length, 1);
|
|
24
|
-
const idx = Math.floor(i / divisor) % obj[keys[n]].length;
|
|
25
|
-
|
|
26
|
-
result[i][keys[n]] = obj[keys[n]][idx];
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return result;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* deta base doesn't support order data by field
|
|
35
|
-
* it will order by key default
|
|
36
|
-
* so we need create a lower key than before to keep latest data in front
|
|
37
|
-
* @returns string
|
|
38
|
-
*/
|
|
39
|
-
async uuid() {
|
|
40
|
-
const items = await this.select({}, { limit: 1 });
|
|
41
|
-
let lastKey;
|
|
42
|
-
|
|
43
|
-
if (items.length && !isNaN(parseInt(items[0].objectId))) {
|
|
44
|
-
lastKey = parseInt(items[0].objectId);
|
|
45
|
-
} else {
|
|
46
|
-
lastKey = Number.MAX_SAFE_INTEGER - performance.now();
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return (lastKey - Math.round(Math.random() * 100)).toString();
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
parseWhere(where) {
|
|
53
|
-
if (think.isEmpty(where)) {
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const parseKey = (k) => (k === 'objectId' ? 'key' : k);
|
|
58
|
-
const conditions = {};
|
|
59
|
-
const _isArrayKeys = [];
|
|
60
|
-
|
|
61
|
-
for (let k in where) {
|
|
62
|
-
if (think.isString(where[k])) {
|
|
63
|
-
conditions[parseKey(k)] = where[k];
|
|
64
|
-
continue;
|
|
65
|
-
}
|
|
66
|
-
if (where[k] === undefined) {
|
|
67
|
-
conditions[parseKey(k)] = null;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
if (!think.isArray(where[k]) || !where[k][0]) {
|
|
71
|
-
continue;
|
|
72
|
-
}
|
|
73
|
-
const handler = where[k][0].toUpperCase();
|
|
74
|
-
|
|
75
|
-
switch (handler) {
|
|
76
|
-
case 'IN':
|
|
77
|
-
conditions[parseKey(k)] = where[k][1];
|
|
78
|
-
if (think.isArray(where[k][1])) {
|
|
79
|
-
_isArrayKeys.push(parseKey(k));
|
|
80
|
-
}
|
|
81
|
-
break;
|
|
82
|
-
case 'NOT IN':
|
|
83
|
-
/**
|
|
84
|
-
* deta base doesn't support not equal with multiple value query
|
|
85
|
-
* so we have to transfer it into equal with some value in most of scene
|
|
86
|
-
*/
|
|
87
|
-
if (Array.isArray(where[k][1]) && parseKey(k) === 'status') {
|
|
88
|
-
const STATUS = ['approved', 'waiting', 'spam'];
|
|
89
|
-
let val = STATUS.filter((s) => !where[k][1].includes(s));
|
|
90
|
-
|
|
91
|
-
if (val.length === 1) {
|
|
92
|
-
val = val[0];
|
|
93
|
-
}
|
|
94
|
-
conditions[parseKey(k)] = val;
|
|
95
|
-
}
|
|
96
|
-
conditions[parseKey(k) + '?ne'] = where[k][1];
|
|
97
|
-
break;
|
|
98
|
-
case 'LIKE': {
|
|
99
|
-
const first = where[k][1][0];
|
|
100
|
-
const last = where[k][1].slice(-1);
|
|
101
|
-
|
|
102
|
-
if (first === '%' && last === '%') {
|
|
103
|
-
conditions[parseKey(k) + '?contains'] = where[k][1].slice(1, -1);
|
|
104
|
-
} else if (first === '%') {
|
|
105
|
-
conditions[parseKey(k) + '?contains'] = where[k][1].slice(1);
|
|
106
|
-
} else if (last === '%') {
|
|
107
|
-
conditions[parseKey(k) + '?pfx'] = where[k][1].slice(0, -1);
|
|
108
|
-
}
|
|
109
|
-
break;
|
|
110
|
-
}
|
|
111
|
-
case '!=':
|
|
112
|
-
conditions[parseKey(k) + '?ne'] = where[k][1];
|
|
113
|
-
break;
|
|
114
|
-
case '>':
|
|
115
|
-
conditions[parseKey(k) + '?gt'] = where[k][1];
|
|
116
|
-
break;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
if (_isArrayKeys.length === 0) {
|
|
121
|
-
return conditions;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return this.complex(conditions, _isArrayKeys);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
where(where) {
|
|
128
|
-
const filter = this.parseWhere(where);
|
|
129
|
-
|
|
130
|
-
if (!where._complex) {
|
|
131
|
-
return filter;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
const filters = [];
|
|
135
|
-
|
|
136
|
-
for (const k in where._complex) {
|
|
137
|
-
if (k === '_logic') {
|
|
138
|
-
continue;
|
|
139
|
-
}
|
|
140
|
-
filters.push({
|
|
141
|
-
...this.parseWhere({ [k]: where._complex[k] }),
|
|
142
|
-
...filter,
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// just support OR logic for deta
|
|
147
|
-
return filters;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
async select(where, { limit, offset, field } = {}) {
|
|
151
|
-
const conditions = this.where(where);
|
|
152
|
-
|
|
153
|
-
if (think.isArray(conditions)) {
|
|
154
|
-
return Promise.all(
|
|
155
|
-
conditions.map((condition) =>
|
|
156
|
-
this.select(condition, { limit, offset, field }),
|
|
157
|
-
),
|
|
158
|
-
).then((data) => data.flat());
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
let data = [];
|
|
162
|
-
|
|
163
|
-
if (
|
|
164
|
-
think.isObject(conditions) &&
|
|
165
|
-
think.isString(conditions.key) &&
|
|
166
|
-
conditions.key
|
|
167
|
-
) {
|
|
168
|
-
/**
|
|
169
|
-
* deta base doesn't support fetch with key field query
|
|
170
|
-
* if you want query by key field
|
|
171
|
-
* you need use `get()` rather than `fetch()` method.
|
|
172
|
-
*/
|
|
173
|
-
const item = await this.instance.get(conditions.key);
|
|
174
|
-
|
|
175
|
-
if (item) data.push(item);
|
|
176
|
-
} else if (offset) {
|
|
177
|
-
/**
|
|
178
|
-
* deta base need last data key when pagination
|
|
179
|
-
* so we need fetch data list again and again
|
|
180
|
-
* because only that we can get last data key
|
|
181
|
-
*/
|
|
182
|
-
while (data.length < limit + offset) {
|
|
183
|
-
const lastData = data[data.length - 1];
|
|
184
|
-
const last = lastData ? lastData.key : undefined;
|
|
185
|
-
const { items } = await this.instance.fetch(conditions, {
|
|
186
|
-
limit,
|
|
187
|
-
last,
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
data = data.concat(items);
|
|
191
|
-
|
|
192
|
-
if (items.length < limit) {
|
|
193
|
-
break;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
data = data.slice(offset, offset + limit);
|
|
198
|
-
} else {
|
|
199
|
-
const { items } = await this.instance.fetch(conditions, {
|
|
200
|
-
limit: limit,
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
data = items || [];
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
data = data.map(({ key, ...cmt }) => ({
|
|
207
|
-
...cmt,
|
|
208
|
-
objectId: key,
|
|
209
|
-
}));
|
|
210
|
-
|
|
211
|
-
if (Array.isArray(field)) {
|
|
212
|
-
const fieldMap = new Set(field);
|
|
213
|
-
|
|
214
|
-
fieldMap.add('objectId');
|
|
215
|
-
data.forEach((item) => {
|
|
216
|
-
for (const key in item) {
|
|
217
|
-
if (!fieldMap.has(key)) {
|
|
218
|
-
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
219
|
-
delete item[key];
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
return data;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
async count(where = {}, { group } = {}) {
|
|
229
|
-
if (!group) {
|
|
230
|
-
const conditions = this.where(where);
|
|
231
|
-
|
|
232
|
-
if (think.isArray(conditions)) {
|
|
233
|
-
return Promise.all(
|
|
234
|
-
conditions.map((condition) => this.count(condition)),
|
|
235
|
-
).then((counts) => counts.reduce((a, b) => a + b, 0));
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
const { count } = await this.instance.fetch(conditions);
|
|
239
|
-
|
|
240
|
-
return count;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
const counts = [];
|
|
244
|
-
|
|
245
|
-
for (let i = 0; i < group.length; i++) {
|
|
246
|
-
const groupName = group[i];
|
|
247
|
-
|
|
248
|
-
if (!where._complex || !Array.isArray(where._complex[groupName])) {
|
|
249
|
-
continue;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
const groupFlatValue = {};
|
|
253
|
-
|
|
254
|
-
group.slice(0, i).forEach((group) => {
|
|
255
|
-
groupFlatValue[group] = null;
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
for (const item of where._complex[groupName][1]) {
|
|
259
|
-
const groupWhere = {
|
|
260
|
-
...where,
|
|
261
|
-
...groupFlatValue,
|
|
262
|
-
_complex: undefined,
|
|
263
|
-
[groupName]: item,
|
|
264
|
-
};
|
|
265
|
-
const num = await this.count(groupWhere);
|
|
266
|
-
|
|
267
|
-
counts.push({
|
|
268
|
-
...groupFlatValue,
|
|
269
|
-
[groupName]: item,
|
|
270
|
-
count: num,
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
return counts;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
async add(data) {
|
|
279
|
-
const uuid = await this.uuid();
|
|
280
|
-
const resp = await this.instance.put(data, uuid);
|
|
281
|
-
|
|
282
|
-
resp.objectId = resp.key;
|
|
283
|
-
delete resp.key;
|
|
284
|
-
|
|
285
|
-
return resp;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
async update(data, where) {
|
|
289
|
-
const items = await this.select(where);
|
|
290
|
-
|
|
291
|
-
return Promise.all(
|
|
292
|
-
items.map(async (item) => {
|
|
293
|
-
const updateData = typeof data === 'function' ? data(item) : data;
|
|
294
|
-
const nextData = { ...item, ...updateData };
|
|
295
|
-
|
|
296
|
-
await this.instance.put(nextData, item.objectId);
|
|
297
|
-
|
|
298
|
-
return nextData;
|
|
299
|
-
}),
|
|
300
|
-
);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
async delete(where) {
|
|
304
|
-
const items = await this.select(where);
|
|
305
|
-
|
|
306
|
-
return Promise.all(
|
|
307
|
-
items.map(({ objectId }) => this.instance.delete(objectId)),
|
|
308
|
-
);
|
|
309
|
-
}
|
|
310
|
-
};
|