diff --git a/apps/portal/nuxt.config.ts b/apps/portal/nuxt.config.ts
index 386f54c..b2d1e48 100644
--- a/apps/portal/nuxt.config.ts
+++ b/apps/portal/nuxt.config.ts
@@ -1,6 +1,14 @@
// Nuxt 3 configuration for Dezky portal
// https://nuxt.com/docs/api/configuration/nuxt-config
+// Base URLs are environment-driven so one build runs in dev (.local) and
+// production (.eu). NUXT_PUBLIC_AUTH_URL / NUXT_PUBLIC_PORTAL_URL are set at
+// BUILD (CI) and RUNTIME (fleet/apps/portal.yaml + portal-secrets); the .local
+// defaults keep local dev working with no env.
+const AUTH_URL = (process.env.NUXT_PUBLIC_AUTH_URL || 'https://auth.dezky.local').replace(/\/$/, '')
+const PORTAL_URL = (process.env.NUXT_PUBLIC_PORTAL_URL || 'https://app.dezky.local').replace(/\/$/, '')
+const PORTAL_OIDC_APP_SLUG = process.env.PORTAL_OIDC_APP_SLUG || 'dezky-portal'
+
export default defineNuxtConfig({
compatibilityDate: '2026-01-01',
devtools: { enabled: true },
@@ -40,6 +48,7 @@ export default defineNuxtConfig({
public: {
authUrl: process.env.NUXT_PUBLIC_AUTH_URL,
portalUrl: process.env.NUXT_PUBLIC_PORTAL_URL,
+ bookingUrl: process.env.NUXT_PUBLIC_BOOKING_URL || 'https://booking.dezky.local',
},
},
@@ -69,10 +78,10 @@ export default defineNuxtConfig({
// NUXT_OIDC_* per-container; locally we just read them directly.
clientId: process.env.PORTAL_OIDC_CLIENT_ID || process.env.NUXT_OIDC_CLIENT_ID || '',
clientSecret: process.env.PORTAL_OIDC_CLIENT_SECRET || process.env.NUXT_OIDC_CLIENT_SECRET || '',
- redirectUri: process.env.NUXT_OIDC_REDIRECT_URI || 'https://app.dezky.local/auth/oidc/callback',
- authorizationUrl: 'https://auth.dezky.local/application/o/authorize/',
- tokenUrl: 'https://auth.dezky.local/application/o/token/',
- userInfoUrl: 'https://auth.dezky.local/application/o/userinfo/',
+ redirectUri: process.env.NUXT_OIDC_REDIRECT_URI || `${PORTAL_URL}/auth/oidc/callback`,
+ authorizationUrl: `${AUTH_URL}/application/o/authorize/`,
+ tokenUrl: `${AUTH_URL}/application/o/token/`,
+ userInfoUrl: `${AUTH_URL}/application/o/userinfo/`,
// Logout is handled by our custom /api/auth/sign-out endpoint, not the
// module's RP-initiated chain. Authentik 2025.10 doesn't reliably
// honor `post_logout_redirect_uri` from the provider invalidation
@@ -82,7 +91,7 @@ export default defineNuxtConfig({
logoutUrl: '',
// Discovery URL — used by id_token validation to fetch JWKS + issuer
openIdConfiguration:
- 'https://auth.dezky.local/application/o/dezky-portal/.well-known/openid-configuration',
+ `${AUTH_URL}/application/o/${PORTAL_OIDC_APP_SLUG}/.well-known/openid-configuration`,
// offline_access asks Authentik for a refresh token. Without it there's
// nothing to refresh with, so session.automaticRefresh can't run and the
// module's refresh() falls back to a full login() redirect on token
diff --git a/apps/portal/pages/admin/scheduling.vue b/apps/portal/pages/admin/scheduling.vue
index d7e7639..75377a1 100644
--- a/apps/portal/pages/admin/scheduling.vue
+++ b/apps/portal/pages/admin/scheduling.vue
@@ -49,7 +49,8 @@ const toast = useToast()
const { tenant } = useTenant()
const slug = computed(() => tenant.value?.slug ?? '')
const { request } = useApiFetch()
-const bookingBase = 'https://booking.dezky.local'
+const bookingBase = (useRuntimeConfig().public.bookingUrl as string) || 'https://booking.dezky.local'
+const bookingHost = bookingBase.replace(/^https?:\/\//, '')
const base = computed(() => `/api/tenants/${slug.value}/scheduling`)
@@ -894,7 +895,7 @@ const maskSecret = (s: string) => (s.length > 12 ? `${s.slice(0, 9)}…${s.slice
Used in the public booking link.
Lowercase letters, numbers and hyphens (2–40 chars).
“{{ hostForm.slug }}” is already taken.
- booking.dezky.local/{{ slug }}/{{ hostForm.slug }} · available ✓
+ {{ bookingHost }}/{{ slug }}/{{ hostForm.slug }} · available ✓