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

11 KiB
Raw Blame History

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 Errorlevel 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 /apihttp://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.