Commit graph

46 commits

Author SHA1 Message Date
704c1aa7fa met_office test: full Spot/Forecast x daily/hourly/three-hourly matrix
First guess (Forecast key -> hourly specifically) was tried and confirmed
wrong live. Rather than guess again, the test route now checks every
configured key against every endpoint and reports the full 2x3 matrix, so
the actual working combination is visible on screen instead of assumed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-25 13:43:24 +00:00
210649d7ad met_office: add forecast_api_key, a second Met Office DataHub key
Met Office DataHub sells 'Global Spot' and 'Forecast' as separate
subscriptions with separate keys. api_key (existing) is Spot, used for the
daily endpoint (confirmed live via hvac). forecast_api_key is new and
optional -- weather's hourly/three-hourly endpoints try it first, falling
back to api_key, then Open-Meteo, per tier.

Test route now checks each key against the endpoint it's actually used for
(Spot -> daily, Forecast -> hourly) rather than only ever testing api_key
against daily.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-25 13:35:17 +00:00
5f84a44d1f Add hotel_location and met_office integrations
Two new schema-driven integrations for hvac's weather compensation
feature: hotel_location (lat/lon, plaintext config) and met_office
(encrypted api_key). Both surface automatically on the portal's
Integrations tab via the existing generic integration UI/routes — no
portal changes needed. Deliberately central here rather than hvac-local
so other apps can reuse the hotel's location later. Added a met_office
connection test (DataHub daily-forecast call) alongside the others.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-25 11:01:46 +00:00
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