usf-cli 1.1.3 → 1.2.2
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/LICENSE.md +5 -5
- package/README.md +67 -49
- package/ci.sh +60 -0
- package/index.js +186 -186
- package/package.json +30 -30
- package/templates/crud_mongo.js +35 -35
- package/templates/crud_mysql.js +59 -59
- package/templates/crud_postgresql.js +29 -29
- package/templates/crud_redis.js +31 -31
- package/templates/default.js +8 -8
- package/utils/dynamic-runner/index.js +89 -89
- package/utils/function-construct/index.js +120 -120
- package/utils/uploader/index.js +364 -359
- package/.idea/modules.xml +0 -8
- package/.idea/usf-cli.iml +0 -12
- package/.idea/vcs.xml +0 -6
package/utils/uploader/index.js
CHANGED
|
@@ -1,359 +1,364 @@
|
|
|
1
|
-
import { makeFile, loader } from "../function-construct/index.js";
|
|
2
|
-
import os from "os";
|
|
3
|
-
import fs from "fs";
|
|
4
|
-
import path from "path";
|
|
5
|
-
import JSZip from "jszip";
|
|
6
|
-
import axios from "axios";
|
|
7
|
-
import COS from "cos-nodejs-sdk-v5";
|
|
8
|
-
|
|
9
|
-
const igonreList = [
|
|
10
|
-
".DS_Store",
|
|
11
|
-
".idea",
|
|
12
|
-
".vscode",
|
|
13
|
-
".git",
|
|
14
|
-
"package-lock.json",
|
|
15
|
-
".uos_test",
|
|
16
|
-
];
|
|
17
|
-
let hasPackageJson = false;
|
|
18
|
-
let hasNodeModules = false;
|
|
19
|
-
|
|
20
|
-
const recordAuthInfoToFile = async function (id, secret) {
|
|
21
|
-
const tempDir = os.tmpdir();
|
|
22
|
-
const auth = { id: id, secret: secret };
|
|
23
|
-
try {
|
|
24
|
-
const file = await makeFile(tempDir, ".uos", "auth.json", true);
|
|
25
|
-
fs.writeFile(file, JSON.stringify(auth, null, 2), (err) => {
|
|
26
|
-
if (err) {
|
|
27
|
-
console.error(`Fail to record auth info.`);
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
console.log(
|
|
31
|
-
`Auth info is recorded.\n\nIf stateless functions are completed, you can run "usf-cli -u" to upload them to UOS platform with UOS auth and deploy automatically.`
|
|
32
|
-
);
|
|
33
|
-
});
|
|
34
|
-
} catch (e) {
|
|
35
|
-
console.log(e);
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
const getAuthInfoFromFile = async function () {
|
|
40
|
-
const tempDir = os.tmpdir();
|
|
41
|
-
const authPath = path.join(tempDir, ".uos", "auth.json");
|
|
42
|
-
const authInfo = await loader(authPath);
|
|
43
|
-
|
|
44
|
-
return authInfo;
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
const convertToValidVariableName = function (folderName) {
|
|
48
|
-
// 移除非法字符并确保以合法字符开头
|
|
49
|
-
let validName = folderName.replace(/[^a-zA-Z0-9_$]/g, "_");
|
|
50
|
-
if (/^[0-9_$]/.test(validName)) {
|
|
51
|
-
validName = "_" + validName; // 如果以数字、下划线或美元符号开头,则添加下划线作为前缀
|
|
52
|
-
}
|
|
53
|
-
return validName;
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
const formatHandler = function (name, entry) {
|
|
57
|
-
return `const { main: ${name} } = require("./${entry}/index.js");\n\nexports.${name}_handler = ${name};\n\n`;
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
const zipDirectory = async function (entries, zip, dir, level = 0) {
|
|
61
|
-
const files = fs.readdirSync(dir);
|
|
62
|
-
|
|
63
|
-
for (const file of files) {
|
|
64
|
-
const filePath = path.join(dir, file);
|
|
65
|
-
const stats = fs.statSync(filePath);
|
|
66
|
-
if (igonreList.includes(file)) {
|
|
67
|
-
continue;
|
|
68
|
-
}
|
|
69
|
-
if (level === 0 && file === "package.json") {
|
|
70
|
-
hasPackageJson = true;
|
|
71
|
-
}
|
|
72
|
-
if (level === 0 && file === "node_modules") {
|
|
73
|
-
hasNodeModules = true;
|
|
74
|
-
}
|
|
75
|
-
if (stats.isDirectory()) {
|
|
76
|
-
const isEntry = await zipDirectory(
|
|
77
|
-
entries,
|
|
78
|
-
zip.folder(file),
|
|
79
|
-
filePath,
|
|
80
|
-
level + 1
|
|
81
|
-
);
|
|
82
|
-
if (isEntry) {
|
|
83
|
-
entries[file] = convertToValidVariableName(file);
|
|
84
|
-
}
|
|
85
|
-
} else {
|
|
86
|
-
const content = fs.readFileSync(filePath);
|
|
87
|
-
zip.file(file, content);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
return level === 1 && files.includes("index.js");
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
const creatZip = async function (dir) {
|
|
94
|
-
const zip = new JSZip();
|
|
95
|
-
const entries = {};
|
|
96
|
-
|
|
97
|
-
dir = dir || process.cwd();
|
|
98
|
-
|
|
99
|
-
await zipDirectory(entries, zip, dir);
|
|
100
|
-
let entryFile = "";
|
|
101
|
-
for (let entry in entries) {
|
|
102
|
-
entryFile += formatHandler(entries[entry], entry);
|
|
103
|
-
}
|
|
104
|
-
zip.file(
|
|
105
|
-
const zipContent = await zip.generateAsync({ type: "nodebuffer" });
|
|
106
|
-
|
|
107
|
-
return { entries, zipContent };
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
const getToken = async function (auth) {
|
|
111
|
-
const res = await axios.get(
|
|
112
|
-
"https://func.unity.cn/v1/func-stateless/functions/" +
|
|
113
|
-
auth.id +
|
|
114
|
-
"/get-transfer-token",
|
|
115
|
-
{
|
|
116
|
-
headers: {
|
|
117
|
-
Authorization: "Basic " + btoa(auth.id + ":" + auth.secret),
|
|
118
|
-
},
|
|
119
|
-
}
|
|
120
|
-
);
|
|
121
|
-
return res.data;
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
const uploadZipToCos = function (cosToken, zipContent) {
|
|
125
|
-
const cos = new COS({
|
|
126
|
-
getAuthorization: function (options, callback) {
|
|
127
|
-
callback({
|
|
128
|
-
TmpSecretId: cosToken.tmpSecretId,
|
|
129
|
-
TmpSecretKey: cosToken.tmpSecretKey,
|
|
130
|
-
SecurityToken: cosToken.token,
|
|
131
|
-
StartTime: cosToken.startTime,
|
|
132
|
-
ExpiredTime: Math.ceil(Date.parse(cosToken.expiredTime) / 1000),
|
|
133
|
-
});
|
|
134
|
-
},
|
|
135
|
-
});
|
|
136
|
-
let taskId;
|
|
137
|
-
return new Promise((resolve, reject) => {
|
|
138
|
-
cos.putObject(
|
|
139
|
-
{
|
|
140
|
-
Bucket: cosToken.bucket,
|
|
141
|
-
Region: cosToken.region,
|
|
142
|
-
Key: cosToken.objectName + `/${path.basename(process.cwd())}.zip`,
|
|
143
|
-
Body: zipContent,
|
|
144
|
-
onTaskReady: function (taskId) {
|
|
145
|
-
taskId = taskId;
|
|
146
|
-
},
|
|
147
|
-
},
|
|
148
|
-
function (err, data) {
|
|
149
|
-
if (err) {
|
|
150
|
-
reject(err);
|
|
151
|
-
} else {
|
|
152
|
-
taskId = null;
|
|
153
|
-
setTimeout(() => {
|
|
154
|
-
resolve();
|
|
155
|
-
}, 500);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
);
|
|
159
|
-
});
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
const createScf = async function (auth, entries) {
|
|
163
|
-
let payLoad = {
|
|
164
|
-
scfList: [],
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
for (let entry in entries) {
|
|
168
|
-
payLoad.scfList.push({
|
|
169
|
-
name: entries[entry],
|
|
170
|
-
zipName: path.basename(process.cwd()),
|
|
171
|
-
handler: `index.${entries[entry]}_handler`,
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
await axios.post(
|
|
176
|
-
"https://func.unity.cn/v1/func-stateless/functions/" + auth.id + "/scf",
|
|
177
|
-
payLoad,
|
|
178
|
-
{
|
|
179
|
-
headers: {
|
|
180
|
-
Authorization: "Basic " + btoa(auth.id + ":" + auth.secret),
|
|
181
|
-
},
|
|
182
|
-
}
|
|
183
|
-
);
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
const loadingAnimation = function (loadingLabel) {
|
|
187
|
-
const frames = ["-", "\\", "|", "/"];
|
|
188
|
-
let i = 0;
|
|
189
|
-
|
|
190
|
-
const interval = setInterval(() => {
|
|
191
|
-
process.stdout.write(`\r${frames[i]} ${loadingLabel}...`);
|
|
192
|
-
i = (i + 1) % frames.length;
|
|
193
|
-
}, 100);
|
|
194
|
-
|
|
195
|
-
return interval;
|
|
196
|
-
};
|
|
197
|
-
|
|
198
|
-
const checkFunctionStatus = function (auth) {
|
|
199
|
-
return new Promise((resolve, reject) => {
|
|
200
|
-
const getFunctionInfo = async () => {
|
|
201
|
-
try {
|
|
202
|
-
const res = await axios.get(
|
|
203
|
-
"https://func.unity.cn/v1/func-stateless/functions/" + auth.id,
|
|
204
|
-
{
|
|
205
|
-
headers: {
|
|
206
|
-
Authorization: "Basic " + btoa(auth.id + ":" + auth.secret),
|
|
207
|
-
},
|
|
208
|
-
}
|
|
209
|
-
);
|
|
210
|
-
|
|
211
|
-
if (res?.data?.functionInfo?.status === "createFailed") {
|
|
212
|
-
reject();
|
|
213
|
-
} else if (res?.data?.functionInfo?.status === "creating") {
|
|
214
|
-
setTimeout(getFunctionInfo, 3000);
|
|
215
|
-
} else {
|
|
216
|
-
resolve(res?.data?.functionInfo);
|
|
217
|
-
}
|
|
218
|
-
} catch (e) {
|
|
219
|
-
reject(e);
|
|
220
|
-
}
|
|
221
|
-
};
|
|
222
|
-
getFunctionInfo();
|
|
223
|
-
});
|
|
224
|
-
};
|
|
225
|
-
|
|
226
|
-
const updateInstallDependency = async function (install = false, auth) {
|
|
227
|
-
let payload = {
|
|
228
|
-
functionInfo: {
|
|
229
|
-
options: {
|
|
230
|
-
installDependency: install ? "TRUE" : "FALSE",
|
|
231
|
-
},
|
|
232
|
-
},
|
|
233
|
-
};
|
|
234
|
-
|
|
235
|
-
await axios.put(
|
|
236
|
-
"https://func.unity.cn/v1/func-stateless/functions/" + auth.id,
|
|
237
|
-
payload,
|
|
238
|
-
{
|
|
239
|
-
headers: {
|
|
240
|
-
Authorization: "Basic " + btoa(auth.id + ":" + auth.secret),
|
|
241
|
-
},
|
|
242
|
-
}
|
|
243
|
-
);
|
|
244
|
-
};
|
|
245
|
-
|
|
246
|
-
const stepWrapper = async function (stepHandler, args, loading, done) {
|
|
247
|
-
let id = loadingAnimation(loading);
|
|
248
|
-
try {
|
|
249
|
-
const res = await stepHandler(...args);
|
|
250
|
-
clearInterval(id);
|
|
251
|
-
console.log(`\r>>> Success to ${done}!`);
|
|
252
|
-
return res;
|
|
253
|
-
} catch (e) {
|
|
254
|
-
clearInterval(id);
|
|
255
|
-
console.error(`\r>>> Failed to ${done}: \n`, e
|
|
256
|
-
process.exit(1);
|
|
257
|
-
}
|
|
258
|
-
};
|
|
259
|
-
|
|
260
|
-
const listFunctions = async function (version, auth) {
|
|
261
|
-
try {
|
|
262
|
-
const params = {
|
|
263
|
-
version: version,
|
|
264
|
-
};
|
|
265
|
-
const res = await axios.get(
|
|
266
|
-
`https://func.unity.cn/v1/func-stateless/functions/${auth.id}/scf`,
|
|
267
|
-
{
|
|
268
|
-
params: params,
|
|
269
|
-
headers: {
|
|
270
|
-
Authorization: "Basic " + btoa(auth.id + ":" + auth.secret),
|
|
271
|
-
},
|
|
272
|
-
}
|
|
273
|
-
);
|
|
274
|
-
const scfs = res.data.scfs || [];
|
|
275
|
-
console.log(
|
|
276
|
-
`\n====== Successfully deployed ${scfs.length} function(s). Current Online Func version is: ${version} ======\n`
|
|
277
|
-
);
|
|
278
|
-
|
|
279
|
-
for (const scf of scfs) {
|
|
280
|
-
console.log(`Function Name: ${scf.name}\nFunction URL: ${scf.url}\n`);
|
|
281
|
-
}
|
|
282
|
-
console.log(`============ Deployment completed ============\n`);
|
|
283
|
-
} catch (error) {
|
|
284
|
-
console.log(error.response);
|
|
285
|
-
}
|
|
286
|
-
};
|
|
287
|
-
|
|
288
|
-
const deployFunctionsToPlatform = async function (dir, auth, install = false) {
|
|
289
|
-
hasNodeModules = false;
|
|
290
|
-
hasPackageJson = false;
|
|
291
|
-
|
|
292
|
-
console.log(`\n============ Deployment starts ============\n`);
|
|
293
|
-
|
|
294
|
-
const { entries, zipContent } = await stepWrapper(
|
|
295
|
-
creatZip,
|
|
296
|
-
[dir],
|
|
297
|
-
"Packaging current project",
|
|
298
|
-
"package current project"
|
|
299
|
-
);
|
|
300
|
-
|
|
301
|
-
if (
|
|
302
|
-
console.error("!!! Can't find
|
|
303
|
-
process.exit(1);
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
if (hasNodeModules
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
1
|
+
import { makeFile, loader } from "../function-construct/index.js";
|
|
2
|
+
import os from "os";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import JSZip from "jszip";
|
|
6
|
+
import axios from "axios";
|
|
7
|
+
import COS from "cos-nodejs-sdk-v5";
|
|
8
|
+
|
|
9
|
+
const igonreList = [
|
|
10
|
+
".DS_Store",
|
|
11
|
+
".idea",
|
|
12
|
+
".vscode",
|
|
13
|
+
".git",
|
|
14
|
+
"package-lock.json",
|
|
15
|
+
".uos_test",
|
|
16
|
+
];
|
|
17
|
+
let hasPackageJson = false;
|
|
18
|
+
let hasNodeModules = false;
|
|
19
|
+
|
|
20
|
+
const recordAuthInfoToFile = async function (id, secret) {
|
|
21
|
+
const tempDir = os.tmpdir();
|
|
22
|
+
const auth = { id: id, secret: secret };
|
|
23
|
+
try {
|
|
24
|
+
const file = await makeFile(tempDir, ".uos", "auth.json", true);
|
|
25
|
+
fs.writeFile(file, JSON.stringify(auth, null, 2), (err) => {
|
|
26
|
+
if (err) {
|
|
27
|
+
console.error(`Fail to record auth info.`);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
console.log(
|
|
31
|
+
`Auth info is recorded.\n\nIf stateless functions are completed, you can run "usf-cli -u" to upload them to UOS platform with UOS auth and deploy automatically.`
|
|
32
|
+
);
|
|
33
|
+
});
|
|
34
|
+
} catch (e) {
|
|
35
|
+
console.log(e);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const getAuthInfoFromFile = async function () {
|
|
40
|
+
const tempDir = os.tmpdir();
|
|
41
|
+
const authPath = path.join(tempDir, ".uos", "auth.json");
|
|
42
|
+
const authInfo = await loader(authPath);
|
|
43
|
+
|
|
44
|
+
return authInfo;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const convertToValidVariableName = function (folderName) {
|
|
48
|
+
// 移除非法字符并确保以合法字符开头
|
|
49
|
+
let validName = folderName.replace(/[^a-zA-Z0-9_$]/g, "_");
|
|
50
|
+
if (/^[0-9_$]/.test(validName)) {
|
|
51
|
+
validName = "_" + validName; // 如果以数字、下划线或美元符号开头,则添加下划线作为前缀
|
|
52
|
+
}
|
|
53
|
+
return validName;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const formatHandler = function (name, entry) {
|
|
57
|
+
return `const { main: ${name} } = require("./${entry}/index.js");\n\nexports.${name}_handler = ${name};\n\n`;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const zipDirectory = async function (entries, zip, dir, level = 0) {
|
|
61
|
+
const files = fs.readdirSync(dir);
|
|
62
|
+
|
|
63
|
+
for (const file of files) {
|
|
64
|
+
const filePath = path.join(dir, file);
|
|
65
|
+
const stats = fs.statSync(filePath);
|
|
66
|
+
if (igonreList.includes(file)) {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (level === 0 && file === "package.json") {
|
|
70
|
+
hasPackageJson = true;
|
|
71
|
+
}
|
|
72
|
+
if (level === 0 && file === "node_modules") {
|
|
73
|
+
hasNodeModules = true;
|
|
74
|
+
}
|
|
75
|
+
if (stats.isDirectory()) {
|
|
76
|
+
const isEntry = await zipDirectory(
|
|
77
|
+
entries,
|
|
78
|
+
zip.folder(file),
|
|
79
|
+
filePath,
|
|
80
|
+
level + 1
|
|
81
|
+
);
|
|
82
|
+
if (isEntry) {
|
|
83
|
+
entries[file] = convertToValidVariableName(file);
|
|
84
|
+
}
|
|
85
|
+
} else {
|
|
86
|
+
const content = fs.readFileSync(filePath);
|
|
87
|
+
zip.file(file, content);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return level === 1 && files.includes("index.js");
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const creatZip = async function (dir) {
|
|
94
|
+
const zip = new JSZip();
|
|
95
|
+
const entries = {};
|
|
96
|
+
|
|
97
|
+
dir = dir || process.cwd();
|
|
98
|
+
|
|
99
|
+
await zipDirectory(entries, zip, dir);
|
|
100
|
+
let entryFile = "";
|
|
101
|
+
for (let entry in entries) {
|
|
102
|
+
entryFile += formatHandler(entries[entry], entry);
|
|
103
|
+
}
|
|
104
|
+
zip.file(`index.js`, entryFile);
|
|
105
|
+
const zipContent = await zip.generateAsync({ type: "nodebuffer" });
|
|
106
|
+
|
|
107
|
+
return { entries, zipContent };
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const getToken = async function (auth) {
|
|
111
|
+
const res = await axios.get(
|
|
112
|
+
"https://func.unity.cn/v1/func-stateless/functions/" +
|
|
113
|
+
auth.id +
|
|
114
|
+
"/get-transfer-token",
|
|
115
|
+
{
|
|
116
|
+
headers: {
|
|
117
|
+
Authorization: "Basic " + btoa(auth.id + ":" + auth.secret),
|
|
118
|
+
},
|
|
119
|
+
}
|
|
120
|
+
);
|
|
121
|
+
return res.data;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const uploadZipToCos = function (cosToken, zipContent) {
|
|
125
|
+
const cos = new COS({
|
|
126
|
+
getAuthorization: function (options, callback) {
|
|
127
|
+
callback({
|
|
128
|
+
TmpSecretId: cosToken.tmpSecretId,
|
|
129
|
+
TmpSecretKey: cosToken.tmpSecretKey,
|
|
130
|
+
SecurityToken: cosToken.token,
|
|
131
|
+
StartTime: cosToken.startTime,
|
|
132
|
+
ExpiredTime: Math.ceil(Date.parse(cosToken.expiredTime) / 1000),
|
|
133
|
+
});
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
let taskId;
|
|
137
|
+
return new Promise((resolve, reject) => {
|
|
138
|
+
cos.putObject(
|
|
139
|
+
{
|
|
140
|
+
Bucket: cosToken.bucket,
|
|
141
|
+
Region: cosToken.region,
|
|
142
|
+
Key: cosToken.objectName + `/${path.basename(process.cwd())}.zip`,
|
|
143
|
+
Body: zipContent,
|
|
144
|
+
onTaskReady: function (taskId) {
|
|
145
|
+
taskId = taskId;
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
function (err, data) {
|
|
149
|
+
if (err) {
|
|
150
|
+
reject(err);
|
|
151
|
+
} else {
|
|
152
|
+
taskId = null;
|
|
153
|
+
setTimeout(() => {
|
|
154
|
+
resolve();
|
|
155
|
+
}, 500);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
);
|
|
159
|
+
});
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const createScf = async function (auth, entries) {
|
|
163
|
+
let payLoad = {
|
|
164
|
+
scfList: [],
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
for (let entry in entries) {
|
|
168
|
+
payLoad.scfList.push({
|
|
169
|
+
name: entries[entry],
|
|
170
|
+
zipName: path.basename(process.cwd()),
|
|
171
|
+
handler: `index.${entries[entry]}_handler`,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
await axios.post(
|
|
176
|
+
"https://func.unity.cn/v1/func-stateless/functions/" + auth.id + "/scf",
|
|
177
|
+
payLoad,
|
|
178
|
+
{
|
|
179
|
+
headers: {
|
|
180
|
+
Authorization: "Basic " + btoa(auth.id + ":" + auth.secret),
|
|
181
|
+
},
|
|
182
|
+
}
|
|
183
|
+
);
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
const loadingAnimation = function (loadingLabel) {
|
|
187
|
+
const frames = ["-", "\\", "|", "/"];
|
|
188
|
+
let i = 0;
|
|
189
|
+
|
|
190
|
+
const interval = setInterval(() => {
|
|
191
|
+
process.stdout.write(`\r${frames[i]} ${loadingLabel}...`);
|
|
192
|
+
i = (i + 1) % frames.length;
|
|
193
|
+
}, 100);
|
|
194
|
+
|
|
195
|
+
return interval;
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const checkFunctionStatus = function (auth) {
|
|
199
|
+
return new Promise((resolve, reject) => {
|
|
200
|
+
const getFunctionInfo = async () => {
|
|
201
|
+
try {
|
|
202
|
+
const res = await axios.get(
|
|
203
|
+
"https://func.unity.cn/v1/func-stateless/functions/" + auth.id,
|
|
204
|
+
{
|
|
205
|
+
headers: {
|
|
206
|
+
Authorization: "Basic " + btoa(auth.id + ":" + auth.secret),
|
|
207
|
+
},
|
|
208
|
+
}
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
if (res?.data?.functionInfo?.status === "createFailed") {
|
|
212
|
+
reject('creating new version failed');
|
|
213
|
+
} else if (res?.data?.functionInfo?.status === "creating") {
|
|
214
|
+
setTimeout(getFunctionInfo, 3000);
|
|
215
|
+
} else {
|
|
216
|
+
resolve(res?.data?.functionInfo);
|
|
217
|
+
}
|
|
218
|
+
} catch (e) {
|
|
219
|
+
reject(e);
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
getFunctionInfo();
|
|
223
|
+
});
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
const updateInstallDependency = async function (install = false, auth) {
|
|
227
|
+
let payload = {
|
|
228
|
+
functionInfo: {
|
|
229
|
+
options: {
|
|
230
|
+
installDependency: install ? "TRUE" : "FALSE",
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
await axios.put(
|
|
236
|
+
"https://func.unity.cn/v1/func-stateless/functions/" + auth.id,
|
|
237
|
+
payload,
|
|
238
|
+
{
|
|
239
|
+
headers: {
|
|
240
|
+
Authorization: "Basic " + btoa(auth.id + ":" + auth.secret),
|
|
241
|
+
},
|
|
242
|
+
}
|
|
243
|
+
);
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
const stepWrapper = async function (stepHandler, args, loading, done) {
|
|
247
|
+
let id = loadingAnimation(loading);
|
|
248
|
+
try {
|
|
249
|
+
const res = await stepHandler(...args);
|
|
250
|
+
clearInterval(id);
|
|
251
|
+
console.log(`\r>>> Success to ${done}!`);
|
|
252
|
+
return res;
|
|
253
|
+
} catch (e) {
|
|
254
|
+
clearInterval(id);
|
|
255
|
+
console.error(`\r>>> Failed to ${done}: \n`, e?.response || e);
|
|
256
|
+
process.exit(1);
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
const listFunctions = async function (version, auth) {
|
|
261
|
+
try {
|
|
262
|
+
const params = {
|
|
263
|
+
version: version,
|
|
264
|
+
};
|
|
265
|
+
const res = await axios.get(
|
|
266
|
+
`https://func.unity.cn/v1/func-stateless/functions/${auth.id}/scf`,
|
|
267
|
+
{
|
|
268
|
+
params: params,
|
|
269
|
+
headers: {
|
|
270
|
+
Authorization: "Basic " + btoa(auth.id + ":" + auth.secret),
|
|
271
|
+
},
|
|
272
|
+
}
|
|
273
|
+
);
|
|
274
|
+
const scfs = res.data.scfs || [];
|
|
275
|
+
console.log(
|
|
276
|
+
`\n====== Successfully deployed ${scfs.length} function(s). Current Online Func version is: ${version} ======\n`
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
for (const scf of scfs) {
|
|
280
|
+
console.log(`Function Name: ${scf.name}\nFunction URL: ${scf.url}\n`);
|
|
281
|
+
}
|
|
282
|
+
console.log(`============ Deployment completed ============\n`);
|
|
283
|
+
} catch (error) {
|
|
284
|
+
console.log(error.response);
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
const deployFunctionsToPlatform = async function (dir, auth, install = false) {
|
|
289
|
+
hasNodeModules = false;
|
|
290
|
+
hasPackageJson = false;
|
|
291
|
+
|
|
292
|
+
console.log(`\n============ Deployment starts ============\n`);
|
|
293
|
+
|
|
294
|
+
const { entries, zipContent } = await stepWrapper(
|
|
295
|
+
creatZip,
|
|
296
|
+
[dir],
|
|
297
|
+
"Packaging current project",
|
|
298
|
+
"package current project"
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
if (Object.keys(entries).length === 0) {
|
|
302
|
+
console.error("!!! Can't find function entry in your project.");
|
|
303
|
+
process.exit(1);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
if (!hasNodeModules && !hasPackageJson) {
|
|
307
|
+
console.error("!!! Can't find package.json & node_modules/ in your project.");
|
|
308
|
+
process.exit(1);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (hasNodeModules ^ hasPackageJson) {
|
|
312
|
+
install = hasPackageJson;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
await stepWrapper(
|
|
316
|
+
updateInstallDependency,
|
|
317
|
+
[install, auth],
|
|
318
|
+
`Changing 'Online Install Dependency' status to ${
|
|
319
|
+
install ? "TRUE" : "FALSE"
|
|
320
|
+
}`,
|
|
321
|
+
`change 'Online Install Dependency' status to ${install ? "TRUE" : "FALSE"}`
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
if (install) {
|
|
325
|
+
igonreList.push("node_modules");
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// const zipFilePath = path.join(process.cwd(), `/${path.basename(process.cwd())}.zip`);
|
|
329
|
+
// fs.writeFileSync(zipFilePath, zipContent);
|
|
330
|
+
|
|
331
|
+
const cosToken = await stepWrapper(
|
|
332
|
+
getToken,
|
|
333
|
+
[auth],
|
|
334
|
+
"Getting cos token",
|
|
335
|
+
"get cos token"
|
|
336
|
+
);
|
|
337
|
+
|
|
338
|
+
await stepWrapper(
|
|
339
|
+
uploadZipToCos,
|
|
340
|
+
[cosToken, zipContent],
|
|
341
|
+
"Uploading function project to cos",
|
|
342
|
+
"upload function project to cos"
|
|
343
|
+
);
|
|
344
|
+
|
|
345
|
+
await stepWrapper(
|
|
346
|
+
createScf,
|
|
347
|
+
[auth, entries],
|
|
348
|
+
"Updating Func Stateless",
|
|
349
|
+
"update Func Stateless"
|
|
350
|
+
);
|
|
351
|
+
|
|
352
|
+
const functionInfo = await stepWrapper(
|
|
353
|
+
checkFunctionStatus,
|
|
354
|
+
[auth],
|
|
355
|
+
"Deploying functions",
|
|
356
|
+
"deploy functions"
|
|
357
|
+
);
|
|
358
|
+
|
|
359
|
+
if (functionInfo.currentVersion) {
|
|
360
|
+
await listFunctions(functionInfo.currentVersion, auth);
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
export { recordAuthInfoToFile, getAuthInfoFromFile, deployFunctionsToPlatform };
|