Commit graph

144 commits

Author SHA1 Message Date
Sirius DevOps
12262d1562 cameras: RTSP preview via ffmpeg, stop opening VLC, drop masscan to 200pps
All checks were successful
build-and-deploy / build (push) Successful in 2m23s
Masscan finds are rtsp:// with no snapshot_url, so the popup skipped the
<img> and the leftover source link handed the OS an rtsp:// URL (VLC).

- Popup always hits /api/cameras/{id}/snapshot (HTTP stills, then one
  ffmpeg frame grab). No credentials. 10s hard timeout.
- rtsp:// is rendered as text, never as an href.
- ffmpeg added to the app image for the RTSP still/MJPEG path.
- Default MASSCAN_RATE 200 (1k/10k saturated the home uplink).
2026-08-24 23:36:34 -04:00
Sirius DevOps
3af9511a3a masscan: drop default rate to 1000 pps (home uplink)
All checks were successful
build-and-deploy / build (push) Successful in 1m39s
10k pps saturated the residential link. New default is 1000 pps
(~0.6 Mbps of SYNs). Live override is /etc/osint-dashboard/masscan.env.
2026-08-24 23:22:03 -04:00
Sirius DevOps
085061492e masscan: active RTSP (554) camera discovery service
All checks were successful
build-and-deploy / build (push) Successful in 2m0s
Continuous whole-IPv4 rolling sweep for open TCP 554, feeding the same
cameras table as the passive scraper (discovery_source=masscan).

- masscan_config.py: env-driven knobs (range, ports, rate, retries, excludes)
- masscan_scanner.py: JSON-lines parser, rtsp://IP/ URL + url_hash dedupe,
  ip-api geolocation, insert/refresh, NATS publish for new finds
- run_masscan_service.py: long-lived rolling-sweep runner (streams results
  in, restarts on pass completion); fails closed without an excludefile
- deploy/: systemd unit + README + excludes file for the Pi host
- .env.example: masscan section

Verified end-to-end against a local Postgres: parse, insert, and dedupe
(0 new on re-ingest) all pass.
2026-08-24 22:23:36 -04:00
Sirius DevOps
11085f8901 ci: retrigger clean deploy (was racing a manual compose up)
All checks were successful
build-and-deploy / build (push) Successful in 2m7s
2026-08-24 22:12:07 -04:00
Sirius DevOps
16436a8c08 fires: default FIRMS_DAYS to 2 (NRT latency leaves day=1 empty)
Some checks failed
build-and-deploy / build (push) Failing after 1m31s
At some hours FIRMS NRT returns zero detections for day range 1;
day range 2 returns thousands. Bump default to 2.
2026-08-24 22:07:51 -04:00
Sirius DevOps
250dbbb5df Camera popups: live MJPEG preview via /api/cameras/{id}/stream passthrough, snapshot fallback, taller thumb
All checks were successful
build-and-deploy / build (push) Successful in 1m52s
2026-08-24 21:38:46 -04:00
Sirius DevOps
1f0b831835 fires: append FIRMS day-range param; API 400s without it
All checks were successful
build-and-deploy / build (push) Successful in 1m54s
FIRMS area-CSV requires an explicit [1..5] day range — requests without
it return 'Invalid day range. Expects [1..5].' as a 400. New
FIRMS_DAYS env (default 1).
2026-08-24 21:32:55 -04:00
Sirius DevOps
1ce3da4403 Migration 004 fix: pg_type catalog table, not pg_typname
All checks were successful
build-and-deploy / build (push) Successful in 1m37s
2026-08-24 21:29:25 -04:00
Sirius DevOps
c1a19ecf06 Migration 004: add 'camera' to event_source_type enum so ingester can store camera events
All checks were successful
build-and-deploy / build (push) Successful in 1m57s
2026-08-24 21:25:21 -04:00
Sirius DevOps
e6648ec1ed fires: resolve FIRMS_MAP_KEY from Postgres keystore, not just env
All checks were successful
build-and-deploy / build (push) Successful in 1m40s
Key saved via the dashboard Keys page sat unused in api_keys while
ingest_fires only checked the env var. Now env first, keystore fallback,
picked up on next poll without a restart.
2026-08-24 21:21:15 -04:00
Sirius DevOps
b1c611d4c4 Ingestor: update JetStream stream subjects on startup so events.camera gets covered (add_stream silently no-ops when stream exists)
All checks were successful
build-and-deploy / build (push) Successful in 1m52s
2026-08-24 21:19:20 -04:00
Sirius DevOps
456712fc32 Ingestor: include events.camera in JetStream stream subjects (camera publishes were hitting NoResponders)
All checks were successful
build-and-deploy / build (push) Successful in 1m51s
2026-08-24 21:11:34 -04:00
Sirius DevOps
347ee3a642 Camera scraper: live public source (fury999io/public-ip-cams), markdown-list parser, batch IP geolocation via ip-api; fix empty-env fallback so default source applies
All checks were successful
build-and-deploy / build (push) Successful in 1m38s
2026-08-24 21:03:33 -04:00
Sirius DevOps
28146c1adc Map: clamp GIBS daily-layer latest to yesterday UTC — today's mosaic isn't ingested yet at 00:xx UTC so tiles 404'd and the map went black
All checks were successful
build-and-deploy / build (push) Successful in 1m39s
2026-08-24 20:47:50 -04:00
f122740d56 CI: verify end-to-end deploy after compose plugin + project-name fixes
All checks were successful
build-and-deploy / build (push) Successful in 1m36s
2026-08-24 18:07:49 -04:00
Sirius DevOps
acbf5cc56d Add news feed panel to dashboard: exec summary pinned + scrolling headlines + 15min auto-refresh
Some checks failed
build-and-deploy / build (push) Has been cancelled
- New News tab consuming GET /api/news (headlines: source domain badge,
  relative timestamp, link-out target=_blank) and GET /api/news/summaries
  (latest executive summary pinned in a glowing briefing card).
- Four states handled: loading, empty (summarizer idle w/o GEMINI key),
  error, success; HTML-escaped titles/domains (newsEsc).
- Lightweight markdown renderer for summary bodies (bold/headers/lists).
- Auto-refresh every 15min (NEWS_REFRESH_MS) + lazy load on tab open + manual
  refresh button.
- Stretch hook: if articles ever carry lat/lon, headline pins plot on the
  GIBS map (renderNewsPins) + geotag indicator in the feed.
- Dark neon styling matching existing dashboard theme.
2026-08-24 18:06:43 -04:00
Sirius DevOps
aec3ecae9a Map: FIRMS heatmap + camera thumbnail popups + unified layer panel
All checks were successful
build-and-deploy / build (push) Successful in 1m37s
- Vendored leaflet.heat 0.2.0; fires overlay is now a real heatmap
  (L.heatLayer) with intensity driven by brightness or VIIRS confidence
  (color mode selector) and a since= time filter (6h/24h/48h/7d/30d/all).
- Camera markers now open popups with the live snapshot thumbnail proxied
  through GET /api/cameras/{id}/snapshot (backend TTL cache) + full
  metadata (vendor, device, discovery source, first/last seen, coords,
  source link); missing-snapshot cams show a placeholder instead of 404.
- New collapsible Layers panel (dark neon) over the map: per-layer
  visibility toggles, opacity sliders (basemap/fires/cameras), live
  counts legend, and a fire heat-intensity ramp that swaps for the
  confidence legend in confidence mode.
- Clamp currentBBox() to world bounds so /api/cameras doesn't 422 at low
  zoom (worldCopyJump spans multiple copies).
- Skip overlay reloads on moveend while a popup is open — popup autopan
  no longer rebuilds the marker group and closes the popup.
- Zoom control moved to top-right so it doesn't collide with the panel.
2026-08-24 18:03:21 -04:00
347f781cf6 CI: fix deploy failures — pin compose project name in docker-compose.yml, drop COMPOSE_PROJECT_NAME=osint override (would orphan osint-dashboard_osint-pgdata); runner now mounts cli-plugins so 'docker compose' works in job containers
Some checks failed
build-and-deploy / build (push) Failing after 3s
2026-08-24 17:58:50 -04:00
Sirius DevOps
5849d72299 Fix news scraper feed discovery + summarizer startup race
Some checks failed
build-and-deploy / build (push) Failing after 4s
The vendored spider only started on urls.txt lines containing '/rss' or
'/feed' — but the curated urls.txt holds 200 homepages, so the crawler
matched ZERO feeds and silently did nothing (2ms, 0 items). Rewrite the
spider with feed autodiscovery: fetch each homepage, find its
<link rel="alternate" type="application/rss+xml"> (or /feed|/rss link),
parse the feed, then follow each item to extract the article. Bump
DEPTH_LIMIT 1->3 (homepage -> feed -> article).

Also make the summarizer resilient to booting before the app container has
run alembic (docker-compose only guarantees `db` is up): add idempotent
ensure_tables() mirroring the scraper's CREATE TABLE IF NOT EXISTS.
2026-08-24 17:42:06 -04:00
Sirius DevOps
3aa6f265bb Add NASA GIBS satellite basemap world map tab (Leaflet)
Some checks failed
build-and-deploy / build (push) Failing after 5s
Interactive world map panel for the dashboard:
- Vendored Leaflet 1.9.4 under app/static/vendor/leaflet/ (served via
  new /static mount on the FastAPI app)
- New Map tab: GIBS WMTS raster basemap (BlueMarble_ShadedRelief_Bathymetry,
  VIIRS/MODIS/Aqua CorrectedReflectance_TrueColor, VIIRS_DayNightBand),
  layer picker + UTC date selector (Latest/-7d/-30d quick picks)
- /api/map/layers: curated GIBS catalog (tms, format, has_time, max_zoom)
- /api/map/times: per-layer date windows from GIBS Domains XML (6h cache,
  graceful 502 on GIBS hiccup; static layers 422)
- Fire hotspots (existing /api/fires) + open cameras (/api/cameras) as
  toggleable bbox-scoped overlays; dark UI matching dashboard theme
2026-08-24 17:35:44 -04:00
Sirius DevOps
91f436390b Add news pipeline: hourly scraper + Gemini summarizer in compose
Some checks failed
build-and-deploy / build (push) Failing after 4s
Vendor the newsPipeline scraper + summarizer into the repo and wire them into
docker-compose against the EXISTING osint-db (no second Postgres), replacing
the upstream k8s CronJobs with in-compose wall-clock loops (:00 scrape, :05
summarize).

- news/scraper: vendored Scrapy project (257 RSS feeds) + hourly loop
  scheduler (run_news_scraper.py)
- news/summerizer: vendored Gemini map-reduce summarizer, cleaned:
  * fix broken google-genai response handling (_extract_text, defensive)
  * fix malformed INSERT/GRANT query in save_summary_to_db
  * OSINT-neutral default MAP_PROMPT; futures/markets language gated behind
    INCLUDE_FUTURES=0 (yfinance lazy-imported)
  * env-configurable model, batch size, lookback window
  + hourly loop scheduler (run_news_summarizer.py, :05)
- alembic 003_news: idempotent articles + article_summaries tables
- API: GET /api/news and GET /api/news/summaries (+ models, schemas)
- tests/test_api_news.py: 5 DB-backed contract tests (all pass vs real PG)
- docs/news.md + .env.example updates

Both services run under the `ingest` compose profile (matching the
ingester/camera-scraper pattern) and build arm64 on the Pi via the existing
Forgejo CI workflow. telebot left out of scope (reserved env only).
2026-08-24 17:28:46 -04:00
Sirius DevOps
172273bed6 Fix alembic migration fork: chain 002_cameras after 002_fires
Some checks failed
build-and-deploy / build (push) Failing after 4s
002_fires and 002_cameras both had down_revision=001_initial, producing two
heads so 'alembic upgrade head' fails at container startup and the dashboard
never boots. Tables are independent; linearize 001 -> 002_fires -> 002_cameras.
2026-08-24 15:44:05 -04:00
Sirius DevOps
cf98af8101 Merge remote-tracking branch 'forgejo/master'
Some checks failed
build-and-deploy / build (push) Failing after 4s
# Conflicts:
#	.env.example
2026-08-24 15:40:31 -04:00
Sirius DevOps
627990efde Add NASA FIRMS active-fire ingest + /api/fires; API keys management page
Coherent merge of two coordinated features on the shared working tree:

FIRMS fire heatmap (backend, t_6e404c14):
- app/fire_sources.py: fetch FIRMS VIIRS area CSV (free MAP_KEY) -> NATS events.fire
- fires hypertable (TimescaleDB, 1-day chunks) with natural-key PK
  (latitude, longitude, acq_time, satellite); idempotent ON CONFLICT DO NOTHING
- alembic/versions/002_fires.py; GET /api/fires?bbox=&since= (JSON only)
- POST /api/ingest/fires; ~15 min poll loop (FIRMS_INTERVAL=900) in ingester
- env-driven config (FIRMS_MAP_KEY/DATASET/BBOX/INTERVAL); docs/firms.md covers
  the zero-cost GIBS VIIRS_SNPP_Thermal_Anomalies_375m_All tile alternative
- 18 tests (parser, mapping, idempotency, API contract) verified vs real
  TimescaleDB+PostGIS (localhost/osint-dashboard-pg image)

API keys page (frontend, t_4433cff2):
- app/keystore.py: api_keys table (self-creating), FIRMS/GEMINI/TELEGRAM
  registry with format validation, ****last4 masking, get_api_key()
- GET/POST/DELETE /api/keys (never returns full values); Keys tab in index.html

DB_NULL_POOL env switch in app/database.py enables a NullPool for tests /
short-lived processes that open a fresh event loop per unit.
2026-08-24 15:37:42 -04:00
Sirius DevOps
69a30115cc Add open IP camera discovery scraper (OSINT map)
Some checks failed
build-and-deploy / build (push) Failing after 5s
- camera_scraper: public-directory-only discovery (Insecam-style HTML +
  plain-text lists), hard private-range guard (fail closed), per-host
  rate limiting, Nominatim geocoding at <=1 req/s, TTL'd local snapshot
  cache, sha256 url_hash dedupe with Postgres upsert
- cameras table (migration 002) + /api/cameras?bbox= + snapshot endpoint
  with cache passthrough in main.py
- run_camera_service: long-running cycle worker following NATS->ingester
  pattern; publishes events.camera for shared ingester
- docker-compose camera-service profile, .env.example knobs

Verified E2E against TimescaleDB+PostGIS: private-range entries dropped,
dedupe across cycles holds, bbox query returns expected rows.
2026-08-24 14:54:51 -04:00
sirius0xdev
bce2d5c3bd
CI: trigger run with compose-plugin-mounted runner (no SSH deploy)
Some checks failed
build-and-deploy / build (push) Failing after 5s
2026-07-09 19:35:29 -04:00
sirius0xdev
6ddd773a75
CI: deploy directly from checkout workspace (local docker), no cp/SSH; explicit COMPOSE_PROJECT_NAME
Some checks failed
build-and-deploy / build (push) Failing after 19s
2026-07-09 19:33:18 -04:00
sirius0xdev
650ad27349
CI: deploy locally on the Pi (no SSH/scp) — runner already has docker socket + /opt/siriusdevops mounted
Some checks failed
build-and-deploy / build (push) Failing after 20s
2026-07-09 19:22:30 -04:00
sirius0xdev
8c7f17f645
Harden ingest_event: coerce non-UUID source_id to NULL (tolerates legacy messages)
All checks were successful
build-and-deploy / build (push) Successful in 4m15s
2026-07-07 19:44:13 -04:00
sirius0xdev
67fee36c7c
Wire ingestion: long-running ingester (sources->NATS->Postgres), fix nats-py API (durable=/LIMITS), drop UUID source_id on RSS
All checks were successful
build-and-deploy / build (push) Successful in 4m18s
2026-07-07 19:35:28 -04:00
sirius0xdev
5496023285
Add long-running ingester (sources->NATS->Postgres) as compose 'ingest' profile service
Some checks failed
build-and-deploy / build (push) Failing after 2m58s
2026-07-07 19:18:37 -04:00
sirius0xdev
0b344760dc
Add .gitignore (pycache, venv, .env)
All checks were successful
build-and-deploy / build (push) Successful in 3m18s
2026-07-07 18:46:06 -04:00
sirius0xdev
17d39cf622
CI: build all compose services (app+db) via docker compose build before deploy
Some checks failed
build-and-deploy / build (push) Failing after 10s
2026-07-07 18:36:12 -04:00
sirius0xdev
8eabe3b5e8
Fix SQLAlchemy 2.0 count().filter(); add search_vector trigger for full-text search
Some checks failed
build-and-deploy / build (push) Failing after 15s
2026-07-07 18:32:02 -04:00
sirius0xdev
49d5756ceb
Make events PK composite (id, ingested_at) for TimescaleDB hypertable partitioning requirement
Some checks failed
build-and-deploy / build (push) Failing after 15s
2026-07-07 18:29:50 -04:00
sirius0xdev
f7de9555a9
Fix migration: drop redundant explicit CREATE TYPE (collides with sa.Enum name=)
Some checks failed
build-and-deploy / build (push) Failing after 19s
2026-07-07 18:28:02 -04:00
sirius0xdev
0a6da2f9c1
Run migrations via entrypoint (alembic upgrade head) instead of nested asyncio.run in startup hook
Some checks failed
build-and-deploy / build (push) Failing after 16s
2026-07-07 18:24:01 -04:00
sirius0xdev
985fedec7b
Preload timescaledb via db command (conf.d ignored by official postgres entrypoint)
Some checks failed
build-and-deploy / build (push) Failing after 15s
2026-07-07 18:21:48 -04:00
sirius0xdev
b40f83f393
Fix Dockerfile.pg: mkdir conf.d before writing timescaledb preload
Some checks failed
build-and-deploy / build (push) Failing after 15s
2026-07-07 18:17:31 -04:00
sirius0xdev
56ed3d7b7e
Rebase custom PG image on official postgres:16 (honors POSTGRES_* env + md5 auth); add timescaledb/postgis via apt
Some checks failed
build-and-deploy / build (push) Failing after 16s
2026-07-07 18:13:40 -04:00
sirius0xdev
5b879dfc6b
Fix PYTHONPATH: app modules live in /app/app, not /app
Some checks failed
build-and-deploy / build (push) Failing after 17s
2026-07-07 18:06:40 -04:00
sirius0xdev
14a9178f8c
Use custom arm64 PG16+PostGIS+TimescaleDB image (no upstream arm64 postgis-timescale tag exists)
Some checks failed
build-and-deploy / build (push) Failing after 17s
2026-07-07 18:00:45 -04:00
sirius0xdev
3d53ed2c1c
Containerize for Pi: env-var config, compose stack, CI workflow
Some checks failed
build-and-deploy / build (push) Failing after 18s
- Add app/config.py (12-factor, env-driven: DB/NATS/MinIO, URL-encoded pw)
- Route database/ingestor/sources/alembic through config; kill hardcoded
  svc.cluster.local hostnames
- Drop unused redis dependency
- Add docker-compose.yml (timescaledb-postgis pg13 arm64 + optional nats)
- Add .env.example, .dockerignore, .forgejo/workflows/build.yml (arm64 build+SSH deploy)
2026-07-07 17:50:51 -04:00
sirius0xdev
93b8b89639
first commit 2026-06-04 20:30:04 -04:00