@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.
- package/dist/404.html +39 -0
- package/dist/500.html +275 -0
- package/dist/index.js +58501 -0
- package/dist/package.json +54 -0
- package/dist/src/config/adapter.js +170 -0
- package/dist/src/config/config.js +134 -0
- package/dist/src/config/extend.js +38 -0
- package/dist/src/config/middleware.js +66 -0
- package/dist/src/config/router.js +1 -0
- package/dist/src/controller/article.js +91 -0
- package/dist/src/controller/comment.js +758 -0
- package/dist/src/controller/db.js +71 -0
- package/dist/src/controller/index.js +36 -0
- package/dist/src/controller/oauth.js +136 -0
- package/dist/src/controller/rest.js +60 -0
- package/dist/src/controller/token/2fa.js +66 -0
- package/dist/src/controller/token.js +75 -0
- package/dist/src/controller/user/password.js +52 -0
- package/dist/src/controller/user.js +289 -0
- package/dist/src/controller/verification.js +35 -0
- package/dist/src/extend/controller.js +25 -0
- package/dist/src/extend/think.js +84 -0
- package/dist/src/locales/en.json +19 -0
- package/dist/src/locales/index.js +12 -0
- package/dist/src/locales/zh-CN.json +19 -0
- package/dist/src/locales/zh-TW.json +19 -0
- package/dist/src/logic/article.js +27 -0
- package/dist/src/logic/base.js +164 -0
- package/dist/src/logic/comment.js +317 -0
- package/dist/src/logic/db.js +81 -0
- package/dist/src/logic/oauth.js +10 -0
- package/dist/src/logic/token/2fa.js +28 -0
- package/dist/src/logic/token.js +53 -0
- package/dist/src/logic/user/password.js +11 -0
- package/dist/src/logic/user.js +117 -0
- package/dist/src/middleware/dashboard.js +23 -0
- package/dist/src/middleware/version.js +6 -0
- package/dist/src/service/akismet.js +41 -0
- package/dist/src/service/avatar.js +35 -0
- package/dist/src/service/markdown/highlight.js +32 -0
- package/dist/src/service/markdown/index.js +63 -0
- package/dist/src/service/markdown/katex.js +49 -0
- package/dist/src/service/markdown/mathCommon.js +156 -0
- package/dist/src/service/markdown/mathjax.js +78 -0
- package/dist/src/service/markdown/utils.js +11 -0
- package/dist/src/service/markdown/xss.js +44 -0
- package/dist/src/service/notify.js +537 -0
- package/dist/src/service/storage/base.js +31 -0
- package/dist/src/service/storage/cloudbase.js +221 -0
- package/dist/src/service/storage/deta.js +307 -0
- package/dist/src/service/storage/github.js +377 -0
- package/dist/src/service/storage/leancloud.js +430 -0
- package/dist/src/service/storage/mongodb.js +179 -0
- package/dist/src/service/storage/mysql.js +123 -0
- package/dist/src/service/storage/postgresql.js +84 -0
- package/dist/src/service/storage/sqlite.js +11 -0
- package/dist/src/service/storage/tidb.js +3 -0
- package/package.json +1 -1
- package/src/controller/comment.js +1 -2
- package/src/extend/think.js +19 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@waline/vercel",
|
|
3
|
+
"version": "1.26.3-deta-alpha.0",
|
|
4
|
+
"description": "vercel server for waline comment system",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"waline",
|
|
7
|
+
"vercel",
|
|
8
|
+
"comment",
|
|
9
|
+
"blog"
|
|
10
|
+
],
|
|
11
|
+
"repository": {
|
|
12
|
+
"url": "https://github.com/walinejs/waline",
|
|
13
|
+
"directory": "packages/server"
|
|
14
|
+
},
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"author": "lizheming <i@imnerd.org>",
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@cloudbase/node-sdk": "2.9.1",
|
|
19
|
+
"@koa/cors": "4.0.0",
|
|
20
|
+
"akismet": "2.0.7",
|
|
21
|
+
"deta": "1.1.0",
|
|
22
|
+
"dompurify": "3.0.0",
|
|
23
|
+
"dy-node-ip2region": "1.0.1",
|
|
24
|
+
"fast-csv": "4.3.6",
|
|
25
|
+
"form-data": "4.0.0",
|
|
26
|
+
"jsdom": "21.1.0",
|
|
27
|
+
"jsonwebtoken": "9.0.0",
|
|
28
|
+
"katex": "0.16.4",
|
|
29
|
+
"leancloud-storage": "4.14.0",
|
|
30
|
+
"markdown-it": "13.0.1",
|
|
31
|
+
"markdown-it-emoji": "2.0.2",
|
|
32
|
+
"markdown-it-sub": "1.0.0",
|
|
33
|
+
"markdown-it-sup": "1.0.0",
|
|
34
|
+
"mathjax-full": "3.2.2",
|
|
35
|
+
"node-fetch": "2.6.9",
|
|
36
|
+
"nodemailer": "6.9.1",
|
|
37
|
+
"nunjucks": "3.2.3",
|
|
38
|
+
"phpass": "0.1.1",
|
|
39
|
+
"prismjs": "1.29.0",
|
|
40
|
+
"speakeasy": "2.0.0",
|
|
41
|
+
"think-helper": "1.1.4",
|
|
42
|
+
"think-logger3": "1.3.1",
|
|
43
|
+
"think-model": "1.5.4",
|
|
44
|
+
"think-model-mysql": "1.1.7",
|
|
45
|
+
"think-model-mysql2": "^2.0.0",
|
|
46
|
+
"think-model-postgresql": "1.1.7",
|
|
47
|
+
"think-mongo": "2.2.1",
|
|
48
|
+
"think-router-rest": "1.0.5",
|
|
49
|
+
"ua-parser-js": "1.0.33"
|
|
50
|
+
},
|
|
51
|
+
"engines": {
|
|
52
|
+
"node": ">=14"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
const { Console } = require('think-logger3');
|
|
2
|
+
const Mysql = require('think-model-mysql');
|
|
3
|
+
const Mysql2 = require('think-model-mysql2');
|
|
4
|
+
const Postgresql = require('think-model-postgresql');
|
|
5
|
+
|
|
6
|
+
let Sqlite = class {};
|
|
7
|
+
|
|
8
|
+
try {
|
|
9
|
+
Sqlite = require('think-model-sqlite');
|
|
10
|
+
} catch (err) {
|
|
11
|
+
console.log(err);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const {
|
|
15
|
+
MYSQL_HOST,
|
|
16
|
+
MYSQL_PORT,
|
|
17
|
+
MYSQL_DB,
|
|
18
|
+
MYSQL_USER,
|
|
19
|
+
MYSQL_PASSWORD,
|
|
20
|
+
MYSQL_PREFIX,
|
|
21
|
+
MYSQL_CHARSET,
|
|
22
|
+
MYSQL_SSL,
|
|
23
|
+
TIDB_HOST,
|
|
24
|
+
TIDB_PORT,
|
|
25
|
+
TIDB_DB,
|
|
26
|
+
TIDB_USER,
|
|
27
|
+
TIDB_PASSWORD,
|
|
28
|
+
TIDB_PREFIX,
|
|
29
|
+
TIDB_CHARSET,
|
|
30
|
+
SQLITE_PATH,
|
|
31
|
+
SQLITE_DB,
|
|
32
|
+
SQLITE_PREFIX,
|
|
33
|
+
PG_DB,
|
|
34
|
+
PG_HOST,
|
|
35
|
+
PG_PASSWORD,
|
|
36
|
+
PG_PORT,
|
|
37
|
+
PG_PREFIX,
|
|
38
|
+
PG_USER,
|
|
39
|
+
PG_SSL,
|
|
40
|
+
MONGO_AUTHSOURCE,
|
|
41
|
+
MONGO_DB,
|
|
42
|
+
MONGO_HOST,
|
|
43
|
+
MONGO_PASSWORD,
|
|
44
|
+
MONGO_PORT,
|
|
45
|
+
MONGO_REPLICASET,
|
|
46
|
+
MONGO_USER,
|
|
47
|
+
} = process.env;
|
|
48
|
+
|
|
49
|
+
let type = 'common';
|
|
50
|
+
const mongoOpt = {};
|
|
51
|
+
|
|
52
|
+
if (MONGO_REPLICASET) mongoOpt.replicaSet = MONGO_REPLICASET;
|
|
53
|
+
if (MONGO_AUTHSOURCE) mongoOpt.authSource = MONGO_AUTHSOURCE;
|
|
54
|
+
|
|
55
|
+
if (MONGO_DB) {
|
|
56
|
+
type = 'mongo';
|
|
57
|
+
for (const envKeys in process.env) {
|
|
58
|
+
if (/MONGO_OPT_/.test(envKeys)) {
|
|
59
|
+
const key = envKeys
|
|
60
|
+
.slice(10)
|
|
61
|
+
.toLocaleLowerCase()
|
|
62
|
+
.replace(/_([a-z])/g, (_, b) => b.toUpperCase());
|
|
63
|
+
|
|
64
|
+
mongoOpt[key] = process.env[envKeys];
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
} else if (PG_DB) {
|
|
68
|
+
type = 'postgresql';
|
|
69
|
+
} else if (SQLITE_PATH) {
|
|
70
|
+
type = 'sqlite';
|
|
71
|
+
} else if (MYSQL_DB) {
|
|
72
|
+
type = 'mysql';
|
|
73
|
+
} else if (TIDB_DB) {
|
|
74
|
+
type = 'tidb';
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
exports.model = {
|
|
78
|
+
type,
|
|
79
|
+
common: {
|
|
80
|
+
logSql: true,
|
|
81
|
+
logger: (msg) => think.logger.info(msg),
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
mongo: {
|
|
85
|
+
host: MONGO_HOST
|
|
86
|
+
? MONGO_HOST.startsWith('[')
|
|
87
|
+
? JSON.parse(MONGO_HOST)
|
|
88
|
+
: MONGO_HOST
|
|
89
|
+
: '127.0.0.1',
|
|
90
|
+
port: MONGO_PORT
|
|
91
|
+
? MONGO_PORT.startsWith('[')
|
|
92
|
+
? JSON.parse(MONGO_PORT)
|
|
93
|
+
: MONGO_PORT
|
|
94
|
+
: 27017,
|
|
95
|
+
user: MONGO_USER,
|
|
96
|
+
password: MONGO_PASSWORD,
|
|
97
|
+
database: MONGO_DB,
|
|
98
|
+
options: mongoOpt,
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
postgresql: {
|
|
102
|
+
handle: Postgresql,
|
|
103
|
+
user: PG_USER,
|
|
104
|
+
password: PG_PASSWORD,
|
|
105
|
+
database: PG_DB,
|
|
106
|
+
host: PG_HOST || '127.0.0.1',
|
|
107
|
+
port: PG_PORT || '3211',
|
|
108
|
+
connectionLimit: 1,
|
|
109
|
+
prefix: PG_PREFIX || 'wl_',
|
|
110
|
+
ssl:
|
|
111
|
+
PG_SSL == 'true'
|
|
112
|
+
? {
|
|
113
|
+
rejectUnauthorized: false,
|
|
114
|
+
}
|
|
115
|
+
: null,
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
sqlite: {
|
|
119
|
+
handle: Sqlite,
|
|
120
|
+
path: SQLITE_PATH,
|
|
121
|
+
database: SQLITE_DB || 'waline',
|
|
122
|
+
connectionLimit: 1,
|
|
123
|
+
prefix: SQLITE_PREFIX || 'wl_',
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
mysql: {
|
|
127
|
+
handle: Mysql,
|
|
128
|
+
dateStrings: true,
|
|
129
|
+
host: MYSQL_HOST || '127.0.0.1',
|
|
130
|
+
port: MYSQL_PORT || '3306',
|
|
131
|
+
database: MYSQL_DB,
|
|
132
|
+
user: MYSQL_USER,
|
|
133
|
+
password: MYSQL_PASSWORD,
|
|
134
|
+
prefix: MYSQL_PREFIX || 'wl_',
|
|
135
|
+
charset: MYSQL_CHARSET || 'utf8mb4',
|
|
136
|
+
ssl:
|
|
137
|
+
MYSQL_SSL === 'true'
|
|
138
|
+
? {
|
|
139
|
+
rejectUnauthorized: false,
|
|
140
|
+
}
|
|
141
|
+
: null,
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
tidb: {
|
|
145
|
+
handle: Mysql2,
|
|
146
|
+
dateStrings: true,
|
|
147
|
+
host: TIDB_HOST || '127.0.0.1',
|
|
148
|
+
port: TIDB_PORT || '4000',
|
|
149
|
+
database: TIDB_DB,
|
|
150
|
+
user: TIDB_USER,
|
|
151
|
+
password: TIDB_PASSWORD,
|
|
152
|
+
prefix: TIDB_PREFIX || 'wl_',
|
|
153
|
+
charset: TIDB_CHARSET || 'utf8mb4',
|
|
154
|
+
ssl: {
|
|
155
|
+
minVersion: 'TLSv1.2',
|
|
156
|
+
rejectUnauthorized: true,
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* logger adapter config
|
|
163
|
+
* @type {Object}
|
|
164
|
+
*/
|
|
165
|
+
exports.logger = {
|
|
166
|
+
type: 'console',
|
|
167
|
+
console: {
|
|
168
|
+
handle: Console,
|
|
169
|
+
},
|
|
170
|
+
};
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
const {
|
|
2
|
+
JWT_TOKEN,
|
|
3
|
+
LEAN_KEY,
|
|
4
|
+
MYSQL_DB,
|
|
5
|
+
MYSQL_PASSWORD,
|
|
6
|
+
TIDB_DB,
|
|
7
|
+
TIDB_PASSWORD,
|
|
8
|
+
SQLITE_PATH,
|
|
9
|
+
PG_DB,
|
|
10
|
+
PG_PASSWORD,
|
|
11
|
+
MONGO_DB,
|
|
12
|
+
MONGO_PASSWORD,
|
|
13
|
+
FORBIDDEN_WORDS,
|
|
14
|
+
TCB_ENV,
|
|
15
|
+
TENCENTCLOUD_SECRETKEY,
|
|
16
|
+
TCB_KEY,
|
|
17
|
+
SECURE_DOMAINS,
|
|
18
|
+
DISABLE_USERAGENT,
|
|
19
|
+
DISABLE_REGION,
|
|
20
|
+
AVATAR_PROXY,
|
|
21
|
+
GITHUB_TOKEN,
|
|
22
|
+
DETA_PROJECT_KEY,
|
|
23
|
+
OAUTH_URL,
|
|
24
|
+
|
|
25
|
+
MARKDOWN_CONFIG = '{}',
|
|
26
|
+
MARKDOWN_HIGHLIGHT,
|
|
27
|
+
MARKDOWN_EMOJI,
|
|
28
|
+
MARKDOWN_SUB,
|
|
29
|
+
MARKDOWN_SUP,
|
|
30
|
+
// mathjax will be the default option for tex
|
|
31
|
+
MARKDOWN_TEX = 'mathjax',
|
|
32
|
+
MARKDOWN_MATHJAX = '{}',
|
|
33
|
+
MARKDOWN_KATEX = '{}',
|
|
34
|
+
|
|
35
|
+
MAIL_SUBJECT,
|
|
36
|
+
MAIL_TEMPLATE,
|
|
37
|
+
MAIL_SUBJECT_ADMIN,
|
|
38
|
+
MAIL_TEMPLATE_ADMIN,
|
|
39
|
+
QQ_TEMPLATE,
|
|
40
|
+
TG_TEMPLATE,
|
|
41
|
+
WX_TEMPLATE,
|
|
42
|
+
DISCORD_TEMPLATE,
|
|
43
|
+
LARK_TEMPLATE,
|
|
44
|
+
|
|
45
|
+
LEVELS,
|
|
46
|
+
} = process.env;
|
|
47
|
+
|
|
48
|
+
let storage = 'leancloud';
|
|
49
|
+
let jwtKey = JWT_TOKEN || LEAN_KEY;
|
|
50
|
+
|
|
51
|
+
if (LEAN_KEY) {
|
|
52
|
+
storage = 'leancloud';
|
|
53
|
+
} else if (MONGO_DB) {
|
|
54
|
+
storage = 'mongodb';
|
|
55
|
+
jwtKey = jwtKey || MONGO_PASSWORD;
|
|
56
|
+
} else if (PG_DB) {
|
|
57
|
+
storage = 'postgresql';
|
|
58
|
+
jwtKey = jwtKey || PG_PASSWORD;
|
|
59
|
+
} else if (SQLITE_PATH) {
|
|
60
|
+
storage = 'sqlite';
|
|
61
|
+
} else if (MYSQL_DB) {
|
|
62
|
+
storage = 'mysql';
|
|
63
|
+
jwtKey = jwtKey || MYSQL_PASSWORD;
|
|
64
|
+
} else if (TIDB_DB) {
|
|
65
|
+
storage = 'tidb';
|
|
66
|
+
jwtKey = jwtKey || TIDB_PASSWORD;
|
|
67
|
+
} else if (GITHUB_TOKEN) {
|
|
68
|
+
storage = 'github';
|
|
69
|
+
jwtKey = jwtKey || GITHUB_TOKEN;
|
|
70
|
+
} else if (think.env === 'cloudbase' || TCB_ENV) {
|
|
71
|
+
storage = 'cloudbase';
|
|
72
|
+
jwtKey = jwtKey || TENCENTCLOUD_SECRETKEY || TCB_KEY || TCB_ENV;
|
|
73
|
+
} else if (DETA_PROJECT_KEY) {
|
|
74
|
+
storage = 'deta';
|
|
75
|
+
jwtKey = jwtKey || DETA_PROJECT_KEY;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (think.env === 'cloudbase' && storage === 'sqlite') {
|
|
79
|
+
throw new Error("You can't use SQLite in CloudBase platform.");
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const forbiddenWords = FORBIDDEN_WORDS ? FORBIDDEN_WORDS.split(/\s*,\s*/) : [];
|
|
83
|
+
|
|
84
|
+
const isFalse = (content) =>
|
|
85
|
+
content && ['0', 'false'].includes(content.toLowerCase());
|
|
86
|
+
|
|
87
|
+
const markdown = {
|
|
88
|
+
config: JSON.parse(MARKDOWN_CONFIG),
|
|
89
|
+
plugin: {
|
|
90
|
+
emoji: !isFalse(MARKDOWN_EMOJI),
|
|
91
|
+
sub: !isFalse(MARKDOWN_SUB),
|
|
92
|
+
sup: !isFalse(MARKDOWN_SUP),
|
|
93
|
+
tex: isFalse(MARKDOWN_TEX) ? false : MARKDOWN_TEX,
|
|
94
|
+
mathjax: JSON.parse(MARKDOWN_MATHJAX),
|
|
95
|
+
katex: JSON.parse(MARKDOWN_KATEX),
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
if (isFalse(MARKDOWN_HIGHLIGHT)) markdown.config.highlight = false;
|
|
100
|
+
|
|
101
|
+
let avatarProxy = '';
|
|
102
|
+
|
|
103
|
+
if (AVATAR_PROXY) {
|
|
104
|
+
avatarProxy = !isFalse(AVATAR_PROXY) ? AVATAR_PROXY : '';
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const oauthUrl = OAUTH_URL || 'https://oauth.lithub.cc';
|
|
108
|
+
|
|
109
|
+
module.exports = {
|
|
110
|
+
workers: 1,
|
|
111
|
+
storage,
|
|
112
|
+
jwtKey,
|
|
113
|
+
forbiddenWords,
|
|
114
|
+
disallowIPList: [],
|
|
115
|
+
secureDomains: SECURE_DOMAINS ? SECURE_DOMAINS.split(/\s*,\s*/) : undefined,
|
|
116
|
+
disableUserAgent: DISABLE_USERAGENT && !isFalse(DISABLE_USERAGENT),
|
|
117
|
+
disableRegion: DISABLE_REGION && !isFalse(DISABLE_REGION),
|
|
118
|
+
levels:
|
|
119
|
+
!LEVELS || isFalse(LEVELS)
|
|
120
|
+
? false
|
|
121
|
+
: LEVELS.split(/\s*,\s*/).map((v) => Number(v)),
|
|
122
|
+
avatarProxy,
|
|
123
|
+
oauthUrl,
|
|
124
|
+
markdown,
|
|
125
|
+
mailSubject: MAIL_SUBJECT,
|
|
126
|
+
mailTemplate: MAIL_TEMPLATE,
|
|
127
|
+
mailSubjectAdmin: MAIL_SUBJECT_ADMIN,
|
|
128
|
+
mailTemplateAdmin: MAIL_TEMPLATE_ADMIN,
|
|
129
|
+
QQTemplate: QQ_TEMPLATE,
|
|
130
|
+
TGTemplate: TG_TEMPLATE,
|
|
131
|
+
WXTemplate: WX_TEMPLATE,
|
|
132
|
+
DiscordTemplate: DISCORD_TEMPLATE,
|
|
133
|
+
LarkTemplate: LARK_TEMPLATE,
|
|
134
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const fetch = require('node-fetch');
|
|
2
|
+
const Model = require('think-model');
|
|
3
|
+
const Mongo = require('think-mongo');
|
|
4
|
+
|
|
5
|
+
module.exports = [
|
|
6
|
+
Model(think.app),
|
|
7
|
+
Mongo(think.app),
|
|
8
|
+
{
|
|
9
|
+
context: {
|
|
10
|
+
get serverURL() {
|
|
11
|
+
const { SERVER_URL } = process.env;
|
|
12
|
+
|
|
13
|
+
if (SERVER_URL) {
|
|
14
|
+
return SERVER_URL;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const { protocol, host } = this;
|
|
18
|
+
|
|
19
|
+
return `${protocol}://${host}`;
|
|
20
|
+
},
|
|
21
|
+
async webhook(type, data) {
|
|
22
|
+
const { WEBHOOK } = process.env;
|
|
23
|
+
|
|
24
|
+
if (!WEBHOOK) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return fetch(WEBHOOK, {
|
|
29
|
+
method: 'POST',
|
|
30
|
+
headers: {
|
|
31
|
+
'content-type': 'application/json',
|
|
32
|
+
},
|
|
33
|
+
body: JSON.stringify({ type, data }),
|
|
34
|
+
}).then((resp) => resp.json());
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
];
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
const cors = require('@koa/cors');
|
|
2
|
+
const routerREST = require('think-router-rest');
|
|
3
|
+
const isDev = think.env === 'development';
|
|
4
|
+
const isTcb = think.env === 'cloudbase';
|
|
5
|
+
const isDeta = think.env === 'deta' || process.env.DETA_RUNTIME === 'true';
|
|
6
|
+
const isAliyunFC =
|
|
7
|
+
think.env === 'aliyun-fc' || Boolean(process.env.FC_RUNTIME_VERSION);
|
|
8
|
+
|
|
9
|
+
module.exports = [
|
|
10
|
+
{
|
|
11
|
+
handle: 'dashboard',
|
|
12
|
+
match: /^\/ui/,
|
|
13
|
+
},
|
|
14
|
+
|
|
15
|
+
{
|
|
16
|
+
handle: 'meta',
|
|
17
|
+
options: {
|
|
18
|
+
logRequest: isDev,
|
|
19
|
+
sendResponseTime: isDev,
|
|
20
|
+
requestTimeoutCallback: isTcb || isDeta || isAliyunFC ? false : () => {},
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
{
|
|
25
|
+
handle: 'version',
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
{ handle: cors },
|
|
29
|
+
|
|
30
|
+
{
|
|
31
|
+
handle: 'trace',
|
|
32
|
+
enable: !think.isCli,
|
|
33
|
+
options: {
|
|
34
|
+
debug: true,
|
|
35
|
+
contentType: () => 'json',
|
|
36
|
+
error(err, ctx) {
|
|
37
|
+
if (/favicon.ico$/.test(ctx.url)) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (think.isPrevent(err)) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
console.error(err);
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
{
|
|
50
|
+
handle: 'payload',
|
|
51
|
+
options: {
|
|
52
|
+
keepExtensions: true,
|
|
53
|
+
limit: '5mb',
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
{
|
|
58
|
+
handle: 'router',
|
|
59
|
+
options: {},
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
{ handle: routerREST },
|
|
63
|
+
|
|
64
|
+
'logic',
|
|
65
|
+
'controller',
|
|
66
|
+
];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = [];
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
const BaseRest = require('./rest');
|
|
2
|
+
|
|
3
|
+
module.exports = class extends BaseRest {
|
|
4
|
+
constructor(ctx) {
|
|
5
|
+
super(ctx);
|
|
6
|
+
this.modelInstance = this.service(
|
|
7
|
+
`storage/${this.config('storage')}`,
|
|
8
|
+
'Counter'
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async getAction() {
|
|
13
|
+
const { path, type } = this.get();
|
|
14
|
+
|
|
15
|
+
// path is required
|
|
16
|
+
if (!Array.isArray(path) || !path.length) {
|
|
17
|
+
return this.json(0);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const resp = await this.modelInstance.select({ url: ['IN', path] });
|
|
21
|
+
|
|
22
|
+
if (think.isEmpty(resp)) {
|
|
23
|
+
const data = type.reduce((o, field) => {
|
|
24
|
+
o[field] = 0;
|
|
25
|
+
|
|
26
|
+
return o;
|
|
27
|
+
}, {});
|
|
28
|
+
|
|
29
|
+
return this.json(type.length === 1 ? data[type[0]] : data);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const respObj = resp.reduce((o, n) => {
|
|
33
|
+
o[n.url] = n;
|
|
34
|
+
|
|
35
|
+
return o;
|
|
36
|
+
}, {});
|
|
37
|
+
|
|
38
|
+
const data = [];
|
|
39
|
+
|
|
40
|
+
for (let i = 0; i < path.length; i++) {
|
|
41
|
+
const url = path[i];
|
|
42
|
+
let counters = {};
|
|
43
|
+
|
|
44
|
+
for (let j = 0; j < type.length; j++) {
|
|
45
|
+
const field = type[j];
|
|
46
|
+
|
|
47
|
+
counters[field] =
|
|
48
|
+
respObj[url] && respObj[url][field] ? respObj[url][field] : 0;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (type.length === 1) {
|
|
52
|
+
counters = counters[type[0]];
|
|
53
|
+
}
|
|
54
|
+
data.push(counters);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return this.json(path.length === 1 ? data[0] : data);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async postAction() {
|
|
61
|
+
const { path, type, action } = this.post();
|
|
62
|
+
const resp = await this.modelInstance.select({ url: path });
|
|
63
|
+
|
|
64
|
+
if (think.isEmpty(resp)) {
|
|
65
|
+
if (action === 'desc') {
|
|
66
|
+
return this.json(0);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const count = 1;
|
|
70
|
+
|
|
71
|
+
await this.modelInstance.add(
|
|
72
|
+
{ url: path, [type]: count },
|
|
73
|
+
{ access: { read: true, write: true } }
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
return this.json(count);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const ret = await this.modelInstance.update(
|
|
80
|
+
(counter) => ({
|
|
81
|
+
[type]:
|
|
82
|
+
action === 'desc'
|
|
83
|
+
? (counter[type] || 1) - 1
|
|
84
|
+
: (counter[type] || 0) + 1,
|
|
85
|
+
}),
|
|
86
|
+
{ objectId: ['IN', resp.map(({ objectId }) => objectId)] }
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
return this.json(ret[0][type]);
|
|
90
|
+
}
|
|
91
|
+
};
|