diff --git a/dashboard/.env b/app/.env similarity index 100% rename from dashboard/.env rename to app/.env diff --git a/dashboard/.gitignore b/app/.gitignore similarity index 100% rename from dashboard/.gitignore rename to app/.gitignore diff --git a/dashboard/README.md b/app/README.md similarity index 100% rename from dashboard/README.md rename to app/README.md diff --git a/dashboard/eslint.config.js b/app/eslint.config.js similarity index 100% rename from dashboard/eslint.config.js rename to app/eslint.config.js diff --git a/dashboard/index.html b/app/index.html similarity index 100% rename from dashboard/index.html rename to app/index.html diff --git a/dashboard/package.json b/app/package.json similarity index 97% rename from dashboard/package.json rename to app/package.json index eca0f14..368c33b 100644 --- a/dashboard/package.json +++ b/app/package.json @@ -1,5 +1,5 @@ { - "name": "dashboard", + "name": "app", "private": true, "version": "0.0.0", "type": "module", diff --git a/dashboard/pnpm-lock.yaml b/app/pnpm-lock.yaml similarity index 100% rename from dashboard/pnpm-lock.yaml rename to app/pnpm-lock.yaml diff --git a/dashboard/src/App.tsx b/app/src/App.tsx similarity index 100% rename from dashboard/src/App.tsx rename to app/src/App.tsx diff --git a/dashboard/src/components/AddComponent.tsx b/app/src/components/AddComponent.tsx similarity index 100% rename from dashboard/src/components/AddComponent.tsx rename to app/src/components/AddComponent.tsx diff --git a/dashboard/src/components/ComponentCard.tsx b/app/src/components/ComponentCard.tsx similarity index 100% rename from dashboard/src/components/ComponentCard.tsx rename to app/src/components/ComponentCard.tsx diff --git a/dashboard/src/components/ComponentEditor.tsx b/app/src/components/ComponentEditor.tsx similarity index 100% rename from dashboard/src/components/ComponentEditor.tsx rename to app/src/components/ComponentEditor.tsx diff --git a/dashboard/src/components/ComponentFields.tsx b/app/src/components/ComponentFields.tsx similarity index 100% rename from dashboard/src/components/ComponentFields.tsx rename to app/src/components/ComponentFields.tsx diff --git a/dashboard/src/components/ComponentGrid.tsx b/app/src/components/ComponentGrid.tsx similarity index 100% rename from dashboard/src/components/ComponentGrid.tsx rename to app/src/components/ComponentGrid.tsx diff --git a/dashboard/src/components/ComponentTable.tsx b/app/src/components/ComponentTable.tsx similarity index 100% rename from dashboard/src/components/ComponentTable.tsx rename to app/src/components/ComponentTable.tsx diff --git a/dashboard/src/components/HealthBadge.tsx b/app/src/components/HealthBadge.tsx similarity index 100% rename from dashboard/src/components/HealthBadge.tsx rename to app/src/components/HealthBadge.tsx diff --git a/dashboard/src/components/LogViewer.tsx b/app/src/components/LogViewer.tsx similarity index 100% rename from dashboard/src/components/LogViewer.tsx rename to app/src/components/LogViewer.tsx diff --git a/dashboard/src/components/RoleBadge.tsx b/app/src/components/RoleBadge.tsx similarity index 100% rename from dashboard/src/components/RoleBadge.tsx rename to app/src/components/RoleBadge.tsx diff --git a/dashboard/src/components/SecretsEditor.tsx b/app/src/components/SecretsEditor.tsx similarity index 100% rename from dashboard/src/components/SecretsEditor.tsx rename to app/src/components/SecretsEditor.tsx diff --git a/dashboard/src/components/ToolCard.tsx b/app/src/components/ToolCard.tsx similarity index 100% rename from dashboard/src/components/ToolCard.tsx rename to app/src/components/ToolCard.tsx diff --git a/dashboard/src/index.css b/app/src/index.css similarity index 100% rename from dashboard/src/index.css rename to app/src/index.css diff --git a/dashboard/src/lib/labels.ts b/app/src/lib/labels.ts similarity index 100% rename from dashboard/src/lib/labels.ts rename to app/src/lib/labels.ts diff --git a/dashboard/src/lib/queryClient.ts b/app/src/lib/queryClient.ts similarity index 100% rename from dashboard/src/lib/queryClient.ts rename to app/src/lib/queryClient.ts diff --git a/dashboard/src/lib/utils.ts b/app/src/lib/utils.ts similarity index 100% rename from dashboard/src/lib/utils.ts rename to app/src/lib/utils.ts diff --git a/dashboard/src/main.tsx b/app/src/main.tsx similarity index 100% rename from dashboard/src/main.tsx rename to app/src/main.tsx diff --git a/dashboard/src/pages/ComponentDetail.tsx b/app/src/pages/ComponentDetail.tsx similarity index 100% rename from dashboard/src/pages/ComponentDetail.tsx rename to app/src/pages/ComponentDetail.tsx diff --git a/dashboard/src/pages/ConfigEditor.tsx b/app/src/pages/ConfigEditor.tsx similarity index 100% rename from dashboard/src/pages/ConfigEditor.tsx rename to app/src/pages/ConfigEditor.tsx diff --git a/dashboard/src/pages/Dashboard.tsx b/app/src/pages/Dashboard.tsx similarity index 100% rename from dashboard/src/pages/Dashboard.tsx rename to app/src/pages/Dashboard.tsx diff --git a/dashboard/src/pages/Tools.tsx b/app/src/pages/Tools.tsx similarity index 100% rename from dashboard/src/pages/Tools.tsx rename to app/src/pages/Tools.tsx diff --git a/dashboard/src/router/routes.tsx b/app/src/router/routes.tsx similarity index 100% rename from dashboard/src/router/routes.tsx rename to app/src/router/routes.tsx diff --git a/dashboard/src/services/api/client.ts b/app/src/services/api/client.ts similarity index 100% rename from dashboard/src/services/api/client.ts rename to app/src/services/api/client.ts diff --git a/dashboard/src/services/api/hooks.ts b/app/src/services/api/hooks.ts similarity index 100% rename from dashboard/src/services/api/hooks.ts rename to app/src/services/api/hooks.ts diff --git a/dashboard/src/types/index.ts b/app/src/types/index.ts similarity index 100% rename from dashboard/src/types/index.ts rename to app/src/types/index.ts diff --git a/dashboard/src/vite-env.d.ts b/app/src/vite-env.d.ts similarity index 100% rename from dashboard/src/vite-env.d.ts rename to app/src/vite-env.d.ts diff --git a/dashboard/tsconfig.app.json b/app/tsconfig.app.json similarity index 100% rename from dashboard/tsconfig.app.json rename to app/tsconfig.app.json diff --git a/dashboard/tsconfig.json b/app/tsconfig.json similarity index 100% rename from dashboard/tsconfig.json rename to app/tsconfig.json diff --git a/dashboard/tsconfig.node.json b/app/tsconfig.node.json similarity index 100% rename from dashboard/tsconfig.node.json rename to app/tsconfig.node.json diff --git a/dashboard/vite.config.ts b/app/vite.config.ts similarity index 100% rename from dashboard/vite.config.ts rename to app/vite.config.ts diff --git a/castle-api/CLAUDE.md b/castle-api/CLAUDE.md new file mode 100644 index 0000000..81b321e --- /dev/null +++ b/castle-api/CLAUDE.md @@ -0,0 +1,31 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working +with code in this repository. + +## Overview + +castle-api is a FastAPI service. Castle API. + +## Commands + +```bash +uv sync # Install dependencies +uv run castle-api # Run service (port 9020) +uv run pytest tests/ -v # Run tests +uv run ruff check . # Lint +uv run ruff format . # Format +``` + +## Architecture + +- `src/castle_api/config.py` — Settings via pydantic-settings, env prefix `CASTLE_API_` +- `src/castle_api/main.py` — FastAPI app, lifespan, health endpoint +- `tests/` — pytest with TestClient fixtures + +## Configuration + +Environment variables with `CASTLE_API_` prefix: +- `CASTLE_API_DATA_DIR` — Data directory (default: ./data) +- `CASTLE_API_HOST` — Bind host (default: 0.0.0.0) +- `CASTLE_API_PORT` — Port (default: 9020) diff --git a/dashboard-api/pyproject.toml b/castle-api/pyproject.toml similarity index 75% rename from dashboard-api/pyproject.toml rename to castle-api/pyproject.toml index f1bdad2..a57bd62 100644 --- a/dashboard-api/pyproject.toml +++ b/castle-api/pyproject.toml @@ -1,7 +1,7 @@ [project] -name = "dashboard-api" +name = "castle-api" version = "0.1.0" -description = "Castle dashboard API" +description = "Castle API" requires-python = ">=3.13" dependencies = [ "fastapi>=0.115.0", @@ -15,14 +15,14 @@ dependencies = [ castle-cli = { path = "../cli", editable = true } [project.scripts] -dashboard-api = "dashboard_api.main:run" +castle-api = "castle_api.main:run" [build-system] requires = ["hatchling"] build-backend = "hatchling.build" [tool.hatch.build.targets.wheel] -packages = ["src/dashboard_api"] +packages = ["src/castle_api"] [dependency-groups] dev = [ @@ -33,4 +33,4 @@ dev = [ ] [tool.ruff.lint.isort] -known-first-party = ["dashboard_api"] +known-first-party = ["castle_api"] diff --git a/castle-api/src/castle_api/__init__.py b/castle-api/src/castle_api/__init__.py new file mode 100644 index 0000000..2fee689 --- /dev/null +++ b/castle-api/src/castle_api/__init__.py @@ -0,0 +1,3 @@ +"""Castle API.""" + +__version__ = "0.1.0" diff --git a/dashboard-api/src/dashboard_api/bus.py b/castle-api/src/castle_api/bus.py similarity index 100% rename from dashboard-api/src/dashboard_api/bus.py rename to castle-api/src/castle_api/bus.py diff --git a/dashboard-api/src/dashboard_api/config.py b/castle-api/src/castle_api/config.py similarity index 81% rename from dashboard-api/src/dashboard_api/config.py rename to castle-api/src/castle_api/config.py index fc2fca9..3c50ec1 100644 --- a/dashboard-api/src/dashboard_api/config.py +++ b/castle-api/src/castle_api/config.py @@ -1,4 +1,4 @@ -"""Configuration for dashboard-api.""" +"""Configuration for castle-api.""" from pathlib import Path @@ -13,7 +13,7 @@ class Settings(BaseSettings): port: int = 9020 model_config = { - "env_prefix": "DASHBOARD_API_", + "env_prefix": "CASTLE_API_", "env_file": ".env", } diff --git a/dashboard-api/src/dashboard_api/config_editor.py b/castle-api/src/castle_api/config_editor.py similarity index 98% rename from dashboard-api/src/dashboard_api/config_editor.py rename to castle-api/src/castle_api/config_editor.py index eac5cfd..0fb666f 100644 --- a/dashboard-api/src/dashboard_api/config_editor.py +++ b/castle-api/src/castle_api/config_editor.py @@ -12,8 +12,8 @@ from pydantic import BaseModel from castle_cli.config import load_config, save_config from castle_cli.manifest import ComponentManifest -from dashboard_api.config import settings -from dashboard_api.stream import broadcast +from castle_api.config import settings +from castle_api.stream import broadcast router = APIRouter(prefix="/config", tags=["config"]) diff --git a/dashboard-api/src/dashboard_api/events.py b/castle-api/src/castle_api/events.py similarity index 97% rename from dashboard-api/src/dashboard_api/events.py rename to castle-api/src/castle_api/events.py index 21d50dd..a23ebf2 100644 --- a/dashboard-api/src/dashboard_api/events.py +++ b/castle-api/src/castle_api/events.py @@ -5,7 +5,7 @@ from __future__ import annotations from fastapi import APIRouter from pydantic import BaseModel -from dashboard_api.bus import bus +from castle_api.bus import bus router = APIRouter(prefix="/events", tags=["events"]) diff --git a/dashboard-api/src/dashboard_api/health.py b/castle-api/src/castle_api/health.py similarity index 97% rename from dashboard-api/src/dashboard_api/health.py rename to castle-api/src/castle_api/health.py index e367ebd..e70900a 100644 --- a/dashboard-api/src/dashboard_api/health.py +++ b/castle-api/src/castle_api/health.py @@ -9,7 +9,7 @@ import httpx from castle_cli.config import CastleConfig -from dashboard_api.models import HealthStatus +from castle_api.models import HealthStatus async def check_all_health(config: CastleConfig) -> list[HealthStatus]: diff --git a/dashboard-api/src/dashboard_api/logs.py b/castle-api/src/castle_api/logs.py similarity index 98% rename from dashboard-api/src/dashboard_api/logs.py rename to castle-api/src/castle_api/logs.py index 1b0195b..abd99a7 100644 --- a/dashboard-api/src/dashboard_api/logs.py +++ b/castle-api/src/castle_api/logs.py @@ -10,7 +10,7 @@ from starlette.responses import StreamingResponse from castle_cli.config import load_config -from dashboard_api.config import settings +from castle_api.config import settings router = APIRouter(prefix="/logs", tags=["logs"]) diff --git a/dashboard-api/src/dashboard_api/main.py b/castle-api/src/castle_api/main.py similarity index 79% rename from dashboard-api/src/dashboard_api/main.py rename to castle-api/src/castle_api/main.py index fdb6b3c..e9ebeed 100644 --- a/dashboard-api/src/dashboard_api/main.py +++ b/castle-api/src/castle_api/main.py @@ -11,16 +11,16 @@ from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from starlette.responses import StreamingResponse -from dashboard_api.bus import bus -from dashboard_api.config import settings -from dashboard_api.config_editor import router as config_router -from dashboard_api.events import router as events_router -from dashboard_api.logs import router as logs_router -from dashboard_api.routes import router as dashboard_router -from dashboard_api.secrets import router as secrets_router -from dashboard_api.services import router as services_router -from dashboard_api.stream import close_all_subscribers, health_poll_loop, subscribe, unsubscribe -from dashboard_api.tools import router as tools_router +from castle_api.bus import bus +from castle_api.config import settings +from castle_api.config_editor import router as config_router +from castle_api.events import router as events_router +from castle_api.logs import router as logs_router +from castle_api.routes import router as dashboard_router +from castle_api.secrets import router as secrets_router +from castle_api.services import router as services_router +from castle_api.stream import close_all_subscribers, health_poll_loop, subscribe, unsubscribe +from castle_api.tools import router as tools_router # Set by _watch_shutdown when uvicorn begins its shutdown sequence. _shutting_down = False @@ -53,8 +53,8 @@ async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]: app = FastAPI( - title="Castle API", - description="Castle dashboard API, event bus, and service management", + title="castle-api", + description="Castle API, event bus, and service management", version="0.1.0", lifespan=lifespan, ) @@ -109,7 +109,7 @@ async def sse_stream() -> StreamingResponse: def run() -> None: """Run the application with uvicorn.""" config = uvicorn.Config( - "dashboard_api.main:app", + "castle_api.main:app", host=settings.host, port=settings.port, reload=False, diff --git a/dashboard-api/src/dashboard_api/models.py b/castle-api/src/castle_api/models.py similarity index 100% rename from dashboard-api/src/dashboard_api/models.py rename to castle-api/src/castle_api/models.py diff --git a/dashboard-api/src/dashboard_api/routes.py b/castle-api/src/castle_api/routes.py similarity index 97% rename from dashboard-api/src/dashboard_api/routes.py rename to castle-api/src/castle_api/routes.py index 0f3f51f..49f445f 100644 --- a/dashboard-api/src/dashboard_api/routes.py +++ b/castle-api/src/castle_api/routes.py @@ -9,9 +9,9 @@ from fastapi import APIRouter, HTTPException, status from castle_cli.config import load_config -from dashboard_api.config import settings -from dashboard_api.health import check_all_health -from dashboard_api.models import ( +from castle_api.config import settings +from castle_api.health import check_all_health +from castle_api.models import ( ComponentDetail, ComponentSummary, GatewayInfo, diff --git a/dashboard-api/src/dashboard_api/secrets.py b/castle-api/src/castle_api/secrets.py similarity index 100% rename from dashboard-api/src/dashboard_api/secrets.py rename to castle-api/src/castle_api/secrets.py diff --git a/dashboard-api/src/dashboard_api/services.py b/castle-api/src/castle_api/services.py similarity index 95% rename from dashboard-api/src/dashboard_api/services.py rename to castle-api/src/castle_api/services.py index 652f0da..10b9e22 100644 --- a/dashboard-api/src/dashboard_api/services.py +++ b/castle-api/src/castle_api/services.py @@ -10,15 +10,15 @@ from starlette.responses import JSONResponse from castle_cli.config import load_config -from dashboard_api.config import settings -from dashboard_api.health import check_all_health -from dashboard_api.models import HealthStatus -from dashboard_api.stream import broadcast +from castle_api.config import settings +from castle_api.health import check_all_health +from castle_api.models import HealthStatus +from castle_api.stream import broadcast router = APIRouter(prefix="/services", tags=["services"]) UNIT_PREFIX = "castle-" -SELF_NAME = "dashboard-api" +SELF_NAME = "castle-api" async def _systemctl(action: str, unit: str) -> tuple[bool, str]: diff --git a/dashboard-api/src/dashboard_api/stream.py b/castle-api/src/castle_api/stream.py similarity index 95% rename from dashboard-api/src/dashboard_api/stream.py rename to castle-api/src/castle_api/stream.py index 2deedc6..a293220 100644 --- a/dashboard-api/src/dashboard_api/stream.py +++ b/castle-api/src/castle_api/stream.py @@ -9,8 +9,8 @@ import time from castle_cli.config import load_config -from dashboard_api.config import settings -from dashboard_api.health import check_all_health +from castle_api.config import settings +from castle_api.health import check_all_health logger = logging.getLogger(__name__) diff --git a/dashboard-api/src/dashboard_api/tools.py b/castle-api/src/castle_api/tools.py similarity index 98% rename from dashboard-api/src/dashboard_api/tools.py rename to castle-api/src/castle_api/tools.py index c66ac1b..0877194 100644 --- a/dashboard-api/src/dashboard_api/tools.py +++ b/castle-api/src/castle_api/tools.py @@ -10,8 +10,8 @@ from fastapi import APIRouter, HTTPException, status from castle_cli.config import load_config from castle_cli.manifest import ComponentManifest -from dashboard_api.config import settings -from dashboard_api.models import ToolCategory, ToolDetail, ToolSummary +from castle_api.config import settings +from castle_api.models import ToolCategory, ToolDetail, ToolSummary router = APIRouter(tags=["tools"]) diff --git a/dashboard-api/tests/__init__.py b/castle-api/tests/__init__.py similarity index 100% rename from dashboard-api/tests/__init__.py rename to castle-api/tests/__init__.py diff --git a/dashboard-api/tests/conftest.py b/castle-api/tests/conftest.py similarity index 94% rename from dashboard-api/tests/conftest.py rename to castle-api/tests/conftest.py index d1bcb6a..04e10de 100644 --- a/dashboard-api/tests/conftest.py +++ b/castle-api/tests/conftest.py @@ -1,4 +1,4 @@ -"""Test fixtures for dashboard-api.""" +"""Test fixtures for castle-api.""" from collections.abc import Generator from pathlib import Path @@ -7,8 +7,8 @@ import pytest import yaml from fastapi.testclient import TestClient -from dashboard_api.config import settings -from dashboard_api.main import app +from castle_api.config import settings +from castle_api.main import app @pytest.fixture diff --git a/dashboard-api/tests/test_health.py b/castle-api/tests/test_health.py similarity index 98% rename from dashboard-api/tests/test_health.py rename to castle-api/tests/test_health.py index c407d2d..345fe8e 100644 --- a/dashboard-api/tests/test_health.py +++ b/castle-api/tests/test_health.py @@ -1,4 +1,4 @@ -"""Tests for dashboard-api health endpoint.""" +"""Tests for castle-api health endpoint.""" from fastapi.testclient import TestClient diff --git a/dashboard-api/tests/test_tools.py b/castle-api/tests/test_tools.py similarity index 100% rename from dashboard-api/tests/test_tools.py rename to castle-api/tests/test_tools.py diff --git a/dashboard-api/uv.lock b/castle-api/uv.lock similarity index 99% rename from dashboard-api/uv.lock rename to castle-api/uv.lock index f959776..07cd083 100644 --- a/dashboard-api/uv.lock +++ b/castle-api/uv.lock @@ -32,6 +32,43 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592, upload-time = "2026-01-06T11:45:19.497Z" }, ] +[[package]] +name = "castle-api" +version = "0.1.0" +source = { editable = "." } +dependencies = [ + { name = "castle-cli" }, + { name = "fastapi" }, + { name = "httpx" }, + { name = "pydantic-settings" }, + { name = "uvicorn" }, +] + +[package.dev-dependencies] +dev = [ + { name = "httpx" }, + { name = "pytest" }, + { name = "pytest-asyncio" }, + { name = "pyyaml" }, +] + +[package.metadata] +requires-dist = [ + { name = "castle-cli", editable = "../cli" }, + { name = "fastapi", specifier = ">=0.115.0" }, + { name = "httpx", specifier = ">=0.27.0" }, + { name = "pydantic-settings", specifier = ">=2.0.0" }, + { name = "uvicorn", specifier = ">=0.34.0" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "httpx", specifier = ">=0.27.0" }, + { name = "pytest", specifier = ">=7.0.0" }, + { name = "pytest-asyncio", specifier = ">=0.23.0" }, + { name = "pyyaml", specifier = ">=6.0.0" }, +] + [[package]] name = "castle-cli" version = "0.1.0" @@ -86,43 +123,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] -[[package]] -name = "dashboard-api" -version = "0.1.0" -source = { editable = "." } -dependencies = [ - { name = "castle-cli" }, - { name = "fastapi" }, - { name = "httpx" }, - { name = "pydantic-settings" }, - { name = "uvicorn" }, -] - -[package.dev-dependencies] -dev = [ - { name = "httpx" }, - { name = "pytest" }, - { name = "pytest-asyncio" }, - { name = "pyyaml" }, -] - -[package.metadata] -requires-dist = [ - { name = "castle-cli", editable = "../cli" }, - { name = "fastapi", specifier = ">=0.115.0" }, - { name = "httpx", specifier = ">=0.27.0" }, - { name = "pydantic-settings", specifier = ">=2.0.0" }, - { name = "uvicorn", specifier = ">=0.34.0" }, -] - -[package.metadata.requires-dev] -dev = [ - { name = "httpx", specifier = ">=0.27.0" }, - { name = "pytest", specifier = ">=7.0.0" }, - { name = "pytest-asyncio", specifier = ">=0.23.0" }, - { name = "pyyaml", specifier = ">=6.0.0" }, -] - [[package]] name = "fastapi" version = "0.129.0" diff --git a/castle.yaml b/castle.yaml index 6b86326..79da175 100644 --- a/castle.yaml +++ b/castle.yaml @@ -1,8 +1,7 @@ gateway: port: 9000 components: - # ── Infrastructure ────────────────────────────────────── - gateway: + castle-gateway: description: Caddy reverse proxy gateway run: runner: command @@ -14,16 +13,15 @@ components: - /home/payne/.castle/generated/Caddyfile - --adapter - caddyfile + manage: + systemd: + exec_reload: caddy reload --config /home/payne/.castle/generated/Caddyfile + --adapter caddyfile expose: http: internal: port: 9000 health_path: / - manage: - systemd: - exec_reload: caddy reload --config /home/payne/.castle/generated/Caddyfile --adapter caddyfile - - # ── Services ────────────────────────────────────────────── central-context: description: Content storage API run: @@ -44,7 +42,9 @@ components: caddy: path_prefix: /central-context notification-bridge: - description: Desktop notification forwarder + description: Desktop notification forwarder. This taps into your notifications + system on the desktop and will forward all notifications the the central context + server. run: runner: python_uv_tool cwd: notification-bridge @@ -63,14 +63,14 @@ components: proxy: caddy: path_prefix: /notifications - dashboard-api: - description: Castle dashboard API + castle-api: + description: Castle API run: runner: python_uv_tool - cwd: dashboard-api + cwd: castle-api env: - DASHBOARD_API_CASTLE_ROOT: /data/repos/castle - tool: dashboard-api + CASTLE_API_CASTLE_ROOT: /data/repos/castle + tool: castle-api manage: systemd: {} expose: @@ -137,10 +137,10 @@ components: manage: systemd: {} tool: {} - dashboard: - description: Castle web dashboard + castle-app: + description: Castle web app build: - working_dir: dashboard + working_dir: app commands: - - pnpm - build diff --git a/cli/src/castle_cli/commands/gateway.py b/cli/src/castle_cli/commands/gateway.py index 789106e..96b58b5 100644 --- a/cli/src/castle_cli/commands/gateway.py +++ b/cli/src/castle_cli/commands/gateway.py @@ -8,13 +8,13 @@ import subprocess from castle_cli.config import GENERATED_DIR, CastleConfig, ensure_dirs, load_config -GATEWAY_COMPONENT = "gateway" +GATEWAY_COMPONENT = "castle-gateway" GATEWAY_UNIT = "castle-gateway.service" -def _find_dashboard_dist(config: CastleConfig) -> str | None: - """Find the dashboard dist/ directory if it exists.""" - dist = config.root / "dashboard" / "dist" +def _find_app_dist(config: CastleConfig) -> str | None: + """Find the app dist/ directory if it exists.""" + dist = config.root / "app" / "dist" if dist.exists() and (dist / "index.html").exists(): return str(dist) return None @@ -42,17 +42,17 @@ def _generate_caddyfile(config: CastleConfig) -> str: lines.append(" }") lines.append("") - # Dashboard SPA at root (must come after more-specific handle_path rules) - dashboard_dist = _find_dashboard_dist(config) - if dashboard_dist: + # App SPA at root (must come after more-specific handle_path rules) + app_dist = _find_app_dist(config) + if app_dist: lines.append(" handle {") - lines.append(f" root * {dashboard_dist}") + lines.append(f" root * {app_dist}") lines.append(" try_files {path} /index.html") lines.append(" file_server") lines.append(" }") else: # Fallback: serve from generated directory - fallback = GENERATED_DIR / "dashboard" + fallback = GENERATED_DIR / "app" lines.append(" handle / {") lines.append(f" root * {fallback}") lines.append(" file_server") @@ -70,11 +70,11 @@ def _write_generated_files(config: CastleConfig) -> None: caddyfile_path.write_text(_generate_caddyfile(config)) print(f" Generated {caddyfile_path}") - dashboard_dist = _find_dashboard_dist(config) - if dashboard_dist: - print(f" Dashboard: {dashboard_dist}") + app_dist = _find_app_dist(config) + if app_dist: + print(f" App: {app_dist}") else: - print(" Dashboard: dist/ not found, using fallback") + print(" App: dist/ not found, using fallback") def run_gateway(args: argparse.Namespace) -> int: diff --git a/dashboard-api/CLAUDE.md b/dashboard-api/CLAUDE.md deleted file mode 100644 index 4fafa51..0000000 --- a/dashboard-api/CLAUDE.md +++ /dev/null @@ -1,31 +0,0 @@ -# CLAUDE.md - -This file provides guidance to Claude Code (claude.ai/code) when working -with code in this repository. - -## Overview - -dashboard-api is a FastAPI service. Castle dashboard API. - -## Commands - -```bash -uv sync # Install dependencies -uv run dashboard-api # Run service (port 9020) -uv run pytest tests/ -v # Run tests -uv run ruff check . # Lint -uv run ruff format . # Format -``` - -## Architecture - -- `src/dashboard_api/config.py` — Settings via pydantic-settings, env prefix `DASHBOARD_API_` -- `src/dashboard_api/main.py` — FastAPI app, lifespan, health endpoint -- `tests/` — pytest with TestClient fixtures - -## Configuration - -Environment variables with `DASHBOARD_API_` prefix: -- `DASHBOARD_API_DATA_DIR` — Data directory (default: ./data) -- `DASHBOARD_API_HOST` — Bind host (default: 0.0.0.0) -- `DASHBOARD_API_PORT` — Port (default: 9020) diff --git a/dashboard-api/src/dashboard_api/__init__.py b/dashboard-api/src/dashboard_api/__init__.py deleted file mode 100644 index 1aa5fc8..0000000 --- a/dashboard-api/src/dashboard_api/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -"""Castle dashboard API.""" - -__version__ = "0.1.0" diff --git a/docs/web-frontends.md b/docs/web-frontends.md index bd18476..8e70978 100644 --- a/docs/web-frontends.md +++ b/docs/web-frontends.md @@ -1,23 +1,20 @@ # Web Frontends in Castle -How to build, serve, and manage web frontends as castle components. Based on -the stack used in [wild-cloud/web](https://github.com/civilsociety-dev/wild-cloud). +How to build, serve, and manage web frontends as castle components. ## Stack -| Layer | Choice | -|-------|--------| -| **Build** | Vite 6 | -| **Language** | TypeScript 5.8 (strict) | -| **Framework** | React 19 | -| **Routing** | React Router 7 | -| **Styling** | Tailwind CSS 4 (`@tailwindcss/vite` plugin) | -| **Components** | shadcn/ui (new-york style) + Radix UI primitives | -| **Icons** | Lucide React | -| **Server state** | TanStack React Query 5 | -| **Forms** | React Hook Form + Zod validation | -| **Testing** | Vitest + Testing Library | -| **Package manager** | pnpm | +- Build: Vite 6 +- Language: TypeScript 5.8 (strict) +- Framework: React 19 +- Routing: React Router 7 +- Styling: Tailwind CSS 4 (`@tailwindcss/vite` plugin) +- Components: shadcn/ui (new-york style) + Radix UI primitives +- Icons: Lucide React +- Server state: TanStack React Query 5 +- Forms: React Hook Form + Zod validation +- Testing: Vitest + Testing Library +- Package manager: pnpm ## Scaffolding a new frontend