underpost 2.8.84 → 2.8.86
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 -0
- package/.env.production +1 -0
- package/.env.test +1 -0
- package/.github/workflows/{ghpkg.yml → ghpkg.ci.yml} +1 -1
- package/.github/workflows/{npmpkg.yml → npmpkg.ci.yml} +1 -1
- package/.github/workflows/{publish.yml → publish.ci.yml} +1 -1
- package/.github/workflows/{pwa-microservices-template.page.yml → pwa-microservices-template-page.cd.yml} +2 -2
- package/.github/workflows/{pwa-microservices-template.test.yml → pwa-microservices-template-test.ci.yml} +1 -1
- package/.github/workflows/release.cd.yml +37 -0
- package/.vscode/settings.json +0 -1
- package/README.md +16 -10
- package/bin/build.js +15 -5
- package/bin/cyberia0.js +78 -0
- package/bin/db.js +1 -3
- package/bin/deploy.js +29 -431
- package/bin/file.js +26 -9
- package/cli.md +102 -61
- package/conf.js +1 -1
- 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/maas/gpu-diag.sh +1 -1
- package/manifests/maas/ssh-cluster-info.sh +14 -0
- package/manifests/prometheus/deployment.yaml +82 -0
- package/package.json +4 -7
- package/src/api/user/user.router.js +24 -1
- package/src/api/user/user.service.js +9 -38
- package/src/cli/cluster.js +83 -29
- package/src/cli/cron.js +12 -45
- package/src/cli/db.js +149 -0
- package/src/cli/deploy.js +40 -81
- package/src/cli/index.js +29 -6
- package/src/cli/monitor.js +9 -16
- package/src/cli/repository.js +12 -5
- package/src/cli/run.js +175 -7
- package/src/cli/ssh.js +32 -0
- package/src/client/Default.index.js +7 -5
- package/src/client/components/core/Account.js +7 -3
- package/src/client/components/core/Chat.js +1 -1
- package/src/client/components/core/CommonJs.js +24 -22
- package/src/client/components/core/Content.js +12 -12
- package/src/client/components/core/Css.js +262 -18
- package/src/client/components/core/CssCore.js +8 -8
- package/src/client/components/core/Docs.js +14 -61
- package/src/client/components/core/DropDown.js +137 -82
- package/src/client/components/core/EventsUI.js +92 -5
- package/src/client/components/core/Input.js +6 -1
- package/src/client/components/core/LoadingAnimation.js +8 -15
- package/src/client/components/core/LogIn.js +3 -0
- package/src/client/components/core/LogOut.js +1 -1
- package/src/client/components/core/Modal.js +601 -137
- package/src/client/components/core/NotificationManager.js +2 -2
- package/src/client/components/core/ObjectLayerEngine.js +638 -0
- package/src/client/components/core/Panel.js +158 -34
- package/src/client/components/core/PanelForm.js +12 -3
- package/src/client/components/core/Recover.js +6 -3
- package/src/client/components/core/Router.js +77 -17
- package/src/client/components/core/Scroll.js +65 -120
- package/src/client/components/core/SignUp.js +1 -0
- package/src/client/components/core/SocketIo.js +3 -3
- package/src/client/components/core/Translate.js +6 -2
- package/src/client/components/core/VanillaJs.js +48 -5
- package/src/client/components/core/Worker.js +3 -1
- package/src/client/components/default/CssDefault.js +17 -3
- package/src/client/components/default/MenuDefault.js +266 -47
- package/src/client/components/default/RoutesDefault.js +8 -14
- package/src/client/public/default/android-chrome-144x144.png +0 -0
- package/src/client/public/default/android-chrome-192x192.png +0 -0
- package/src/client/public/default/android-chrome-256x256.png +0 -0
- package/src/client/public/default/android-chrome-36x36.png +0 -0
- package/src/client/public/default/android-chrome-48x48.png +0 -0
- package/src/client/public/default/android-chrome-72x72.png +0 -0
- package/src/client/public/default/android-chrome-96x96.png +0 -0
- package/src/client/public/default/apple-touch-icon-114x114-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon-114x114.png +0 -0
- package/src/client/public/default/apple-touch-icon-120x120-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon-120x120.png +0 -0
- package/src/client/public/default/apple-touch-icon-144x144-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon-144x144.png +0 -0
- package/src/client/public/default/apple-touch-icon-152x152-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon-152x152.png +0 -0
- package/src/client/public/default/apple-touch-icon-180x180-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon-180x180.png +0 -0
- package/src/client/public/default/apple-touch-icon-57x57-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon-57x57.png +0 -0
- package/src/client/public/default/apple-touch-icon-60x60-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon-60x60.png +0 -0
- package/src/client/public/default/apple-touch-icon-72x72-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon-72x72.png +0 -0
- package/src/client/public/default/apple-touch-icon-76x76-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon-76x76.png +0 -0
- package/src/client/public/default/apple-touch-icon-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon.png +0 -0
- package/src/client/public/default/assets/background/dark.jpg +0 -0
- package/src/client/public/default/assets/background/dark.svg +557 -0
- package/src/client/public/default/assets/logo/base-icon.png +0 -0
- package/src/client/public/default/assets/logo/underpost.gif +0 -0
- package/src/client/public/default/assets/mailer/api-user-check.png +0 -0
- package/src/client/public/default/assets/mailer/api-user-invalid-token.png +0 -0
- package/src/client/public/default/assets/mailer/api-user-recover.png +0 -0
- package/src/client/public/default/favicon-16x16.png +0 -0
- package/src/client/public/default/favicon-32x32.png +0 -0
- package/src/client/public/default/favicon.ico +0 -0
- package/src/client/public/default/mstile-144x144.png +0 -0
- package/src/client/public/default/mstile-150x150.png +0 -0
- package/src/client/public/default/mstile-310x150.png +0 -0
- package/src/client/public/default/mstile-310x310.png +0 -0
- package/src/client/public/default/mstile-70x70.png +0 -0
- package/src/client/public/default/safari-pinned-tab.svg +24 -0
- package/src/client/ssr/body/DefaultSplashScreen.js +2 -2
- package/src/index.js +9 -1
- package/src/mailer/MailerProvider.js +37 -0
- package/src/monitor.js +24 -0
- package/src/runtime/lampp/Dockerfile +30 -39
- package/src/runtime/lampp/Lampp.js +11 -2
- package/src/server/client-build-docs.js +205 -0
- package/src/server/client-build-live.js +1 -1
- package/src/server/client-build.js +16 -166
- package/src/server/client-dev-server.js +1 -1
- package/src/server/conf.js +14 -277
- package/src/server/proxy.js +1 -2
- package/src/server/start.js +3 -3
- package/src/server/valkey.js +102 -41
- package/docker-compose.yml +0 -67
- package/prometheus.yml +0 -36
package/src/server/valkey.js
CHANGED
|
@@ -5,22 +5,71 @@ import { loggerFactory } from './logger.js';
|
|
|
5
5
|
|
|
6
6
|
const logger = loggerFactory(import.meta);
|
|
7
7
|
|
|
8
|
+
// Per-instance registries keyed by `${host}${path}`
|
|
8
9
|
const ValkeyInstances = {};
|
|
10
|
+
const DummyStores = {}; // in-memory Maps per instance
|
|
11
|
+
const ValkeyStatus = {}; // 'connected' | 'dummy' | 'error' | undefined
|
|
9
12
|
|
|
10
|
-
|
|
13
|
+
// Backward-compatible overall flag: true if any instance is connected
|
|
14
|
+
const isValkeyEnable = () => Object.values(ValkeyStatus).some((s) => s === 'connected');
|
|
11
15
|
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
const isValkeyEnable = () => valkeyEnabled;
|
|
16
|
+
const _instanceKey = (opts = { host: '', path: '' }) => `${opts.host || ''}${opts.path || ''}`;
|
|
15
17
|
|
|
16
18
|
const createValkeyConnection = async (
|
|
17
|
-
instance = { host: '',
|
|
18
|
-
valkeyServerConnectionOptions = { host: '',
|
|
19
|
+
instance = { host: '', path: '' },
|
|
20
|
+
valkeyServerConnectionOptions = { host: '', path: '' },
|
|
19
21
|
) => {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
);
|
|
23
|
-
|
|
22
|
+
const key = _instanceKey(instance);
|
|
23
|
+
// Initialize dummy store for the instance
|
|
24
|
+
if (!DummyStores[key]) DummyStores[key] = new Map();
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
const client = await ValkeyAPI.valkeyClientFactory(valkeyServerConnectionOptions);
|
|
28
|
+
|
|
29
|
+
// Attach listeners for visibility
|
|
30
|
+
client.on?.('ready', () => {
|
|
31
|
+
ValkeyStatus[key] = 'connected';
|
|
32
|
+
logger.info('Valkey connected', { instance, status: ValkeyStatus[key] });
|
|
33
|
+
});
|
|
34
|
+
client.on?.('error', (err) => {
|
|
35
|
+
// Switch to dummy if not yet connected
|
|
36
|
+
if (ValkeyStatus[key] !== 'connected') {
|
|
37
|
+
ValkeyStatus[key] = 'dummy';
|
|
38
|
+
} else {
|
|
39
|
+
ValkeyStatus[key] = 'error';
|
|
40
|
+
}
|
|
41
|
+
logger.warn('Valkey error', { err: err?.message, instance, status: ValkeyStatus[key] });
|
|
42
|
+
});
|
|
43
|
+
client.on?.('end', () => {
|
|
44
|
+
if (ValkeyStatus[key] !== 'dummy') ValkeyStatus[key] = 'error';
|
|
45
|
+
logger.warn('Valkey connection ended', { instance, status: ValkeyStatus[key] });
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// Probe connectivity with a short timeout
|
|
49
|
+
const probe = async () => {
|
|
50
|
+
try {
|
|
51
|
+
// basic ping via SET/GET roundtrip
|
|
52
|
+
const probeKey = `__vk_probe_${Date.now()}`;
|
|
53
|
+
await client.set(probeKey, '1');
|
|
54
|
+
await client.get(probeKey);
|
|
55
|
+
ValkeyStatus[key] = 'connected';
|
|
56
|
+
} catch (e) {
|
|
57
|
+
ValkeyStatus[key] = 'dummy';
|
|
58
|
+
logger.warn('Valkey probe failed, falling back to dummy', { instance, error: e?.message });
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// Race with timeout to avoid hanging
|
|
63
|
+
await Promise.race([probe(), new Promise((resolve) => setTimeout(resolve, 1000))]);
|
|
64
|
+
|
|
65
|
+
ValkeyInstances[key] = client;
|
|
66
|
+
if (!ValkeyStatus[key]) ValkeyStatus[key] = 'dummy';
|
|
67
|
+
} catch (err) {
|
|
68
|
+
ValkeyStatus[key] = 'dummy';
|
|
69
|
+
logger.warn('Valkey client creation failed, using dummy', { instance, error: err?.message });
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return ValkeyInstances[key];
|
|
24
73
|
};
|
|
25
74
|
|
|
26
75
|
const selectDtoFactory = (payload, select) => {
|
|
@@ -33,18 +82,12 @@ const selectDtoFactory = (payload, select) => {
|
|
|
33
82
|
|
|
34
83
|
const valkeyClientFactory = async (options) => {
|
|
35
84
|
const valkey = new Valkey({
|
|
36
|
-
// port: 6379,
|
|
37
|
-
// host: 'valkey-service.default.svc.cluster.local',
|
|
38
85
|
port: options?.port ? options.port : undefined,
|
|
39
86
|
host: options?.host ? options.host : undefined,
|
|
87
|
+
// Keep retry strategy minimal; state handled in createValkeyConnection
|
|
40
88
|
retryStrategy: (attempt) => {
|
|
41
|
-
if (attempt === 1)
|
|
42
|
-
|
|
43
|
-
valkeyEnabled = false;
|
|
44
|
-
logger.warn('Valkey service not enabled', { ...options, valkeyEnabled });
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
return 1000; // 1 second interval attempt
|
|
89
|
+
if (attempt === 1) return undefined; // stop aggressive retries early
|
|
90
|
+
return 1000; // retry interval if library continues
|
|
48
91
|
},
|
|
49
92
|
}); // Connect to 127.0.0.1:6379
|
|
50
93
|
// new Valkey(6380); // 127.0.0.1:6380
|
|
@@ -60,34 +103,52 @@ const valkeyClientFactory = async (options) => {
|
|
|
60
103
|
return valkey;
|
|
61
104
|
};
|
|
62
105
|
|
|
63
|
-
const getValkeyObject = async (options = { host: '',
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
return null;
|
|
67
|
-
}
|
|
68
|
-
const object = await ValkeyInstances[`${options.host}${options.path}`].get(key);
|
|
106
|
+
const getValkeyObject = async (options = { host: '', path: '' }, key = '') => {
|
|
107
|
+
const k = _instanceKey(options);
|
|
108
|
+
const status = ValkeyStatus[k];
|
|
69
109
|
try {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
110
|
+
if (status === 'connected' && ValkeyInstances[k]) {
|
|
111
|
+
const value = await ValkeyInstances[k].get(key);
|
|
112
|
+
if (value == null) return null;
|
|
113
|
+
try {
|
|
114
|
+
return JSON.parse(value);
|
|
115
|
+
} catch {
|
|
116
|
+
// not JSON, return raw string
|
|
117
|
+
return value;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
} catch (err) {
|
|
121
|
+
logger.warn('Valkey get failed, using dummy', { key, err: err?.message });
|
|
74
122
|
}
|
|
123
|
+
// Dummy fallback returns stored value as-is (string or object)
|
|
124
|
+
return DummyStores[k]?.get(key) ?? null;
|
|
75
125
|
};
|
|
76
126
|
|
|
77
|
-
const setValkeyObject = async (options = { host: '',
|
|
78
|
-
|
|
79
|
-
|
|
127
|
+
const setValkeyObject = async (options = { host: '', path: '' }, key = '', payload = {}) => {
|
|
128
|
+
const k = _instanceKey(options);
|
|
129
|
+
const isString = typeof payload === 'string';
|
|
130
|
+
const value = isString ? payload : JSON.stringify(payload);
|
|
131
|
+
try {
|
|
132
|
+
if (ValkeyStatus[k] === 'connected' && ValkeyInstances[k]) {
|
|
133
|
+
return await ValkeyInstances[k].set(key, value);
|
|
134
|
+
}
|
|
135
|
+
} catch (err) {
|
|
136
|
+
logger.warn('Valkey set failed, writing to dummy', { key, err: err?.message });
|
|
137
|
+
}
|
|
138
|
+
if (!DummyStores[k]) DummyStores[k] = new Map();
|
|
139
|
+
// Store raw string or object accordingly
|
|
140
|
+
DummyStores[k].set(key, isString ? payload : payload);
|
|
141
|
+
return 'OK';
|
|
80
142
|
};
|
|
81
143
|
|
|
82
|
-
const updateValkeyObject = async (options = { host: '',
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
return await
|
|
144
|
+
const updateValkeyObject = async (options = { host: '', path: '' }, key = '', payload = {}) => {
|
|
145
|
+
let base = await getValkeyObject(options, key);
|
|
146
|
+
if (typeof base !== 'object' || base === null) base = {};
|
|
147
|
+
base.updatedAt = new Date().toISOString();
|
|
148
|
+
return await setValkeyObject(options, key, { ...base, ...payload });
|
|
87
149
|
};
|
|
88
150
|
|
|
89
|
-
const valkeyObjectFactory = async (options = { host: 'localhost', object: {} },
|
|
90
|
-
if (!valkeyEnabled) throw new Error(disableValkeyErrorMessage);
|
|
151
|
+
const valkeyObjectFactory = async (options = { host: 'localhost', object: {} }, model = '') => {
|
|
91
152
|
const idoDate = new Date().toISOString();
|
|
92
153
|
options.object = options.object || {};
|
|
93
154
|
const { object } = options;
|
|
@@ -95,7 +156,7 @@ const valkeyObjectFactory = async (options = { host: 'localhost', object: {} },
|
|
|
95
156
|
object._id = _id;
|
|
96
157
|
object.createdAt = idoDate;
|
|
97
158
|
object.updatedAt = idoDate;
|
|
98
|
-
switch (
|
|
159
|
+
switch (model) {
|
|
99
160
|
case 'user': {
|
|
100
161
|
const role = 'guest';
|
|
101
162
|
object._id = `${role}${_id}`;
|
|
@@ -115,7 +176,7 @@ const valkeyObjectFactory = async (options = { host: 'localhost', object: {} },
|
|
|
115
176
|
};
|
|
116
177
|
}
|
|
117
178
|
default:
|
|
118
|
-
throw new Error(`
|
|
179
|
+
throw new Error(`model schema not found: ${model}`);
|
|
119
180
|
}
|
|
120
181
|
};
|
|
121
182
|
|
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.84'
|
|
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
|