Update Harbor client and gateway functionality
Build and Deploy Gateway / build-and-push (push) Successful in 35s
Build and Deploy Gateway / deploy (push) Successful in 14s

This commit is contained in:
2026-08-17 15:23:16 +03:00
parent 0b39211fbd
commit 7c255192b0
41 changed files with 1443 additions and 218 deletions
+12 -2
View File
@@ -1,6 +1,6 @@
# Harbor application state v1
`GET /api/state` is the canonical Harbor domain snapshot. Successful mutations return the same snapshot as `state`. The persisted owner is `state.json` schema v5; React keeps only drafts, disclosure, focus, animation and transport freshness.
`GET /api/state` is the canonical Harbor domain snapshot. Successful mutations return the same snapshot as `state`. The persisted owner is `state.json` schema v6; React keeps only drafts, disclosure, focus, animation and transport freshness.
An abbreviated snapshot:
@@ -89,10 +89,20 @@ The canonical API is scoped by profile:
Provider failure retains the last successful list and metadata, marks only the target profile stale and records the last successful timestamp. Refreshing, pinging or deleting an inactive profile does not mutate the applied config/runtime. Background refresh iterates profiles independently every 15 minutes.
## Ordered routing rules
`route.localRules` is the desired ordered list. Every rule has an explicit `outbound: "vpn" | "direct"`; the first enabled matcher wins and disabled rules retain their position without entering the generated config. `route.activeLocalRules` is the exact canonical list used to generate the running rules-enabled config, not a second desired owner.
The route-rules mutation uses the whole-array `PUT /api/route-rules/v2` with `rulesContractVersion: 2` and `expectedRulesRevision`. Contract v2 requires an explicit outbound on every rule. The versioned path prevents a stale v2 tab from writing to a rolled-back v1 backend; the legacy path on a v2 backend rejects its payload without changing state, config or runtime. A client that receives a snapshot without capability version 2 can read legacy rules as direct but keeps the editor read-only.
In Connect `gateway-direct`, local user rules are intentionally omitted and the snapshot reports no active or pending local rules. Gateway device policy `Напрямую` bypasses sing-box before these rules; policy `VPN` and an ordinary local/Gateway VPN pipeline evaluate them.
## Compatibility and migration
Schema v5 migrates the legacy singleton and `subscription-cache.json` into one profile named `Основной`. Stable endpoint identity preserves unambiguous desired/applied selection, including transport variants whose normalized IDs differ from old labels. An explicitly stopped legacy state does not resurrect an old applied target.
Schema v6 adds the routing-rule outbound. Rules read from schemas v0-v5 migrate to `outbound: "direct"` in their existing order and both desired/applied arrays are normalized together. A schema-v6 rule without a valid outbound is rejected rather than silently rewritten.
Migration atomically backs up the previous `state.json`. After the embedded profile is committed, Harbor also backs up and removes the legacy subscription cache so there is one persisted owner. Invalid legacy cache/config returns to a truthful stopped first-run state instead of starting stale generated config.
The old HTTP projection remains bounded for one release, but schema v5 persistence is not downgrade-compatible. To roll back to a pre-v5 build, stop Harbor and restore both pre-migration backups rather than deploying old code over v5 data.
The old HTTP projection remains bounded for one release. Schema v6 persistence is not downgrade-compatible: stop Harbor and restore the `state.json.backup-v<fromVersion>-*` matching the rollback binary instead of deploying old code over v6 data. Rolling back before profiles still also requires the matching legacy subscription-cache backup.
+12 -2
View File
@@ -1,6 +1,6 @@
# Harbor state recovery
Harbor keeps the existing data directory and `state.json` path. The current persisted format is `schemaVersion: 5`: schema v2 introduced local route rules, v3 added rule enabled state, v4 added stable server IDs, and v5 embeds the canonical `profiles[]` collection with desired/applied profile identity.
Harbor keeps the existing data directory and `state.json` path. The current persisted format is `schemaVersion: 6`: schema v2 introduced local route rules, v3 added rule enabled state, v4 added stable server IDs, v5 embeds the canonical `profiles[]` collection with desired/applied profile identity, and v6 adds an explicit `vpn` or `direct` outbound to every route rule.
## Atomic writes
@@ -26,6 +26,16 @@ subscription-cache.json.backup-v1-2026-08-11T12-00-00-000Z
An invalid legacy provider config is backed up but not started. Harbor removes stale generated config and returns to a stopped first-run state.
## Migration to ordered VPN/Direct rules
When schemas v0-v5 are read, Harbor preserves the order of `routeRules` and `appliedRouteRules` and adds `outbound: "direct"` to legacy entries before atomically committing schema v6. The original file is preserved using its actual source version, for example:
```text
state.json.backup-v5-2026-08-17T12-00-00-000Z
```
After migration, malformed schema-v6 rules are rejected; Harbor does not reinterpret a missing or unknown outbound as direct.
## Corrupt JSON
If `state.json` cannot be parsed, Harbor renames the exact damaged bytes to:
@@ -45,4 +55,4 @@ Perform recovery while Harbor is stopped:
3. Restore only matching state/cache backups to their original filenames.
4. Start Harbor and verify `GET /api/state` before applying a profile.
A pre-v5 binary cannot interpret canonical profiles. Downgrade therefore requires both pre-migration backups; deploying old code alone is not a safe rollback.
A pre-v6 binary cannot interpret the explicit ordered VPN/Direct rule contract. Restore `state.json.backup-v<fromVersion>-*` matching the rollback binary; deploying old code over schema v6 is not safe. A rollback to pre-v5 additionally requires the matching state and subscription-cache backups because that binary cannot interpret canonical profiles.