badmfck-api-server 4.1.37 → 4.1.39

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.
@@ -249,10 +249,11 @@ class APIService extends BaseService_1.BaseService {
249
249
  origin: (origin, callback) => {
250
250
  if (!origin)
251
251
  return callback(null, true);
252
+ if (this.noCors)
253
+ return callback(null, true);
252
254
  try {
253
255
  const o = new URL(String(origin));
254
256
  const originNorm = o.origin.replace(/\/$/, "");
255
- const hostNorm = o.host.replace(/\/$/, "");
256
257
  const ok = corsSet.has(originNorm);
257
258
  return callback(null, ok);
258
259
  }
@@ -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;
@@ -49,10 +49,12 @@ declare const _TConfig: {
49
49
  };
50
50
  readonly $__bluegreen_optional: true;
51
51
  readonly nginx: {
52
- readonly config: "";
53
- readonly $__config_optional: true;
54
- readonly symlink: "";
55
- readonly $__symlink_optional: true;
52
+ readonly config_dir: "";
53
+ readonly $__config_dir_optional: true;
54
+ readonly domain: "";
55
+ readonly $__domain_optional: true;
56
+ readonly upstream_name: "";
57
+ readonly $__upstream_name_optional: true;
56
58
  };
57
59
  readonly $__nginx_optional: true;
58
60
  };
@@ -69,8 +71,9 @@ export declare const REQ_DEPLOYMENT_CONFIG: Req<void, Map<string, {
69
71
  green_config: {};
70
72
  } | undefined;
71
73
  nginx?: {
72
- config?: string | undefined;
73
- symlink?: string | undefined;
74
+ config_dir?: string | undefined;
75
+ domain?: string | undefined;
76
+ upstream_name?: string | undefined;
74
77
  } | undefined;
75
78
  name: string;
76
79
  destination: string;
@@ -98,8 +101,9 @@ export declare const REQ_DEPLOYMENT_CONFIG_ADD: Req<{
98
101
  green_config: {};
99
102
  } | undefined;
100
103
  nginx?: {
101
- config?: string | undefined;
102
- symlink?: string | undefined;
104
+ config_dir?: string | undefined;
105
+ domain?: string | undefined;
106
+ upstream_name?: string | undefined;
103
107
  } | undefined;
104
108
  name: string;
105
109
  destination: string;
@@ -126,8 +130,9 @@ export declare const REQ_DEPLOYMENT_CONFIG_ADD: Req<{
126
130
  green_config: {};
127
131
  } | undefined;
128
132
  nginx?: {
129
- config?: string | undefined;
130
- symlink?: string | undefined;
133
+ config_dir?: string | undefined;
134
+ domain?: string | undefined;
135
+ upstream_name?: string | undefined;
131
136
  } | undefined;
132
137
  name: string;
133
138
  destination: string;
@@ -80,10 +80,12 @@ const _TConfig = {
80
80
  },
81
81
  $__bluegreen_optional: true,
82
82
  nginx: {
83
- config: "",
84
- $__config_optional: true,
85
- symlink: "",
86
- $__symlink_optional: true,
83
+ config_dir: "",
84
+ $__config_dir_optional: true,
85
+ domain: "",
86
+ $__domain_optional: true,
87
+ upstream_name: "",
88
+ $__upstream_name_optional: true,
87
89
  },
88
90
  $__nginx_optional: true,
89
91
  };
@@ -4,6 +4,8 @@ interface IDeployerParams {
4
4
  host: string;
5
5
  username: string;
6
6
  password: string;
7
+ includes?: string[];
8
+ excludes?: string[];
7
9
  }
8
10
  export declare function Deploy(opt: IDeployerParams | string): Promise<string>;
9
11
  export {};
@@ -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
- if (configSrc.indexOf("static scheme:Scheme = local;") !== -1) {
27
- configSrc = configSrc.replace("static scheme:Scheme = local;", "static scheme:Scheme = live;");
28
- fs_1.default.writeFileSync(config, configSrc);
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 parts = String(pkg.version || "0.0.0").split(".").map(p => parseInt(p, 10) || 0);
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: ${pkg.version} -> ${newVersion}`);
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,178 @@ async function Deploy(opt) {
44
65
  console.log("Build");
45
66
  var execResult = run("npm run build");
46
67
  console.log(execResult);
47
- console.log("Archiving");
48
- execResult = run("tar -czvf " + archiveName + " ./bin package.json");
49
- console.log(execResult);
68
+ const items = ["./bin", "package.json"];
69
+ if (Array.isArray(opt.includes)) {
70
+ for (const inc of opt.includes) {
71
+ if (typeof inc !== "string" || inc.length === 0)
72
+ continue;
73
+ if (inc.startsWith("-")) {
74
+ console.warn(`Skipping include that starts with '-': ${inc}`);
75
+ continue;
76
+ }
77
+ if (!fs_1.default.existsSync(inc)) {
78
+ console.warn(`Include path does not exist (tar will error): ${inc}`);
79
+ }
80
+ items.push(inc);
81
+ }
82
+ }
83
+ const excludes = new Set();
84
+ const addExclude = (raw, source) => {
85
+ if (typeof raw !== "string")
86
+ return;
87
+ const trimmed = raw.trim();
88
+ if (trimmed.length === 0 || trimmed.startsWith("#"))
89
+ return;
90
+ if (trimmed.startsWith("-")) {
91
+ console.warn(`Skipping exclude starting with '-' from ${source}: ${trimmed}`);
92
+ return;
93
+ }
94
+ excludes.add(trimmed);
95
+ };
96
+ const ignoreFile = path_1.default.resolve(".deployignore");
97
+ if (fs_1.default.existsSync(ignoreFile)) {
98
+ const lines = fs_1.default.readFileSync(ignoreFile, "utf-8").split(/\r?\n/);
99
+ for (const line of lines)
100
+ addExclude(line, ".deployignore");
101
+ }
102
+ if (Array.isArray(opt.excludes)) {
103
+ for (const ex of opt.excludes)
104
+ addExclude(ex, "config.excludes");
105
+ }
106
+ const excludeArgs = Array.from(excludes).map(p => `--exclude=${p}`);
107
+ console.log("Archiving:", items.join(", "));
108
+ if (excludeArgs.length > 0)
109
+ console.log("Excluding:", Array.from(excludes).join(", "));
110
+ try {
111
+ (0, child_process_1.execFileSync)("tar", ["-czvf", archiveName, ...excludeArgs, ...items], { encoding: "utf-8", stdio: "inherit" });
112
+ }
113
+ catch (err) {
114
+ console.error(`\ntar failed while creating ${archiveName}\n`);
115
+ const out = (err.stdout || "").toString().trim();
116
+ const errOut = (err.stderr || "").toString().trim();
117
+ if (out)
118
+ console.error(out);
119
+ if (errOut)
120
+ console.error(errOut);
121
+ process.exit(typeof err.status === "number" ? err.status : 1);
122
+ }
50
123
  if (!fs_1.default.existsSync(path_1.default.resolve(archiveName))) {
51
124
  console.error("ARCHIVE NOT CREATED!");
52
125
  return "";
53
126
  }
54
- const cmd = `curl \
55
- -H "Authorization: Bearer ${crypto_1.default.createHash("sha256").update(opt.username + ":" + opt.password).digest("hex")}" \
56
- -F "token=${opt.token}" \
57
- -F "name=${opt.name}" \
58
- -F "file=@${archiveName}" \
59
- "${opt.host}"`;
60
- console.log("Deploying: \n" + cmd);
61
- execResult = run(cmd);
62
- console.log(execResult);
127
+ const authToken = crypto_1.default.createHash("sha256").update(opt.username + ":" + opt.password).digest("hex");
128
+ const archivePath = path_1.default.resolve(archiveName);
129
+ const fileBlob = await fs_1.default.openAsBlob(archivePath);
130
+ const formData = new FormData();
131
+ formData.set("token", opt.token);
132
+ formData.set("name", opt.name);
133
+ formData.set("file", fileBlob, archiveName);
134
+ console.log(`Deploying to ${opt.host} (archive: ${archiveName})`);
135
+ const startedAt = Date.now();
136
+ const uploadResponse = await __1.Http.post(opt.host, formData, {
137
+ headers: { authorization: `Bearer ${authToken}` },
138
+ timeoutMs: 5 * 60 * 1000,
139
+ retry: { enabled: false },
140
+ });
141
+ const elapsedMs = Date.now() - startedAt;
142
+ const useColor = process.stdout.isTTY && !process.env.NO_COLOR;
143
+ const c = (code, t) => useColor ? `\x1b[${code}m${t}\x1b[0m` : t;
144
+ const red = (t) => c("31", t);
145
+ const green = (t) => c("32", t);
146
+ const yellow = (t) => c("33", t);
147
+ const dim = (t) => c("2", t);
148
+ const bold = (t) => c("1", t);
149
+ const line = dim("─".repeat(60));
63
150
  const deployedAt = new Date();
64
151
  const pad = (n) => String(n).padStart(2, "0");
65
152
  const stamp = `${deployedAt.getFullYear()}-${pad(deployedAt.getMonth() + 1)}-${pad(deployedAt.getDate())} ${pad(deployedAt.getHours())}:${pad(deployedAt.getMinutes())}:${pad(deployedAt.getSeconds())}`;
66
- console.log(`\nDeployed v${newVersion} at ${stamp}\n`);
153
+ console.log("");
154
+ if (!uploadResponse.ok) {
155
+ const statusStr = uploadResponse.status !== undefined
156
+ ? `HTTP ${uploadResponse.status}`
157
+ : "network error (no response)";
158
+ console.error(red(bold("✗ DEPLOY FAILED — transport error")));
159
+ console.error(line);
160
+ console.error(` ${dim("host: ")} ${opt.host}`);
161
+ console.error(` ${dim("status: ")} ${statusStr}`);
162
+ console.error(` ${dim("elapsed: ")} ${elapsedMs}ms`);
163
+ if (uploadResponse.error !== undefined) {
164
+ const errText = typeof uploadResponse.error === "string"
165
+ ? uploadResponse.error
166
+ : JSON.stringify(uploadResponse.error);
167
+ const clipped = errText.length > 400 ? errText.slice(0, 400) + `... (${errText.length} total)` : errText;
168
+ console.error(` ${dim("error: ")} ${clipped}`);
169
+ }
170
+ console.error(line);
171
+ process.exit(1);
172
+ }
173
+ const body = uploadResponse.data;
174
+ let appError = null;
175
+ if (body && typeof body === "object") {
176
+ if (__1.ErrorUtils.isError(body?.error))
177
+ appError = body.error;
178
+ else if (__1.ErrorUtils.isError(body?.data?.error))
179
+ appError = body.data.error;
180
+ else if (__1.ErrorUtils.isError(body?.data))
181
+ appError = body.data;
182
+ else if (__1.ErrorUtils.isError(body))
183
+ appError = body;
184
+ }
185
+ if (appError) {
186
+ console.error(red(bold("✗ DEPLOY REJECTED BY SERVER")));
187
+ console.error(line);
188
+ console.error(` ${dim("host: ")} ${opt.host}`);
189
+ console.error(` ${dim("code: ")} ${appError.code}`);
190
+ console.error(` ${dim("message: ")} ${appError.message}`);
191
+ if (appError.httpStatus)
192
+ console.error(` ${dim("httpCode: ")} ${appError.httpStatus}`);
193
+ if (appError.details !== undefined) {
194
+ const d = typeof appError.details === "string" ? appError.details : JSON.stringify(appError.details);
195
+ const clipped = d.length > 400 ? d.slice(0, 400) + `... (${d.length} total)` : d;
196
+ console.error(` ${dim("details: ")} ${clipped}`);
197
+ }
198
+ console.error(` ${dim("elapsed: ")} ${elapsedMs}ms`);
199
+ console.error(line);
200
+ process.exit(1);
201
+ }
202
+ const emptyResponse = body === null || body === undefined
203
+ || (typeof body === "object" && Object.keys(body).length === 0);
204
+ if (emptyResponse) {
205
+ console.log(yellow(bold("⚠ DEPLOY LIKELY OK (server returned empty response)")));
206
+ }
207
+ else {
208
+ console.log(green(bold("✓ DEPLOY SUCCESSFUL")));
209
+ }
210
+ console.log(line);
211
+ console.log(` ${dim("project: ")} ${opt.name}`);
212
+ console.log(` ${dim("package: ")} ${pkgName}`);
213
+ console.log(` ${dim("version: ")} ${oldVersion} ${dim("→")} ${bold(newVersion)}`);
214
+ console.log(` ${dim("archive: ")} ${archiveName}`);
215
+ console.log(` ${dim("host: ")} ${opt.host}`);
216
+ console.log(` ${dim("status: ")} HTTP ${uploadResponse.status}`);
217
+ console.log(` ${dim("elapsed: ")} ${elapsedMs}ms`);
218
+ console.log(` ${dim("time: ")} ${stamp}`);
219
+ const info = body?.data?.data;
220
+ if (info && typeof info === "object" && info.bluegreen === true) {
221
+ const slotBadge = info.activeThisDeploy
222
+ ? green("← active in production")
223
+ : yellow("(inactive — deploy landed but nginx still points at " + info.active + "; run /pckg/switch to promote)");
224
+ console.log(` ${dim("mode: ")} blue-green`);
225
+ console.log(` ${dim("slot: ")} ${bold(String(info.slot))} ${slotBadge}`);
226
+ if (info.active && info.active !== info.slot) {
227
+ console.log(` ${dim("active: ")} ${info.active}`);
228
+ }
229
+ }
230
+ else if (info && typeof info === "object" && info.bluegreen === false) {
231
+ console.log(` ${dim("mode: ")} single-instance`);
232
+ }
233
+ if (!emptyResponse) {
234
+ const payload = JSON.stringify(body);
235
+ const clipped = payload.length > 300 ? payload.slice(0, 300) + `... (${payload.length} total)` : payload;
236
+ console.log(` ${dim("response:")} ${clipped}`);
237
+ }
238
+ console.log(line);
239
+ console.log("");
67
240
  return archiveName;
68
241
  }
69
242
  exports.Deploy = Deploy;
@@ -46,8 +46,13 @@ export declare class DeployerService extends BaseService {
46
46
  setup: TDeployerSetup | null;
47
47
  notifier: IDeploymentNotifier | null;
48
48
  busy: Map<string, boolean>;
49
+ private _setupWatchDebounceTimer;
50
+ private _lastSetupHash;
49
51
  constructor(options: IDeployerServiceOptions);
50
52
  init(): Promise<void>;
53
+ private _loadSetup;
54
+ private _reloadSetup;
55
+ private _watchSetup;
51
56
  proceed(data: {
52
57
  auth?: string;
53
58
  name: string;
@@ -63,7 +68,14 @@ export declare class DeployerService extends BaseService {
63
68
  };
64
69
  data?: undefined;
65
70
  } | {
66
- data: boolean;
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
+ };
67
79
  error?: undefined;
68
80
  }>;
69
81
  switchSlot(data: {
@@ -107,6 +119,7 @@ export declare class DeployerService extends BaseService {
107
119
  }>;
108
120
  tokensMatch(a: string | undefined, b: string | undefined): boolean;
109
121
  private _checkUserAuth;
122
+ private _renderNginx;
110
123
  runPM2(found: IConfig, projectState?: IProjectState | null): Promise<IError | null>;
111
124
  private static readonly SKIP_DIRS;
112
125
  private static readonly TEMPLATE_EXTENSIONS;
@@ -37,6 +37,8 @@ class DeployerService extends BaseService_1.BaseService {
37
37
  setup = null;
38
38
  notifier = null;
39
39
  busy = new Map();
40
+ _setupWatchDebounceTimer = null;
41
+ _lastSetupHash = null;
40
42
  constructor(options) {
41
43
  super("DeployerService");
42
44
  this.options = options;
@@ -45,41 +47,84 @@ class DeployerService extends BaseService_1.BaseService {
45
47
  super.init();
46
48
  const cs = new ConfigService_1.ConfigService(this.options.projects_path);
47
49
  await cs.init();
50
+ const initial = await this._loadSetup();
51
+ if (!initial) {
52
+ throw new Error("Setup file missing/invalid at start — cannot start deployer");
53
+ }
54
+ this.setup = initial;
55
+ this._lastSetupHash = crypto_1.default.createHash("md5").update(JSON.stringify(initial)).digest("hex");
56
+ if (this.setup.notifier?.URL && this.setup.notifier?.KEY) {
57
+ this.notifier = new Notifier_1.Notifier({ URL: this.setup.notifier.URL, KEY: this.setup.notifier.KEY });
58
+ await this.notifier.init();
59
+ }
60
+ if (this.setup.watchdog) {
61
+ const wd = new Watchdog_1.Watchdog(this.notifier);
62
+ await wd.init();
63
+ }
64
+ this._watchSetup();
65
+ exports.REQ_DEPLOYMENT_PROCEED.listener = async (data) => this.proceed(data);
66
+ exports.REQ_DEPLOYMENT_SWITCH.listener = async (data) => this.switchSlot(data);
67
+ exports.REQ_DEPLOYMENT_STATUS.listener = async (data) => this.status(data);
68
+ }
69
+ async _loadSetup() {
48
70
  const setupFile = path_1.default.resolve(this.options.setup_path, "setup.json");
49
71
  if (!fs_1.default.existsSync(setupFile)) {
50
72
  (0, LogService_1.logError)("Setup file not found: " + setupFile);
51
- throw new Error("Setup file not found: " + setupFile);
73
+ return null;
52
74
  }
75
+ let raw;
53
76
  try {
54
- this.setup = JSON.parse(fs_1.default.readFileSync(setupFile, "utf-8"));
77
+ raw = fs_1.default.readFileSync(setupFile, "utf-8");
55
78
  }
56
79
  catch (e) {
57
80
  (0, LogService_1.logError)("Failed to read setup file: " + e.message);
58
- throw new Error("Failed to read setup file: " + e.message);
81
+ return null;
59
82
  }
60
- const validation = await __1.Validator.validateStructure(_TSetup, this.setup);
61
- if (validation && validation.length > 0) {
62
- (0, LogService_1.logError)("Setup file validation failed: " + JSON.stringify(validation));
63
- throw new Error("Setup file validation failed: " + JSON.stringify(validation));
83
+ let parsed;
84
+ try {
85
+ parsed = JSON.parse(raw);
64
86
  }
65
- if (!this.setup) {
66
- (0, LogService_1.logError)("Setup file is empty or invalid: " + setupFile);
67
- throw new Error("Setup file is empty or invalid: " + setupFile);
87
+ catch (e) {
88
+ (0, LogService_1.logError)("Failed to parse setup.json: " + e.message);
89
+ return null;
68
90
  }
69
- let notifier = null;
70
- if (this.setup && this.setup.notifier) {
71
- if (this.setup.notifier.URL && this.setup.notifier.KEY) {
72
- notifier = new Notifier_1.Notifier({ URL: this.setup.notifier.URL, KEY: this.setup.notifier.KEY });
73
- await notifier.init();
74
- }
91
+ const errors = await __1.Validator.validateStructure(_TSetup, parsed);
92
+ if (errors && errors.length > 0) {
93
+ (0, LogService_1.logError)("Setup validation failed: " + JSON.stringify(errors));
94
+ return null;
75
95
  }
76
- if (this.setup && this.setup.watchdog) {
77
- const wd = new Watchdog_1.Watchdog(notifier);
78
- await wd.init();
96
+ return parsed;
97
+ }
98
+ async _reloadSetup() {
99
+ const fresh = await this._loadSetup();
100
+ if (!fresh) {
101
+ (0, LogService_1.logWarn)("Setup reload failed — keeping previous good setup in memory");
102
+ return;
79
103
  }
80
- exports.REQ_DEPLOYMENT_PROCEED.listener = async (data) => this.proceed(data);
81
- exports.REQ_DEPLOYMENT_SWITCH.listener = async (data) => this.switchSlot(data);
82
- exports.REQ_DEPLOYMENT_STATUS.listener = async (data) => this.status(data);
104
+ const hash = crypto_1.default.createHash("md5").update(JSON.stringify(fresh)).digest("hex");
105
+ if (hash === this._lastSetupHash)
106
+ return;
107
+ this._lastSetupHash = hash;
108
+ this.setup = fresh;
109
+ (0, LogService_1.logWarn)("Setup reloaded from disk (users list refreshed live; notifier/watchdog need service restart to change)");
110
+ }
111
+ _watchSetup() {
112
+ try {
113
+ fs_1.default.watch(this.options.setup_path, (_eventType, filename) => {
114
+ if (filename !== "setup.json")
115
+ return;
116
+ if (this._setupWatchDebounceTimer)
117
+ clearTimeout(this._setupWatchDebounceTimer);
118
+ this._setupWatchDebounceTimer = setTimeout(() => {
119
+ this._setupWatchDebounceTimer = null;
120
+ this._reloadSetup();
121
+ }, 200);
122
+ });
123
+ }
124
+ catch (e) {
125
+ (0, LogService_1.logError)("Failed to fs.watch setup file: " + e.message);
126
+ }
127
+ setInterval(() => { this._reloadSetup(); }, 60_000).unref();
83
128
  }
84
129
  async proceed(data) {
85
130
  const user = this._checkUserAuth(data.auth);
@@ -109,6 +154,7 @@ class DeployerService extends BaseService_1.BaseService {
109
154
  const safeName = (path_1.default.basename(file.name || "").replace(/[^a-zA-Z0-9._-]/g, "_")) || "upload";
110
155
  let projectState = null;
111
156
  let projectStatePath = null;
157
+ let projectStateWasCreated = false;
112
158
  if (found.bluegreen && found.bluegreen.blue_config && found.bluegreen.green_config) {
113
159
  projectStatePath = path_1.default.resolve(this.options.projects_path, data.name + "_state_.json");
114
160
  if (fs_1.default.existsSync(projectStatePath)) {
@@ -125,6 +171,7 @@ class DeployerService extends BaseService_1.BaseService {
125
171
  lastDeployed: "blue",
126
172
  history: []
127
173
  };
174
+ projectStateWasCreated = true;
128
175
  }
129
176
  else {
130
177
  projectState.lastDeployed = projectState.active === "blue" ? "green" : "blue";
@@ -186,37 +233,31 @@ class DeployerService extends BaseService_1.BaseService {
186
233
  if (found.config) {
187
234
  await this.applyConfig(found.destination, found.config);
188
235
  }
189
- if (found.nginx?.config) {
190
- const nginxPath = path_1.default.resolve(found.destination, found.nginx.config);
191
- if (!fs_1.default.existsSync(nginxPath)) {
192
- if (found.email && found.email.length > 0) {
193
- for (let email of found.email) {
194
- this.notifier?.notifyEmailStatus(email, "error-nginx-missing", found.name, user.login);
195
- }
196
- }
197
- throw { code: 22, message: "nginx config declared but file not found: " + nginxPath, httpStatus: 500 };
198
- }
199
- try {
200
- NginxHelper_1.NginxHelper.applyTemplate(nginxPath, found.config ?? {});
201
- }
202
- catch (e) {
236
+ if (found.pm2) {
237
+ const pm2Result = await this.runPM2(found, projectState);
238
+ if (__1.ErrorUtils.isError(pm2Result)) {
203
239
  if (found.email && found.email.length > 0) {
204
240
  for (let email of found.email) {
205
- this.notifier?.notifyEmailStatus(email, "error-nginx-render", found.name, user.login);
241
+ this.notifier?.notifyEmailStatus(email, "error-pm2", found.name, user.login);
206
242
  }
207
243
  }
208
- throw { code: 23, message: "nginx template render failed: " + String(e?.message ?? e), httpStatus: 500 };
244
+ throw pm2Result;
209
245
  }
210
246
  }
211
- if (found.pm2) {
212
- const pm2Result = await this.runPM2(found, projectState);
213
- if (__1.ErrorUtils.isError(pm2Result)) {
247
+ if (found.nginx?.config_dir && (!projectState || projectStateWasCreated)) {
248
+ const activeCfg = (found.config ?? {});
249
+ const nginxErr = await this._renderNginx(found, data.name, activeCfg.PORT ?? "");
250
+ if (nginxErr) {
214
251
  if (found.email && found.email.length > 0) {
215
252
  for (let email of found.email) {
216
- this.notifier?.notifyEmailStatus(email, "error-pm2", found.name, user.login);
253
+ this.notifier?.notifyEmailStatus(email, "error-nginx", found.name, user.login);
217
254
  }
218
255
  }
219
- throw pm2Result;
256
+ throw nginxErr;
257
+ }
258
+ const hookErr = await NginxHelper_1.NginxHelper.runHook(found.bluegreen?.on_switch);
259
+ if (hookErr) {
260
+ (0, LogService_1.logWarn)("nginx reload after initial deploy failed: " + hookErr);
220
261
  }
221
262
  }
222
263
  if (projectState && projectStatePath) {
@@ -249,7 +290,16 @@ class DeployerService extends BaseService_1.BaseService {
249
290
  }
250
291
  }, 5000);
251
292
  }
252
- return { data: true };
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
+ } };
253
303
  }
254
304
  finally {
255
305
  this.busy.set(realName, false);
@@ -313,14 +363,18 @@ class DeployerService extends BaseService_1.BaseService {
313
363
  fs_1.default.writeFileSync(tmp, JSON.stringify(projectState, null, 4), "utf-8");
314
364
  fs_1.default.renameSync(tmp, projectStatePath);
315
365
  let hookError = null;
316
- if (found.nginx?.config && found.nginx?.symlink) {
317
- const symlinkTarget = path_1.default.resolve(found.destination, target, found.nginx.config);
318
- const swapErr = NginxHelper_1.NginxHelper.swapSymlink(found.nginx.symlink, symlinkTarget);
319
- if (swapErr) {
320
- return { error: swapErr };
366
+ if (found.nginx?.config_dir) {
367
+ const slotCfg = target === "blue" ? found.bluegreen.blue_config : found.bluegreen.green_config;
368
+ const merged = { ...(found.config ?? {}), ...(slotCfg ?? {}) };
369
+ const nginxErr = await this._renderNginx(found, data.name, merged.PORT ?? "");
370
+ if (nginxErr) {
371
+ (0, LogService_1.logError)("nginx render during switch failed (state already flipped): " + nginxErr.message);
372
+ hookError = nginxErr.message;
321
373
  }
322
374
  }
323
- hookError = await NginxHelper_1.NginxHelper.runHook(found.bluegreen.on_switch);
375
+ if (!hookError) {
376
+ hookError = await NginxHelper_1.NginxHelper.runHook(found.bluegreen.on_switch);
377
+ }
324
378
  const status = hookError ? "switch-hook-failed" : action === "rollback" ? `rolled-back-to-${target}` : `switched-to-${target}`;
325
379
  if (found.email && found.email.length > 0) {
326
380
  for (const email of found.email) {
@@ -387,6 +441,43 @@ class DeployerService extends BaseService_1.BaseService {
387
441
  }
388
442
  return matched;
389
443
  }
444
+ async _renderNginx(found, projectName, activePort) {
445
+ if (!found.nginx?.config_dir)
446
+ return null;
447
+ if (!activePort) {
448
+ return { code: 52, message: "Cannot render nginx: no PORT in merged config for project " + projectName, httpStatus: 500 };
449
+ }
450
+ const dir = found.nginx.config_dir;
451
+ try {
452
+ fs_1.default.mkdirSync(dir, { recursive: true });
453
+ }
454
+ catch (e) {
455
+ return { code: 50, message: "Cannot create nginx config_dir: " + String(e?.message ?? e), httpStatus: 500 };
456
+ }
457
+ const safeName = projectName.replace(/[^a-zA-Z0-9_]/g, "_");
458
+ const upstreamName = found.nginx.upstream_name || (safeName + "_backend");
459
+ const upstreamPath = path_1.default.resolve(dir, projectName + ".upstream.conf");
460
+ const serverPath = path_1.default.resolve(dir, projectName + ".server.conf");
461
+ const domain = found.nginx.domain || projectName;
462
+ if (!found.nginx.domain) {
463
+ (0, LogService_1.logWarn)("nginx.domain not set for project " + projectName +
464
+ " — seeding server.conf with server_name=" + projectName + " (edit or delete .server.conf to fix)");
465
+ }
466
+ const seedResult = NginxHelper_1.NginxHelper.seedServerConfig(serverPath, NginxHelper_1.DEFAULT_SERVER_TEMPLATE, {
467
+ DOMAIN: domain,
468
+ UPSTREAM: upstreamName,
469
+ });
470
+ if (seedResult.error)
471
+ return seedResult.error;
472
+ if (seedResult.seeded) {
473
+ (0, LogService_1.logWarn)("Seeded nginx server config for " + projectName + " at " + serverPath +
474
+ " — run `certbot --nginx -d " + domain + "` to add SSL");
475
+ }
476
+ const upstreamErr = NginxHelper_1.NginxHelper.writeUpstream(upstreamPath, upstreamName, activePort);
477
+ if (upstreamErr)
478
+ return upstreamErr;
479
+ return null;
480
+ }
390
481
  async runPM2(found, projectState) {
391
482
  let pm2attrs = projectState?.lastDeployed === "blue" ? found.bluegreen?.blue_pm2_attributes : found.bluegreen?.green_pm2_attributes;
392
483
  const pm2Attrs = Array.isArray(pm2attrs) ? pm2attrs.filter(s => typeof s === "string" && s.length > 0) : found.pm2_attributes;
@@ -1,9 +1,10 @@
1
+ import { IError } from "../structures/Interfaces";
2
+ export declare const DEFAULT_SERVER_TEMPLATE = "# Seeded by deployer for _{{DOMAIN}}_. SAFE TO EDIT.\n# Deployer NEVER overwrites this file after the initial seed.\n# Add SSL by running: certbot --nginx -d _{{DOMAIN}}_\n\nserver {\n server_name _{{DOMAIN}}_;\n listen 80;\n\n location / {\n proxy_pass http://_{{UPSTREAM}}_;\n proxy_http_version 1.1;\n proxy_set_header Host $host;\n proxy_set_header X-Real-IP $remote_addr;\n proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n proxy_set_header X-Forwarded-Proto $scheme;\n proxy_read_timeout 60s;\n }\n}\n";
1
3
  export declare class NginxHelper {
2
- static applyTemplate(filePath: string, values: Record<string, string>): void;
3
- static swapSymlink(symlinkPath: string, target: string): {
4
- code: number;
5
- message: string;
6
- httpStatus: number;
7
- } | null;
4
+ static writeUpstream(filePath: string, upstreamName: string, port: string): IError | null;
5
+ static seedServerConfig(filePath: string, template: string, values: Record<string, string>): {
6
+ seeded: boolean;
7
+ error?: IError;
8
+ };
8
9
  static runHook(cmd: string[] | undefined | null): Promise<string | null>;
9
10
  }
@@ -3,50 +3,78 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.NginxHelper = void 0;
6
+ exports.NginxHelper = exports.DEFAULT_SERVER_TEMPLATE = void 0;
7
7
  const fs_1 = __importDefault(require("fs"));
8
8
  const path_1 = __importDefault(require("path"));
9
9
  const child_process_1 = require("child_process");
10
10
  const util_1 = require("util");
11
- const crypto_1 = __importDefault(require("crypto"));
12
11
  const LogService_1 = require("../LogService");
13
12
  const execFileAsync = (0, util_1.promisify)(child_process_1.execFile);
13
+ exports.DEFAULT_SERVER_TEMPLATE = `# Seeded by deployer for _{{DOMAIN}}_. SAFE TO EDIT.
14
+ # Deployer NEVER overwrites this file after the initial seed.
15
+ # Add SSL by running: certbot --nginx -d _{{DOMAIN}}_
16
+
17
+ server {
18
+ server_name _{{DOMAIN}}_;
19
+ listen 80;
20
+
21
+ location / {
22
+ proxy_pass http://_{{UPSTREAM}}_;
23
+ proxy_http_version 1.1;
24
+ proxy_set_header Host $host;
25
+ proxy_set_header X-Real-IP $remote_addr;
26
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
27
+ proxy_set_header X-Forwarded-Proto $scheme;
28
+ proxy_read_timeout 60s;
29
+ }
30
+ }
31
+ `;
14
32
  class NginxHelper {
15
- static applyTemplate(filePath, values) {
16
- if (!fs_1.default.existsSync(filePath))
17
- throw new Error("nginx config file not found: " + filePath);
18
- const original = fs_1.default.readFileSync(filePath, "utf-8");
19
- const hashBefore = crypto_1.default.createHash("md5").update(original).digest("hex");
20
- let content = original;
21
- for (const [key, value] of Object.entries(values)) {
22
- content = content.replaceAll(`_{{${key}}}_`, value);
23
- }
24
- const hashAfter = crypto_1.default.createHash("md5").update(content).digest("hex");
25
- if (hashBefore !== hashAfter) {
26
- fs_1.default.writeFileSync(filePath, content, "utf-8");
33
+ static writeUpstream(filePath, upstreamName, port) {
34
+ const content = `# Auto-generated by deployer on each activate — do not edit.
35
+ upstream ${upstreamName} {
36
+ server 127.0.0.1:${port};
37
+ }
38
+ `;
39
+ try {
40
+ const tmp = filePath + ".tmp";
41
+ try {
42
+ fs_1.default.unlinkSync(tmp);
43
+ }
44
+ catch { }
45
+ fs_1.default.writeFileSync(tmp, content, "utf-8");
46
+ fs_1.default.renameSync(tmp, filePath);
47
+ return null;
27
48
  }
28
- const leftover = content.match(/_\{\{[A-Za-z0-9_]+\}\}_/g);
29
- if (leftover && leftover.length > 0) {
30
- const unique = [...new Set(leftover)];
31
- (0, LogService_1.logWarn)("Unresolved template placeholders in nginx config " + filePath + ": " + unique.join(", "));
49
+ catch (e) {
50
+ return { code: 48, message: "Failed to write nginx upstream file: " + String(e?.message ?? e), httpStatus: 500 };
32
51
  }
33
52
  }
34
- static swapSymlink(symlinkPath, target) {
35
- if (!fs_1.default.existsSync(target)) {
36
- return { code: 48, message: "nginx symlink target missing: " + target, httpStatus: 500 };
53
+ static seedServerConfig(filePath, template, values) {
54
+ if (fs_1.default.existsSync(filePath)) {
55
+ return { seeded: false };
37
56
  }
38
57
  try {
39
- const tmp = symlinkPath + ".tmp";
58
+ let content = template;
59
+ for (const [key, value] of Object.entries(values)) {
60
+ content = content.replaceAll(`_{{${key}}}_`, value);
61
+ }
62
+ const leftover = content.match(/_\{\{[A-Za-z0-9_]+\}\}_/g);
63
+ if (leftover && leftover.length > 0) {
64
+ const unique = [...new Set(leftover)];
65
+ (0, LogService_1.logWarn)("Unresolved placeholders in seeded nginx server config " + filePath + ": " + unique.join(", "));
66
+ }
67
+ const tmp = filePath + ".tmp";
40
68
  try {
41
69
  fs_1.default.unlinkSync(tmp);
42
70
  }
43
71
  catch { }
44
- fs_1.default.symlinkSync(target, tmp);
45
- fs_1.default.renameSync(tmp, symlinkPath);
46
- return null;
72
+ fs_1.default.writeFileSync(tmp, content, "utf-8");
73
+ fs_1.default.renameSync(tmp, filePath);
74
+ return { seeded: true };
47
75
  }
48
76
  catch (e) {
49
- return { code: 49, message: "nginx symlink swap failed: " + String(e?.message ?? e), httpStatus: 500 };
77
+ return { seeded: false, error: { code: 49, message: "Failed to seed nginx server config: " + String(e?.message ?? e), httpStatus: 500 } };
50
78
  }
51
79
  }
52
80
  static async runHook(cmd) {
@@ -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
- const ct = (headers["content-type"] || headers["Content-Type"] || "").toLowerCase();
428
- if (!ct)
429
- headers["content-type"] = "application/json";
430
- const ct2 = (headers["content-type"] || headers["Content-Type"] || "").toLowerCase();
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
- body = opts.body;
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
- export { MicroserviceHost, MicroserviceClient, Http, ZipUtils, UID, YYYYMMDDHH, JSONStableStringify, APIService, Initializer, LocalRequest, ValidationModel, MysqlService, TimeframeService, Validator, LogService, DataProvider, ErrorUtils, ExternalService, DBService, Deploy, S_MONITOR_REGISTRATE_ACTION };
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; } });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "badmfck-api-server",
3
- "version": "4.1.37",
3
+ "version": "4.1.39",
4
4
  "description": "Simple API http server based on express",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",