underpost 2.8.853 → 2.8.856
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/.env.development +1 -1
- package/.env.production +1 -1
- package/.env.test +1 -1
- package/.github/workflows/pwa-microservices-template-page.cd.yml +1 -1
- package/.github/workflows/release.cd.yml +36 -0
- package/README.md +5 -63
- package/bin/db.js +1 -3
- package/bin/deploy.js +12 -312
- package/bin/file.js +2 -0
- package/cli.md +5 -2
- package/manifests/deployment/{dd-template-development → dd-default-development}/deployment.yaml +16 -16
- package/manifests/deployment/{dd-template-development → dd-default-development}/proxy.yaml +3 -3
- package/manifests/grafana/deployment.yaml +57 -0
- package/manifests/grafana/kustomization.yaml +7 -0
- package/manifests/grafana/pvc.yaml +12 -0
- package/manifests/grafana/service.yaml +14 -0
- package/manifests/prometheus/deployment.yaml +82 -0
- package/package.json +1 -2
- package/src/cli/cluster.js +41 -2
- package/src/cli/index.js +5 -0
- package/src/cli/run.js +30 -7
- package/src/client/Default.index.js +0 -2
- package/src/client/components/core/Modal.js +2 -2
- package/src/client/components/core/Scroll.js +65 -120
- package/src/index.js +1 -1
- package/src/server/conf.js +1 -272
- package/src/server/proxy.js +1 -2
- package/docker-compose.yml +0 -67
- package/prometheus.yml +0 -36
|
@@ -1,131 +1,76 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Modal } from './Modal.js';
|
|
3
|
-
import { append, s } from './VanillaJs.js';
|
|
1
|
+
import { s } from './VanillaJs.js';
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
class Scroll {
|
|
4
|
+
/**
|
|
5
|
+
* Attach scroll listener to an element (resolved with s(selector)).
|
|
6
|
+
* @param {string} selector - selector passed to s(selector)
|
|
7
|
+
* @param {function} [callback] - callback function to be called on scroll
|
|
8
|
+
* @param {object} options
|
|
9
|
+
* @param {number} [options.threshold=1] - px margin to treat as bottom
|
|
10
|
+
* @param {number} [options.precision=3] - decimal places for percentages
|
|
11
|
+
*/
|
|
12
|
+
static setEvent(selector, callback = async () => {}, options = { threshold: 1, precision: 3 }) {
|
|
13
|
+
const el = s(selector);
|
|
14
|
+
if (!el) return;
|
|
15
|
+
|
|
16
|
+
const threshold = options.threshold ?? 1; // px tolerance for bottom detection
|
|
17
|
+
const precision = options.precision ?? 3;
|
|
18
|
+
let ticking = false;
|
|
19
|
+
|
|
20
|
+
const round = (v) => {
|
|
21
|
+
const m = Math.pow(10, precision);
|
|
22
|
+
return Math.round(v * m) / m;
|
|
11
23
|
};
|
|
12
|
-
return Scroll.data[selector];
|
|
13
|
-
},
|
|
14
|
-
getScrollPosition: function (selector) {
|
|
15
|
-
// Scroll.data[selector].element.clientHeight -
|
|
16
|
-
return Scroll.data[selector].element.scrollTop;
|
|
17
|
-
},
|
|
18
|
-
scrollHandler: async function () {
|
|
19
|
-
for (const selector in Scroll.data) await Scroll.data[selector].callback(Scroll.getScrollPosition(selector));
|
|
20
|
-
},
|
|
21
|
-
addEvent: function (selector = '', callback = (position = 0) => {}) {
|
|
22
|
-
Scroll.data[selector].callback = callback;
|
|
23
|
-
},
|
|
24
|
-
removeEvent: function (selector) {
|
|
25
|
-
delete Scroll.data[selector];
|
|
26
|
-
},
|
|
27
|
-
to: function (elector = '', options = { top: 100, left: 100, behavior: 'smooth' }) {
|
|
28
|
-
Scroll.data[selector].element.scrollTo({
|
|
29
|
-
top: options.top || Scroll.getScrollPosition(selector),
|
|
30
|
-
left: options.left || 0,
|
|
31
|
-
behavior: options.behavior || 'smooth',
|
|
32
|
-
});
|
|
33
|
-
},
|
|
34
|
-
topRefreshEvents: {},
|
|
35
|
-
addTopRefreshEvent: function (options = { id: '', callback: () => {}, condition: () => {} }) {
|
|
36
|
-
this.topRefreshEvents[options.id] = options;
|
|
37
|
-
},
|
|
38
|
-
removeTopRefreshEvent: function (id = '') {
|
|
39
|
-
delete this.topRefreshEvents[id];
|
|
40
|
-
},
|
|
41
|
-
pullTopRefresh: function () {
|
|
42
|
-
return;
|
|
43
|
-
append(
|
|
44
|
-
'body',
|
|
45
|
-
html` <style>
|
|
46
|
-
.pull-refresh-icon-container {
|
|
47
|
-
height: 60px;
|
|
48
|
-
width: 100%;
|
|
49
|
-
z-index: 10;
|
|
50
|
-
transition: 0.3s;
|
|
51
|
-
left: 0px;
|
|
52
|
-
}
|
|
53
|
-
.pull-refresh-icon {
|
|
54
|
-
width: 60px;
|
|
55
|
-
height: 60px;
|
|
56
|
-
margin: auto;
|
|
57
|
-
color: white;
|
|
58
|
-
font-size: 30px;
|
|
59
|
-
}
|
|
60
|
-
</style>
|
|
61
|
-
${borderChar(2, 'black', [' .pull-refresh-icon-container'])}
|
|
62
|
-
<div style="top: -60px" class="abs pull-refresh-icon-container">
|
|
63
|
-
<div class="in pull-refresh-icon">
|
|
64
|
-
<div class="abs center"><i class="fa-solid fa-arrows-rotate"></i></div>
|
|
65
|
-
</div>
|
|
66
|
-
</div>`,
|
|
67
|
-
);
|
|
68
24
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
25
|
+
const listener = (event) => {
|
|
26
|
+
if (ticking) return;
|
|
27
|
+
ticking = true;
|
|
28
|
+
|
|
29
|
+
requestAnimationFrame(() => {
|
|
30
|
+
const scrollHeight = el.scrollHeight;
|
|
31
|
+
const clientHeight = el.clientHeight;
|
|
32
|
+
const scrollTop = el.scrollTop;
|
|
33
|
+
|
|
34
|
+
// pixels left to scroll (clamped to >= 0)
|
|
35
|
+
const remaining = Math.max(0, scrollHeight - clientHeight - scrollTop);
|
|
73
36
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
// console.warn('touchstart', touchstartY);
|
|
77
|
-
});
|
|
37
|
+
// maximum possible remaining (0 if content fits without scrolling)
|
|
38
|
+
const maxRemaining = Math.max(0, scrollHeight - clientHeight);
|
|
78
39
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
(!s(`.btn-bar-center-icon-close`).classList.contains('hide') &&
|
|
83
|
-
!s(
|
|
84
|
-
`.btn-icon-menu-mode-${Modal.Data['modal-menu'].options.mode !== 'slide-menu-right' ? 'left' : 'right'}`,
|
|
85
|
-
).classList.contains('hide'))
|
|
86
|
-
)
|
|
87
|
-
return;
|
|
40
|
+
// percentRemaining: 1 = top (all remaining), 0 = bottom (none remaining)
|
|
41
|
+
let percentRemaining = maxRemaining === 0 ? 0 : remaining / maxRemaining;
|
|
42
|
+
percentRemaining = Math.max(0, Math.min(1, percentRemaining));
|
|
88
43
|
|
|
89
|
-
|
|
90
|
-
|
|
44
|
+
// percentScrolled: complementary value (0 = top, 1 = bottom)
|
|
45
|
+
let percentScrolled = 1 - percentRemaining;
|
|
46
|
+
percentScrolled = Math.max(0, Math.min(1, percentScrolled));
|
|
91
47
|
|
|
92
|
-
|
|
48
|
+
const payload = {
|
|
49
|
+
scrollHeight,
|
|
50
|
+
clientHeight,
|
|
51
|
+
scrollTop,
|
|
52
|
+
remaining, // px left (>= 0)
|
|
53
|
+
scrollBottom: remaining <= threshold ? 0 : remaining,
|
|
54
|
+
atBottom: remaining <= threshold,
|
|
55
|
+
percentRemaining: round(percentRemaining), // 0..1
|
|
56
|
+
percentScrolled: round(percentScrolled), // 0..1
|
|
57
|
+
};
|
|
93
58
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
59
|
+
// replace this with an event dispatch or callback if you prefer
|
|
60
|
+
// console.warn('scroll', event, JSON.stringify(payload, null, 2));
|
|
61
|
+
callback(payload);
|
|
97
62
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
if (Scroll.topRefreshEvents[event].condition()) Scroll.topRefreshEvents[event].callback();
|
|
110
|
-
}
|
|
111
|
-
reload = false;
|
|
112
|
-
});
|
|
113
|
-
Scroll.addTopRefreshEvent({
|
|
114
|
-
id: 'main-body',
|
|
115
|
-
callback: () => {
|
|
116
|
-
location.reload();
|
|
117
|
-
},
|
|
118
|
-
condition: () => {
|
|
119
|
-
return (
|
|
120
|
-
s('.main-body') &&
|
|
121
|
-
s('.main-body').scrollTop === 0 &&
|
|
122
|
-
!Object.keys(Modal.Data).find(
|
|
123
|
-
(idModal) => !['modal-menu', 'main-body', 'bottom-bar', 'main-body-top'].includes(idModal),
|
|
124
|
-
)
|
|
125
|
-
);
|
|
126
|
-
},
|
|
127
|
-
});
|
|
128
|
-
},
|
|
129
|
-
};
|
|
63
|
+
ticking = false;
|
|
64
|
+
});
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
el.addEventListener('scroll', listener, { passive: true });
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
removeEvent: () => el.removeEventListener('scroll', listener),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
}
|
|
130
74
|
|
|
131
75
|
export { Scroll };
|
|
76
|
+
export default Scroll;
|
package/src/index.js
CHANGED
package/src/server/conf.js
CHANGED
|
@@ -770,152 +770,22 @@ const validateTemplatePath = (absolutePath = '') => {
|
|
|
770
770
|
return true;
|
|
771
771
|
};
|
|
772
772
|
|
|
773
|
-
const deployTest = async (dataDeploy = [{ deployId: 'default' }]) => {
|
|
774
|
-
const failed = [];
|
|
775
|
-
for (const deploy of dataDeploy) {
|
|
776
|
-
const deployServerConfPath = fs.existsSync(`./engine-private/replica/${deploy.deployId}/conf.server.json`)
|
|
777
|
-
? `./engine-private/replica/${deploy.deployId}/conf.server.json`
|
|
778
|
-
: `./engine-private/conf/${deploy.deployId}/conf.server.json`;
|
|
779
|
-
const serverConf = loadReplicas(JSON.parse(fs.readFileSync(deployServerConfPath, 'utf8')));
|
|
780
|
-
let fail = false;
|
|
781
|
-
for (const host of Object.keys(serverConf))
|
|
782
|
-
for (const path of Object.keys(serverConf[host])) {
|
|
783
|
-
const { singleReplica } = serverConf[host][path];
|
|
784
|
-
if (singleReplica) continue;
|
|
785
|
-
const urlTest = `https://${host}${path}`;
|
|
786
|
-
try {
|
|
787
|
-
const result = await axios.get(urlTest, { timeout: 10000 });
|
|
788
|
-
const test = result.data.split('<title>');
|
|
789
|
-
if (test[1])
|
|
790
|
-
logger.info('Success deploy', {
|
|
791
|
-
...deploy,
|
|
792
|
-
result: test[1].split('</title>')[0],
|
|
793
|
-
urlTest,
|
|
794
|
-
});
|
|
795
|
-
else {
|
|
796
|
-
logger.error('Error deploy', {
|
|
797
|
-
...deploy,
|
|
798
|
-
result: result.data,
|
|
799
|
-
urlTest,
|
|
800
|
-
});
|
|
801
|
-
fail = true;
|
|
802
|
-
}
|
|
803
|
-
} catch (error) {
|
|
804
|
-
logger.error('Error deploy', {
|
|
805
|
-
...deploy,
|
|
806
|
-
message: error.message,
|
|
807
|
-
urlTest,
|
|
808
|
-
});
|
|
809
|
-
fail = true;
|
|
810
|
-
}
|
|
811
|
-
}
|
|
812
|
-
if (fail) failed.push(deploy);
|
|
813
|
-
}
|
|
814
|
-
return { failed };
|
|
815
|
-
};
|
|
816
|
-
|
|
817
773
|
const awaitDeployMonitor = async (init = false, deltaMs = 1000) => {
|
|
818
774
|
if (init) fs.writeFileSync(`./tmp/await-deploy`, '', 'utf8');
|
|
819
775
|
await timer(deltaMs);
|
|
820
776
|
if (fs.existsSync(`./tmp/await-deploy`)) return await awaitDeployMonitor();
|
|
821
777
|
};
|
|
822
778
|
|
|
823
|
-
const getDeployGroupId = () => {
|
|
824
|
-
const deployGroupIndexArg = process.argv.findIndex((a) => a.match(`deploy-group:`));
|
|
825
|
-
if (deployGroupIndexArg > -1) return process.argv[deployGroupIndexArg].split(':')[1].trim();
|
|
826
|
-
return 'dd';
|
|
827
|
-
};
|
|
828
|
-
|
|
829
779
|
const getDeployId = () => {
|
|
830
780
|
const deployIndexArg = process.argv.findIndex((a) => a.match(`deploy-id:`));
|
|
831
781
|
if (deployIndexArg > -1) return process.argv[deployIndexArg].split(':')[1].trim();
|
|
832
|
-
|
|
833
|
-
if (fs.existsSync(`./engine-private/conf/${deployId}`)) return deployId;
|
|
834
|
-
else if (fs.existsSync(`./engine-private/replica/${deployId}`)) return deployId;
|
|
835
|
-
}
|
|
836
|
-
return 'default';
|
|
782
|
+
return 'dd-default';
|
|
837
783
|
};
|
|
838
784
|
|
|
839
785
|
const getCronBackUpFolder = (host = '', path = '') => {
|
|
840
786
|
return `${host}${path.replace(/\\/g, '/').replace(`/`, '-')}`;
|
|
841
787
|
};
|
|
842
788
|
|
|
843
|
-
const execDeploy = async (options = { deployId: 'default' }, currentAttempt = 1) => {
|
|
844
|
-
const { deployId } = options;
|
|
845
|
-
shellExec(Cmd.delete(deployId));
|
|
846
|
-
shellExec(Cmd.conf(deployId));
|
|
847
|
-
shellExec(Cmd.run(deployId));
|
|
848
|
-
const maxTime = 1000 * 60;
|
|
849
|
-
const minTime = 20 * 1000;
|
|
850
|
-
const intervalTime = 1000;
|
|
851
|
-
return await new Promise(async (resolve) => {
|
|
852
|
-
let currentTime = 0;
|
|
853
|
-
const attempt = () => {
|
|
854
|
-
if (currentTime >= minTime && !fs.existsSync(`./tmp/await-deploy`)) {
|
|
855
|
-
clearInterval(processMonitor);
|
|
856
|
-
return resolve(true);
|
|
857
|
-
}
|
|
858
|
-
cliSpinner(
|
|
859
|
-
intervalTime,
|
|
860
|
-
`[deploy.js] `,
|
|
861
|
-
` Load instance | attempt:${currentAttempt} | elapsed time ${currentTime / 1000}s / ${maxTime / 1000}s`,
|
|
862
|
-
'yellow',
|
|
863
|
-
'material',
|
|
864
|
-
);
|
|
865
|
-
currentTime += intervalTime;
|
|
866
|
-
if (currentTime >= maxTime) {
|
|
867
|
-
clearInterval(processMonitor);
|
|
868
|
-
return resolve(false);
|
|
869
|
-
}
|
|
870
|
-
};
|
|
871
|
-
const processMonitor = setInterval(attempt, intervalTime);
|
|
872
|
-
});
|
|
873
|
-
};
|
|
874
|
-
|
|
875
|
-
const deployRun = async (dataDeploy, currentAttempt = 1) => {
|
|
876
|
-
if (!fs.existsSync(`./tmp`)) fs.mkdirSync(`./tmp`, { recursive: true });
|
|
877
|
-
await fixDependencies();
|
|
878
|
-
const maxAttempts = 3;
|
|
879
|
-
for (const deploy of dataDeploy) {
|
|
880
|
-
let currentAttempt = 1;
|
|
881
|
-
const attempt = async () => {
|
|
882
|
-
const success = await execDeploy(deploy, currentAttempt);
|
|
883
|
-
currentAttempt++;
|
|
884
|
-
if (!success && currentAttempt <= maxAttempts) await attempt();
|
|
885
|
-
};
|
|
886
|
-
await attempt();
|
|
887
|
-
}
|
|
888
|
-
const { failed } = await deployTest(dataDeploy);
|
|
889
|
-
if (failed.length > 0) {
|
|
890
|
-
for (const deploy of failed) logger.error(deploy.deployId, Cmd.run(deploy.deployId));
|
|
891
|
-
if (currentAttempt === maxAttempts) return logger.error(`max deploy attempts exceeded`);
|
|
892
|
-
await read({ prompt: 'Press enter to retry failed processes\n' });
|
|
893
|
-
currentAttempt++;
|
|
894
|
-
await deployRun(failed, currentAttempt);
|
|
895
|
-
} else logger.info(`Deploy process successfully`);
|
|
896
|
-
};
|
|
897
|
-
|
|
898
|
-
const restoreMacroDb = async (deployGroupId = '', deployId = null) => {
|
|
899
|
-
const dataDeploy = await getDataDeploy({ deployGroupId, buildSingleReplica: false });
|
|
900
|
-
for (const deployGroup of dataDeploy) {
|
|
901
|
-
if (deployId && deployGroup.deployId !== deployId) continue;
|
|
902
|
-
if (!deployGroup.replicaHost) {
|
|
903
|
-
const deployServerConfPath = `./engine-private/conf/${deployGroup.deployId}/conf.server.json`;
|
|
904
|
-
const serverConf = JSON.parse(fs.readFileSync(deployServerConfPath, 'utf8'));
|
|
905
|
-
|
|
906
|
-
for (const host of Object.keys(serverConf)) {
|
|
907
|
-
for (const path of Object.keys(serverConf[host])) {
|
|
908
|
-
const { db, singleReplica } = serverConf[host][path];
|
|
909
|
-
if (db && !singleReplica) {
|
|
910
|
-
const cmd = `node bin/db ${host}${path} import ${deployGroup.deployId} cron`;
|
|
911
|
-
shellExec(cmd);
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
|
-
}
|
|
915
|
-
}
|
|
916
|
-
}
|
|
917
|
-
};
|
|
918
|
-
|
|
919
789
|
const mergeFile = async (parts = [], outputFilePath) => {
|
|
920
790
|
await new Promise((resolve) => {
|
|
921
791
|
splitFile
|
|
@@ -977,99 +847,6 @@ const rebuildConfFactory = ({ deployId, valkey, mongo }) => {
|
|
|
977
847
|
return { hosts };
|
|
978
848
|
};
|
|
979
849
|
|
|
980
|
-
const getRestoreCronCmd = async (options = { host: '', path: '', conf: {}, deployId: '' }) => {
|
|
981
|
-
const { host, path, conf, deployId } = options;
|
|
982
|
-
const { runtime, db, git, directory } = conf[host][path];
|
|
983
|
-
const { provider, name, user, password = '', backupPath = '' } = db;
|
|
984
|
-
|
|
985
|
-
if (['xampp', 'lampp'].includes(runtime)) {
|
|
986
|
-
logger.info('Create database', `node bin/db ${host}${path} create ${deployId}`);
|
|
987
|
-
shellExec(`node bin/db ${host}${path} create ${deployId}`);
|
|
988
|
-
}
|
|
989
|
-
|
|
990
|
-
if (git) {
|
|
991
|
-
if (directory && !fs.existsSync(directory)) fs.mkdirSync(directory, { recursive: true });
|
|
992
|
-
|
|
993
|
-
shellExec(`git clone ${git}`);
|
|
994
|
-
|
|
995
|
-
// fs.mkdirSync(`./public/${host}${path}`, { recursive: true });
|
|
996
|
-
|
|
997
|
-
if (fs.existsSync(`./${git.split('/').pop()}`))
|
|
998
|
-
fs.moveSync(`./${git.split('/').pop()}`, directory ? directory : `./public/${host}${path}`, {
|
|
999
|
-
overwrite: true,
|
|
1000
|
-
});
|
|
1001
|
-
}
|
|
1002
|
-
|
|
1003
|
-
let cmd, currentBackupTimestamp, baseBackUpPath;
|
|
1004
|
-
|
|
1005
|
-
if (process.argv.includes('cron')) {
|
|
1006
|
-
baseBackUpPath = `${process.cwd()}/engine-private/cron-backups/${getCronBackUpFolder(host, path)}`;
|
|
1007
|
-
|
|
1008
|
-
const files = await fs.readdir(baseBackUpPath, { withFileTypes: true });
|
|
1009
|
-
|
|
1010
|
-
currentBackupTimestamp = files
|
|
1011
|
-
.map((fileObj) => parseInt(fileObj.name))
|
|
1012
|
-
.sort((a, b) => a - b)
|
|
1013
|
-
.reverse()[0];
|
|
1014
|
-
}
|
|
1015
|
-
|
|
1016
|
-
switch (provider) {
|
|
1017
|
-
case 'mariadb':
|
|
1018
|
-
{
|
|
1019
|
-
if (process.argv.includes('cron')) {
|
|
1020
|
-
cmd = `mysql -u ${user} -p${password} ${name} < ${baseBackUpPath}/${currentBackupTimestamp}/${name}.sql`;
|
|
1021
|
-
if (fs.existsSync(`${baseBackUpPath}/${currentBackupTimestamp}/${name}-parths.json`)) {
|
|
1022
|
-
const names = JSON.parse(
|
|
1023
|
-
fs.readFileSync(`${baseBackUpPath}/${currentBackupTimestamp}/${name}-parths.json`, 'utf8'),
|
|
1024
|
-
).map((p) => p.replaceAll(`\\`, '/').replaceAll('C:/', '/').replaceAll('c:/', '/'));
|
|
1025
|
-
|
|
1026
|
-
await mergeFile(names, `${baseBackUpPath}/${currentBackupTimestamp}/${name}.sql`);
|
|
1027
|
-
}
|
|
1028
|
-
} else {
|
|
1029
|
-
cmd = `mysql -u ${user} -p${password} ${name} < ${
|
|
1030
|
-
backupPath ? backupPath : `./engine-private/sql-backups/${name}.sql`
|
|
1031
|
-
}`;
|
|
1032
|
-
if (
|
|
1033
|
-
fs.existsSync(
|
|
1034
|
-
`${
|
|
1035
|
-
backupPath ? backupPath.split('/').slice(0, -1).join('/') : `./engine-private/sql-backups`
|
|
1036
|
-
}/${name}-parths.json`,
|
|
1037
|
-
)
|
|
1038
|
-
) {
|
|
1039
|
-
const names = JSON.parse(
|
|
1040
|
-
fs.readFileSync(
|
|
1041
|
-
`${
|
|
1042
|
-
backupPath ? backupPath.split('/').slice(0, -1).join('/') : `./engine-private/sql-backups`
|
|
1043
|
-
}/${name}-parths.json`,
|
|
1044
|
-
'utf8',
|
|
1045
|
-
),
|
|
1046
|
-
).map((p) => p.replaceAll(`\\`, '/').replaceAll('C:/', '/').replaceAll('c:/', '/'));
|
|
1047
|
-
|
|
1048
|
-
await mergeFile(
|
|
1049
|
-
names,
|
|
1050
|
-
`${
|
|
1051
|
-
backupPath ? backupPath.split('/').slice(0, -1).join('/') : `./engine-private/sql-backups`
|
|
1052
|
-
}/${name}.sql`,
|
|
1053
|
-
);
|
|
1054
|
-
}
|
|
1055
|
-
}
|
|
1056
|
-
}
|
|
1057
|
-
break;
|
|
1058
|
-
|
|
1059
|
-
case 'mongoose':
|
|
1060
|
-
{
|
|
1061
|
-
if (process.argv.includes('cron')) {
|
|
1062
|
-
cmd = `mongorestore -d ${name} ${baseBackUpPath}/${currentBackupTimestamp}/${name}`;
|
|
1063
|
-
} else cmd = `mongorestore -d ${name} ${backupPath ? backupPath : `./engine-private/mongodb-backup/${name}`}`;
|
|
1064
|
-
}
|
|
1065
|
-
break;
|
|
1066
|
-
}
|
|
1067
|
-
|
|
1068
|
-
// logger.info('Restore', cmd);
|
|
1069
|
-
|
|
1070
|
-
return cmd;
|
|
1071
|
-
};
|
|
1072
|
-
|
|
1073
850
|
const getPathsSSR = (conf) => {
|
|
1074
851
|
const paths = ['src/client/ssr/Render.js'];
|
|
1075
852
|
for (const o of conf.head) paths.push(`src/client/ssr/head/${o}.js`);
|
|
@@ -1093,37 +870,6 @@ const Cmd = {
|
|
|
1093
870
|
}${options?.git ? `--git ` : ''}${deployList} ${jobList}`,
|
|
1094
871
|
};
|
|
1095
872
|
|
|
1096
|
-
const fixDependencies = async () => {
|
|
1097
|
-
return;
|
|
1098
|
-
// sed -i "$line_number s,.*,$new_text," "$file"
|
|
1099
|
-
// sed -i "$line_number c \\$new_text" "$file"
|
|
1100
|
-
const dep = fs.readFileSync(`./node_modules/peer/dist/module.mjs`, 'utf8');
|
|
1101
|
-
const errorLine = `import {WebSocketServer as $hSjDC$WebSocketServer} from "ws";`;
|
|
1102
|
-
|
|
1103
|
-
fs.writeFileSync(
|
|
1104
|
-
`./node_modules/peer/dist/module.mjs`,
|
|
1105
|
-
dep.replaceAll(
|
|
1106
|
-
errorLine,
|
|
1107
|
-
`import WebSocketServer from "ws";
|
|
1108
|
-
let $hSjDC$WebSocketServer = WebSocketServer.Server;`,
|
|
1109
|
-
),
|
|
1110
|
-
'utf8',
|
|
1111
|
-
);
|
|
1112
|
-
};
|
|
1113
|
-
|
|
1114
|
-
const maintenanceMiddleware = (req, res, port, proxyRouter) => {
|
|
1115
|
-
if (process.argv.includes('maintenance') && globalThis.defaultHtmlSrcMaintenance) {
|
|
1116
|
-
if (req.method.toUpperCase() === 'GET') {
|
|
1117
|
-
res.set('Content-Type', 'text/html');
|
|
1118
|
-
return res.status(503).send(globalThis.defaultHtmlSrcMaintenance);
|
|
1119
|
-
}
|
|
1120
|
-
return res.status(503).json({
|
|
1121
|
-
status: 'error',
|
|
1122
|
-
message: 'Server is under maintenance',
|
|
1123
|
-
});
|
|
1124
|
-
}
|
|
1125
|
-
};
|
|
1126
|
-
|
|
1127
873
|
const splitFileFactory = async (name, _path) => {
|
|
1128
874
|
const stats = fs.statSync(_path);
|
|
1129
875
|
const maxSizeInBytes = 1024 * 1024 * 50; // 50 mb
|
|
@@ -1152,14 +898,6 @@ const splitFileFactory = async (name, _path) => {
|
|
|
1152
898
|
return false;
|
|
1153
899
|
};
|
|
1154
900
|
|
|
1155
|
-
const setUpProxyMaintenanceServer = ({ deployGroupId }) => {
|
|
1156
|
-
shellExec(`pm2 kill`);
|
|
1157
|
-
shellExec(`node bin/deploy valkey-service`);
|
|
1158
|
-
const proxyDeployId = fs.readFileSync(`./engine-private/deploy/${deployGroupId}.proxy`, 'utf8').trim();
|
|
1159
|
-
shellExec(`node bin/deploy conf ${proxyDeployId} production`);
|
|
1160
|
-
shellExec(`npm start ${proxyDeployId} maintenance`);
|
|
1161
|
-
};
|
|
1162
|
-
|
|
1163
901
|
const getNpmRootPath = () =>
|
|
1164
902
|
shellExec(`npm root -g`, {
|
|
1165
903
|
stdout: true,
|
|
@@ -1258,17 +996,9 @@ export {
|
|
|
1258
996
|
getDataDeploy,
|
|
1259
997
|
validateTemplatePath,
|
|
1260
998
|
buildReplicaId,
|
|
1261
|
-
restoreMacroDb,
|
|
1262
|
-
getDeployGroupId,
|
|
1263
|
-
execDeploy,
|
|
1264
|
-
deployRun,
|
|
1265
999
|
getCronBackUpFolder,
|
|
1266
|
-
getRestoreCronCmd,
|
|
1267
1000
|
mergeFile,
|
|
1268
|
-
fixDependencies,
|
|
1269
1001
|
getDeployId,
|
|
1270
|
-
maintenanceMiddleware,
|
|
1271
|
-
setUpProxyMaintenanceServer,
|
|
1272
1002
|
getPathsSSR,
|
|
1273
1003
|
buildKindPorts,
|
|
1274
1004
|
buildPortProxyRouter,
|
|
@@ -1276,7 +1006,6 @@ export {
|
|
|
1276
1006
|
getNpmRootPath,
|
|
1277
1007
|
getUnderpostRootPath,
|
|
1278
1008
|
writeEnv,
|
|
1279
|
-
deployTest,
|
|
1280
1009
|
pathPortAssignmentFactory,
|
|
1281
1010
|
deployRangePortFactory,
|
|
1282
1011
|
awaitDeployMonitor,
|
package/src/server/proxy.js
CHANGED
|
@@ -6,7 +6,7 @@ import dotenv from 'dotenv';
|
|
|
6
6
|
import { createProxyMiddleware } from 'http-proxy-middleware';
|
|
7
7
|
import { loggerFactory, loggerMiddleware } from './logger.js';
|
|
8
8
|
import { createSslServer, sslRedirectMiddleware } from './ssl.js';
|
|
9
|
-
import { buildPortProxyRouter, buildProxyRouter
|
|
9
|
+
import { buildPortProxyRouter, buildProxyRouter } from './conf.js';
|
|
10
10
|
import UnderpostStartUp from './start.js';
|
|
11
11
|
|
|
12
12
|
dotenv.config();
|
|
@@ -48,7 +48,6 @@ const buildProxy = async () => {
|
|
|
48
48
|
onProxyReq: (proxyReq, req, res, options) => {
|
|
49
49
|
// https://wtools.io/check-http-status-code
|
|
50
50
|
// http://nexodev.org
|
|
51
|
-
maintenanceMiddleware(req, res, port, proxyRouter);
|
|
52
51
|
sslRedirectMiddleware(req, res, port, proxyRouter);
|
|
53
52
|
},
|
|
54
53
|
pathRewrite: {
|
package/docker-compose.yml
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
# https://docs.docker.com/compose/compose-file/compose-file-v3
|
|
2
|
-
# https://docs.docker.com/engine/reference/commandline/compose/
|
|
3
|
-
version: '3'
|
|
4
|
-
services:
|
|
5
|
-
prometheus:
|
|
6
|
-
image: prom/prometheus
|
|
7
|
-
ports:
|
|
8
|
-
- 9090:9090
|
|
9
|
-
volumes:
|
|
10
|
-
- ./prometheus_data:/prometheus
|
|
11
|
-
- ./prometheus.yml:/etc/prometheus/prometheus.yml
|
|
12
|
-
command:
|
|
13
|
-
- '--config.file=/etc/prometheus/prometheus.yml'
|
|
14
|
-
networks:
|
|
15
|
-
- load-balancer
|
|
16
|
-
|
|
17
|
-
grafana:
|
|
18
|
-
image: grafana/grafana
|
|
19
|
-
ports:
|
|
20
|
-
- 3000:3000
|
|
21
|
-
volumes:
|
|
22
|
-
- ./grafana_data:/var/lib/grafana
|
|
23
|
-
# - ./grafana.ini:/etc/grafana/grafana.ini
|
|
24
|
-
networks:
|
|
25
|
-
- load-balancer
|
|
26
|
-
depends_on:
|
|
27
|
-
- prometheus
|
|
28
|
-
|
|
29
|
-
underpost-engine:
|
|
30
|
-
build:
|
|
31
|
-
dockerfile: ./Dockerfile
|
|
32
|
-
context: . # workdir path
|
|
33
|
-
# image: underpost-engine
|
|
34
|
-
# container_name: <name> ignore for replicas
|
|
35
|
-
ports:
|
|
36
|
-
- '22'
|
|
37
|
-
- '80' # host port allocated dynamically, host ports are unique independent of replicas
|
|
38
|
-
- '443'
|
|
39
|
-
- '3306'
|
|
40
|
-
- '27017'
|
|
41
|
-
- '4001-4002:3001'
|
|
42
|
-
- '3002-3020'
|
|
43
|
-
volumes:
|
|
44
|
-
- ./logs:/code/logs
|
|
45
|
-
deploy:
|
|
46
|
-
mode: replicated
|
|
47
|
-
replicas: 2
|
|
48
|
-
restart_policy:
|
|
49
|
-
condition: on-failure
|
|
50
|
-
delay: 5s
|
|
51
|
-
max_attempts: 3
|
|
52
|
-
window: 120s
|
|
53
|
-
resources:
|
|
54
|
-
limits:
|
|
55
|
-
cpus: '2'
|
|
56
|
-
memory: 400M
|
|
57
|
-
reservations:
|
|
58
|
-
cpus: '0.25'
|
|
59
|
-
memory: 20M
|
|
60
|
-
labels: # labels in Compose file instead of Dockerfile
|
|
61
|
-
engine.version: '2.8.853'
|
|
62
|
-
networks:
|
|
63
|
-
- load-balancer
|
|
64
|
-
|
|
65
|
-
networks:
|
|
66
|
-
load-balancer:
|
|
67
|
-
driver: bridge
|
package/prometheus.yml
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
# my global config
|
|
2
|
-
global:
|
|
3
|
-
scrape_interval: 5s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
|
|
4
|
-
evaluation_interval: 5s # Evaluate rules every 15 seconds. The default is every 1 minute.
|
|
5
|
-
# scrape_timeout is set to the global default (10s).
|
|
6
|
-
|
|
7
|
-
# Alertmanager configuration
|
|
8
|
-
alerting:
|
|
9
|
-
alertmanagers:
|
|
10
|
-
- static_configs:
|
|
11
|
-
- targets:
|
|
12
|
-
# - alertmanager:9093
|
|
13
|
-
|
|
14
|
-
# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
|
|
15
|
-
rule_files:
|
|
16
|
-
# - "first_rules.yml"
|
|
17
|
-
# - "second_rules.yml"
|
|
18
|
-
|
|
19
|
-
# A scrape configuration containing exactly one endpoint to scrape:
|
|
20
|
-
# Here it's Prometheus itself.
|
|
21
|
-
scrape_configs:
|
|
22
|
-
# The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
|
|
23
|
-
- job_name: 'prometheus_service'
|
|
24
|
-
|
|
25
|
-
# metrics_path defaults to '/metrics'
|
|
26
|
-
# scheme defaults to 'http'.
|
|
27
|
-
|
|
28
|
-
static_configs:
|
|
29
|
-
- targets: ['host.docker.internal:9090']
|
|
30
|
-
|
|
31
|
-
- job_name: 'nodejs_service'
|
|
32
|
-
static_configs:
|
|
33
|
-
- targets: ['host.docker.internal:4001', 'host.docker.internal:4002']
|
|
34
|
-
# - targets: ['localhost:4001', 'localhost:4002']
|
|
35
|
-
# - targets: ["host.docker.internal:3002"] # Windows
|
|
36
|
-
# - targets: ["docker.for.mac.localhost:9090"] # macOs
|