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:
Achmad
2026-06-24 04:00:49 +00:00
parent 78872de897
commit 4cab047432
48 changed files with 464 additions and 81 deletions
+152
View File
@@ -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.
+60 -31
View File
@@ -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
[REQUIREMENTS.md](REQUIREMENTS.md) for the full spec.
## Status (Slice 1build green, MVP core flow)
## Status (Slice 2sandboxes, routes, real auth, all MVP features)
`./scripts/build.sh` produces three Linux/amd64 binaries and a static
dashboard. The MVP core flow — login, deploy a microservice or the PHP
gateway, watch progress and logs in real time — works end to end. The
sandbox / template / route management described in REQUIREMENTS.md is
**deferred to Slice 2** and is not yet built. See
[REQUIREMENTS.md](REQUIREMENTS.md#status) for a per-feature checklist.
dashboard. The full MVP flow works end to end:
- Real Bitbucket auth via `git ls-remote` against the api-gateway.
- Real repo and branch listing via agent WS frames.
- 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
```
.
├── protocol/ # shared wire types (Event, DeployRequest)
├── protocol/ # shared wire types (Event, DeployRequest, RouteOverride, ...)
├── agentlib/ # Go. Shared agent library: gitutil + deployer (Go/PHP flavours)
├── control-plane/ # Go. HTTP API + WS hub + SQLite/.log persistence
├── 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
├── dashboard/ # NextJS static export, served by nginx
├── nginx/ # reverse proxy + try_files for the dashboard
├── scripts/ # build, deploy, ssh wrappers, nginx patch
├── nginx/ # reference nginx config (manually applied on 186)
├── scripts/ # build, deploy, ssh wrappers
├── docker-compose.yml # all three services on alpine:latest
├── go.work # Go workspace — one build, five modules
└── 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
the binary as the container command. `alpine:3.20` must be pre-loaded
on the host (see [Offline VMs](#offline-vms)).
- **`NewPHP`** — for the API Gateway. Runs `composer install --no-dev`
on the host as a best-effort step (skipped if `composer` or
`composer.json` are absent), then `docker run php:8.3-apache` with the
repo bind-mounted at `/app`. `php:8.3-apache` must be pre-loaded on
- **`NewPHP`** — for the API Gateway (erangel). Runs
`git reset --hard → fetch → checkout → pull → composer install
(best-effort) → docker run php:8.3-apache`, with the repo
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
PHP project.
@@ -93,8 +112,10 @@ This script:
2. SSHs to **172.18.139.186** (`administrator`) and pushes
`bin/control-plane`, `bin/agent-gateway`, and `dashboard/out/` to
`~/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.
@@ -108,7 +129,7 @@ docker compose up -d
```
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-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/deployments/{id}`.
## MVP stubs (intentional, not Slice 1 scope)
## MVP stubs (intentional, deferred)
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
connected. Real impl: a `git ls-remote` probe frame to the agent
(`control-plane/internal/api/api.go:126`).
- `handleListRepos` / `handleListBranches` — hardcoded fixtures.
Real impl: a `list_repos` / `list_branches` frame to the connected
agent. The `gitutil.ListBranches` helper and the `agentlib` frame
protocol are not yet wired up.
- `handleListDeployments` (GET) — returns `[]`. Real impl reads SQLite
(`control-plane/internal/api/api.go:182`).
- WS auth on `/ws/deployments/*` — open. Real impl checks session token.
- Sandbox, Template, Route, Environment CRUD — entirely deferred to
Slice 2. The data model, REST endpoints, and dashboard pages do not
exist yet.
- `CheckOrigin` in the WS upgrader — open CORS, intentional for an
internal tool.
- "Drop on backpressure" policy for slow WS subscribers — replace with
flow control or persistent event log if the dashboard ever needs
catch-up replay.
- O(n) log tail scan in `store.TailLogs` — fine for tail use; swap to
a ring buffer if logs get huge.
## Slice 2 dashboard
The dashboard has these pages:
- `/` — 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
+52 -33
View File
@@ -1,17 +1,25 @@
# Sandbox Deployment Platform (SDP)
## Status (Slice 1build green, MVP core flow)
## Status (Slice 2sandboxes, routes, real auth, all MVP features)
The build is green: `./scripts/build.sh` produces three Linux/amd64
binaries and a static dashboard. The core MVP loop works end to end
login, deploy a microservice or the PHP gateway, watch progress and
logs in real time.
binaries and a static dashboard. The full MVP flow works end to end:
Sandbox / Template / Route / Environment management is **deferred to
Slice 2** and is not yet built. Real auth via agent-mediated
`git ls-remote` and real branch/repo listing from agents are also
deferred (the current code has hardcoded fixtures and an "accept any
creds if an agent is connected" stub for these).
- Real Bitbucket auth via `git ls-remote` against the api-gateway.
- Real repo and branch listing via agent WS frames.
- 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 [Status checklist](#status-checklist) at the bottom of this
document for a per-feature status.
@@ -114,16 +122,19 @@ IP Address:
172.18.139.186
Repository Root:
~/SDP
/var/www/html/erangel-ocean
```
Contains:
```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.
- `done` Micro agent runs `git fetch → checkout → pull → go build →
docker run` and streams progress and logs back.
- `done` Gateway agent runs `git fetch → checkout → pull → composer
install (best-effort) → docker run` and streams progress and logs
- `done` Gateway agent runs `git reset --hard → fetch → checkout →
pull → composer install (best-effort) → docker run → re-apply route
overrides → apache graceful reload` and streams progress and logs
back.
- `done` Dashboard subscribes to a deployment by id over WebSocket
and renders stages + live log tail.
- `done` SQLite persistence for deployment rows, stage transitions,
and append-only log files.
- `next` Replace `validateViaAgent` stub with a real
`git ls-remote` frame.
- `next` Replace hardcoded `handleListRepos` /
`handleListBranches` with agent frames (the `gitutil.ListBranches`
helper and the `agentlib` frame protocol are partially set up but
not wired through).
- `done` Real `validateViaAgent` via the agent's `git ls-remote`
frame.
- `done` Real `list_repos` / `list_branches` via agent frames; the
hardcoded fixtures are gone.
- `done` `list_routes` RPC exposes the live `<key>_url` map from
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).
- `next` Sandbox template CRUD and "clone template into sandbox".
- `next` Route management (sandbox vs OCP per service).
- `next` Environment CRUD (persisted named envs, not just inline).
- `next` Actual route push to the API Gateway (the gateway agent
has to update the gateway's routing config, currently this is
the manual `scripts/patch-nginx.sh` step).
- `next` Port allocation table and helpers.
- `done` Sandbox CRUD (data model + REST endpoints + dashboard
pages).
- `done` Sandbox template CRUD and "clone template into sandbox".
- `done` Route management (sandbox vs OCP per service) with live
read-back from the gateway's `config.php`.
- `done` Environment CRUD (persisted named envs, not just inline).
- `done` Actual route push to the API Gateway: the gateway agent
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
- `done` Login endpoint accepts any creds if an agent is connected
(MVP stub).
- `done` Session cookie + in-memory session store.
- `next` Real auth via agent-mediated `git ls-remote`.
- `done` Real auth via agent-mediated `git ls-remote` against the
api-gateway. Login fails fast if no gateway agent is connected.
- `done` Session cookie + in-memory session store, 12-hour TTL,
logout invalidates the token.
- `later` RBAC roles (admin / backend / qa / viewer).
## Out of scope for MVP (per the "Future Enhancements" section)
+7 -2
View File
@@ -5,8 +5,13 @@ import (
"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 {
Addr string // listen addr, e.g. ":8080"
Addr string // listen addr, default :3452
DataDir string // SQLite + .log files live here
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.
func Load() Config {
c := Config{
Addr: envOr("SDP_ADDR", ":8080"),
Addr: envOr("SDP_ADDR", defaultAddr),
DataDir: envOr("SDP_DATA", "./data"),
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
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
+7
View File
@@ -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
+8
View File
@@ -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
+8
View File
@@ -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
+8
View File
@@ -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
+8
View File
@@ -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
+8
View File
@@ -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
+6
View File
@@ -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
View File
@@ -10,12 +10,12 @@ services:
image: alpine:latest
container_name: sdp-control-plane
restart: unless-stopped
command: ["/SDP/bin/control-plane", "-addr", ":8080", "-data", "/SDP/data"]
command: ["/SDP/bin/control-plane", "-addr", ":3452", "-data", "/SDP/data"]
volumes:
- ./bin/control-plane:/SDP/bin/control-plane:ro
- sdp-data:/SDP/data
ports:
- "8080:8080"
- "3452:3452"
agent-micro:
image: alpine:latest
@@ -28,7 +28,7 @@ services:
- -node
- micro
- -cp
- ws://control-plane:8080/ws/agent
- ws://control-plane:3452/ws/agent
volumes:
- ./bin/agent-micro:/SDP/bin/agent-micro:ro
- /var/run/docker.sock:/var/run/docker.sock
@@ -48,7 +48,7 @@ services:
- -node
- gateway
- -cp
- ws://control-plane:8080/ws/agent
- ws://control-plane:3452/ws/agent
volumes:
- ./bin/agent-gateway:/SDP/bin/agent-gateway:ro
- /var/run/docker.sock:/var/run/docker.sock
+4 -3
View File
@@ -1,11 +1,12 @@
# SDP nginx serves the static NextJS export and proxies API + WS
# to the Go control plane.
# SDP nginx reference config for serving the dashboard at the host
# 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
# routing works. /api and /ws are matched first and proxied upstream.
upstream control_plane {
server 127.0.0.1:8080;
server 127.0.0.1:3452;
keepalive 16;
}
+47
View File
@@ -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
View File
@@ -4,8 +4,8 @@
# 92 (micro): ~/SDP/agent-micro
# 186 (gateway): ~/SDP/control-plane, ~/SDP/agent-gateway, ~/SDP/dashboard
#
# On 186 we also splice the SDP location into nginx's existing default site
# and reload. Run scripts/build.sh first.
# Nginx is configured by hand on 186 (out of scope for this script).
# Run scripts/build.sh first.
set -euo pipefail
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"
echo " control-plane, agent-gateway, dashboard copied"
# Patch nginx on 186
echo
echo "==> 186: patching nginx"
"$REPO_ROOT/scripts/patch-nginx.sh"
echo
echo "done."
echo "done. (configure nginx by hand on 186; see AGENTS.md for the location block.)"