@underpostnet/cyberia 3.3.76 → 3.4.0
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 +1 -0
- package/.github/workflows/coverall.cyberia.ci.yml +17 -3
- package/.github/workflows/cyberia-client.cd.yml +2 -1
- package/.github/workflows/cyberia-server.cd.yml +2 -1
- package/.github/workflows/docker-image.cyberia-client.ci.yml +1 -1
- package/.github/workflows/docker-image.cyberia-client.dev.ci.yml +1 -1
- package/.github/workflows/docker-image.cyberia-server.ci.yml +1 -1
- package/.github/workflows/docker-image.cyberia-server.dev.ci.yml +1 -1
- package/.github/workflows/docker-image.engine-cyberia.ci.yml +1 -1
- package/.github/workflows/docker-image.engine-cyberia.dev.ci.yml +1 -1
- package/.github/workflows/engine-cyberia.ci.yml +7 -1
- package/.github/workflows/ghpkg.ci.yml +19 -2
- package/.github/workflows/gitlab.ci.yml +8 -6
- package/.github/workflows/hardhat.ci.yml +4 -1
- package/.github/workflows/npmpkg.ci.yml +9 -5
- package/.github/workflows/publish.ci.yml +21 -12
- package/.github/workflows/publish.cyberia.ci.yml +25 -14
- package/.github/workflows/pwa-microservices-template-page.cd.yml +4 -2
- package/CHANGELOG.md +110 -262
- package/CLI-HELP.md +29 -2
- package/Dockerfile +1 -1
- package/Dockerfile.dev +1 -1
- package/Dockerfile.test +1 -1
- package/README.md +1 -1
- package/bin/build.js +67 -14
- package/bin/cyberia.js +4 -4
- package/bin/index.js +4 -4
- package/compose.env +1 -0
- package/conf.js +1 -1043
- package/deploy/cyberia-client/deploy.sh +13 -11
- package/deploy/cyberia-server/deploy.sh +13 -11
- package/deploy/dd-cyberia/sync-deploy.sh +7 -28
- package/deploy/lib/config.sh +1 -1
- package/deploy/lib/github-actions-logging.sh +3 -2
- package/deploy/lib/host.sh +1 -1
- package/deploy/pwa-microservices-template/deploy.sh +7 -1
- package/deployment.yaml +1 -1
- package/docker-compose.yml +7 -7
- package/docs/coverage/cyberia/api/cyberia-instance/cyberia-fallback-capture.js.html +1591 -0
- package/docs/coverage/cyberia/api/cyberia-instance/index.html +116 -0
- package/docs/coverage/cyberia/api/cyberia-server-defaults/cyberia-server-defaults.js.html +4138 -0
- package/docs/coverage/cyberia/api/cyberia-server-defaults/index.html +116 -0
- package/docs/coverage/cyberia/api/object-layer/index.html +116 -0
- package/docs/coverage/cyberia/api/object-layer/object-layer.model.js.html +1393 -0
- package/docs/coverage/cyberia/base.css +362 -0
- package/docs/coverage/cyberia/block-navigation.js +82 -0
- package/docs/coverage/cyberia/favicon.png +0 -0
- package/docs/coverage/cyberia/index.html +161 -0
- package/docs/coverage/cyberia/prettify.css +101 -0
- package/docs/coverage/cyberia/prettify.js +937 -0
- package/docs/coverage/cyberia/projects/cyberia/atlas-sprite-sheet-generator.js.html +1315 -0
- package/docs/coverage/cyberia/projects/cyberia/index.html +176 -0
- package/docs/coverage/cyberia/projects/cyberia/instance-backup.js.html +715 -0
- package/docs/coverage/cyberia/projects/cyberia/instance-data.js.html +2182 -0
- package/docs/coverage/cyberia/projects/cyberia/shape-generator.js.html +3265 -0
- package/docs/coverage/cyberia/projects/cyberia/stat-balance.js.html +328 -0
- package/docs/coverage/cyberia/sort-arrow-sprite.png +0 -0
- package/docs/coverage/cyberia/sorter.js +205 -0
- package/hardhat/package-lock.json +6 -6
- package/hardhat/package.json +2 -2
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +1 -1
- package/manifests/cronjobs/dd-cron/dd-cron-vultr.yaml +1 -1
- package/manifests/deployment/dd-cyberia-development/deployment.yaml +1 -1
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/package.json +19 -17
- package/scripts/test-monitor.sh +1 -1
- package/src/api/file/file.router.js +7 -1
- package/src/cli/baremetal.js +1 -1
- package/src/cli/cluster.js +73 -18
- package/src/cli/deploy.js +211 -30
- package/src/cli/docker-compose.js +1 -1
- package/src/cli/index.js +36 -0
- package/src/cli/kubectl.js +4 -3
- package/src/cli/release.js +5 -1
- package/src/cli/repository.js +14 -11
- package/src/cli/run.js +10 -3
- package/src/cli/secrets.js +393 -200
- package/src/cli/socketsecurity.js +260 -0
- package/src/client/components/core/Badge.js +0 -1
- package/src/client/components/core/CssCore.js +11 -1
- package/src/client/components/core/Modal.js +189 -66
- package/src/client/components/core/SearchBox.js +0 -1
- package/src/client/components/core/ToolTip.js +15 -0
- package/src/client/components/cryptokoyn/AppShellCryptokoyn.js +0 -2
- package/src/client/components/cyberia-portal/RouterCyberiaPortal.js +0 -4
- package/src/client/components/default/AppShellDefault.js +1 -4
- package/src/client/components/itemledger/AppShellItemledger.js +0 -2
- package/src/client/components/underpost/AppShellUnderpost.js +12 -0
- package/src/client/components/underpost/CssUnderpost.js +15 -2
- package/src/client/public/cyberia-docs/CYBERIA-CLIENT.md +8 -0
- package/src/client/public/cyberia-docs/CYBERIA.md +1 -1
- package/src/client/public/cyberia-docs/OFF-CHAIN-ECONOMY.md +54 -56
- package/src/client/public/cyberia-docs/ROADMAP.md +1 -1
- package/src/client/public/cyberia-docs/UNDERPOST-PLATFORM.md +30 -29
- package/src/client/public/cyberia-docs/WHITE-PAPER.md +1 -1
- package/src/client/ssr/body/UnderpostDefaultSplashScreen.js +54 -25
- package/src/client/ssr/head/Css.js +12 -10
- package/src/client/ssr/views/Cyberia404.js +9 -13
- package/src/client/ssr/views/NotFound.js +75 -0
- package/src/client-builder/client-bundle.js +2 -2
- package/src/index.js +13 -1
- package/src/runtime/engine-cyberia/Dockerfile +1 -1
- package/src/runtime/engine-cyberia/Dockerfile.dev +1 -1
- package/src/runtime/engine-cyberia/Dockerfile.test +1 -1
- package/src/runtime/engine-cyberia/compose.env +1 -0
- package/src/runtime/engine-cyberia/docker-compose.yml +7 -7
- package/src/server/build/catalog.js +1 -0
- package/src/server/build/execution.js +19 -5
- package/src/server/build/package.js +166 -3
- package/src/server/build/testing.js +2 -1
- package/src/server/network/underpost-gateway.js +113 -14
- package/src/server/runtime/conf.js +57 -7
- package/src/server/runtime/config-scope.js +2 -0
- package/src/server/runtime/process.js +4 -3
- package/src/server/runtime/start.js +11 -2
- package/src/server/security/socketsecurity.js +1735 -0
- package/test/integration/infra/1-security/config-scope.test.js +3 -0
- package/test/integration/infra/1-security/socketsecurity.test.js +1041 -0
- package/test/integration/infra/1-security/sops-secret-store.test.js +201 -71
- package/test/integration/infra/4-ingress/underpost-ingress.test.js +1 -1
- package/test/unit/build-template.test.js +24 -0
- package/test/unit/catalog.test.js +13 -2
- package/test/unit/conf-resolution.test.js +41 -7
- package/test/unit/cyberia/instance-data.test.js +314 -0
- package/test/unit/deploy-legacy-gateway-sweep.test.js +63 -0
- package/test/unit/deploy-log-table.test.js +38 -0
- package/test/unit/execution-profiles.test.js +1 -0
- package/test/unit/gateway-pod-fetch.test.js +95 -0
- package/test/unit/letsencrypt-issuer.test.js +101 -0
- package/test/unit/package.test.js +118 -0
- package/test/unit/release-bump.test.js +3 -1
- package/test/unit/start-options.test.js +12 -0
|
@@ -13,50 +13,48 @@
|
|
|
13
13
|
Cyberia Online uses the **Fountain & Sink** economy — the industry standard for sustainable in-game economies, pioneered by _Ultima Online_ and refined in _EVE Online_ and _World of Warcraft_.
|
|
14
14
|
|
|
15
15
|
```
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
│
|
|
16
|
+
┌──────────────────────┐
|
|
17
|
+
│ FOUNTAINS │
|
|
18
|
+
│ Supply Creation ↑ │
|
|
19
|
+
│ │
|
|
20
|
+
│ • botSpawnCoins │
|
|
21
|
+
│ • playerSpawnCoins │
|
|
22
|
+
└──────────┬───────────┘
|
|
23
|
+
│
|
|
24
24
|
▼
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
│ craftingFeePercent │
|
|
48
|
-
└─────────────────────────────┘
|
|
25
|
+
┌──────────────────────┐
|
|
26
|
+
│ CIRCULATING SUPPLY │
|
|
27
|
+
│ Active Coin Supply │
|
|
28
|
+
└──────────┬───────────┘
|
|
29
|
+
│
|
|
30
|
+
┌────────────┴────────────┐
|
|
31
|
+
│ │
|
|
32
|
+
▼ ▼
|
|
33
|
+
┌─────────────────────┐ ┌─────────────────────┐
|
|
34
|
+
│ TRANSFERS & LOOT │ │ SINKS │
|
|
35
|
+
│ │ │ │
|
|
36
|
+
│ Supply moves ↔ │ │ Supply destroyed ↓│
|
|
37
|
+
│ │ │ │
|
|
38
|
+
│ • PvP / PvE loot │ │ • respawn cost │
|
|
39
|
+
│ • kill rewards │ │ • portal fee │
|
|
40
|
+
│ • minimum loot │ │ • crafting fee │
|
|
41
|
+
└─────────────────────┘ └─────────────────────┘
|
|
42
|
+
│ │
|
|
43
|
+
│ ▼
|
|
44
|
+
│ Circulating Supply ↓
|
|
45
|
+
│
|
|
46
|
+
└──────────────► Wallet Redistribution
|
|
49
47
|
```
|
|
50
48
|
|
|
51
49
|
### Core Rules
|
|
52
50
|
|
|
53
|
-
| Rule
|
|
54
|
-
|
|
|
55
|
-
| **Bots are infinite mint**
|
|
56
|
-
| **Players are zero-sum**
|
|
57
|
-
| **Loot is a contributor race
|
|
58
|
-
| **Kill floor**
|
|
59
|
-
| **Sinks scale with activity**
|
|
51
|
+
| Rule | Description |
|
|
52
|
+
| ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
53
|
+
| **Bots are infinite mint** | Every bot respawn resets wallet to `botSpawnCoins`. Supply bounded by player kill rate. |
|
|
54
|
+
| **Players are zero-sum** | A player kill scatters the victim's coins as a grid drop but creates no new ones — redistribution only. A player who dies to bots still drops coins (per `coinKillPercentVsPlayer`); the contributing bots may reclaim them. |
|
|
55
|
+
| **Loot is a contributor race** | Every kill scatters the victim's configured `dropItemIds` (plus coins where the entity carries them); only damage contributors may collect, and the first eligible collider wins. Damage amount is irrelevant — only contribution counts. The contributor set is tracked uniformly for **any** victim (player, bot, resource) and includes both players and the bots that dealt damage: a contributing player collects on collision, a contributing bot has the loot transferred to it on collision. A death with no contributor drops nothing. |
|
|
56
|
+
| **Kill floor** | `coinKillMinAmount` guarantees every successful kill pays out even against a near-empty wallet. |
|
|
57
|
+
| **Sinks scale with activity** | Fees burn a fraction — more activity = more burn, preventing indefinite inflation. |
|
|
60
58
|
|
|
61
59
|
---
|
|
62
60
|
|
|
@@ -123,15 +121,15 @@ All economy logic is encapsulated in a single file enforcing the single-source p
|
|
|
123
121
|
|
|
124
122
|
### Method Reference
|
|
125
123
|
|
|
126
|
-
| Method
|
|
127
|
-
|
|
|
128
|
-
| `FountainInitPlayer(player)`
|
|
129
|
-
| `FountainInitBot(bot)`
|
|
130
|
-
| `botCoinDropAmount(bot)`
|
|
131
|
-
| `pvpCoinDropAmount(player)`
|
|
132
|
-
| `SinkRespawnCost(player)`
|
|
133
|
-
| `SinkPortalFee(player)`
|
|
134
|
-
| `SinkCraftingFee(player, item)`
|
|
124
|
+
| Method | Caller | Description |
|
|
125
|
+
| ------------------------------- | ---------------------------------- | ---------------------------------------------------- |
|
|
126
|
+
| `FountainInitPlayer(player)` | `handlers.go` on WebSocket connect | Credits `playerSpawnCoins` to new player |
|
|
127
|
+
| `FountainInitBot(bot)` | `collision.go` on bot respawn | Resets bot wallet to `botSpawnCoins` (infinite mint) |
|
|
128
|
+
| `botCoinDropAmount(bot)` | `collision.go handleBotDeath` | Coin stake a dead bot scatters as a grid drop |
|
|
129
|
+
| `pvpCoinDropAmount(player)` | `collision.go handlePlayerDeath` | Coin stake a dead player scatters as a grid drop |
|
|
130
|
+
| `SinkRespawnCost(player)` | `collision.go handlePlayerDeath` | Burns `respawnCostPercent`% of player coins |
|
|
131
|
+
| `SinkPortalFee(player)` | Portal handler | Burns flat `portalFee` coins on portal use |
|
|
132
|
+
| `SinkCraftingFee(player, item)` | Craft handler | Burns `craftingFeePercent`% on crafting |
|
|
135
133
|
|
|
136
134
|
### Kill Loot Logic
|
|
137
135
|
|
|
@@ -222,14 +220,14 @@ Offset Size Field
|
|
|
222
220
|
15 str itemId bytes
|
|
223
221
|
```
|
|
224
222
|
|
|
225
|
-
| `fct_type` | Constant
|
|
226
|
-
| ---------- |
|
|
227
|
-
| `0x00` | `FCTTypeDamage`
|
|
228
|
-
| `0x01` | `FCTTypeRegen`
|
|
229
|
-
| `0x02` | `FCTTypeCoinGain`
|
|
230
|
-
| `0x03` | `FCTTypeCoinLoss`
|
|
231
|
-
| `0x04` | `FCTTypeItemGain`
|
|
232
|
-
| `0x05` | `FCTTypeItemLoss`
|
|
223
|
+
| `fct_type` | Constant | Color | Display | Status |
|
|
224
|
+
| ---------- | ----------------- | ------ | -------------- | ------- |
|
|
225
|
+
| `0x00` | `FCTTypeDamage` | Red | `-N` HP lost | active |
|
|
226
|
+
| `0x01` | `FCTTypeRegen` | Green | `+N` HP gained | active |
|
|
227
|
+
| `0x02` | `FCTTypeCoinGain` | Yellow | `+N` coins | retired |
|
|
228
|
+
| `0x03` | `FCTTypeCoinLoss` | Yellow | `-N` coins | retired |
|
|
229
|
+
| `0x04` | `FCTTypeItemGain` | Cyan | `+N ItemID` | retired |
|
|
230
|
+
| `0x05` | `FCTTypeItemLoss` | Purple | `-N ItemID` | retired |
|
|
233
231
|
|
|
234
232
|
One uniform visibility rule: **every combat FCT (damage and regen, on any
|
|
235
233
|
entity — player, bot, or resource) is broadcast with its exact amount to every
|
|
@@ -23,35 +23,36 @@ The platform is the operational backbone. It should stay the source of truth for
|
|
|
23
23
|
|
|
24
24
|
`underpost` is the shared control surface for platform operations. Top-level commands (`underpost <cmd> --help` for options):
|
|
25
25
|
|
|
26
|
-
| Group | Command
|
|
27
|
-
| ------------------ |
|
|
28
|
-
| **Project / repo** | `new`
|
|
29
|
-
| | `clone`
|
|
30
|
-
| | `pull`
|
|
31
|
-
| | `cmt`
|
|
32
|
-
| | `push`
|
|
33
|
-
| | `install`
|
|
34
|
-
| | `root`
|
|
35
|
-
| **Build / config** | `client`
|
|
36
|
-
| | `static`
|
|
37
|
-
| | `env`
|
|
38
|
-
| | `config`
|
|
39
|
-
| | `metadata`
|
|
40
|
-
| **Deploy / infra** | `cluster`
|
|
41
|
-
| | `deploy`
|
|
42
|
-
| | `image`
|
|
43
|
-
| | `secret`
|
|
44
|
-
| | `lxd`
|
|
45
|
-
| | `baremetal`
|
|
46
|
-
| | `ip`
|
|
47
|
-
| **Data / ops** | `db`
|
|
48
|
-
| | `cron`
|
|
49
|
-
| | `fs`
|
|
50
|
-
| | `monitor`
|
|
51
|
-
| | `ssh`
|
|
52
|
-
| | `run`
|
|
53
|
-
| | `test`
|
|
54
|
-
| | `
|
|
26
|
+
| Group | Command | Purpose |
|
|
27
|
+
| ------------------ | ---------------- | ------------------------------------------------------------- |
|
|
28
|
+
| **Project / repo** | `new` | Initialize a new project, service, or configuration |
|
|
29
|
+
| | `clone` | Clone a GitHub repository into the current directory |
|
|
30
|
+
| | `pull` | Pull latest changes from a repository |
|
|
31
|
+
| | `cmt` | Manage commits (commit types and options) |
|
|
32
|
+
| | `push` | Push committed changes to a remote repository |
|
|
33
|
+
| | `install` | Import Underpost npm dependencies by copying |
|
|
34
|
+
| | `root` | Print the npm installation root path |
|
|
35
|
+
| **Build / config** | `client` | Build client assets / single replicas; sync environment ports |
|
|
36
|
+
| | `static` | Static build of pages, bundles, and documentation |
|
|
37
|
+
| | `env` | Set env vars and config for a deploy ID |
|
|
38
|
+
| | `config` | Manage Underpost configuration via operators |
|
|
39
|
+
| | `metadata` | Import/export cluster metadata |
|
|
40
|
+
| **Deploy / infra** | `cluster` | Manage Kubernetes clusters (defaults to Kind init) |
|
|
41
|
+
| | `deploy` | Manage deployments (defaults to development pods) |
|
|
42
|
+
| | `image` | Build, save, and load Docker images into clusters |
|
|
43
|
+
| | `secret` | Manage secrets across platforms |
|
|
44
|
+
| | `lxd` | Manage LXD VMs as K3s nodes (control plane / workers) |
|
|
45
|
+
| | `baremetal` | Bare-metal provisioning workflows |
|
|
46
|
+
| | `ip` | Show current public machine IP addresses |
|
|
47
|
+
| **Data / ops** | `db` | Database backup / restore and related operations |
|
|
48
|
+
| | `cron` | Run cron jobs or generate/apply K8s CronJob manifests |
|
|
49
|
+
| | `fs` | File storage (defaults to upload) |
|
|
50
|
+
| | `monitor` | Health-server monitoring for deployments |
|
|
51
|
+
| | `ssh` | Manage SSH credentials and sessions for cluster nodes |
|
|
52
|
+
| | `run` | Run scripts via named runners |
|
|
53
|
+
| | `test` | Run the test suites |
|
|
54
|
+
| | `socketsecurity` | Dependency and source code security audit, Socket patches |
|
|
55
|
+
| | `release` | Release orchestrator for building and shipping CLI versions |
|
|
55
56
|
|
|
56
57
|
Cyberia-specific operations belong in `cyberia`, not in parallel platform commands. Use the base CLI for infrastructure and delivery, then layer Cyberia workflows on top.
|
|
57
58
|
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// Skeleton of the Underpost app shell as it first paints: `barMode: 'top-bottom-bar'` with a left
|
|
2
|
+
// `slide-menu` (closed). Keep the boxes on the 50px grid the live bars use so nothing jumps when
|
|
3
|
+
// the shell replaces this.
|
|
1
4
|
SSRComponent = ({ backgroundImage }) => html`
|
|
2
5
|
${backgroundImage
|
|
3
6
|
? html`<style>
|
|
@@ -7,38 +10,56 @@ SSRComponent = ({ backgroundImage }) => html`
|
|
|
7
10
|
</style>`
|
|
8
11
|
: ''}
|
|
9
12
|
<style>
|
|
10
|
-
|
|
13
|
+
body {
|
|
14
|
+
background: #222121;
|
|
15
|
+
}
|
|
16
|
+
.ssr-top-bar,
|
|
17
|
+
.ssr-bottom-bar {
|
|
11
18
|
background: #121212;
|
|
12
|
-
|
|
19
|
+
width: 100%;
|
|
20
|
+
height: 50px;
|
|
21
|
+
left: 0px;
|
|
22
|
+
}
|
|
23
|
+
.ssr-top-bar {
|
|
24
|
+
top: 0px;
|
|
25
|
+
}
|
|
26
|
+
.ssr-bottom-bar {
|
|
27
|
+
bottom: 0px;
|
|
13
28
|
}
|
|
14
29
|
.ssr-btn {
|
|
30
|
+
top: 5px;
|
|
15
31
|
height: 40px;
|
|
16
32
|
width: 40px;
|
|
17
33
|
}
|
|
18
|
-
.
|
|
19
|
-
|
|
34
|
+
/* Top bar: app icon, then the search box beside it, profile on the right. */
|
|
35
|
+
.ssr-btn-app-icon {
|
|
36
|
+
left: 5px;
|
|
37
|
+
}
|
|
38
|
+
.ssr-btn-profile {
|
|
39
|
+
right: 5px;
|
|
40
|
+
border-radius: 50%;
|
|
41
|
+
}
|
|
42
|
+
.ssr-top-bar .ssr-search-box {
|
|
43
|
+
left: 55px;
|
|
44
|
+
}
|
|
45
|
+
/* Bottom bar: the menu hamburger on the left; left/right/home/theme/lang on the right. */
|
|
46
|
+
.ssr-btn-menu {
|
|
47
|
+
left: 5px;
|
|
48
|
+
}
|
|
49
|
+
.ssr-btn-nav-0 {
|
|
20
50
|
right: 5px;
|
|
21
51
|
}
|
|
22
|
-
.ssr-btn-1 {
|
|
23
|
-
top: 55px;
|
|
52
|
+
.ssr-btn-nav-1 {
|
|
24
53
|
right: 55px;
|
|
25
54
|
}
|
|
26
|
-
.ssr-btn-2 {
|
|
27
|
-
top: 55px;
|
|
55
|
+
.ssr-btn-nav-2 {
|
|
28
56
|
right: 105px;
|
|
29
57
|
}
|
|
30
|
-
.ssr-btn-3 {
|
|
31
|
-
|
|
32
|
-
left: 5px;
|
|
33
|
-
}
|
|
34
|
-
.ssr-btn-4 {
|
|
35
|
-
top: 5px;
|
|
36
|
-
left: 5px;
|
|
58
|
+
.ssr-btn-nav-3 {
|
|
59
|
+
right: 155px;
|
|
37
60
|
}
|
|
38
|
-
.ssr-btn-
|
|
39
|
-
|
|
40
|
-
right: 5px;
|
|
41
|
-
border-radius: 50%;
|
|
61
|
+
.ssr-btn-nav-4 {
|
|
62
|
+
right: 205px;
|
|
42
63
|
}
|
|
43
64
|
.ssr-loader {
|
|
44
65
|
width: auto;
|
|
@@ -72,19 +93,27 @@ SSRComponent = ({ backgroundImage }) => html`
|
|
|
72
93
|
</style>
|
|
73
94
|
<div class="ssr-background-image"></div>
|
|
74
95
|
<div class="ssr-abs ssr-background" style="opacity: 1">
|
|
75
|
-
<div class="ssr-
|
|
76
|
-
${
|
|
77
|
-
.fill()
|
|
96
|
+
<div class="ssr-abs ssr-top-bar">
|
|
97
|
+
${['app-icon', 'profile']
|
|
78
98
|
.map(
|
|
79
|
-
(
|
|
80
|
-
html`<div class="ssr-abs ssr-btn ssr-btn-${
|
|
99
|
+
(id) =>
|
|
100
|
+
html`<div class="ssr-abs ssr-btn ssr-btn-${id}">
|
|
81
101
|
<div class="ssr-shimmer-dark"></div>
|
|
82
102
|
</div>`,
|
|
83
103
|
)
|
|
84
104
|
.join('')}
|
|
85
|
-
|
|
86
105
|
<div class="ssr-abs ssr-search-box"><div class="ssr-shimmer-dark-search-box"></div></div>
|
|
87
106
|
</div>
|
|
107
|
+
<div class="ssr-abs ssr-bottom-bar">
|
|
108
|
+
${['menu', 'nav-0', 'nav-1', 'nav-2', 'nav-3', 'nav-4']
|
|
109
|
+
.map(
|
|
110
|
+
(id) =>
|
|
111
|
+
html`<div class="ssr-abs ssr-btn ssr-btn-${id}">
|
|
112
|
+
<div class="ssr-shimmer-dark"></div>
|
|
113
|
+
</div>`,
|
|
114
|
+
)
|
|
115
|
+
.join('')}
|
|
116
|
+
</div>
|
|
88
117
|
<div class="ssr-abs ssr-center ssr-loader"><div class="loader"></div></div>
|
|
89
118
|
</div>
|
|
90
119
|
`;
|
|
@@ -146,15 +146,17 @@ SSRComponent = () => html`
|
|
|
146
146
|
background-position: 80px 0;
|
|
147
147
|
}
|
|
148
148
|
}
|
|
149
|
+
/* Dark bars (#121212): a translucent white band reads as a moving highlight on them, where
|
|
150
|
+
near-black stops only darkened the bar and hid the animation. */
|
|
149
151
|
.ssr-shimmer-dark {
|
|
150
152
|
height: 100%;
|
|
151
153
|
background-image: linear-gradient(
|
|
152
154
|
to right,
|
|
153
|
-
|
|
154
|
-
rgba(
|
|
155
|
-
rgba(
|
|
156
|
-
rgba(
|
|
157
|
-
rgba(
|
|
155
|
+
rgba(255, 255, 255, 0.03) 0%,
|
|
156
|
+
rgba(255, 255, 255, 0.07) 30%,
|
|
157
|
+
rgba(255, 255, 255, 0.16) 45%,
|
|
158
|
+
rgba(255, 255, 255, 0.07) 60%,
|
|
159
|
+
rgba(255, 255, 255, 0.03) 100%
|
|
158
160
|
);
|
|
159
161
|
animation: ssr-shimmer-dark 3s linear infinite;
|
|
160
162
|
}
|
|
@@ -170,11 +172,11 @@ SSRComponent = () => html`
|
|
|
170
172
|
height: 100%;
|
|
171
173
|
background-image: linear-gradient(
|
|
172
174
|
to right,
|
|
173
|
-
|
|
174
|
-
rgba(
|
|
175
|
-
rgba(
|
|
176
|
-
rgba(
|
|
177
|
-
rgba(
|
|
175
|
+
rgba(255, 255, 255, 0.03) 0%,
|
|
176
|
+
rgba(255, 255, 255, 0.07) 30%,
|
|
177
|
+
rgba(255, 255, 255, 0.16) 45%,
|
|
178
|
+
rgba(255, 255, 255, 0.07) 60%,
|
|
179
|
+
rgba(255, 255, 255, 0.03) 100%
|
|
178
180
|
);
|
|
179
181
|
animation: ssr-shimmer-dark-search-box 3s linear infinite;
|
|
180
182
|
}
|
|
@@ -9,12 +9,16 @@
|
|
|
9
9
|
// /TEST/<bad> → the TEST 404 (return → /TEST/)
|
|
10
10
|
// /<bad> → the default 404 (return → /)
|
|
11
11
|
//
|
|
12
|
+
// The page never touches the URL. The gateway delivers it by intercepting the
|
|
13
|
+
// workload's 404, so the address bar must keep the URI the client requested —
|
|
14
|
+
// rewriting it to `<base>/404` would present the status page as a destination.
|
|
15
|
+
//
|
|
12
16
|
// Base path resolution (first hit wins):
|
|
13
17
|
// 1. window.CYBERIA_BASE_PATH — injected by the serving layer from the
|
|
14
18
|
// deployment's CYBERIA_BASE_PATH env ("/FOREST", "/TEST", "" for default).
|
|
15
19
|
// Authoritative: only the runtime knows its own instance prefix, since the
|
|
16
20
|
// reverse proxy strips it before the backend sees the request.
|
|
17
|
-
// 2. Fallback: the first URL path segment
|
|
21
|
+
// 2. Fallback: the first URL path segment (none for a bare "/404").
|
|
18
22
|
//
|
|
19
23
|
// Visual contract: cyberpunk-MMORPG pixel art — Press Start 2P / VT323, neon
|
|
20
24
|
// cyan/magenta on near-black, hard edges, scanlines, RGB-split glitch. Fully
|
|
@@ -25,8 +29,7 @@ const s = (el) => document.querySelector(el);
|
|
|
25
29
|
const append = (el, htmlStr) => s(el).insertAdjacentHTML('beforeend', htmlStr);
|
|
26
30
|
|
|
27
31
|
// The instance sub-path this 404 belongs to ("/FOREST", "/TEST", or "" default).
|
|
28
|
-
// Prefer the injected value; fall back to the first URL segment
|
|
29
|
-
// trailing "/404" so the page is idempotent once already normalised).
|
|
32
|
+
// Prefer the injected value; fall back to the first URL segment.
|
|
30
33
|
const basePath = () => {
|
|
31
34
|
const injected = window.CYBERIA_BASE_PATH;
|
|
32
35
|
if (typeof injected === 'string') return injected === '/' ? '' : injected.replace(/\/$/, '');
|
|
@@ -35,7 +38,6 @@ const basePath = () => {
|
|
|
35
38
|
};
|
|
36
39
|
|
|
37
40
|
const homeHref = () => `${basePath()}/`;
|
|
38
|
-
const notFoundHref = () => `${basePath()}/404`;
|
|
39
41
|
|
|
40
42
|
// Instance label shown on the badge — the code the sub-path maps to, or MAIN.
|
|
41
43
|
const instanceLabel = () => {
|
|
@@ -44,11 +46,6 @@ const instanceLabel = () => {
|
|
|
44
46
|
};
|
|
45
47
|
|
|
46
48
|
const main = () => {
|
|
47
|
-
// Normalise the URL to <base>/404 so a bad path reads as the instance's 404
|
|
48
|
-
// (e.g. /FOREST/nope → /FOREST/404) without a server round-trip or a loop.
|
|
49
|
-
const target = notFoundHref();
|
|
50
|
-
if (window.location.pathname !== target) window.history.replaceState(null, '', target + window.location.search);
|
|
51
|
-
|
|
52
49
|
append(
|
|
53
50
|
'body',
|
|
54
51
|
html`
|
|
@@ -155,7 +152,7 @@ const main = () => {
|
|
|
155
152
|
text-align: center;
|
|
156
153
|
padding: 24px;
|
|
157
154
|
}
|
|
158
|
-
.badge {
|
|
155
|
+
.status-badge {
|
|
159
156
|
display: inline-flex;
|
|
160
157
|
align-items: center;
|
|
161
158
|
gap: 8px;
|
|
@@ -168,7 +165,7 @@ const main = () => {
|
|
|
168
165
|
margin-bottom: 26px;
|
|
169
166
|
box-shadow: 0 0 12px rgba(34, 211, 238, 0.35);
|
|
170
167
|
}
|
|
171
|
-
.badge .dot {
|
|
168
|
+
.status-badge .dot {
|
|
172
169
|
width: 8px;
|
|
173
170
|
height: 8px;
|
|
174
171
|
background: var(--green);
|
|
@@ -296,7 +293,7 @@ const main = () => {
|
|
|
296
293
|
</style>
|
|
297
294
|
<div class="grid"></div>
|
|
298
295
|
<main class="wrap">
|
|
299
|
-
<span class="badge"><span class="dot"></span> INSTANCE ${instanceLabel()}</span>
|
|
296
|
+
<span class="status-badge"><span class="dot"></span> INSTANCE ${instanceLabel()}</span>
|
|
300
297
|
<div class="code" data-text="404">404</div>
|
|
301
298
|
<div class="headline">SIGNAL LOST</div>
|
|
302
299
|
<p class="sub">
|
|
@@ -322,7 +319,6 @@ SSRComponent = () =>
|
|
|
322
319
|
const append = ${append};
|
|
323
320
|
const basePath = ${basePath};
|
|
324
321
|
const homeHref = ${homeHref};
|
|
325
|
-
const notFoundHref = ${notFoundHref};
|
|
326
322
|
const instanceLabel = ${instanceLabel};
|
|
327
323
|
const main = ${main};
|
|
328
324
|
window.onload = main;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// NotFound.js — the default 404 status page for PWA clients.
|
|
2
|
+
//
|
|
3
|
+
// Built under `status-pages/404/` and served by the gateway, which intercepts the
|
|
4
|
+
// runtime's bare 404 and answers with this document. The request URI is left
|
|
5
|
+
// exactly as the client sent it: the page renders in place and never navigates.
|
|
6
|
+
|
|
7
|
+
const s = (el) => document.querySelector(el);
|
|
8
|
+
|
|
9
|
+
const append = (el, html) => s(el).insertAdjacentHTML('beforeend', html);
|
|
10
|
+
|
|
11
|
+
const getLang = () =>
|
|
12
|
+
(localStorage.getItem('lang') || navigator.language || navigator.userLanguage || s('html').lang)
|
|
13
|
+
.slice(0, 2)
|
|
14
|
+
.toLowerCase();
|
|
15
|
+
|
|
16
|
+
const main = () => {
|
|
17
|
+
const Translate = {
|
|
18
|
+
Data: {
|
|
19
|
+
['page-not-found']: {
|
|
20
|
+
en: 'Page not found',
|
|
21
|
+
es: 'Página no encontrada',
|
|
22
|
+
},
|
|
23
|
+
['back']: {
|
|
24
|
+
en: 'Back to <br> homepage',
|
|
25
|
+
es: 'Volver a <br> la pagina principal',
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
instance: function (id) {
|
|
29
|
+
return this.Data[id][getLang()] ? this.Data[id][getLang()] : this.Data[id]['en'];
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
const icon = html`<svg xmlns="http://www.w3.org/2000/svg" width="80" height="80" viewBox="0 0 24 24">
|
|
33
|
+
<path
|
|
34
|
+
fill="currentColor"
|
|
35
|
+
d="M15.097 5.904A6.5 6.5 0 0 0 4 10.504l.001 1h-2v-1a8.5 8.5 0 1 1 15.176 5.258l5.344 5.345l-1.414 1.414l-5.344-5.345A8.48 8.48 0 0 1 10.5 19h-1v-2h1a6.5 6.5 0 0 0 4.596-11.096M1.672 13.257L4.5 16.086l2.829-2.829l1.414 1.415L5.915 17.5l2.828 2.828l-1.414 1.415L4.5 18.914l-2.828 2.829l-1.414-1.415L3.086 17.5L.258 14.672z"
|
|
36
|
+
/>
|
|
37
|
+
</svg>`;
|
|
38
|
+
|
|
39
|
+
append(
|
|
40
|
+
'body',
|
|
41
|
+
html` <style>
|
|
42
|
+
body {
|
|
43
|
+
font-family: arial;
|
|
44
|
+
font-size: 20px;
|
|
45
|
+
background-color: #d8d8d8;
|
|
46
|
+
color: #333;
|
|
47
|
+
}
|
|
48
|
+
a {
|
|
49
|
+
color: black;
|
|
50
|
+
}
|
|
51
|
+
</style>
|
|
52
|
+
|
|
53
|
+
<div class="abs center" style="top: 45%">
|
|
54
|
+
${icon}
|
|
55
|
+
<br />
|
|
56
|
+
<br />
|
|
57
|
+
<span class="bold">404</span>
|
|
58
|
+
<br />
|
|
59
|
+
<br />${Translate.instance('page-not-found')} <br />
|
|
60
|
+
<br />
|
|
61
|
+
<a target="_top" href="${location.origin}">${Translate.instance('back')}</a>
|
|
62
|
+
</div>`,
|
|
63
|
+
);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
SSRComponent = () =>
|
|
67
|
+
html`<script>
|
|
68
|
+
{
|
|
69
|
+
const s = ${s};
|
|
70
|
+
const append = ${append};
|
|
71
|
+
const getLang = ${getLang};
|
|
72
|
+
const main = ${main};
|
|
73
|
+
window.onload = main;
|
|
74
|
+
}
|
|
75
|
+
</script>`;
|
|
@@ -320,8 +320,8 @@ const pullClientBundle = async ({ deployId, hosts = [], dev = false }) => {
|
|
|
320
320
|
|
|
321
321
|
// The previous deployment's document root can be owned by the web server user, so the
|
|
322
322
|
// replace goes through sudo; the extracted tree this process just wrote does not.
|
|
323
|
-
shellExec(`sudo rm -rf ${publicPath}`);
|
|
324
|
-
if (routePath !== '/') shellExec(`sudo mkdir -p public/${host}`);
|
|
323
|
+
shellExec(`sudo rm -rf ${shellArgumentFactory(publicPath)}`);
|
|
324
|
+
if (routePath !== '/') shellExec(`sudo mkdir -p ${shellArgumentFactory(`public/${host}`)}`);
|
|
325
325
|
fs.copySync(outputPath, publicPath);
|
|
326
326
|
|
|
327
327
|
logger.info('pulled bundle', { host, routePath, publicPath });
|
package/src/index.js
CHANGED
|
@@ -25,6 +25,7 @@ import UnderpostPackage from './cli/package.js';
|
|
|
25
25
|
import UnderpostRepository from './cli/repository.js';
|
|
26
26
|
import UnderpostRun from './cli/run.js';
|
|
27
27
|
import UnderpostSecret from './cli/secrets.js';
|
|
28
|
+
import UnderpostSocketSecurity from './cli/socketsecurity.js';
|
|
28
29
|
import UnderpostSSH from './cli/ssh.js';
|
|
29
30
|
import UnderpostState from './cli/state.js';
|
|
30
31
|
import UnderpostStatic from './cli/static.js';
|
|
@@ -52,7 +53,7 @@ class Underpost {
|
|
|
52
53
|
* @type {String}
|
|
53
54
|
* @memberof Underpost
|
|
54
55
|
*/
|
|
55
|
-
static version = 'v3.
|
|
56
|
+
static version = 'v3.4.0';
|
|
56
57
|
|
|
57
58
|
/**
|
|
58
59
|
* Required Node.js major version
|
|
@@ -384,6 +385,16 @@ class Underpost {
|
|
|
384
385
|
static get package() {
|
|
385
386
|
return UnderpostPackage.API;
|
|
386
387
|
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Socket supply-chain security cli API
|
|
391
|
+
* @static
|
|
392
|
+
* @type {UnderpostSocketSecurity.API}
|
|
393
|
+
* @memberof Underpost
|
|
394
|
+
*/
|
|
395
|
+
static get socketSecurity() {
|
|
396
|
+
return UnderpostSocketSecurity.API;
|
|
397
|
+
}
|
|
387
398
|
}
|
|
388
399
|
|
|
389
400
|
if (!process.version || !process.version.startsWith(`${Underpost.majorNodejsVersion}.`))
|
|
@@ -422,6 +433,7 @@ export {
|
|
|
422
433
|
UnderpostRepository,
|
|
423
434
|
UnderpostRun,
|
|
424
435
|
UnderpostSecret,
|
|
436
|
+
UnderpostSocketSecurity,
|
|
425
437
|
UnderpostSSH,
|
|
426
438
|
UnderpostState,
|
|
427
439
|
UnderpostDns,
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# Stage 1 — builder: clone the private deploy, build it, then scrub secrets.
|
|
7
7
|
# ---------------------------------------------------------------------------
|
|
8
8
|
FROM rockylinux/rockylinux:9 AS builder
|
|
9
|
-
ARG UNDERPOST_VERSION=3.
|
|
9
|
+
ARG UNDERPOST_VERSION=3.4.0
|
|
10
10
|
# Pin Node to an exact patch: dnf's nodejs:24 module lags (24.14.1) while
|
|
11
11
|
# underpost's dependencies require >=24.15.0, so install the official binary.
|
|
12
12
|
ARG NODE_VERSION=24.15.0
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# Stage 1 — builder: clone the private deploy, build it, then scrub secrets.
|
|
7
7
|
# ---------------------------------------------------------------------------
|
|
8
8
|
FROM rockylinux/rockylinux:9 AS builder
|
|
9
|
-
ARG UNDERPOST_VERSION=3.
|
|
9
|
+
ARG UNDERPOST_VERSION=3.4.0
|
|
10
10
|
# Pin Node to an exact patch: dnf's nodejs:24 module lags (24.14.1) while
|
|
11
11
|
# underpost's dependencies require >=24.15.0, so install the official binary.
|
|
12
12
|
ARG NODE_VERSION=24.15.0
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# Stage 1 — builder: clone the private deploy, build it, then scrub secrets.
|
|
7
7
|
# ---------------------------------------------------------------------------
|
|
8
8
|
FROM rockylinux/rockylinux:9 AS builder
|
|
9
|
-
ARG UNDERPOST_VERSION=3.
|
|
9
|
+
ARG UNDERPOST_VERSION=3.4.0
|
|
10
10
|
# Pin Node to an exact patch: dnf's nodejs:24 module lags (24.14.1) while
|
|
11
11
|
# underpost's dependencies require >=24.15.0, so install the official binary.
|
|
12
12
|
ARG NODE_VERSION=24.15.0
|
|
@@ -24,6 +24,7 @@ IPFS_CLUSTER_IMAGE=ipfs/ipfs-cluster:latest
|
|
|
24
24
|
# The engine-cyberia image ships only the underpost CLI; at start it runs the
|
|
25
25
|
# `underpost start --build` bootstrap, which clones ${GITHUB_USERNAME}/engine-cyberia.
|
|
26
26
|
GITHUB_USERNAME=underpostnet
|
|
27
|
+
GITHUB_ORG_NAME=underpost
|
|
27
28
|
GITHUB_TOKEN=change_me_private_repo_token
|
|
28
29
|
|
|
29
30
|
# --- MongoDB (translation of Secret mongodb-secret) --------------------------
|