diff --git a/server/src/plugins/service-admin-http/middleware.ts b/server/src/plugins/service-admin-http/middleware.ts index 3708dc9..ae4b8c2 100644 --- a/server/src/plugins/service-admin-http/middleware.ts +++ b/server/src/plugins/service-admin-http/middleware.ts @@ -65,12 +65,8 @@ export function registerMiddleware(app: H3, deps: AdminDeps): void { const tenant = await deps.repo.getTenantBySlug(tenantSlug); if (tenant && tenant.is_active) { event.context.tenant = tenant; - // Set PG search_path to the tenant's schema. - if (tenant.schema_name !== "public") { - await deps.repo.adapter.setSearchPath(tenant.schema_name); - } + await deps.repo.adapter.setSearchPath(tenant.schema_name); } else { - // Fall back to default tenant. const defaultTenant = await deps.repo.getTenantBySlug("default"); if (defaultTenant) { event.context.tenant = defaultTenant; diff --git a/server/src/plugins/service-admin-http/routes-admin.ts b/server/src/plugins/service-admin-http/routes-admin.ts index a321cad..dd272d1 100644 --- a/server/src/plugins/service-admin-http/routes-admin.ts +++ b/server/src/plugins/service-admin-http/routes-admin.ts @@ -2219,6 +2219,21 @@ export function registerAdminRoutes(app: H3, deps: AdminDeps): void { return new Response(null, { status: 302, headers: { location: `/admin/kiosks/${id}` } }); }); + // ---- Tenant switcher fragment (htmx) ---------------------------------------- + app.get("/admin/_tenant_switcher", async (event) => { + const tenants = await deps.repo.listTenants(); + if (tenants.length <= 1) return new Response("", { headers: { "content-type": "text/html" } }); + const current = (event.context as any).tenant?.slug ?? "default"; + const options = tenants.map((t: any) => + `` + ).join(""); + const html = `
`; + return new Response(html, { headers: { "content-type": "text/html" } }); + }); + // ---- JSON API (admin scope) — used by Node-RED bf-* nodes --------------- // // All payloads run through `stripSecrets` so credential-bearing fields diff --git a/server/src/shared/db/pg-adapter.ts b/server/src/shared/db/pg-adapter.ts index 4524ded..fdc931e 100644 --- a/server/src/shared/db/pg-adapter.ts +++ b/server/src/shared/db/pg-adapter.ts @@ -13,9 +13,9 @@ import type { DbAdapter, RunResult, Row, SqlValue } from "./db-adapter.js"; export class PgAdapter implements DbAdapter { private readonly pool: Pool; - /** Per-async-context client when inside transaction(). */ private currentTxClient: PoolClient | null = null; private txDepth = 0; + private searchPath = "public"; constructor(connectionString: string, poolMax: number = 10) { this.pool = new Pool({ @@ -70,8 +70,12 @@ export class PgAdapter implements DbAdapter { private async runner