Commit graph

34 commits

Author SHA1 Message Date
1f9fe2ee29 chore: add gaiia library dep, remove stale honeybadger/tidewave/cbor
- Add vendored Gaiia library (path: vendor/gaiia) with auto-generated GraphQL
  queries and mutations from the Gaiia API schema
- Add startAddInventoryItemsJob + updateInventoryItem mutations
- Remove `:cbor` (was unused — no references anywhere)
- Remove stale Honeybadger filter module, tests, router plug, and config
- Remove stale Tidewave plug from endpoint (dev-only, dep already gone)
- Unlock leftover deps from lock file
2026-05-11 14:46:27 -05:00
50324c61ce feat(metrics): expose Prometheus /metrics via PromEx on :9568
Add PromEx with Application/BEAM/Phoenix/LiveView/Ecto/Oban plugins plus
a custom plugin that bridges existing Towerops Oban/Redis telemetry events.
The metrics HTTP server runs isolated on port 9568 so scrape traffic never
traverses the public Traefik IngressRoute.

The pod template carries prometheus.io annotations (scrape, port, path, job)
so the external Prometheus on 10.0.15.31 discovers each pod through the
existing kubernetes-pods scrape job (apiserver-proxy).
2026-05-08 10:25:25 -05:00
4a8d8cb208 feat(coverage): write rasters to NFS-backed dir, serve via dedicated static plug
Coverage rasters were being written to priv/static/coverage on the pod's
ephemeral filesystem — fine on a single-pod dev box, broken under k8s
where pods share NFS at /data and a restart wipes computed predictions.

* New :coverage_storage_dir config (defaults to priv/static/coverage for
  dev/test, "/data/coverage" in production).
* Raster.output_dir/absolute_raster_path read this config and resolve
  {otp_app, rel} or absolute paths.
* Endpoint now mounts a dedicated Plug.Static at /coverage from that
  same config — replicas all serve the same NFS-backed file regardless
  of which one ran the compute.
* Drop "coverage" from static_paths/0 since the dedicated plug owns
  that prefix now.
2026-05-07 07:38:05 -05:00
e97748437c build(assets): split coverage hooks into a lazy-loaded chunk
Switch esbuild to ESM with --splitting and load app.js as type=module.
Heavy hooks now ship in their own chunk and only download when their
LiveView mounts.

- assets/js/lib/leaflet.ts: extract ensureLeaflet (vendored Leaflet
  loader) into a shared module with a typed window.L declaration.
- assets/js/hooks/coverage_hooks.ts: extract CoverageMap,
  MultiCoverageMap, and CoverageLocationPicker (~510 lines) with
  proper interface types for hook context and tightened typing on
  payloads / event handlers (CoveragePayload is now a real type).
- app.ts: replace those three inline hook bodies with lazyHook
  stubs that call import('./hooks/coverage_hooks') on first mount,
  forwarding lifecycle hooks once the chunk resolves. Also adds a
  scoped `declare const L: any` so the remaining Leaflet hooks
  type-check.
- root.html.heex: switch the script tag to type=module so the
  emitted ESM bundle can use dynamic import().
- esbuild config: add --splitting --format=esm.

Build output now produces:
  app.js                      1.2 MB
  coverage_hooks-<hash>.js   16  KB  (lazy)
  chunk-<hash>.js             2.4 KB  (shared, lazy)
2026-05-06 16:11:32 -05:00
158ee9d323 feat: add Texas LIDAR DEM catalog with on-demand elevation reads
Catalog-only system for LIDAR-derived 1m DEMs covering Texas. We do not
mirror raster data — the DB stores tile metadata (URL + footprint
geometry) and elevation values are streamed on-demand from public USGS
3DEP / TNRIS Cloud-Optimized GeoTIFFs via GDAL /vsicurl/ HTTP byte-range
reads. Used for future tower coverage line-of-sight calculations.

- Enable PostGIS extension; tile footprints indexed via GiST
- Catalog sync from USGS 3DEP (primary) and TNRIS (fallback for gaps)
- Tile dedup by ST_Contains so TNRIS doesn't duplicate 3DEP coverage
- Single-point reads via gdallocationinfo with tile fallthrough on nodata
- Grid retrieval via gdal_translate AAIGrid with multi-tile mosaicing
- Monthly Oban cron worker keeps catalog fresh
- gdal-bin added to runtime image; geo_postgis dep added
2026-05-04 13:03:28 -05:00
550a23dafc
fix SSL CA trust store for HTTPS in containers
Start a named Finch pool with CAStore certificate path and configure
Req to use it by default. Fixes integration sync failures on Dokku
where the OS CA trust store is unavailable.
2026-03-11 14:48:54 -05:00
7607a583cf
perf(ci): optimize database setup with structure.sql (99.7% faster)
Replace sequential migration execution (4m8s for 172 migrations) with
structure.sql loading (416ms), reducing CI database setup time by 99.7%.

Changes:
- Configure Ecto to dump/load schema from priv/repo/structure.sql
- Update test alias: ecto.migrate → ecto.load
- Remove redundant database setup step from CI workflow
- Commit structure.sql to repository for version control

Benefits:
- CI database setup: 4m8s → 416ms (248s → 0.4s)
- Consistent schema snapshots across environments
- Faster local test runs

Maintenance:
- Run 'mix ecto.dump' after adding new migrations
- Structure file auto-updates with schema changes
2026-03-05 13:30:52 -06:00
0f32086933
Ignore pod shutdown errors in ErrorTracker
Add ErrorTrackerIgnorer to drop port_died, write_failed, epipe, and
broken pipe errors that occur during K8s pod shutdown. Same patterns
already filtered by HoneybadgerFilter and LoggerFilters.
2026-02-17 10:59:51 -06:00
c92dfff6c9
Add ErrorTracker, remove Honeybadger email notice filter
Replace the HoneybadgerNoticeFilter that emailed raw stacktraces on
every exception with ErrorTracker for self-hosted error tracking.
Honeybadger itself is retained. ErrorTracker dashboard is mounted at
/admin/errors behind the superuser auth wall.
2026-02-17 08:13:34 -06:00
c6a4e88a4a
fix: suppress benign shutdown errors during K8s pod rollouts
Filter port_died and write_failed/epipe errors from Honeybadger reports,
email notifications, and logs. Reorder supervision tree so Oban drains
before its dependencies shut down, and add 40s shutdown grace period.
2026-02-12 12:16:40 -06:00
debdd1a21f
feat: email raw stacktrace on every Honeybadger exception
Add a Honeybadger NoticeFilter that sends an email with the full
error class, message, stacktrace, and server info to graham@mcintire.me
whenever an exception is reported. The notice passes through unchanged
so Honeybadger still receives it normally.
2026-02-12 10:33:48 -06:00
d431931cfe
update hammer 2026-02-08 10:08:31 -06:00
ecf6edaf85
update docker compose instructions 2026-02-03 13:00:51 -06:00
02833d0b44 Add rate limiting to auth and API endpoints using Hammer 2026-02-01 14:59:54 -06:00
c856b2142c
Add timezone validation to reject invalid IANA timezone strings
Prevents invalid timezone values from being stored in the database
by validating against Elixir's time zone database during registration.
2026-02-01 11:56:04 -06:00
3c00dcf37c remove passkeys for now 2026-01-31 09:50:35 -06:00
1f6151b5a8 filter snmp communities from log 2026-01-28 14:51:47 -06:00
756a6b4cd4
poller agent fix and trigger device rediscovery 2026-01-26 14:51:32 -06:00
3495767b8d
ignore startup errors 2026-01-23 10:06:25 -06:00
028f2600a2
Auto-expand MIB directories to include subdirectories
- Update MibTranslator to automatically expand configured MIB directories
- snmptranslate doesn't search recursively, so we explicitly list all subdirs
- Simplify config to only specify root priv/mibs directory
- Update both config.exs (production) and dev.exs (development)
- Subdirectories like mikrotik/, cisco/, net-snmp/ are auto-discovered
2026-01-19 14:06:51 -06:00
77d70b28b7
Update MIB directory configuration for embedded files
- Update config.exs to point to /app/priv/mibs in production
- Update dev.exs to include priv/mibs root for standard MIBs
- Remove obsolete :mib_dir (singular) config
- MIB files are now part of the release and included in Docker image
- Paths work in both development and production environments
2026-01-19 14:04:48 -06:00
d78c42c8f4
test improvements 2026-01-18 17:15:44 -06:00
1f123bbeb9
add honeybadger and snmp overhaul 2026-01-18 16:16:08 -06:00
22f81acfa6
credo cleanup 2026-01-17 15:00:52 -06:00
538f582fec
Update agent setup instructions with Watchtower auto-updates
- Change default agent image to gmcintire/towerops-agent:main
- Add Watchtower service to docker-compose instructions
- Configure 15-minute update check interval for development
- Add DOCKER_API_VERSION=1.44 to prevent API compatibility errors
- Remove deprecated docker-compose version field
2026-01-16 17:38:30 -06:00
ab6ecbdfdc
Convert JavaScript to TypeScript with proper type definitions
- Add comprehensive type definitions for vendor libraries (Chart.js, topbar)
- Add TypeScript types for LiveView hooks and WebAuthn interfaces
- Convert all JS files to TypeScript with proper typing:
  - app.js → app.ts
  - webauthn.js → webauthn.ts
  - webauthn_utils.js → webauthn_utils.ts
  - passkey_settings.js → passkey_settings.ts
  - passkey_login.js → passkey_login.ts
  - passkey_discoverable.js → passkey_discoverable.ts
- Update tsconfig.json with modern ES2022 and strict type checking
- Update esbuild config to use app.ts as entry point
- All assets compile successfully with esbuild's native TypeScript support
- No node_modules required, following Phoenix/Elixir conventions
2026-01-15 13:29:49 -06:00
0a6f30299b
Filter out Bandit HTTP/0.9 errors from port scanners
Added log filter to suppress noisy errors from:
- HTTP/0.9 requests (obsolete protocol from 1991)
- Invalid HTTP version errors from port scanners and bots
- Malformed HTTP requests from automated tools

These errors are expected on public-facing servers and don't indicate
application problems. The filter uses Elixir's logger handler filters
to suppress them at the logging layer.

Resolves sporadic 'Bandit.HTTPError: Invalid HTTP version: {0, 9}' errors
in production logs.
2026-01-15 10:00:53 -06:00
749efe4798
Fix agent API to accept protobuf content type
The :accepts plug in the agent_api pipeline was only allowing JSON,
which caused 406 errors when the agent sent protobuf requests.

Changes:
- Register application/x-protobuf MIME type in config
- Add protobuf to agent_api pipeline accepts list
- Agent can now successfully communicate using protobuf format
2026-01-14 16:58:11 -06:00
569b5cb671
Make agent Docker image configurable and update success criteria
- Added agent_docker_image config option (defaults to towerops/agent:latest)
- Updated agent UI modal to use configurable image URL
- Can override in runtime.exs or environment config for production
- Updated AGENT_NEXT_STEPS.md success criteria with completion status
- 8/12 success criteria now complete
2026-01-14 09:16:48 -06:00
4f924ba9a7
add passkey 2026-01-09 12:26:32 -06:00
4242044cbd
Add parameter filtering to redact SNMP community strings from logs 2026-01-05 13:48:39 -06:00
c52f313e2d
1. User Authentication
- Full auth system with email/password (using phx.gen.auth)
  - Login, registration, password reset
  - Session management with remember-me functionality
  - Magic link login support

  2. Organization Management
  - Multi-tenant organization system
  - Organizations schema with unique slugs
  - Automatic organization creation when users register
  - Organization switcher UI at /orgs

  3. Membership System
  - Users can belong to multiple organizations
  - 4 permission levels: Owner, Admin, Member, Viewer
  - Complete permission matrix implemented
  - Join/leave organizations

  4. Invitation System
  - Email-based invitations with secure tokens
  - 7-day expiration on invites
  - Track who invited and who accepted

  5. Authorization
  - Full policy system (Organizations.Policy)
  - can?(membership, :action, :resource) helper
  - Enforced via plugs in router

  6. LiveView Pages
  - /orgs - List all your organizations
  - /orgs/new - Create new organization
  - /orgs/:slug - Organization dashboard (placeholder)

  7. Database Schema
  - users table
  - organizations table
  - organization_memberships table
  - organization_invitations table
  - All migrations run successfully
2025-12-21 13:31:59 -06:00
20d2d8dbd8
format 2025-12-21 11:31:08 -06:00
ba463dc5a2
init 2025-12-21 11:10:43 -06:00