astro-tractstack 2.0.35 → 2.0.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/templates/src/middleware.ts +57 -9
package/package.json
CHANGED
|
@@ -9,6 +9,14 @@ interface Locals {
|
|
|
9
9
|
};
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
interface CacheEntry {
|
|
13
|
+
tenantId: string;
|
|
14
|
+
timestamp: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const domainCache = new Map<string, CacheEntry>();
|
|
18
|
+
const CACHE_TTL = 5 * 60 * 1000; // 5 minutes
|
|
19
|
+
|
|
12
20
|
export async function onRequest(
|
|
13
21
|
context: APIContext & { locals: Locals },
|
|
14
22
|
next: MiddlewareNext
|
|
@@ -16,37 +24,77 @@ export async function onRequest(
|
|
|
16
24
|
const isMultiTenantEnabled =
|
|
17
25
|
import.meta.env.PUBLIC_ENABLE_MULTI_TENANT === 'true';
|
|
18
26
|
|
|
19
|
-
// Only set tenant in locals if we have a trusted subdomain (production multi-tenant)
|
|
20
27
|
if (isMultiTenantEnabled && !import.meta.env.DEV) {
|
|
21
28
|
const hostname =
|
|
22
29
|
context.request.headers.get('x-forwarded-host') ||
|
|
23
30
|
context.request.headers.get('host');
|
|
24
31
|
|
|
25
32
|
if (hostname) {
|
|
26
|
-
|
|
33
|
+
let tenantId: string | null = null;
|
|
34
|
+
|
|
35
|
+
// Strategy 1: Regex Pattern (Fastest)
|
|
27
36
|
const parts = hostname.split('.');
|
|
28
37
|
if (
|
|
38
|
+
parts.length === 3 &&
|
|
39
|
+
['freewebpress', 'tractstack'].includes(parts[1]) &&
|
|
40
|
+
parts[2] === 'com'
|
|
41
|
+
) {
|
|
42
|
+
tenantId = parts[0];
|
|
43
|
+
} else if (
|
|
29
44
|
parts.length >= 4 &&
|
|
30
45
|
parts[1] === 'sandbox' &&
|
|
31
46
|
['freewebpress', 'tractstack'].includes(parts[2]) &&
|
|
32
47
|
parts[3] === 'com'
|
|
33
48
|
) {
|
|
34
|
-
|
|
49
|
+
tenantId = parts[0];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Strategy 2: Cache Lookup (Fast)
|
|
53
|
+
if (!tenantId) {
|
|
54
|
+
const cached = domainCache.get(hostname);
|
|
55
|
+
if (cached) {
|
|
56
|
+
if (Date.now() - cached.timestamp < CACHE_TTL) {
|
|
57
|
+
tenantId = cached.tenantId;
|
|
58
|
+
} else {
|
|
59
|
+
domainCache.delete(hostname);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Strategy 3: Backend Lookup (Fallback)
|
|
65
|
+
if (!tenantId) {
|
|
66
|
+
try {
|
|
67
|
+
// We assume the backend is always reachable on localhost:8080 in this architecture
|
|
68
|
+
const response = await fetch(
|
|
69
|
+
`http://127.0.0.1:8080/api/v1/resolve-domain?host=${encodeURIComponent(hostname)}`
|
|
70
|
+
);
|
|
71
|
+
if (response.ok) {
|
|
72
|
+
const data = await response.json();
|
|
73
|
+
if (data.tenantId) {
|
|
74
|
+
tenantId = data.tenantId;
|
|
75
|
+
domainCache.set(hostname, {
|
|
76
|
+
tenantId: data.tenantId,
|
|
77
|
+
timestamp: Date.now(),
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
} catch (error) {
|
|
82
|
+
console.error(`Failed to resolve domain ${hostname}:`, error);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (tenantId) {
|
|
35
87
|
context.locals.tenant = {
|
|
36
|
-
id:
|
|
88
|
+
id: tenantId,
|
|
37
89
|
domain: hostname,
|
|
38
90
|
isMultiTenant: true,
|
|
39
91
|
isLocalhost: false,
|
|
40
92
|
};
|
|
41
93
|
|
|
42
|
-
|
|
43
|
-
context.request.headers.set('X-Tenant-ID', parts[0]);
|
|
94
|
+
context.request.headers.set('X-Tenant-ID', tenantId);
|
|
44
95
|
}
|
|
45
96
|
}
|
|
46
97
|
}
|
|
47
98
|
|
|
48
|
-
// If no trusted subdomain detected, leave locals.tenant undefined
|
|
49
|
-
// This allows the cascading check to fall through to PUBLIC_TENANTID
|
|
50
|
-
|
|
51
99
|
return next();
|
|
52
100
|
}
|