cyberia 3.1.3 → 3.2.5
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.example +0 -2
- package/.github/workflows/engine-cyberia.cd.yml +10 -8
- package/.github/workflows/engine-cyberia.ci.yml +12 -29
- package/.github/workflows/ghpkg.ci.yml +4 -4
- package/.github/workflows/npmpkg.ci.yml +28 -11
- package/.github/workflows/publish.ci.yml +21 -2
- package/.github/workflows/pwa-microservices-template-page.cd.yml +4 -5
- package/.github/workflows/pwa-microservices-template-test.ci.yml +3 -3
- package/.github/workflows/release.cd.yml +13 -8
- package/CHANGELOG.md +433 -1
- package/CLI-HELP.md +57 -7
- package/Dockerfile +4 -2
- package/README.md +347 -22
- package/bin/build.js +5 -2
- package/bin/cyberia.js +1789 -112
- package/bin/deploy.js +177 -124
- package/bin/file.js +3 -0
- package/bin/index.js +1789 -112
- package/conf.js +64 -8
- package/deployment.yaml +92 -20
- package/hardhat/hardhat.config.js +13 -13
- package/hardhat/ignition/modules/ObjectLayerToken.js +1 -1
- package/hardhat/package-lock.json +2554 -5859
- package/hardhat/package.json +13 -22
- package/hardhat/scripts/deployObjectLayerToken.js +1 -1
- package/hardhat/test/ObjectLayerToken.js +4 -2
- package/hardhat/types/ethers-contracts/ObjectLayerToken.ts +690 -0
- package/hardhat/types/ethers-contracts/common.ts +92 -0
- package/hardhat/types/ethers-contracts/factories/ObjectLayerToken__factory.ts +1055 -0
- package/hardhat/types/ethers-contracts/factories/index.ts +4 -0
- package/hardhat/types/ethers-contracts/hardhat.d.ts +47 -0
- package/hardhat/types/ethers-contracts/index.ts +6 -0
- package/jsdoc.dd-cyberia.json +64 -55
- package/jsdoc.json +64 -55
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +5 -4
- package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +5 -4
- package/manifests/deployment/dd-cyberia-development/deployment.yaml +92 -20
- package/manifests/deployment/dd-cyberia-development/proxy.yaml +54 -18
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-test-development/deployment.yaml +88 -74
- package/manifests/deployment/dd-test-development/proxy.yaml +13 -4
- package/manifests/deployment/playwright/deployment.yaml +1 -1
- package/nodemon.json +1 -1
- package/package.json +22 -16
- package/proxy.yaml +54 -18
- package/scripts/rhel-grpc-setup.sh +56 -0
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.controller.js +44 -0
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.model.js +16 -0
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +5 -0
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +80 -7
- package/src/api/cyberia-dialogue/cyberia-dialogue.controller.js +93 -0
- package/src/api/cyberia-dialogue/cyberia-dialogue.model.js +36 -0
- package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +29 -0
- package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +51 -0
- package/src/api/cyberia-entity/cyberia-entity.controller.js +74 -0
- package/src/api/cyberia-entity/cyberia-entity.model.js +24 -0
- package/src/api/cyberia-entity/cyberia-entity.router.js +27 -0
- package/src/api/cyberia-entity/cyberia-entity.service.js +42 -0
- package/src/api/cyberia-instance/cyberia-fallback-world.js +368 -0
- package/src/api/cyberia-instance/cyberia-instance.controller.js +92 -0
- package/src/api/cyberia-instance/cyberia-instance.model.js +84 -0
- package/src/api/cyberia-instance/cyberia-instance.router.js +63 -0
- package/src/api/cyberia-instance/cyberia-instance.service.js +191 -0
- package/src/api/cyberia-instance/cyberia-portal-connector.js +486 -0
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.controller.js +74 -0
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.defaults.js +413 -0
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +228 -0
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +27 -0
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +42 -0
- package/src/api/cyberia-map/cyberia-map.controller.js +79 -0
- package/src/api/cyberia-map/cyberia-map.model.js +30 -0
- package/src/api/cyberia-map/cyberia-map.router.js +40 -0
- package/src/api/cyberia-map/cyberia-map.service.js +74 -0
- package/src/api/file/file.ref.json +18 -0
- package/src/api/ipfs/ipfs.controller.js +4 -25
- package/src/api/ipfs/ipfs.model.js +43 -34
- package/src/api/ipfs/ipfs.router.js +8 -13
- package/src/api/ipfs/ipfs.service.js +54 -102
- package/src/api/object-layer/README.md +347 -22
- package/src/api/object-layer/object-layer.router.js +30 -0
- package/src/api/object-layer/object-layer.service.js +114 -31
- package/src/api/user/user.service.js +8 -7
- package/src/cli/cluster.js +7 -7
- package/src/cli/db.js +710 -827
- package/src/cli/deploy.js +151 -93
- package/src/cli/env.js +29 -0
- package/src/cli/fs.js +5 -2
- package/src/cli/index.js +48 -2
- package/src/cli/kubectl.js +211 -0
- package/src/cli/release.js +284 -0
- package/src/cli/repository.js +438 -75
- package/src/cli/run.js +195 -35
- package/src/cli/secrets.js +73 -0
- package/src/cli/test.js +3 -3
- package/src/client/Cryptokoyn.index.js +3 -4
- package/src/client/CyberiaPortal.index.js +3 -4
- package/src/client/Default.index.js +3 -4
- package/src/client/Itemledger.index.js +3 -4
- package/src/client/Underpost.index.js +3 -4
- package/src/client/components/core/AppStore.js +69 -0
- package/src/client/components/core/CalendarCore.js +2 -2
- package/src/client/components/core/DropDown.js +137 -17
- package/src/client/components/core/Keyboard.js +2 -2
- package/src/client/components/core/LogIn.js +2 -2
- package/src/client/components/core/LogOut.js +2 -2
- package/src/client/components/core/Modal.js +0 -1
- package/src/client/components/core/Panel.js +0 -1
- package/src/client/components/core/PanelForm.js +19 -19
- package/src/client/components/core/SocketIo.js +82 -29
- package/src/client/components/core/SocketIoHandler.js +75 -0
- package/src/client/components/core/Stream.js +143 -95
- package/src/client/components/core/Webhook.js +40 -7
- package/src/client/components/cryptokoyn/AppStoreCryptokoyn.js +5 -0
- package/src/client/components/cryptokoyn/LogInCryptokoyn.js +3 -3
- package/src/client/components/cryptokoyn/LogOutCryptokoyn.js +2 -2
- package/src/client/components/cryptokoyn/MenuCryptokoyn.js +3 -3
- package/src/client/components/cryptokoyn/SocketIoCryptokoyn.js +3 -51
- package/src/client/components/cyberia/InstanceEngineCyberia.js +700 -0
- package/src/client/components/cyberia/MapEngineCyberia.js +1359 -2
- package/src/client/components/cyberia/ObjectLayerEngineModal.js +17 -6
- package/src/client/components/cyberia/ObjectLayerEngineViewer.js +92 -54
- package/src/client/components/cyberia-portal/AppStoreCyberiaPortal.js +5 -0
- package/src/client/components/cyberia-portal/CommonCyberiaPortal.js +216 -30
- package/src/client/components/cyberia-portal/LogInCyberiaPortal.js +3 -3
- package/src/client/components/cyberia-portal/LogOutCyberiaPortal.js +2 -2
- package/src/client/components/cyberia-portal/MenuCyberiaPortal.js +40 -7
- package/src/client/components/cyberia-portal/RoutesCyberiaPortal.js +4 -0
- package/src/client/components/cyberia-portal/SocketIoCyberiaPortal.js +3 -49
- package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +4 -0
- package/src/client/components/default/AppStoreDefault.js +5 -0
- package/src/client/components/default/LogInDefault.js +3 -3
- package/src/client/components/default/LogOutDefault.js +2 -2
- package/src/client/components/default/MenuDefault.js +5 -5
- package/src/client/components/default/SocketIoDefault.js +3 -51
- package/src/client/components/itemledger/AppStoreItemledger.js +5 -0
- package/src/client/components/itemledger/LogInItemledger.js +3 -3
- package/src/client/components/itemledger/LogOutItemledger.js +2 -2
- package/src/client/components/itemledger/MenuItemledger.js +3 -3
- package/src/client/components/itemledger/SocketIoItemledger.js +3 -51
- package/src/client/components/underpost/AppStoreUnderpost.js +5 -0
- package/src/client/components/underpost/LogInUnderpost.js +3 -3
- package/src/client/components/underpost/LogOutUnderpost.js +2 -2
- package/src/client/components/underpost/MenuUnderpost.js +5 -5
- package/src/client/components/underpost/SocketIoUnderpost.js +3 -51
- package/src/client/services/core/core.service.js +20 -8
- package/src/client/services/cyberia-dialogue/cyberia-dialogue.service.js +105 -0
- package/src/client/services/cyberia-entity/cyberia-entity.management.js +57 -0
- package/src/client/services/cyberia-entity/cyberia-entity.service.js +105 -0
- package/src/client/services/cyberia-instance/cyberia-instance.management.js +194 -0
- package/src/client/services/cyberia-instance/cyberia-instance.service.js +122 -0
- package/src/client/services/cyberia-instance-conf/cyberia-instance-conf.service.js +105 -0
- package/src/client/services/cyberia-map/cyberia-map.management.js +193 -0
- package/src/client/services/cyberia-map/cyberia-map.service.js +126 -0
- package/src/client/services/instance/instance.management.js +2 -2
- package/src/client/services/ipfs/ipfs.service.js +3 -23
- package/src/client/services/object-layer/object-layer.management.js +3 -3
- package/src/client/services/object-layer/object-layer.service.js +21 -0
- package/src/client/services/user/user.management.js +2 -2
- package/src/client/ssr/pages/CyberiaServerMetrics.js +1 -1
- package/src/grpc/cyberia/OFF_CHAIN_ECONOMY.md +305 -0
- package/src/grpc/cyberia/README.md +326 -0
- package/src/grpc/cyberia/grpc-server.js +530 -0
- package/src/index.js +24 -1
- package/src/runtime/express/Dockerfile +4 -0
- package/src/runtime/express/Express.js +18 -1
- package/src/runtime/lampp/Dockerfile +13 -2
- package/src/runtime/lampp/Lampp.js +27 -4
- package/src/runtime/wp/Dockerfile +68 -0
- package/src/runtime/wp/Wp.js +639 -0
- package/src/server/auth.js +24 -1
- package/src/server/backup.js +37 -9
- package/src/server/client-build-docs.js +9 -2
- package/src/server/client-build.js +31 -31
- package/src/server/client-formatted.js +109 -57
- package/src/server/conf.js +24 -9
- package/src/server/cron.js +25 -23
- package/src/server/dns.js +2 -1
- package/src/server/ipfs-client.js +24 -1
- package/src/server/object-layer.js +149 -108
- package/src/server/peer.js +8 -0
- package/src/server/runtime.js +25 -1
- package/src/server/semantic-layer-generator-floor.js +359 -0
- package/src/server/semantic-layer-generator-skin.js +1294 -0
- package/src/server/semantic-layer-generator.js +116 -555
- package/src/server/start.js +2 -2
- package/src/ws/IoInterface.js +1 -10
- package/src/ws/IoServer.js +14 -33
- package/src/ws/core/channels/core.ws.chat.js +65 -20
- package/src/ws/core/channels/core.ws.mailer.js +113 -32
- package/src/ws/core/channels/core.ws.stream.js +90 -31
- package/src/ws/core/core.ws.connection.js +12 -33
- package/src/ws/core/core.ws.emit.js +10 -26
- package/src/ws/core/core.ws.server.js +25 -58
- package/src/ws/default/channels/default.ws.main.js +53 -12
- package/src/ws/default/default.ws.connection.js +26 -13
- package/src/ws/default/default.ws.server.js +30 -12
- package/src/client/components/cryptokoyn/CommonCryptokoyn.js +0 -29
- package/src/client/components/cryptokoyn/ElementsCryptokoyn.js +0 -38
- package/src/client/components/cyberia-portal/ElementsCyberiaPortal.js +0 -38
- package/src/client/components/default/ElementsDefault.js +0 -38
- package/src/client/components/itemledger/CommonItemledger.js +0 -29
- package/src/client/components/itemledger/ElementsItemledger.js +0 -38
- package/src/client/components/underpost/CommonUnderpost.js +0 -29
- package/src/client/components/underpost/ElementsUnderpost.js +0 -38
- package/src/ws/core/management/core.ws.chat.js +0 -8
- package/src/ws/core/management/core.ws.mailer.js +0 -16
- package/src/ws/core/management/core.ws.stream.js +0 -8
- package/src/ws/default/management/default.ws.main.js +0 -8
|
@@ -0,0 +1,530 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* gRPC server for the Cyberia Engine data pipeline.
|
|
3
|
+
*
|
|
4
|
+
* Runs alongside Express on a separate port (default 50051).
|
|
5
|
+
* Provides read-only RPCs for the Go game server to fetch
|
|
6
|
+
* ObjectLayers, Instances, and Maps from MongoDB.
|
|
7
|
+
*
|
|
8
|
+
* @module src/grpc/cyberia/grpc-server.js
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import * as grpc from '@grpc/grpc-js';
|
|
12
|
+
import * as protoLoader from '@grpc/proto-loader';
|
|
13
|
+
import path from 'path';
|
|
14
|
+
import { fileURLToPath } from 'url';
|
|
15
|
+
import { DataBaseProvider } from '../../db/DataBaseProvider.js';
|
|
16
|
+
import { loggerFactory } from '../../server/logger.js';
|
|
17
|
+
import {
|
|
18
|
+
CYBERIA_INSTANCE_CONF_DEFAULTS as FALLBACK_CONFIG_DEFAULTS,
|
|
19
|
+
ENTITY_TYPE_DEFAULTS,
|
|
20
|
+
STATUS_ICONS,
|
|
21
|
+
} from '../../api/cyberia-instance-conf/cyberia-instance-conf.defaults.js';
|
|
22
|
+
import { generateFallbackWorld } from '../../api/cyberia-instance/cyberia-fallback-world.js';
|
|
23
|
+
|
|
24
|
+
const logger = loggerFactory(import.meta);
|
|
25
|
+
|
|
26
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
27
|
+
const __dirname = path.dirname(__filename);
|
|
28
|
+
|
|
29
|
+
const PROTO_PATH = path.resolve(__dirname, '../../../cyberia-server/proto/cyberia.proto');
|
|
30
|
+
|
|
31
|
+
const packageDefinition = protoLoader.loadSync(PROTO_PATH, {
|
|
32
|
+
keepCase: false,
|
|
33
|
+
longs: Number,
|
|
34
|
+
enums: String,
|
|
35
|
+
defaults: true,
|
|
36
|
+
oneofs: true,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const proto = grpc.loadPackageDefinition(packageDefinition).cyberia;
|
|
40
|
+
|
|
41
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
42
|
+
// Helpers
|
|
43
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
44
|
+
|
|
45
|
+
function getModels(dbKey) {
|
|
46
|
+
const bucket = DataBaseProvider.instance[dbKey];
|
|
47
|
+
if (!bucket || !bucket.mongoose || !bucket.mongoose.models) {
|
|
48
|
+
throw new Error(`DataBaseProvider not loaded for key "${dbKey}"`);
|
|
49
|
+
}
|
|
50
|
+
return bucket.mongoose.models;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ── Mongoose doc → protobuf message converters ───────────────────
|
|
54
|
+
|
|
55
|
+
function toObjectLayerMsg(doc) {
|
|
56
|
+
const d = doc.data || {};
|
|
57
|
+
const item = d.item || {};
|
|
58
|
+
const stats = d.stats || {};
|
|
59
|
+
const ledger = d.ledger || {};
|
|
60
|
+
const render = d.render || {};
|
|
61
|
+
const rf = doc.objectLayerRenderFramesId;
|
|
62
|
+
let frameDuration = 250;
|
|
63
|
+
let isStateless = false;
|
|
64
|
+
if (rf && typeof rf === 'object') {
|
|
65
|
+
if (rf.frame_duration != null) frameDuration = rf.frame_duration;
|
|
66
|
+
if (rf.is_stateless != null) isStateless = rf.is_stateless;
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
mongoId: String(doc._id),
|
|
70
|
+
stats: {
|
|
71
|
+
effect: stats.effect || 0,
|
|
72
|
+
resistance: stats.resistance || 0,
|
|
73
|
+
agility: stats.agility || 0,
|
|
74
|
+
range: stats.range || 0,
|
|
75
|
+
intelligence: stats.intelligence || 0,
|
|
76
|
+
utility: stats.utility || 0,
|
|
77
|
+
},
|
|
78
|
+
item: {
|
|
79
|
+
id: item.id || '',
|
|
80
|
+
type: item.type || '',
|
|
81
|
+
description: item.description || '',
|
|
82
|
+
activable: !!item.activable,
|
|
83
|
+
},
|
|
84
|
+
ledger: {
|
|
85
|
+
type: ledger.type || 'OFF_CHAIN',
|
|
86
|
+
address: ledger.address || '',
|
|
87
|
+
tokenId: ledger.tokenId || '',
|
|
88
|
+
},
|
|
89
|
+
render: {
|
|
90
|
+
cid: render.cid || '',
|
|
91
|
+
metadataCid: render.metadataCid || '',
|
|
92
|
+
},
|
|
93
|
+
sha256: doc.sha256 || '',
|
|
94
|
+
cid: doc.cid || '',
|
|
95
|
+
frameDuration,
|
|
96
|
+
isStateless,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function toEntityMsg(ent) {
|
|
101
|
+
return {
|
|
102
|
+
entityType: ent.entityType || 'floor',
|
|
103
|
+
initCellX: ent.initCellX || 0,
|
|
104
|
+
initCellY: ent.initCellY || 0,
|
|
105
|
+
dimX: ent.dimX || 1,
|
|
106
|
+
dimY: ent.dimY || 1,
|
|
107
|
+
color: ent.color || '',
|
|
108
|
+
objectLayerItemIds: ent.objectLayerItemIds || [],
|
|
109
|
+
spawnRadius: ent.spawnRadius || 0,
|
|
110
|
+
aggroRange: ent.aggroRange || 0,
|
|
111
|
+
maxLife: ent.maxLife || 0,
|
|
112
|
+
lifeRegen: ent.lifeRegen || 0,
|
|
113
|
+
portalSubtype: ent.portalSubtype || '',
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function toMapMsg(doc) {
|
|
118
|
+
return {
|
|
119
|
+
mongoId: String(doc._id),
|
|
120
|
+
code: doc.code || '',
|
|
121
|
+
name: doc.name || '',
|
|
122
|
+
gridX: doc.gridX || 16,
|
|
123
|
+
gridY: doc.gridY || 16,
|
|
124
|
+
cellWidth: doc.cellWidth || 32,
|
|
125
|
+
cellHeight: doc.cellHeight || 32,
|
|
126
|
+
entities: (doc.entities || []).map(toEntityMsg),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function toInstanceMsg(doc) {
|
|
131
|
+
return {
|
|
132
|
+
mongoId: String(doc._id),
|
|
133
|
+
code: doc.code || '',
|
|
134
|
+
name: doc.name || '',
|
|
135
|
+
description: doc.description || '',
|
|
136
|
+
tags: doc.tags || [],
|
|
137
|
+
mapCodes: doc.cyberiaMapCodes || [],
|
|
138
|
+
portals: (doc.portals || []).map((p) => ({
|
|
139
|
+
sourceMapCode: p.sourceMapCode || '',
|
|
140
|
+
sourceCellX: p.sourceCellX || 0,
|
|
141
|
+
sourceCellY: p.sourceCellY || 0,
|
|
142
|
+
targetMapCode: p.targetMapCode || '',
|
|
143
|
+
targetCellX: p.targetCellX || 0,
|
|
144
|
+
targetCellY: p.targetCellY || 0,
|
|
145
|
+
portalMode: p.portalMode || 'inter-portal',
|
|
146
|
+
})),
|
|
147
|
+
topologyMode: doc.topologyMode || 'hybrid',
|
|
148
|
+
seed: doc.seed || '',
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Converts a CyberiaInstanceConf Mongoose document (or plain object) into
|
|
154
|
+
* a complete InstanceConfig proto message.
|
|
155
|
+
* Any field that is null/undefined in `gc` falls back to FALLBACK_CONFIG_DEFAULTS,
|
|
156
|
+
* so partial DB documents always produce a fully playable config.
|
|
157
|
+
*/
|
|
158
|
+
function toInstanceConfig(gc) {
|
|
159
|
+
const fb = FALLBACK_CONFIG_DEFAULTS;
|
|
160
|
+
if (!gc) return buildFallbackConfig();
|
|
161
|
+
|
|
162
|
+
const colors =
|
|
163
|
+
gc.colors && gc.colors.length > 0
|
|
164
|
+
? gc.colors.map((c) => ({
|
|
165
|
+
key: c.key || '',
|
|
166
|
+
r: c.r ?? 0,
|
|
167
|
+
g: c.g ?? 0,
|
|
168
|
+
b: c.b ?? 0,
|
|
169
|
+
a: c.a ?? 255,
|
|
170
|
+
}))
|
|
171
|
+
: fb.colors.map((c) => ({ ...c }));
|
|
172
|
+
|
|
173
|
+
// Merge entity defaults: use canonical ENTITY_TYPE_DEFAULTS as base, overlay with
|
|
174
|
+
// any instance-specific overrides stored in gc.entityDefaults.
|
|
175
|
+
const gcDefaults = gc.entityDefaults && gc.entityDefaults.length > 0 ? gc.entityDefaults : [];
|
|
176
|
+
const gcDefaultsMap = Object.fromEntries(gcDefaults.map((d) => [d.entityType, d]));
|
|
177
|
+
const entityDefaults = ENTITY_TYPE_DEFAULTS.map((canonical) => {
|
|
178
|
+
const override = gcDefaultsMap[canonical.entityType] ?? {};
|
|
179
|
+
const dols = override.defaultObjectLayers ?? canonical.defaultObjectLayers ?? [];
|
|
180
|
+
return {
|
|
181
|
+
entityType: canonical.entityType,
|
|
182
|
+
liveItemIds: override.liveItemIds ?? canonical.liveItemIds,
|
|
183
|
+
deadItemIds: override.deadItemIds ?? canonical.deadItemIds,
|
|
184
|
+
colorKey: override.colorKey ?? canonical.colorKey,
|
|
185
|
+
defaultObjectLayers: dols.map((ol) => ({
|
|
186
|
+
itemId: ol.itemId || '',
|
|
187
|
+
active: !!ol.active,
|
|
188
|
+
quantity: ol.quantity || 0,
|
|
189
|
+
})),
|
|
190
|
+
};
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
return {
|
|
194
|
+
cellSize: gc.cellSize ?? fb.cellSize,
|
|
195
|
+
fps: gc.fps ?? fb.fps,
|
|
196
|
+
interpolationMs: gc.interpolationMs ?? fb.interpolationMs,
|
|
197
|
+
defaultObjWidth: gc.defaultObjWidth ?? fb.defaultObjWidth,
|
|
198
|
+
defaultObjHeight: gc.defaultObjHeight ?? fb.defaultObjHeight,
|
|
199
|
+
cameraSmoothing: gc.cameraSmoothing ?? fb.cameraSmoothing,
|
|
200
|
+
cameraZoom: gc.cameraZoom ?? fb.cameraZoom,
|
|
201
|
+
defaultWidthScreenFactor: gc.defaultWidthScreenFactor ?? fb.defaultWidthScreenFactor,
|
|
202
|
+
defaultHeightScreenFactor: gc.defaultHeightScreenFactor ?? fb.defaultHeightScreenFactor,
|
|
203
|
+
devUi: gc.devUi ?? fb.devUi,
|
|
204
|
+
colors,
|
|
205
|
+
aoiRadius: gc.aoiRadius ?? fb.aoiRadius,
|
|
206
|
+
portalHoldTimeMs: gc.portalHoldTimeMs ?? fb.portalHoldTimeMs,
|
|
207
|
+
portalSpawnRadius: gc.portalSpawnRadius ?? fb.portalSpawnRadius,
|
|
208
|
+
entityBaseSpeed: gc.entityBaseSpeed ?? fb.entityBaseSpeed,
|
|
209
|
+
entityBaseMaxLife: gc.entityBaseMaxLife ?? fb.entityBaseMaxLife,
|
|
210
|
+
entityBaseActionCooldownMs: gc.entityBaseActionCooldownMs ?? fb.entityBaseActionCooldownMs,
|
|
211
|
+
entityBaseMinActionCooldownMs: gc.entityBaseMinActionCooldownMs ?? fb.entityBaseMinActionCooldownMs,
|
|
212
|
+
botAggroRange: gc.botAggroRange ?? fb.botAggroRange,
|
|
213
|
+
defaultPlayerWidth: gc.defaultPlayerWidth ?? fb.defaultPlayerWidth,
|
|
214
|
+
defaultPlayerHeight: gc.defaultPlayerHeight ?? fb.defaultPlayerHeight,
|
|
215
|
+
playerBaseLifeRegenMin: gc.playerBaseLifeRegenMin ?? fb.playerBaseLifeRegenMin,
|
|
216
|
+
playerBaseLifeRegenMax: gc.playerBaseLifeRegenMax ?? fb.playerBaseLifeRegenMax,
|
|
217
|
+
sumStatsLimit: gc.sumStatsLimit ?? fb.sumStatsLimit,
|
|
218
|
+
maxActiveLayers: gc.maxActiveLayers ?? fb.maxActiveLayers,
|
|
219
|
+
initialLifeFraction: gc.initialLifeFraction ?? fb.initialLifeFraction,
|
|
220
|
+
respawnDurationMs: gc.respawnDurationMs ?? fb.respawnDurationMs,
|
|
221
|
+
collisionLifeLoss: gc.collisionLifeLoss ?? fb.collisionLifeLoss,
|
|
222
|
+
// Economy — Fountain & Sink (nested, mirrors EconomyRules proto message).
|
|
223
|
+
economyRules: {
|
|
224
|
+
botSpawnCoins: gc.economyRules?.botSpawnCoins ?? fb.economyRules.botSpawnCoins,
|
|
225
|
+
playerSpawnCoins: gc.economyRules?.playerSpawnCoins ?? fb.economyRules.playerSpawnCoins,
|
|
226
|
+
coinKillPercentVsBot: gc.economyRules?.coinKillPercentVsBot ?? fb.economyRules.coinKillPercentVsBot,
|
|
227
|
+
coinKillPercentVsPlayer: gc.economyRules?.coinKillPercentVsPlayer ?? fb.economyRules.coinKillPercentVsPlayer,
|
|
228
|
+
coinKillMinAmount: gc.economyRules?.coinKillMinAmount ?? fb.economyRules.coinKillMinAmount,
|
|
229
|
+
respawnCostPercent: gc.economyRules?.respawnCostPercent ?? fb.economyRules.respawnCostPercent,
|
|
230
|
+
portalFee: gc.economyRules?.portalFee ?? fb.economyRules.portalFee,
|
|
231
|
+
craftingFeePercent: gc.economyRules?.craftingFeePercent ?? fb.economyRules.craftingFeePercent,
|
|
232
|
+
},
|
|
233
|
+
lifeRegenChance: gc.lifeRegenChance ?? fb.lifeRegenChance,
|
|
234
|
+
maxChance: gc.maxChance ?? fb.maxChance,
|
|
235
|
+
entityDefaults,
|
|
236
|
+
skillConfig: (gc.skillConfig || []).map((sc) => ({
|
|
237
|
+
triggerItemId: sc.triggerItemId || '',
|
|
238
|
+
logicEventIds: sc.logicEventIds || [],
|
|
239
|
+
})),
|
|
240
|
+
skillRules: {
|
|
241
|
+
projectileSpawnChance: gc.skillRules?.projectileSpawnChance ?? fb.skillRules.projectileSpawnChance,
|
|
242
|
+
projectileLifetimeMs: gc.skillRules?.projectileLifetimeMs ?? fb.skillRules.projectileLifetimeMs,
|
|
243
|
+
projectileWidth: gc.skillRules?.projectileWidth ?? fb.skillRules.projectileWidth,
|
|
244
|
+
projectileHeight: gc.skillRules?.projectileHeight ?? fb.skillRules.projectileHeight,
|
|
245
|
+
projectileSpeedMultiplier: gc.skillRules?.projectileSpeedMultiplier ?? fb.skillRules.projectileSpeedMultiplier,
|
|
246
|
+
doppelgangerSpawnChance: gc.skillRules?.doppelgangerSpawnChance ?? fb.skillRules.doppelgangerSpawnChance,
|
|
247
|
+
doppelgangerLifetimeMs: gc.skillRules?.doppelgangerLifetimeMs ?? fb.skillRules.doppelgangerLifetimeMs,
|
|
248
|
+
doppelgangerSpawnRadius: gc.skillRules?.doppelgangerSpawnRadius ?? fb.skillRules.doppelgangerSpawnRadius,
|
|
249
|
+
doppelgangerInitialLifeFraction:
|
|
250
|
+
gc.skillRules?.doppelgangerInitialLifeFraction ?? fb.skillRules.doppelgangerInitialLifeFraction,
|
|
251
|
+
},
|
|
252
|
+
// Equipment rules — governs activation constraints (nested, mirrors EquipmentRules proto message).
|
|
253
|
+
equipmentRules: {
|
|
254
|
+
activeItemTypes: gc.equipmentRules?.activeItemTypes ?? fb.equipmentRules.activeItemTypes,
|
|
255
|
+
onePerType: gc.equipmentRules?.onePerType ?? fb.equipmentRules.onePerType,
|
|
256
|
+
requireSkin: gc.equipmentRules?.requireSkin ?? fb.equipmentRules.requireSkin,
|
|
257
|
+
},
|
|
258
|
+
// Status icon mapping — u8 ID → icon filename stem + border colour.
|
|
259
|
+
// Border colours come from the frozen STATUS_ICONS constant (canonical
|
|
260
|
+
// source of truth). DB entries may override iconId but borderColor is
|
|
261
|
+
// always canonical — the DB schema defaults are generic grey, not the
|
|
262
|
+
// actual per-status colours.
|
|
263
|
+
statusIcons: STATUS_ICONS.map((canon) => {
|
|
264
|
+
const dbEntry = (gc.statusIcons || []).find((s) => s.id === canon.id);
|
|
265
|
+
const bc = canon.borderColor || {};
|
|
266
|
+
return {
|
|
267
|
+
id: canon.id,
|
|
268
|
+
iconId: (dbEntry && dbEntry.iconId) || canon.iconId || '',
|
|
269
|
+
borderColorR: bc.r ?? 100,
|
|
270
|
+
borderColorG: bc.g ?? 100,
|
|
271
|
+
borderColorB: bc.b ?? 100,
|
|
272
|
+
borderColorA: bc.a ?? 200,
|
|
273
|
+
};
|
|
274
|
+
}),
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Builds a minimal InstanceConfig with playable defaults.
|
|
280
|
+
* Derived from CYBERIA_INSTANCE_CONF_DEFAULTS (shared with the Mongoose model).
|
|
281
|
+
* Used when the requested instance does not exist in the database.
|
|
282
|
+
*/
|
|
283
|
+
function buildFallbackConfig() {
|
|
284
|
+
return JSON.parse(JSON.stringify(FALLBACK_CONFIG_DEFAULTS));
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
288
|
+
// RPC handler factory
|
|
289
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
290
|
+
|
|
291
|
+
function buildHandlers(dbKey) {
|
|
292
|
+
return {
|
|
293
|
+
async ping(_call, callback) {
|
|
294
|
+
callback(null, { serverTimeMs: Date.now() });
|
|
295
|
+
},
|
|
296
|
+
|
|
297
|
+
// Server-streaming: streams all ObjectLayers
|
|
298
|
+
async getObjectLayerBatch(call) {
|
|
299
|
+
try {
|
|
300
|
+
const models = getModels(dbKey);
|
|
301
|
+
const filter = {};
|
|
302
|
+
if (call.request.itemTypeFilter) {
|
|
303
|
+
filter['data.item.type'] = call.request.itemTypeFilter;
|
|
304
|
+
}
|
|
305
|
+
const cursor = models.ObjectLayer.find(filter)
|
|
306
|
+
.populate('objectLayerRenderFramesId', { _id: 1, frame_duration: 1, is_stateless: 1 })
|
|
307
|
+
.lean()
|
|
308
|
+
.cursor();
|
|
309
|
+
for await (const doc of cursor) {
|
|
310
|
+
call.write(toObjectLayerMsg(doc));
|
|
311
|
+
}
|
|
312
|
+
call.end();
|
|
313
|
+
} catch (err) {
|
|
314
|
+
logger.error('getObjectLayerBatch:', err);
|
|
315
|
+
call.destroy(new Error(err.message));
|
|
316
|
+
}
|
|
317
|
+
},
|
|
318
|
+
|
|
319
|
+
async getObjectLayer(call, callback) {
|
|
320
|
+
try {
|
|
321
|
+
const models = getModels(dbKey);
|
|
322
|
+
const doc = await models.ObjectLayer.findOne({ 'data.item.id': call.request.itemId })
|
|
323
|
+
.populate('objectLayerRenderFramesId', { _id: 1, frame_duration: 1, is_stateless: 1 })
|
|
324
|
+
.lean();
|
|
325
|
+
if (!doc)
|
|
326
|
+
return callback({ code: grpc.status.NOT_FOUND, message: `ObjectLayer "${call.request.itemId}" not found` });
|
|
327
|
+
callback(null, toObjectLayerMsg(doc));
|
|
328
|
+
} catch (err) {
|
|
329
|
+
logger.error('getObjectLayer:', err);
|
|
330
|
+
callback({ code: grpc.status.INTERNAL, message: err.message });
|
|
331
|
+
}
|
|
332
|
+
},
|
|
333
|
+
|
|
334
|
+
async getMapData(call, callback) {
|
|
335
|
+
try {
|
|
336
|
+
const models = getModels(dbKey);
|
|
337
|
+
const { mapCode, instanceCode } = call.request;
|
|
338
|
+
const doc = await models.CyberiaMap.findOne({ code: mapCode }).lean();
|
|
339
|
+
if (!doc) return callback({ code: grpc.status.NOT_FOUND, message: `Map "${mapCode}" not found` });
|
|
340
|
+
|
|
341
|
+
// Track which instance is serving each mapCode in real time.
|
|
342
|
+
// instanceCode is provided by the Go server in every GetMapData request.
|
|
343
|
+
if (instanceCode) {
|
|
344
|
+
models.GlobalMapCodeRegistry.findOneAndUpdate(
|
|
345
|
+
{ mapCode },
|
|
346
|
+
{ instanceCode, status: 'active' },
|
|
347
|
+
{ upsert: true, new: true, timestamps: true },
|
|
348
|
+
).catch((err) => logger.warn('getMapData registry update failed:', err.message));
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
callback(null, { map: toMapMsg(doc) });
|
|
352
|
+
} catch (err) {
|
|
353
|
+
logger.error('getMapData:', err);
|
|
354
|
+
callback({ code: grpc.status.INTERNAL, message: err.message });
|
|
355
|
+
}
|
|
356
|
+
},
|
|
357
|
+
|
|
358
|
+
async getFullInstance(call, callback) {
|
|
359
|
+
try {
|
|
360
|
+
const models = getModels(dbKey);
|
|
361
|
+
// Normalise empty instanceCode to the canonical fallback name.
|
|
362
|
+
const instanceCode = call.request.instanceCode || 'default';
|
|
363
|
+
const inst = await models.CyberiaInstance.findOne({ code: instanceCode }).populate('conf').lean();
|
|
364
|
+
|
|
365
|
+
// ── Fallback: instance not found → return a multi-map procedural world ──
|
|
366
|
+
if (!inst) {
|
|
367
|
+
logger.info(`Instance "${instanceCode}" not found — returning fallback world.`);
|
|
368
|
+
const world = generateFallbackWorld();
|
|
369
|
+
const fallbackConf = buildFallbackConfig();
|
|
370
|
+
|
|
371
|
+
// Collect all objectLayerItemIds from the generated maps so the
|
|
372
|
+
// Go server can resolve atlases at startup.
|
|
373
|
+
const fallbackItemIds = new Set();
|
|
374
|
+
for (const m of world.maps) {
|
|
375
|
+
for (const e of m.entities || []) {
|
|
376
|
+
for (const id of e.objectLayerItemIds || []) fallbackItemIds.add(id);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
// Also include system OL items from entity defaults.
|
|
380
|
+
for (const d of fallbackConf.entityDefaults || []) {
|
|
381
|
+
for (const id of d.liveItemIds || []) fallbackItemIds.add(id);
|
|
382
|
+
for (const id of d.deadItemIds || []) fallbackItemIds.add(id);
|
|
383
|
+
for (const ol of d.defaultObjectLayers || []) {
|
|
384
|
+
if (ol.itemId) fallbackItemIds.add(ol.itemId);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const fallbackOlDocs = fallbackItemIds.size
|
|
389
|
+
? await models.ObjectLayer.find({ 'data.item.id': { $in: [...fallbackItemIds] } })
|
|
390
|
+
.populate('objectLayerRenderFramesId', { _id: 1, frame_duration: 1, is_stateless: 1 })
|
|
391
|
+
.lean()
|
|
392
|
+
: [];
|
|
393
|
+
|
|
394
|
+
callback(null, {
|
|
395
|
+
instance: toInstanceMsg({
|
|
396
|
+
_id: '',
|
|
397
|
+
code: 'fallback',
|
|
398
|
+
name: 'Fallback Instance',
|
|
399
|
+
description: 'Auto-generated procedural world (not persisted)',
|
|
400
|
+
tags: ['fallback', 'procedural'],
|
|
401
|
+
cyberiaMapCodes: world.instance.cyberiaMapCodes,
|
|
402
|
+
portals: world.portals,
|
|
403
|
+
topologyMode: 'procedural',
|
|
404
|
+
seed: instanceCode,
|
|
405
|
+
}),
|
|
406
|
+
maps: world.maps.map((m) => ({
|
|
407
|
+
mongoId: '',
|
|
408
|
+
code: m.code,
|
|
409
|
+
name: m.name,
|
|
410
|
+
gridX: m.gridX,
|
|
411
|
+
gridY: m.gridY,
|
|
412
|
+
cellWidth: m.cellWidth,
|
|
413
|
+
cellHeight: m.cellHeight,
|
|
414
|
+
entities: (m.entities || []).map(toEntityMsg),
|
|
415
|
+
})),
|
|
416
|
+
objectLayers: fallbackOlDocs.map(toObjectLayerMsg),
|
|
417
|
+
config: toInstanceConfig(fallbackConf),
|
|
418
|
+
});
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// ── Instance found — load maps + entity OLs + config default OLs ──────
|
|
423
|
+
const conf = inst.conf || {};
|
|
424
|
+
const mapCodes = inst.cyberiaMapCodes || [];
|
|
425
|
+
const mapDocs = mapCodes.length ? await models.CyberiaMap.find({ code: { $in: mapCodes } }).lean() : [];
|
|
426
|
+
|
|
427
|
+
// Collect all item IDs referenced by map entities.
|
|
428
|
+
const itemIds = new Set();
|
|
429
|
+
for (const m of mapDocs) {
|
|
430
|
+
for (const e of m.entities || []) {
|
|
431
|
+
for (const id of e.objectLayerItemIds || []) itemIds.add(id);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// Also include "system" item IDs from the instance config so the
|
|
436
|
+
// Go server has their OL data cached without extra round trips:
|
|
437
|
+
// Include OL item IDs from entityDefaults so the Go server has all
|
|
438
|
+
// default atlas data cached at startup without needing extra round trips.
|
|
439
|
+
for (const d of conf.entityDefaults || []) {
|
|
440
|
+
for (const id of d.liveItemIds || []) itemIds.add(id);
|
|
441
|
+
for (const id of d.deadItemIds || []) itemIds.add(id);
|
|
442
|
+
for (const ol of d.defaultObjectLayers || []) {
|
|
443
|
+
if (ol.itemId) itemIds.add(ol.itemId);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const olDocs = itemIds.size
|
|
448
|
+
? await models.ObjectLayer.find({ 'data.item.id': { $in: [...itemIds] } })
|
|
449
|
+
.populate('objectLayerRenderFramesId', { _id: 1, frame_duration: 1, is_stateless: 1 })
|
|
450
|
+
.lean()
|
|
451
|
+
: [];
|
|
452
|
+
|
|
453
|
+
callback(null, {
|
|
454
|
+
instance: toInstanceMsg(inst),
|
|
455
|
+
maps: mapDocs.map(toMapMsg),
|
|
456
|
+
objectLayers: olDocs.map(toObjectLayerMsg),
|
|
457
|
+
config: toInstanceConfig(conf),
|
|
458
|
+
});
|
|
459
|
+
} catch (err) {
|
|
460
|
+
logger.error('getFullInstance:', err);
|
|
461
|
+
callback({ code: grpc.status.INTERNAL, message: err.message });
|
|
462
|
+
}
|
|
463
|
+
},
|
|
464
|
+
|
|
465
|
+
async getObjectLayerManifest(_call, callback) {
|
|
466
|
+
try {
|
|
467
|
+
const models = getModels(dbKey);
|
|
468
|
+
const docs = await models.ObjectLayer.find({}, { 'data.item.id': 1, sha256: 1 }).lean();
|
|
469
|
+
callback(null, {
|
|
470
|
+
entries: docs.map((d) => ({ itemId: d.data?.item?.id || '', sha256: d.sha256 || '' })),
|
|
471
|
+
});
|
|
472
|
+
} catch (err) {
|
|
473
|
+
logger.error('getObjectLayerManifest:', err);
|
|
474
|
+
callback({ code: grpc.status.INTERNAL, message: err.message });
|
|
475
|
+
}
|
|
476
|
+
},
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
481
|
+
// Server lifecycle
|
|
482
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
483
|
+
|
|
484
|
+
const GrpcServer = {
|
|
485
|
+
_server: null,
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* @param {Object} opts
|
|
489
|
+
* @param {string} opts.host - DataBaseProvider host key
|
|
490
|
+
* @param {string} opts.path - DataBaseProvider path key
|
|
491
|
+
* @param {number} [opts.port=50051]
|
|
492
|
+
*/
|
|
493
|
+
async start({ host, path: dbPath, port = 50051 } = {}) {
|
|
494
|
+
const dbKey = `${host}${dbPath}`;
|
|
495
|
+
const server = new grpc.Server({
|
|
496
|
+
'grpc.max_send_message_length': 64 * 1024 * 1024,
|
|
497
|
+
'grpc.max_receive_message_length': 16 * 1024 * 1024,
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
server.addService(proto.CyberiaDataService.service, buildHandlers(dbKey));
|
|
501
|
+
|
|
502
|
+
// gRPC runs over Kubernetes internal network (ClusterIP) — always insecure
|
|
503
|
+
const creds = grpc.ServerCredentials.createInsecure();
|
|
504
|
+
|
|
505
|
+
return new Promise((resolve, reject) => {
|
|
506
|
+
server.bindAsync(`0.0.0.0:${port}`, creds, (err) => {
|
|
507
|
+
if (err) {
|
|
508
|
+
logger.error('gRPC bind failed:', err);
|
|
509
|
+
return reject(err);
|
|
510
|
+
}
|
|
511
|
+
GrpcServer._server = server;
|
|
512
|
+
logger.info(`gRPC server listening on 0.0.0.0:${port}`);
|
|
513
|
+
resolve(server);
|
|
514
|
+
});
|
|
515
|
+
});
|
|
516
|
+
},
|
|
517
|
+
|
|
518
|
+
async stop() {
|
|
519
|
+
if (!GrpcServer._server) return;
|
|
520
|
+
return new Promise((resolve) => {
|
|
521
|
+
GrpcServer._server.tryShutdown(() => {
|
|
522
|
+
GrpcServer._server = null;
|
|
523
|
+
logger.info('gRPC server stopped');
|
|
524
|
+
resolve();
|
|
525
|
+
});
|
|
526
|
+
});
|
|
527
|
+
},
|
|
528
|
+
};
|
|
529
|
+
|
|
530
|
+
export { GrpcServer };
|
package/src/index.js
CHANGED
|
@@ -10,6 +10,7 @@ import UnderpostKickStart from './cli/kickstart.js';
|
|
|
10
10
|
import UnderpostCluster from './cli/cluster.js';
|
|
11
11
|
import UnderpostDB from './cli/db.js';
|
|
12
12
|
import UnderpostDeploy from './cli/deploy.js';
|
|
13
|
+
import UnderpostKubectl from './cli/kubectl.js';
|
|
13
14
|
import UnderpostRootEnv from './cli/env.js';
|
|
14
15
|
import UnderpostFileStorage from './cli/fs.js';
|
|
15
16
|
import UnderpostIPFS from './cli/ipfs.js';
|
|
@@ -22,6 +23,7 @@ import UnderpostSecret from './cli/secrets.js';
|
|
|
22
23
|
import UnderpostSSH from './cli/ssh.js';
|
|
23
24
|
import UnderpostStatic from './cli/static.js';
|
|
24
25
|
import UnderpostTest from './cli/test.js';
|
|
26
|
+
import UnderpostRelease from './cli/release.js';
|
|
25
27
|
import UnderpostSystemProvisionig from './cli/system.js';
|
|
26
28
|
|
|
27
29
|
import UnderpostDns from './server/dns.js';
|
|
@@ -42,7 +44,7 @@ class Underpost {
|
|
|
42
44
|
* @type {String}
|
|
43
45
|
* @memberof Underpost
|
|
44
46
|
*/
|
|
45
|
-
static version = 'v3.
|
|
47
|
+
static version = 'v3.2.5';
|
|
46
48
|
|
|
47
49
|
/**
|
|
48
50
|
* Required Node.js major version
|
|
@@ -126,6 +128,15 @@ class Underpost {
|
|
|
126
128
|
static get db() {
|
|
127
129
|
return UnderpostDB.API;
|
|
128
130
|
}
|
|
131
|
+
/**
|
|
132
|
+
* Kubectl cli API
|
|
133
|
+
* @static
|
|
134
|
+
* @type {UnderpostKubectl.API}
|
|
135
|
+
* @memberof Underpost
|
|
136
|
+
*/
|
|
137
|
+
static get kubectl() {
|
|
138
|
+
return UnderpostKubectl.API;
|
|
139
|
+
}
|
|
129
140
|
/**
|
|
130
141
|
* Deployment cli API
|
|
131
142
|
* @static
|
|
@@ -280,6 +291,16 @@ class Underpost {
|
|
|
280
291
|
static get tls() {
|
|
281
292
|
return UnderpostTLS.API;
|
|
282
293
|
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Release orchestrator cli API
|
|
297
|
+
* @static
|
|
298
|
+
* @type {UnderpostRelease.API}
|
|
299
|
+
* @memberof Underpost
|
|
300
|
+
*/
|
|
301
|
+
static get release() {
|
|
302
|
+
return UnderpostRelease.API;
|
|
303
|
+
}
|
|
283
304
|
}
|
|
284
305
|
|
|
285
306
|
if (!process.version || !process.version.startsWith(`${Underpost.majorNodejsVersion}.`))
|
|
@@ -300,6 +321,7 @@ export {
|
|
|
300
321
|
UnderpostCluster,
|
|
301
322
|
UnderpostDB,
|
|
302
323
|
UnderpostDeploy,
|
|
324
|
+
UnderpostKubectl,
|
|
303
325
|
UnderpostRootEnv,
|
|
304
326
|
UnderpostFileStorage,
|
|
305
327
|
UnderpostImage,
|
|
@@ -317,6 +339,7 @@ export {
|
|
|
317
339
|
UnderpostBackup,
|
|
318
340
|
UnderpostCron,
|
|
319
341
|
UnderpostStartUp,
|
|
342
|
+
UnderpostRelease,
|
|
320
343
|
UnderpostTLS,
|
|
321
344
|
};
|
|
322
345
|
|
|
@@ -30,6 +30,10 @@ RUN dnf clean all
|
|
|
30
30
|
RUN node --version
|
|
31
31
|
RUN npm --version
|
|
32
32
|
|
|
33
|
+
# Create non-root user for secure container execution (cron jobs, init containers)
|
|
34
|
+
# Deployment containers override to root via securityContext when npm install -g is needed
|
|
35
|
+
RUN useradd -m -u 1000 -s /bin/bash dd
|
|
36
|
+
|
|
33
37
|
# Set working directory
|
|
34
38
|
WORKDIR /home/dd
|
|
35
39
|
|
|
@@ -79,6 +79,7 @@ class ExpressService {
|
|
|
79
79
|
peer,
|
|
80
80
|
valkey,
|
|
81
81
|
apiBaseHost,
|
|
82
|
+
grpc,
|
|
82
83
|
redirectTarget,
|
|
83
84
|
rootHostPath,
|
|
84
85
|
confSSR,
|
|
@@ -135,7 +136,17 @@ class ExpressService {
|
|
|
135
136
|
});
|
|
136
137
|
|
|
137
138
|
// Static file serving
|
|
138
|
-
app.use(
|
|
139
|
+
app.use(
|
|
140
|
+
'/',
|
|
141
|
+
express.static(directory ? directory : `.${rootHostPath}`, {
|
|
142
|
+
setHeaders: (res, filePath) => {
|
|
143
|
+
if (filePath.includes('/assets/')) {
|
|
144
|
+
res.set('Access-Control-Allow-Origin', '*');
|
|
145
|
+
res.set('Cross-Origin-Resource-Policy', 'cross-origin');
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
}),
|
|
149
|
+
);
|
|
139
150
|
|
|
140
151
|
// Handle redirection-only instances
|
|
141
152
|
if (redirect) {
|
|
@@ -198,6 +209,12 @@ class ExpressService {
|
|
|
198
209
|
});
|
|
199
210
|
}
|
|
200
211
|
|
|
212
|
+
// gRPC server
|
|
213
|
+
if (path === '/' && grpc && grpc.module) {
|
|
214
|
+
const { GrpcServer } = await import(`../../grpc/${grpc.module}/grpc-server.js`);
|
|
215
|
+
await GrpcServer.start({ host, path, port: grpc.port || 50051 });
|
|
216
|
+
}
|
|
217
|
+
|
|
201
218
|
// API router loading
|
|
202
219
|
if (apis && apis.length > 0) {
|
|
203
220
|
const authMiddleware = authMiddlewareFactory({ host, path });
|
|
@@ -20,8 +20,8 @@ RUN dnf -y update && \
|
|
|
20
20
|
perl && \
|
|
21
21
|
dnf clean all
|
|
22
22
|
|
|
23
|
-
# --- Download and install XAMPP
|
|
24
|
-
RUN curl -L -o /tmp/xampp-linux-installer.run "https://sourceforge.net/projects/xampp/files/XAMPP%20Linux/
|
|
23
|
+
# --- Download and install XAMPP (PHP 8.2)
|
|
24
|
+
RUN curl -L -o /tmp/xampp-linux-installer.run "https://sourceforge.net/projects/xampp/files/XAMPP%20Linux/8.2.12/xampp-linux-x64-8.2.12-0-installer.run" && \
|
|
25
25
|
chmod +x /tmp/xampp-linux-installer.run && \
|
|
26
26
|
bash -c "/tmp/xampp-linux-installer.run --mode unattended" && \
|
|
27
27
|
ln -sf /opt/lampp/lampp /usr/bin/lampp
|
|
@@ -31,6 +31,13 @@ RUN mkdir -p /opt/lampp/htdocs && \
|
|
|
31
31
|
chown -R root:root /opt/lampp/htdocs && \
|
|
32
32
|
chmod -R a+rX /opt/lampp/htdocs
|
|
33
33
|
|
|
34
|
+
# Add XAMPP binaries and /usr/local/bin to PATH for all shells
|
|
35
|
+
ENV PATH="/opt/lampp/bin:/usr/local/bin:${PATH}"
|
|
36
|
+
RUN echo 'export PATH="/opt/lampp/bin:/usr/local/bin:${PATH}"' > /etc/profile.d/lampp.sh
|
|
37
|
+
|
|
38
|
+
# Provide a no-op sendmail so PHP plugins don't error on mail calls
|
|
39
|
+
RUN printf '#!/bin/sh\ncat > /dev/null\n' > /usr/sbin/sendmail && chmod +x /usr/sbin/sendmail
|
|
40
|
+
|
|
34
41
|
# Install Node.js
|
|
35
42
|
RUN curl -fsSL https://rpm.nodesource.com/setup_24.x | bash -
|
|
36
43
|
RUN dnf install nodejs -y
|
|
@@ -40,6 +47,10 @@ RUN dnf clean all
|
|
|
40
47
|
RUN node --version
|
|
41
48
|
RUN npm --version
|
|
42
49
|
|
|
50
|
+
# Create non-root user for secure container execution (cron jobs, init containers)
|
|
51
|
+
# Deployment containers override to root via securityContext when npm install -g is needed
|
|
52
|
+
RUN useradd -m -u 1000 -s /bin/bash dd
|
|
53
|
+
|
|
43
54
|
# Set working directory
|
|
44
55
|
WORKDIR /home/dd
|
|
45
56
|
|