Skip to content

Latest commit

 

History

History
113 lines (92 loc) · 6.55 KB

File metadata and controls

113 lines (92 loc) · 6.55 KB

Plan — Issue #3770: Improve MeshCore Region/Scope Management

STATUS: IMPLEMENTED. All 4 capabilities delivered. New global table meshcore_saved_regions (migration 108) + repository + routes + 4 UI touchpoints. Full vitest suite green (excluding 4 pre-existing environmental spiderfier suites that fail on the base tree too).

Goal

A user-maintained list of saved MeshCore regions, plus four UI touchpoints to use it:

  1. Save a region reported by a repeater (from the discover-regions UI).
  2. Add / Delete entries manually in a management list.
  3. Channel settings: pick a saved region to set the channel scope (instead of typing).
  4. Per-message: pick a saved region to override the scope for one message.

Data model

New table: meshcore_saved_regions — GLOBAL (no sourceId)

Rationale: a scope is a transport code derived purely from a region name (sha256("#region")[:16]). It is not tied to a source/node — the same physical mesh region applies across every MeshCore source. This mirrors channel_database and automations, which are global-by-design. The saved list is a convenience catalog of names; uniqueness is on the (normalized) name.

Columns (all 3 backends, following automations style):

column type notes
id INTEGER PK autoincrement (SQLite/PG); MySQL int auto_increment
name TEXT / varchar(64) region name, stored WITHOUT leading #, normalized (lowercase letters/digits/hyphen)
note TEXT nullable optional user note / where it came from
createdAt INTEGER / bigint
updatedAt INTEGER / bigint

Unique index on name (case-insensitive handled in repo by normalizing before insert/lookup).

Migration 108_meshcore_saved_regions

  • src/server/migrations/108_meshcore_saved_regions.ts: CREATE TABLE IF NOT EXISTS for all three backends (idempotent), unique index on name.
  • Register in src/db/migrations.ts (number 108, name meshcore_saved_regions, settingsKey migration_108_meshcore_saved_regions).
  • Bump src/db/migrations.test.ts: count 107 → 108, last name meshcore_saved_regions.

Schema wiring

  • src/db/schema/savedRegions.tssavedRegionsSqlite/Postgres/Mysql + inferred types.
  • Export from src/db/schema/index.ts.
  • Register in src/db/activeSchema.ts (import, add to active select map under key meshcoreSavedRegions, add to ActiveSchema type).

Repository

  • src/db/repositories/savedRegions.tsSavedRegionsRepository extends BaseRepository:
    • getAllAsync(): Promise<DbSavedRegion[]> (ordered by name)
    • getByNameAsync(name)
    • addAsync(name, note?): Promise<DbSavedRegion> — normalize name, idempotent upsert (return existing if name already saved)
    • deleteAsync(id): Promise<void>
    • Name normalization helper: strip leading #, lowercase, keep [a-z0-9-], reject empty.
  • Wire into DatabaseService: import, public savedRegionsRepo, get savedRegions() getter, init in initialize().

Backend API routes (src/server/routes/meshcoreRoutes.ts)

These are catalog-management routes; they touch the DB only (no node IO), but live under the meshcore router for cohesion. Region management is global, so they are NOT under /sources/:id — use a top-level meshcore regions router OR mount on the existing meshcore router with permission requirePermission('settings','write') (read = 'settings','read'). Confirm existing top-level mounting; if the meshcore router is always source-scoped, add routes under /sources/:id/meshcore/saved-regions operating on the global table (sourceId ignored) to reuse existing auth wiring.

  • GET .../saved-regions → list ({ success, regions: DbSavedRegion[] })
  • POST .../saved-regions → body { name, note? } → add → { success, region }
  • DELETE .../saved-regions/:id → delete → { success }

Frontend

API client (useMeshCore.ts)

Add to the hook: savedRegions: DbSavedRegion[], fetchSavedRegions(), addSavedRegion(name, note?), deleteSavedRegion(id). Use csrfFetch against ${mcPrefix}/saved-regions. Fetch on mount alongside other initial loads.

Touchpoint 1 — Save from repeater discovery (MeshCoreSettingsView.tsx)

The discovered-regions chips (~L272-291) already render per region. Add a small "+ Save" affordance per chip (or a save icon) that calls addSavedRegion(region). Disable/checkmark when already saved (cross-ref savedRegions).

Touchpoint 2 — Manage list (add/delete)

New section in MeshCoreSettingsView.tsx (region/scope area): list of saved regions, each with a delete button; an input + Add button to add manually. Reuse the normalization/validation already used for scope input.

Touchpoint 3 — Channel settings scope dropdown

Where the channel scope is set. Per the investigation, channel scope is set via updateChannelScope repo + the settings default-scope field; confirm the channel edit UI (MeshCoreChannelsConfigSection.tsx / channel edit modal). Replace/augment the manual scope text input with a <datalist> or <select> populated from savedRegions (free-typing still allowed for new names). Minimal change: add list= + <datalist> of saved regions to the existing scope input, matching the per-message override pattern already in MeshCoreChannelsView.tsx.

Touchpoint 4 — Per-message scope override dropdown

MeshCoreChannelsView.tsx (~L516-563) already has a scope-override <input> with a <datalist id="mc-scope-region-suggestions"> populated from discoveredRegions. Extend that datalist (or add the saved regions) so saved regions appear as suggestions — union of discoveredRegions + savedRegions, de-duplicated.

Tests

  • src/db/repositories/savedRegions.test.ts — add/normalize/dedupe/delete/list, name validation, global (no sourceId).
  • src/db/migrations.test.ts — count + last name update.
  • Route tests in the existing meshcore routes test file (or new) — list/add/delete happy path + auth + validation (mock DatabaseService.savedRegions.*Async).
  • UI: light test for the union/dedup of suggestions if non-trivial; otherwise rely on existing component tests compiling.

Verification

  • Full vitest suite (0 failures), tsc/build, lint. No system-test label (no device-comms change).

Scope notes / cut lines

  • This feature is DB + routes + UI only; no protocol/native-backend change → low risk.
  • If the channel-edit scope UI proves to not exist as a discrete field, deliver the datalist on the per-message override + the manage list + save-from-discovery, and note the channel-settings dropdown as a follow-up. All four are targeted though.