Slice 2: port 3452, nginx sandbox mount, AGENTS.md, docs, deploy script cleanup
- control-plane default listen addr is now :3452 (was :8080). An unusual port to avoid collisions on the VM. - agent-micro and agent-gateway default SDP_CP_URL points at ws://localhost:3452/ws/agent. docker-compose.yml updates the control plane command, host port mapping, and agent -cp URLs. - nginx/nginx.conf (the legacy root-mount reference) uses 127.0.0.1:3452 for the upstream. nginx/sandbox.conf is the new deployment config: four location blocks for the /sandbox/credit-card mount — _next/static serves cached chunks, /api/ and /ws/ proxy to 127.0.0.1:3452, /sandbox/credit-card serves the static dashboard with try_files for SPA routing. - scripts/patch-nginx.sh: deleted. The user configures nginx on 186 by hand. scripts/deploy.sh no longer calls it. - AGENTS.md: new file. Documents the build/lint/test commands (with the golang:1.24-alpine container — local Go can't fetch the toolchain), the wire protocol, the Slice-2 conventions (sdp-<repo> container naming, snapshot persistence, PreGitReset/AfterStart hooks), the repo-path gotcha, and the build-artifacts-in-git rationale. - dashboard/out: now tracked in git, alongside bin/. The dashboard static export is scp'd to 186 on deploy; the VMs have no internet so they can't regenerate it. .gitignore comment explains this and warns against re-ignoring. - README.md / REQUIREMENTS.md: status updated to 'Slice 2 done', per-feature checklist marked. Erangel repo path corrected to /var/www/html/erangel-ocean (was wrongly ~/SDP in earlier docs).
This commit is contained in:
@@ -0,0 +1,152 @@
|
|||||||
|
# AGENTS.md — Sandbox Deployment Platform
|
||||||
|
|
||||||
|
## Build, lint, test
|
||||||
|
|
||||||
|
The build script is the only way to compile — local Go can't fetch the
|
||||||
|
1.24 toolchain. Run:
|
||||||
|
|
||||||
|
```
|
||||||
|
./scripts/build.sh # cross-compiles 3 Go binaries + builds the Next.js dashboard
|
||||||
|
./scripts/deploy.sh # SSHes the artifacts to 92 and 186; needs sshpass
|
||||||
|
```
|
||||||
|
|
||||||
|
The script uses a `golang:1.24-alpine` container with a persistent
|
||||||
|
`sdp-gocache` named volume. `GO_IMAGE=...` overrides the image. Outputs:
|
||||||
|
`bin/{control-plane,agent-micro,agent-gateway}` (Linux/amd64, static) and
|
||||||
|
`dashboard/out/`.
|
||||||
|
|
||||||
|
Per-module Go work uses the same container:
|
||||||
|
|
||||||
|
```
|
||||||
|
docker run --rm -v "$PWD:/src" -w /src/<module> golang:1.24-alpine sh -c \
|
||||||
|
"apk add --no-cache git >/dev/null && git config --global --add safe.directory /src && go vet ./..."
|
||||||
|
```
|
||||||
|
|
||||||
|
For a single test:
|
||||||
|
|
||||||
|
```
|
||||||
|
docker run --rm -v "$PWD:/src" -w /src/control-plane golang:1.24-alpine sh -c \
|
||||||
|
"apk add --no-cache git >/dev/null && git config --global --add safe.directory /src && go test ./internal/store/..."
|
||||||
|
```
|
||||||
|
|
||||||
|
There is one test file today: `control-plane/internal/store/store_test.go`
|
||||||
|
(round-trips all Slice-2 CRUD).
|
||||||
|
|
||||||
|
The dashboard has no separate typecheck or lint script — `npm run build`
|
||||||
|
runs both. `cd dashboard && npm run build` locally is fine; node_modules
|
||||||
|
is gitignored.
|
||||||
|
|
||||||
|
## Layout
|
||||||
|
|
||||||
|
Five Go modules in a workspace (`go.work`):
|
||||||
|
|
||||||
|
- `protocol/` — wire types shared by CP and agents. Keep small.
|
||||||
|
- `agentlib/` — `gitutil` (askpass-via-stdin credential helper;
|
||||||
|
`git ls-remote`, `fetch`, `checkout`, `pull`, `for-each-ref`,
|
||||||
|
`reset --hard`) and `deployer` (per-deployment state machine; `NewGo`
|
||||||
|
for microservices, `NewPHP` for erangel).
|
||||||
|
- `control-plane/` — HTTP API + WS hub + SQLite. Routes split across
|
||||||
|
`internal/api/{login,sandboxes,templates,environments,routes,deployments,repos}.go`.
|
||||||
|
`internal/ws/hub.go` exposes `CallAgent` for sync RPCs.
|
||||||
|
- `agent-micro/` — runs on 172.18.136.92.
|
||||||
|
- `agent-gateway/` — runs on 172.18.139.186; owns erangel at
|
||||||
|
`/var/www/html/erangel-ocean` and the `<service>_url` patching.
|
||||||
|
|
||||||
|
Dashboard is a separate `next build` static export under
|
||||||
|
`dashboard/src/app/`. Static export means dynamic routes need
|
||||||
|
`generateStaticParams` (see the `sandboxes/[id]` page for the pattern).
|
||||||
|
|
||||||
|
## Wire protocol
|
||||||
|
|
||||||
|
The agent → control-plane channel is one `protocol.Event` per WS text
|
||||||
|
message. The control-plane → agent channel is an ad-hoc envelope
|
||||||
|
`{op, id, data}`. `op` values: `deploy`, `stop`, `list_repos`,
|
||||||
|
`list_branches`, `list_routes`, `probe`, `push_routes`. RPC replies have
|
||||||
|
`{op:"reply", id, ok, error?}` and a `data` field. The two shapes are
|
||||||
|
disambiguated by `kind` (event) vs `op` (rpc reply). New ops go in
|
||||||
|
`agentlib/.../main.go`'s switch and the control-plane's `repos.go` /
|
||||||
|
`sandboxes.go` / `routes.go` handlers — there is no central registry.
|
||||||
|
|
||||||
|
## Conventions
|
||||||
|
|
||||||
|
- `ponytail:` comments mark intentional shortcuts and "TODO: real
|
||||||
|
impl"-style carve-outs. They survive into main. Don't remove without
|
||||||
|
fixing the underlying limitation.
|
||||||
|
- Slice-2 stable container name: `sdp-<repo>` (no deployment id). The
|
||||||
|
next deploy force-removes the existing one. One live container per
|
||||||
|
repo at a time.
|
||||||
|
- Gateway agent persists the per-branch OCP-default snapshot to
|
||||||
|
`<repoPath>/.sdp/ocp-defaults.json`. Re-captured on every deploy so
|
||||||
|
branch switches don't break "Restore OCP" buttons.
|
||||||
|
- `NewPHP` runs `git reset --hard` before fetch (via
|
||||||
|
`Spec.PreGitReset`), and the agent passes an `AfterStart` closure
|
||||||
|
that re-applies active route overrides after the container is up.
|
||||||
|
This is what survives `git reset --hard` + checkout.
|
||||||
|
- `protocol.Event.ContainerID` is set on the deployer side; the
|
||||||
|
deployer writes it back via `Store.SetContainerID`. (Currently the
|
||||||
|
field on the event is unused; container id is recorded in SQLite.)
|
||||||
|
- Cookie auth: `sdp_session` HttpOnly cookie; the `withAuth` middleware
|
||||||
|
skips `/api/login`. WebSocket endpoints are NOT auth-gated by the
|
||||||
|
middleware — they rely on the agent being on a private network.
|
||||||
|
- Crendentials travel with each deploy/probe/push_routes frame from
|
||||||
|
control plane to agent. Never logged. Never persisted on the agent.
|
||||||
|
|
||||||
|
## Gotchas
|
||||||
|
|
||||||
|
- Host Go (`/usr/bin/go`) is older than the `go 1.24` modules require
|
||||||
|
and the toolchain download is blocked. Use the `golang:1.24-alpine`
|
||||||
|
container. Do not edit code expecting `go build` to work locally.
|
||||||
|
- The micro agent and gateway agent `main.go` files duplicate most
|
||||||
|
logic (dial / writer / readLoop / runDeploy). The shared code is in
|
||||||
|
`agentlib/`. When adding a new op, both files need a switch case.
|
||||||
|
- `moby/moby/client` v0.5.0 uses `netip.Addr` for `PortBinding.HostIP`,
|
||||||
|
not a string.
|
||||||
|
- `sdp-<repo>` containers must be in a state where `docker rm -f` works
|
||||||
|
(the `Slice-2` "one live per repo" rule). Don't manually `docker run`
|
||||||
|
a second container with the same name.
|
||||||
|
- The erangel repo path is `/var/www/html/erangel-ocean` on 186, NOT
|
||||||
|
`~/SDP` (README's earlier value is wrong; the spec was fixed in
|
||||||
|
Slice 2). `APACHE_DOCUMENT_ROOT` is set to the same path so the
|
||||||
|
gateway is served at `/erangel/`.
|
||||||
|
- `agent-gateway/.../main.go` re-imports the `routesState` type and
|
||||||
|
uses `rs` as both a value and a parameter name in some helpers.
|
||||||
|
Compiles fine; just be aware when grepping.
|
||||||
|
- Static-export dynamic routes: `generateStaticParams` must return at
|
||||||
|
least one placeholder; the actual id is read at runtime in the
|
||||||
|
client component. See `dashboard/src/app/dashboard/sandboxes/[id]/`.
|
||||||
|
|
||||||
|
## Verifying changes locally
|
||||||
|
|
||||||
|
```
|
||||||
|
# Typecheck + build everything
|
||||||
|
./scripts/build.sh
|
||||||
|
|
||||||
|
# Run the only Go test
|
||||||
|
docker run --rm -v "$PWD:/src" -w /src/control-plane golang:1.24-alpine sh -c \
|
||||||
|
"apk add --no-cache git >/dev/null && git config --global --add safe.directory /src && go test ./..."
|
||||||
|
|
||||||
|
# Smoke the control plane
|
||||||
|
./bin/control-plane -addr :3452 -data /tmp/sdp-data &
|
||||||
|
curl -i -X POST http://127.0.0.1:3452/api/login -d '{"username":"x","password":"y"}'
|
||||||
|
# Expects 401 ("login failed — git ls-remote rejected") when no gateway agent is connected.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Out of scope
|
||||||
|
|
||||||
|
RBAC, suspend/resume, sandbox cloning beyond "clone template into
|
||||||
|
sandbox", per-sandbox Docker networks, per-sandbox resource limits,
|
||||||
|
health monitoring, the 172.18.136.93 infra agent, notifications.
|
||||||
|
These are listed as `later` in REQUIREMENTS.md.
|
||||||
|
|
||||||
|
## Do not
|
||||||
|
|
||||||
|
- Do not commit or push unless the user explicitly says "commit" or
|
||||||
|
"push".
|
||||||
|
- Do not change the gateway repo path back to `~/SDP` (old docs say
|
||||||
|
so; reality is `/var/www/html/erangel-ocean`).
|
||||||
|
- Do not rebuild the dashboard via `next start` for production; the
|
||||||
|
output is served by nginx on 186. Configure nginx by hand; the
|
||||||
|
reference config is in `nginx/nginx.conf` and uses
|
||||||
|
`root /home/administrator/SDP/dashboard;` (i.e. the path
|
||||||
|
`deploy.sh` scp's the static export to).
|
||||||
|
- Do not log or persist Bitbucket creds anywhere.
|
||||||
@@ -5,27 +5,42 @@ branch into an isolated sandbox, with the API Gateway routing selected
|
|||||||
services to the sandbox and the rest to OCP. See
|
services to the sandbox and the rest to OCP. See
|
||||||
[REQUIREMENTS.md](REQUIREMENTS.md) for the full spec.
|
[REQUIREMENTS.md](REQUIREMENTS.md) for the full spec.
|
||||||
|
|
||||||
## Status (Slice 1 — build green, MVP core flow)
|
## Status (Slice 2 — sandboxes, routes, real auth, all MVP features)
|
||||||
|
|
||||||
`./scripts/build.sh` produces three Linux/amd64 binaries and a static
|
`./scripts/build.sh` produces three Linux/amd64 binaries and a static
|
||||||
dashboard. The MVP core flow — login, deploy a microservice or the PHP
|
dashboard. The full MVP flow works end to end:
|
||||||
gateway, watch progress and logs in real time — works end to end. The
|
|
||||||
sandbox / template / route management described in REQUIREMENTS.md is
|
- Real Bitbucket auth via `git ls-remote` against the api-gateway.
|
||||||
**deferred to Slice 2** and is not yet built. See
|
- Real repo and branch listing via agent WS frames.
|
||||||
[REQUIREMENTS.md](REQUIREMENTS.md#status) for a per-feature checklist.
|
- Sandbox / template / environment CRUD with persisted metadata in
|
||||||
|
SQLite.
|
||||||
|
- Route overrides per sandbox, with live read-back of the
|
||||||
|
`<service>_url` map from the gateway's `config.php` after every
|
||||||
|
branch switch. The agent patches the file and gracefully reloads
|
||||||
|
apache.
|
||||||
|
- Per-deploy port binding: the user picks the host port per service
|
||||||
|
(e.g. eredar at `172.18.136.92:9001`), the container's exposed port
|
||||||
|
is published to that port.
|
||||||
|
- Erangel deploy: `git reset --hard → fetch → checkout → pull →
|
||||||
|
composer install → start container → re-apply route overrides`.
|
||||||
|
Per-branch OCP-default snapshot persisted to
|
||||||
|
`<repo>/.sdp/ocp-defaults.json`.
|
||||||
|
|
||||||
|
See [REQUIREMENTS.md](REQUIREMENTS.md#status) for the per-feature
|
||||||
|
checklist.
|
||||||
|
|
||||||
## Layout
|
## Layout
|
||||||
|
|
||||||
```
|
```
|
||||||
.
|
.
|
||||||
├── protocol/ # shared wire types (Event, DeployRequest)
|
├── protocol/ # shared wire types (Event, DeployRequest, RouteOverride, ...)
|
||||||
├── agentlib/ # Go. Shared agent library: gitutil + deployer (Go/PHP flavours)
|
├── agentlib/ # Go. Shared agent library: gitutil + deployer (Go/PHP flavours)
|
||||||
├── control-plane/ # Go. HTTP API + WS hub + SQLite/.log persistence
|
├── control-plane/ # Go. HTTP API + WS hub + SQLite/.log persistence
|
||||||
├── agent-micro/ # Go. Runs on 172.18.136.92, deploys Go microservices
|
├── agent-micro/ # Go. Runs on 172.18.136.92, deploys Go microservices
|
||||||
├── agent-gateway/ # Go. Runs on 172.18.139.186, deploys the PHP API Gateway
|
├── agent-gateway/ # Go. Runs on 172.18.139.186, deploys the PHP API Gateway
|
||||||
├── dashboard/ # NextJS static export, served by nginx
|
├── dashboard/ # NextJS static export, served by nginx
|
||||||
├── nginx/ # reverse proxy + try_files for the dashboard
|
├── nginx/ # reference nginx config (manually applied on 186)
|
||||||
├── scripts/ # build, deploy, ssh wrappers, nginx patch
|
├── scripts/ # build, deploy, ssh wrappers
|
||||||
├── docker-compose.yml # all three services on alpine:latest
|
├── docker-compose.yml # all three services on alpine:latest
|
||||||
├── go.work # Go workspace — one build, five modules
|
├── go.work # Go workspace — one build, five modules
|
||||||
└── bin/ # build output (gitignored)
|
└── bin/ # build output (gitignored)
|
||||||
@@ -39,10 +54,14 @@ for two build flavours:
|
|||||||
`docker run alpine:3.20` with the host repo bind-mounted at `/src` and
|
`docker run alpine:3.20` with the host repo bind-mounted at `/src` and
|
||||||
the binary as the container command. `alpine:3.20` must be pre-loaded
|
the binary as the container command. `alpine:3.20` must be pre-loaded
|
||||||
on the host (see [Offline VMs](#offline-vms)).
|
on the host (see [Offline VMs](#offline-vms)).
|
||||||
- **`NewPHP`** — for the API Gateway. Runs `composer install --no-dev`
|
- **`NewPHP`** — for the API Gateway (erangel). Runs
|
||||||
on the host as a best-effort step (skipped if `composer` or
|
`git reset --hard → fetch → checkout → pull → composer install
|
||||||
`composer.json` are absent), then `docker run php:8.3-apache` with the
|
(best-effort) → docker run php:8.3-apache`, with the repo
|
||||||
repo bind-mounted at `/app`. `php:8.3-apache` must be pre-loaded on
|
bind-mounted at `/var/www/html/erangel-ocean` and
|
||||||
|
`APACHE_DOCUMENT_ROOT=/var/www/html/erangel-ocean` so the gateway is
|
||||||
|
served at `/erangel/`, mirroring production. After the container is
|
||||||
|
up, the agent's `AfterStart` callback re-applies the active route
|
||||||
|
overrides and reloads apache. `php:8.3-apache` must be pre-loaded on
|
||||||
the host. The agent is written in Go; the thing it deploys is a
|
the host. The agent is written in Go; the thing it deploys is a
|
||||||
PHP project.
|
PHP project.
|
||||||
|
|
||||||
@@ -93,8 +112,10 @@ This script:
|
|||||||
2. SSHs to **172.18.139.186** (`administrator`) and pushes
|
2. SSHs to **172.18.139.186** (`administrator`) and pushes
|
||||||
`bin/control-plane`, `bin/agent-gateway`, and `dashboard/out/` to
|
`bin/control-plane`, `bin/agent-gateway`, and `dashboard/out/` to
|
||||||
`~/SDP/`
|
`~/SDP/`
|
||||||
3. Idempotently splices the SDP location block into
|
|
||||||
`/etc/nginx/sites-available/default` on 186 and reloads nginx
|
Nginx on 186 is configured by hand; the dashboard ends up at
|
||||||
|
`/home/administrator/SDP/dashboard/`. The required location block is
|
||||||
|
in [nginx/nginx.conf](nginx/nginx.conf).
|
||||||
|
|
||||||
Override the creds via `SDP_92_PASS` / `SDP_186_PASS` env vars.
|
Override the creds via `SDP_92_PASS` / `SDP_186_PASS` env vars.
|
||||||
|
|
||||||
@@ -108,7 +129,7 @@ docker compose up -d
|
|||||||
```
|
```
|
||||||
|
|
||||||
Three services come up on `alpine:latest`:
|
Three services come up on `alpine:latest`:
|
||||||
- `control-plane` → `:8080`
|
- `control-plane` → `:3452` (an unusual port to avoid collisions)
|
||||||
- `agent-micro` (connects to control plane, has docker socket + repos mounted)
|
- `agent-micro` (connects to control plane, has docker socket + repos mounted)
|
||||||
- `agent-gateway` (same shape)
|
- `agent-gateway` (same shape)
|
||||||
|
|
||||||
@@ -135,24 +156,32 @@ Three services come up on `alpine:latest`:
|
|||||||
`/ws/agent` on the control plane; the dashboard subscribes to
|
`/ws/agent` on the control plane; the dashboard subscribes to
|
||||||
`/ws/deployments/{id}`.
|
`/ws/deployments/{id}`.
|
||||||
|
|
||||||
## MVP stubs (intentional, not Slice 1 scope)
|
## MVP stubs (intentional, deferred)
|
||||||
|
|
||||||
These are marked with `ponytail:` comments in the code and are
|
These are marked with `ponytail:` comments in the code and are
|
||||||
scheduled for later slices. They are **not** in scope for Slice 1.
|
scheduled for later slices.
|
||||||
|
|
||||||
- `validateViaAgent` (login) — accepts any creds if an agent is
|
- `CheckOrigin` in the WS upgrader — open CORS, intentional for an
|
||||||
connected. Real impl: a `git ls-remote` probe frame to the agent
|
internal tool.
|
||||||
(`control-plane/internal/api/api.go:126`).
|
- "Drop on backpressure" policy for slow WS subscribers — replace with
|
||||||
- `handleListRepos` / `handleListBranches` — hardcoded fixtures.
|
flow control or persistent event log if the dashboard ever needs
|
||||||
Real impl: a `list_repos` / `list_branches` frame to the connected
|
catch-up replay.
|
||||||
agent. The `gitutil.ListBranches` helper and the `agentlib` frame
|
- O(n) log tail scan in `store.TailLogs` — fine for tail use; swap to
|
||||||
protocol are not yet wired up.
|
a ring buffer if logs get huge.
|
||||||
- `handleListDeployments` (GET) — returns `[]`. Real impl reads SQLite
|
|
||||||
(`control-plane/internal/api/api.go:182`).
|
## Slice 2 dashboard
|
||||||
- WS auth on `/ws/deployments/*` — open. Real impl checks session token.
|
|
||||||
- Sandbox, Template, Route, Environment CRUD — entirely deferred to
|
The dashboard has these pages:
|
||||||
Slice 2. The data model, REST endpoints, and dashboard pages do not
|
|
||||||
exist yet.
|
- `/` — login (real git-ls-remote via the gateway agent).
|
||||||
|
- `/dashboard` — quick deploy (ad-hoc single-service deploy).
|
||||||
|
- `/dashboard/sandboxes` — list, create, clone-from-template.
|
||||||
|
- `/dashboard/sandboxes/{id}` — sandbox detail. Live routes from the
|
||||||
|
gateway's `config.php`, per-route toggle (OCP / sandbox override),
|
||||||
|
microservice deploys with per-service host port and env.
|
||||||
|
- `/dashboard/templates` — template CRUD.
|
||||||
|
- `/dashboard/environments` — env CRUD.
|
||||||
|
- `/dashboard/history` — deployment history (filterable by sandbox).
|
||||||
|
|
||||||
## See also
|
## See also
|
||||||
|
|
||||||
|
|||||||
+52
-33
@@ -1,17 +1,25 @@
|
|||||||
# Sandbox Deployment Platform (SDP)
|
# Sandbox Deployment Platform (SDP)
|
||||||
|
|
||||||
## Status (Slice 1 — build green, MVP core flow)
|
## Status (Slice 2 — sandboxes, routes, real auth, all MVP features)
|
||||||
|
|
||||||
The build is green: `./scripts/build.sh` produces three Linux/amd64
|
The build is green: `./scripts/build.sh` produces three Linux/amd64
|
||||||
binaries and a static dashboard. The core MVP loop works end to end —
|
binaries and a static dashboard. The full MVP flow works end to end:
|
||||||
login, deploy a microservice or the PHP gateway, watch progress and
|
|
||||||
logs in real time.
|
|
||||||
|
|
||||||
Sandbox / Template / Route / Environment management is **deferred to
|
- Real Bitbucket auth via `git ls-remote` against the api-gateway.
|
||||||
Slice 2** and is not yet built. Real auth via agent-mediated
|
- Real repo and branch listing via agent WS frames.
|
||||||
`git ls-remote` and real branch/repo listing from agents are also
|
- Sandbox / template / environment CRUD with persisted metadata in
|
||||||
deferred (the current code has hardcoded fixtures and an "accept any
|
SQLite.
|
||||||
creds if an agent is connected" stub for these).
|
- Route overrides per sandbox, with live read-back of the
|
||||||
|
`<service>_url` map from the gateway's `config.php` after every
|
||||||
|
branch switch. The agent patches the file and gracefully reloads
|
||||||
|
apache.
|
||||||
|
- Per-deploy port binding: the user picks the host port per service
|
||||||
|
(e.g. eredar at `172.18.136.92:9001`), the container's exposed port
|
||||||
|
is published to that port.
|
||||||
|
- Erangel deploy: `git reset --hard → fetch → checkout → pull →
|
||||||
|
composer install → start container → re-apply route overrides`.
|
||||||
|
Per-branch OCP-default snapshot persisted to
|
||||||
|
`<repo>/.sdp/ocp-defaults.json`.
|
||||||
|
|
||||||
See [Status checklist](#status-checklist) at the bottom of this
|
See [Status checklist](#status-checklist) at the bottom of this
|
||||||
document for a per-feature status.
|
document for a per-feature status.
|
||||||
@@ -114,16 +122,19 @@ IP Address:
|
|||||||
172.18.139.186
|
172.18.139.186
|
||||||
|
|
||||||
Repository Root:
|
Repository Root:
|
||||||
~/SDP
|
/var/www/html/erangel-ocean
|
||||||
```
|
```
|
||||||
|
|
||||||
Contains:
|
Contains:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
~/SDP
|
/var/www/html/erangel-ocean
|
||||||
```
|
```
|
||||||
|
|
||||||
The API Gateway repository.
|
The API Gateway repository (erangel). The container
|
||||||
|
`php:8.3-apache` bind-mounts this path at the same path inside the
|
||||||
|
container and serves the gateway at `/erangel/`, mirroring the
|
||||||
|
production URL space.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -1460,37 +1471,45 @@ scheduled for Slice 2. `later` = out of scope for MVP.
|
|||||||
the per-operation Bitbucket creds.
|
the per-operation Bitbucket creds.
|
||||||
- `done` Micro agent runs `git fetch → checkout → pull → go build →
|
- `done` Micro agent runs `git fetch → checkout → pull → go build →
|
||||||
docker run` and streams progress and logs back.
|
docker run` and streams progress and logs back.
|
||||||
- `done` Gateway agent runs `git fetch → checkout → pull → composer
|
- `done` Gateway agent runs `git reset --hard → fetch → checkout →
|
||||||
install (best-effort) → docker run` and streams progress and logs
|
pull → composer install (best-effort) → docker run → re-apply route
|
||||||
|
overrides → apache graceful reload` and streams progress and logs
|
||||||
back.
|
back.
|
||||||
- `done` Dashboard subscribes to a deployment by id over WebSocket
|
- `done` Dashboard subscribes to a deployment by id over WebSocket
|
||||||
and renders stages + live log tail.
|
and renders stages + live log tail.
|
||||||
- `done` SQLite persistence for deployment rows, stage transitions,
|
- `done` SQLite persistence for deployment rows, stage transitions,
|
||||||
and append-only log files.
|
and append-only log files.
|
||||||
- `next` Replace `validateViaAgent` stub with a real
|
- `done` Real `validateViaAgent` via the agent's `git ls-remote`
|
||||||
`git ls-remote` frame.
|
frame.
|
||||||
- `next` Replace hardcoded `handleListRepos` /
|
- `done` Real `list_repos` / `list_branches` via agent frames; the
|
||||||
`handleListBranches` with agent frames (the `gitutil.ListBranches`
|
hardcoded fixtures are gone.
|
||||||
helper and the `agentlib` frame protocol are partially set up but
|
- `done` `list_routes` RPC exposes the live `<key>_url` map from
|
||||||
not wired through).
|
the gateway's `config.php` after every branch switch.
|
||||||
|
- `done` `GET /api/deployments` reads deployment history from
|
||||||
|
SQLite (filterable by sandbox).
|
||||||
|
|
||||||
## Sandbox & routing (Slice 2)
|
## Sandbox & routing
|
||||||
|
|
||||||
- `next` Sandbox CRUD (data model + REST endpoints + dashboard page).
|
- `done` Sandbox CRUD (data model + REST endpoints + dashboard
|
||||||
- `next` Sandbox template CRUD and "clone template into sandbox".
|
pages).
|
||||||
- `next` Route management (sandbox vs OCP per service).
|
- `done` Sandbox template CRUD and "clone template into sandbox".
|
||||||
- `next` Environment CRUD (persisted named envs, not just inline).
|
- `done` Route management (sandbox vs OCP per service) with live
|
||||||
- `next` Actual route push to the API Gateway (the gateway agent
|
read-back from the gateway's `config.php`.
|
||||||
has to update the gateway's routing config, currently this is
|
- `done` Environment CRUD (persisted named envs, not just inline).
|
||||||
the manual `scripts/patch-nginx.sh` step).
|
- `done` Actual route push to the API Gateway: the gateway agent
|
||||||
- `next` Port allocation table and helpers.
|
rewrites `application/config/production/config.php` and gracefully
|
||||||
|
reloads apache. A per-branch OCP-default snapshot is captured
|
||||||
|
automatically and persisted to `<repo>/.sdp/ocp-defaults.json`.
|
||||||
|
- `done` Per-deploy port binding: the user specifies the host port;
|
||||||
|
the agent publishes the container's exposed port to it. Concurrency
|
||||||
|
is "one live container per repo" (the stable name is `sdp-<repo>`).
|
||||||
|
|
||||||
## Auth
|
## Auth
|
||||||
|
|
||||||
- `done` Login endpoint accepts any creds if an agent is connected
|
- `done` Real auth via agent-mediated `git ls-remote` against the
|
||||||
(MVP stub).
|
api-gateway. Login fails fast if no gateway agent is connected.
|
||||||
- `done` Session cookie + in-memory session store.
|
- `done` Session cookie + in-memory session store, 12-hour TTL,
|
||||||
- `next` Real auth via agent-mediated `git ls-remote`.
|
logout invalidates the token.
|
||||||
- `later` RBAC roles (admin / backend / qa / viewer).
|
- `later` RBAC roles (admin / backend / qa / viewer).
|
||||||
|
|
||||||
## Out of scope for MVP (per the "Future Enhancements" section)
|
## Out of scope for MVP (per the "Future Enhancements" section)
|
||||||
|
|||||||
@@ -5,8 +5,13 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Slice-2: the control plane listens on an unusual port to avoid
|
||||||
|
// collisions with anything else on the VM. Nginx on 186 proxies
|
||||||
|
// /sandbox/credit-card/api/ and /sandbox/credit-card/ws/ to it.
|
||||||
|
const defaultAddr = ":3452"
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Addr string // listen addr, e.g. ":8080"
|
Addr string // listen addr, default :3452
|
||||||
DataDir string // SQLite + .log files live here
|
DataDir string // SQLite + .log files live here
|
||||||
AgentHealth string // map of nodeID -> agent base URL (TODO: real map)
|
AgentHealth string // map of nodeID -> agent base URL (TODO: real map)
|
||||||
}
|
}
|
||||||
@@ -14,7 +19,7 @@ type Config struct {
|
|||||||
// Load reads flags and env. Env wins over defaults; flags win over env.
|
// Load reads flags and env. Env wins over defaults; flags win over env.
|
||||||
func Load() Config {
|
func Load() Config {
|
||||||
c := Config{
|
c := Config{
|
||||||
Addr: envOr("SDP_ADDR", ":8080"),
|
Addr: envOr("SDP_ADDR", defaultAddr),
|
||||||
DataDir: envOr("SDP_DATA", "./data"),
|
DataDir: envOr("SDP_DATA", "./data"),
|
||||||
AgentHealth: envOr("SDP_AGENT_HEALTH", ""),
|
AgentHealth: envOr("SDP_AGENT_HEALTH", ""),
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
|||||||
|
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[165],{3155:function(e,t,n){(window.__NEXT_P=window.__NEXT_P||[]).push(["/_not-found",function(){return n(4032)}])},4032:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"default",{enumerable:!0,get:function(){return i}}),n(6921);let o=n(3827);n(4090);let r={error:{fontFamily:'system-ui,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji"',height:"100vh",textAlign:"center",display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center"},desc:{display:"inline-block"},h1:{display:"inline-block",margin:"0 20px 0 0",padding:"0 23px 0 0",fontSize:24,fontWeight:500,verticalAlign:"top",lineHeight:"49px"},h2:{fontSize:14,fontWeight:400,lineHeight:"49px",margin:0}};function i(){return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)("title",{children:"404: This page could not be found."}),(0,o.jsx)("div",{style:r.error,children:(0,o.jsxs)("div",{children:[(0,o.jsx)("style",{dangerouslySetInnerHTML:{__html:"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}),(0,o.jsx)("h1",{className:"next-error-h1",style:r.h1,children:"404"}),(0,o.jsx)("div",{style:r.desc,children:(0,o.jsx)("h2",{style:r.h2,children:"This page could not be found."})})]})})]})}("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&void 0===t.default.__esModule&&(Object.defineProperty(t.default,"__esModule",{value:!0}),Object.assign(t.default,t),e.exports=t.default)}},function(e){e.O(0,[971,69,744],function(){return e(e.s=3155)}),_N_E=e.O()}]);
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
|||||||
|
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[185],{8216:function(n,e,u){Promise.resolve().then(u.t.bind(u,3385,23))},3385:function(){}},function(n){n.O(0,[971,69,744],function(){return n(n.s=8216)}),_N_E=n.O()}]);
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
|||||||
|
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[744],{3734:function(e,n,t){Promise.resolve().then(t.t.bind(t,7690,23)),Promise.resolve().then(t.t.bind(t,8955,23)),Promise.resolve().then(t.t.bind(t,5613,23)),Promise.resolve().then(t.t.bind(t,1902,23)),Promise.resolve().then(t.t.bind(t,1778,23)),Promise.resolve().then(t.t.bind(t,7831,23))}},function(e){var n=function(n){return e(e.s=n)};e.O(0,[971,69],function(){return n(5317),n(3734)}),_N_E=e.O()}]);
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[888],{1597:function(n,_,u){(window.__NEXT_P=window.__NEXT_P||[]).push(["/_app",function(){return u(7174)}])}},function(n){var _=function(_){return n(n.s=_)};n.O(0,[774,179],function(){return _(1597),_(4546)}),_N_E=n.O()}]);
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[820],{1981:function(n,_,u){(window.__NEXT_P=window.__NEXT_P||[]).push(["/_error",function(){return u(5103)}])}},function(n){n.O(0,[888,774,179],function(){return n(n.s=1981)}),_N_E=n.O()}]);
|
||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
|||||||
|
!function(){"use strict";var e,t,n,r,o,u,i,c,f,a={},l={};function d(e){var t=l[e];if(void 0!==t)return t.exports;var n=l[e]={exports:{}},r=!0;try{a[e](n,n.exports,d),r=!1}finally{r&&delete l[e]}return n.exports}d.m=a,e=[],d.O=function(t,n,r,o){if(n){o=o||0;for(var u=e.length;u>0&&e[u-1][2]>o;u--)e[u]=e[u-1];e[u]=[n,r,o];return}for(var i=1/0,u=0;u<e.length;u++){for(var n=e[u][0],r=e[u][1],o=e[u][2],c=!0,f=0;f<n.length;f++)i>=o&&Object.keys(d.O).every(function(e){return d.O[e](n[f])})?n.splice(f--,1):(c=!1,o<i&&(i=o));if(c){e.splice(u--,1);var a=r();void 0!==a&&(t=a)}}return t},d.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return d.d(t,{a:t}),t},n=Object.getPrototypeOf?function(e){return Object.getPrototypeOf(e)}:function(e){return e.__proto__},d.t=function(e,r){if(1&r&&(e=this(e)),8&r||"object"==typeof e&&e&&(4&r&&e.__esModule||16&r&&"function"==typeof e.then))return e;var o=Object.create(null);d.r(o);var u={};t=t||[null,n({}),n([]),n(n)];for(var i=2&r&&e;"object"==typeof i&&!~t.indexOf(i);i=n(i))Object.getOwnPropertyNames(i).forEach(function(t){u[t]=function(){return e[t]}});return u.default=function(){return e},d.d(o,u),o},d.d=function(e,t){for(var n in t)d.o(t,n)&&!d.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},d.f={},d.e=function(e){return Promise.all(Object.keys(d.f).reduce(function(t,n){return d.f[n](e,t),t},[]))},d.u=function(e){},d.miniCssF=function(e){return"static/css/157e72c563f45c59.css"},d.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r={},o="_N_E:",d.l=function(e,t,n,u){if(r[e]){r[e].push(t);return}if(void 0!==n)for(var i,c,f=document.getElementsByTagName("script"),a=0;a<f.length;a++){var l=f[a];if(l.getAttribute("src")==e||l.getAttribute("data-webpack")==o+n){i=l;break}}i||(c=!0,(i=document.createElement("script")).charset="utf-8",i.timeout=120,d.nc&&i.setAttribute("nonce",d.nc),i.setAttribute("data-webpack",o+n),i.src=d.tu(e)),r[e]=[t];var s=function(t,n){i.onerror=i.onload=null,clearTimeout(p);var o=r[e];if(delete r[e],i.parentNode&&i.parentNode.removeChild(i),o&&o.forEach(function(e){return e(n)}),t)return t(n)},p=setTimeout(s.bind(null,void 0,{type:"timeout",target:i}),12e4);i.onerror=s.bind(null,i.onerror),i.onload=s.bind(null,i.onload),c&&document.head.appendChild(i)},d.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},d.tt=function(){return void 0===u&&(u={createScriptURL:function(e){return e}},"undefined"!=typeof trustedTypes&&trustedTypes.createPolicy&&(u=trustedTypes.createPolicy("nextjs#bundler",u))),u},d.tu=function(e){return d.tt().createScriptURL(e)},d.p="/sandbox/credit-card/_next/",i={272:0},d.f.j=function(e,t){var n=d.o(i,e)?i[e]:void 0;if(0!==n){if(n)t.push(n[2]);else if(272!=e){var r=new Promise(function(t,r){n=i[e]=[t,r]});t.push(n[2]=r);var o=d.p+d.u(e),u=Error();d.l(o,function(t){if(d.o(i,e)&&(0!==(n=i[e])&&(i[e]=void 0),n)){var r=t&&("load"===t.type?"missing":t.type),o=t&&t.target&&t.target.src;u.message="Loading chunk "+e+" failed.\n("+r+": "+o+")",u.name="ChunkLoadError",u.type=r,u.request=o,n[1](u)}},"chunk-"+e,e)}else i[e]=0}},d.O.j=function(e){return 0===i[e]},c=function(e,t){var n,r,o=t[0],u=t[1],c=t[2],f=0;if(o.some(function(e){return 0!==i[e]})){for(n in u)d.o(u,n)&&(d.m[n]=u[n]);if(c)var a=c(d)}for(e&&e(t);f<o.length;f++)r=o[f],d.o(i,r)&&i[r]&&i[r][0](),i[r]=0;return d.O(a)},(f=self.webpackChunk_N_E=self.webpackChunk_N_E||[]).forEach(c.bind(null,0)),f.push=c.bind(null,f.push.bind(f)),d.nc=void 0}();
|
||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
|||||||
|
self.__BUILD_MANIFEST={__rewrites:{afterFiles:[],beforeFiles:[],fallback:[]},"/_error":["static/chunks/pages/_error-9a890acb1e81c3fc.js"],sortedPages:["/_app","/_error"]},self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB();
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
self.__SSG_MANIFEST=new Set(["\u002Fdashboard\u002Fsandboxes\u002F[id]"]);self.__SSG_MANIFEST_CB&&self.__SSG_MANIFEST_CB()
|
||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,7 @@
|
|||||||
|
2:I[3498,["504","static/chunks/504-b386cf1902f7f6c4.js","303","static/chunks/303-30e01c8e944885c6.js","702","static/chunks/app/dashboard/page-fe092df0d98d16a1.js"],"Dashboard"]
|
||||||
|
3:I[9534,["504","static/chunks/504-b386cf1902f7f6c4.js","792","static/chunks/792-ff5864056e955fdc.js","663","static/chunks/app/dashboard/layout-49ba819eac821f2e.js"],"DashboardNav"]
|
||||||
|
4:I[5613,[],""]
|
||||||
|
5:I[1778,[],""]
|
||||||
|
0:["vJ6MiW7DlY0bX8PUKZLrC",[[["",{"children":["dashboard",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],["",{"children":["dashboard",{"children":["__PAGE__",{},["$L1",["$","$L2",null,{}],null]]},[null,["$","div",null,{"className":"min-h-screen bg-zinc-50","children":[["$","$L3",null,{}],["$","main",null,{"children":["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children","dashboard","children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined","styles":null}]}]]}],null]]},[null,["$","html",null,{"lang":"en","className":"h-full","children":["$","body",null,{"className":"h-full bg-background text-foreground antialiased","children":["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[],"styles":null}]}]}],null]],[[["$","link","0",{"rel":"stylesheet","href":"/sandbox/credit-card/_next/static/css/157e72c563f45c59.css","precedence":"next","crossOrigin":""}]],"$L6"]]]]
|
||||||
|
6:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}],["$","meta","1",{"charSet":"utf-8"}],["$","title","2",{"children":"SDP"}],["$","meta","3",{"name":"description","content":"Sandbox Deployment Platform"}]]
|
||||||
|
1:null
|
||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,8 @@
|
|||||||
|
2:I[7831,[],""]
|
||||||
|
3:I[5405,["504","static/chunks/504-b386cf1902f7f6c4.js","17","static/chunks/app/dashboard/environments/page-a627adbd0bb7fff2.js"],""]
|
||||||
|
4:I[5613,[],""]
|
||||||
|
5:I[1778,[],""]
|
||||||
|
6:I[9534,["504","static/chunks/504-b386cf1902f7f6c4.js","792","static/chunks/792-ff5864056e955fdc.js","663","static/chunks/app/dashboard/layout-49ba819eac821f2e.js"],"DashboardNav"]
|
||||||
|
0:["vJ6MiW7DlY0bX8PUKZLrC",[[["",{"children":["dashboard",{"children":["environments",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",true],["",{"children":["dashboard",{"children":["environments",{"children":["__PAGE__",{},["$L1",["$","$L2",null,{"propsForComponent":{"params":{}},"Component":"$3","isStaticGeneration":true}],null]]},["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children","dashboard","children","environments","children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined","styles":null}]]},[null,["$","div",null,{"className":"min-h-screen bg-zinc-50","children":[["$","$L6",null,{}],["$","main",null,{"children":["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children","dashboard","children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined","styles":null}]}]]}],null]]},[null,["$","html",null,{"lang":"en","className":"h-full","children":["$","body",null,{"className":"h-full bg-background text-foreground antialiased","children":["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[],"styles":null}]}]}],null]],[[["$","link","0",{"rel":"stylesheet","href":"/sandbox/credit-card/_next/static/css/157e72c563f45c59.css","precedence":"next","crossOrigin":""}]],"$L7"]]]]
|
||||||
|
7:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}],["$","meta","1",{"charSet":"utf-8"}],["$","title","2",{"children":"SDP"}],["$","meta","3",{"name":"description","content":"Sandbox Deployment Platform"}]]
|
||||||
|
1:null
|
||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,8 @@
|
|||||||
|
2:I[7831,[],""]
|
||||||
|
3:I[3331,["504","static/chunks/504-b386cf1902f7f6c4.js","834","static/chunks/app/dashboard/history/page-d2d7ef8172cb6bd1.js"],""]
|
||||||
|
4:I[5613,[],""]
|
||||||
|
5:I[1778,[],""]
|
||||||
|
6:I[9534,["504","static/chunks/504-b386cf1902f7f6c4.js","792","static/chunks/792-ff5864056e955fdc.js","663","static/chunks/app/dashboard/layout-49ba819eac821f2e.js"],"DashboardNav"]
|
||||||
|
0:["vJ6MiW7DlY0bX8PUKZLrC",[[["",{"children":["dashboard",{"children":["history",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",true],["",{"children":["dashboard",{"children":["history",{"children":["__PAGE__",{},["$L1",["$","$L2",null,{"propsForComponent":{"params":{}},"Component":"$3","isStaticGeneration":true}],null]]},["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children","dashboard","children","history","children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined","styles":null}]]},[null,["$","div",null,{"className":"min-h-screen bg-zinc-50","children":[["$","$L6",null,{}],["$","main",null,{"children":["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children","dashboard","children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined","styles":null}]}]]}],null]]},[null,["$","html",null,{"lang":"en","className":"h-full","children":["$","body",null,{"className":"h-full bg-background text-foreground antialiased","children":["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[],"styles":null}]}]}],null]],[[["$","link","0",{"rel":"stylesheet","href":"/sandbox/credit-card/_next/static/css/157e72c563f45c59.css","precedence":"next","crossOrigin":""}]],"$L7"]]]]
|
||||||
|
7:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}],["$","meta","1",{"charSet":"utf-8"}],["$","title","2",{"children":"SDP"}],["$","meta","3",{"name":"description","content":"Sandbox Deployment Platform"}]]
|
||||||
|
1:null
|
||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,8 @@
|
|||||||
|
2:I[7831,[],""]
|
||||||
|
3:I[7334,["504","static/chunks/504-b386cf1902f7f6c4.js","303","static/chunks/303-30e01c8e944885c6.js","792","static/chunks/792-ff5864056e955fdc.js","509","static/chunks/app/dashboard/sandboxes/page-fa8d8c9854e58bd7.js"],""]
|
||||||
|
4:I[5613,[],""]
|
||||||
|
5:I[1778,[],""]
|
||||||
|
6:I[9534,["504","static/chunks/504-b386cf1902f7f6c4.js","792","static/chunks/792-ff5864056e955fdc.js","663","static/chunks/app/dashboard/layout-49ba819eac821f2e.js"],"DashboardNav"]
|
||||||
|
0:["vJ6MiW7DlY0bX8PUKZLrC",[[["",{"children":["dashboard",{"children":["sandboxes",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",true],["",{"children":["dashboard",{"children":["sandboxes",{"children":["__PAGE__",{},["$L1",["$","$L2",null,{"propsForComponent":{"params":{}},"Component":"$3","isStaticGeneration":true}],null]]},["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children","dashboard","children","sandboxes","children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined","styles":null}]]},[null,["$","div",null,{"className":"min-h-screen bg-zinc-50","children":[["$","$L6",null,{}],["$","main",null,{"children":["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children","dashboard","children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined","styles":null}]}]]}],null]]},[null,["$","html",null,{"lang":"en","className":"h-full","children":["$","body",null,{"className":"h-full bg-background text-foreground antialiased","children":["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[],"styles":null}]}]}],null]],[[["$","link","0",{"rel":"stylesheet","href":"/sandbox/credit-card/_next/static/css/157e72c563f45c59.css","precedence":"next","crossOrigin":""}]],"$L7"]]]]
|
||||||
|
7:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}],["$","meta","1",{"charSet":"utf-8"}],["$","title","2",{"children":"SDP"}],["$","meta","3",{"name":"description","content":"Sandbox Deployment Platform"}]]
|
||||||
|
1:null
|
||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,8 @@
|
|||||||
|
2:I[1103,["504","static/chunks/504-b386cf1902f7f6c4.js","882","static/chunks/app/dashboard/sandboxes/%5Bid%5D/page-c1742b81a03d9067.js"],""]
|
||||||
|
3:I[5613,[],""]
|
||||||
|
5:I[1778,[],""]
|
||||||
|
6:I[9534,["504","static/chunks/504-b386cf1902f7f6c4.js","792","static/chunks/792-ff5864056e955fdc.js","663","static/chunks/app/dashboard/layout-49ba819eac821f2e.js"],"DashboardNav"]
|
||||||
|
4:["id","_","d"]
|
||||||
|
0:["vJ6MiW7DlY0bX8PUKZLrC",[[["",{"children":["dashboard",{"children":["sandboxes",{"children":[["id","_","d"],{"children":["__PAGE__?{\"id\":\"_\"}",{}]}]}]}]},"$undefined","$undefined",true],["",{"children":["dashboard",{"children":["sandboxes",{"children":[["id","_","d"],{"children":["__PAGE__",{},["$L1",["$","$L2",null,{}],null]]},["$","$L3",null,{"parallelRouterKey":"children","segmentPath":["children","dashboard","children","sandboxes","children","$4","children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined","styles":null}]]},["$","$L3",null,{"parallelRouterKey":"children","segmentPath":["children","dashboard","children","sandboxes","children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined","styles":null}]]},[null,["$","div",null,{"className":"min-h-screen bg-zinc-50","children":[["$","$L6",null,{}],["$","main",null,{"children":["$","$L3",null,{"parallelRouterKey":"children","segmentPath":["children","dashboard","children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined","styles":null}]}]]}],null]]},[null,["$","html",null,{"lang":"en","className":"h-full","children":["$","body",null,{"className":"h-full bg-background text-foreground antialiased","children":["$","$L3",null,{"parallelRouterKey":"children","segmentPath":["children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[],"styles":null}]}]}],null]],[[["$","link","0",{"rel":"stylesheet","href":"/sandbox/credit-card/_next/static/css/157e72c563f45c59.css","precedence":"next","crossOrigin":""}]],"$L7"]]]]
|
||||||
|
7:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}],["$","meta","1",{"charSet":"utf-8"}],["$","title","2",{"children":"SDP"}],["$","meta","3",{"name":"description","content":"Sandbox Deployment Platform"}]]
|
||||||
|
1:null
|
||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,8 @@
|
|||||||
|
2:I[7831,[],""]
|
||||||
|
3:I[4700,["504","static/chunks/504-b386cf1902f7f6c4.js","123","static/chunks/app/dashboard/templates/page-5d6b88cb4f5f8cdf.js"],""]
|
||||||
|
4:I[5613,[],""]
|
||||||
|
5:I[1778,[],""]
|
||||||
|
6:I[9534,["504","static/chunks/504-b386cf1902f7f6c4.js","792","static/chunks/792-ff5864056e955fdc.js","663","static/chunks/app/dashboard/layout-49ba819eac821f2e.js"],"DashboardNav"]
|
||||||
|
0:["vJ6MiW7DlY0bX8PUKZLrC",[[["",{"children":["dashboard",{"children":["templates",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",true],["",{"children":["dashboard",{"children":["templates",{"children":["__PAGE__",{},["$L1",["$","$L2",null,{"propsForComponent":{"params":{}},"Component":"$3","isStaticGeneration":true}],null]]},["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children","dashboard","children","templates","children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined","styles":null}]]},[null,["$","div",null,{"className":"min-h-screen bg-zinc-50","children":[["$","$L6",null,{}],["$","main",null,{"children":["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children","dashboard","children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined","styles":null}]}]]}],null]]},[null,["$","html",null,{"lang":"en","className":"h-full","children":["$","body",null,{"className":"h-full bg-background text-foreground antialiased","children":["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[],"styles":null}]}]}],null]],[[["$","link","0",{"rel":"stylesheet","href":"/sandbox/credit-card/_next/static/css/157e72c563f45c59.css","precedence":"next","crossOrigin":""}]],"$L7"]]]]
|
||||||
|
7:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}],["$","meta","1",{"charSet":"utf-8"}],["$","title","2",{"children":"SDP"}],["$","meta","3",{"name":"description","content":"Sandbox Deployment Platform"}]]
|
||||||
|
1:null
|
||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,6 @@
|
|||||||
|
2:I[5733,["504","static/chunks/504-b386cf1902f7f6c4.js","931","static/chunks/app/page-0f77936ff7b5e685.js"],"LoginForm"]
|
||||||
|
3:I[5613,[],""]
|
||||||
|
4:I[1778,[],""]
|
||||||
|
0:["vJ6MiW7DlY0bX8PUKZLrC",[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",{"children":["__PAGE__",{},["$L1",["$","main",null,{"className":"flex min-h-screen items-center justify-center bg-muted/30 p-4","children":["$","$L2",null,{}]}],null]]},[null,["$","html",null,{"lang":"en","className":"h-full","children":["$","body",null,{"className":"h-full bg-background text-foreground antialiased","children":["$","$L3",null,{"parallelRouterKey":"children","segmentPath":["children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[],"styles":null}]}]}],null]],[[["$","link","0",{"rel":"stylesheet","href":"/sandbox/credit-card/_next/static/css/157e72c563f45c59.css","precedence":"next","crossOrigin":""}]],"$L5"]]]]
|
||||||
|
5:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}],["$","meta","1",{"charSet":"utf-8"}],["$","title","2",{"children":"SDP"}],["$","meta","3",{"name":"description","content":"Sandbox Deployment Platform"}]]
|
||||||
|
1:null
|
||||||
+4
-4
@@ -10,12 +10,12 @@ services:
|
|||||||
image: alpine:latest
|
image: alpine:latest
|
||||||
container_name: sdp-control-plane
|
container_name: sdp-control-plane
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
command: ["/SDP/bin/control-plane", "-addr", ":8080", "-data", "/SDP/data"]
|
command: ["/SDP/bin/control-plane", "-addr", ":3452", "-data", "/SDP/data"]
|
||||||
volumes:
|
volumes:
|
||||||
- ./bin/control-plane:/SDP/bin/control-plane:ro
|
- ./bin/control-plane:/SDP/bin/control-plane:ro
|
||||||
- sdp-data:/SDP/data
|
- sdp-data:/SDP/data
|
||||||
ports:
|
ports:
|
||||||
- "8080:8080"
|
- "3452:3452"
|
||||||
|
|
||||||
agent-micro:
|
agent-micro:
|
||||||
image: alpine:latest
|
image: alpine:latest
|
||||||
@@ -28,7 +28,7 @@ services:
|
|||||||
- -node
|
- -node
|
||||||
- micro
|
- micro
|
||||||
- -cp
|
- -cp
|
||||||
- ws://control-plane:8080/ws/agent
|
- ws://control-plane:3452/ws/agent
|
||||||
volumes:
|
volumes:
|
||||||
- ./bin/agent-micro:/SDP/bin/agent-micro:ro
|
- ./bin/agent-micro:/SDP/bin/agent-micro:ro
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
@@ -48,7 +48,7 @@ services:
|
|||||||
- -node
|
- -node
|
||||||
- gateway
|
- gateway
|
||||||
- -cp
|
- -cp
|
||||||
- ws://control-plane:8080/ws/agent
|
- ws://control-plane:3452/ws/agent
|
||||||
volumes:
|
volumes:
|
||||||
- ./bin/agent-gateway:/SDP/bin/agent-gateway:ro
|
- ./bin/agent-gateway:/SDP/bin/agent-gateway:ro
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
|||||||
+4
-3
@@ -1,11 +1,12 @@
|
|||||||
# SDP nginx — serves the static NextJS export and proxies API + WS
|
# SDP nginx — reference config for serving the dashboard at the host
|
||||||
# to the Go control plane.
|
# root. The actual deployment on 186 mounts the dashboard under
|
||||||
|
# /sandbox/credit-card; see nginx/sandbox.conf for that.
|
||||||
#
|
#
|
||||||
# try_files: any unknown path falls back to /index.html so client-side
|
# try_files: any unknown path falls back to /index.html so client-side
|
||||||
# routing works. /api and /ws are matched first and proxied upstream.
|
# routing works. /api and /ws are matched first and proxied upstream.
|
||||||
|
|
||||||
upstream control_plane {
|
upstream control_plane {
|
||||||
server 127.0.0.1:8080;
|
server 127.0.0.1:3452;
|
||||||
keepalive 16;
|
keepalive 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
# SDP nginx for 186 — mount the dashboard under /sandbox/credit-card
|
||||||
|
# and proxy /api/ + /ws/ to the control plane on 127.0.0.1:3452.
|
||||||
|
#
|
||||||
|
# Splice the four location blocks into the existing
|
||||||
|
# /etc/nginx/sites-available/default server { } on 186. Order doesn't
|
||||||
|
# matter for these specific prefixes (they're disjoint), but
|
||||||
|
# longest-prefix-first is the convention.
|
||||||
|
#
|
||||||
|
# Verify with `sudo nginx -t` then `sudo systemctl reload nginx`.
|
||||||
|
|
||||||
|
# Static asset chunks (hashed filenames, safe to cache forever).
|
||||||
|
location /sandbox/credit-card/_next/static/ {
|
||||||
|
alias /home/administrator/SDP/dashboard/_next/static/;
|
||||||
|
expires 1y;
|
||||||
|
add_header Cache-Control "public, immutable";
|
||||||
|
}
|
||||||
|
|
||||||
|
# API: control plane on 127.0.0.1:3452. The proxy_pass has no path
|
||||||
|
# component, so the original /api/... URI is forwarded unchanged.
|
||||||
|
location /sandbox/credit-card/api/ {
|
||||||
|
proxy_pass http://127.0.0.1:3452;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_read_timeout 3600s;
|
||||||
|
}
|
||||||
|
|
||||||
|
# WebSocket: deployment log subscriptions + agent WS.
|
||||||
|
location /sandbox/credit-card/ws/ {
|
||||||
|
proxy_pass http://127.0.0.1:3452;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_read_timeout 3600s;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Everything else under /sandbox/credit-card: serve the static
|
||||||
|
# dashboard. Client-side routes (e.g. /sandbox/credit-card/dashboard/sandboxes/abc)
|
||||||
|
# fall through to /index.html, which Next.js hydrates and the React
|
||||||
|
# Router takes over.
|
||||||
|
location /sandbox/credit-card {
|
||||||
|
alias /home/administrator/SDP/dashboard/;
|
||||||
|
index index.html;
|
||||||
|
try_files $uri $uri/ $uri.html /index.html;
|
||||||
|
}
|
||||||
+3
-8
@@ -4,8 +4,8 @@
|
|||||||
# 92 (micro): ~/SDP/agent-micro
|
# 92 (micro): ~/SDP/agent-micro
|
||||||
# 186 (gateway): ~/SDP/control-plane, ~/SDP/agent-gateway, ~/SDP/dashboard
|
# 186 (gateway): ~/SDP/control-plane, ~/SDP/agent-gateway, ~/SDP/dashboard
|
||||||
#
|
#
|
||||||
# On 186 we also splice the SDP location into nginx's existing default site
|
# Nginx is configured by hand on 186 (out of scope for this script).
|
||||||
# and reload. Run scripts/build.sh first.
|
# Run scripts/build.sh first.
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
cd "$(dirname "$0")/.."
|
cd "$(dirname "$0")/.."
|
||||||
@@ -47,10 +47,5 @@ $SCP_186 -r "$REPO_ROOT/dashboard/out/." "$HOST_186:~/SDP/dashboard/"
|
|||||||
$SSH_186 "$HOST_186" "chmod +x ~/SDP/bin/control-plane ~/SDP/bin/agent-gateway"
|
$SSH_186 "$HOST_186" "chmod +x ~/SDP/bin/control-plane ~/SDP/bin/agent-gateway"
|
||||||
echo " control-plane, agent-gateway, dashboard copied"
|
echo " control-plane, agent-gateway, dashboard copied"
|
||||||
|
|
||||||
# Patch nginx on 186
|
|
||||||
echo
|
echo
|
||||||
echo "==> 186: patching nginx"
|
echo "done. (configure nginx by hand on 186; see AGENTS.md for the location block.)"
|
||||||
"$REPO_ROOT/scripts/patch-nginx.sh"
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo "done."
|
|
||||||
|
|||||||
Reference in New Issue
Block a user