Commit Graph

713 Commits

Author SHA1 Message Date
Louis King 6ede32b2d4 fix: ensure CI required checks run on all PRs
Remove paths-ignore from the pull_request trigger so the CI workflow
always runs and the required Lint/Test/Build Package checks report on
docs-only PRs (previously skipped entirely, blocking merges).

Drop the github.event_name == 'push' gate on the build job so the
required Build Package check also reports on pull requests.
2026-06-17 21:54:20 +01:00
JingleManSweep b38d0f20ff Merge pull request #266 from ipnet-mesh/chore/added-explicit-compose-pull-policies
Added explicit pull policies for core services
2026-06-16 22:58:33 +01:00
Louis King 3f739a783b Added explicit pull policies for core services 2026-06-16 22:55:19 +01:00
JingleManSweep 6ed2e6dc6a Merge pull request #265 from ipnet-mesh/fix/packets-feature-flag-check
fix: normalize packets feature flag check to !== false
2026-06-16 22:09:43 +01:00
Louis King 9071634606 fix: normalize packets feature flag check to !== false
The packets feature was the only flag gated with a strict `=== true` check
in the SPA JavaScript while every other flag uses `!== false`. Align packets
to the shared pattern across route registration, mobile nav, page titles, the
home nav card, and the messages/advertisements packet-detail link gates.
2026-06-16 22:02:59 +01:00
JingleManSweep f045de0dc3 Merge pull request #264 from ipnet-mesh/fix/postgres-charts-flatline
fix: normalize date-bucket keys for Postgres dashboard charts
2026-06-16 21:18:41 +01:00
Louis King cf5add9924 fix: normalize date-bucket keys for Postgres dashboard charts
Dashboard charts (activity, message-activity, node-count) rendered as
flat zeros on Postgres because func.date() returns a str on SQLite but
a datetime.date on Postgres — the dict lookup by string key always
missed. Fixed with a dialect-neutral _date_bucket_key() helper and
pinned the Postgres session timezone to UTC at the engine level.

Also adds dual-backend test infrastructure (TEST_DATABASE_BACKEND env
var), per-worker Postgres databases for pytest-xdist isolation, and
strengthened regression tests asserting non-zero date buckets.
2026-06-16 21:16:00 +01:00
JingleManSweep d4d55b16d9 Merge pull request #263 from ipnet-mesh/fix/node-list-nulls-last
fix: sink NULL last_seen nodes to bottom of node list
2026-06-16 19:25:19 +01:00
Louis King 8bf45362bb fix: sink NULL last_seen nodes to bottom of node list
After the Postgres migration, nodes with no last_seen timestamp floated
to the top of the default list because Postgres sorts NULLs first under
ORDER BY ... DESC, whereas SQLite (the previous backend) sorts them last.

Wrap the last_seen ORDER BY with SQLAlchemy nullslast() so NULL last_seen
nodes always sink to the end regardless of database backend or sort
direction. Adds three regression tests covering DESC, ASC, and all-NULL
cases.
2026-06-16 19:20:19 +01:00
JingleManSweep 34410dea2f Merge pull request #260 from ipnet-mesh/chore/revert-to-postgres-17
Reverted to Postgres 17
2026-06-15 20:05:28 +01:00
Louis King a554e095df Reverted to Postgres 17 2026-06-15 20:03:19 +01:00
JingleManSweep e8fd31a464 Merge pull request #258 from ipnet-mesh/renovate/postgres-18.x
chore(deps): update postgres docker tag to v18
2026-06-15 19:56:01 +01:00
renovate[bot] a57b84d482 chore(deps): update postgres docker tag to v18 2026-06-15 18:32:18 +00:00
JingleManSweep 520a11ada2 Merge pull request #259 from ipnet-mesh/fix/migrate-overlay-network
Added migrate service to overlay network
2026-06-15 19:22:07 +01:00
Louis King ccc8943971 Added migrate service to overlay network 2026-06-15 19:18:55 +01:00
JingleManSweep 1ba3e178a8 Merge pull request #243 from ipnet-mesh/feat/postgres-support
Add optional PostgreSQL backend (v0.14.0)
2026-06-15 18:57:55 +01:00
Louis King 4662e46d3a Merge branch 'main' of github.com:ipnet-mesh/meshcore-hub into feat/postgres-support 2026-06-15 15:11:29 +01:00
JingleManSweep 72b2b938f6 Merge pull request #256 from ipnet-mesh/chore/ghcr-package-cleanup
ci: prune old untagged GHCR package versions
2026-06-15 15:08:32 +01:00
Louis King ebb6547457 ci: prune old untagged GHCR package versions
Add actions/delete-package-versions@v5 to both Docker build pipelines to
clean up orphaned untagged manifests left behind by multi-arch rebuilds.

Uses delete-only-untagged-versions so tagged versions (v*, latest, main,
sha-*) are never deleted, with min-versions-to-keep: 10 to protect the
freshly-pushed image's own untagged child/attestation manifests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 15:00:34 +01:00
Louis King 8c77c2a978 Merge branch 'main' of github.com:ipnet-mesh/meshcore-hub into feat/postgres-support 2026-06-14 23:34:42 +01:00
JingleManSweep 7f19d47aa0 Merge pull request #255 from ipnet-mesh/fix/observer-filter-cache-key-collision
fix: forward/cache repeated query params (observer filters)
v0.13.4
2026-06-14 23:32:47 +01:00
Louis King fd55968b39 fix(web): forward repeated query params through the API proxy
The SPA reaches the backend via the web api_proxy, which forwarded query
params using dict(request.query_params). dict() on Starlette's QueryParams
multidict keeps only the last value of a repeated key, so a multi-valued
filter like ?observed_by=A&observed_by=B was forwarded to the backend as
observed_by=B only. The backend then filtered to B's events, making a
message observed only by A disappear as soon as B was also selected — the
reported "filters act like AND" symptom. This happens independently of the
Redis response cache.

Forward request.query_params.multi_items() (a list of (key, value) tuples)
so all repeated values reach the backend. Add web proxy regression tests
asserting both observed_by values are forwarded, and capture forwarded
params in the MockHttpClient.

This is the primary fix; the earlier cache-key change (multi_items in
sorted_query_string) addressed the same collapse pattern at the cache layer.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 23:30:35 +01:00
Louis King b9083d6bb7 fix(cache): preserve repeated query params in cache key
The API response cache key was built from request.query_params.items(),
which in Starlette keeps only the last value of a repeated key. Repeated
params like observed_by (?observed_by=A&observed_by=B) therefore collapsed
to observed_by=B in the key, colliding with any other filter set sharing the
same last value. The first response cached under that key was then served
for the whole TTL, making observer-filtered Messages/Adverts return stale,
wrong results (e.g. an A-only message vanishing when B was also enabled).

Use multi_items() so all repeated values are included, sorted by the full
(key, value) tuple so order-independent filter sets map to one key. Add
regression tests covering preservation, order-independence, and the
{A,B} vs {B} collision case.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 23:25:15 +01:00
Louis King 7603b16092 Fixed pre-commit issues 2026-06-14 22:58:52 +01:00
Louis King fd582ebbc2 Merge branch 'main' of github.com:ipnet-mesh/meshcore-hub into feat/postgres-support 2026-06-14 22:53:20 +01:00
JingleManSweep 9dc494ec54 Merge pull request #253 from ipnet-mesh/renovate/actions-cache-5.x
chore(deps): update actions/cache action to v5
2026-06-14 22:25:48 +01:00
renovate[bot] 1a94da9f32 chore(deps): update actions/cache action to v5 2026-06-14 21:23:48 +00:00
JingleManSweep 62fa83b9e0 Merge pull request #254 from ipnet-mesh/fix/dockerfile-oci-source-label
fix(docker): correct OCI source label to ipnet-mesh/meshcore-hub
2026-06-14 22:22:57 +01:00
Louis King 0abee2f4e5 fix(docker): correct OCI source label to ipnet-mesh/meshcore-hub
The org.opencontainers.image.source label was pointing at
meshcore-dev/meshcore-hub (the upstream MeshCore project's org)
instead of this repo's canonical location at ipnet-mesh/meshcore-hub.
Verify via: docker inspect ghcr.io/ipnet-mesh/meshcore-hub:main
2026-06-14 22:20:58 +01:00
JingleManSweep c8547a7720 Merge pull request #252 from ipnet-mesh/chore/ci-workflow-optimisations
chore(ci): optimise GitHub workflows
2026-06-14 22:19:03 +01:00
Louis King 5866428f69 chore(ci): optimise GitHub workflows
Add concurrency (PR-cancel only), dependency caching, timeouts, and
path filters across all four workflows. Pin opencode action to v1.17.7
and tighten the /oc trigger. Skip MQTT broker rebuild when upstream
SHA is unchanged. Gate sdist/wheel build job to main-only pushes.
2026-06-14 22:16:57 +01:00
JingleManSweep bd8d62fa9f Merge pull request #251 from ipnet-mesh/chore/faster-pytest
chore(tests): speed up pytest from >2min to ~12s
2026-06-14 22:06:02 +01:00
Louis King 96a78d79f6 chore(tests): speed up pytest from >2min to ~12s
- Default-off coverage in pyproject.toml addopts; opt-in via make test-cov
- Add pytest-xdist for parallel execution (make test = pytest -nauto --no-cov)
- Promote API test fixtures to session/module scope (engine, app, mocks);
  per-test isolation via table truncation instead of schema rebuild
- Remove Makefile include .env/export that leaked config vars into tests;
  docker-compose reads .env natively
- Add _ignore_dotenv autouse fixture: disables env_file, clears leaked env
  vars from Settings fields and Click CLI envvars
- Patch time.sleep in 3 subscriber scheduler tests (~3s -> ~0.03s)
- Fix pytest.raises(Exception, match='') warning -> IntegrityError
- Add .venv activation to .envrc
- Suppress warn_unused_ignores for tests in mypy config (single-file
  pre-commit checks lack full-project context)
2026-06-14 22:03:30 +01:00
Louis King 34b6e6b328 test: cover Postgres migration helper and CLI command
Raise patch coverage on the v0.14.0 Postgres backend:

- test_db_migrate: _is_superuser plus the full migrate_sqlite_to_postgres
  flow (dry-run, copy, non-empty-target refusal, --truncate, missing schema),
  routing create_database_engine to SQLite files so a postgresql:// target
  satisfies the guard while the run executes SQLite -> SQLite in CI.
- test_main: the `db migrate-to-postgres` CLI command via CliRunner
  (success, dry-run, row-count mismatch, ValueError -> ClickException).
- conftest: neutralise dotenv.load_dotenv before collection. Importing the
  CLI entrypoint runs load_dotenv() at import time, which leaked a local .env
  into os.environ and broke unrelated config/redis tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 21:11:02 +01:00
Louis King 93d894888c Merge branch 'main' of github.com:ipnet-mesh/meshcore-hub into feat/postgres-support 2026-06-14 20:38:42 +01:00
JingleManSweep 6c85ea04c0 Merge pull request #250 from ipnet-mesh/chore/nix-support
Added nix-shell support
2026-06-14 20:37:32 +01:00
Louis King 3f6c9bceac Added nix-shell support 2026-06-14 20:27:52 +01:00
JingleManSweep ab1cd6e541 Merge pull request #249 from ipnet-mesh/fix/collapse-message-newlines
fix(web): collapse newlines in rendered message text
v0.13.3
2026-06-14 19:59:03 +01:00
Louis King 434d78e24a fix(web): collapse newlines in rendered message text
Messages containing runs of newlines were preserved by the pre-wrap
styling on the messages view, stretching table rows and cards. Collapse
any run of newlines (and surrounding whitespace) into a single space at
display time, leaving stored text untouched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 19:56:40 +01:00
JingleManSweep adc7f77dfc Merge pull request #247 from ipnet-mesh/chore/python-requires
Updated Python Requirements
v0.13.2
2026-06-14 18:47:03 +01:00
Louis King 13ab4682ed Updates 2026-06-14 18:42:55 +01:00
JingleManSweep 6c4c98236d Merge pull request #245 from ipnet-mesh/feat/system-announcement-maintenance
feat(web): system announcement banner and maintenance mode
2026-06-14 18:37:03 +01:00
Louis King 5015946ab5 fix(deps): pin fastapi below 0.137.0 to fix CI route introspection
FastAPI 0.137.0 regressed include_router: endpoints registered via
app.include_router no longer appear in app.routes (they still serve, but
introspection breaks). tests/test_api/test_app_factory.py asserts on
app.routes, so CI (which pip-installs the latest fastapi) failed on the
/metrics route check while local pinned 0.136.3 passed. Constrain fastapi
to <0.137.0 until a fixed release is available.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 18:34:51 +01:00
Louis King 2563ad4cf2 test(web): isolate SYSTEM_ANNOUNCEMENT/SYSTEM_MAINTENANCE from local .env
The web_app fixture already neutralizes NETWORK_ANNOUNCEMENT so a developer's
local .env does not leak into tests. Do the same for the two new system
settings, fixing test_system_banner_absent_when_none when SYSTEM_ANNOUNCEMENT
is set in the environment.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 17:55:17 +01:00
Louis King 22a5ed26d5 fix(web): center the system announcement banner like the network banner
Apply the #flash-banner flex centering rule to #system-banner so it matches
the network announcement layout.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 17:48:31 +01:00
Louis King 413e3f7e7b docs: add v0.13.0 upgrade notes for system announcement and maintenance mode
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 17:47:55 +01:00
Louis King ebad9013d3 feat(web): pass SYSTEM_ANNOUNCEMENT/SYSTEM_MAINTENANCE through docker-compose
Wire the two new web settings into the web service env block, matching the
NETWORK_ANNOUNCEMENT passthrough. SYSTEM_MAINTENANCE defaults to false.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 17:47:26 +01:00
Louis King 17e6b65f8c feat(web): add system announcement banner and maintenance mode
Add two operator-controlled, startup-time settings:

- SYSTEM_ANNOUNCEMENT: non-dismissable Markdown banner rendered above the
  network announcement on every page (navbar -> system -> network).
- SYSTEM_MAINTENANCE: when enabled, forces all feature flags off so the nav
  collapses to Home, hides the profile menu, and the SPA renders a maintenance
  page for every route. The maintenance page makes no API calls, so the API
  and database can be offline while the web component keeps running.

CLI exposes --system-announcement and tri-state --system-maintenance; the bool
falls back to pydantic settings to parse SYSTEM_MAINTENANCE reliably from env.
Adds i18n strings (en/nl), tests, and docs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 17:42:42 +01:00
JingleManSweep 5d2f0b90d7 Merge pull request #244 from ipnet-mesh/feat/observer-filter-badges
feat(web): observer filter as toggle badges on adverts/messages
v0.13.1
2026-06-14 12:59:30 +01:00
Louis King 56696bdcd6 feat(web): observer filter as toggle badges on adverts/messages
Replace the multi-select Observer dropdown buried in the filter panel with
a row of clickable observer badges rendered between the filter panel and the
data list (and below the Sorting dropdown on mobile).

- Selection is stored in localStorage (shared across Adverts and Messages)
  as the disabled set, so new observers default to enabled.
- Badge style reflects enabled (filled) vs disabled (muted) state; the last
  enabled observer cannot be toggled off.
- Observer filter is sourced from localStorage instead of the URL query;
  a two-phase fetch resolves the enabled include-list (the API filters by
  inclusion only) before fetching data, with no flash of unfiltered results.
- Toggling re-scopes data and resets to page 1.
- Add enable/disable tooltip strings (en/nl) and the previously-missing
  Dutch observer label.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 12:47:57 +01:00