Commit graph

43 commits

Author SHA1 Message Date
9fce62525d settings: MQTT clients support multiple topic scopes on one connection
A single client sometimes legitimately needs several unrelated topic trees
(e.g. hvac-backend needs both shellies/# for TRVs and hvac/mhi/# for MHI
aircon) — the previous model only supported one topic_scope per client,
forcing either an overbroad '#' grant or multiple broker logins for one app.

topic_scope is now newline/comma-parsed into a list; each pattern gets its
own ACL grant on the client's role (createDynsecClient loops per topic).
Stored newline-joined (still a single TEXT column, no migration). Added
PATCH /mqtt-clients/:id (additive: add_topic_scope) to grant a client more
topics later without revoking/recreating it — backed by a new
addTopicsToRole() in mqtt-dynsec.js.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-13 14:01:07 +00:00
86f03aac0b settings: serve MQTT credentials to stack apps via service clients
MQTT Broker Clients gained a 'service consumer' flag. Device clients stay
show-once (a human copies the password into hardware). Service clients (a
stack app like hvac-backend that must re-read its own broker login on every
restart) get a STABLE username (= name slug, no random suffix) and their
password stored AES-256-GCM encrypted (crypto.js, same as integration
secrets), served at runtime via:

  GET /settings/api/internal/mqtt-client/:username   (bearer SETTINGS_SECRET)

This is the credential path the hvac plan always intended — show-once
genuinely can't feed a machine consumer. Revoke/delete drop the stored
secret; the internal endpoint refuses revoked clients; secret_enc is never
returned to the admin UI. Reject duplicate usernames up front with 409.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-13 13:42:50 +00:00
b7a8c31988 Revert mqtt_plant — go back to one shared MQTT integration
Per-app MQTT slugs meant a confusing mix of shared and per-app
credentials in one settings page. Consolidating on a single shared
identity instead — matches the original "MQTT is shared infra"
decision, and publish access stays scoped per-device regardless.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-29 07:50:29 +00:00
abaa57e0f1 Add per-app MQTT Broker (Plant) integration slug
Lets plant have its own dedicated MQTT credentials instead of sharing
the single central mqtt integration with utilities/hvac, so its
dynsec client can be scoped to plant/# without touching other apps'
access. Generalizes the test-connection dispatch to match any
mqtt_* slug, not just the literal 'mqtt' one.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-28 22:46:49 +00:00
0bdfe67bf5 Add self-service MQTT Broker Clients management
New /settings/api/mqtt-clients routes (list/create/revoke/delete) manage
per-consumer dynamic-security identities on the shared broker (LXC 104) —
name + topic scope + publish/subscribe flags in, generated username/password
out (shown once, never stored). Replaces manually running mosquitto_ctrl
over SSH by hand for every new device or app that needs broker access.

Implementation SSHs into the broker LXC and runs mosquitto_ctrl inside a
throwaway container on its Docker network (src/lib/ssh.js + mqtt-dynsec.js)
rather than reimplementing the dynamic-security plugin's JSON wire protocol
from scratch — reuses the exact commands verified by hand while wiring up
the water-softener and mqtt-inspector clients this session. Needs the
shared deploy SSH key mounted (Dockerfile/compose changes) and
MQTT_ADMIN_USER/PASS threaded in via stack-init.

mqtt_clients table is bookkeeping only (name/scope/active) — the broker's
own dynamic-security.json remains the source of truth for auth.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-28 17:11:35 +00:00
221be89667 Add MQTT Broker integration (config UI + connection test)
schema.js already had an uncommitted mqtt entry (host/port config,
username/password secrets) from a prior session — commits it now, adds
the missing test-connection case (routes/integrations.js), and the mqtt
package dependency it needs. The existing generic integration card UI in
portal's AdminSettings.tsx needs no changes — it renders any schema
entry automatically. initDb() auto-seeds the 'mqtt' row on next restart,
so the "MQTT Broker" card will appear in Settings -> Integrations.

Also adds a .gitignore — node_modules was untracked in this repo.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-28 14:58:53 +00:00
aaf857a5fd Add Anthropic as a central integration
Lets the Claude API key be managed once in Portal → Settings →
Integrations and shared across apps (forecasting, wages), instead of
each app storing its own copy.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-24 20:42:56 +00:00
1fde9f8613 Add Hosted Tables integration — configurable site URL syncs to auth
Registers hosted_tables in the integration schema with a single site_url config
field. On save, syncHostedTables() PATCHes auth to update restaurant_bookings
base_path and active state, so the portal app appears only when a URL is set.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-15 15:41:51 +00:00
f5723d20c8 Bump version to 1.0.3 (deploy flow test) 2026-07-14 15:41:04 +00:00
b470185a24 Bump version to 1.0.2 (deploy flow test) 2026-07-14 15:16:46 +00:00
8d3d66f8f0 Bump version to 1.0.1 (deploy test) 2026-07-14 15:10:32 +00:00
37c122dc99 Return message field on SambaPOS test success to confirm both SQL + GraphQL connected
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-14 13:55:32 +00:00
6dc5df45e5 Fix SambaPOS GraphQL token URL: derive from origin so /api/graphql suffix in endpoint field doesn't break it
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-14 13:45:52 +00:00
d6449d8b52 Fix SambaPOS GraphQL token endpoint: /Token (SambaPOS OAuth) not /connect/token, GraphQL at /api/graphql
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-14 13:40:09 +00:00
e6470ad865 Fix SambaPOS tests: switch SQL to mssql (named instance support), fix GraphQL token URL to use origin not full path
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-14 13:24:27 +00:00
f185b73678 Split SambaPOS into separate SQL + GraphQL credential sets, add both connection tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-14 13:09:04 +00:00
4f6fe6bb37 Add Nextcloud WebDAV directory browser endpoint
GET /settings/api/integrations/nextcloud/browse?path=<path>
Uses stored Nextcloud credentials to PROPFIND the given path and
returns a sorted list of subdirectories for the UI picker.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-14 11:09:42 +00:00
8a4c4182ec Add Nextcloud and Azure test connection handlers
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-14 11:05:17 +00:00
f43c46e794 Add backup_path config field to Nextcloud integration
Allows a Nextcloud subdirectory to be specified as the target for
stack-level backups run by the management backup service.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-14 11:03:47 +00:00
01e1f2407b Update azure integration: Azure AD → Azure Document Intelligence
Repurpose the azure slot from Azure AD (tenant_id/client_id/client_secret)
to Azure Document Intelligence (endpoint/api_key), matching the kitchen
app's OCR credential fields. The kitchen can now delegate its OCR credentials
to the central settings service.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-12 22:58:35 +00:00
558cce44fa Bust auth creds cache after saving workforce/smtp integrations
After a successful PUT to /integrations/workforce or /integrations/smtp,
fire-and-forget POST to auth's /internal/cache-bust so credential changes
(including default location) propagate instantly without a container restart.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-08 11:57:44 +00:00
71650ed904 SMTP: add reply_to config field
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 15:47:11 +00:00
0571613012 Fix locations endpoint base URL default: my.workforce.com not my.tanda.co
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 15:38:09 +00:00
b70491dea2 Workforce locations: fetch each location by ID for name, fall back gracefully
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 13:14:11 +00:00
c862c1f9e3 Workforce locations: derive from teams since /api/v2/locations may be billing-locked
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 13:11:17 +00:00
5a9a05bb62 Workforce locations: include Tanda error body in 502 response
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 12:59:29 +00:00
db1b168300 Workforce: add location_id config field + locations fetch endpoint
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 12:52:35 +00:00
24f3fcdc7b Workforce: bearer token only, remove email/password fields
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 12:42:49 +00:00
e758add8ae Support Workforce bearer token as alternative to email/password OAuth
Adds bearer_token secret field — if configured, the auth service uses it
directly and skips the OAuth password grant entirely. Falls back to
email/password flow if no token is set.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 12:34:52 +00:00
5053b37adf Add internal GET endpoint for global_config keys
Allows other services (e.g. room-planner) to read shared config
stored in settings_db without cross-database queries. Auth via
SETTINGS_SECRET bearer token, same pattern as /internal/integration.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-04 13:13:20 +00:00
9eaf05a4aa Workforce test: use platform scope
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-02 01:11:27 +00:00
fa7222a4c8 Workforce test: remove scope restriction to match auth service
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-02 01:08:31 +00:00
1ec923dc1f Workforce test: use department scope to match auth service
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-02 01:03:19 +00:00
49cd17f957 Use me+department scopes in Workforce connection test
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-02 00:57:01 +00:00
a6627ee763 Request department and staff scopes in Workforce connection test
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-02 00:55:17 +00:00
ed6a978a48 Add nodemailer dependency for SMTP connection test
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-02 00:42:27 +00:00
6f0a24787a Remove OAuth scope restriction from Workforce connection test
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-02 00:41:37 +00:00
e84b916ecf Fix Workforce connection test: use form-encoded OAuth body
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-02 00:35:55 +00:00
a886b4b30e Add connection tests for Workforce and SMTP integrations
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-02 00:33:48 +00:00
2339a75d07 Add Workforce and SMTP integrations to settings schema
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-02 00:24:11 +00:00
31f8bba377 Add internal service-to-service integration endpoint 2026-07-01 19:33:08 +00:00
f5f87fdd1f Always return all schema config fields in maskRow
Config object starts empty so the frontend had nothing to render for
plaintext fields (region, username etc). Now populates all schema-defined
fields with empty string when not yet saved.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-01 19:22:12 +00:00
e7b08ccf31 Initial settings service
Fastify API for storing third-party integration credentials (Newbook, Resos,
Nextcloud, Azure, SambaPOS) with AES-256-GCM encryption for sensitive fields.
Includes Newbook room sync endpoint and global_config store for shared app data.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-01 18:56:19 +00:00