From 845143a489dab4f0f2456cb3788a1916e161b67a Mon Sep 17 00:00:00 2001 From: xpltd_admin Date: Fri, 3 Apr 2026 22:53:38 -0600 Subject: [PATCH] Create Development-Guide wiki page for fractafrag --- Development-Guide.-.md | 229 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 229 insertions(+) create mode 100644 Development-Guide.-.md diff --git a/Development-Guide.-.md b/Development-Guide.-.md new file mode 100644 index 0000000..3baff5b --- /dev/null +++ b/Development-Guide.-.md @@ -0,0 +1,229 @@ +# Development Guide + +| Meta | Value | +|------|-------| +| **Repo** | `xpltdco/fractafrag` | +| **Page** | `Development-Guide` | +| **Audience** | developers, agents | +| **Last Updated** | 2026-04-04 | +| **Status** | current | + +## Code Organization Patterns + +### Adding a New API Endpoint + +1. **Create/edit router** in `services/api/app/routers/` — define the FastAPI route +2. **Add Pydantic schemas** in `services/api/app/schemas/schemas.py` — request/response models +3. **Register router** in `services/api/app/main.py` (if new file): `app.include_router(router, prefix="/api/v1/...")` +4. **Add auth dependency** if needed: `current_user: User = Depends(get_current_user)` +5. **Database access** via async session: `db: AsyncSession = Depends(get_db)` +6. **Add frontend API call** in the React frontend via Axios or React Query hook + +### Adding a New Database Table + +1. **Add SQL** to `db/init.sql` (for fresh installs) +2. **Add ORM model** in `services/api/app/models/models.py` using SQLAlchemy declarative +3. **Create Alembic migration** for existing databases: + ```bash + docker compose exec api alembic revision --autogenerate -m "add my_table" + docker compose exec api alembic upgrade head + ``` +4. **Add Pydantic schemas** for the new model + +### Adding a Celery Task + +1. **Define task** in `services/api/app/worker/__init__.py` +2. **Use sync DB session** (Celery runs in sync context — use `psycopg2`, not `asyncpg`) +3. **Add retry logic** if the task can fail transiently: `@celery_app.task(bind=True, max_retries=3)` +4. **Enqueue from API** router: `from app.worker import my_task; my_task.delay(args)` +5. **Add periodic schedule** if needed (Beat schedule in worker `__init__.py`) + +### Adding an MCP Tool + +1. **Add tool function** in `services/mcp/server.py` with `@mcp.tool()` decorator +2. **Call the API** internally via httpx: `POST http://api:8000/api/v1/...` +3. **Return structured data** (dict with clear field names for agent consumption) + +### Adding a Frontend Page + +1. **Create page component** in `services/frontend/src/pages/` +2. **Add route** in the React Router configuration +3. **Create API hooks** using TanStack Query: `useQuery`, `useMutation` +4. **Style with Tailwind** CSS utility classes + +## Testing + +### Test Framework + +**pytest + pytest-asyncio** for the API backend. + +### Running Tests + +```bash +# Via Docker (recommended) +make test +# or: docker compose exec api python -m pytest tests/ -v + +# Locally (requires dev deps) +cd services/api +pip install -e ".[dev]" +pytest tests/ -v +``` + +### Test Structure + +``` +services/api/tests/ +├── conftest.py # Fixtures (async client, test DB) +├── test_auth.py # Auth endpoint tests +├── test_shaders.py # Shader CRUD tests +├── test_feed.py # Feed ranking tests +└── ... +``` + +Tests use an in-memory SQLite database (via `aiosqlite`) for speed. The `conftest.py` sets up a test FastAPI client with overridden database dependency. + +### Writing Tests + +```python +import pytest +from httpx import AsyncClient + +@pytest.mark.asyncio +async def test_create_shader(client: AsyncClient, auth_headers: dict): + response = await client.post( + "/api/v1/shaders", + json={ + "title": "Test Shader", + "glsl_code": "void mainImage(out vec4 f, in vec2 c) { f = vec4(1.0); }", + "shader_type": "2d", + "status": "draft" + }, + headers=auth_headers + ) + assert response.status_code == 201 + assert response.json()["title"] == "Test Shader" +``` + +## CI/CD Pipeline + +### Forgejo Actions + +**Workflow:** `.forgejo/workflows/ci.yml` + +**Triggers:** Push or PR to `master` branch. + +**Steps:** +1. Checkout code +2. Install Python deps: `pip install -e ".[dev]"` (from `services/api/`) +3. Lint: `ruff check app/ tests/` (continue on error) +4. Tests: `pytest tests/ -v` + +**Working directory:** `services/api/` + +### Local CI Simulation + +```bash +cd services/api +ruff check app/ tests/ +pytest tests/ -v +``` + +## Coding Conventions + +### Python (API) + +- **Async-first** — all route handlers and DB access use `async`/`await` +- **Type hints** throughout — function signatures, return types, Pydantic models +- **Pydantic v2** for all request/response serialization +- **SQLAlchemy 2** declarative models with async sessions +- **Dependency injection** via FastAPI `Depends()` for auth, DB, Redis +- **Linter:** ruff (configured in pyproject.toml) + +### TypeScript (Frontend) + +- **React 18** with functional components and hooks +- **TanStack Query** for all API data (no manual fetch/useEffect) +- **Zustand** for client-only state (auth, UI preferences) +- **Tailwind CSS** for styling (no custom CSS files unless necessary) +- **Axios** for HTTP client (configured with base URL and interceptors) + +### File Naming + +- **Python:** snake_case for files and modules (`glsl_validator.py`) +- **TypeScript:** PascalCase for components (`ShaderCanvas.tsx`), camelCase for utilities +- **Docker:** service names are lowercase (`api`, `mcp`, `renderer`) + +## Development Workflow + +### Hot Reload + +With `docker-compose.override.yml` (applied automatically): +- **API:** `uvicorn --reload` watches `services/api/app/` for Python changes +- **Frontend:** Vite HMR for instant browser updates +- **MCP:** Volume mount for live changes (manual restart needed) +- **Worker:** Requires manual restart after task changes: `docker compose restart worker` + +### Database Shell + +```bash +make db-shell +# or: docker compose exec postgres psql -U fracta -d fractafrag + +# Useful queries +\dt -- List tables +\d shaders -- Describe table +SELECT COUNT(*) FROM shaders; -- Count shaders +SELECT * FROM system_config; -- App settings (if any) +``` + +### Redis Shell + +```bash +docker compose exec redis redis-cli + +# Useful commands +KEYS * -- List all keys +KEYS blocklist:* -- List blocklisted tokens +TTL blocklist:some-token -- Check token expiry +INFO memory -- Memory usage +``` + +### API Testing with curl + +```bash +# Register +curl -X POST http://localhost/api/v1/auth/register \ + -H "Content-Type: application/json" \ + -d '{"username":"test","email":"test@test.com","password":"test1234"}' + +# Login (save token) +TOKEN=$(curl -s -X POST http://localhost/api/v1/auth/login \ + -H "Content-Type: application/json" \ + -d '{"username":"test","password":"test1234"}' | jq -r .access_token) + +# Create shader +curl -X POST http://localhost/api/v1/shaders \ + -H "Authorization: Bearer $TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"title":"Test","glsl_code":"void mainImage(out vec4 f,in vec2 c){f=vec4(1.);}","shader_type":"2d","status":"draft"}' + +# Browse feed +curl http://localhost/api/v1/feed +``` + +### Debugging Celery Tasks + +```bash +# View worker logs +docker compose logs -f worker + +# Start worker with debug logging +docker compose exec worker python -m celery -A app.worker worker --loglevel=debug + +# Inspect registered tasks +docker compose exec worker python -m celery -A app.worker inspect registered + +# Purge all pending tasks +docker compose exec worker python -m celery -A app.worker purge +``` \ No newline at end of file