Compare commits
51 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4509b0c217 | |||
| f3ce08497a | |||
| fd60b0bb57 | |||
| b7b05bb4e3 | |||
| d370d5ec23 | |||
| 1b82e1d3a6 | |||
| 93bf434a47 | |||
| 010408cc45 | |||
| 23f9d4a8fb | |||
| 3d5bf16d37 | |||
| d9a1640b10 | |||
| 5347944c4c | |||
| 48a8598d3b | |||
| a0eb393c6c | |||
| d294818581 | |||
| 9e0366e780 | |||
| 20404b30bb | |||
| b7a54c8461 | |||
| b6e44cb4f8 | |||
| 49b959aee5 | |||
| ae37d79aa8 | |||
| 8fb4183abe | |||
| 6fd2d9bec4 | |||
| ee6ad10db9 | |||
| 5f42a3be18 | |||
| 0e452941dd | |||
| 87cb517623 | |||
| 439741e55f | |||
| 3c26b8deba | |||
| 4569fef11d | |||
| 7a93d43b7e | |||
| d28d6e8dac | |||
| efcedde649 | |||
| 0ac4898027 | |||
| e131798f3b | |||
| 9062f8fa8d | |||
| 60ba3e5b4f | |||
| 70d39b87d1 | |||
| 519e872027 | |||
| 2b4b9b3e96 | |||
| 9a802b4212 | |||
| 1a50306f7d | |||
| e8ced74429 | |||
| 8b8cb8210c | |||
| 4a2e660a4a | |||
| 07d40d729f | |||
| 8da593c450 | |||
| 437a519c36 | |||
| c906cd46ad | |||
| cce3e061a7 | |||
| ab19a7ccde |
@@ -0,0 +1,50 @@
|
|||||||
|
# Control Center - Environment Variables
|
||||||
|
# ======================================
|
||||||
|
|
||||||
|
# ── Backend Variables ───────────────────────────────────────────────────
|
||||||
|
# Server configuration
|
||||||
|
PORT=8080
|
||||||
|
CORS_ORIGIN=http://localhost:3000
|
||||||
|
LOG_LEVEL=info
|
||||||
|
ENVIRONMENT=development
|
||||||
|
|
||||||
|
# Database connection (PostgreSQL DSN)
|
||||||
|
# Format: postgresql://user:password@host:port/database?sslmode=disable
|
||||||
|
DATABASE_URL=postgresql://controlcenter:controlcenter@localhost:5432/controlcenter?sslmode=disable
|
||||||
|
|
||||||
|
# Gateway (OpenClaw) connection
|
||||||
|
# WebSocket gateway config (primary path)
|
||||||
|
WS_GATEWAY_URL=ws://host.docker.internal:18789/
|
||||||
|
# Gateway auth token — same as OPENCLAW_GATEWAY_TOKEN (set in environment)
|
||||||
|
GATEWAY_TOKEN=
|
||||||
|
|
||||||
|
# REST poller config (fallback, only used if WS fails to connect)
|
||||||
|
GATEWAY_URL=http://host.docker.internal:18789/api/agents
|
||||||
|
# Polling interval for agent state updates (fallback only)
|
||||||
|
GATEWAY_POLL_INTERVAL=5s
|
||||||
|
|
||||||
|
# ── Frontend Variables (via Vite) ───────────────────────────────────────
|
||||||
|
# The Vite config exposes these as import.meta.env.VITE_*
|
||||||
|
# Set via environment variable when building: VITE_API_URL
|
||||||
|
# VITE_API_URL=http://localhost:8080
|
||||||
|
|
||||||
|
# ── Docker Compose Specific ─────────────────────────────────────────────
|
||||||
|
# When using docker-compose, these are set in the services section
|
||||||
|
# See docker-compose.yml for service-specific environment variables
|
||||||
|
|
||||||
|
# ── Database Configuration ───────────────────────────────────────────────
|
||||||
|
# Set in the db service environment section of docker-compose.yml
|
||||||
|
# POSTGRES_USER=controlcenter
|
||||||
|
# POSTGRES_PASSWORD=controlcenter
|
||||||
|
# POSTGRES_DB=controlcenter
|
||||||
|
|
||||||
|
# ── Development Notes ───────────────────────────────────────────────────
|
||||||
|
# For local development without Docker:
|
||||||
|
# 1. Start PostgreSQL locally
|
||||||
|
# 2. Run: go run ./cmd/server/main.go
|
||||||
|
# 3. Run: npm run dev in frontend/
|
||||||
|
#
|
||||||
|
# For Docker deployment:
|
||||||
|
# 1. Copy .env.example to .env (backend only)
|
||||||
|
# 2. Run: docker compose up -d
|
||||||
|
# 3. Access frontend at http://localhost:3000
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
name: Deploy (Dev)
|
||||||
|
|
||||||
|
on:
|
||||||
|
repository_dispatch:
|
||||||
|
types:
|
||||||
|
- dev-build-success
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
env:
|
||||||
|
BINARY_NAME: server
|
||||||
|
DEV_HOST: ${{ secrets.DEV_HOST }}
|
||||||
|
DEV_USER: ${{ secrets.DEV_USER }}
|
||||||
|
DEPLOY_BINARY_PATH: /opt/control-center/server
|
||||||
|
DEPLOY_FRONTEND_PATH: /usr/share/nginx/html
|
||||||
|
SERVICE_NAME: control-center-server
|
||||||
|
FRONTEND_SERVICE: nginx
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Download Go binary
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: go-backend-binary
|
||||||
|
|
||||||
|
- name: Download frontend dist
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: frontend-dist
|
||||||
|
path: dist
|
||||||
|
|
||||||
|
- name: Make binary executable
|
||||||
|
run: chmod +x ${{ env.BINARY_NAME }}
|
||||||
|
|
||||||
|
- name: Generate deploy script
|
||||||
|
run: |
|
||||||
|
cat > deploy.sh <<'SCRIPT'
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
BINARY="${1}"
|
||||||
|
FRONTEND_DIST="${2:-dist}"
|
||||||
|
BINARY_PATH="${3:-/opt/control-center/server}"
|
||||||
|
FRONTEND_PATH="${4:-/usr/share/nginx/html}"
|
||||||
|
BINARY_SERVICE="${5:-control-center-server}"
|
||||||
|
FRONTEND_SERVICE="${6:-nginx}"
|
||||||
|
|
||||||
|
TIMESTAMP=$(date +%Y%m%d%H%M%S)
|
||||||
|
BACKUP="${BINARY_PATH}.${TIMESTAMP}.bak"
|
||||||
|
|
||||||
|
echo "=== deploy backend ==="
|
||||||
|
|
||||||
|
if [ -f "$BINARY_PATH" ]; then
|
||||||
|
echo "backing up current binary"
|
||||||
|
cp "$BINARY_PATH" "$BACKUP"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "installing new binary"
|
||||||
|
cp "$BINARY" "$BINARY_PATH"
|
||||||
|
chmod +x "$BINARY_PATH"
|
||||||
|
|
||||||
|
echo "restarting service"
|
||||||
|
systemctl reload-or-restart "$BINARY_SERVICE" || systemctl restart "$BINARY_SERVICE"
|
||||||
|
|
||||||
|
sleep 3
|
||||||
|
|
||||||
|
if ! systemctl is-active --quiet "$BINARY_SERVICE"; then
|
||||||
|
echo "FAILED: $BINARY_SERVICE did not start — rolling back"
|
||||||
|
if [ -f "$BACKUP" ]; then
|
||||||
|
cp "$BACKUP" "$BINARY_PATH"
|
||||||
|
systemctl restart "$BINARY_SERVICE"
|
||||||
|
fi
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "backend deploy ok — keeping last 3 backups"
|
||||||
|
ls -t "${BINARY_PATH}."*.bak 2>/dev/null | tail -n +4 | xargs -r rm -f
|
||||||
|
|
||||||
|
echo "=== deploy frontend ==="
|
||||||
|
if [ -d "$FRONTEND_DIST" ] && [ -n "$(ls -A "$FRONTEND_DIST" 2>/dev/null)" ]; then
|
||||||
|
rsync -a --delete "$FRONTEND_DIST/" "$FRONTEND_PATH/"
|
||||||
|
systemctl reload "$FRONTEND_SERVICE" 2>/dev/null ||:
|
||||||
|
echo "frontend deploy ok"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "=== deploy complete ==="
|
||||||
|
SCRIPT
|
||||||
|
chmod +x deploy.sh
|
||||||
|
|
||||||
|
- name: Copy artifacts to dev server
|
||||||
|
uses: appleboy/scp-action@v0.1.7
|
||||||
|
with:
|
||||||
|
host: ${{ env.DEV_HOST }}
|
||||||
|
username: ${{ env.DEV_USER }}
|
||||||
|
key: ${{ secrets.DEV_SSH_KEY }}
|
||||||
|
source: "${{ env.BINARY_NAME }},deploy.sh,dist"
|
||||||
|
target: "/tmp/control-center-deploy"
|
||||||
|
|
||||||
|
- name: Execute deploy on dev server
|
||||||
|
uses: appleboy/ssh-action@v1
|
||||||
|
with:
|
||||||
|
host: ${{ env.DEV_HOST }}
|
||||||
|
username: ${{ env.DEV_USER }}
|
||||||
|
key: ${{ secrets.DEV_SSH_KEY }}
|
||||||
|
script: |
|
||||||
|
set -euo pipefail
|
||||||
|
cd /tmp/control-center-deploy
|
||||||
|
sudo ./deploy.sh \
|
||||||
|
"${{ env.BINARY_NAME }}" \
|
||||||
|
"dist" \
|
||||||
|
"${{ env.DEPLOY_BINARY_PATH }}" \
|
||||||
|
"${{ env.DEPLOY_FRONTEND_PATH }}" \
|
||||||
|
"${{ env.SERVICE_NAME }}" \
|
||||||
|
"${{ env.FRONTEND_SERVICE }}"
|
||||||
|
rm -rf /tmp/control-center-deploy
|
||||||
|
|
||||||
|
- name: Notify on failure
|
||||||
|
if: failure()
|
||||||
|
uses: appleboy/ssh-action@v1
|
||||||
|
with:
|
||||||
|
host: ${{ env.DEV_HOST }}
|
||||||
|
username: ${{ env.DEV_USER }}
|
||||||
|
key: ${{ secrets.DEV_SSH_KEY }}
|
||||||
|
script: |
|
||||||
|
echo "deploy failed — commit ${{ github.sha }}" > /tmp/control-center-deploy-failure.log
|
||||||
+63
-20
@@ -1,4 +1,4 @@
|
|||||||
name: Dev Build
|
name: Dev Build & Deploy
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
@@ -6,39 +6,82 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches: [dev]
|
branches: [dev]
|
||||||
|
|
||||||
|
env:
|
||||||
|
GO_VERSION: "1.23"
|
||||||
|
NODE_VERSION: "22"
|
||||||
|
REGISTRY: code.cubecraftcreations.com
|
||||||
|
BACKEND_IMAGE: ${{ gitea.repository }}/backend
|
||||||
|
FRONTEND_IMAGE: ${{ gitea.repository }}/frontend
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-test:
|
test-and-build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Setup .NET
|
- name: Install Go
|
||||||
uses: actions/setup-dotnet@v4
|
run: |
|
||||||
with:
|
curl -fsSL "https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz" | sudo tar -C /usr/local -xz
|
||||||
dotnet-version: '9.0.x'
|
echo "/usr/local/go/bin" >> $GITHUB_PATH
|
||||||
|
|
||||||
- name: Restore backend
|
- name: Install Node.js
|
||||||
run: dotnet restore
|
run: |
|
||||||
working-directory: ./backend
|
curl -fsSL "https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.xz" | sudo tar -C /usr/local --strip-components=1 -xJ
|
||||||
|
echo "/usr/local/bin" >> $GITHUB_PATH
|
||||||
|
|
||||||
|
- name: Run backend tests
|
||||||
|
run: go test ./...
|
||||||
|
working-directory: ./go-backend
|
||||||
|
|
||||||
- name: Build backend
|
- name: Build backend
|
||||||
run: dotnet build --no-restore --configuration Release
|
run: go build -ldflags="-w -s" -o /tmp/server ./cmd/server
|
||||||
working-directory: ./backend
|
working-directory: ./go-backend
|
||||||
|
|
||||||
- name: Test backend
|
|
||||||
run: dotnet test --no-build --configuration Release
|
|
||||||
working-directory: ./backend
|
|
||||||
|
|
||||||
- name: Setup Node
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: "24"
|
|
||||||
|
|
||||||
- name: Install frontend deps
|
- name: Install frontend deps
|
||||||
run: npm ci
|
run: npm ci
|
||||||
working-directory: ./frontend
|
working-directory: ./frontend
|
||||||
|
|
||||||
|
- name: Lint frontend
|
||||||
|
run: npm run lint
|
||||||
|
working-directory: ./frontend
|
||||||
|
|
||||||
- name: Build frontend
|
- name: Build frontend
|
||||||
run: npm run build
|
run: npm run build
|
||||||
working-directory: ./frontend
|
working-directory: ./frontend
|
||||||
|
|
||||||
|
docker-build-push:
|
||||||
|
needs: test-and-build
|
||||||
|
if: gitea.event_name == 'push'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Login to Gitea Container Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ${{ env.REGISTRY }}
|
||||||
|
username: ${{ gitea.actor }}
|
||||||
|
password: ${{ secrets.ACCESS_TOKEN }}
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Build & push backend image
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
context: ./go-backend
|
||||||
|
push: true
|
||||||
|
tags: |
|
||||||
|
${{ env.REGISTRY }}/${{ env.BACKEND_IMAGE }}:dev
|
||||||
|
${{ env.REGISTRY }}/${{ env.BACKEND_IMAGE }}:${{ gitea.sha }}
|
||||||
|
|
||||||
|
- name: Build & push frontend image
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
context: ./frontend
|
||||||
|
push: true
|
||||||
|
tags: |
|
||||||
|
${{ env.REGISTRY }}/${{ env.FRONTEND_IMAGE }}:dev
|
||||||
|
${{ env.REGISTRY }}/${{ env.FRONTEND_IMAGE }}:${{ gitea.sha }}
|
||||||
|
|||||||
@@ -0,0 +1,268 @@
|
|||||||
|
# Control Center Deployment Guide
|
||||||
|
|
||||||
|
This document covers the Docker Compose deployment and kiosk configuration for the Control Center Go + React application.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Start all services (backend, frontend, database)
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
docker compose logs -f
|
||||||
|
|
||||||
|
# Stop all services
|
||||||
|
docker compose down
|
||||||
|
|
||||||
|
# Stop and remove volumes (database data)
|
||||||
|
docker compose down -v
|
||||||
|
```
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────┐
|
||||||
|
│ Frontend │ Port 3000 (host) → 80 (container)
|
||||||
|
│ React + nginx │ Serves SPA, proxies /api/ to backend
|
||||||
|
└────────┬────────┘
|
||||||
|
│
|
||||||
|
│ HTTP
|
||||||
|
│
|
||||||
|
┌────────▼────────┐
|
||||||
|
│ Backend │ Port 8080 (host) → 8080 (container)
|
||||||
|
│ Go HTTP API │ PostgreSQL-backed REST API
|
||||||
|
└────────┬────────┘
|
||||||
|
│
|
||||||
|
│ PostgreSQL
|
||||||
|
│
|
||||||
|
┌────────▼────────┐
|
||||||
|
│ PostgreSQL │ Port 5432 (internal only)
|
||||||
|
│ Database │ Persistent volume at /var/lib/postgresql/data
|
||||||
|
└─────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Services
|
||||||
|
|
||||||
|
### Backend (`go-backend`)
|
||||||
|
|
||||||
|
- **Image**: Custom `alpine:latest` with Go binary
|
||||||
|
- **Port**: 8080
|
||||||
|
- **Build**: Multi-stage from `go-backend/Dockerfile`
|
||||||
|
- **Environment Variables**:
|
||||||
|
- `PORT` (default: 8080)
|
||||||
|
- `DATABASE_URL` (PostgreSQL DSN)
|
||||||
|
- `CORS_ORIGIN` (default: `*`)
|
||||||
|
- `LOG_LEVEL` (default: `info`)
|
||||||
|
- `ENVIRONMENT` (default: `development`)
|
||||||
|
- `GATEWAY_URL` (OpenClaw gateway endpoint)
|
||||||
|
|
||||||
|
### Frontend (`frontend`)
|
||||||
|
|
||||||
|
- **Image**: `nginx:1.27-alpine`
|
||||||
|
- **Port**: 80 (internal) → 3000 (host)
|
||||||
|
- **Build**: Multi-stage from `frontend/Dockerfile`
|
||||||
|
- Node 22 for build
|
||||||
|
- Nginx 1.27 for serving
|
||||||
|
- **Config**: Custom nginx config in `frontend/nginx.conf`
|
||||||
|
- **Environment Variables**:
|
||||||
|
- `VITE_API_URL` (passed at build time via Vite config)
|
||||||
|
|
||||||
|
### Database (`db`)
|
||||||
|
|
||||||
|
- **Image**: `postgres:16-alpine`
|
||||||
|
- **Port**: 5432 (internal only)
|
||||||
|
- **Volume**: `postgres-data:/var/lib/postgresql/data`
|
||||||
|
- **Environment Variables**:
|
||||||
|
- `POSTGRES_USER` (default: `controlcenter`)
|
||||||
|
- `POSTGRES_PASSWORD` (default: `controlcenter`)
|
||||||
|
- `POSTGRES_DB` (default: `controlcenter`)
|
||||||
|
|
||||||
|
## Kiosk Mode
|
||||||
|
|
||||||
|
For dedicated display installations (e.g., control center dashboard), Chromium can run in kiosk mode.
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
1. **Install the systemd service** (on Debian/Ubuntu with systemd):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo cp kiosk/control-center-kiosk.service /etc/systemd/system/
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Enable auto-start**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl enable control-center-kiosk
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Start the service**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl start control-center-kiosk
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Check status and logs**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl status control-center-kiosk
|
||||||
|
sudo journalctl -u control-center-kiosk -f
|
||||||
|
```
|
||||||
|
|
||||||
|
### Manual Launch
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# From project root
|
||||||
|
./kiosk/start-kiosk.sh http://localhost:3000
|
||||||
|
```
|
||||||
|
|
||||||
|
### Uninstall
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Stop and disable service
|
||||||
|
sudo systemctl stop control-center-kiosk
|
||||||
|
sudo systemctl disable control-center-kiosk
|
||||||
|
sudo rm /etc/systemd/system/control-center-kiosk.service
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
```
|
||||||
|
|
||||||
|
### Kiosk Requirements
|
||||||
|
|
||||||
|
- **Browser**: `chromium-browser` (install via `apt-get install chromium`)
|
||||||
|
- **Display**: X11 session with `DISPLAY=:0`
|
||||||
|
- **User**: Must run as a user with X11 access (typically `overseer`)
|
||||||
|
- **Permissions**: Read access to the project directory
|
||||||
|
|
||||||
|
## Environment Variables Reference
|
||||||
|
|
||||||
|
### Backend (`go-backend/.env`)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
PORT=8080
|
||||||
|
DATABASE_URL=postgresql://controlcenter:controlcenter@localhost:5432/controlcenter?sslmode=disable
|
||||||
|
CORS_ORIGIN=*
|
||||||
|
LOG_LEVEL=info
|
||||||
|
ENVIRONMENT=development
|
||||||
|
GATEWAY_URL=http://localhost:18789/api/agents
|
||||||
|
GATEWAY_POLL_INTERVAL=5s
|
||||||
|
```
|
||||||
|
|
||||||
|
### Frontend (build-time)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
VITE_API_URL=http://localhost:8080
|
||||||
|
```
|
||||||
|
|
||||||
|
### Docker Compose
|
||||||
|
|
||||||
|
Set via `services.<name>.environment` in `docker-compose.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
backend:
|
||||||
|
environment:
|
||||||
|
- DATABASE_URL=...
|
||||||
|
frontend:
|
||||||
|
environment:
|
||||||
|
- VITE_API_URL=...
|
||||||
|
db:
|
||||||
|
environment:
|
||||||
|
- POSTGRES_USER=...
|
||||||
|
- POSTGRES_PASSWORD=...
|
||||||
|
- POSTGRES_DB=...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
### Local Development (non-Docker)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Backend
|
||||||
|
cd go-backend
|
||||||
|
go run ./cmd/server/main.go
|
||||||
|
|
||||||
|
# Frontend
|
||||||
|
cd frontend
|
||||||
|
npm install
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### Database Migrations
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# If using pgx/migrate or similar
|
||||||
|
# The database is created automatically on first connection if it doesn't exist
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Backend won't connect to database
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check database container status
|
||||||
|
docker compose ps
|
||||||
|
|
||||||
|
# View database logs
|
||||||
|
docker compose logs db
|
||||||
|
|
||||||
|
# Test database connectivity from backend
|
||||||
|
docker compose exec backend ping db
|
||||||
|
```
|
||||||
|
|
||||||
|
### Frontend can't reach backend
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check network connectivity
|
||||||
|
docker compose exec frontend ping backend
|
||||||
|
|
||||||
|
# Verify backend is running
|
||||||
|
docker compose logs backend
|
||||||
|
```
|
||||||
|
|
||||||
|
### Kiosk browser won't start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check Chromium installation
|
||||||
|
which chromium-browser
|
||||||
|
|
||||||
|
# Check X11 forwarding
|
||||||
|
echo $DISPLAY
|
||||||
|
|
||||||
|
# Manual launch for debugging
|
||||||
|
./kiosk/start-kiosk.sh http://localhost:3000
|
||||||
|
```
|
||||||
|
|
||||||
|
### Port conflicts
|
||||||
|
|
||||||
|
If ports 8080, 3000, or 5432 are already in use, modify `docker-compose.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
backend:
|
||||||
|
ports:
|
||||||
|
- "8081:8080" # Change host port
|
||||||
|
frontend:
|
||||||
|
ports:
|
||||||
|
- "3001:80" # Change host port
|
||||||
|
```
|
||||||
|
|
||||||
|
## Production Considerations
|
||||||
|
|
||||||
|
1. **HTTPS**: Add a reverse proxy (nginx/Traefik) for SSL termination
|
||||||
|
2. **Database security**: Use strong passwords, enable SSL
|
||||||
|
3. **CORS**: Restrict `CORS_ORIGIN` to production domain
|
||||||
|
4. **Logs**: Configure log aggregation (e.g., ELK, Loki)
|
||||||
|
5. **Backups**: Regular PostgreSQL volume backups
|
||||||
|
6. **Monitoring**: Add health checks and alerting
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
| File/Directory | Purpose |
|
||||||
|
|----------------|---------|
|
||||||
|
| `docker-compose.yml` | Service definitions and configuration |
|
||||||
|
| `.env.example` | Environment variable template |
|
||||||
|
| `go-backend/Dockerfile` | Backend build definition |
|
||||||
|
| `frontend/Dockerfile` | Frontend build definition |
|
||||||
|
| `frontend/nginx.conf` | Nginx config for SPA routing |
|
||||||
|
| `kiosk/start-kiosk.sh` | Kiosk browser startup script |
|
||||||
|
| `kiosk/control-center-kiosk.service` | Systemd unit for auto-start |
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
// Package models defines the database entities for the Control Center Go backend.
|
||||||
|
// Structs map 1:1 to the PostgreSQL schema defined in backend/migrations/.
|
||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AgentStatus represents the possible lifecycle states of an agent.
|
||||||
|
type AgentStatus string
|
||||||
|
|
||||||
|
const (
|
||||||
|
AgentStatusActive AgentStatus = "active"
|
||||||
|
AgentStatusIdle AgentStatus = "idle"
|
||||||
|
AgentStatusThinking AgentStatus = "thinking"
|
||||||
|
AgentStatusError AgentStatus = "error"
|
||||||
|
AgentStatusOffline AgentStatus = "offline"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Agent represents a registered agent and its current state.
|
||||||
|
type Agent struct {
|
||||||
|
ID pgtype.UUID `db:"id" json:"id"`
|
||||||
|
Name string `db:"name" json:"name"`
|
||||||
|
Status AgentStatus `db:"status" json:"status"`
|
||||||
|
Task *string `db:"task" json:"task,omitempty"`
|
||||||
|
Progress int32 `db:"progress" json:"progress"`
|
||||||
|
SessionKey *string `db:"session_key" json:"session_key,omitempty"`
|
||||||
|
Channel *string `db:"channel" json:"channel,omitempty"`
|
||||||
|
LastActivity time.Time `db:"last_activity" json:"last_activity"`
|
||||||
|
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||||
|
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SessionStatus represents the possible states of an agent session.
|
||||||
|
type SessionStatus string
|
||||||
|
|
||||||
|
const (
|
||||||
|
SessionStatusRunning SessionStatus = "running"
|
||||||
|
SessionStatusCompleted SessionStatus = "completed"
|
||||||
|
SessionStatusCrashed SessionStatus = "crashed"
|
||||||
|
SessionStatusTerminated SessionStatus = "terminated"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Session tracks an agent session over time.
|
||||||
|
type Session struct {
|
||||||
|
ID pgtype.UUID `db:"id" json:"id"`
|
||||||
|
AgentID pgtype.UUID `db:"agent_id" json:"agent_id"`
|
||||||
|
StartedAt time.Time `db:"started_at" json:"started_at"`
|
||||||
|
EndedAt *time.Time `db:"ended_at" json:"ended_at,omitempty"`
|
||||||
|
Status SessionStatus `db:"status" json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TaskLogStatus represents the possible states of a task log entry.
|
||||||
|
type TaskLogStatus string
|
||||||
|
|
||||||
|
const (
|
||||||
|
TaskLogStatusPending TaskLogStatus = "pending"
|
||||||
|
TaskLogStatusRunning TaskLogStatus = "running"
|
||||||
|
TaskLogStatusCompleted TaskLogStatus = "completed"
|
||||||
|
TaskLogStatusFailed TaskLogStatus = "failed"
|
||||||
|
TaskLogStatusCancelled TaskLogStatus = "cancelled"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TaskLog records a historical task assigned to an agent.
|
||||||
|
type TaskLog struct {
|
||||||
|
ID pgtype.UUID `db:"id" json:"id"`
|
||||||
|
AgentID pgtype.UUID `db:"agent_id" json:"agent_id"`
|
||||||
|
Task string `db:"task" json:"task"`
|
||||||
|
Status TaskLogStatus `db:"status" json:"status"`
|
||||||
|
StartedAt time.Time `db:"started_at" json:"started_at"`
|
||||||
|
CompletedAt *time.Time `db:"completed_at" json:"completed_at,omitempty"`
|
||||||
|
ErrorMessage *string `db:"error_message" json:"error_message,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProjectStatus represents the possible states of a project.
|
||||||
|
type ProjectStatus string
|
||||||
|
|
||||||
|
const (
|
||||||
|
ProjectStatusPlanned ProjectStatus = "planned"
|
||||||
|
ProjectStatusInProgress ProjectStatus = "in_progress"
|
||||||
|
ProjectStatusCompleted ProjectStatus = "completed"
|
||||||
|
ProjectStatusPaused ProjectStatus = "paused"
|
||||||
|
ProjectStatusCancelled ProjectStatus = "cancelled"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Project represents a project managed by the Control Center.
|
||||||
|
type Project struct {
|
||||||
|
ID pgtype.UUID `db:"id" json:"id"`
|
||||||
|
Name string `db:"name" json:"name"`
|
||||||
|
Description *string `db:"description" json:"description,omitempty"`
|
||||||
|
Status ProjectStatus `db:"status" json:"status"`
|
||||||
|
AgentID *pgtype.UUID `db:"agent_id" json:"agent_id,omitempty"`
|
||||||
|
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||||
|
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AgentEvent represents an event in the agent lifecycle or telemetry stream.
|
||||||
|
type AgentEvent struct {
|
||||||
|
ID pgtype.UUID `db:"id" json:"id"`
|
||||||
|
AgentID pgtype.UUID `db:"agent_id" json:"agent_id"`
|
||||||
|
EventType string `db:"event_type" json:"event_type"`
|
||||||
|
Payload *map[string]interface{} `db:"payload" json:"payload,omitempty"`
|
||||||
|
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
-- Migration: 001_initial_schema (down)
|
||||||
|
-- Description: Reverts the core Control Center database schema.
|
||||||
|
|
||||||
|
-- Drop in reverse dependency order to avoid FK conflicts
|
||||||
|
DROP TABLE IF EXISTS agent_events;
|
||||||
|
DROP TABLE IF EXISTS task_logs;
|
||||||
|
DROP TABLE IF EXISTS sessions;
|
||||||
|
DROP TABLE IF EXISTS projects;
|
||||||
|
DROP TABLE IF EXISTS agents;
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
-- Migration: 001_initial_schema
|
||||||
|
-- Description: Creates the core Control Center database schema.
|
||||||
|
|
||||||
|
-- Enable UUID extension
|
||||||
|
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||||
|
|
||||||
|
-- ============================================
|
||||||
|
-- Table: agents
|
||||||
|
-- ============================================
|
||||||
|
CREATE TABLE agents (
|
||||||
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
status TEXT NOT NULL DEFAULT 'idle'
|
||||||
|
CHECK (status IN ('active', 'idle', 'thinking', 'error', 'offline')),
|
||||||
|
task TEXT,
|
||||||
|
progress INT NOT NULL DEFAULT 0
|
||||||
|
CHECK (progress >= 0 AND progress <= 100),
|
||||||
|
session_key TEXT,
|
||||||
|
channel TEXT,
|
||||||
|
last_activity TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
COMMENT ON TABLE agents IS 'Registered agents and their current state';
|
||||||
|
COMMENT ON COLUMN agents.status IS 'Agent lifecycle status: active, idle, thinking, error, offline';
|
||||||
|
|
||||||
|
-- ============================================
|
||||||
|
-- Table: sessions
|
||||||
|
-- ============================================
|
||||||
|
CREATE TABLE sessions (
|
||||||
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
agent_id UUID NOT NULL,
|
||||||
|
started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
ended_at TIMESTAMPTZ,
|
||||||
|
status TEXT NOT NULL DEFAULT 'running'
|
||||||
|
CHECK (status IN ('running', 'completed', 'crashed', 'terminated')),
|
||||||
|
CONSTRAINT fk_sessions_agent
|
||||||
|
FOREIGN KEY (agent_id) REFERENCES agents(id)
|
||||||
|
ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
COMMENT ON TABLE sessions IS 'Agent session history';
|
||||||
|
|
||||||
|
-- ============================================
|
||||||
|
-- Table: task_logs
|
||||||
|
-- ============================================
|
||||||
|
CREATE TABLE task_logs (
|
||||||
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
agent_id UUID NOT NULL,
|
||||||
|
task TEXT NOT NULL,
|
||||||
|
status TEXT NOT NULL DEFAULT 'pending'
|
||||||
|
CHECK (status IN ('pending', 'running', 'completed', 'failed', 'cancelled')),
|
||||||
|
started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
completed_at TIMESTAMPTZ,
|
||||||
|
error_message TEXT,
|
||||||
|
CONSTRAINT fk_task_logs_agent
|
||||||
|
FOREIGN KEY (agent_id) REFERENCES agents(id)
|
||||||
|
ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
COMMENT ON TABLE task_logs IS 'Historical record of tasks assigned to agents';
|
||||||
|
|
||||||
|
-- ============================================
|
||||||
|
-- Table: projects
|
||||||
|
-- ============================================
|
||||||
|
CREATE TABLE projects (
|
||||||
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
description TEXT,
|
||||||
|
status TEXT NOT NULL DEFAULT 'planned'
|
||||||
|
CHECK (status IN ('planned', 'in_progress', 'completed', 'paused', 'cancelled')),
|
||||||
|
agent_id UUID,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
CONSTRAINT fk_projects_agent
|
||||||
|
FOREIGN KEY (agent_id) REFERENCES agents(id)
|
||||||
|
ON DELETE SET NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
COMMENT ON TABLE projects IS 'Projects managed by the Control Center';
|
||||||
|
|
||||||
|
-- ============================================
|
||||||
|
-- Table: agent_events
|
||||||
|
-- ============================================
|
||||||
|
CREATE TABLE agent_events (
|
||||||
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
agent_id UUID NOT NULL,
|
||||||
|
event_type TEXT NOT NULL,
|
||||||
|
payload JSONB,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
CONSTRAINT fk_agent_events_agent
|
||||||
|
FOREIGN KEY (agent_id) REFERENCES agents(id)
|
||||||
|
ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
COMMENT ON TABLE agent_events IS 'Event stream for agent lifecycle and telemetry';
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
-- Migration: 002_add_indexes (down)
|
||||||
|
-- Description: Remove all indexes added in 002_add_indexes.
|
||||||
|
|
||||||
|
DROP INDEX IF EXISTS idx_agents_status;
|
||||||
|
DROP INDEX IF EXISTS idx_agents_last_activity;
|
||||||
|
DROP INDEX IF EXISTS idx_agents_created_at;
|
||||||
|
|
||||||
|
DROP INDEX IF EXISTS idx_sessions_agent_id;
|
||||||
|
DROP INDEX IF EXISTS idx_sessions_status;
|
||||||
|
DROP INDEX IF EXISTS idx_sessions_started_at;
|
||||||
|
|
||||||
|
DROP INDEX IF EXISTS idx_task_logs_agent_started;
|
||||||
|
DROP INDEX IF EXISTS idx_task_logs_status;
|
||||||
|
|
||||||
|
DROP INDEX IF EXISTS idx_agent_events_agent_created;
|
||||||
|
DROP INDEX IF EXISTS idx_agent_events_event_type;
|
||||||
|
|
||||||
|
DROP INDEX IF EXISTS idx_projects_status;
|
||||||
|
DROP INDEX IF EXISTS idx_projects_agent_id;
|
||||||
|
DROP INDEX IF EXISTS idx_projects_created;
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
-- Migration: 002_add_indexes
|
||||||
|
-- Description: Add performance indexes for common query patterns.
|
||||||
|
|
||||||
|
-- agents: status filtering, activity ordering
|
||||||
|
CREATE INDEX idx_agents_status ON agents(status);
|
||||||
|
CREATE INDEX idx_agents_last_activity ON agents(last_activity DESC);
|
||||||
|
CREATE INDEX idx_agents_created_at ON agents(created_at DESC);
|
||||||
|
|
||||||
|
-- sessions: agent session lookups, active session checks
|
||||||
|
CREATE INDEX idx_sessions_agent_id ON sessions(agent_id);
|
||||||
|
CREATE INDEX idx_sessions_status ON sessions(status);
|
||||||
|
CREATE INDEX idx_sessions_started_at ON sessions(started_at DESC);
|
||||||
|
|
||||||
|
-- task_logs: agent task history, chronological ordering
|
||||||
|
CREATE INDEX idx_task_logs_agent_started ON task_logs(agent_id, started_at DESC);
|
||||||
|
CREATE INDEX idx_task_logs_status ON task_logs(status);
|
||||||
|
|
||||||
|
-- agent_events: event stream queries
|
||||||
|
CREATE INDEX idx_agent_events_agent_created ON agent_events(agent_id, created_at DESC);
|
||||||
|
CREATE INDEX idx_agent_events_event_type ON agent_events(event_type);
|
||||||
|
|
||||||
|
-- projects: status filtering, agent assignment
|
||||||
|
CREATE INDEX idx_projects_status ON projects(status);
|
||||||
|
CREATE INDEX idx_projects_agent_id ON projects(agent_id);
|
||||||
|
CREATE INDEX idx_projects_created ON projects(created_at DESC);
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
FROM catthehacker/ubuntu:act-latest
|
||||||
|
|
||||||
|
# Install Go 1.23
|
||||||
|
RUN curl -sL https://go.dev/dl/go1.23.6.linux-amd64.tar.gz | tar -C /usr/local -xz
|
||||||
|
|
||||||
|
# Install Node 22
|
||||||
|
RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \
|
||||||
|
&& apt-get install -y nodejs \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
ENV PATH="/usr/local/go/bin:${PATH}"
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
# Control Center - Go + React + PostgreSQL Deployment
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
services:
|
||||||
|
# ── Backend Service (Go) ───────────────────────────────────────────────
|
||||||
|
backend:
|
||||||
|
build:
|
||||||
|
context: ./go-backend
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
ports:
|
||||||
|
- "8080:8080"
|
||||||
|
environment:
|
||||||
|
- DATABASE_URL=postgresql://controlcenter:controlcenter@db:5432/controlcenter?sslmode=disable
|
||||||
|
- CORS_ORIGIN=http://localhost:3000
|
||||||
|
- LOG_LEVEL=info
|
||||||
|
- ENVIRONMENT=production
|
||||||
|
- PORT=8080
|
||||||
|
- GATEWAY_URL=http://host.docker.internal:18789/api/agents
|
||||||
|
- WS_GATEWAY_URL=ws://host.docker.internal:18789/
|
||||||
|
- GATEWAY_TOKEN=${GATEWAY_TOKEN:-}
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "-qO-", "http://localhost:8080/health"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 10s
|
||||||
|
networks:
|
||||||
|
- control-center-network
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
# ── Frontend Service (React) ───────────────────────────────────────────
|
||||||
|
frontend:
|
||||||
|
build:
|
||||||
|
context: ./frontend
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
ports:
|
||||||
|
- "3000:80"
|
||||||
|
depends_on:
|
||||||
|
- backend
|
||||||
|
environment:
|
||||||
|
- VITE_API_URL=http://localhost:8080
|
||||||
|
networks:
|
||||||
|
- control-center-network
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
# ── Database Service (PostgreSQL 16) ───────────────────────────────────
|
||||||
|
db:
|
||||||
|
image: postgres:16-alpine
|
||||||
|
container_name: control-center-db
|
||||||
|
environment:
|
||||||
|
- POSTGRES_USER=controlcenter
|
||||||
|
- POSTGRES_PASSWORD=controlcenter
|
||||||
|
- POSTGRES_DB=controlcenter
|
||||||
|
volumes:
|
||||||
|
- postgres-data:/var/lib/postgresql/data
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U controlcenter -d controlcenter"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
start_period: 10s
|
||||||
|
networks:
|
||||||
|
- control-center-network
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
networks:
|
||||||
|
control-center-network:
|
||||||
|
driver: bridge
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
postgres-data:
|
||||||
+304
@@ -0,0 +1,304 @@
|
|||||||
|
# Control Center — Project Context
|
||||||
|
|
||||||
|
> **Last updated:** 2026-05-21
|
||||||
|
> **Repo:** `CubeCraft-Creations/Control-Center` | **Host:** `code.cubecraftcreations.com`
|
||||||
|
> **Local clone:** `/mnt/ai-storage/projects/Control-Center` | **Default branch:** `dev`
|
||||||
|
> **Discord:** `DISCORD_DEV_CONTROL_CENTER_CHANNEL_ID`
|
||||||
|
> **Linear Epic:** [CUB-119](https://linear.app/cubecraft-creations/issue/CUB-119)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Real-time dashboard for monitoring and controlling the OpenClaw agent fleet. Displays agent statuses, active tasks, sessions, and projects. Uses SSE for live updates from the Go backend, which connects to the OpenClaw gateway via WebSocket for live agent data.
|
||||||
|
|
||||||
|
**Completed refactor:** ASP.NET Core + Angular → Go + React is done (CUB-119 epic). All legacy code is removed from git.
|
||||||
|
|
||||||
|
## Tech Stack
|
||||||
|
|
||||||
|
| Layer | Technology | Notes |
|
||||||
|
|-------|-----------|-------|
|
||||||
|
| Backend | Go 1.24+ | Chi router, pgx (PostgreSQL), SSE broker, gorilla/websocket |
|
||||||
|
| Frontend | React 18 + TypeScript | Vite, Tailwind CSS, React Query, TanStack Router |
|
||||||
|
| Database | PostgreSQL 16+ | snake_case naming, 2 migrations |
|
||||||
|
| Real-time | SSE + WebSocket | SSE for browser, WebSocket for OpenClaw gateway |
|
||||||
|
| Gateway Integration | WebSocket client | OpenClaw gateway `/ws` — live agent + session RPC |
|
||||||
|
| API Client | TypeScript SDK | `api-client/` — shared models, WS client, HTTP client |
|
||||||
|
| CI/CD | Gitea Actions | `.gitea/workflows/dev.yml`, `deploy-dev.yaml` |
|
||||||
|
| Deployment | Docker Compose | PostgreSQL + Go backend + React/nginx |
|
||||||
|
| Testing | Vitest, Go test | Unit + integration tests for WS client, gateway, handlers |
|
||||||
|
| Design | Kiosk layout | Bottom nav (mobile), nav rail (desktop), quick-jump drawer |
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
OpenClaw Gateway (WebSocket)
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
Go Backend (Chi + pgx)
|
||||||
|
├── Gateway WS Client (connect, reconnect, agents.list, sessions.list RPC)
|
||||||
|
├── SSE Broker (fan-out: agent.status, agent.task, fleet.update)
|
||||||
|
├── REST API (/api/agents, /api/sessions, /api/tasks, /api/projects)
|
||||||
|
└── Repository/Store layers → PostgreSQL
|
||||||
|
│
|
||||||
|
├── SSE /api/events
|
||||||
|
▼
|
||||||
|
React Frontend
|
||||||
|
├── SSEProvider → useRealtimeSync → React Query cache
|
||||||
|
├── HubPage (dashboard), LogsPage, ProjectsPage, SessionsPage, SettingsPage
|
||||||
|
└── Layout (header bar + nav rail + bottom nav + quick-jump drawer)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Key Architecture Decisions
|
||||||
|
1. **Go replaced ASP.NET Core** — lighter runtime, faster cold-start, better concurrency for gateway polling
|
||||||
|
2. **React replaced Angular** — lighter than Angular for dashboard/kiosk use
|
||||||
|
3. **SSE over SignalR** — simpler server-side, unidirectional events sufficient for browser updates
|
||||||
|
4. **WebSocket for gateway integration** — bidirectional needed for RPC (agents.list, sessions.list)
|
||||||
|
5. **PostgreSQL** — shared with Extrudex pattern; migrations in `go-backend/migrations/`
|
||||||
|
6. **Agent state seeded on first boot** via `gateway.SeedDemoAgents` for offline dev
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
Control-Center/
|
||||||
|
├── go-backend/ # Go backend
|
||||||
|
│ ├── cmd/server/main.go # Entrypoint, wire deps, start gateway poller
|
||||||
|
│ ├── Dockerfile / go.mod / go.sum
|
||||||
|
│ ├── migrations/ # 001_initial_schema, 002_add_indexes
|
||||||
|
│ └── internal/
|
||||||
|
│ ├── config/config.go # Env vars (DATABASE_URL, GATEWAY_URL, etc.)
|
||||||
|
│ ├── db/db.go # PostgreSQL pool (pgx)
|
||||||
|
│ ├── gateway/
|
||||||
|
│ │ ├── client.go # GW poller → sync DB + SSE fan-out
|
||||||
|
│ │ ├── events.go # SSE event broker
|
||||||
|
│ │ ├── events_test.go
|
||||||
|
│ │ ├── sync.go # Initial sync from gateway
|
||||||
|
│ │ ├── sync_test.go
|
||||||
|
│ │ ├── wsclient.go # WebSocket client (handshake, connect, reconnect, RPC)
|
||||||
|
│ │ └── wsclient_test.go
|
||||||
|
│ ├── handler/
|
||||||
|
│ │ ├── agent.go # CRUD + history
|
||||||
|
│ │ ├── project.go # List projects
|
||||||
|
│ │ ├── session.go # List sessions
|
||||||
|
│ │ ├── sse.go # SSE broker: subscribe + broadcast
|
||||||
|
│ │ ├── task.go # List tasks
|
||||||
|
│ │ ├── helpers.go
|
||||||
|
│ │ ├── handler_test.go
|
||||||
|
│ │ └── mock_repos_test.go
|
||||||
|
│ ├── models/models.go # Domain types
|
||||||
|
│ ├── repository/ # DB access layer + interfaces
|
||||||
|
│ ├── router/router.go # Chi router: REST + SSE mount
|
||||||
|
│ └── store/ # Agent, Project, Session, Task stores
|
||||||
|
├── api-client/ # Shared TypeScript SDK
|
||||||
|
│ └── src/
|
||||||
|
│ ├── models/types.ts # Agent, Session, Task, Project, SSE event types
|
||||||
|
│ ├── services/http-client.ts # Axios REST client
|
||||||
|
│ ├── utils/
|
||||||
|
│ │ ├── config.ts # Client config
|
||||||
|
│ │ └── status-mapper.ts # Agent status → display mapping
|
||||||
|
│ └── websocket/
|
||||||
|
│ └── ws-client.ts # WebSocket client (handshake, Send, RPC, reconnector)
|
||||||
|
├── frontend/ # React frontend
|
||||||
|
│ ├── Dockerfile + nginx.conf
|
||||||
|
│ ├── package.json + vite.config.ts
|
||||||
|
│ ├── src/
|
||||||
|
│ │ ├── App.tsx / main.tsx
|
||||||
|
│ │ ├── components/
|
||||||
|
│ │ │ ├── ErrorBoundary.tsx
|
||||||
|
│ │ │ └── Layout.tsx # Header bar + nav rail + bottom nav + quick-jump
|
||||||
|
│ │ ├── contexts/
|
||||||
|
│ │ │ └── SSEContext.tsx # SSEProvider — wraps entire app
|
||||||
|
│ │ ├── hooks/
|
||||||
|
│ │ │ ├── useLocalStorage.ts
|
||||||
|
│ │ │ ├── useRealtimeSync.ts # SSE messages → React Query cache
|
||||||
|
│ │ │ ├── useRealtimeSync.test.tsx
|
||||||
|
│ │ │ ├── useSSE.ts # SSE: connect, reconnect, typed events
|
||||||
|
│ │ │ ├── useSSE.test.ts
|
||||||
|
│ │ │ └── useTheme.tsx
|
||||||
|
│ │ ├── pages/
|
||||||
|
│ │ │ ├── HubPage.tsx # Fleet dashboard (agent grid + stats)
|
||||||
|
│ │ │ ├── LogsPage.tsx # Agent log viewer
|
||||||
|
│ │ │ ├── ProjectsPage.tsx # Project list
|
||||||
|
│ │ │ ├── SessionsPage.tsx # Session list
|
||||||
|
│ │ │ └── SettingsPage.tsx # Settings + theme toggle
|
||||||
|
│ │ ├── services/
|
||||||
|
│ │ │ ├── api.ts # Axios REST client
|
||||||
|
│ │ │ └── sse.ts # SSE utilities
|
||||||
|
│ │ └── types/index.ts
|
||||||
|
│ └── vitest.config.ts
|
||||||
|
├── frontend-legacy/ # Original Angular frontend (kept for reference, not in git)
|
||||||
|
├── backend/ # Original ASP.NET backend (kept for reference, not in git)
|
||||||
|
│ ├── ControlCenter/ # ASP.NET Core project
|
||||||
|
│ └── Api/ # API layer
|
||||||
|
├── design/
|
||||||
|
│ ├── command-hub-spec.md # Detailed design spec
|
||||||
|
│ └── mockups/ # Desktop kiosk, mobile, quick-jump drawer
|
||||||
|
├── kiosk/
|
||||||
|
│ ├── control-center-kiosk.service # Systemd service
|
||||||
|
│ └── start-kiosk.sh # Kiosk startup script
|
||||||
|
├── reference/
|
||||||
|
│ └── CONTROL_CENTER_CONTEXT.md # Older context file (superseded by this one)
|
||||||
|
├── ci-image/Dockerfile # CI build image
|
||||||
|
├── docker-compose.yml
|
||||||
|
└── .env.example
|
||||||
|
```
|
||||||
|
|
||||||
|
## Database Schema (PostgreSQL)
|
||||||
|
|
||||||
|
### agents
|
||||||
|
| Column | Type | Notes |
|
||||||
|
|--------|------|-------|
|
||||||
|
| id | UUID PK | |
|
||||||
|
| display_name | VARCHAR(256) NOT NULL | |
|
||||||
|
| role | VARCHAR(256) | |
|
||||||
|
| status | VARCHAR(32) | active, idle, thinking, error |
|
||||||
|
| current_task | VARCHAR(512) | |
|
||||||
|
| task_progress | INTEGER DEFAULT 0 | |
|
||||||
|
| session_key | VARCHAR(256) | |
|
||||||
|
| channel | VARCHAR(256) | |
|
||||||
|
| last_activity | TIMESTAMP | |
|
||||||
|
| error_message | TEXT | |
|
||||||
|
| created_at | TIMESTAMP | |
|
||||||
|
| updated_at | TIMESTAMP | |
|
||||||
|
|
||||||
|
### sessions
|
||||||
|
| Column | Type |
|
||||||
|
|--------|------|
|
||||||
|
| id | UUID PK |
|
||||||
|
| session_key | VARCHAR(256) UNIQUE |
|
||||||
|
| agent_id | UUID FK → agents |
|
||||||
|
| channel | VARCHAR(256) |
|
||||||
|
| status | VARCHAR(32) |
|
||||||
|
| context_tokens | INTEGER |
|
||||||
|
| total_tokens | INTEGER |
|
||||||
|
| estimated_cost | NUMERIC |
|
||||||
|
| model | VARCHAR(256) |
|
||||||
|
| started_at | TIMESTAMP |
|
||||||
|
| last_activity_at | TIMESTAMP |
|
||||||
|
|
||||||
|
### tasks
|
||||||
|
| Column | Type |
|
||||||
|
|--------|------|
|
||||||
|
| id | UUID PK |
|
||||||
|
| agent_id | UUID FK → agents |
|
||||||
|
| title | VARCHAR(512) |
|
||||||
|
| description | TEXT |
|
||||||
|
| status | VARCHAR(32) |
|
||||||
|
| progress | INTEGER DEFAULT 0 |
|
||||||
|
| session_key | VARCHAR(256) |
|
||||||
|
| created_at, updated_at | TIMESTAMP |
|
||||||
|
|
||||||
|
### projects
|
||||||
|
| Column | Type |
|
||||||
|
|--------|------|
|
||||||
|
| id | UUID PK |
|
||||||
|
| name | VARCHAR(256) UNIQUE |
|
||||||
|
| description | TEXT |
|
||||||
|
| status | VARCHAR(32) |
|
||||||
|
| agent_ids | TEXT[] |
|
||||||
|
| created_at, updated_at | TIMESTAMP |
|
||||||
|
|
||||||
|
## API Endpoints
|
||||||
|
|
||||||
|
| Method | Endpoint | Description |
|
||||||
|
|--------|----------|-------------|
|
||||||
|
| GET | /health | Health check (probes DB) |
|
||||||
|
| GET | /api/agents | List all agents |
|
||||||
|
| POST | /api/agents | Create agent |
|
||||||
|
| GET | /api/agents/:id | Get agent detail |
|
||||||
|
| PUT | /api/agents/:id | Update agent |
|
||||||
|
| DELETE | /api/agents/:id | Delete agent |
|
||||||
|
| GET | /api/agents/:id/history | Agent status history |
|
||||||
|
| GET | /api/sessions | List sessions |
|
||||||
|
| GET | /api/tasks | List tasks |
|
||||||
|
| GET | /api/projects | List projects |
|
||||||
|
| GET | /api/events | SSE event stream |
|
||||||
|
|
||||||
|
## SSE Events
|
||||||
|
|
||||||
|
| Event Type | Payload |
|
||||||
|
|------------|---------|
|
||||||
|
| `agent.status` | Agent status change |
|
||||||
|
| `agent.task` | Agent current task updated |
|
||||||
|
| `agent.progress` | Task progress percentage |
|
||||||
|
| `fleet.update` | Full fleet snapshot |
|
||||||
|
| `connected` | Connection established |
|
||||||
|
|
||||||
|
## CI/CD Pipeline
|
||||||
|
|
||||||
|
### dev.yml
|
||||||
|
- Lint + typecheck: Go vet + golangci-lint + tsc
|
||||||
|
- Test: Go test + vitest
|
||||||
|
- Build: Go build + npm build
|
||||||
|
- Docker: Build and push images
|
||||||
|
- Triggers: push to dev/main
|
||||||
|
|
||||||
|
### deploy-dev.yaml
|
||||||
|
- Workflow dispatch
|
||||||
|
- SCP deploy script to dev host
|
||||||
|
- systemctl restart with rollback
|
||||||
|
|
||||||
|
## Docker Compose
|
||||||
|
|
||||||
|
| Service | Image/Build | Ports | Depends On |
|
||||||
|
|---------|-------------|-------|------------|
|
||||||
|
| postgres | postgres:16-alpine | 5432 | — |
|
||||||
|
| backend | ./go-backend/Dockerfile | 8080 | postgres (healthy) |
|
||||||
|
| frontend | ./frontend/Dockerfile | 3000 | backend (healthy) |
|
||||||
|
|
||||||
|
## Linear Issue Map
|
||||||
|
|
||||||
|
| CUB | Title | Status |
|
||||||
|
|-----|-------|--------|
|
||||||
|
| 119 | **Epic: Control Center Refactor — .NET → Go + React** | Todo |
|
||||||
|
| 120 | PostgreSQL schema + migrations | Done |
|
||||||
|
| 121 | React pages wired to real API | Done |
|
||||||
|
| 122 | React frontend scaffold | Done |
|
||||||
|
| 123 | Gateway integration + SSE streaming | Done |
|
||||||
|
| 124 | Go backend scaffold | Done |
|
||||||
|
| 125 | Real-time SSE frontend | Done |
|
||||||
|
| 126 | Docker Compose deployment | Done |
|
||||||
|
| 127 | CRUD API endpoints | Done |
|
||||||
|
| 200 | Live WebSocket gateway client (CUB-200-207 sub-epic) | In Review |
|
||||||
|
| 201 | agents.list + sessions.list RPC and data mapping | In Review |
|
||||||
|
| 202 | Real-time event subscription + SSE fan-out | In Review |
|
||||||
|
| 203 | WS client scaffold — handshake, connect, reconnect loop | In Review |
|
||||||
|
| 204 | Config, wiring, and graceful fallback | In Review |
|
||||||
|
| 205 | Unit tests — gateway utility functions | In Review |
|
||||||
|
| 206 | Unit tests — WSClient handshake, Send/RPC, frame router, reconnect | In Review |
|
||||||
|
| 207 | Unit tests — event handlers and initial sync | In Review |
|
||||||
|
|
||||||
|
### Legacy Issues (Angular/ASP.NET — all Done)
|
||||||
|
CUB-19 through CUB-63: All 27 Control Center issues completed, including minion mapping, breakroom UI, dark mode theme, agent cards, quick-jump drawer, adaptive nav, SignalR hub, and status animations.
|
||||||
|
|
||||||
|
## Known Limitations / Next Steps
|
||||||
|
|
||||||
|
1. Agent detail/history views are scaffolded but not fully implemented
|
||||||
|
2. 16-bit minion breakroom concept (CUB-59-63) was on Angular — needs React port if desired
|
||||||
|
3. `.env` must be created from `.env.example` with a valid `GATEWAY_URL` for live agent data
|
||||||
|
4. Docker containers not currently running — start with `docker compose up --build -d`
|
||||||
|
|
||||||
|
## Default Agent Assignments
|
||||||
|
|
||||||
|
| Area | Agent | Notes |
|
||||||
|
|------|-------|-------|
|
||||||
|
| Backend (Go API, Gateway WS, SSE) | Dex | gitea-dex MCP |
|
||||||
|
| Database (PostgreSQL schema) | Hex | gitea-hex MCP |
|
||||||
|
| Frontend (React, Tailwind) | Rex | gitea-rex MCP |
|
||||||
|
| Design (wireframes, UX) | Sketch | |
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /mnt/ai-storage/projects/Control-Center
|
||||||
|
git checkout dev
|
||||||
|
git pull origin dev
|
||||||
|
|
||||||
|
# Docker Compose (recommended)
|
||||||
|
cp .env.example .env # edit GATEWAY_URL first
|
||||||
|
docker compose up --build -d
|
||||||
|
|
||||||
|
# Manual
|
||||||
|
cd go-backend && go run cmd/server/main.go # → :8080
|
||||||
|
cd frontend && npm install && npm run dev # → :5173 (Vite proxy to :8080)
|
||||||
|
```
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files.
|
||||||
|
|
||||||
|
# Compiled output
|
||||||
|
/dist
|
||||||
|
/tmp
|
||||||
|
/out-tsc
|
||||||
|
/bazel-out
|
||||||
|
|
||||||
|
# Node
|
||||||
|
/node_modules
|
||||||
|
npm-debug.log
|
||||||
|
yarn-error.log
|
||||||
|
|
||||||
|
# IDEs and editors
|
||||||
|
.idea/
|
||||||
|
.project
|
||||||
|
.classpath
|
||||||
|
.c9/
|
||||||
|
*.launch
|
||||||
|
.settings/
|
||||||
|
*.sublime-workspace
|
||||||
|
|
||||||
|
# Visual Studio Code
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
!.vscode/mcp.json
|
||||||
|
.history/*
|
||||||
|
|
||||||
|
# Miscellaneous
|
||||||
|
/.angular/cache
|
||||||
|
.sass-cache/
|
||||||
|
/connect.lock
|
||||||
|
/coverage
|
||||||
|
/libpeerconnection.log
|
||||||
|
testem.log
|
||||||
|
/typings
|
||||||
|
__screenshots__/
|
||||||
|
|
||||||
|
# System files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
# ============================================================
|
||||||
|
# Control Center Frontend — Multi-stage Docker Build
|
||||||
|
# Angular 21 + nginx for static serving + API proxy
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
# --- Build Stage ---
|
||||||
|
FROM node:22-slim AS builder
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install dependencies first (layer caching)
|
||||||
|
COPY package.json package-lock.json ./
|
||||||
|
RUN npm ci
|
||||||
|
|
||||||
|
# Copy source and build production bundle
|
||||||
|
COPY . .
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# --- Runtime Stage ---
|
||||||
|
FROM nginx:1.27-alpine AS runtime
|
||||||
|
|
||||||
|
# Remove default nginx config
|
||||||
|
RUN rm /etc/nginx/conf.d/default.conf
|
||||||
|
|
||||||
|
# Copy custom nginx config
|
||||||
|
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
|
||||||
|
# Copy built Angular app from builder stage
|
||||||
|
COPY --from=builder /app/dist/frontend/browser /usr/share/nginx/html
|
||||||
|
|
||||||
|
# Expose HTTP port
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
|
# Health check — confirm nginx is serving
|
||||||
|
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||||
|
CMD wget -qO- http://localhost/ || exit 1
|
||||||
|
|
||||||
|
CMD ["nginx", "-g", "daemon off;"]
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
# Frontend
|
||||||
|
|
||||||
|
This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 21.2.8.
|
||||||
|
|
||||||
|
## Development server
|
||||||
|
|
||||||
|
To start a local development server, run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ng serve
|
||||||
|
```
|
||||||
|
|
||||||
|
Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files.
|
||||||
|
|
||||||
|
## Code scaffolding
|
||||||
|
|
||||||
|
Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ng generate component component-name
|
||||||
|
```
|
||||||
|
|
||||||
|
For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ng generate --help
|
||||||
|
```
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
To build the project run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ng build
|
||||||
|
```
|
||||||
|
|
||||||
|
This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed.
|
||||||
|
|
||||||
|
## Running unit tests
|
||||||
|
|
||||||
|
To execute unit tests with the [Vitest](https://vitest.dev/) test runner, use the following command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ng test
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running end-to-end tests
|
||||||
|
|
||||||
|
For end-to-end (e2e) testing, run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ng e2e
|
||||||
|
```
|
||||||
|
|
||||||
|
Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
|
||||||
|
|
||||||
|
## Additional Resources
|
||||||
|
|
||||||
|
For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
# Gzip compression
|
||||||
|
gzip on;
|
||||||
|
gzip_vary on;
|
||||||
|
gzip_proxied any;
|
||||||
|
gzip_comp_level 6;
|
||||||
|
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml;
|
||||||
|
|
||||||
|
# Cache static assets (Angular uses content hashes)
|
||||||
|
location /assets/ {
|
||||||
|
expires 1y;
|
||||||
|
add_header Cache-Control "public, immutable";
|
||||||
|
}
|
||||||
|
|
||||||
|
# Cache hashed JS/CSS bundles
|
||||||
|
location ~* \.(js|css)$ {
|
||||||
|
expires 1y;
|
||||||
|
add_header Cache-Control "public, immutable";
|
||||||
|
}
|
||||||
|
|
||||||
|
# Proxy API requests to backend
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://backend:8080/api/;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
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_set_header X-Forwarded-Proto $scheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Proxy SignalR WebSocket connections to backend
|
||||||
|
location /hubs/ {
|
||||||
|
proxy_pass http://backend:8080/hubs/;
|
||||||
|
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_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_read_timeout 86400;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Angular SPA — all other routes fall back to index.html
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
}
|
||||||
Generated
+8034
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"name": "frontend",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"scripts": {
|
||||||
|
"ng": "ng",
|
||||||
|
"start": "ng serve",
|
||||||
|
"build": "ng build",
|
||||||
|
"watch": "ng build --watch --configuration development",
|
||||||
|
"test": "ng test"
|
||||||
|
},
|
||||||
|
"private": true,
|
||||||
|
"packageManager": "npm@11.11.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@angular/animations": "^21.2.10",
|
||||||
|
"@angular/cdk": "^21.2.8",
|
||||||
|
"@angular/common": "^21.2.0",
|
||||||
|
"@angular/compiler": "^21.2.0",
|
||||||
|
"@angular/core": "^21.2.0",
|
||||||
|
"@angular/forms": "^21.2.0",
|
||||||
|
"@angular/material": "^21.2.8",
|
||||||
|
"@angular/platform-browser": "^21.2.0",
|
||||||
|
"@angular/router": "^21.2.0",
|
||||||
|
"@microsoft/signalr": "^10.0.0",
|
||||||
|
"rxjs": "~7.8.0",
|
||||||
|
"tslib": "^2.3.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@angular/build": "^21.2.8",
|
||||||
|
"@angular/cli": "^21.2.8",
|
||||||
|
"@angular/compiler-cli": "^21.2.0",
|
||||||
|
"prettier": "^3.8.1",
|
||||||
|
"typescript": "~5.9.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,15 @@
|
|||||||
|
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
|
||||||
|
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
|
||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "./out-tsc/app",
|
||||||
|
"types": []
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*.ts"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"src/**/*.spec.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
|
||||||
|
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
|
||||||
|
{
|
||||||
|
"compileOnSave": false,
|
||||||
|
"compilerOptions": {
|
||||||
|
"strict": true,
|
||||||
|
"noImplicitOverride": true,
|
||||||
|
"noPropertyAccessFromIndexSignature": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"importHelpers": true,
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "preserve"
|
||||||
|
},
|
||||||
|
"angularCompilerOptions": {
|
||||||
|
"enableI18nLegacyMessageIdFormat": false,
|
||||||
|
"strictInjectionParameters": true,
|
||||||
|
"strictInputAccessModifiers": true,
|
||||||
|
"strictTemplates": true
|
||||||
|
},
|
||||||
|
"files": [],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.app.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user