@waline/vercel 1.26.3 → 1.26.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.
Files changed (60) hide show
  1. package/dist/404.html +39 -0
  2. package/dist/500.html +275 -0
  3. package/dist/index.js +58501 -0
  4. package/dist/package.json +54 -0
  5. package/dist/src/config/adapter.js +170 -0
  6. package/dist/src/config/config.js +134 -0
  7. package/dist/src/config/extend.js +38 -0
  8. package/dist/src/config/middleware.js +66 -0
  9. package/dist/src/config/router.js +1 -0
  10. package/dist/src/controller/article.js +91 -0
  11. package/dist/src/controller/comment.js +758 -0
  12. package/dist/src/controller/db.js +71 -0
  13. package/dist/src/controller/index.js +36 -0
  14. package/dist/src/controller/oauth.js +136 -0
  15. package/dist/src/controller/rest.js +60 -0
  16. package/dist/src/controller/token/2fa.js +66 -0
  17. package/dist/src/controller/token.js +75 -0
  18. package/dist/src/controller/user/password.js +52 -0
  19. package/dist/src/controller/user.js +289 -0
  20. package/dist/src/controller/verification.js +35 -0
  21. package/dist/src/extend/controller.js +25 -0
  22. package/dist/src/extend/think.js +84 -0
  23. package/dist/src/locales/en.json +19 -0
  24. package/dist/src/locales/index.js +12 -0
  25. package/dist/src/locales/zh-CN.json +19 -0
  26. package/dist/src/locales/zh-TW.json +19 -0
  27. package/dist/src/logic/article.js +27 -0
  28. package/dist/src/logic/base.js +164 -0
  29. package/dist/src/logic/comment.js +317 -0
  30. package/dist/src/logic/db.js +81 -0
  31. package/dist/src/logic/oauth.js +10 -0
  32. package/dist/src/logic/token/2fa.js +28 -0
  33. package/dist/src/logic/token.js +53 -0
  34. package/dist/src/logic/user/password.js +11 -0
  35. package/dist/src/logic/user.js +117 -0
  36. package/dist/src/middleware/dashboard.js +23 -0
  37. package/dist/src/middleware/version.js +6 -0
  38. package/dist/src/service/akismet.js +41 -0
  39. package/dist/src/service/avatar.js +35 -0
  40. package/dist/src/service/markdown/highlight.js +32 -0
  41. package/dist/src/service/markdown/index.js +63 -0
  42. package/dist/src/service/markdown/katex.js +49 -0
  43. package/dist/src/service/markdown/mathCommon.js +156 -0
  44. package/dist/src/service/markdown/mathjax.js +78 -0
  45. package/dist/src/service/markdown/utils.js +11 -0
  46. package/dist/src/service/markdown/xss.js +44 -0
  47. package/dist/src/service/notify.js +537 -0
  48. package/dist/src/service/storage/base.js +31 -0
  49. package/dist/src/service/storage/cloudbase.js +221 -0
  50. package/dist/src/service/storage/deta.js +307 -0
  51. package/dist/src/service/storage/github.js +377 -0
  52. package/dist/src/service/storage/leancloud.js +430 -0
  53. package/dist/src/service/storage/mongodb.js +179 -0
  54. package/dist/src/service/storage/mysql.js +123 -0
  55. package/dist/src/service/storage/postgresql.js +84 -0
  56. package/dist/src/service/storage/sqlite.js +11 -0
  57. package/dist/src/service/storage/tidb.js +3 -0
  58. package/package.json +1 -1
  59. package/src/controller/comment.js +1 -2
  60. package/src/extend/think.js +19 -0
@@ -0,0 +1,377 @@
1
+ const { parseString, writeToString } = require('fast-csv');
2
+ const fetch = require('node-fetch');
3
+ const path = require('path');
4
+ const Base = require('./base');
5
+
6
+ const CSV_HEADERS = {
7
+ Comment: [
8
+ 'objectId',
9
+ 'user_id',
10
+ 'comment',
11
+ 'insertedAt',
12
+ 'ip',
13
+ 'link',
14
+ 'mail',
15
+ 'nick',
16
+ 'pid',
17
+ 'rid',
18
+ 'status',
19
+ 'ua',
20
+ 'url',
21
+ 'createdAt',
22
+ 'updatedAt',
23
+ ],
24
+ Counter: ['objectId', 'time', 'url', 'createdAt', 'updatedAt'],
25
+ Users: [
26
+ 'objectId',
27
+ 'display_name',
28
+ 'email',
29
+ 'password',
30
+ 'type',
31
+ 'url',
32
+ 'avatar',
33
+ 'label',
34
+ 'github',
35
+ 'twitter',
36
+ 'facebook',
37
+ 'google',
38
+ 'weibo',
39
+ 'qq',
40
+ 'createdAt',
41
+ 'updatedAt',
42
+ ],
43
+ };
44
+
45
+ class Github {
46
+ constructor(repo, token) {
47
+ this.token = token;
48
+ this.repo = repo;
49
+ }
50
+
51
+ // content api can only get file < 1MB
52
+ async get(filename) {
53
+ const resp = await fetch(
54
+ 'https://api.github.com/repos/' +
55
+ path.join(this.repo, 'contents', filename),
56
+ {
57
+ headers: {
58
+ accept: 'application/vnd.github.v3+json',
59
+ authorization: 'token ' + this.token,
60
+ 'user-agent': 'Waline',
61
+ },
62
+ }
63
+ )
64
+ .then((resp) => resp.json())
65
+ .catch((e) => {
66
+ const isTooLarge = e.message.includes('"too_large"');
67
+
68
+ if (!isTooLarge) {
69
+ throw e;
70
+ }
71
+
72
+ return this.getLargeFile(filename);
73
+ });
74
+
75
+ return {
76
+ data: Buffer.from(resp.content, 'base64').toString('utf-8'),
77
+ sha: resp.sha,
78
+ };
79
+ }
80
+
81
+ // blob api can get file larger than 1MB
82
+ async getLargeFile(filename) {
83
+ const { tree } = await fetch(
84
+ 'https://api.github.com/repos/' +
85
+ path.join(this.repo, 'git/trees/HEAD') +
86
+ '?recursive=1',
87
+ {
88
+ headers: {
89
+ accept: 'application/vnd.github.v3+json',
90
+ authorization: 'token ' + this.token,
91
+ 'user-agent': 'Waline',
92
+ },
93
+ }
94
+ ).then((resp) => resp.json());
95
+
96
+ const file = tree.find(({ path }) => path === filename);
97
+
98
+ if (!file) {
99
+ const error = new Error('NOT FOUND');
100
+
101
+ error.statusCode = 404;
102
+ throw error;
103
+ }
104
+
105
+ return fetch(file.url, {
106
+ headers: {
107
+ accept: 'application/vnd.github.v3+json',
108
+ authorization: 'token ' + this.token,
109
+ 'user-agent': 'Waline',
110
+ },
111
+ }).then((resp) => resp.json());
112
+ }
113
+
114
+ async set(filename, content, { sha }) {
115
+ return fetch(
116
+ 'https://api.github.com/repos/' +
117
+ path.join(this.repo, 'contents', filename),
118
+ {
119
+ method: 'PUT',
120
+ headers: {
121
+ accept: 'application/vnd.github.v3+json',
122
+ authorization: 'token ' + this.token,
123
+ 'user-agent': 'Waline',
124
+ },
125
+ body: JSON.stringify({
126
+ sha,
127
+ message: 'feat(waline): update comment data',
128
+ content: Buffer.from(content, 'utf-8').toString('base64'),
129
+ }),
130
+ }
131
+ );
132
+ }
133
+ }
134
+
135
+ module.exports = class extends Base {
136
+ constructor(tableName) {
137
+ super();
138
+ this.tableName = tableName;
139
+
140
+ const { GITHUB_TOKEN, GITHUB_REPO, GITHUB_PATH } = process.env;
141
+
142
+ this.git = new Github(GITHUB_REPO, GITHUB_TOKEN);
143
+ this.basePath = GITHUB_PATH;
144
+ }
145
+
146
+ async collection(tableName) {
147
+ const filename = path.join(this.basePath, tableName + '.csv');
148
+ const file = await this.git.get(filename).catch((e) => {
149
+ if (e.statusCode === 404) {
150
+ return '';
151
+ }
152
+ throw e;
153
+ });
154
+
155
+ return new Promise((resolve, reject) => {
156
+ const data = [];
157
+
158
+ data.sha = file.sha;
159
+
160
+ return parseString(file.data, {
161
+ headers: file ? true : CSV_HEADERS[tableName],
162
+ })
163
+ .on('error', reject)
164
+ .on('data', (row) => data.push(row))
165
+ .on('end', () => resolve(data));
166
+ });
167
+ }
168
+
169
+ async save(tableName, data, sha) {
170
+ const filename = path.join(this.basePath, tableName + '.csv');
171
+ const csv = await writeToString(data, {
172
+ headers: sha ? true : CSV_HEADERS[tableName],
173
+ writeHeaders: true,
174
+ });
175
+
176
+ return this.git.set(filename, csv, { sha });
177
+ }
178
+
179
+ parseWhere(where) {
180
+ const _where = [];
181
+
182
+ if (think.isEmpty(where)) {
183
+ return _where;
184
+ }
185
+
186
+ const filters = [];
187
+
188
+ for (let k in where) {
189
+ if (k === '_complex') {
190
+ continue;
191
+ }
192
+
193
+ if (k === 'objectId') {
194
+ filters.push((item) => item.id === where[k]);
195
+ continue;
196
+ }
197
+ if (think.isString(where[k])) {
198
+ filters.push((item) => item[k] === where[k]);
199
+ continue;
200
+ }
201
+ if (where[k] === undefined) {
202
+ filters.push((item) => item[k] === null || item[k] === undefined);
203
+ }
204
+ if (!Array.isArray(where[k]) || !where[k][0]) {
205
+ continue;
206
+ }
207
+
208
+ const handler = where[k][0].toUpperCase();
209
+
210
+ switch (handler) {
211
+ case 'IN':
212
+ filters.push((item) => where[k][1].includes(item[k]));
213
+ break;
214
+ case 'NOT IN':
215
+ filters.push((item) => !where[k][1].includes(item[k]));
216
+ break;
217
+ case 'LIKE': {
218
+ const first = where[k][1][0];
219
+ const last = where[k][1].slice(-1);
220
+ let reg;
221
+
222
+ if (first === '%' && last === '%') {
223
+ reg = new RegExp(where[k][1].slice(1, -1));
224
+ } else if (first === '%') {
225
+ reg = new RegExp(where[k][1].slice(1) + '$');
226
+ } else if (last === '%') {
227
+ reg = new RegExp('^' + where[k][1].slice(0, -1));
228
+ }
229
+ filters.push((item) => reg.test(item[k]));
230
+ break;
231
+ }
232
+ case '!=':
233
+ filters.push((item) => item[k] !== where[k][1]);
234
+ break;
235
+ case '>':
236
+ filters.push((item) => item[k] >= where[k][1]);
237
+ break;
238
+ }
239
+ }
240
+
241
+ return filters;
242
+ }
243
+
244
+ where(data, where) {
245
+ const filter = this.parseWhere(where);
246
+
247
+ if (!where._complex) {
248
+ return data.filter((item) => filter.every((fn) => fn(item)));
249
+ }
250
+
251
+ const logicMap = {
252
+ and: Array.prototype.every,
253
+ or: Array.prototype.some,
254
+ };
255
+ const filters = [];
256
+
257
+ for (const k in where._complex) {
258
+ if (k === '_logic') {
259
+ continue;
260
+ }
261
+
262
+ filters.push([...filter, ...this.parseWhere({ [k]: where._complex[k] })]);
263
+ }
264
+
265
+ const logicFn = logicMap[where._complex._logic];
266
+
267
+ return data.filter((item) =>
268
+ logicFn.call(filters, (filter) => filter.every((fn) => fn(item)))
269
+ );
270
+ }
271
+
272
+ async select(where, { desc, limit, offset, field } = {}) {
273
+ const instance = await this.collection(this.tableName);
274
+ let data = this.where(instance, where);
275
+
276
+ if (desc) {
277
+ data.sort((a, b) => {
278
+ if (['insertedAt', 'createdAt', 'updatedAt'].includes(desc)) {
279
+ const aTime = new Date(a[desc]).getTime();
280
+ const bTime = new Date(b[desc]).getTime();
281
+
282
+ return bTime - aTime;
283
+ }
284
+
285
+ return a[desc] - b[desc];
286
+ });
287
+ }
288
+
289
+ data = data.slice(limit || 0, offset || data.length);
290
+ if (field) {
291
+ field.push('id');
292
+ const fieldObj = {};
293
+
294
+ field.forEach((f) => (fieldObj[f] = true));
295
+ data = data.map((item) => {
296
+ const ret = {};
297
+
298
+ for (const k in item) {
299
+ if (fieldObj[k]) {
300
+ ret[k] = item[k];
301
+ }
302
+ }
303
+
304
+ return ret;
305
+ });
306
+ }
307
+
308
+ return data.map(({ id, ...cmt }) => ({ ...cmt, objectId: id }));
309
+ }
310
+
311
+ async count(where = {}, { group } = {}) {
312
+ const instance = await this.collection(this.tableName);
313
+ const data = this.where(instance, where);
314
+
315
+ if (!group) {
316
+ return data.length;
317
+ }
318
+
319
+ const counts = {};
320
+
321
+ for (let i = 0; i < data.length; i++) {
322
+ const key = group.map((field) => data[field]).join();
323
+
324
+ if (!counts[key]) {
325
+ counts[key] = { count: 0 };
326
+ group.forEach((field) => {
327
+ counts[key][field] = data[field];
328
+ });
329
+ }
330
+ counts[key].count += 1;
331
+ }
332
+
333
+ return Object.keys(counts);
334
+ }
335
+
336
+ async add(
337
+ data
338
+ // { access: { read = true, write = true } = { read: true, write: true } } = {}
339
+ ) {
340
+ const instance = await this.collection(this.tableName);
341
+ const id = Math.random().toString(36).substr(2, 15);
342
+
343
+ instance.push({ ...data, id });
344
+ await this.save(this.tableName, instance, instance.sha);
345
+
346
+ return { ...data, objectId: id };
347
+ }
348
+
349
+ async update(data, where) {
350
+ delete data.objectId;
351
+
352
+ const instance = await this.collection(this.tableName);
353
+ const list = this.where(instance, where);
354
+
355
+ list.forEach((item) => {
356
+ if (typeof data === 'function') {
357
+ data(item);
358
+ } else {
359
+ for (const k in data) {
360
+ item[k] = data[k];
361
+ }
362
+ }
363
+ });
364
+ await this.save(this.tableName, instance, instance.sha);
365
+
366
+ return list;
367
+ }
368
+
369
+ async delete(where) {
370
+ const instance = await this.collection(this.tableName);
371
+ const deleteData = this.where(instance, where);
372
+ const deleteId = deleteData.map(({ id }) => id);
373
+ const data = instance.filter((data) => !deleteId.includes(data.id));
374
+
375
+ await this.save(this.tableName, data, instance.sha);
376
+ }
377
+ };