# TowerOps Network monitoring and alerting platform built with Phoenix LiveView. ## Features - **Multi-tenant architecture** - Organizations with role-based permissions - **Site hierarchy** - Organize equipment across multiple sites - **Automated monitoring** - Real-time ping monitoring with configurable intervals - **Time-series data** - Efficient storage with TimescaleDB (optional) - **Real-time updates** - LiveView dashboard with PubSub - **Equipment tracking** - Monitor network devices by IP address ## Quick Start ### Prerequisites - Elixir 1.14+ - PostgreSQL 14+ - (Optional) TimescaleDB for production-grade time-series performance ### Setup ```bash # Install dependencies mix setup # Start the server mix phx.server ``` Visit [`localhost:4000`](http://localhost:4000) from your browser. ### TimescaleDB (Production Only) **Development**: Uses standard PostgreSQL (no TimescaleDB required). **Production**: TimescaleDB is automatically enabled for optimal performance with time-series data. ```bash # Production deployment - install TimescaleDB first brew tap timescale/tap && brew install timescaledb # macOS # Then run migrations with MIX_ENV=prod MIX_ENV=prod mix ecto.migrate ``` See [TIMESCALEDB.md](TIMESCALEDB.md) for detailed installation and configuration. **How it works**: Migrations detect the environment (`MIX_ENV`) and only enable TimescaleDB features (hypertables, compression, retention policies, continuous aggregates) in production. ## Encryption Setup (Production) TowerOps uses AES-256-GCM encryption for sensitive data (SNMP communities, MikroTik API passwords, etc.). ### Development/Test Encryption keys are pre-configured in `config/dev.exs` and `config/test.exs`. No action required. ### Production Set the `CLOAK_KEY` environment variable with a base64-encoded 32-byte key: ```bash # Generate encryption key openssl rand -base64 32 ``` **Important**: - Store the generated key securely in 1Password or your secrets manager - **Never commit the production key to version control** - Losing the encryption key makes encrypted data unrecoverable #### Kubernetes Deployment **If secret doesn't exist yet** (new deployment): ```bash # Generate CLOAK_KEY (store in 1Password first!) CLOAK_KEY=$(openssl rand -base64 32) # Create towerops-secrets with all required keys kubectl create secret generic towerops-secrets \ --from-literal=RELEASE_COOKIE=$(openssl rand -base64 32) \ --from-literal=SECRET_KEY_BASE=$(mix phx.gen.secret) \ --from-literal=CLOAK_KEY="$CLOAK_KEY" \ -n towerops ``` **If secret already exists** (add CLOAK_KEY to existing secret): ```bash # Store new key in 1Password first! # Bash/Zsh: CLOAK_KEY=$(openssl rand -base64 32) # Fish shell: set CLOAK_KEY (openssl rand -base64 32) # Method 1: Using kubectl create with dry-run and apply kubectl create secret generic towerops-secrets \ --from-literal=CLOAK_KEY="$CLOAK_KEY" \ --dry-run=client -o yaml | \ kubectl apply -f - -n towerops # Method 2: Direct inline generation (works in all shells) kubectl create secret generic towerops-secrets \ --from-literal=CLOAK_KEY="$(openssl rand -base64 32)" \ --dry-run=client -o yaml | \ kubectl apply -f - -n towerops # Restart pods to pick up new key kubectl rollout restart deployment/towerops -n towerops ``` ## Development ### Database ```bash mix ecto.create # Create database mix ecto.migrate # Run migrations mix ecto.reset # Drop, create, and migrate ``` ### Testing ```bash mix test # Run all tests mix test --trace # Run with detailed output ``` ### Code Quality ```bash mix format # Format code with Styler mix compile --warnings-as-errors ``` ### Firmware Version Tracking The system automatically checks for latest firmware versions daily (2 AM dev, 4 AM prod). To manually trigger a firmware check: ```bash # Start IEx console iex -S mix phx.server # Manually trigger firmware version fetch Oban.insert(Towerops.Workers.FirmwareVersionFetcherWorker.new(%{})) ``` The worker will: 1. Fetch the latest MikroTik RouterOS version from RSS feed 2. Store version information in the database 3. Enable firmware update indicators on device detail pages Check the logs for fetch results: ```elixir # View recent Oban jobs Towerops.Repo.all(Oban.Job) |> Enum.take(5) ``` ## Learn more * Official website: https://www.phoenixframework.org/ * Guides: https://hexdocs.pm/phoenix/overview.html * Docs: https://hexdocs.pm/phoenix * Forum: https://elixirforum.com/c/phoenix-forum * Source: https://github.com/phoenixframework/phoenix