underpost 3.2.4 → 3.2.8

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 (141) hide show
  1. package/.github/workflows/release.cd.yml +1 -2
  2. package/CHANGELOG.md +268 -1
  3. package/CLI-HELP.md +26 -13
  4. package/Dockerfile +0 -4
  5. package/README.md +3 -3
  6. package/bin/build.js +13 -3
  7. package/bin/deploy.js +570 -1
  8. package/bin/file.js +5 -0
  9. package/conf.js +11 -2
  10. package/jsconfig.json +1 -1
  11. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +2 -3
  12. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +2 -3
  13. package/manifests/deployment/dd-default-development/deployment.yaml +2 -6
  14. package/manifests/deployment/dd-test-development/deployment.yaml +136 -66
  15. package/manifests/deployment/dd-test-development/proxy.yaml +41 -5
  16. package/package.json +20 -11
  17. package/src/api/core/core.controller.js +10 -10
  18. package/src/api/core/core.service.js +10 -10
  19. package/src/api/default/default.controller.js +10 -10
  20. package/src/api/default/default.service.js +10 -10
  21. package/src/api/document/document.controller.js +12 -12
  22. package/src/api/document/document.model.js +10 -16
  23. package/src/api/file/file.controller.js +8 -8
  24. package/src/api/file/file.model.js +10 -10
  25. package/src/api/file/file.service.js +36 -36
  26. package/src/api/test/test.controller.js +8 -8
  27. package/src/api/test/test.service.js +8 -8
  28. package/src/api/user/guest.service.js +99 -0
  29. package/src/api/user/user.controller.js +6 -6
  30. package/src/api/user/user.model.js +8 -13
  31. package/src/api/user/user.service.js +3 -20
  32. package/src/cli/deploy.js +33 -30
  33. package/src/cli/fs.js +62 -5
  34. package/src/cli/image.js +43 -1
  35. package/src/cli/index.js +5 -1
  36. package/src/cli/release.js +58 -2
  37. package/src/cli/repository.js +35 -3
  38. package/src/cli/run.js +304 -38
  39. package/src/cli/ssh.js +1 -1
  40. package/src/cli/static.js +43 -115
  41. package/src/client/Default.index.js +21 -33
  42. package/src/client/components/core/404.js +4 -4
  43. package/src/client/components/core/500.js +4 -4
  44. package/src/client/components/core/Account.js +73 -60
  45. package/src/client/components/core/AgGrid.js +23 -33
  46. package/src/client/components/core/Alert.js +12 -13
  47. package/src/client/components/core/AppStore.js +1 -1
  48. package/src/client/components/core/Auth.js +20 -32
  49. package/src/client/components/core/Badge.js +7 -13
  50. package/src/client/components/core/BtnIcon.js +15 -17
  51. package/src/client/components/core/CalendarCore.js +42 -63
  52. package/src/client/components/core/Chat.js +13 -15
  53. package/src/client/components/core/ClientEvents.js +87 -0
  54. package/src/client/components/core/ColorPaletteElement.js +309 -0
  55. package/src/client/components/core/Content.js +17 -14
  56. package/src/client/components/core/Css.js +15 -71
  57. package/src/client/components/core/CssCore.js +12 -16
  58. package/src/client/components/core/D3Chart.js +4 -4
  59. package/src/client/components/core/Docs.js +60 -59
  60. package/src/client/components/core/DropDown.js +69 -91
  61. package/src/client/components/core/EventBus.js +92 -0
  62. package/src/client/components/core/EventsUI.js +14 -17
  63. package/src/client/components/core/FileExplorer.js +102 -234
  64. package/src/client/components/core/FullScreen.js +47 -75
  65. package/src/client/components/core/Input.js +24 -69
  66. package/src/client/components/core/Keyboard.js +25 -18
  67. package/src/client/components/core/KeyboardAvoidance.js +145 -0
  68. package/src/client/components/core/LoadingAnimation.js +25 -31
  69. package/src/client/components/core/LogIn.js +41 -41
  70. package/src/client/components/core/LogOut.js +23 -14
  71. package/src/client/components/core/Modal.js +397 -176
  72. package/src/client/components/core/NotificationManager.js +14 -18
  73. package/src/client/components/core/Panel.js +54 -50
  74. package/src/client/components/core/PanelForm.js +25 -125
  75. package/src/client/components/core/Polyhedron.js +110 -214
  76. package/src/client/components/core/PublicProfile.js +39 -32
  77. package/src/client/components/core/Recover.js +52 -48
  78. package/src/client/components/core/Responsive.js +88 -32
  79. package/src/client/components/core/RichText.js +9 -18
  80. package/src/client/components/core/Router.js +24 -3
  81. package/src/client/components/core/SearchBox.js +37 -37
  82. package/src/client/components/core/SignUp.js +39 -30
  83. package/src/client/components/core/SocketIo.js +31 -2
  84. package/src/client/components/core/SocketIoHandler.js +6 -6
  85. package/src/client/components/core/ToggleSwitch.js +8 -20
  86. package/src/client/components/core/ToolTip.js +5 -17
  87. package/src/client/components/core/Translate.js +56 -59
  88. package/src/client/components/core/Validator.js +26 -16
  89. package/src/client/components/core/Wallet.js +15 -26
  90. package/src/client/components/core/Worker.js +140 -25
  91. package/src/client/components/core/windowGetDimensions.js +7 -7
  92. package/src/client/components/default/{MenuDefault.js → AppShellDefault.js} +87 -87
  93. package/src/client/components/default/CssDefault.js +12 -12
  94. package/src/client/components/default/LogInDefault.js +6 -4
  95. package/src/client/components/default/LogOutDefault.js +6 -4
  96. package/src/client/components/default/RouterDefault.js +47 -0
  97. package/src/client/components/default/SettingsDefault.js +4 -4
  98. package/src/client/components/default/SignUpDefault.js +6 -4
  99. package/src/client/components/default/TranslateDefault.js +3 -3
  100. package/src/client/services/core/core.service.js +17 -49
  101. package/src/client/services/default/default.management.js +139 -242
  102. package/src/client/services/default/default.service.js +10 -16
  103. package/src/client/services/document/document.service.js +14 -19
  104. package/src/client/services/file/file.service.js +8 -13
  105. package/src/client/services/test/test.service.js +8 -13
  106. package/src/client/services/user/guest.service.js +79 -0
  107. package/src/client/services/user/user.management.js +5 -5
  108. package/src/client/services/user/user.service.js +14 -20
  109. package/src/client/ssr/body/404.js +3 -3
  110. package/src/client/ssr/body/500.js +3 -3
  111. package/src/client/ssr/body/CacheControl.js +5 -2
  112. package/src/client/ssr/body/DefaultSplashScreen.js +19 -12
  113. package/src/client/ssr/mailer/DefaultRecoverEmail.js +19 -20
  114. package/src/client/ssr/mailer/DefaultVerifyEmail.js +15 -16
  115. package/src/client/ssr/offline/Maintenance.js +12 -11
  116. package/src/client/ssr/offline/NoNetworkConnection.js +3 -3
  117. package/src/client/ssr/pages/Test.js +2 -2
  118. package/src/client/sw/core.sw.js +212 -0
  119. package/src/index.js +1 -1
  120. package/src/runtime/express/Dockerfile +4 -4
  121. package/src/runtime/lampp/Dockerfile +8 -7
  122. package/src/runtime/wp/Dockerfile +11 -17
  123. package/src/server/backup.js +1 -2
  124. package/src/server/client-build-docs.js +45 -46
  125. package/src/server/client-build.js +334 -60
  126. package/src/server/client-formatted.js +47 -16
  127. package/src/server/conf.js +29 -13
  128. package/src/server/cron.js +6 -8
  129. package/src/server/dns.js +2 -1
  130. package/src/server/ipfs-client.js +232 -91
  131. package/src/server/process.js +13 -27
  132. package/src/server/start.js +6 -3
  133. package/src/server/valkey.js +134 -235
  134. package/tsconfig.docs.json +15 -0
  135. package/typedoc.json +20 -0
  136. package/jsdoc.json +0 -52
  137. package/src/client/components/core/ColorPalette.js +0 -5267
  138. package/src/client/components/core/JoyStick.js +0 -80
  139. package/src/client/components/default/RoutesDefault.js +0 -49
  140. package/src/client/sw/default.sw.js +0 -127
  141. package/src/client/sw/template.sw.js +0 -84
package/bin/deploy.js CHANGED
@@ -20,7 +20,7 @@ import {
20
20
  } from '../src/server/conf.js';
21
21
  import colors from 'colors';
22
22
  import { program } from '../src/cli/index.js';
23
- import { timer } from '../src/client/components/core/CommonJs.js';
23
+ import { timer, getCapVariableName } from '../src/client/components/core/CommonJs.js';
24
24
  import Underpost from '../src/index.js';
25
25
 
26
26
  colors.enable();
@@ -842,6 +842,413 @@ nvidia/gpu-operator \
842
842
  break;
843
843
  }
844
844
 
845
+ case 'add-server-client': {
846
+ // node bin/deploy add-server-client <clientId> <deployId> <host> <path>
847
+ // Example: node bin/deploy add-server-client cecinasmarcelina dd-core cecinasmarcelina.com /
848
+ // Example: node bin/deploy add-server-client cecinasmarcelina dd-core www.cecinasmarcelina.com /
849
+ const clientId = process.argv[3];
850
+ const deployId = process.argv[4];
851
+ const rawHost = process.argv[5];
852
+ const path = process.argv[6];
853
+
854
+ // Normalize: strip www. prefix to get the base host
855
+ const baseHost = rawHost.startsWith('www.') ? rawHost.slice(4) : rawHost;
856
+ const mainHost = `www.${baseHost}`;
857
+ const redirectHost = baseHost;
858
+
859
+ loadConf('clean');
860
+ loadConf();
861
+ shellExec(`node bin/deploy clone-client default ${clientId} dd-default ${deployId}`);
862
+ // default.net has the main client entry → maps to www.<host> (main)
863
+ shellExec(`node bin/deploy clone-server default.net / ${mainHost} ${path} dd-default ${deployId} ${clientId}`);
864
+ // www.default.net has the redirect/empty entry → maps to <host> (redirect)
865
+ shellExec(`node bin/deploy clone-server www.default.net / ${redirectHost} ${path} dd-default ${deployId}`);
866
+ fs.removeSync(`./engine-private/conf/dd-default`);
867
+ break;
868
+ }
869
+
870
+ case 'clone-client': {
871
+ // node bin/deploy clone-client <fromClientId> <toClientId> <fromDeployId> [toDeployId]
872
+ // Example: node bin/deploy clone-client dogmadual mynewclient dd-core
873
+ // Example: node bin/deploy clone-client dogmadual mynewclient dd-core dd-other
874
+ //
875
+ // Clones a client configuration and its src/ files with a new name.
876
+ // If toDeployId is omitted, it defaults to fromDeployId (same deploy).
877
+ // Steps:
878
+ // - conf.client.json: clones the fromClientId entry as toClientId (renames identifiers)
879
+ // - conf.ssr.json: clones the SSR entry under the new PascalCase name
880
+ // - conf.server.json + dev variants: duplicates paths referencing fromClientId for toClientId
881
+ // - src/client/components/<fromClientId>/ → src/client/components/<toClientId>/ (renames identifiers)
882
+ // - src/client/services/<fromClientId>/ → src/client/services/<toClientId>/ (renames identifiers)
883
+ // - src/client/ssr/head/<From>Scripts.js → <To>Scripts.js
884
+ // - src/client/ssr/body/<From>*.js → <To>*.js (SplashScreen, etc.)
885
+ // - src/client/ssr/mailer/<From>*.js → <To>*.js (VerifyEmail, RecoverEmail, etc.)
886
+ // - src/client/<From>.index.js → <To>.index.js
887
+ // - src/client/public/<fromClientId>/ → src/client/public/<toClientId>/
888
+
889
+ const fromClientId = process.argv[3];
890
+ const toClientId = process.argv[4];
891
+ const fromDeployId = process.argv[5];
892
+ const toDeployId = process.argv[6] || fromDeployId;
893
+
894
+ if (!fromClientId || !toClientId || !fromDeployId) {
895
+ logger.error('Usage: node bin/deploy clone-client <fromClientId> <toClientId> <fromDeployId> [toDeployId]');
896
+ logger.error('Example: node bin/deploy clone-client dogmadual mynewclient dd-core');
897
+ process.exit(1);
898
+ }
899
+
900
+ const fromCapName = getCapVariableName(fromClientId);
901
+ const toCapName = getCapVariableName(toClientId);
902
+
903
+ const confFromFolder = `./engine-private/conf/${fromDeployId}`;
904
+ const confToFolder = `./engine-private/conf/${toDeployId}`;
905
+
906
+ if (!fs.existsSync(confFromFolder)) {
907
+ logger.error(`Source config folder not found: ${confFromFolder}`);
908
+ process.exit(1);
909
+ }
910
+ if (!fs.existsSync(confToFolder)) {
911
+ logger.error(`Target config folder not found: ${confToFolder}`);
912
+ process.exit(1);
913
+ }
914
+
915
+ const formattedSrc = (src) => src.replaceAll(fromCapName, toCapName).replaceAll(fromClientId, toClientId);
916
+
917
+ // 1. Clone conf.client.json entry
918
+ {
919
+ const clientConf = JSON.parse(fs.readFileSync(`${confToFolder}/conf.client.json`, 'utf8'));
920
+ if (clientConf[toClientId]) {
921
+ logger.warn(`conf.client.json: "${toClientId}" already exists, skipping`);
922
+ } else {
923
+ const fromClientConf = JSON.parse(fs.readFileSync(`${confFromFolder}/conf.client.json`, 'utf8'));
924
+ if (!fromClientConf[fromClientId]) {
925
+ logger.error(`conf.client.json: "${fromClientId}" not found in ${confFromFolder}`);
926
+ process.exit(1);
927
+ }
928
+ clientConf[toClientId] = JSON.parse(formattedSrc(JSON.stringify(fromClientConf[fromClientId])));
929
+ fs.writeFileSync(`${confToFolder}/conf.client.json`, JSON.stringify(clientConf, null, 4), 'utf8');
930
+ logger.info(`conf.client.json: cloned "${fromClientId}" → "${toClientId}"`);
931
+ }
932
+ }
933
+
934
+ // 2. Clone conf.ssr.json entry
935
+ {
936
+ const ssrConf = JSON.parse(fs.readFileSync(`${confToFolder}/conf.ssr.json`, 'utf8'));
937
+ if (ssrConf[toCapName]) {
938
+ logger.warn(`conf.ssr.json: "${toCapName}" already exists, skipping`);
939
+ } else {
940
+ const fromSsrConf = JSON.parse(fs.readFileSync(`${confFromFolder}/conf.ssr.json`, 'utf8'));
941
+ if (fromSsrConf[fromCapName]) {
942
+ ssrConf[toCapName] = JSON.parse(formattedSrc(JSON.stringify(fromSsrConf[fromCapName])));
943
+ fs.writeFileSync(`${confToFolder}/conf.ssr.json`, JSON.stringify(ssrConf, null, 4), 'utf8');
944
+ logger.info(`conf.ssr.json: cloned "${fromCapName}" → "${toCapName}"`);
945
+ } else {
946
+ logger.warn(`conf.ssr.json: "${fromCapName}" not found in ${confFromFolder}, skipping`);
947
+ }
948
+ }
949
+ }
950
+
951
+ // 3. Clone server conf entries (conf.server.json + dev variants)
952
+ const cloneServerConfEntries = (filePath, label) => {
953
+ if (!fs.existsSync(filePath)) return;
954
+ const serverConf = JSON.parse(fs.readFileSync(filePath, 'utf8'));
955
+ let count = 0;
956
+ for (const host of Object.keys(serverConf)) {
957
+ for (const path of Object.keys(serverConf[host])) {
958
+ const entry = serverConf[host][path];
959
+ if (entry.client === fromClientId) {
960
+ const newPath = path === '/' ? `/${toClientId}` : path.replace(fromClientId, toClientId);
961
+ if (!serverConf[host][newPath]) {
962
+ serverConf[host][newPath] = JSON.parse(formattedSrc(JSON.stringify(entry)));
963
+ count++;
964
+ }
965
+ }
966
+ }
967
+ }
968
+ if (count > 0) {
969
+ fs.writeFileSync(filePath, JSON.stringify(serverConf, null, 4), 'utf8');
970
+ }
971
+ logger.info(`${label}: cloned ${count} server path(s) for "${toClientId}"`);
972
+ };
973
+
974
+ cloneServerConfEntries(`${confToFolder}/conf.server.json`, 'conf.server.json');
975
+ const devFiles = fs
976
+ .readdirSync(confToFolder)
977
+ .filter((f) => f.startsWith('conf.server.dev.') && f.endsWith('.json'));
978
+ for (const devFile of devFiles) {
979
+ cloneServerConfEntries(`${confToFolder}/${devFile}`, devFile);
980
+ }
981
+
982
+ // 4. Clone src/client/components/<fromClientId>/ → <toClientId>/
983
+ const srcFromFolder = `./src/client/components/${fromClientId}`;
984
+ const srcToFolder = `./src/client/components/${toClientId}`;
985
+
986
+ if (!fs.existsSync(srcFromFolder)) {
987
+ logger.warn(`Source component folder not found: ${srcFromFolder}, skipping src clone`);
988
+ } else if (fs.existsSync(srcToFolder)) {
989
+ logger.warn(`Target component folder already exists: ${srcToFolder}, skipping src clone`);
990
+ } else {
991
+ fs.mkdirSync(srcToFolder, { recursive: true });
992
+ const files = fs.readdirSync(srcFromFolder, { recursive: true });
993
+ for (const relativePath of files) {
994
+ const fromFilePath = `${srcFromFolder}/${relativePath}`;
995
+ if (fs.statSync(fromFilePath).isDirectory()) {
996
+ fs.mkdirSync(`${srcToFolder}/${formattedSrc(relativePath)}`, { recursive: true });
997
+ continue;
998
+ }
999
+ const toFileName = formattedSrc(relativePath);
1000
+ fs.writeFileSync(`${srcToFolder}/${toFileName}`, formattedSrc(fs.readFileSync(fromFilePath, 'utf8')), 'utf8');
1001
+ }
1002
+ logger.info(`src/client/components: cloned ${srcFromFolder} → ${srcToFolder} (${files.length} files)`);
1003
+ }
1004
+
1005
+ // 5. Clone src/client/services/<fromClientId>/ → <toClientId>/
1006
+ {
1007
+ const svcFromFolder = `./src/client/services/${fromClientId}`;
1008
+ const svcToFolder = `./src/client/services/${toClientId}`;
1009
+ if (!fs.existsSync(svcFromFolder)) {
1010
+ logger.warn(`Source services folder not found: ${svcFromFolder}, skipping`);
1011
+ } else if (fs.existsSync(svcToFolder)) {
1012
+ logger.warn(`Target services folder already exists: ${svcToFolder}, skipping`);
1013
+ } else {
1014
+ fs.mkdirSync(svcToFolder, { recursive: true });
1015
+ const svcFiles = fs.readdirSync(svcFromFolder, { recursive: true });
1016
+ for (const relativePath of svcFiles) {
1017
+ const fromFilePath = `${svcFromFolder}/${relativePath}`;
1018
+ if (fs.statSync(fromFilePath).isDirectory()) {
1019
+ fs.mkdirSync(`${svcToFolder}/${formattedSrc(relativePath)}`, { recursive: true });
1020
+ continue;
1021
+ }
1022
+ const toFileName = formattedSrc(relativePath);
1023
+ fs.writeFileSync(
1024
+ `${svcToFolder}/${toFileName}`,
1025
+ formattedSrc(fs.readFileSync(fromFilePath, 'utf8')),
1026
+ 'utf8',
1027
+ );
1028
+ }
1029
+ logger.info(`src/client/services: cloned ${svcFromFolder} → ${svcToFolder} (${svcFiles.length} files)`);
1030
+ }
1031
+ }
1032
+
1033
+ // 6. Clone SSR head scripts
1034
+ const fromScriptsPath = `./src/client/ssr/head/${fromCapName}Scripts.js`;
1035
+ const toScriptsPath = `./src/client/ssr/head/${toCapName}Scripts.js`;
1036
+ if (fs.existsSync(fromScriptsPath) && !fs.existsSync(toScriptsPath)) {
1037
+ fs.writeFileSync(toScriptsPath, formattedSrc(fs.readFileSync(fromScriptsPath, 'utf8')), 'utf8');
1038
+ logger.info(`ssr/head: cloned ${fromCapName}Scripts.js → ${toCapName}Scripts.js`);
1039
+ } else if (fs.existsSync(toScriptsPath)) {
1040
+ logger.warn(`ssr/head: ${toCapName}Scripts.js already exists, skipping`);
1041
+ }
1042
+
1043
+ // 7. Clone SSR body files
1044
+ {
1045
+ const ssrConf = JSON.parse(fs.readFileSync(`${confToFolder}/conf.ssr.json`, 'utf8'));
1046
+ const toSsr = ssrConf[toCapName];
1047
+ if (toSsr && Array.isArray(toSsr.body)) {
1048
+ for (const bodyName of toSsr.body) {
1049
+ if (!bodyName.startsWith(toCapName)) continue;
1050
+ const fromBodyName = bodyName.replace(toCapName, fromCapName);
1051
+ const fromBodyPath = `./src/client/ssr/body/${fromBodyName}.js`;
1052
+ const toBodyPath = `./src/client/ssr/body/${bodyName}.js`;
1053
+ if (fs.existsSync(fromBodyPath) && !fs.existsSync(toBodyPath)) {
1054
+ fs.writeFileSync(toBodyPath, formattedSrc(fs.readFileSync(fromBodyPath, 'utf8')), 'utf8');
1055
+ logger.info(`ssr/body: cloned ${fromBodyName}.js → ${bodyName}.js`);
1056
+ } else if (fs.existsSync(toBodyPath)) {
1057
+ logger.warn(`ssr/body: ${bodyName}.js already exists, skipping`);
1058
+ }
1059
+ }
1060
+ }
1061
+ }
1062
+
1063
+ // 8. Clone SSR mailer files
1064
+ {
1065
+ const ssrConf = JSON.parse(fs.readFileSync(`${confToFolder}/conf.ssr.json`, 'utf8'));
1066
+ const toSsr = ssrConf[toCapName];
1067
+ if (toSsr && toSsr.mailer && typeof toSsr.mailer === 'object') {
1068
+ for (const mailerName of Object.values(toSsr.mailer)) {
1069
+ if (!mailerName.startsWith(toCapName)) continue;
1070
+ const fromMailerName = mailerName.replace(toCapName, fromCapName);
1071
+ const fromMailerPath = `./src/client/ssr/mailer/${fromMailerName}.js`;
1072
+ const toMailerPath = `./src/client/ssr/mailer/${mailerName}.js`;
1073
+ if (fs.existsSync(fromMailerPath) && !fs.existsSync(toMailerPath)) {
1074
+ fs.writeFileSync(toMailerPath, formattedSrc(fs.readFileSync(fromMailerPath, 'utf8')), 'utf8');
1075
+ logger.info(`ssr/mailer: cloned ${fromMailerName}.js → ${mailerName}.js`);
1076
+ } else if (fs.existsSync(toMailerPath)) {
1077
+ logger.warn(`ssr/mailer: ${mailerName}.js already exists, skipping`);
1078
+ }
1079
+ }
1080
+ }
1081
+ }
1082
+
1083
+ // 9. Clone client index
1084
+ const fromIndexPath = `./src/client/${fromCapName}.index.js`;
1085
+ const toIndexPath = `./src/client/${toCapName}.index.js`;
1086
+ if (fs.existsSync(fromIndexPath) && !fs.existsSync(toIndexPath)) {
1087
+ fs.writeFileSync(toIndexPath, formattedSrc(fs.readFileSync(fromIndexPath, 'utf8')), 'utf8');
1088
+ logger.info(`client: cloned ${fromCapName}.index.js → ${toCapName}.index.js`);
1089
+ } else if (fs.existsSync(toIndexPath)) {
1090
+ logger.warn(`client: ${toCapName}.index.js already exists, skipping`);
1091
+ }
1092
+
1093
+ // 10. Clone public assets
1094
+ const fromPublicPath = `./src/client/public/${fromClientId}`;
1095
+ const toPublicPath = `./src/client/public/${toClientId}`;
1096
+ if (fs.existsSync(fromPublicPath) && !fs.existsSync(toPublicPath)) {
1097
+ fs.copySync(fromPublicPath, toPublicPath);
1098
+ logger.info(`public: cloned ${fromPublicPath} → ${toPublicPath}`);
1099
+ } else if (fs.existsSync(toPublicPath)) {
1100
+ logger.warn(`public: ${toPublicPath} already exists, skipping`);
1101
+ }
1102
+
1103
+ // 11. Rebuild default conf
1104
+ shellExec(`node bin new --default-conf --deploy-id ${toDeployId}`);
1105
+ logger.info(`Rebuilt default conf for ${toDeployId}`);
1106
+
1107
+ break;
1108
+ }
1109
+
1110
+ case 'clone-server': {
1111
+ // node bin/deploy clone-server <fromHost> <fromPath> <toHost> <toPath> <fromDeployId> <toDeployId>
1112
+ // Example: node bin/deploy clone-server www.dogmadual.com / www.newsite.com / dd-core dd-other
1113
+ // Example: node bin/deploy clone-server www.nexodev.org / www.newdomain.org /app dd-core dd-core
1114
+ //
1115
+ // Clones a specific server host/path entry from one deploy's conf to another,
1116
+ // optionally renaming the host and path in the target.
1117
+ // - conf.server.json: copies the fromHost/fromPath entry to toHost/toPath
1118
+ // - conf.server.dev.*.json: same treatment for all dev variants
1119
+
1120
+ const fromHost = process.argv[3];
1121
+ const fromPath = process.argv[4];
1122
+ const toHost = process.argv[5];
1123
+ const toPath = process.argv[6];
1124
+ const fromDeployId = process.argv[7];
1125
+ const toDeployId = process.argv[8];
1126
+ const overrideClientId = process.argv[9];
1127
+
1128
+ if (!fromHost || !fromPath || !toHost || !toPath || !fromDeployId || !toDeployId) {
1129
+ logger.error(
1130
+ 'Usage: node bin/deploy clone-server <fromHost> <fromPath> <toHost> <toPath> <fromDeployId> <toDeployId> [clientId]',
1131
+ );
1132
+ logger.error(
1133
+ 'Example: node bin/deploy clone-server www.dogmadual.com / www.newsite.com / dd-core dd-other newsite',
1134
+ );
1135
+ process.exit(1);
1136
+ }
1137
+
1138
+ const confFromFolder = `./engine-private/conf/${fromDeployId}`;
1139
+ const confToFolder = `./engine-private/conf/${toDeployId}`;
1140
+
1141
+ if (!fs.existsSync(confFromFolder)) {
1142
+ logger.error(`Source config folder not found: ${confFromFolder}`);
1143
+ process.exit(1);
1144
+ }
1145
+ if (!fs.existsSync(confToFolder)) {
1146
+ logger.error(`Target config folder not found: ${confToFolder}`);
1147
+ process.exit(1);
1148
+ }
1149
+
1150
+ const cloneServerEntry = (fromFilePath, toFilePath, label) => {
1151
+ if (!fs.existsSync(fromFilePath)) {
1152
+ logger.warn(`${label}: source file not found, skipping`);
1153
+ return;
1154
+ }
1155
+ const fromServerConf = JSON.parse(fs.readFileSync(fromFilePath, 'utf8'));
1156
+ if (!fromServerConf[fromHost] || !fromServerConf[fromHost][fromPath]) {
1157
+ logger.warn(`${label}: "${fromHost}" "${fromPath}" not found in source, skipping`);
1158
+ return;
1159
+ }
1160
+
1161
+ const toServerConf = fs.existsSync(toFilePath) ? JSON.parse(fs.readFileSync(toFilePath, 'utf8')) : {};
1162
+
1163
+ if (!toServerConf[toHost]) toServerConf[toHost] = {};
1164
+
1165
+ if (toServerConf[toHost][toPath]) {
1166
+ logger.warn(`${label}: "${toHost}" "${toPath}" already exists in target, skipping`);
1167
+ return;
1168
+ }
1169
+
1170
+ toServerConf[toHost][toPath] = JSON.parse(JSON.stringify(fromServerConf[fromHost][fromPath]));
1171
+ // Override client field if --client= is provided
1172
+ const entry = toServerConf[toHost][toPath];
1173
+ if (overrideClientId && entry.client) {
1174
+ entry.client = overrideClientId;
1175
+ }
1176
+ // Update db.name to use the client-specific env variable
1177
+ if (entry.client && entry.db && entry.db.name) {
1178
+ const upperClientId = entry.client.replaceAll('-', '_').toUpperCase();
1179
+ entry.db.name = `env:DB_NAME_${upperClientId}`;
1180
+ }
1181
+ fs.writeFileSync(toFilePath, JSON.stringify(toServerConf, null, 4), 'utf8');
1182
+ logger.info(
1183
+ `${label}: cloned "${fromHost}" "${fromPath}" → "${toHost}" "${toPath}" (${fromDeployId} → ${toDeployId})`,
1184
+ );
1185
+ };
1186
+
1187
+ // 1. Main conf.server.json
1188
+ cloneServerEntry(`${confFromFolder}/conf.server.json`, `${confToFolder}/conf.server.json`, 'conf.server.json');
1189
+
1190
+ // 2. Dev variants (clone from source dev files)
1191
+ const devFiles = fs
1192
+ .readdirSync(confFromFolder)
1193
+ .filter((f) => f.startsWith('conf.server.dev.') && f.endsWith('.json'));
1194
+ for (const devFile of devFiles) {
1195
+ cloneServerEntry(`${confFromFolder}/${devFile}`, `${confToFolder}/${devFile}`, devFile);
1196
+ }
1197
+
1198
+ // 3. Create individual dev file for the new entry (conf.server.dev.<clientId>.json)
1199
+ {
1200
+ const mainToPath = `${confToFolder}/conf.server.json`;
1201
+ if (fs.existsSync(mainToPath)) {
1202
+ const toServerConf = JSON.parse(fs.readFileSync(mainToPath, 'utf8'));
1203
+ const entry = toServerConf[toHost] && toServerConf[toHost][toPath];
1204
+ if (entry && entry.client) {
1205
+ const devFileName = `conf.server.dev.${entry.client}.json`;
1206
+ const devFilePath = `${confToFolder}/${devFileName}`;
1207
+ if (!fs.existsSync(devFilePath)) {
1208
+ const devConf = { [toHost]: { [toPath]: entry } };
1209
+ fs.writeFileSync(devFilePath, JSON.stringify(devConf, null, 4), 'utf8');
1210
+ logger.info(`${devFileName}: created dev file for "${toHost}" "${toPath}"`);
1211
+ } else {
1212
+ logger.info(`${devFileName}: already exists, skipping creation`);
1213
+ }
1214
+ }
1215
+ }
1216
+ }
1217
+
1218
+ // 4. Add DB_NAME_<UPPER> env variable to target deploy .env.* files
1219
+ {
1220
+ const mainFromPath = `${confFromFolder}/conf.server.json`;
1221
+ if (fs.existsSync(mainFromPath)) {
1222
+ const fromServerConf = JSON.parse(fs.readFileSync(mainFromPath, 'utf8'));
1223
+ const sourceEntry = fromServerConf[fromHost] && fromServerConf[fromHost][fromPath];
1224
+ if (sourceEntry && sourceEntry.client) {
1225
+ const clientId = overrideClientId || sourceEntry.client;
1226
+ const upperClientId = clientId.replaceAll('-', '_').toUpperCase();
1227
+ const envKey = `DB_NAME_${upperClientId}`;
1228
+ const envValue = `${envKey}=${clientId}`;
1229
+ for (const envFile of ['.env.production', '.env.development', '.env.test']) {
1230
+ const envPath = `${confToFolder}/${envFile}`;
1231
+ if (fs.existsSync(envPath)) {
1232
+ const envContent = fs.readFileSync(envPath, 'utf8');
1233
+ if (!envContent.includes(envKey)) {
1234
+ fs.writeFileSync(envPath, envContent.trimEnd() + '\n' + envValue + '\n', 'utf8');
1235
+ logger.info(`${envFile}: added ${envValue}`);
1236
+ } else {
1237
+ logger.info(`${envFile}: ${envKey} already exists, skipping`);
1238
+ }
1239
+ }
1240
+ }
1241
+ }
1242
+ }
1243
+ }
1244
+
1245
+ // 5. Rebuild default conf
1246
+ shellExec(`node bin new --default-conf --deploy-id ${toDeployId}`);
1247
+ logger.info(`Rebuilt default conf for ${toDeployId}`);
1248
+
1249
+ break;
1250
+ }
1251
+
845
1252
  case 'add-api': {
846
1253
  // node bin/deploy add-api <apiId> <deployId> [clientId] [host]
847
1254
  // Example: node bin/deploy add-api cyberia-dialogue dd-cyberia cyberia-portal
@@ -959,6 +1366,168 @@ nvidia/gpu-operator \
959
1366
 
960
1367
  break;
961
1368
  }
1369
+
1370
+ case 'add-component': {
1371
+ // node bin/deploy add-component <componentId> [deployId] [clientId] [submoduleId]
1372
+ // Example: node bin/deploy add-component ColorPaletteElement dd-cyberia cyberia-portal core
1373
+ // Example: node bin/deploy add-component ColorPaletteElement dd-cyberia
1374
+ // Example: node bin/deploy add-component ColorPaletteElement
1375
+ // Adds a component to client conf.main and related replicas.
1376
+ // - if deployId is omitted: iterates deploys in engine-private/deploy/dd.router
1377
+ // - if clientId is omitted: iterates all clients in each conf.client.json
1378
+ // - if submoduleId is omitted: iterates all available components.<submoduleId> arrays per client
1379
+ // - validates src/client/components/<submoduleId>/<componentId>.js exists before adding
1380
+ // Idempotent: skips if the component is already present.
1381
+
1382
+ const componentId = process.argv[3];
1383
+ const deployIdArg = process.argv[4];
1384
+ const clientIdArg = process.argv[5];
1385
+ const submoduleIdArg = process.argv[6];
1386
+
1387
+ if (!componentId) {
1388
+ logger.error('Usage: node bin/deploy add-component <componentId> [deployId] [clientId] [submoduleId]');
1389
+ logger.error('Example: node bin/deploy add-component ColorPaletteElement dd-cyberia cyberia-portal core');
1390
+ logger.error('Example: node bin/deploy add-component ColorPaletteElement');
1391
+ process.exit(1);
1392
+ }
1393
+
1394
+ const deployIds = deployIdArg
1395
+ ? [deployIdArg]
1396
+ : fs
1397
+ .readFileSync(`./engine-private/deploy/dd.router`, 'utf8')
1398
+ .split(',')
1399
+ .map((d) => d.trim())
1400
+ .filter(Boolean);
1401
+
1402
+ const addComponentToClientConf = ({ filePath, label, targetClientId, targetSubmoduleId }) => {
1403
+ if (!fs.existsSync(filePath)) return { added: 0, checked: 0, hasComponentFile: false };
1404
+
1405
+ const confClient = JSON.parse(fs.readFileSync(filePath, 'utf8'));
1406
+ const clientIds = targetClientId ? [targetClientId] : Object.keys(confClient);
1407
+ let added = 0;
1408
+ let checked = 0;
1409
+ let hasComponentFile = false;
1410
+ let changed = false;
1411
+
1412
+ for (const currentClientId of clientIds) {
1413
+ const clientConf = confClient[currentClientId];
1414
+ if (!clientConf) {
1415
+ logger.warn(`${label}: clientId "${currentClientId}" not found`);
1416
+ continue;
1417
+ }
1418
+
1419
+ const components = clientConf.components || {};
1420
+ const submoduleIds = targetSubmoduleId
1421
+ ? [targetSubmoduleId]
1422
+ : Object.keys(components).filter((key) => Array.isArray(components[key]));
1423
+
1424
+ for (const currentSubmoduleId of submoduleIds) {
1425
+ checked++;
1426
+
1427
+ if (!Array.isArray(components[currentSubmoduleId])) {
1428
+ logger.warn(`${label}: clientId "${currentClientId}" has no components.${currentSubmoduleId} array`);
1429
+ continue;
1430
+ }
1431
+
1432
+ const componentPath = `./src/client/components/${currentSubmoduleId}/${componentId}.js`;
1433
+ if (!fs.existsSync(componentPath)) {
1434
+ continue;
1435
+ }
1436
+ hasComponentFile = true;
1437
+
1438
+ if (components[currentSubmoduleId].includes(componentId)) {
1439
+ logger.info(
1440
+ `${label}: "${componentId}" already in "${currentClientId}" components.${currentSubmoduleId}`,
1441
+ );
1442
+ continue;
1443
+ }
1444
+
1445
+ components[currentSubmoduleId].push(componentId);
1446
+ changed = true;
1447
+ added++;
1448
+ logger.info(`${label}: added "${componentId}" to "${currentClientId}" components.${currentSubmoduleId}`);
1449
+ }
1450
+ }
1451
+
1452
+ if (changed) {
1453
+ fs.writeFileSync(filePath, JSON.stringify(confClient, null, 4), 'utf8');
1454
+ }
1455
+
1456
+ return { added, checked, hasComponentFile };
1457
+ };
1458
+
1459
+ let totalAdded = 0;
1460
+ let totalChecked = 0;
1461
+ let hasAnyComponentFile = false;
1462
+ const processedDeployIds = [];
1463
+
1464
+ for (const deployId of deployIds) {
1465
+ const confFolder = `./engine-private/conf/${deployId}`;
1466
+ if (!fs.existsSync(confFolder)) {
1467
+ logger.warn(`Config folder not found: ${confFolder}`);
1468
+ continue;
1469
+ }
1470
+
1471
+ processedDeployIds.push(deployId);
1472
+
1473
+ const mainResult = addComponentToClientConf({
1474
+ filePath: `${confFolder}/conf.client.json`,
1475
+ label: `conf/${deployId}/conf.client.json`,
1476
+ targetClientId: clientIdArg,
1477
+ targetSubmoduleId: submoduleIdArg,
1478
+ });
1479
+ totalAdded += mainResult.added;
1480
+ totalChecked += mainResult.checked;
1481
+ hasAnyComponentFile = hasAnyComponentFile || mainResult.hasComponentFile;
1482
+
1483
+ const replicaBase = './engine-private/replica';
1484
+ if (fs.existsSync(replicaBase)) {
1485
+ const replicaDirs = fs
1486
+ .readdirSync(replicaBase)
1487
+ .filter((d) => d.startsWith(`${deployId}-`) && fs.statSync(`${replicaBase}/${d}`).isDirectory());
1488
+
1489
+ for (const replicaDir of replicaDirs) {
1490
+ const replicaResult = addComponentToClientConf({
1491
+ filePath: `${replicaBase}/${replicaDir}/conf.client.json`,
1492
+ label: `replica/${replicaDir}/conf.client.json`,
1493
+ targetClientId: clientIdArg,
1494
+ targetSubmoduleId: submoduleIdArg,
1495
+ });
1496
+ totalAdded += replicaResult.added;
1497
+ totalChecked += replicaResult.checked;
1498
+ hasAnyComponentFile = hasAnyComponentFile || replicaResult.hasComponentFile;
1499
+ }
1500
+ }
1501
+ }
1502
+
1503
+ if (totalChecked === 0) {
1504
+ logger.error('No eligible target entries found to add the component');
1505
+ process.exit(1);
1506
+ }
1507
+
1508
+ if (!hasAnyComponentFile) {
1509
+ if (submoduleIdArg) {
1510
+ logger.error(`Component file not found: ./src/client/components/${submoduleIdArg}/${componentId}.js`);
1511
+ } else {
1512
+ logger.error(`Component file not found in available submodules for "${componentId}"`);
1513
+ }
1514
+ process.exit(1);
1515
+ }
1516
+
1517
+ for (const deployId of processedDeployIds) {
1518
+ shellExec(`node bin new --default-conf --deploy-id ${deployId}`);
1519
+ logger.info(`Rebuilt default conf for ${deployId}`);
1520
+ }
1521
+
1522
+ logger.info(`add-component summary`, {
1523
+ componentId,
1524
+ deployIds: processedDeployIds,
1525
+ added: totalAdded,
1526
+ checked: totalChecked,
1527
+ });
1528
+
1529
+ break;
1530
+ }
962
1531
  }
963
1532
  } catch (error) {
964
1533
  logger.error(error, error.stack);
package/bin/file.js CHANGED
@@ -101,8 +101,11 @@ try {
101
101
  './src/server/semantic-layer-generator.js',
102
102
  './src/server/semantic-layer-generator-floor.js',
103
103
  './src/server/semantic-layer-generator-skin.js',
104
+ './src/server/semantic-layer-generator-resource.js',
104
105
  './src/server/besu-genesis-generator.js',
105
106
  './src/grpc/cyberia',
107
+ './src/runtime/cyberia-server',
108
+ './src/runtime/cyberia-client',
106
109
  './test/shape-generator.test.js',
107
110
  'bin/cyberia.js',
108
111
  ]) {
@@ -120,6 +123,8 @@ try {
120
123
  `./.github/workflows/gitlab.ci.yml`,
121
124
  `./.github/workflows/publish.ci.yml`,
122
125
  `./.github/workflows/release.cd.yml`,
126
+ `./src/client/services/user/guest.service.js`,
127
+ './src/api/user/guest.service.js',
123
128
  './src/ws/IoInterface.js',
124
129
  './src/ws/IoServer.js',
125
130
  ])
package/conf.js CHANGED
@@ -65,10 +65,12 @@ const DefaultConf = /**/ {
65
65
  'SearchBox',
66
66
  'SocketIoHandler',
67
67
  'AppStore',
68
+ 'ClientEvents',
69
+ 'EventBus',
68
70
  ],
69
71
  default: [
70
- 'MenuDefault',
71
- 'RoutesDefault',
72
+ 'AppShellDefault',
73
+ 'RouterDefault',
72
74
  'AppStoreDefault',
73
75
  'CssDefault',
74
76
  'LogInDefault',
@@ -149,6 +151,13 @@ const DefaultConf = /**/ {
149
151
  import_name: 'socket.io/client-dist/socket.io.esm.min.js',
150
152
  import_name_build: '/dist/socket.io/socket.io.esm.min.js',
151
153
  },
154
+ {
155
+ folder: './node_modules/dexie/dist',
156
+ public_folder: '/dist/dexie',
157
+ import_name: 'dexie',
158
+ import_name_build: '/dist/dexie/dexie.mjs',
159
+ },
160
+
152
161
  { folder: './node_modules/peerjs/dist', public_folder: '/dist/peerjs' },
153
162
  ],
154
163
  services: ['default', 'core', 'user', 'test', 'file', 'document'],
package/jsconfig.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "compilerOptions": {
3
3
  "module": "CommonJS",
4
4
  "target": "ES6",
5
- "moduleResolution": "Node",
5
+ "moduleResolution": "bundler",
6
6
  "checkJs": false
7
7
  },
8
8
  "exclude": ["node_modules", "public", "engine-private", "volume"]
@@ -23,15 +23,14 @@ spec:
23
23
  spec:
24
24
  containers:
25
25
  - name: dd-cron-backup
26
- image: underpost/underpost-engine:v3.2.4
26
+ image: underpost/underpost-engine:v3.2.8
27
27
  command:
28
28
  - /bin/sh
29
29
  - -c
30
30
  - >
31
31
  cd /home/dd/engine &&
32
- node bin run secret &&
33
32
  node bin env dd-cron production &&
34
- node bin cron --git --dev --kubeadm dd-lampp,dd-cyberia,dd-core,dd-test backup
33
+ node bin cron dd-lampp,dd-cyberia,dd-core,dd-test backup --git --kubeadm
35
34
  volumeMounts:
36
35
  - mountPath: /home/dd/engine
37
36
  name: underpost-cron-container-volume