@wopr-network/platform-core 1.72.2 → 1.72.4
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.
|
@@ -73,6 +73,7 @@ export function buildUpstreamHeaders(incoming, user, tenantSubdomain) {
|
|
|
73
73
|
if (host)
|
|
74
74
|
headers.set("host", host);
|
|
75
75
|
headers.set("x-platform-user-id", user.id);
|
|
76
|
+
headers.set("x-paperclip-user-id", user.id); // compat: Paperclip sidecar reads this header
|
|
76
77
|
headers.set("x-platform-tenant", tenantSubdomain);
|
|
77
78
|
if (user.email)
|
|
78
79
|
headers.set("x-platform-user-email", user.email);
|
|
@@ -81,17 +82,26 @@ export function buildUpstreamHeaders(incoming, user, tenantSubdomain) {
|
|
|
81
82
|
return headers;
|
|
82
83
|
}
|
|
83
84
|
/**
|
|
84
|
-
* Resolve the upstream container URL for a tenant subdomain
|
|
85
|
-
*
|
|
85
|
+
* Resolve the upstream container URL for a tenant subdomain.
|
|
86
|
+
*
|
|
87
|
+
* First checks the in-memory proxy route table (populated during
|
|
88
|
+
* provisioning). Falls back to deriving from the persistent profile
|
|
89
|
+
* data so that routing survives server restarts without needing
|
|
90
|
+
* Caddy or in-memory state.
|
|
86
91
|
*/
|
|
87
|
-
function resolveContainerUrl(container, subdomain) {
|
|
92
|
+
function resolveContainerUrl(container, subdomain, profile) {
|
|
88
93
|
if (!container.fleet)
|
|
89
94
|
return null;
|
|
95
|
+
// Fast path: in-memory route table (populated during provisioning)
|
|
90
96
|
const routes = container.fleet.proxy.getRoutes();
|
|
91
97
|
const route = routes.find((r) => r.subdomain === subdomain);
|
|
92
|
-
if (
|
|
93
|
-
return
|
|
94
|
-
|
|
98
|
+
if (route?.healthy) {
|
|
99
|
+
return `http://${route.upstreamHost}:${route.upstreamPort}`;
|
|
100
|
+
}
|
|
101
|
+
// Fallback: derive from persistent profile data (survives restarts)
|
|
102
|
+
const containerName = `wopr-${profile.name.replace(/_/g, "-")}`;
|
|
103
|
+
const port = profile.env?.PORT || "3100";
|
|
104
|
+
return `http://${containerName}:${port}`;
|
|
95
105
|
}
|
|
96
106
|
/**
|
|
97
107
|
* Create a tenant subdomain proxy middleware.
|
|
@@ -133,10 +143,10 @@ export function createTenantProxyMiddleware(container, config) {
|
|
|
133
143
|
if (!hasAccess) {
|
|
134
144
|
return c.json({ error: "Forbidden: not a member of this tenant" }, 403);
|
|
135
145
|
}
|
|
136
|
-
// Resolve fleet container URL
|
|
137
|
-
const upstream = resolveContainerUrl(container, subdomain);
|
|
146
|
+
// Resolve fleet container URL (route table or profile fallback)
|
|
147
|
+
const upstream = resolveContainerUrl(container, subdomain, profile);
|
|
138
148
|
if (!upstream) {
|
|
139
|
-
return c.json({ error: "
|
|
149
|
+
return c.json({ error: "Container unavailable" }, 503);
|
|
140
150
|
}
|
|
141
151
|
const url = new URL(c.req.url);
|
|
142
152
|
const targetUrl = `${upstream}${url.pathname}${url.search}`;
|
|
@@ -68,7 +68,23 @@ export function mountRoutes(app, container, config, plugins = []) {
|
|
|
68
68
|
if (container.fleet) {
|
|
69
69
|
app.use("*", createTenantProxyMiddleware(container, {
|
|
70
70
|
platformDomain: config.platformDomain,
|
|
71
|
-
resolveUser: async () =>
|
|
71
|
+
resolveUser: async (req) => {
|
|
72
|
+
try {
|
|
73
|
+
const { getAuth } = await import("../auth/better-auth.js");
|
|
74
|
+
const auth = getAuth();
|
|
75
|
+
const session = await auth.api.getSession({ headers: req.headers });
|
|
76
|
+
if (!session?.user)
|
|
77
|
+
return undefined;
|
|
78
|
+
return {
|
|
79
|
+
id: session.user.id,
|
|
80
|
+
email: session.user.email ?? undefined,
|
|
81
|
+
name: session.user.name ?? undefined,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
},
|
|
72
88
|
}));
|
|
73
89
|
}
|
|
74
90
|
}
|
package/package.json
CHANGED
|
@@ -95,6 +95,7 @@ export function buildUpstreamHeaders(incoming: Headers, user: ProxyUserInfo, ten
|
|
|
95
95
|
const host = incoming.get("host");
|
|
96
96
|
if (host) headers.set("host", host);
|
|
97
97
|
headers.set("x-platform-user-id", user.id);
|
|
98
|
+
headers.set("x-paperclip-user-id", user.id); // compat: Paperclip sidecar reads this header
|
|
98
99
|
headers.set("x-platform-tenant", tenantSubdomain);
|
|
99
100
|
if (user.email) headers.set("x-platform-user-email", user.email);
|
|
100
101
|
if (user.name) headers.set("x-platform-user-name", user.name);
|
|
@@ -102,15 +103,31 @@ export function buildUpstreamHeaders(incoming: Headers, user: ProxyUserInfo, ten
|
|
|
102
103
|
}
|
|
103
104
|
|
|
104
105
|
/**
|
|
105
|
-
* Resolve the upstream container URL for a tenant subdomain
|
|
106
|
-
*
|
|
106
|
+
* Resolve the upstream container URL for a tenant subdomain.
|
|
107
|
+
*
|
|
108
|
+
* First checks the in-memory proxy route table (populated during
|
|
109
|
+
* provisioning). Falls back to deriving from the persistent profile
|
|
110
|
+
* data so that routing survives server restarts without needing
|
|
111
|
+
* Caddy or in-memory state.
|
|
107
112
|
*/
|
|
108
|
-
function resolveContainerUrl(
|
|
113
|
+
function resolveContainerUrl(
|
|
114
|
+
container: PlatformContainer,
|
|
115
|
+
subdomain: string,
|
|
116
|
+
profile: { name: string; env?: Record<string, string> },
|
|
117
|
+
): string | null {
|
|
109
118
|
if (!container.fleet) return null;
|
|
119
|
+
|
|
120
|
+
// Fast path: in-memory route table (populated during provisioning)
|
|
110
121
|
const routes = container.fleet.proxy.getRoutes();
|
|
111
122
|
const route = routes.find((r) => r.subdomain === subdomain);
|
|
112
|
-
if (
|
|
113
|
-
|
|
123
|
+
if (route?.healthy) {
|
|
124
|
+
return `http://${route.upstreamHost}:${route.upstreamPort}`;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Fallback: derive from persistent profile data (survives restarts)
|
|
128
|
+
const containerName = `wopr-${profile.name.replace(/_/g, "-")}`;
|
|
129
|
+
const port = profile.env?.PORT || "3100";
|
|
130
|
+
return `http://${containerName}:${port}`;
|
|
114
131
|
}
|
|
115
132
|
|
|
116
133
|
/**
|
|
@@ -161,10 +178,10 @@ export function createTenantProxyMiddleware(
|
|
|
161
178
|
return c.json({ error: "Forbidden: not a member of this tenant" }, 403);
|
|
162
179
|
}
|
|
163
180
|
|
|
164
|
-
// Resolve fleet container URL
|
|
165
|
-
const upstream = resolveContainerUrl(container, subdomain);
|
|
181
|
+
// Resolve fleet container URL (route table or profile fallback)
|
|
182
|
+
const upstream = resolveContainerUrl(container, subdomain, profile);
|
|
166
183
|
if (!upstream) {
|
|
167
|
-
return c.json({ error: "
|
|
184
|
+
return c.json({ error: "Container unavailable" }, 503);
|
|
168
185
|
}
|
|
169
186
|
|
|
170
187
|
const url = new URL(c.req.url);
|
|
@@ -106,7 +106,21 @@ export function mountRoutes(
|
|
|
106
106
|
"*",
|
|
107
107
|
createTenantProxyMiddleware(container, {
|
|
108
108
|
platformDomain: config.platformDomain,
|
|
109
|
-
resolveUser: async () =>
|
|
109
|
+
resolveUser: async (req: Request) => {
|
|
110
|
+
try {
|
|
111
|
+
const { getAuth } = await import("../auth/better-auth.js");
|
|
112
|
+
const auth = getAuth();
|
|
113
|
+
const session = await auth.api.getSession({ headers: req.headers });
|
|
114
|
+
if (!session?.user) return undefined;
|
|
115
|
+
return {
|
|
116
|
+
id: session.user.id,
|
|
117
|
+
email: session.user.email ?? undefined,
|
|
118
|
+
name: session.user.name ?? undefined,
|
|
119
|
+
};
|
|
120
|
+
} catch {
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
},
|
|
110
124
|
}),
|
|
111
125
|
);
|
|
112
126
|
}
|