diff --git a/CLAUDE.md b/CLAUDE.md index a65d18d5..e010b181 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -18,42 +18,10 @@ Towerops is a Phoenix 1.8 web application built with Elixir, using Ecto for data ### Data Model Relationships ``` -┌─────────────┐ -│ User │ -│ (Accounts) │ -└──────┬──────┘ - │ owns/member of (many-to-many) - │ - ↓ -┌──────────────────────────────────────────────────────┐ -│ Organization │ -│ (Organizations) │ -└────┬────────────┬─────────────┬──────────────────────┘ - │ │ │ - │ has many │ has many │ has many - │ │ │ - ↓ ↓ ↓ -┌─────────┐ ┌──────────┐ ┌────────────────┐ -│ Site │ │Equipment │ │ AgentToken │ -│ (Sites) │ │(Equipment│ │ (Agents) │ -└────┬────┘ └────┬─────┘ └────────┬───────┘ - │ │ │ - │ has many │ has one │ assigned via - │ │ │ - ↓ ↓ ↓ -┌──────────┐ ┌──────────────┐ ┌──────────────────┐ -│Equipment │ │ SNMPDevice │ │ AgentAssignment │◀──┐ -│(Equipment│ │(Snmp.Devices)│ │ (Agents) │ │ -└────┬─────┘ └──────────────┘ └──────────────────┘ │ - │ │ │ - │ has many └───────────────┘ - ├────────────────┬─────────────────┘ - │ │ assigns equipment - ↓ ↓ to agent -┌──────────────┐ ┌─────────┐ -│MonitoringCheck│ │ Alert │ -│ (Monitoring) │ │ (Alerts)│ -└──────────────┘ └─────────┘ +User (Accounts) → owns/member of → Organization (Organizations) + ├─ Site (Sites) → Equipment + ├─ Equipment → SNMPDevice, MonitoringCheck, Alert + └─ AgentToken → AgentAssignment → Equipment Key Relationships: - User can own/belong to multiple Organizations @@ -61,963 +29,399 @@ Key Relationships: - Equipment belongs to both Site and Organization (denormalized) - Equipment can be assigned to one AgentToken via AgentAssignment - Equipment has one SNMPDevice with Sensors and Interfaces -- Equipment has many MonitoringChecks (polling results) -- Equipment has many Alerts (equipment_down, equipment_up) +- Equipment has many MonitoringChecks (polling results) and Alerts - AgentToken authenticates remote agents for local SNMP polling ``` -**Note to AI assistants**: When you make changes to the data model (migrations, schema additions, new contexts), update this diagram to reflect the current state. +**Note**: Update this diagram when making data model changes. ## Essential Commands ### Setup and Development -- `mix setup` - Install dependencies, create/migrate database, setup and build assets -- `mix phx.server` - Start the Phoenix server (accessible at http://localhost:4000) -- `iex -S mix phx.server` - Start server with interactive Elixir shell +- `mix setup` - Install dependencies, create/migrate database, build assets +- `mix phx.server` - Start Phoenix server (http://localhost:4000) +- `iex -S mix phx.server` - Start server with IEx shell ### Testing and Quality -- `mix test` - Run all tests (automatically creates and migrates test database) -- `mix test test/path/to/specific_test.exs` - Run a specific test file -- `mix test --failed` - Re-run only previously failed tests -- `mix test --cover` - Run tests with coverage report (view: `open cover/modules_*.html`) -- `mix precommit` - **Run before committing**: compiles with warnings as errors, unlocks unused deps, formats code, and runs tests -- `mix dialyzer` - Run static type analysis - -**Coverage Notes**: -- Target: 90% minimum -- Protobuf-generated modules (Towerops.Agent.*) show 0% but don't need tests -- TimescaleDB aggregate tests should be tagged `@tag :skip` (test DB lacks continuous aggregates) +- `mix test` - Run all tests +- `mix test --failed` - Re-run failed tests +- `mix test --cover` - Run with coverage (target: 90% minimum) +- `mix precommit` - **Run before committing**: compiles with warnings as errors, formats, runs tests +- `mix dialyzer` - Static type analysis ### Database -- `mix ecto.create` - Create the database -- `mix ecto.migrate` - Run pending migrations -- `mix ecto.reset` - Drop, create, and migrate database -- `mix ecto.gen.migration migration_name_using_underscores` - Generate a new migration file +- `mix ecto.create/migrate/reset` - Database operations +- `mix ecto.gen.migration name_using_underscores` - Generate migration ### Assets -- `mix assets.build` - Build CSS and JS assets (Tailwind + esbuild) -- `mix assets.deploy` - Build minified assets for production +- `mix assets.build/deploy` - Build CSS/JS assets (auto-rebuilds on save in dev) ## Architecture ### Application Structure -Standard Phoenix conventions with separation between business logic (`lib/towerops/`) and web interface (`lib/towerops_web/`). +Standard Phoenix conventions: business logic in `lib/towerops/`, web interface in `lib/towerops_web/`. **Key Configuration**: -- **Binary IDs**: Generators use binary (UUID) primary keys by default (`binary_id: true`) -- **Timestamps**: Use `:utc_datetime` for all timestamps -- **Web server**: Bandit adapter (not Cowboy) -- **HTTP client**: `:req` library (Req module) - this is the only approved HTTP client +- **Binary IDs**: UUID primary keys by default (`binary_id: true`) +- **Timestamps**: `:utc_datetime` for all timestamps +- **Web server**: Bandit adapter +- **HTTP client**: `:req` library (Req module) - ONLY approved client - **Ecto repos**: `[Towerops.Repo]` -### Web Layer Patterns +### Web Layer -All LiveViews, LiveComponents, and HTML modules automatically get these imports/aliases via `html_helpers/0`: -- `ToweropsWeb.CoreComponents` - Core UI components (`<.button>`, `<.input>`, `<.form>`, etc.) -- `ToweropsWeb.Layouts` - Layout components -- `Phoenix.LiveView.JS` - Client-side JS commands -- Gettext for translations -- Verified routes with `~p` sigil +All LiveViews get these imports via `html_helpers/0`: +- `ToweropsWeb.CoreComponents` - Core UI (`<.button>`, `<.input>`, `<.form>`) +- `ToweropsWeb.Layouts`, `Phoenix.LiveView.JS`, Gettext, verified routes (`~p`) ### Rate Limiting -Uses Hammer (ETS-backed) for rate limiting to prevent brute-force attacks. - -**Endpoints Protected:** -- **Auth endpoints** (10 req/min per IP): `/users/log-in`, `/users/register`, `/users/reset-password`, TOTP verification, mobile auth QR flows -- **API v1 endpoints** (1000 req/min per IP): `/api/v1/*` - relaxed for Terraform/automation -- **Admin API endpoints**: Not rate limited (superuser only, different attack profile) - -**Implementation:** -- Plug: `ToweropsWeb.Plugs.RateLimit` with `:auth` or `:api` type -- Uses client's real IP from `X-Forwarded-For` or `X-Real-IP` headers (for proxy/load balancer) -- Returns `429 Too Many Requests` with `Retry-After` header when limit exceeded - -**Configuration:** -- Disabled in test environment via `config :towerops, :rate_limiting_enabled, false` -- ETS backend configured in `config/config.exs` -- Hammer process started in `application.ex` supervision tree +Uses Hammer (ETS-backed): +- **Auth endpoints**: 10 req/min per IP (`/users/log-in`, `/users/register`, TOTP) +- **API v1**: 1000 req/min per IP (`/api/v1/*`) +- **Admin API**: Not rate limited (superuser only) +- Returns `429 Too Many Requests` with `Retry-After` header +- Disabled in test: `config :towerops, :rate_limiting_enabled, false` ### Asset Pipeline -- **Tailwind CSS v4**: Uses new `@import "tailwindcss"` syntax in `assets/css/app.css`, no `tailwind.config.js` needed -- **esbuild**: Bundles JavaScript from `assets/js/app.js` -- All vendor scripts/styles must be imported into app.js/app.css - cannot reference external src/href in layouts -- No inline `