badmfck-api-server 4.1.28 → 4.1.29
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/APIService.d.ts +5 -0
- package/dist/apiServer/APIService.js +8 -0
- package/dist/apiServer/deployment/ConfigService.d.ts +148 -0
- package/dist/apiServer/deployment/ConfigService.js +229 -0
- package/dist/apiServer/deployment/Deployer.d.ts +8 -0
- package/dist/apiServer/deployment/Deployer.js +55 -0
- package/dist/apiServer/deployment/DeployerService.d.ts +109 -0
- package/dist/apiServer/deployment/DeployerService.js +543 -0
- package/dist/apiServer/deployment/NginxHelper.d.ts +2 -0
- package/dist/apiServer/deployment/NginxHelper.js +37 -0
- package/dist/apiServer/deployment/Notifier.d.ts +16 -0
- package/dist/apiServer/deployment/Notifier.js +59 -0
- package/dist/apiServer/deployment/Watchdog.d.ts +14 -0
- package/dist/apiServer/deployment/Watchdog.js +61 -0
- package/dist/apiServer/routes/Liveness.js +1 -1
- package/dist/apiServer/routes/Readiness.d.ts +1 -1
- package/dist/apiServer/routes/Readiness.js +1 -1
- package/package.json +1 -1
|
@@ -5,6 +5,7 @@ import { IBaseEndpoint } from './BaseEndpoint';
|
|
|
5
5
|
import { HTTPRequestVO, IError, TransferPacketVO } from './structures/Interfaces';
|
|
6
6
|
import Signal, { Req } from "badmfck-signal";
|
|
7
7
|
import http from 'http';
|
|
8
|
+
import { IDeployerServiceOptions } from './deployment/DeployerService';
|
|
8
9
|
export interface IMonitorUser {
|
|
9
10
|
login: string;
|
|
10
11
|
password: string;
|
|
@@ -12,6 +13,8 @@ export interface IMonitorUser {
|
|
|
12
13
|
export interface IDocumentationUser extends IMonitorUser {
|
|
13
14
|
endpoints?: string[];
|
|
14
15
|
}
|
|
16
|
+
export interface IDeployerUser extends IMonitorUser {
|
|
17
|
+
}
|
|
15
18
|
export interface APIServiceNetworkLogItem {
|
|
16
19
|
id: number;
|
|
17
20
|
created: number;
|
|
@@ -45,6 +48,7 @@ export interface APIServiceOptions<TInterceptor = unknown> {
|
|
|
45
48
|
monitor?: IMonitorUser[];
|
|
46
49
|
documentation?: IDocumentationUser[];
|
|
47
50
|
};
|
|
51
|
+
deployer?: IDeployerServiceOptions | null;
|
|
48
52
|
appVersion?: string;
|
|
49
53
|
fileTempDir: string;
|
|
50
54
|
fileLimit: number;
|
|
@@ -68,6 +72,7 @@ export declare class APIService<TInterceptorResult = any, TInternalCallParams =
|
|
|
68
72
|
private version;
|
|
69
73
|
private options;
|
|
70
74
|
private monitor;
|
|
75
|
+
private deployer;
|
|
71
76
|
private started;
|
|
72
77
|
private requestsCount;
|
|
73
78
|
private noCors;
|
|
@@ -48,6 +48,8 @@ const MysqlAdapter_1 = require("./db/MysqlAdapter");
|
|
|
48
48
|
const DocumentService_1 = require("./DocumentService");
|
|
49
49
|
const Documentation_1 = require("./documentation/Documentation");
|
|
50
50
|
const MicroserviceHostController_1 = require("./external/MicroserviceHostController");
|
|
51
|
+
const DeployerService_1 = require("./deployment/DeployerService");
|
|
52
|
+
const Deployer_1 = require("./deployment/Deployer");
|
|
51
53
|
let nextLogID = 0;
|
|
52
54
|
function defaultOptions() {
|
|
53
55
|
return {
|
|
@@ -110,6 +112,7 @@ class APIService extends BaseService_1.BaseService {
|
|
|
110
112
|
version = PACKAGE_VERSION;
|
|
111
113
|
options;
|
|
112
114
|
monitor = null;
|
|
115
|
+
deployer = null;
|
|
113
116
|
started = new Date();
|
|
114
117
|
requestsCount = 0;
|
|
115
118
|
noCors = false;
|
|
@@ -146,6 +149,11 @@ class APIService extends BaseService_1.BaseService {
|
|
|
146
149
|
new DocumentService_1.DocumentGenerator(this.options.endpoints);
|
|
147
150
|
this.monitor = new MonitorService_1.MonitorService();
|
|
148
151
|
this.monitor.init();
|
|
152
|
+
if (this.options.deployer && this.options.deployer.users.length > 0) {
|
|
153
|
+
this.options.endpoints.push(new Deployer_1.Deployer());
|
|
154
|
+
this.deployer = new DeployerService_1.DeployerService(this.options.deployer);
|
|
155
|
+
this.deployer.init();
|
|
156
|
+
}
|
|
149
157
|
this.options.endpoints.push(new Liveness_1.Liveness(this.started), new Readiness_1.Readiness(this.started));
|
|
150
158
|
exports.REQ_HTTP_REQUESTS_COUNT.listener = async () => this.requestsCount;
|
|
151
159
|
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import Signal, { Req } from "badmfck-signal";
|
|
2
|
+
import { BaseService } from "../BaseService";
|
|
3
|
+
import { ValidationModel } from "../helper/Validator";
|
|
4
|
+
import { IError } from "../structures/Interfaces";
|
|
5
|
+
declare const _ProjectState: {
|
|
6
|
+
readonly active: "";
|
|
7
|
+
readonly $__active_values: readonly ["blue", "green"];
|
|
8
|
+
readonly lastDeployed: "";
|
|
9
|
+
readonly $__lastDeployed_values: readonly ["blue", "green"];
|
|
10
|
+
readonly history: readonly [{
|
|
11
|
+
readonly slot: "";
|
|
12
|
+
readonly $__slot_values: readonly ["blue", "green"];
|
|
13
|
+
readonly action: "";
|
|
14
|
+
readonly $__action_values: readonly ["deploy", "switch", "rollback"];
|
|
15
|
+
readonly user: "";
|
|
16
|
+
readonly at: 0;
|
|
17
|
+
}];
|
|
18
|
+
};
|
|
19
|
+
export type IProjectState = ValidationModel<typeof _ProjectState>;
|
|
20
|
+
declare const _TConfig: {
|
|
21
|
+
readonly name: "";
|
|
22
|
+
readonly destination: "";
|
|
23
|
+
readonly description: "";
|
|
24
|
+
readonly token: "";
|
|
25
|
+
readonly unpack: true;
|
|
26
|
+
readonly ignoreDirectoryClean: false;
|
|
27
|
+
readonly backup: true;
|
|
28
|
+
readonly install: true;
|
|
29
|
+
readonly pm2: true;
|
|
30
|
+
readonly pm2_attributes: readonly [""];
|
|
31
|
+
readonly $__pm2_attributes_optional: true;
|
|
32
|
+
readonly chmod: 0;
|
|
33
|
+
readonly email: readonly [""];
|
|
34
|
+
readonly phone: readonly [""];
|
|
35
|
+
readonly liveness: "";
|
|
36
|
+
readonly $__liveness_optional: true;
|
|
37
|
+
readonly readiness: "";
|
|
38
|
+
readonly $__readiness_optional: true;
|
|
39
|
+
readonly config: {};
|
|
40
|
+
readonly bluegreen: {
|
|
41
|
+
readonly blue_config: {};
|
|
42
|
+
readonly blue_pm2_attributes: readonly [""];
|
|
43
|
+
readonly $__blue_pm2_attributes_optional: true;
|
|
44
|
+
readonly green_config: {};
|
|
45
|
+
readonly green_pm2_attributes: readonly [""];
|
|
46
|
+
readonly $__green_pm2_attributes_optional: true;
|
|
47
|
+
readonly on_switch: readonly [""];
|
|
48
|
+
readonly $__on_switch_optional: true;
|
|
49
|
+
};
|
|
50
|
+
readonly $__bluegreen_optional: true;
|
|
51
|
+
readonly nginx: {};
|
|
52
|
+
readonly $__nginx_optional: true;
|
|
53
|
+
};
|
|
54
|
+
export type IConfig = ValidationModel<typeof _TConfig>;
|
|
55
|
+
export declare const REQ_DEPLOYMENT_CONFIG: Req<void, Map<string, {
|
|
56
|
+
pm2_attributes?: string[] | undefined;
|
|
57
|
+
liveness?: string | undefined;
|
|
58
|
+
readiness?: string | undefined;
|
|
59
|
+
bluegreen?: {
|
|
60
|
+
blue_pm2_attributes?: string[] | undefined;
|
|
61
|
+
green_pm2_attributes?: string[] | undefined;
|
|
62
|
+
on_switch?: string[] | undefined;
|
|
63
|
+
blue_config: {};
|
|
64
|
+
green_config: {};
|
|
65
|
+
} | undefined;
|
|
66
|
+
nginx?: {} | undefined;
|
|
67
|
+
name: string;
|
|
68
|
+
destination: string;
|
|
69
|
+
description: string;
|
|
70
|
+
token: string;
|
|
71
|
+
unpack: boolean;
|
|
72
|
+
ignoreDirectoryClean: boolean;
|
|
73
|
+
backup: boolean;
|
|
74
|
+
install: boolean;
|
|
75
|
+
pm2: boolean;
|
|
76
|
+
chmod: number;
|
|
77
|
+
email: string[];
|
|
78
|
+
phone: string[];
|
|
79
|
+
config: {};
|
|
80
|
+
}>>;
|
|
81
|
+
export declare const REQ_DEPLOYMENT_CONFIG_ADD: Req<{
|
|
82
|
+
pm2_attributes?: string[] | undefined;
|
|
83
|
+
liveness?: string | undefined;
|
|
84
|
+
readiness?: string | undefined;
|
|
85
|
+
bluegreen?: {
|
|
86
|
+
blue_pm2_attributes?: string[] | undefined;
|
|
87
|
+
green_pm2_attributes?: string[] | undefined;
|
|
88
|
+
on_switch?: string[] | undefined;
|
|
89
|
+
blue_config: {};
|
|
90
|
+
green_config: {};
|
|
91
|
+
} | undefined;
|
|
92
|
+
nginx?: {} | undefined;
|
|
93
|
+
name: string;
|
|
94
|
+
destination: string;
|
|
95
|
+
description: string;
|
|
96
|
+
token: string;
|
|
97
|
+
unpack: boolean;
|
|
98
|
+
ignoreDirectoryClean: boolean;
|
|
99
|
+
backup: boolean;
|
|
100
|
+
install: boolean;
|
|
101
|
+
pm2: boolean;
|
|
102
|
+
chmod: number;
|
|
103
|
+
email: string[];
|
|
104
|
+
phone: string[];
|
|
105
|
+
config: {};
|
|
106
|
+
}, IError | {
|
|
107
|
+
pm2_attributes?: string[] | undefined;
|
|
108
|
+
liveness?: string | undefined;
|
|
109
|
+
readiness?: string | undefined;
|
|
110
|
+
bluegreen?: {
|
|
111
|
+
blue_pm2_attributes?: string[] | undefined;
|
|
112
|
+
green_pm2_attributes?: string[] | undefined;
|
|
113
|
+
on_switch?: string[] | undefined;
|
|
114
|
+
blue_config: {};
|
|
115
|
+
green_config: {};
|
|
116
|
+
} | undefined;
|
|
117
|
+
nginx?: {} | undefined;
|
|
118
|
+
name: string;
|
|
119
|
+
destination: string;
|
|
120
|
+
description: string;
|
|
121
|
+
token: string;
|
|
122
|
+
unpack: boolean;
|
|
123
|
+
ignoreDirectoryClean: boolean;
|
|
124
|
+
backup: boolean;
|
|
125
|
+
install: boolean;
|
|
126
|
+
pm2: boolean;
|
|
127
|
+
chmod: number;
|
|
128
|
+
email: string[];
|
|
129
|
+
phone: string[];
|
|
130
|
+
config: {};
|
|
131
|
+
}>;
|
|
132
|
+
export declare const REQ_DEPLOYMENT_CONFIG_DELETE: Req<string, boolean | IError>;
|
|
133
|
+
export declare const S_CONFIG_UPDATED: Signal<void>;
|
|
134
|
+
export declare class ConfigService extends BaseService {
|
|
135
|
+
private configurations;
|
|
136
|
+
private dir;
|
|
137
|
+
private busy;
|
|
138
|
+
private retake;
|
|
139
|
+
private watchDebounceTimer;
|
|
140
|
+
constructor(dir: string);
|
|
141
|
+
init(): Promise<void>;
|
|
142
|
+
configurationDelete(name: string): Promise<IError | boolean>;
|
|
143
|
+
configurationAdd(req: IConfig): Promise<IError | IConfig>;
|
|
144
|
+
getConfig(): void;
|
|
145
|
+
validateRequest(data: IConfig): Promise<IError | undefined>;
|
|
146
|
+
checkName(name: string): IError | undefined;
|
|
147
|
+
}
|
|
148
|
+
export {};
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.ConfigService = exports.S_CONFIG_UPDATED = exports.REQ_DEPLOYMENT_CONFIG_DELETE = exports.REQ_DEPLOYMENT_CONFIG_ADD = exports.REQ_DEPLOYMENT_CONFIG = void 0;
|
|
30
|
+
const fs_1 = __importDefault(require("fs"));
|
|
31
|
+
const path_1 = __importDefault(require("path"));
|
|
32
|
+
const badmfck_signal_1 = __importStar(require("badmfck-signal"));
|
|
33
|
+
const BaseService_1 = require("../BaseService");
|
|
34
|
+
const Validator_1 = require("../helper/Validator");
|
|
35
|
+
const LogService_1 = require("../LogService");
|
|
36
|
+
const DefaultErrors_1 = __importStar(require("../structures/DefaultErrors"));
|
|
37
|
+
const _ProjectState = {
|
|
38
|
+
active: "",
|
|
39
|
+
$__active_values: ["blue", "green"],
|
|
40
|
+
lastDeployed: "",
|
|
41
|
+
$__lastDeployed_values: ["blue", "green"],
|
|
42
|
+
history: [{
|
|
43
|
+
slot: "",
|
|
44
|
+
$__slot_values: ["blue", "green"],
|
|
45
|
+
action: "",
|
|
46
|
+
$__action_values: ["deploy", "switch", "rollback"],
|
|
47
|
+
user: "",
|
|
48
|
+
at: 0,
|
|
49
|
+
}],
|
|
50
|
+
};
|
|
51
|
+
const _TConfig = {
|
|
52
|
+
name: "",
|
|
53
|
+
destination: "",
|
|
54
|
+
description: "",
|
|
55
|
+
token: "",
|
|
56
|
+
unpack: true,
|
|
57
|
+
ignoreDirectoryClean: false,
|
|
58
|
+
backup: true,
|
|
59
|
+
install: true,
|
|
60
|
+
pm2: true,
|
|
61
|
+
pm2_attributes: [""],
|
|
62
|
+
$__pm2_attributes_optional: true,
|
|
63
|
+
chmod: 0,
|
|
64
|
+
email: [""],
|
|
65
|
+
phone: [""],
|
|
66
|
+
liveness: "",
|
|
67
|
+
$__liveness_optional: true,
|
|
68
|
+
readiness: "",
|
|
69
|
+
$__readiness_optional: true,
|
|
70
|
+
config: {},
|
|
71
|
+
bluegreen: {
|
|
72
|
+
blue_config: {},
|
|
73
|
+
blue_pm2_attributes: [""],
|
|
74
|
+
$__blue_pm2_attributes_optional: true,
|
|
75
|
+
green_config: {},
|
|
76
|
+
green_pm2_attributes: [""],
|
|
77
|
+
$__green_pm2_attributes_optional: true,
|
|
78
|
+
on_switch: [""],
|
|
79
|
+
$__on_switch_optional: true,
|
|
80
|
+
},
|
|
81
|
+
$__bluegreen_optional: true,
|
|
82
|
+
nginx: {},
|
|
83
|
+
$__nginx_optional: true,
|
|
84
|
+
};
|
|
85
|
+
exports.REQ_DEPLOYMENT_CONFIG = new badmfck_signal_1.Req();
|
|
86
|
+
exports.REQ_DEPLOYMENT_CONFIG_ADD = new badmfck_signal_1.Req();
|
|
87
|
+
exports.REQ_DEPLOYMENT_CONFIG_DELETE = new badmfck_signal_1.Req();
|
|
88
|
+
exports.S_CONFIG_UPDATED = new badmfck_signal_1.default();
|
|
89
|
+
class ConfigService extends BaseService_1.BaseService {
|
|
90
|
+
configurations = new Map();
|
|
91
|
+
dir;
|
|
92
|
+
busy = false;
|
|
93
|
+
retake = false;
|
|
94
|
+
watchDebounceTimer = null;
|
|
95
|
+
constructor(dir) {
|
|
96
|
+
super("ConfigService");
|
|
97
|
+
this.dir = dir;
|
|
98
|
+
}
|
|
99
|
+
async init() {
|
|
100
|
+
super.init();
|
|
101
|
+
exports.REQ_DEPLOYMENT_CONFIG.listener = async (req) => this.configurations;
|
|
102
|
+
exports.REQ_DEPLOYMENT_CONFIG_ADD.listener = async (req) => this.configurationAdd(req);
|
|
103
|
+
exports.REQ_DEPLOYMENT_CONFIG_DELETE.listener = async (name) => this.configurationDelete(name);
|
|
104
|
+
if (!fs_1.default.existsSync(this.dir))
|
|
105
|
+
fs_1.default.mkdirSync(this.dir, { recursive: true });
|
|
106
|
+
this.getConfig();
|
|
107
|
+
fs_1.default.watch(this.dir, (_eventType, filename) => {
|
|
108
|
+
if (!filename || !filename.endsWith(".json"))
|
|
109
|
+
return;
|
|
110
|
+
if (this.watchDebounceTimer)
|
|
111
|
+
clearTimeout(this.watchDebounceTimer);
|
|
112
|
+
this.watchDebounceTimer = setTimeout(() => {
|
|
113
|
+
this.watchDebounceTimer = null;
|
|
114
|
+
this.getConfig();
|
|
115
|
+
}, 200);
|
|
116
|
+
});
|
|
117
|
+
setInterval(() => this.getConfig(), 60_000).unref();
|
|
118
|
+
}
|
|
119
|
+
async configurationDelete(name) {
|
|
120
|
+
try {
|
|
121
|
+
fs_1.default.unlinkSync(path_1.default.resolve(this.dir, name + ".json"));
|
|
122
|
+
}
|
|
123
|
+
catch (e) {
|
|
124
|
+
return { code: 33, message: "Can't delete configuration file." };
|
|
125
|
+
}
|
|
126
|
+
this.getConfig();
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
async configurationAdd(req) {
|
|
130
|
+
const err = await this.validateRequest(req);
|
|
131
|
+
if (DefaultErrors_1.ErrorUtils.isError(err))
|
|
132
|
+
return err;
|
|
133
|
+
try {
|
|
134
|
+
fs_1.default.writeFileSync(path_1.default.resolve(this.dir, req.name + ".json"), JSON.stringify(req));
|
|
135
|
+
}
|
|
136
|
+
catch (e) {
|
|
137
|
+
return { code: 31, message: "Can't write configuration file." };
|
|
138
|
+
}
|
|
139
|
+
this.getConfig();
|
|
140
|
+
const conf = this.configurations.get(req.name);
|
|
141
|
+
if (!conf)
|
|
142
|
+
return { code: 32, message: "Can't read configuration file." };
|
|
143
|
+
return conf;
|
|
144
|
+
}
|
|
145
|
+
getConfig() {
|
|
146
|
+
if (this.busy) {
|
|
147
|
+
this.retake = true;
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
this.retake = false;
|
|
151
|
+
this.busy = true;
|
|
152
|
+
const temp = new Map();
|
|
153
|
+
let isError = false;
|
|
154
|
+
try {
|
|
155
|
+
const files = fs_1.default.readdirSync(this.dir);
|
|
156
|
+
for (let f of files) {
|
|
157
|
+
if (f.indexOf(".json") === -1)
|
|
158
|
+
continue;
|
|
159
|
+
try {
|
|
160
|
+
const file = fs_1.default.readFileSync(path_1.default.resolve(this.dir, f));
|
|
161
|
+
const config = JSON.parse(file.toString());
|
|
162
|
+
temp.set(config.name, config);
|
|
163
|
+
}
|
|
164
|
+
catch (e) {
|
|
165
|
+
isError = true;
|
|
166
|
+
(0, LogService_1.logError)("Can't read config file: " + f, e);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
if (!isError) {
|
|
170
|
+
this.configurations = temp;
|
|
171
|
+
exports.S_CONFIG_UPDATED.invoke();
|
|
172
|
+
(0, LogService_1.logWarn)("Configurations updated");
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
catch (e) {
|
|
176
|
+
(0, LogService_1.logError)("Can't read config directory", e);
|
|
177
|
+
}
|
|
178
|
+
this.busy = false;
|
|
179
|
+
if (this.retake) {
|
|
180
|
+
this.retake = false;
|
|
181
|
+
this.getConfig();
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
async validateRequest(data) {
|
|
185
|
+
const validationErrors = await Validator_1.Validator.validateStructure(_TConfig, data);
|
|
186
|
+
if (validationErrors && validationErrors.length > 0)
|
|
187
|
+
return { ...DefaultErrors_1.default.INVALID_DATA, stack: validationErrors, details: "Invalid configuration data." };
|
|
188
|
+
const errors = [];
|
|
189
|
+
const cname = this.checkName(data.name);
|
|
190
|
+
if (DefaultErrors_1.ErrorUtils.isError(cname))
|
|
191
|
+
errors.push(cname);
|
|
192
|
+
let dest = data.destination.replace(/[^a-zA-Z0-9.\/_\-`~!]/g, "");
|
|
193
|
+
dest = dest.replace("./", "").replace("../", "").replace("..\\", "").replace(".\\", "");
|
|
194
|
+
if (dest !== data.destination)
|
|
195
|
+
errors.push({ code: 22, message: "Incorrect destination." });
|
|
196
|
+
if (data.email) {
|
|
197
|
+
if (typeof data.email === "string") {
|
|
198
|
+
if (Validator_1.Validator.validateValue(data.email, { type: "string", min: 5, max: 128 }) !== Validator_1.ValidationReport.OK)
|
|
199
|
+
errors.push({ code: 29, message: "Invalid email." });
|
|
200
|
+
}
|
|
201
|
+
else if (Array.isArray(data.email)) {
|
|
202
|
+
for (let i of data.email) {
|
|
203
|
+
if (Validator_1.Validator.validateValue(i, { type: "string", min: 5, max: 128 }) !== Validator_1.ValidationReport.OK)
|
|
204
|
+
errors.push({ code: 29, message: "Invalid email." });
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
if (data.phone) {
|
|
209
|
+
if (typeof data.phone === "string") {
|
|
210
|
+
if (Validator_1.Validator.validateValue(data.phone, { type: "string", min: 9, max: 16 }) !== Validator_1.ValidationReport.OK)
|
|
211
|
+
errors.push({ code: 30, message: "Invalid phone." });
|
|
212
|
+
}
|
|
213
|
+
else if (Array.isArray(data.phone)) {
|
|
214
|
+
for (let i of data.phone) {
|
|
215
|
+
if (Validator_1.Validator.validateValue(i, { type: "string", min: 9, max: 16 }) !== Validator_1.ValidationReport.OK)
|
|
216
|
+
errors.push({ code: 30, message: "Invalid phone." });
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
if (errors.length > 0)
|
|
221
|
+
return { ...DefaultErrors_1.default.INVALID_DATA, stack: errors, details: "Invalid configuration data." };
|
|
222
|
+
}
|
|
223
|
+
checkName(name) {
|
|
224
|
+
const n = name.replace(/[^a-zA-Z0-9\.\-]/g, "");
|
|
225
|
+
if (n !== name)
|
|
226
|
+
return { code: 21, message: "Incorrect name." };
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
exports.ConfigService = ConfigService;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { BaseEndpoint } from "../BaseEndpoint";
|
|
2
|
+
import { HTTPRequestVO, TransferPacketVO } from "../structures/Interfaces";
|
|
3
|
+
export declare class Deployer extends BaseEndpoint {
|
|
4
|
+
constructor();
|
|
5
|
+
file(req: HTTPRequestVO): Promise<TransferPacketVO<any>>;
|
|
6
|
+
switchSlot(req: HTTPRequestVO): Promise<TransferPacketVO<any>>;
|
|
7
|
+
status(req: HTTPRequestVO): Promise<TransferPacketVO<any>>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Deployer = void 0;
|
|
4
|
+
const BaseEndpoint_1 = require("../BaseEndpoint");
|
|
5
|
+
const DeployerService_1 = require("./DeployerService");
|
|
6
|
+
class Deployer extends BaseEndpoint_1.BaseEndpoint {
|
|
7
|
+
constructor() {
|
|
8
|
+
super("pckg");
|
|
9
|
+
this.registerEndpoints([
|
|
10
|
+
{
|
|
11
|
+
ignoreInterceptor: true,
|
|
12
|
+
endpoint: "add",
|
|
13
|
+
handler: this.file,
|
|
14
|
+
validationModel: {
|
|
15
|
+
token: "",
|
|
16
|
+
name: "",
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
ignoreInterceptor: true,
|
|
21
|
+
endpoint: "switch",
|
|
22
|
+
handler: this.switchSlot,
|
|
23
|
+
validationModel: {
|
|
24
|
+
token: "",
|
|
25
|
+
name: "",
|
|
26
|
+
scheme: "",
|
|
27
|
+
$__scheme_values: ["blue", "green"],
|
|
28
|
+
$__scheme_optional: true
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
ignoreInterceptor: true,
|
|
33
|
+
endpoint: "status",
|
|
34
|
+
handler: this.status,
|
|
35
|
+
validationModel: {
|
|
36
|
+
name: "",
|
|
37
|
+
$__name_optional: true
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
]);
|
|
41
|
+
}
|
|
42
|
+
async file(req) {
|
|
43
|
+
if (!req.files)
|
|
44
|
+
return { error: { code: 10, message: "No files were uploaded.", httpStatus: 400 } };
|
|
45
|
+
const result = await DeployerService_1.REQ_DEPLOYMENT_PROCEED.request({ ...req.data, files: req.files, auth: req.headers.authorization });
|
|
46
|
+
return { data: result };
|
|
47
|
+
}
|
|
48
|
+
async switchSlot(req) {
|
|
49
|
+
return await DeployerService_1.REQ_DEPLOYMENT_SWITCH.request({ ...req.data, auth: req.headers.authorization });
|
|
50
|
+
}
|
|
51
|
+
async status(req) {
|
|
52
|
+
return await DeployerService_1.REQ_DEPLOYMENT_STATUS.request({ ...req.data, auth: req.headers.authorization });
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
exports.Deployer = Deployer;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { Req } from "badmfck-signal";
|
|
2
|
+
import { BaseService } from "../BaseService";
|
|
3
|
+
import { IDeployerUser } from "../APIService";
|
|
4
|
+
import { IConfig, IProjectState } from "./ConfigService";
|
|
5
|
+
import { UploadedFile } from "express-fileupload";
|
|
6
|
+
import { IDeploymentNotifier } from "./Notifier";
|
|
7
|
+
import { IError } from "../structures/Interfaces";
|
|
8
|
+
export interface IDeployerServiceOptions {
|
|
9
|
+
projects_path: string;
|
|
10
|
+
users: IDeployerUser[];
|
|
11
|
+
watchdog?: boolean;
|
|
12
|
+
notifier?: {
|
|
13
|
+
URL?: string;
|
|
14
|
+
KEY?: string;
|
|
15
|
+
controller?: BaseService;
|
|
16
|
+
} | null;
|
|
17
|
+
}
|
|
18
|
+
export declare const REQ_DEPLOYMENT_PROCEED: Req<{
|
|
19
|
+
auth: string;
|
|
20
|
+
name: string;
|
|
21
|
+
token: string;
|
|
22
|
+
files: {
|
|
23
|
+
file: UploadedFile;
|
|
24
|
+
};
|
|
25
|
+
}, any>;
|
|
26
|
+
export declare const REQ_DEPLOYMENT_SWITCH: Req<{
|
|
27
|
+
auth?: string | undefined;
|
|
28
|
+
name: string;
|
|
29
|
+
token: string;
|
|
30
|
+
scheme?: "blue" | "green" | undefined;
|
|
31
|
+
}, any>;
|
|
32
|
+
export declare const REQ_DEPLOYMENT_STATUS: Req<{
|
|
33
|
+
auth?: string | undefined;
|
|
34
|
+
name?: string | undefined;
|
|
35
|
+
}, any>;
|
|
36
|
+
export declare class DeployerService extends BaseService {
|
|
37
|
+
options: IDeployerServiceOptions;
|
|
38
|
+
notifier: IDeploymentNotifier | null;
|
|
39
|
+
busy: Map<string, boolean>;
|
|
40
|
+
constructor(options: IDeployerServiceOptions);
|
|
41
|
+
init(): Promise<void>;
|
|
42
|
+
proceed(data: {
|
|
43
|
+
auth?: string;
|
|
44
|
+
name: string;
|
|
45
|
+
token: string;
|
|
46
|
+
files: {
|
|
47
|
+
file: UploadedFile;
|
|
48
|
+
};
|
|
49
|
+
}): Promise<{
|
|
50
|
+
error: {
|
|
51
|
+
code: number;
|
|
52
|
+
message: string;
|
|
53
|
+
httpStatus: number;
|
|
54
|
+
};
|
|
55
|
+
data?: undefined;
|
|
56
|
+
} | {
|
|
57
|
+
data: boolean;
|
|
58
|
+
error?: undefined;
|
|
59
|
+
}>;
|
|
60
|
+
switchSlot(data: {
|
|
61
|
+
auth?: string;
|
|
62
|
+
name: string;
|
|
63
|
+
token: string;
|
|
64
|
+
scheme?: "blue" | "green";
|
|
65
|
+
}): Promise<{
|
|
66
|
+
error: {
|
|
67
|
+
code: number;
|
|
68
|
+
message: string;
|
|
69
|
+
httpStatus: number;
|
|
70
|
+
};
|
|
71
|
+
data?: undefined;
|
|
72
|
+
} | {
|
|
73
|
+
data: {
|
|
74
|
+
active: "blue" | "green";
|
|
75
|
+
previousActive: "blue" | "green";
|
|
76
|
+
action: "rollback" | "switch";
|
|
77
|
+
hookError: string | null;
|
|
78
|
+
};
|
|
79
|
+
error?: undefined;
|
|
80
|
+
}>;
|
|
81
|
+
status(data: {
|
|
82
|
+
auth?: string;
|
|
83
|
+
name?: string;
|
|
84
|
+
}): Promise<{
|
|
85
|
+
error: {
|
|
86
|
+
code: number;
|
|
87
|
+
message: string;
|
|
88
|
+
httpStatus: number;
|
|
89
|
+
};
|
|
90
|
+
data?: undefined;
|
|
91
|
+
} | {
|
|
92
|
+
data: Record<string, {
|
|
93
|
+
hasBlueGreen: boolean;
|
|
94
|
+
state: IProjectState | null;
|
|
95
|
+
readyToSwitch: boolean;
|
|
96
|
+
}>;
|
|
97
|
+
error?: undefined;
|
|
98
|
+
}>;
|
|
99
|
+
tokensMatch(a: string | undefined, b: string | undefined): boolean;
|
|
100
|
+
private _checkUserAuth;
|
|
101
|
+
runPM2(found: IConfig, projectState?: IProjectState | null): Promise<IError | null>;
|
|
102
|
+
private static readonly SKIP_DIRS;
|
|
103
|
+
private static readonly TEMPLATE_EXTENSIONS;
|
|
104
|
+
applyConfig(dir: string, config: Record<string, string>): Promise<void>;
|
|
105
|
+
processFile(filePath: string, config: Record<string, string>): Promise<void>;
|
|
106
|
+
private _isDestinationSafe;
|
|
107
|
+
unpack(destination: string, uploadedFile: string, install?: boolean, ignoreDirectoryClean?: boolean): Promise<IError | null>;
|
|
108
|
+
upload(file: UploadedFile, uploadPath: string): Promise<IError | null>;
|
|
109
|
+
}
|