badmfck-api-server 4.1.38 → 4.1.40
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/apiServer/deployment/Activate.d.ts +10 -0
- package/dist/apiServer/deployment/Activate.js +137 -0
- package/dist/apiServer/deployment/Deploy.d.ts +2 -0
- package/dist/apiServer/deployment/Deploy.js +198 -18
- package/dist/apiServer/deployment/DeployerService.d.ts +8 -1
- package/dist/apiServer/deployment/DeployerService.js +10 -1
- package/dist/apiServer/http/Http.js +14 -7
- package/dist/index.d.ts +2 -1
- package/dist/index.js +3 -1
- package/package.json +1 -1
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
interface IActivateParams {
|
|
2
|
+
name: string;
|
|
3
|
+
token: string;
|
|
4
|
+
host: string;
|
|
5
|
+
username: string;
|
|
6
|
+
password: string;
|
|
7
|
+
switch_host?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function Activate(opt: IActivateParams | string, scheme?: "blue" | "green"): Promise<string>;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.Activate = void 0;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
9
|
+
const __1 = require("../..");
|
|
10
|
+
async function Activate(opt, scheme) {
|
|
11
|
+
if (typeof opt === "string") {
|
|
12
|
+
const configPath = opt;
|
|
13
|
+
if (!fs_1.default.existsSync(configPath)) {
|
|
14
|
+
throw new Error(`Config file not found: ${configPath}`);
|
|
15
|
+
}
|
|
16
|
+
opt = JSON.parse(fs_1.default.readFileSync(configPath).toString("utf-8"));
|
|
17
|
+
if (!opt.name || !opt.token || !opt.host || !opt.username || !opt.password) {
|
|
18
|
+
throw new Error(`Invalid activate config file (missing required fields): ${configPath}`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
if (scheme && scheme !== "blue" && scheme !== "green") {
|
|
22
|
+
throw new Error(`Invalid scheme: ${scheme}. Must be "blue" or "green" (or omitted).`);
|
|
23
|
+
}
|
|
24
|
+
let switchUrl;
|
|
25
|
+
if (opt.switch_host) {
|
|
26
|
+
switchUrl = opt.switch_host;
|
|
27
|
+
}
|
|
28
|
+
else if (/\/add\/?$/.test(opt.host)) {
|
|
29
|
+
switchUrl = opt.host.replace(/\/add\/?$/, "/switch");
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
throw new Error(`Cannot derive switch URL from host "${opt.host}". ` +
|
|
33
|
+
`Either add "switch_host" to config, or make sure "host" ends with /add.`);
|
|
34
|
+
}
|
|
35
|
+
const authToken = crypto_1.default.createHash("sha256").update(opt.username + ":" + opt.password).digest("hex");
|
|
36
|
+
const requestBody = { name: opt.name, token: opt.token };
|
|
37
|
+
if (scheme)
|
|
38
|
+
requestBody.scheme = scheme;
|
|
39
|
+
console.log(`Activating${scheme ? ` slot=${scheme}` : " (auto: lastDeployed)"} for ${opt.name} at ${switchUrl}`);
|
|
40
|
+
const startedAt = Date.now();
|
|
41
|
+
const response = await __1.Http.post(switchUrl, requestBody, {
|
|
42
|
+
headers: { authorization: `Bearer ${authToken}` },
|
|
43
|
+
timeoutMs: 60 * 1000,
|
|
44
|
+
retry: { enabled: false },
|
|
45
|
+
});
|
|
46
|
+
const elapsedMs = Date.now() - startedAt;
|
|
47
|
+
const useColor = process.stdout.isTTY && !process.env.NO_COLOR;
|
|
48
|
+
const c = (code, t) => useColor ? `\x1b[${code}m${t}\x1b[0m` : t;
|
|
49
|
+
const red = (t) => c("31", t);
|
|
50
|
+
const green = (t) => c("32", t);
|
|
51
|
+
const yellow = (t) => c("33", t);
|
|
52
|
+
const dim = (t) => c("2", t);
|
|
53
|
+
const bold = (t) => c("1", t);
|
|
54
|
+
const line = dim("─".repeat(60));
|
|
55
|
+
const now = new Date();
|
|
56
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
57
|
+
const stamp = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`;
|
|
58
|
+
console.log("");
|
|
59
|
+
if (!response.ok) {
|
|
60
|
+
const statusStr = response.status !== undefined
|
|
61
|
+
? `HTTP ${response.status}`
|
|
62
|
+
: "network error (no response)";
|
|
63
|
+
console.error(red(bold("✗ ACTIVATE FAILED — transport error")));
|
|
64
|
+
console.error(line);
|
|
65
|
+
console.error(` ${dim("host: ")} ${switchUrl}`);
|
|
66
|
+
console.error(` ${dim("status: ")} ${statusStr}`);
|
|
67
|
+
console.error(` ${dim("elapsed: ")} ${elapsedMs}ms`);
|
|
68
|
+
if (response.error !== undefined) {
|
|
69
|
+
const errText = typeof response.error === "string"
|
|
70
|
+
? response.error
|
|
71
|
+
: JSON.stringify(response.error);
|
|
72
|
+
const clipped = errText.length > 400 ? errText.slice(0, 400) + `... (${errText.length} total)` : errText;
|
|
73
|
+
console.error(` ${dim("error: ")} ${clipped}`);
|
|
74
|
+
}
|
|
75
|
+
console.error(line);
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
const body = response.data;
|
|
79
|
+
let appError = null;
|
|
80
|
+
if (body && typeof body === "object") {
|
|
81
|
+
if (__1.ErrorUtils.isError(body?.error))
|
|
82
|
+
appError = body.error;
|
|
83
|
+
else if (__1.ErrorUtils.isError(body?.data?.error))
|
|
84
|
+
appError = body.data.error;
|
|
85
|
+
else if (__1.ErrorUtils.isError(body?.data))
|
|
86
|
+
appError = body.data;
|
|
87
|
+
else if (__1.ErrorUtils.isError(body))
|
|
88
|
+
appError = body;
|
|
89
|
+
}
|
|
90
|
+
if (appError) {
|
|
91
|
+
console.error(red(bold("✗ ACTIVATE REJECTED BY SERVER")));
|
|
92
|
+
console.error(line);
|
|
93
|
+
console.error(` ${dim("host: ")} ${switchUrl}`);
|
|
94
|
+
console.error(` ${dim("code: ")} ${appError.code}`);
|
|
95
|
+
console.error(` ${dim("message: ")} ${appError.message}`);
|
|
96
|
+
if (appError.httpStatus)
|
|
97
|
+
console.error(` ${dim("httpCode: ")} ${appError.httpStatus}`);
|
|
98
|
+
if (appError.details !== undefined) {
|
|
99
|
+
const d = typeof appError.details === "string" ? appError.details : JSON.stringify(appError.details);
|
|
100
|
+
const clipped = d.length > 400 ? d.slice(0, 400) + `... (${d.length} total)` : d;
|
|
101
|
+
console.error(` ${dim("details: ")} ${clipped}`);
|
|
102
|
+
}
|
|
103
|
+
console.error(` ${dim("elapsed: ")} ${elapsedMs}ms`);
|
|
104
|
+
console.error(line);
|
|
105
|
+
process.exit(1);
|
|
106
|
+
}
|
|
107
|
+
const info = body?.data ?? {};
|
|
108
|
+
const action = info.action ?? "switch";
|
|
109
|
+
const active = info.active ?? "?";
|
|
110
|
+
const previous = info.previousActive ?? "?";
|
|
111
|
+
const hookError = info.hookError ?? null;
|
|
112
|
+
if (hookError) {
|
|
113
|
+
console.log(yellow(bold("⚠ SLOT FLIPPED BUT nginx RELOAD FAILED")));
|
|
114
|
+
}
|
|
115
|
+
else if (action === "rollback") {
|
|
116
|
+
console.log(green(bold("✓ ROLLBACK SUCCESSFUL")));
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
console.log(green(bold("✓ ACTIVATE SUCCESSFUL")));
|
|
120
|
+
}
|
|
121
|
+
console.log(line);
|
|
122
|
+
console.log(` ${dim("project: ")} ${opt.name}`);
|
|
123
|
+
console.log(` ${dim("action: ")} ${action}`);
|
|
124
|
+
console.log(` ${dim("active: ")} ${previous} ${dim("→")} ${bold(active)}`);
|
|
125
|
+
console.log(` ${dim("host: ")} ${switchUrl}`);
|
|
126
|
+
console.log(` ${dim("status: ")} HTTP ${response.status}`);
|
|
127
|
+
console.log(` ${dim("elapsed: ")} ${elapsedMs}ms`);
|
|
128
|
+
console.log(` ${dim("time: ")} ${stamp}`);
|
|
129
|
+
if (hookError) {
|
|
130
|
+
console.log(` ${dim("hookErr: ")} ${yellow(hookError)}`);
|
|
131
|
+
console.log(dim(` (state + symlink already committed; retry the reload manually: nginx -s reload)`));
|
|
132
|
+
}
|
|
133
|
+
console.log(line);
|
|
134
|
+
console.log("");
|
|
135
|
+
return active;
|
|
136
|
+
}
|
|
137
|
+
exports.Activate = Activate;
|
|
@@ -18,23 +18,44 @@ async function Deploy(opt) {
|
|
|
18
18
|
if (!opt.name || !opt.token || !opt.host || !opt.username || !opt.password) {
|
|
19
19
|
throw new Error(`Invalid deploy config file: ${opt}`);
|
|
20
20
|
}
|
|
21
|
+
if (opt.includes && !Array.isArray(opt.includes)) {
|
|
22
|
+
throw new Error(`Invalid deploy config file: includes must be an array`);
|
|
23
|
+
}
|
|
24
|
+
if (opt.excludes && !Array.isArray(opt.excludes)) {
|
|
25
|
+
throw new Error(`Invalid deploy config file: excludes must be an array`);
|
|
26
|
+
}
|
|
21
27
|
}
|
|
22
28
|
const archiveName = __1.UID.sha256(opt.name.replaceAll(".", "_")) + ".tar.gz";
|
|
23
29
|
console.log("Changing Config to live");
|
|
24
30
|
const config = path_1.default.resolve("src", "Config.ts");
|
|
25
31
|
let configSrc = fs_1.default.readFileSync(config).toString("utf-8");
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
32
|
+
const schemeRe = /(\bscheme\s*:\s*Scheme\s*=\s*)local\b/g;
|
|
33
|
+
let replaced = 0;
|
|
34
|
+
const updated = configSrc.replace(schemeRe, (_match, prefix) => {
|
|
35
|
+
replaced++;
|
|
36
|
+
return prefix + "live";
|
|
37
|
+
});
|
|
38
|
+
if (replaced > 0) {
|
|
39
|
+
fs_1.default.writeFileSync(config, updated);
|
|
40
|
+
console.log(`Config: switched to live (${replaced} occurrence${replaced === 1 ? "" : "s"})`);
|
|
41
|
+
}
|
|
42
|
+
else if (/\bscheme\s*:\s*Scheme\s*=\s*live\b/.test(configSrc)) {
|
|
43
|
+
console.log("Config already in live mode — nothing to change");
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
console.warn("Config: no 'scheme: Scheme = local|live' pattern found in " + config +
|
|
47
|
+
" — check the file was not renamed/restructured");
|
|
29
48
|
}
|
|
30
49
|
const pkgPath = path_1.default.resolve("package.json");
|
|
31
50
|
const pkg = JSON.parse(fs_1.default.readFileSync(pkgPath).toString("utf-8"));
|
|
32
|
-
const
|
|
51
|
+
const oldVersion = String(pkg.version || "0.0.0");
|
|
52
|
+
const pkgName = String(pkg.name || opt.name);
|
|
53
|
+
const parts = oldVersion.split(".").map(p => parseInt(p, 10) || 0);
|
|
33
54
|
while (parts.length < 3)
|
|
34
55
|
parts.push(0);
|
|
35
56
|
parts[parts.length - 1] += 1;
|
|
36
57
|
const newVersion = parts.join(".");
|
|
37
|
-
console.log(`Bumping version: ${
|
|
58
|
+
console.log(`Bumping version: ${oldVersion} -> ${newVersion}`);
|
|
38
59
|
pkg.version = newVersion;
|
|
39
60
|
fs_1.default.writeFileSync(pkgPath, JSON.stringify(pkg, null, "\t") + "\n");
|
|
40
61
|
if (fs_1.default.existsSync(path_1.default.resolve(archiveName))) {
|
|
@@ -44,26 +65,185 @@ async function Deploy(opt) {
|
|
|
44
65
|
console.log("Build");
|
|
45
66
|
var execResult = run("npm run build");
|
|
46
67
|
console.log(execResult);
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
68
|
+
const items = [];
|
|
69
|
+
const binExists = fs_1.default.existsSync("./bin");
|
|
70
|
+
if (binExists) {
|
|
71
|
+
items.push("./bin", "package.json");
|
|
72
|
+
}
|
|
73
|
+
if (Array.isArray(opt.includes)) {
|
|
74
|
+
for (const inc of opt.includes) {
|
|
75
|
+
if (typeof inc !== "string" || inc.length === 0)
|
|
76
|
+
continue;
|
|
77
|
+
if (inc.startsWith("-")) {
|
|
78
|
+
console.warn(`Skipping include that starts with '-': ${inc}`);
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (!fs_1.default.existsSync(inc)) {
|
|
82
|
+
console.warn(`Include path does not exist (tar will error): ${inc}`);
|
|
83
|
+
}
|
|
84
|
+
items.push(inc);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (items.length === 0) {
|
|
88
|
+
throw new Error("Nothing to archive (no ./bin and no includes specified)");
|
|
89
|
+
}
|
|
90
|
+
const excludes = new Set();
|
|
91
|
+
const addExclude = (raw, source) => {
|
|
92
|
+
if (typeof raw !== "string")
|
|
93
|
+
return;
|
|
94
|
+
const trimmed = raw.trim();
|
|
95
|
+
if (trimmed.length === 0 || trimmed.startsWith("#"))
|
|
96
|
+
return;
|
|
97
|
+
if (trimmed.startsWith("-")) {
|
|
98
|
+
console.warn(`Skipping exclude starting with '-' from ${source}: ${trimmed}`);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
excludes.add(trimmed);
|
|
102
|
+
};
|
|
103
|
+
const ignoreFile = path_1.default.resolve(".deployignore");
|
|
104
|
+
if (fs_1.default.existsSync(ignoreFile)) {
|
|
105
|
+
const lines = fs_1.default.readFileSync(ignoreFile, "utf-8").split(/\r?\n/);
|
|
106
|
+
for (const line of lines)
|
|
107
|
+
addExclude(line, ".deployignore");
|
|
108
|
+
}
|
|
109
|
+
if (Array.isArray(opt.excludes)) {
|
|
110
|
+
for (const ex of opt.excludes)
|
|
111
|
+
addExclude(ex, "config.excludes");
|
|
112
|
+
}
|
|
113
|
+
const excludeArgs = Array.from(excludes).map(p => `--exclude=${p}`);
|
|
114
|
+
console.log("Archiving:", items.join(", "));
|
|
115
|
+
if (excludeArgs.length > 0)
|
|
116
|
+
console.log("Excluding:", Array.from(excludes).join(", "));
|
|
117
|
+
try {
|
|
118
|
+
(0, child_process_1.execFileSync)("tar", ["-czvf", archiveName, ...excludeArgs, ...items], { encoding: "utf-8", stdio: "inherit" });
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
console.error(`\ntar failed while creating ${archiveName}\n`);
|
|
122
|
+
const out = (err.stdout || "").toString().trim();
|
|
123
|
+
const errOut = (err.stderr || "").toString().trim();
|
|
124
|
+
if (out)
|
|
125
|
+
console.error(out);
|
|
126
|
+
if (errOut)
|
|
127
|
+
console.error(errOut);
|
|
128
|
+
process.exit(typeof err.status === "number" ? err.status : 1);
|
|
129
|
+
}
|
|
50
130
|
if (!fs_1.default.existsSync(path_1.default.resolve(archiveName))) {
|
|
51
131
|
console.error("ARCHIVE NOT CREATED!");
|
|
52
132
|
return "";
|
|
53
133
|
}
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
"
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
134
|
+
const authToken = crypto_1.default.createHash("sha256").update(opt.username + ":" + opt.password).digest("hex");
|
|
135
|
+
const archivePath = path_1.default.resolve(archiveName);
|
|
136
|
+
const fileBlob = await fs_1.default.openAsBlob(archivePath);
|
|
137
|
+
const formData = new FormData();
|
|
138
|
+
formData.set("token", opt.token);
|
|
139
|
+
formData.set("name", opt.name);
|
|
140
|
+
formData.set("file", fileBlob, archiveName);
|
|
141
|
+
console.log(`Deploying to ${opt.host} (archive: ${archiveName})`);
|
|
142
|
+
const startedAt = Date.now();
|
|
143
|
+
const uploadResponse = await __1.Http.post(opt.host, formData, {
|
|
144
|
+
headers: { authorization: `Bearer ${authToken}` },
|
|
145
|
+
timeoutMs: 5 * 60 * 1000,
|
|
146
|
+
retry: { enabled: false },
|
|
147
|
+
});
|
|
148
|
+
const elapsedMs = Date.now() - startedAt;
|
|
149
|
+
const useColor = process.stdout.isTTY && !process.env.NO_COLOR;
|
|
150
|
+
const c = (code, t) => useColor ? `\x1b[${code}m${t}\x1b[0m` : t;
|
|
151
|
+
const red = (t) => c("31", t);
|
|
152
|
+
const green = (t) => c("32", t);
|
|
153
|
+
const yellow = (t) => c("33", t);
|
|
154
|
+
const dim = (t) => c("2", t);
|
|
155
|
+
const bold = (t) => c("1", t);
|
|
156
|
+
const line = dim("─".repeat(60));
|
|
63
157
|
const deployedAt = new Date();
|
|
64
158
|
const pad = (n) => String(n).padStart(2, "0");
|
|
65
159
|
const stamp = `${deployedAt.getFullYear()}-${pad(deployedAt.getMonth() + 1)}-${pad(deployedAt.getDate())} ${pad(deployedAt.getHours())}:${pad(deployedAt.getMinutes())}:${pad(deployedAt.getSeconds())}`;
|
|
66
|
-
console.log(
|
|
160
|
+
console.log("");
|
|
161
|
+
if (!uploadResponse.ok) {
|
|
162
|
+
const statusStr = uploadResponse.status !== undefined
|
|
163
|
+
? `HTTP ${uploadResponse.status}`
|
|
164
|
+
: "network error (no response)";
|
|
165
|
+
console.error(red(bold("✗ DEPLOY FAILED — transport error")));
|
|
166
|
+
console.error(line);
|
|
167
|
+
console.error(` ${dim("host: ")} ${opt.host}`);
|
|
168
|
+
console.error(` ${dim("status: ")} ${statusStr}`);
|
|
169
|
+
console.error(` ${dim("elapsed: ")} ${elapsedMs}ms`);
|
|
170
|
+
if (uploadResponse.error !== undefined) {
|
|
171
|
+
const errText = typeof uploadResponse.error === "string"
|
|
172
|
+
? uploadResponse.error
|
|
173
|
+
: JSON.stringify(uploadResponse.error);
|
|
174
|
+
const clipped = errText.length > 400 ? errText.slice(0, 400) + `... (${errText.length} total)` : errText;
|
|
175
|
+
console.error(` ${dim("error: ")} ${clipped}`);
|
|
176
|
+
}
|
|
177
|
+
console.error(line);
|
|
178
|
+
process.exit(1);
|
|
179
|
+
}
|
|
180
|
+
const body = uploadResponse.data;
|
|
181
|
+
let appError = null;
|
|
182
|
+
if (body && typeof body === "object") {
|
|
183
|
+
if (__1.ErrorUtils.isError(body?.error))
|
|
184
|
+
appError = body.error;
|
|
185
|
+
else if (__1.ErrorUtils.isError(body?.data?.error))
|
|
186
|
+
appError = body.data.error;
|
|
187
|
+
else if (__1.ErrorUtils.isError(body?.data))
|
|
188
|
+
appError = body.data;
|
|
189
|
+
else if (__1.ErrorUtils.isError(body))
|
|
190
|
+
appError = body;
|
|
191
|
+
}
|
|
192
|
+
if (appError) {
|
|
193
|
+
console.error(red(bold("✗ DEPLOY REJECTED BY SERVER")));
|
|
194
|
+
console.error(line);
|
|
195
|
+
console.error(` ${dim("host: ")} ${opt.host}`);
|
|
196
|
+
console.error(` ${dim("code: ")} ${appError.code}`);
|
|
197
|
+
console.error(` ${dim("message: ")} ${appError.message}`);
|
|
198
|
+
if (appError.httpStatus)
|
|
199
|
+
console.error(` ${dim("httpCode: ")} ${appError.httpStatus}`);
|
|
200
|
+
if (appError.details !== undefined) {
|
|
201
|
+
const d = typeof appError.details === "string" ? appError.details : JSON.stringify(appError.details);
|
|
202
|
+
const clipped = d.length > 400 ? d.slice(0, 400) + `... (${d.length} total)` : d;
|
|
203
|
+
console.error(` ${dim("details: ")} ${clipped}`);
|
|
204
|
+
}
|
|
205
|
+
console.error(` ${dim("elapsed: ")} ${elapsedMs}ms`);
|
|
206
|
+
console.error(line);
|
|
207
|
+
process.exit(1);
|
|
208
|
+
}
|
|
209
|
+
const emptyResponse = body === null || body === undefined
|
|
210
|
+
|| (typeof body === "object" && Object.keys(body).length === 0);
|
|
211
|
+
if (emptyResponse) {
|
|
212
|
+
console.log(yellow(bold("⚠ DEPLOY LIKELY OK (server returned empty response)")));
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
console.log(green(bold("✓ DEPLOY SUCCESSFUL")));
|
|
216
|
+
}
|
|
217
|
+
console.log(line);
|
|
218
|
+
console.log(` ${dim("project: ")} ${opt.name}`);
|
|
219
|
+
console.log(` ${dim("package: ")} ${pkgName}`);
|
|
220
|
+
console.log(` ${dim("version: ")} ${oldVersion} ${dim("→")} ${bold(newVersion)}`);
|
|
221
|
+
console.log(` ${dim("archive: ")} ${archiveName}`);
|
|
222
|
+
console.log(` ${dim("host: ")} ${opt.host}`);
|
|
223
|
+
console.log(` ${dim("status: ")} HTTP ${uploadResponse.status}`);
|
|
224
|
+
console.log(` ${dim("elapsed: ")} ${elapsedMs}ms`);
|
|
225
|
+
console.log(` ${dim("time: ")} ${stamp}`);
|
|
226
|
+
const info = body?.data?.data;
|
|
227
|
+
if (info && typeof info === "object" && info.bluegreen === true) {
|
|
228
|
+
const slotBadge = info.activeThisDeploy
|
|
229
|
+
? green("← active in production")
|
|
230
|
+
: yellow("(inactive — deploy landed but nginx still points at " + info.active + "; run /pckg/switch to promote)");
|
|
231
|
+
console.log(` ${dim("mode: ")} blue-green`);
|
|
232
|
+
console.log(` ${dim("slot: ")} ${bold(String(info.slot))} ${slotBadge}`);
|
|
233
|
+
if (info.active && info.active !== info.slot) {
|
|
234
|
+
console.log(` ${dim("active: ")} ${info.active}`);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
else if (info && typeof info === "object" && info.bluegreen === false) {
|
|
238
|
+
console.log(` ${dim("mode: ")} single-instance`);
|
|
239
|
+
}
|
|
240
|
+
if (!emptyResponse) {
|
|
241
|
+
const payload = JSON.stringify(body);
|
|
242
|
+
const clipped = payload.length > 300 ? payload.slice(0, 300) + `... (${payload.length} total)` : payload;
|
|
243
|
+
console.log(` ${dim("response:")} ${clipped}`);
|
|
244
|
+
}
|
|
245
|
+
console.log(line);
|
|
246
|
+
console.log("");
|
|
67
247
|
return archiveName;
|
|
68
248
|
}
|
|
69
249
|
exports.Deploy = Deploy;
|
|
@@ -68,7 +68,14 @@ export declare class DeployerService extends BaseService {
|
|
|
68
68
|
};
|
|
69
69
|
data?: undefined;
|
|
70
70
|
} | {
|
|
71
|
-
data:
|
|
71
|
+
data: {
|
|
72
|
+
ok: boolean;
|
|
73
|
+
project: string;
|
|
74
|
+
bluegreen: boolean;
|
|
75
|
+
slot: "blue" | "green" | null;
|
|
76
|
+
active: "blue" | "green" | null;
|
|
77
|
+
activeThisDeploy: boolean;
|
|
78
|
+
};
|
|
72
79
|
error?: undefined;
|
|
73
80
|
}>;
|
|
74
81
|
switchSlot(data: {
|
|
@@ -290,7 +290,16 @@ class DeployerService extends BaseService_1.BaseService {
|
|
|
290
290
|
}
|
|
291
291
|
}, 5000);
|
|
292
292
|
}
|
|
293
|
-
return { data:
|
|
293
|
+
return { data: {
|
|
294
|
+
ok: true,
|
|
295
|
+
project: realName,
|
|
296
|
+
bluegreen: !!projectState,
|
|
297
|
+
slot: projectState?.lastDeployed ?? null,
|
|
298
|
+
active: projectState?.active ?? null,
|
|
299
|
+
activeThisDeploy: projectState
|
|
300
|
+
? projectState.active === projectState.lastDeployed
|
|
301
|
+
: true,
|
|
302
|
+
} };
|
|
294
303
|
}
|
|
295
304
|
finally {
|
|
296
305
|
this.busy.set(realName, false);
|
|
@@ -424,15 +424,22 @@ class Http {
|
|
|
424
424
|
const hasBody = opts.body !== undefined && opts.body !== null;
|
|
425
425
|
const allowBody = opts.allowBody ?? (opts.method !== "GET" && opts.method !== "DELETE");
|
|
426
426
|
if (hasBody && allowBody) {
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
headers["content-type"]
|
|
430
|
-
|
|
431
|
-
if (ct2.includes("application/json") && typeof opts.body !== "string" && !(opts.body instanceof Buffer)) {
|
|
432
|
-
body = JSON.stringify(opts.body);
|
|
427
|
+
if (typeof FormData !== "undefined" && opts.body instanceof FormData) {
|
|
428
|
+
body = opts.body;
|
|
429
|
+
delete headers["content-type"];
|
|
430
|
+
delete headers["Content-Type"];
|
|
433
431
|
}
|
|
434
432
|
else {
|
|
435
|
-
|
|
433
|
+
const ct = (headers["content-type"] || headers["Content-Type"] || "").toLowerCase();
|
|
434
|
+
if (!ct)
|
|
435
|
+
headers["content-type"] = "application/json";
|
|
436
|
+
const ct2 = (headers["content-type"] || headers["Content-Type"] || "").toLowerCase();
|
|
437
|
+
if (ct2.includes("application/json") && typeof opts.body !== "string" && !(opts.body instanceof Buffer)) {
|
|
438
|
+
body = JSON.stringify(opts.body);
|
|
439
|
+
}
|
|
440
|
+
else {
|
|
441
|
+
body = opts.body;
|
|
442
|
+
}
|
|
436
443
|
}
|
|
437
444
|
}
|
|
438
445
|
const responseType = opts.responseType ?? "json";
|
package/dist/index.d.ts
CHANGED
|
@@ -17,4 +17,5 @@ import { Http } from "./apiServer/http/Http";
|
|
|
17
17
|
import { MicroserviceHost } from "./apiServer/external/MicroserviceHost";
|
|
18
18
|
import { MicroserviceClient } from "./apiServer/external/MicroserviceClient";
|
|
19
19
|
import { Deploy } from "./apiServer/deployment/Deploy";
|
|
20
|
-
|
|
20
|
+
import { Activate } from "./apiServer/deployment/Activate";
|
|
21
|
+
export { MicroserviceHost, MicroserviceClient, Http, ZipUtils, UID, YYYYMMDDHH, JSONStableStringify, APIService, Initializer, LocalRequest, ValidationModel, MysqlService, TimeframeService, Validator, LogService, DataProvider, ErrorUtils, ExternalService, DBService, Deploy, Activate, S_MONITOR_REGISTRATE_ACTION };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.S_MONITOR_REGISTRATE_ACTION = exports.Deploy = exports.DBService = exports.ExternalService = exports.ErrorUtils = exports.DataProvider = exports.LogService = exports.Validator = exports.TimeframeService = exports.MysqlService = exports.LocalRequest = exports.Initializer = exports.APIService = exports.JSONStableStringify = exports.YYYYMMDDHH = exports.UID = exports.ZipUtils = exports.Http = exports.MicroserviceClient = exports.MicroserviceHost = void 0;
|
|
3
|
+
exports.S_MONITOR_REGISTRATE_ACTION = exports.Activate = exports.Deploy = exports.DBService = exports.ExternalService = exports.ErrorUtils = exports.DataProvider = exports.LogService = exports.Validator = exports.TimeframeService = exports.MysqlService = exports.LocalRequest = exports.Initializer = exports.APIService = exports.JSONStableStringify = exports.YYYYMMDDHH = exports.UID = exports.ZipUtils = exports.Http = exports.MicroserviceClient = exports.MicroserviceHost = void 0;
|
|
4
4
|
const APIService_1 = require("./apiServer/APIService");
|
|
5
5
|
Object.defineProperty(exports, "APIService", { enumerable: true, get: function () { return APIService_1.APIService; } });
|
|
6
6
|
Object.defineProperty(exports, "Initializer", { enumerable: true, get: function () { return APIService_1.Initializer; } });
|
|
@@ -40,3 +40,5 @@ const MicroserviceClient_1 = require("./apiServer/external/MicroserviceClient");
|
|
|
40
40
|
Object.defineProperty(exports, "MicroserviceClient", { enumerable: true, get: function () { return MicroserviceClient_1.MicroserviceClient; } });
|
|
41
41
|
const Deploy_1 = require("./apiServer/deployment/Deploy");
|
|
42
42
|
Object.defineProperty(exports, "Deploy", { enumerable: true, get: function () { return Deploy_1.Deploy; } });
|
|
43
|
+
const Activate_1 = require("./apiServer/deployment/Activate");
|
|
44
|
+
Object.defineProperty(exports, "Activate", { enumerable: true, get: function () { return Activate_1.Activate; } });
|