Files
calypso/AGENTS.md
Othman H. Suseno 70b7841d1a add sources
2026-01-15 17:39:32 +07:00

138 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# AGENTS
## Overview
- Languages: Go backend with `github.com/gin-gonic/gin`, `zap`, PostgreSQL migrations and REST handlers under `backend/internal`, plus a Vite+React+TypeScript frontend under `frontend/`.
- Keep the system deterministic: Go 1.22+, PostgreSQL 14+, Node 18+, npm 10+ (use `node --version`/`npm --version` to confirm).
- Secret material lives in `/etc/calypso/config.yaml` or environment variables (never commit private keys, JWT secrets, or database passwords).
- The backend expects `CALYPSO_DB_PASSWORD` and `CALYPSO_JWT_SECRET` (minimum 32 characters) before running locally or in CI.
## Environment Setup
- Run `sudo ./scripts/install-requirements.sh` from the repo root to provision system dependencies on Ubuntu-like hosts before building the backend.
- `config.yaml.example` provides defaults; copy it to `/etc/calypso/config.yaml`, then edit the file or override with the `CALYPSO_*` environment variables before launching.
- For frontend work, `npm install` from `frontend/` once before subsequent builds or `npm run dev` sessions.
## Backend Tooling (Go)
### Build & Run
- `make build` (`go build -o bin/calypso-api ./cmd/calypso-api`) produces the local binary.
- `make run` (`go run ./cmd/calypso-api -config config.yaml.example`) is the quickest local dev server; supply `-config` pointing at your working config.
- Production cross-build: `make build-linux` sets `CGO_ENABLED=0 GOOS=linux GOARCH=amd64` and strips symbols.
- `deploy/systemd/calypso-api.service` is a template—copy it to `/etc/systemd/system/`, `daemon-reload`, `enable`, and `start` to run as service.
### Testing
- `make test` runs `go test ./...` across all packages; it is the authoritative test runner today.
- To run a single test, narrow the package and pass `-run`: `cd backend && go test ./internal/auth -run '^TestHandler_Login$'` (use `.` to target the current package and regular expressions for test names).
- `make test-coverage` generates `coverage.out` and opens a coverage report via `go tool cover -html=coverage.out`.
### Lint & Format
- `make fmt` (`go fmt ./...`) keeps source gofmt-clean; run it after edits and before committing.
- `make lint` requires `golangci-lint`; install it with `make install-deps` (which calls `go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest`).
- `make deps` handles dependency downloads and tidies `go.mod`/`go.sum` when vendor changes are necessary.
## Backend Style Guidelines (Go)
### Imports
- Group imports with standard library packages first, blank line, then third-party packages (gofmt enforces this).
- Do not alias imports unless needed to disambiguate. Keep third-party import paths explicit (e.g., `github.com/gin-gonic/gin`).
- Prefer explicit package names (`config`, `database`, `logger`) rather than dot-importing.
### Formatting & Files
- Always run `gofmt` (or `make fmt`) before committing; gofmt enforces tabs for indentation and consistent formatting.
- Keep files small (100300 lines) and break responsibilities into `internal/common`, `internal/iam`, etc., mirroring the current layout.
- Maintain doc comments on exported functions, types, and struct fields; these comments start with the identifier they describe.
### Types & Naming
- Use PascalCase (UpperCamel) for exported types/fields/methods and camelCase for internal helpers.
- Keep configuration structs descriptive (`ServerConfig`, `DatabaseConfig`) and align YAML tags with snake_case field names.
- When introducing new structs, mimic the existing tag conventions (`yaml:"field_name"`).
- Use short, meaningful receiver names (`func (h *Handler) ...`), ideally 13 characters, matching current files.
- Name boolean helpers `isX`/`shouldY` and prefer `cfg`/`db`/`ctx` as short local variables for configuration, database, and contexts.
### Error Handling
- Wrap errors with `fmt.Errorf("context: %w", err)` and return early. Do not swallow errors unless there is a clear action (e.g., logging + fallback).
- Use `gin.Context` to respond with the appropriate HTTP status codes (`c.JSON(http.StatusBadRequest, gin.H{...})`).
- Log warnings vs. errors via the shared `logger.Logger` (`h.logger.Warn(...)` for expected issues, `Error` for failures, `Info` for happy paths).
- Avoid `panic` in handlers; only panic during initialization (e.g., new logger). Instead, log and return a 5xx status.
- Keep timeouts and cancellation explicit using `context.WithTimeout` when communicating with databases or external systems.
### Logging & Observability
- Use `zap`-backed `logger.NewLogger`, pass service-specific names (e.g., `router`, `auth-handler`), and attach structured pairs (`"user_id", user.ID`).
- Allow log format and level to be configured via `CALYPSO_LOG_FORMAT`/`CALYPSO_LOG_LEVEL` if present.
- Prefer adding caller info and stack traces on `Error`level logs when helpful. Do not sprinkle raw `fmt.Println` or `log.Print`.
### HTTP & Routing
- All HTTP routing lives under `internal/common/router`; register handlers through route groups and middleware (auth/authZ) as seen in `router.NewRouter`.
- Use Gin middleware for sessions, JWT validation, and auditing. Keep handler methods grouped by feature (auth, iam, tasks).
- Keep handler structs simple: inject shared dependencies (`db`, `config`, `logger`) via constructors, then refer to them as fields.
- For JSON requests/responses, mirror struct field names in `json` tags (snake_case) even if Go fields are camelCase.
### Database & Migrations
- Use prepared statements or parameterized queries (see `iam` and `sessions` tables) with `$1` style placeholders.
- Run migrations automatically from `internal/common/database/migrations/` via `database.RunMigrations` at startup.
- Hunters: use `database.DB` wrapper for connection pool handling, close connections via `defer db.Close()`.
- When adding new tables, drop SQL files into `db/migrations/` and re-run migrations via the running service or a migration helper.
### Security Notes
- JWT secrets must be strong (>=32 characters). Use environment variables, never embed secrets in code or checked-in configs.
- Database passwords, API keys, and secrets are populated via env vars (`CALYPSO_DB_PASSWORD`, `CALYPSO_JWT_SECRET`, `CALYPSO_JWT_SECRET`) or `config.yaml` on the host.
- All mutating endpoints should log audit events and guard via IAM roles (see `iam` package for role resolution).
- Bacula client registration and capability pushes require the `bacula-admin` role; enforce it via `requireRole("bacula-admin")` on `/api/v1/bacula/clients` routes.
## Frontend Tooling (React + TypeScript)
### Build & Run
- Work inside `frontend/`. Start the dev server with `npm run dev` (proxies `/api``http://localhost:8080`).
- Production builds use `npm run build`, which runs `tsc` before `vite build` to ensure typing correctness.
- Serve production output via `npm run preview` if you need to smoke-test a `dist/` snapshot.
### Testing
- No automated frontend tests run currently. If you add a test harness (Jest/Vitest), ensure `npm test` or equivalent is added to `package.json`.
- For manual verification, interact with the dev server after logging into the backend API from `localhost:3000`.
### Lint & Formatting
- `npm run lint` runs ESLint across `.ts/.tsx` files with the `@typescript-eslint` recommended rules and `react-hooks` enforcement. It also blocks unused directives and sets `--max-warnings 0`.
- The linter requires you to keep hooks exhaustive and avoid unused vars; prefix intentionally unused arguments with `_` (e.g., `_event`).
- `tsconfig.json` sets `strict` mode, `noUnusedLocals`, `noUnusedParameters`, and `noFallthroughCasesInSwitch`. Respect these by keeping types precise and handling every branch.
## Frontend Style Guidelines (TypeScript + React)
### Imports & Aliases
- Use the `@/` alias defined in `tsconfig.json` for slices inside `src/` (e.g., `import Layout from '@/components/Layout'`).
- Import React/third-party packages at the top, followed by `@/` aliased modules, maintaining alphabetical order within each group.
- Prefer named exports for hooks and API utilities, default exports for page components (e.g., `export default function LoginPage()`).
### Formatting & Syntax
- Use single quotes for strings, omit semicolons (the repo follows Prettier/Vite defaults), and keep 2-space indentation inside JSX.
- Keep JSX clean: wrap complex class combinations and conditional fragments in template literals or helper functions, avoid inline object literal props unless necessary.
- Keep React components in kebab-case file names (e.g., `Login.tsx`, `Layout.tsx`) and align component names with file names.
### Types & Naming
- Use `interface` or `type` aliases for props and API shapes (e.g., `interface LoginRequest`), and add optional fields with `?`.
- Names for state setters, handlers, and store selectors follow camelCase (`setAuth`, `handleSubmit`, `loginMutation`).
- Use descriptive names for UI data (`navigation`, `userMenu`, `alerts`). Keep boolean flags prefixed with `is`, `has`, or `should`.
### State, Hooks, & Effects
- Prefer `useState`/`useEffect` for local UI state and `Zustand` stores (`useAuthStore`) for shared state.
- Keep `useEffect` dependency arrays explicit; avoid disabling exhaustive-deps rules unless documented.
- Manage async server interactions with TanStack Query (`useMutation`, `useQuery`) and centralize API clients (`src/api/client.ts`).
### Styling & Layout
- TailwindCSS classes are used for layout (`flex`, `gap`, `bg-...`). Keep className strings readable and group related classes together (spacing, color, typography).
- Use utility classes for responsive behavior (`lg:hidden`, `px-4`). For dynamic class toggling, compute the string in JS before passing it to `className`.
- Keep inline styles minimal; prefer CSS utilities and shared constants (colors, text sizing) defined in tailwind config.
### API & Networking
- Use `src/api/client.ts` as the single Axios instance (`baseURL: '/api/v1'`). Attach request/response interceptors for auth token injection/401 handling.
- Return typed promises from API helpers (e.g., `Promise<LoginResponse>`). Let callers handle success/errors via TanStack Query callbacks.
- Use `authApi.getMe`, `authApi.login`, etc., for all backend interactions; do not re-implement Axios calls directly in UI components.
### Error Handling
- Surface backend errors by reading `error.response?.data?.error` (as seen in `LoginPage`) and displaying friendly messages via inline UI alerts or toast components.
- Clear authentication state on 401 responses using the Axios interceptor (`useAuthStore.getState().clearAuth()`), then redirect to `/login`.
- Display loading or pending states (`loginMutation.isPending`) instead of disabling UI abruptly; provide visual feedback for async actions.
## Repository Practices
- Keep database migration files under `db/migrations/` and refer to them from `internal/common/database/migrations/` for auto-run.
- When adding commands or packages, update the relevant `Makefile` target or `package.json` script, and document the command in this AGENTS file.
- Commit `go.mod`, `go.sum`, and `package-lock.json`/`package.json` updates together with code changes.
- Avoid committing binaries (`bin/`), coverage artifacts (`coverage.out`), or built frontend output (`frontend/dist/`). These are ignored by `.gitignore` but double-check before commits.
## Cursor & Copilot Rules
- There are no `.cursor/rules/`, `.cursorrules`, or `.github/copilot-instructions.md` files in this repository. Follow the guidelines laid out above.