feat: migrate routing to rspace.online/rcal basePath
Move from standalone rcal.online domain to path-based routing under rspace.online/rcal. Updates Traefik labels for primary path-based routing while keeping subdomain routing for spaces on rcal.online. Adds basePath /rcal to next.config.js and updates middleware to handle both rcal.online and rspace.online subdomain patterns. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e480a693f5
commit
2cc63c9b23
|
|
@ -11,6 +11,9 @@ services:
|
||||||
- INFISICAL_CLIENT_ID=${INFISICAL_CLIENT_ID}
|
- INFISICAL_CLIENT_ID=${INFISICAL_CLIENT_ID}
|
||||||
- INFISICAL_CLIENT_SECRET=${INFISICAL_CLIENT_SECRET}
|
- INFISICAL_CLIENT_SECRET=${INFISICAL_CLIENT_SECRET}
|
||||||
- INFISICAL_PROJECT_SLUG=rcal-online
|
- INFISICAL_PROJECT_SLUG=rcal-online
|
||||||
|
- GOOGLE_CLIENT_ID=${GOOGLE_CLIENT_ID}
|
||||||
|
- GOOGLE_CLIENT_SECRET=${GOOGLE_CLIENT_SECRET}
|
||||||
|
- GOOGLE_OAUTH_REDIRECT_URI=https://rspace.online/rcal/api/auth/google/callback
|
||||||
depends_on:
|
depends_on:
|
||||||
rcal-postgres:
|
rcal-postgres:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
|
@ -23,10 +26,16 @@ services:
|
||||||
- /tmp
|
- /tmp
|
||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.http.routers.rcal.rule=Host(`rcal.jeffemmett.com`) || Host(`rcal.online`) || Host(`www.rcal.online`) || Host(`booking.xhiva.art`) || HostRegexp(`{subdomain:[a-z0-9-]+}.rcal.online`)"
|
# Primary: path-based routing under rspace.online
|
||||||
- "traefik.http.routers.rcal.priority=130"
|
- "traefik.http.routers.rcal.rule=(Host(`rspace.online`) || HostRegexp(`{subdomain:[a-z0-9-]+}.rspace.online`)) && PathPrefix(`/rcal`)"
|
||||||
|
- "traefik.http.routers.rcal.priority=140"
|
||||||
- "traefik.http.routers.rcal.entrypoints=web"
|
- "traefik.http.routers.rcal.entrypoints=web"
|
||||||
- "traefik.http.services.rcal.loadbalancer.server.port=3000"
|
- "traefik.http.services.rcal.loadbalancer.server.port=3000"
|
||||||
|
# Subdomain routing for spaces: {space}.rcal.online
|
||||||
|
- "traefik.http.routers.rcal-spaces.rule=HostRegexp(`{subdomain:[a-z0-9-]+}.rcal.online`)"
|
||||||
|
- "traefik.http.routers.rcal-spaces.priority=130"
|
||||||
|
- "traefik.http.routers.rcal-spaces.entrypoints=web"
|
||||||
|
- "traefik.http.routers.rcal-spaces.service=rcal"
|
||||||
networks:
|
networks:
|
||||||
- traefik-public
|
- traefik-public
|
||||||
- rcal-internal
|
- rcal-internal
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
const nextConfig = {
|
const nextConfig = {
|
||||||
reactStrictMode: true,
|
reactStrictMode: true,
|
||||||
output: 'standalone',
|
output: 'standalone',
|
||||||
|
basePath: '/rcal',
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = nextConfig
|
module.exports = nextConfig
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,11 @@ import type { NextRequest } from 'next/server';
|
||||||
* Middleware to handle subdomain-based routing.
|
* Middleware to handle subdomain-based routing.
|
||||||
*
|
*
|
||||||
* Routes:
|
* Routes:
|
||||||
* - rcal.online -> landing page (/)
|
* - rspace.online/rcal/* -> primary (basePath handles this)
|
||||||
* - www.rcal.online -> landing page (/)
|
* - rcal.online -> redirected by rspace-redirects to rspace.online/rcal
|
||||||
* - demo.rcal.online -> calendar demo (/demo)
|
* - demo.rcal.online -> calendar demo (/demo)
|
||||||
* - <space>.rcal.online -> rewrite to /s/<space>
|
* - <space>.rcal.online -> rewrite to /s/<space>
|
||||||
|
* - <space>.rspace.online/rcal/* -> rewrite to /s/<space>
|
||||||
*
|
*
|
||||||
* Also handles localhost for development.
|
* Also handles localhost for development.
|
||||||
*/
|
*/
|
||||||
|
|
@ -18,12 +19,22 @@ export function middleware(request: NextRequest) {
|
||||||
|
|
||||||
let subdomain: string | null = null;
|
let subdomain: string | null = null;
|
||||||
|
|
||||||
// Match production: <sub>.rcal.online
|
// Match subdomain from rcal.online: <sub>.rcal.online
|
||||||
const match = hostname.match(/^([a-z0-9][a-z0-9-]*[a-z0-9]|[a-z0-9])\.\w+\.online/);
|
const rcalMatch = hostname.match(/^([a-z0-9][a-z0-9-]*[a-z0-9]|[a-z0-9])\.rcal\.online/);
|
||||||
if (match && match[1] !== 'www') {
|
if (rcalMatch && rcalMatch[1] !== 'www') {
|
||||||
subdomain = match[1];
|
subdomain = rcalMatch[1];
|
||||||
} else if (hostname.includes('localhost')) {
|
}
|
||||||
// Development: <sub>.localhost:port
|
|
||||||
|
// Match subdomain from rspace.online: <sub>.rspace.online
|
||||||
|
if (!subdomain) {
|
||||||
|
const rspaceMatch = hostname.match(/^([a-z0-9][a-z0-9-]*[a-z0-9]|[a-z0-9])\.rspace\.online/);
|
||||||
|
if (rspaceMatch && rspaceMatch[1] !== 'www' && rspaceMatch[1] !== 'registry') {
|
||||||
|
subdomain = rspaceMatch[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Development: <sub>.localhost:port
|
||||||
|
if (!subdomain && hostname.includes('localhost')) {
|
||||||
const parts = hostname.split('.localhost')[0].split('.');
|
const parts = hostname.split('.localhost')[0].split('.');
|
||||||
if (parts.length > 0 && parts[0] !== 'localhost') {
|
if (parts.length > 0 && parts[0] !== 'localhost') {
|
||||||
subdomain = parts[parts.length - 1];
|
subdomain = parts[parts.length - 1];
|
||||||
|
|
@ -31,7 +42,7 @@ export function middleware(request: NextRequest) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subdomain && subdomain.length > 0) {
|
if (subdomain && subdomain.length > 0) {
|
||||||
// demo.rcal.online → serve the calendar demo
|
// demo subdomain → serve the calendar demo
|
||||||
if (subdomain === 'demo') {
|
if (subdomain === 'demo') {
|
||||||
if (url.pathname === '/') {
|
if (url.pathname === '/') {
|
||||||
url.pathname = '/demo';
|
url.pathname = '/demo';
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue