diff --git a/.ignore b/.ignore new file mode 100644 index 00000000..d74488f8 --- /dev/null +++ b/.ignore @@ -0,0 +1,21 @@ +# Used by AI AGENTS to skip analyzing these files +.history +.tmp +_.bak +node_modules/ +/coverage/ +/dist/ +/env/ +_.DS*Store +.vscode/values-schema.yaml +*.env +/.secrets +chart/apl/values.schema.json +chart/apl/README.md +workflow/ +\_.new +.envrc +otomi.cpuprofile +/.idea/ +tmp +\*\*values-repo.yaml diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..2a2b82c7 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,26 @@ +## OVERVIEW + +App Platform API — Express/TypeScript REST API managing Kubernetes teams, workloads, and services. Uses **Git as database** (YAML files in a values repo). + +## CONVENTIONS + +- **OpenAPI-first**: Never add routes manually. Define in YAML spec, implement handler matching `operationId` +- **Handler signature (all versions)**: `export const opId = (req: OpenApiRequestExt, res: Response): void` — send via `res.json()` +- **Path params use curly braces in filesystem**: `src/api/v1/teams/{teamId}/services.ts` — Express resolves `:teamId` + +## Deprecations + +- /v1 is deprecated, new endpoints implemented in /v2 +- src/ai not used + +## KEY PATTERNS + +- **Git-as-Database**: CRUD → OtomiStack → FileStore (memory) + Git (disk) → commit → deploy +- **Multi-tenant isolation**: Team resources scoped by `teamId` in paths and CASL abilities +- **OpenAPI validation**: express-openapi-validator validates all requests/responses against specs +- **FileStore path mapping**: FileMap defines glob patterns + templates per AplKind (e.g., `env/teams/{teamId}/services/{name}.yaml`) + +## ANTI-PATTERNS + +- **DO NOT** edit `src/generated-schema.ts` — auto-generated from `npm run build:models` +- **DO NOT** add routes without OpenAPI spec — express-openapi-validator rejects unspecified routes diff --git a/src/api/AGENTS.md b/src/api/AGENTS.md new file mode 100644 index 00000000..58f49928 --- /dev/null +++ b/src/api/AGENTS.md @@ -0,0 +1,27 @@ +# API Route Handlers + +## OVERVIEW + +Versioned REST endpoint handlers. Each file exports functions matching OpenAPI `operationId`s. + +## STRUCTURE + +``` +api/ +├── v1/ # Legacy handlers — (req, res) signature, call req.otomi.* +├── v2/ # Current handlers — (req, res) signature, call req.otomi.*Apl* +│ └── teams/{teamId}/ # Team-scoped with sub-resource dirs +├── alpha/ # Experimental (AI features, team extensions) +│ ├── ai/ # Deprecated +│ └── teams/ # Deprecated +└── apiDocs.ts # Swagger UI endpoint +``` + +## CONVENTIONS + +- **v2 handlers**: `export const opId = (req: OpenApiRequestExt, res: Response): void` — call `req.otomi.*Apl*()`, send via `res.json()` + +## ANTI-PATTERNS + +- **DO NOT** put business logic in handlers — delegate to `req.otomi` (OtomiStack) +- **DO NOT** create handler files without corresponding OpenAPI spec entry diff --git a/src/openapi/AGENTS.md b/src/openapi/AGENTS.md new file mode 100644 index 00000000..9e1e8ee7 --- /dev/null +++ b/src/openapi/AGENTS.md @@ -0,0 +1,22 @@ +# OpenAPI Specifications + +## OVERVIEW + +YAML specs defining all API endpoints, schemas, ACLs, and documentation links. Single source of truth for the entire API surface. + +## CONVENTIONS + +- **`operationId`**: Must match exported function name in handler file +- **`x-eov-operation-handler`**: Path to handler file relative to `src/api/` (e.g., `v1/teams`) +- **`x-aclSchema`**: References schema name for CASL authorization +- **`x-acl`**: Maps roles to CRUD abilities (`create-any`, `read`, `update`, `delete-any`) +- **`x-formtype`**: UI hint for console form generation (`SelectWidget`, etc.) +- **`x-externalDocsPath`**: Appended to base docs URL for per-resource documentation +- **Schema files** define the resource type at top level (e.g., `Service:` in `service.yaml`) + +## ANTI-PATTERNS + +- **DO NOT** add paths without `operationId` and `x-eov-operation-handler` +- **DO NOT** define schemas inline in `api.yaml` — create separate `{resource}.yaml` +- **DO NOT** forget `x-aclSchema` — endpoints without it bypass authorization +- After changes: run `npm run build:models` to regenerate `generated-schema.ts`