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.
Files changed (128) hide show
  1. package/.env.development +1 -0
  2. package/.env.production +1 -0
  3. package/.env.test +1 -0
  4. package/.github/workflows/{ghpkg.yml → ghpkg.ci.yml} +1 -1
  5. package/.github/workflows/{npmpkg.yml → npmpkg.ci.yml} +1 -1
  6. package/.github/workflows/{publish.yml → publish.ci.yml} +1 -1
  7. package/.github/workflows/{pwa-microservices-template.page.yml → pwa-microservices-template-page.cd.yml} +2 -2
  8. package/.github/workflows/{pwa-microservices-template.test.yml → pwa-microservices-template-test.ci.yml} +1 -1
  9. package/.github/workflows/release.cd.yml +37 -0
  10. package/.vscode/settings.json +0 -1
  11. package/README.md +16 -10
  12. package/bin/build.js +15 -5
  13. package/bin/cyberia0.js +78 -0
  14. package/bin/db.js +1 -3
  15. package/bin/deploy.js +29 -431
  16. package/bin/file.js +26 -9
  17. package/cli.md +102 -61
  18. package/conf.js +1 -1
  19. package/manifests/deployment/{dd-template-development → dd-default-development}/deployment.yaml +16 -16
  20. package/manifests/deployment/{dd-template-development → dd-default-development}/proxy.yaml +3 -3
  21. package/manifests/grafana/deployment.yaml +57 -0
  22. package/manifests/grafana/kustomization.yaml +7 -0
  23. package/manifests/grafana/pvc.yaml +12 -0
  24. package/manifests/grafana/service.yaml +14 -0
  25. package/manifests/maas/gpu-diag.sh +1 -1
  26. package/manifests/maas/ssh-cluster-info.sh +14 -0
  27. package/manifests/prometheus/deployment.yaml +82 -0
  28. package/package.json +4 -7
  29. package/src/api/user/user.router.js +24 -1
  30. package/src/api/user/user.service.js +9 -38
  31. package/src/cli/cluster.js +83 -29
  32. package/src/cli/cron.js +12 -45
  33. package/src/cli/db.js +149 -0
  34. package/src/cli/deploy.js +40 -81
  35. package/src/cli/index.js +29 -6
  36. package/src/cli/monitor.js +9 -16
  37. package/src/cli/repository.js +12 -5
  38. package/src/cli/run.js +175 -7
  39. package/src/cli/ssh.js +32 -0
  40. package/src/client/Default.index.js +7 -5
  41. package/src/client/components/core/Account.js +7 -3
  42. package/src/client/components/core/Chat.js +1 -1
  43. package/src/client/components/core/CommonJs.js +24 -22
  44. package/src/client/components/core/Content.js +12 -12
  45. package/src/client/components/core/Css.js +262 -18
  46. package/src/client/components/core/CssCore.js +8 -8
  47. package/src/client/components/core/Docs.js +14 -61
  48. package/src/client/components/core/DropDown.js +137 -82
  49. package/src/client/components/core/EventsUI.js +92 -5
  50. package/src/client/components/core/Input.js +6 -1
  51. package/src/client/components/core/LoadingAnimation.js +8 -15
  52. package/src/client/components/core/LogIn.js +3 -0
  53. package/src/client/components/core/LogOut.js +1 -1
  54. package/src/client/components/core/Modal.js +601 -137
  55. package/src/client/components/core/NotificationManager.js +2 -2
  56. package/src/client/components/core/ObjectLayerEngine.js +638 -0
  57. package/src/client/components/core/Panel.js +158 -34
  58. package/src/client/components/core/PanelForm.js +12 -3
  59. package/src/client/components/core/Recover.js +6 -3
  60. package/src/client/components/core/Router.js +77 -17
  61. package/src/client/components/core/Scroll.js +65 -120
  62. package/src/client/components/core/SignUp.js +1 -0
  63. package/src/client/components/core/SocketIo.js +3 -3
  64. package/src/client/components/core/Translate.js +6 -2
  65. package/src/client/components/core/VanillaJs.js +48 -5
  66. package/src/client/components/core/Worker.js +3 -1
  67. package/src/client/components/default/CssDefault.js +17 -3
  68. package/src/client/components/default/MenuDefault.js +266 -47
  69. package/src/client/components/default/RoutesDefault.js +8 -14
  70. package/src/client/public/default/android-chrome-144x144.png +0 -0
  71. package/src/client/public/default/android-chrome-192x192.png +0 -0
  72. package/src/client/public/default/android-chrome-256x256.png +0 -0
  73. package/src/client/public/default/android-chrome-36x36.png +0 -0
  74. package/src/client/public/default/android-chrome-48x48.png +0 -0
  75. package/src/client/public/default/android-chrome-72x72.png +0 -0
  76. package/src/client/public/default/android-chrome-96x96.png +0 -0
  77. package/src/client/public/default/apple-touch-icon-114x114-precomposed.png +0 -0
  78. package/src/client/public/default/apple-touch-icon-114x114.png +0 -0
  79. package/src/client/public/default/apple-touch-icon-120x120-precomposed.png +0 -0
  80. package/src/client/public/default/apple-touch-icon-120x120.png +0 -0
  81. package/src/client/public/default/apple-touch-icon-144x144-precomposed.png +0 -0
  82. package/src/client/public/default/apple-touch-icon-144x144.png +0 -0
  83. package/src/client/public/default/apple-touch-icon-152x152-precomposed.png +0 -0
  84. package/src/client/public/default/apple-touch-icon-152x152.png +0 -0
  85. package/src/client/public/default/apple-touch-icon-180x180-precomposed.png +0 -0
  86. package/src/client/public/default/apple-touch-icon-180x180.png +0 -0
  87. package/src/client/public/default/apple-touch-icon-57x57-precomposed.png +0 -0
  88. package/src/client/public/default/apple-touch-icon-57x57.png +0 -0
  89. package/src/client/public/default/apple-touch-icon-60x60-precomposed.png +0 -0
  90. package/src/client/public/default/apple-touch-icon-60x60.png +0 -0
  91. package/src/client/public/default/apple-touch-icon-72x72-precomposed.png +0 -0
  92. package/src/client/public/default/apple-touch-icon-72x72.png +0 -0
  93. package/src/client/public/default/apple-touch-icon-76x76-precomposed.png +0 -0
  94. package/src/client/public/default/apple-touch-icon-76x76.png +0 -0
  95. package/src/client/public/default/apple-touch-icon-precomposed.png +0 -0
  96. package/src/client/public/default/apple-touch-icon.png +0 -0
  97. package/src/client/public/default/assets/background/dark.jpg +0 -0
  98. package/src/client/public/default/assets/background/dark.svg +557 -0
  99. package/src/client/public/default/assets/logo/base-icon.png +0 -0
  100. package/src/client/public/default/assets/logo/underpost.gif +0 -0
  101. package/src/client/public/default/assets/mailer/api-user-check.png +0 -0
  102. package/src/client/public/default/assets/mailer/api-user-invalid-token.png +0 -0
  103. package/src/client/public/default/assets/mailer/api-user-recover.png +0 -0
  104. package/src/client/public/default/favicon-16x16.png +0 -0
  105. package/src/client/public/default/favicon-32x32.png +0 -0
  106. package/src/client/public/default/favicon.ico +0 -0
  107. package/src/client/public/default/mstile-144x144.png +0 -0
  108. package/src/client/public/default/mstile-150x150.png +0 -0
  109. package/src/client/public/default/mstile-310x150.png +0 -0
  110. package/src/client/public/default/mstile-310x310.png +0 -0
  111. package/src/client/public/default/mstile-70x70.png +0 -0
  112. package/src/client/public/default/safari-pinned-tab.svg +24 -0
  113. package/src/client/ssr/body/DefaultSplashScreen.js +2 -2
  114. package/src/index.js +9 -1
  115. package/src/mailer/MailerProvider.js +37 -0
  116. package/src/monitor.js +24 -0
  117. package/src/runtime/lampp/Dockerfile +30 -39
  118. package/src/runtime/lampp/Lampp.js +11 -2
  119. package/src/server/client-build-docs.js +205 -0
  120. package/src/server/client-build-live.js +1 -1
  121. package/src/server/client-build.js +16 -166
  122. package/src/server/client-dev-server.js +1 -1
  123. package/src/server/conf.js +14 -277
  124. package/src/server/proxy.js +1 -2
  125. package/src/server/start.js +3 -3
  126. package/src/server/valkey.js +102 -41
  127. package/docker-compose.yml +0 -67
  128. package/prometheus.yml +0 -36
@@ -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
- let valkeyEnabled = true;
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 disableValkeyErrorMessage = 'valkey is not enabled';
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: '', port: 0 },
18
- valkeyServerConnectionOptions = { host: '', port: 0 },
19
+ instance = { host: '', path: '' },
20
+ valkeyServerConnectionOptions = { host: '', path: '' },
19
21
  ) => {
20
- ValkeyInstances[`${instance.host}${instance.path}`] = await ValkeyAPI.valkeyClientFactory(
21
- valkeyServerConnectionOptions,
22
- );
23
- return ValkeyInstances[`${instance.host}${instance.path}`];
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
- valkey.disconnect();
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: '', port: 0 }, key = '') => {
64
- if (!valkeyEnabled) {
65
- logger.warn(disableValkeyErrorMessage + ' get', key);
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
- return JSON.parse(object);
71
- } catch (error) {
72
- logger.error(error);
73
- return object;
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: '', port: 0 }, key = '', payload = {}) => {
78
- if (!valkeyEnabled) throw new Error(disableValkeyErrorMessage);
79
- return await ValkeyInstances[`${options.host}${options.path}`].set(key, JSON.stringify(payload));
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: '', port: 0 }, key = '', payload = {}) => {
83
- if (!valkeyEnabled) throw new Error(disableValkeyErrorMessage);
84
- const object = await getValkeyObject(key);
85
- object.updatedAt = new Date().toISOString();
86
- return await ValkeyInstances[`${options.host}${options.path}`].set(key, JSON.stringify({ ...object, ...payload }));
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: {} }, module = '') => {
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 (module) {
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(`module schema not found: ${module}`);
179
+ throw new Error(`model schema not found: ${model}`);
119
180
  }
120
181
  };
121
182
 
@@ -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