underpost 2.8.82 → 2.8.85
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} +5 -5
- package/.github/workflows/{npmpkg.yml → npmpkg.ci.yml} +5 -5
- package/.github/workflows/{publish.yml → publish.ci.yml} +1 -1
- package/.github/workflows/{pwa-microservices-template.page.yml → pwa-microservices-template-page.cd.yml} +1 -1
- package/.github/workflows/{pwa-microservices-template.test.yml → pwa-microservices-template-test.ci.yml} +1 -1
- package/.vscode/extensions.json +1 -1
- package/.vscode/settings.json +0 -44
- package/README.md +62 -2
- package/bin/build.js +15 -5
- package/bin/deploy.js +42 -92
- package/bin/file.js +33 -9
- package/bin/vs.js +12 -4
- package/cli.md +90 -42
- package/conf.js +1 -1
- package/docker-compose.yml +1 -1
- package/manifests/deployment/dd-template-development/deployment.yaml +2 -2
- package/manifests/deployment/tensorflow/tf-gpu-test.yaml +65 -0
- package/manifests/maas/device-scan.sh +3 -3
- package/manifests/maas/gpu-diag.sh +19 -0
- package/manifests/maas/maas-setup.sh +10 -10
- package/manifests/maas/snap-clean.sh +26 -0
- package/package.json +4 -6
- package/src/api/user/user.router.js +24 -1
- package/src/api/user/user.service.js +1 -4
- package/src/cli/baremetal.js +105 -73
- package/src/cli/cloud-init.js +21 -12
- package/src/cli/cluster.js +227 -133
- package/src/cli/deploy.js +34 -0
- package/src/cli/index.js +28 -1
- package/src/cli/monitor.js +8 -12
- package/src/cli/repository.js +7 -4
- package/src/cli/run.js +367 -0
- package/src/cli/ssh.js +32 -0
- package/src/cli/test.js +1 -1
- package/src/client/Default.index.js +7 -3
- package/src/client/components/core/Account.js +1 -1
- 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 +1 -5
- package/src/client/components/core/Css.js +258 -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/LoadingAnimation.js +8 -15
- package/src/client/components/core/Modal.js +597 -136
- 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 +1 -1
- package/src/client/components/core/Router.js +77 -17
- 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 +0 -3
- 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 +264 -45
- package/src/client/components/default/RoutesDefault.js +6 -12
- 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 +34 -17
- 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.js +16 -166
- package/src/server/conf.js +18 -8
- package/src/server/process.js +16 -19
- package/src/server/valkey.js +102 -41
|
@@ -17,11 +17,11 @@ import dotenv from 'dotenv';
|
|
|
17
17
|
import AdmZip from 'adm-zip';
|
|
18
18
|
import * as dir from 'path';
|
|
19
19
|
import { shellExec } from './process.js';
|
|
20
|
-
import swaggerAutoGen from 'swagger-autogen';
|
|
21
20
|
import { SitemapStream, streamToPromise } from 'sitemap';
|
|
22
21
|
import { Readable } from 'stream';
|
|
23
22
|
import { buildIcons, buildTextImg, getBufferPngText } from './client-icons.js';
|
|
24
23
|
import Underpost from '../index.js';
|
|
24
|
+
import { buildDocs } from './client-build-docs.js';
|
|
25
25
|
|
|
26
26
|
dotenv.config();
|
|
27
27
|
|
|
@@ -333,14 +333,7 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
|
|
|
333
333
|
minifyBuild || process.env.NODE_ENV === 'production' ? UglifyJS.minify(jsSrc).code : jsSrc,
|
|
334
334
|
'utf8',
|
|
335
335
|
);
|
|
336
|
-
|
|
337
|
-
// const title = `${metadata && metadata.title ? metadata.title : cap(client)}${
|
|
338
|
-
// view.title ? ` | ${view.title}` : view.path !== '/' ? ` | ${titleFormatted(view.path)}` : ''
|
|
339
|
-
// }`;
|
|
340
|
-
|
|
341
|
-
const title = `${
|
|
342
|
-
view.title ? `${view.title} | ` : view.path !== '/' ? `${titleFormatted(view.path)} | ` : ''
|
|
343
|
-
}${metadata && metadata.title ? metadata.title : cap(client)}`;
|
|
336
|
+
const title = metadata.title ? metadata.title : title;
|
|
344
337
|
|
|
345
338
|
const canonicalURL = `https://${host}${path}${
|
|
346
339
|
view.path === '/' ? (path === '/' ? '' : '/') : path === '/' ? `${view.path.slice(1)}/` : `${view.path}/`
|
|
@@ -441,27 +434,13 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
|
|
|
441
434
|
case 'CyberiaDefaultSplashScreen':
|
|
442
435
|
case 'NexodevSplashScreen':
|
|
443
436
|
case 'DefaultSplashScreen':
|
|
444
|
-
if (backgroundImage)
|
|
437
|
+
if (backgroundImage)
|
|
445
438
|
ssrHeadComponents += SrrComponent({
|
|
439
|
+
...metadata,
|
|
446
440
|
backgroundImage: (path === '/' ? path : `${path}/`) + backgroundImage,
|
|
447
441
|
});
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
// .toString('base64')}`,
|
|
451
|
-
break;
|
|
452
|
-
} else {
|
|
453
|
-
ssrHeadComponents += SrrComponent({ metadata });
|
|
454
|
-
break;
|
|
455
|
-
const bufferBackgroundImage = await getBufferPngText({
|
|
456
|
-
text: ' ',
|
|
457
|
-
textColor: metadata?.themeColor ? metadata.themeColor : '#ececec',
|
|
458
|
-
size: '100x100',
|
|
459
|
-
bgColor: metadata?.themeColor ? metadata.themeColor : '#ececec',
|
|
460
|
-
});
|
|
461
|
-
ssrHeadComponents += SrrComponent({
|
|
462
|
-
backgroundImage: `data:image/png;base64,${bufferBackgroundImage.toString('base64')}`,
|
|
463
|
-
});
|
|
464
|
-
}
|
|
442
|
+
else ssrHeadComponents += SrrComponent({ metadata });
|
|
443
|
+
break;
|
|
465
444
|
|
|
466
445
|
case 'CyberiaSplashScreenLore': {
|
|
467
446
|
ssrBodyComponents += SrrComponent({
|
|
@@ -557,145 +536,16 @@ Sitemap: https://${host}${path === '/' ? '' : path}/sitemap.xml`,
|
|
|
557
536
|
}
|
|
558
537
|
|
|
559
538
|
if (!enableLiveRebuild && !process.argv.includes('l') && !process.argv.includes('deploy') && docsBuild) {
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
shellExec(`npm run docs`, { silent: true });
|
|
571
|
-
|
|
572
|
-
// coverage
|
|
573
|
-
if (!fs.existsSync(`./coverage`)) {
|
|
574
|
-
shellExec(`npm test`);
|
|
575
|
-
}
|
|
576
|
-
const coverageBuildPath = `${jsDocsConfig.opts.destination}/coverage`;
|
|
577
|
-
fs.mkdirSync(coverageBuildPath, { recursive: true });
|
|
578
|
-
fs.copySync(`./coverage`, coverageBuildPath);
|
|
579
|
-
|
|
580
|
-
// uml
|
|
581
|
-
// shellExec(`node bin/deploy uml ${host} ${path}`);
|
|
582
|
-
|
|
583
|
-
// https://swagger-autogen.github.io/docs/
|
|
584
|
-
|
|
585
|
-
const basePath = path === '/' ? `${process.env.BASE_API}` : `/${process.env.BASE_API}`;
|
|
586
|
-
|
|
587
|
-
const doc = {
|
|
588
|
-
info: {
|
|
589
|
-
version: packageData.version, // by default: '1.0.0'
|
|
590
|
-
title: metadata?.title ? `${metadata.title}` : 'REST API', // by default: 'REST API'
|
|
591
|
-
description: metadata?.description ? metadata.description : '', // by default: ''
|
|
592
|
-
},
|
|
593
|
-
servers: [
|
|
594
|
-
{
|
|
595
|
-
url:
|
|
596
|
-
process.env.NODE_ENV === 'development'
|
|
597
|
-
? `http://localhost:${port}${path}${basePath}`
|
|
598
|
-
: `https://${host}${path}${basePath}`, // by default: 'http://localhost:3000'
|
|
599
|
-
description: `${process.env.NODE_ENV} server`, // by default: ''
|
|
600
|
-
},
|
|
601
|
-
],
|
|
602
|
-
tags: [
|
|
603
|
-
// by default: empty Array
|
|
604
|
-
{
|
|
605
|
-
name: 'user', // Tag name
|
|
606
|
-
description: 'User API operations', // Tag description
|
|
607
|
-
},
|
|
608
|
-
],
|
|
609
|
-
components: {
|
|
610
|
-
schemas: {
|
|
611
|
-
userRequest: {
|
|
612
|
-
username: 'user123',
|
|
613
|
-
password: 'Password123',
|
|
614
|
-
email: 'user@example.com',
|
|
615
|
-
},
|
|
616
|
-
userResponse: {
|
|
617
|
-
status: 'success',
|
|
618
|
-
data: {
|
|
619
|
-
token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7Il9pZCI6IjY2YzM3N2Y1N2Y5OWU1OTY5YjgxZG...',
|
|
620
|
-
user: {
|
|
621
|
-
_id: '66c377f57f99e5969b81de89',
|
|
622
|
-
email: 'user@example.com',
|
|
623
|
-
emailConfirmed: false,
|
|
624
|
-
username: 'user123',
|
|
625
|
-
role: 'user',
|
|
626
|
-
profileImageId: '66c377f57f99e5969b81de87',
|
|
627
|
-
},
|
|
628
|
-
},
|
|
629
|
-
},
|
|
630
|
-
userUpdateResponse: {
|
|
631
|
-
status: 'success',
|
|
632
|
-
data: {
|
|
633
|
-
_id: '66c377f57f99e5969b81de89',
|
|
634
|
-
email: 'user@example.com',
|
|
635
|
-
emailConfirmed: false,
|
|
636
|
-
username: 'user123222',
|
|
637
|
-
role: 'user',
|
|
638
|
-
profileImageId: '66c377f57f99e5969b81de87',
|
|
639
|
-
},
|
|
640
|
-
},
|
|
641
|
-
userGetResponse: {
|
|
642
|
-
status: 'success',
|
|
643
|
-
data: {
|
|
644
|
-
_id: '66c377f57f99e5969b81de89',
|
|
645
|
-
email: 'user@example.com',
|
|
646
|
-
emailConfirmed: false,
|
|
647
|
-
username: 'user123222',
|
|
648
|
-
role: 'user',
|
|
649
|
-
profileImageId: '66c377f57f99e5969b81de87',
|
|
650
|
-
},
|
|
651
|
-
},
|
|
652
|
-
userLogInRequest: {
|
|
653
|
-
email: 'user@example.com',
|
|
654
|
-
password: 'Password123',
|
|
655
|
-
},
|
|
656
|
-
userBadRequestResponse: {
|
|
657
|
-
status: 'error',
|
|
658
|
-
message: 'Bad request. Please check your inputs, and try again',
|
|
659
|
-
},
|
|
660
|
-
},
|
|
661
|
-
securitySchemes: {
|
|
662
|
-
bearerAuth: {
|
|
663
|
-
type: 'http',
|
|
664
|
-
scheme: 'bearer',
|
|
665
|
-
},
|
|
666
|
-
},
|
|
667
|
-
},
|
|
668
|
-
};
|
|
669
|
-
|
|
670
|
-
// plantuml
|
|
671
|
-
logger.info('copy plantuml', `${rootClientPath}/docs/plantuml`);
|
|
672
|
-
fs.copySync(`./src/client/public/default/plantuml`, `${rootClientPath}/docs/plantuml`);
|
|
673
|
-
|
|
674
|
-
logger.warn('build swagger api docs', doc.info);
|
|
675
|
-
|
|
676
|
-
const outputFile = `./public/${host}${path === '/' ? path : `${path}/`}swagger-output.json`;
|
|
677
|
-
const routes = [];
|
|
678
|
-
for (const api of apis) {
|
|
679
|
-
if (['user'].includes(api)) routes.push(`./src/api/${api}/${api}.router.js`);
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
/* NOTE: If you are using the express Router, you must pass in the 'routes' only the
|
|
683
|
-
root file where the route starts, such as index.js, app.js, routes.js, etc ... */
|
|
684
|
-
|
|
685
|
-
await swaggerAutoGen({ openapi: '3.0.0' })(outputFile, routes, doc);
|
|
686
|
-
|
|
687
|
-
const htmlFiles = await fs.readdir(`./public/${host}/docs/engine/${Underpost.version.replace('v', '')}`);
|
|
688
|
-
for (const htmlFile of htmlFiles) {
|
|
689
|
-
if (htmlFile.match('.html')) {
|
|
690
|
-
fs.writeFileSync(
|
|
691
|
-
`./public/${host}/docs/engine/${Underpost.version.replace('v', '')}/${htmlFile}`,
|
|
692
|
-
fs
|
|
693
|
-
.readFileSync(`./public/${host}/docs/engine/${Underpost.version.replace('v', '')}/${htmlFile}`, 'utf8')
|
|
694
|
-
.replaceAll('Tutorials', 'References'),
|
|
695
|
-
'utf8',
|
|
696
|
-
);
|
|
697
|
-
}
|
|
698
|
-
}
|
|
539
|
+
await buildDocs({
|
|
540
|
+
host,
|
|
541
|
+
path,
|
|
542
|
+
port,
|
|
543
|
+
metadata,
|
|
544
|
+
apis,
|
|
545
|
+
publicClientId,
|
|
546
|
+
rootClientPath,
|
|
547
|
+
packageData,
|
|
548
|
+
});
|
|
699
549
|
}
|
|
700
550
|
|
|
701
551
|
if (client) {
|
package/src/server/conf.js
CHANGED
|
@@ -89,12 +89,19 @@ const Config = {
|
|
|
89
89
|
};
|
|
90
90
|
|
|
91
91
|
const loadConf = (deployId, envInput, subConf) => {
|
|
92
|
+
if (deployId === 'current') {
|
|
93
|
+
console.log(process.env.DEPLOY_ID);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
92
96
|
if (deployId === 'clean') {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
shellExec(`git checkout
|
|
96
|
-
shellExec(`git checkout
|
|
97
|
-
shellExec(`git checkout
|
|
97
|
+
const path = envInput ?? '.';
|
|
98
|
+
fs.removeSync(`${path}/.env`);
|
|
99
|
+
shellExec(`git checkout ${path}/.env.production`);
|
|
100
|
+
shellExec(`git checkout ${path}/.env.development`);
|
|
101
|
+
shellExec(`git checkout ${path}/.env.test`);
|
|
102
|
+
if (fs.existsSync(`${path}/jsdoc.json`)) shellExec(`git checkout ${path}/jsdoc.json`);
|
|
103
|
+
shellExec(`git checkout ${path}/package.json`);
|
|
104
|
+
shellExec(`git checkout ${path}/package-lock.json`);
|
|
98
105
|
return;
|
|
99
106
|
}
|
|
100
107
|
const folder = fs.existsSync(`./engine-private/replica/${deployId}`)
|
|
@@ -1171,7 +1178,7 @@ const writeEnv = (envPath, envObj) =>
|
|
|
1171
1178
|
'utf8',
|
|
1172
1179
|
);
|
|
1173
1180
|
|
|
1174
|
-
const buildCliDoc = (program) => {
|
|
1181
|
+
const buildCliDoc = (program, oldVersion, newVersion) => {
|
|
1175
1182
|
let md = shellExec(`node bin help`, { silent: true, stdout: true }).split('Options:');
|
|
1176
1183
|
const baseOptions =
|
|
1177
1184
|
`## ${md[0].split(`\n`)[2]}
|
|
@@ -1207,13 +1214,15 @@ const buildCliDoc = (program) => {
|
|
|
1207
1214
|
`
|
|
1208
1215
|
`;
|
|
1209
1216
|
});
|
|
1217
|
+
md = md.replaceAll(oldVersion, newVersion);
|
|
1210
1218
|
fs.writeFileSync(`./src/client/public/nexodev/docs/references/Command Line Interface.md`, md, 'utf8');
|
|
1211
1219
|
fs.writeFileSync(`./cli.md`, md, 'utf8');
|
|
1212
1220
|
const readmeSplit = `pwa-microservices-template</a>`;
|
|
1213
1221
|
const readme = fs.readFileSync(`./README.md`, 'utf8').split(readmeSplit);
|
|
1214
1222
|
fs.writeFileSync(
|
|
1215
1223
|
'./README.md',
|
|
1216
|
-
|
|
1224
|
+
(
|
|
1225
|
+
readme[0] +
|
|
1217
1226
|
readmeSplit +
|
|
1218
1227
|
`
|
|
1219
1228
|
|
|
@@ -1223,7 +1232,8 @@ const buildCliDoc = (program) => {
|
|
|
1223
1232
|
|
|
1224
1233
|
<a target="_top" href="https://github.com/underpostnet/pwa-microservices-template/blob/master/cli.md">See complete CLI Docs here.</a>
|
|
1225
1234
|
|
|
1226
|
-
|
|
1235
|
+
`
|
|
1236
|
+
).replaceAll(oldVersion, newVersion),
|
|
1227
1237
|
'utf8',
|
|
1228
1238
|
);
|
|
1229
1239
|
};
|
package/src/server/process.js
CHANGED
|
@@ -10,8 +10,6 @@ dotenv.config();
|
|
|
10
10
|
|
|
11
11
|
const logger = loggerFactory(import.meta);
|
|
12
12
|
|
|
13
|
-
// process.exit();
|
|
14
|
-
|
|
15
13
|
const getRootDirectory = () => process.cwd().replace(/\\/g, '/');
|
|
16
14
|
|
|
17
15
|
const ProcessController = {
|
|
@@ -63,27 +61,26 @@ const shellCd = (cd, options = { disableLog: false }) => {
|
|
|
63
61
|
return shell.cd(cd);
|
|
64
62
|
};
|
|
65
63
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
64
|
+
const openTerminal = (cmd, options = { single: false }) => {
|
|
65
|
+
if (options.single === true) {
|
|
66
|
+
shellExec(`setsid gnome-terminal -- bash -ic "${cmd}; exec bash" >/dev/null 2>&1 &`);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
shellExec(`gnome-terminal -- bash -c "${cmd}; exec bash" & disown`, {
|
|
70
|
+
async: true,
|
|
71
|
+
stdout: true,
|
|
72
|
+
});
|
|
73
|
+
};
|
|
77
74
|
|
|
78
|
-
|
|
75
|
+
const daemonProcess = (cmd) => `exec bash -c '${cmd}; exec tail -f /dev/null'`;
|
|
79
76
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
77
|
+
// list all terminals: pgrep gnome-terminal
|
|
78
|
+
// list last terminal: pgrep -n gnome-terminal
|
|
79
|
+
const getTerminalPid = () => JSON.parse(shellExec(`pgrep -n gnome-terminal`, { stdout: true, silent: true }));
|
|
83
80
|
|
|
81
|
+
function pbcopy(data) {
|
|
84
82
|
clipboard.writeSync(data || '🦄');
|
|
85
|
-
|
|
86
83
|
logger.info(`copied to clipboard`, clipboard.readSync());
|
|
87
84
|
}
|
|
88
85
|
|
|
89
|
-
export { ProcessController, getRootDirectory, shellExec, shellCd, pbcopy };
|
|
86
|
+
export { ProcessController, getRootDirectory, shellExec, shellCd, pbcopy, openTerminal, getTerminalPid, daemonProcess };
|
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
|
|