@pablo2410/core-server 0.6.0 → 0.7.0

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/dist/auth.d.ts CHANGED
@@ -1,6 +1,12 @@
1
- import * as express from 'express';
2
1
  import { Request, CookieOptions } from 'express';
3
2
 
3
+ /**
4
+ * Types for the createSubdomainAuth factory.
5
+ *
6
+ * These types define the contract between the shared auth SDK and each
7
+ * subdomain's local database, role mapping, and portal enrichment logic.
8
+ */
9
+
4
10
  /** Payload stored inside the session JWT. */
5
11
  interface SessionPayload {
6
12
  openId: string;
@@ -24,42 +30,14 @@ interface PortalUser {
24
30
  companyId?: number;
25
31
  siteId?: number;
26
32
  }
27
- interface ExchangeTokenResponse {
28
- accessToken: string;
29
- tokenType: string;
30
- expiresIn: number;
31
- refreshToken?: string;
32
- scope: string;
33
- idToken: string;
34
- }
35
- interface GetUserInfoResponse {
36
- openId: string;
37
- projectId: string;
38
- name: string;
39
- email?: string | null;
40
- platform?: string | null;
41
- loginMethod?: string | null;
42
- }
43
- interface GetUserInfoWithJwtResponse {
44
- openId: string;
45
- projectId: string;
46
- name: string;
47
- email?: string | null;
48
- platform?: string | null;
49
- loginMethod?: string | null;
50
- }
51
33
  /** Environment variables required by the auth SDK. */
52
34
  interface AuthEnv {
53
- /** Manus app ID (VITE_APP_ID) */
54
- appId: string;
55
35
  /** JWT secret for signing local sessions (JWT_SECRET) */
56
36
  jwtSecret: string;
57
37
  /** Shared portal JWT secret for cross-subdomain SSO (PORTAL_JWT_SECRET) */
58
38
  portalJwtSecret: string;
59
39
  /** Cookie domain for cross-subdomain sharing, e.g. ".oplyticsdigital.net" */
60
40
  cookieDomain: string;
61
- /** Manus OAuth server URL */
62
- oAuthServerUrl: string;
63
41
  /** Owner's openId for auto-admin assignment */
64
42
  ownerOpenId: string;
65
43
  /** Portal API base URL for user enrichment */
@@ -99,7 +77,7 @@ interface UpsertUserPayload {
99
77
  * This decouples the auth SDK from any specific ORM or schema.
100
78
  */
101
79
  interface DbAdapter<TUser extends BaseUser = BaseUser> {
102
- /** Find a user by their Manus openId. Returns null/undefined if not found. */
80
+ /** Find a user by their openId. Returns null/undefined if not found. */
103
81
  getUserByOpenId(openId: string): Promise<TUser | null | undefined>;
104
82
  /** Create or update a user record. */
105
83
  upsertUser(payload: UpsertUserPayload): Promise<void>;
@@ -169,20 +147,12 @@ interface SubdomainAuth<TUser extends BaseUser = BaseUser> {
169
147
  * from portal → backfill enterprise. Returns the local user record.
170
148
  */
171
149
  authenticateRequest(req: Request): Promise<TUser>;
172
- /** Exchange an OAuth authorization code for tokens. */
173
- exchangeCodeForToken(code: string, state: string): Promise<ExchangeTokenResponse>;
174
- /** Get user info from Manus OAuth using an access token. */
175
- getUserInfo(accessToken: string): Promise<GetUserInfoResponse>;
176
- /** Get user info from Manus OAuth using a JWT. */
177
- getUserInfoWithJwt(jwtToken: string): Promise<GetUserInfoWithJwtResponse>;
178
150
  /** Fetch a user from the portal API by openId. */
179
151
  getPortalUser(openId: string): Promise<PortalUser | null>;
180
152
  /** Get cookie options for setting session cookies. */
181
153
  getSessionCookieOptions(req: Request): Pick<CookieOptions, "domain" | "httpOnly" | "path" | "sameSite" | "secure">;
182
154
  /** Validate a returnTo URL to prevent open-redirect attacks. */
183
155
  isValidReturnUrl(url: string): boolean;
184
- /** Register the /api/oauth/callback route on an Express app. */
185
- registerOAuthRoutes(app: express.Express): void;
186
156
  }
187
157
 
188
158
  /**
@@ -194,12 +164,10 @@ interface SubdomainAuth<TUser extends BaseUser = BaseUser> {
194
164
  * - Portal API user enrichment with X-Service-Key header
195
165
  * - Auto-create user from JWT payload if not in local DB
196
166
  * - Enterprise backfill from portal API
197
- * - Manus OAuth code exchange and user info
198
- * - OAuth callback route registration
199
167
  *
200
168
  * Based on SQDCP's battle-tested sdk.ts + cookies.ts.
201
169
  */
202
170
 
203
171
  declare function createSubdomainAuth<TUser extends BaseUser = BaseUser>(config: SubdomainAuthConfig<TUser>): SubdomainAuth<TUser>;
204
172
 
205
- export { type AuthEnv, type BaseUser, type DbAdapter, type ExchangeTokenResponse, type GetUserInfoResponse, type GetUserInfoWithJwtResponse, type PortalUser, type RoleMapper, type SessionPayload, type SubdomainAuth, type SubdomainAuthConfig, type UpsertUserPayload, createSubdomainAuth };
173
+ export { type AuthEnv, type BaseUser, type DbAdapter, type PortalUser, type RoleMapper, type SessionPayload, type SubdomainAuth, type SubdomainAuthConfig, type UpsertUserPayload, createSubdomainAuth };
package/dist/auth.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createSubdomainAuth
3
- } from "./chunk-LH6QZJM4.js";
3
+ } from "./chunk-B63ROBSD.js";
4
4
  export {
5
5
  createSubdomainAuth
6
6
  };
@@ -1,26 +1,7 @@
1
1
  // src/auth/createSubdomainAuth.ts
2
2
  var DEFAULT_COOKIE_NAME = "app_session_id";
3
3
  var ONE_YEAR_MS = 1e3 * 60 * 60 * 24 * 365;
4
- var AXIOS_TIMEOUT_MS = 3e4;
5
- var EXCHANGE_TOKEN_PATH = "/webdev.v1.WebDevAuthPublicService/ExchangeToken";
6
- var GET_USER_INFO_PATH = "/webdev.v1.WebDevAuthPublicService/GetUserInfo";
7
- var GET_USER_INFO_WITH_JWT_PATH = "/webdev.v1.WebDevAuthPublicService/GetUserInfoWithJwt";
8
4
  var isNonEmptyString = (value) => typeof value === "string" && value.length > 0;
9
- function deriveLoginMethod(platforms, fallback) {
10
- if (fallback && fallback.length > 0) return fallback;
11
- if (!Array.isArray(platforms) || platforms.length === 0) return null;
12
- const set = new Set(
13
- platforms.filter((p) => typeof p === "string")
14
- );
15
- if (set.has("REGISTERED_PLATFORM_EMAIL")) return "email";
16
- if (set.has("REGISTERED_PLATFORM_GOOGLE")) return "google";
17
- if (set.has("REGISTERED_PLATFORM_APPLE")) return "apple";
18
- if (set.has("REGISTERED_PLATFORM_MICROSOFT") || set.has("REGISTERED_PLATFORM_AZURE"))
19
- return "microsoft";
20
- if (set.has("REGISTERED_PLATFORM_GITHUB")) return "github";
21
- const first = Array.from(set)[0];
22
- return first ? first.toLowerCase() : null;
23
- }
24
5
  function createSubdomainAuth(config) {
25
6
  const {
26
7
  env,
@@ -59,16 +40,6 @@ function createSubdomainAuth(config) {
59
40
  }
60
41
  return _axios;
61
42
  }
62
- let oauthClient = null;
63
- async function getOAuthClient() {
64
- if (oauthClient) return oauthClient;
65
- const axios = await getAxios();
66
- oauthClient = axios.create({
67
- baseURL: env.oAuthServerUrl,
68
- timeout: AXIOS_TIMEOUT_MS
69
- });
70
- return oauthClient;
71
- }
72
43
  let portalClient = null;
73
44
  function getPortalClient() {
74
45
  if (portalClient) return portalClient;
@@ -143,7 +114,7 @@ function createSubdomainAuth(config) {
143
114
  }
144
115
  async function createSessionToken(openId, options = {}) {
145
116
  return signSession(
146
- { openId, appId: env.appId, name: options.name || "" },
117
+ { openId, appId: serviceName, name: options.name || "" },
147
118
  options
148
119
  );
149
120
  }
@@ -251,44 +222,6 @@ function createSubdomainAuth(config) {
251
222
  portalUserId: portalUser.id
252
223
  };
253
224
  }
254
- async function exchangeCodeForToken(code, state) {
255
- const client = await getOAuthClient();
256
- const redirectUri = atob(state);
257
- const { data } = await client.post(
258
- EXCHANGE_TOKEN_PATH,
259
- {
260
- clientId: env.appId,
261
- grantType: "authorization_code",
262
- code,
263
- redirectUri
264
- }
265
- );
266
- return data;
267
- }
268
- async function getUserInfo(accessToken) {
269
- const client = await getOAuthClient();
270
- const { data } = await client.post(
271
- GET_USER_INFO_PATH,
272
- { accessToken }
273
- );
274
- const loginMethod = deriveLoginMethod(
275
- data?.platforms,
276
- data?.platform ?? data.platform ?? null
277
- );
278
- return { ...data, platform: loginMethod, loginMethod };
279
- }
280
- async function getUserInfoWithJwt(jwtToken) {
281
- const client = await getOAuthClient();
282
- const { data } = await client.post(
283
- GET_USER_INFO_WITH_JWT_PATH,
284
- { jwtToken, projectId: env.appId }
285
- );
286
- const loginMethod = deriveLoginMethod(
287
- data?.platforms,
288
- data?.platform ?? data.platform ?? null
289
- );
290
- return { ...data, platform: loginMethod, loginMethod };
291
- }
292
225
  async function authenticateRequest(req) {
293
226
  const cookie = await getCookie();
294
227
  const cookies = req.headers.cookie ? new Map(Object.entries(cookie.parse(req.headers.cookie))) : /* @__PURE__ */ new Map();
@@ -339,26 +272,10 @@ function createSubdomainAuth(config) {
339
272
  user = await db.getUserByOpenId(sessionUserId) ?? null;
340
273
  }
341
274
  } catch (error) {
342
- try {
343
- const userInfo = await getUserInfoWithJwt(sessionCookie ?? "");
344
- await db.upsertUser({
345
- openId: userInfo.openId,
346
- name: userInfo.name || null,
347
- email: userInfo.email ?? null,
348
- loginMethod: userInfo.loginMethod ?? userInfo.platform ?? null,
349
- lastSignedIn: signedInAt
350
- });
351
- user = await db.getUserByOpenId(userInfo.openId) ?? null;
352
- } catch (oauthError) {
353
- console.error(
354
- `${tag} All user sync methods failed:`,
355
- error,
356
- oauthError
357
- );
358
- throw Object.assign(new Error("Failed to sync user info"), {
359
- statusCode: 403
360
- });
361
- }
275
+ console.error(`${tag} Failed to sync user info:`, error);
276
+ throw Object.assign(new Error("Failed to sync user info"), {
277
+ statusCode: 403
278
+ });
362
279
  }
363
280
  }
364
281
  if (!user) {
@@ -391,62 +308,17 @@ function createSubdomainAuth(config) {
391
308
  });
392
309
  return user;
393
310
  }
394
- function registerOAuthRoutes(app) {
395
- app.get(
396
- "/api/oauth/callback",
397
- async (req, res) => {
398
- const code = typeof req.query.code === "string" ? req.query.code : void 0;
399
- const state = typeof req.query.state === "string" ? req.query.state : void 0;
400
- if (!code || !state) {
401
- res.status(400).json({ error: "code and state are required" });
402
- return;
403
- }
404
- try {
405
- const tokenResponse = await exchangeCodeForToken(code, state);
406
- const userInfo = await getUserInfo(tokenResponse.accessToken);
407
- if (!userInfo.openId) {
408
- res.status(400).json({ error: "openId missing from user info" });
409
- return;
410
- }
411
- await db.upsertUser({
412
- openId: userInfo.openId,
413
- name: userInfo.name || null,
414
- email: userInfo.email ?? null,
415
- loginMethod: userInfo.loginMethod ?? userInfo.platform ?? null,
416
- lastSignedIn: /* @__PURE__ */ new Date()
417
- });
418
- const sessionToken = await createSessionToken(userInfo.openId, {
419
- name: userInfo.name || "",
420
- expiresInMs: sessionExpiryMs
421
- });
422
- const cookieOptions = getSessionCookieOptions(req);
423
- res.cookie(cookieName, sessionToken, {
424
- ...cookieOptions,
425
- maxAge: sessionExpiryMs
426
- });
427
- res.redirect(302, "/");
428
- } catch (error) {
429
- console.error(`${tag} OAuth callback failed`, error);
430
- res.status(500).json({ error: "OAuth callback failed" });
431
- }
432
- }
433
- );
434
- }
435
311
  return {
436
312
  verifySession,
437
313
  createSessionToken,
438
314
  authenticateRequest,
439
- exchangeCodeForToken,
440
- getUserInfo,
441
- getUserInfoWithJwt,
442
315
  getPortalUser,
443
316
  getSessionCookieOptions,
444
- isValidReturnUrl,
445
- registerOAuthRoutes
317
+ isValidReturnUrl
446
318
  };
447
319
  }
448
320
 
449
321
  export {
450
322
  createSubdomainAuth
451
323
  };
452
- //# sourceMappingURL=chunk-LH6QZJM4.js.map
324
+ //# sourceMappingURL=chunk-B63ROBSD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/auth/createSubdomainAuth.ts"],"sourcesContent":["/**\n * createSubdomainAuth — shared SSO auth factory for Oplytics.digital subdomains.\n *\n * Encapsulates:\n * - JWT session verification (PORTAL_JWT_SECRET first, fallback to JWT_SECRET)\n * - Cookie domain handling via COOKIE_DOMAIN env var\n * - Portal API user enrichment with X-Service-Key header\n * - Auto-create user from JWT payload if not in local DB\n * - Enterprise backfill from portal API\n *\n * Based on SQDCP's battle-tested sdk.ts + cookies.ts.\n */\n\nimport type { Request, CookieOptions } from \"express\";\nimport type {\n AuthEnv,\n BaseUser,\n DbAdapter,\n PortalUser,\n RoleMapper,\n SessionPayload,\n SubdomainAuth,\n SubdomainAuthConfig,\n} from \"./types\";\n\n// ─── Constants ──────────────────────────────────────────────────────\n\nconst DEFAULT_COOKIE_NAME = \"app_session_id\";\nconst ONE_YEAR_MS = 1000 * 60 * 60 * 24 * 365;\n\n// ─── Helpers ────────────────────────────────────────────────────────\n\nconst isNonEmptyString = (value: unknown): value is string =>\n typeof value === \"string\" && value.length > 0;\n\n// ─── Factory ────────────────────────────────────────────────────────\n\nexport function createSubdomainAuth<TUser extends BaseUser = BaseUser>(\n config: SubdomainAuthConfig<TUser>\n): SubdomainAuth<TUser> {\n const {\n env,\n db,\n // Default to identity (no role translation). Subdomains should adopt the\n // Portal's canonical roles verbatim; a custom mapper is legacy.\n mapRole = (role: string) => role,\n userSource = \"db\",\n cookieName = DEFAULT_COOKIE_NAME,\n sessionExpiryMs = ONE_YEAR_MS,\n serviceName = \"Subdomain\",\n portalUserCacheTtlMs = 30_000,\n } = config;\n\n const tag = `[${serviceName} Auth]`;\n\n if (userSource === \"db\" && !db) {\n throw new Error(\n `${tag} userSource \"db\" requires a db adapter in createSubdomainAuth config`\n );\n }\n\n // Short-TTL cache of Portal users (portal mode), keyed by openId, so we don't\n // hit the Portal on every request.\n const portalUserCache = new Map<string, { user: PortalUser; at: number }>();\n\n // ── Lazy-loaded dependencies (jose, cookie, axios) ──────────────\n // We use dynamic imports so consumers don't need to install these\n // as peer deps if they only use other core-server features.\n\n let _jose: typeof import(\"jose\") | null = null;\n async function getJose() {\n if (!_jose) _jose = await import(\"jose\");\n return _jose;\n }\n\n let _cookie: typeof import(\"cookie\") | null = null;\n async function getCookie() {\n if (!_cookie) _cookie = await import(\"cookie\");\n return _cookie;\n }\n\n let _axios: typeof import(\"axios\").default | null = null;\n async function getAxios() {\n if (!_axios) {\n const mod = await import(\"axios\");\n _axios = mod.default;\n }\n return _axios;\n }\n\n // ── Portal API client ──────────────────────────────────────────\n\n let portalClient: import(\"axios\").AxiosInstance | null = null;\n function getPortalClient() {\n if (portalClient) return portalClient;\n // Synchronous check — we only create if we have the URL\n if (!env.portalApiUrl) return null;\n // We need axios synchronously here, but it should already be loaded\n // by the time portal client is needed (after authenticateRequest calls getAxios)\n return null; // Will be created lazily in async context\n }\n\n async function getPortalClientAsync() {\n if (portalClient) return portalClient;\n if (!env.portalApiUrl) return null;\n const axios = await getAxios();\n portalClient = axios.create({\n baseURL: env.portalApiUrl,\n timeout: 10_000,\n headers: {\n \"Content-Type\": \"application/json\",\n ...(env.portalApiKey ? { \"X-Service-Key\": env.portalApiKey } : {}),\n },\n });\n return portalClient;\n }\n\n // ── JWT Secrets ────────────────────────────────────────────────\n\n function getPortalSecret() {\n const secret = env.portalJwtSecret;\n if (!secret) throw new Error(\"PORTAL_JWT_SECRET is not configured\");\n return new TextEncoder().encode(secret);\n }\n\n function getSessionSecret() {\n // Prefer portalJwtSecret for signing so cookies work cross-subdomain\n const secret = env.portalJwtSecret || env.jwtSecret;\n return new TextEncoder().encode(secret);\n }\n\n // ── Cookie Helpers ─────────────────────────────────────────────\n\n function getCookieDomain(): string | undefined {\n return env.cookieDomain || undefined;\n }\n\n function isSecureRequest(req: Request): boolean {\n if (req.protocol === \"https\") return true;\n const forwardedProto = req.headers[\"x-forwarded-proto\"];\n if (!forwardedProto) return false;\n const protoList = Array.isArray(forwardedProto)\n ? forwardedProto\n : forwardedProto.split(\",\");\n return protoList.some((p) => p.trim().toLowerCase() === \"https\");\n }\n\n function getSessionCookieOptions(\n req: Request\n ): Pick<\n CookieOptions,\n \"domain\" | \"httpOnly\" | \"path\" | \"sameSite\" | \"secure\"\n > {\n const secure = isSecureRequest(req);\n const domain = getCookieDomain();\n return {\n domain,\n httpOnly: true,\n path: \"/\",\n sameSite: \"lax\",\n secure,\n };\n }\n\n function isValidReturnUrl(url: string): boolean {\n if (url.startsWith(\"/\") && !url.startsWith(\"//\")) return true;\n try {\n const parsed = new URL(url);\n if (parsed.protocol !== \"https:\") return false;\n return (\n parsed.hostname === \"oplyticsdigital.net\" ||\n parsed.hostname.endsWith(\".oplyticsdigital.net\")\n );\n } catch {\n return false;\n }\n }\n\n // ── Session Token ──────────────────────────────────────────────\n\n async function signSession(\n payload: SessionPayload,\n options: { expiresInMs?: number } = {}\n ): Promise<string> {\n const jose = await getJose();\n const issuedAt = Date.now();\n const expiresInMs = options.expiresInMs ?? sessionExpiryMs;\n const expirationSeconds = Math.floor((issuedAt + expiresInMs) / 1000);\n const secretKey = getSessionSecret();\n\n return new jose.SignJWT({\n openId: payload.openId,\n appId: payload.appId,\n name: payload.name,\n })\n .setProtectedHeader({ alg: \"HS256\", typ: \"JWT\" })\n .setExpirationTime(expirationSeconds)\n .sign(secretKey);\n }\n\n async function createSessionToken(\n openId: string,\n options: { expiresInMs?: number; name?: string } = {}\n ): Promise<string> {\n return signSession(\n { openId, appId: serviceName, name: options.name || \"\" },\n options\n );\n }\n\n // ── Session Verification ───────────────────────────────────────\n\n async function verifySession(\n cookieValue: string | undefined | null\n ): Promise<SessionPayload | null> {\n if (!cookieValue) {\n console.warn(`${tag} Missing session cookie`);\n return null;\n }\n\n const jose = await getJose();\n\n // Try PORTAL_JWT_SECRET first (cross-subdomain SSO)\n if (env.portalJwtSecret) {\n try {\n const portalKey = getPortalSecret();\n const { payload } = await jose.jwtVerify(cookieValue, portalKey, {\n algorithms: [\"HS256\"],\n });\n const { openId, appId, name } = payload as Record<string, unknown>;\n if (\n !isNonEmptyString(openId) ||\n !isNonEmptyString(appId) ||\n !isNonEmptyString(name)\n ) {\n console.warn(`${tag} Portal JWT payload missing required fields`);\n return null;\n }\n return { openId, appId, name };\n } catch {\n // Portal secret didn't work — fall through to local secret\n }\n }\n\n // Fallback: try local JWT_SECRET\n try {\n const secretKey = getSessionSecret();\n const { payload } = await jose.jwtVerify(cookieValue, secretKey, {\n algorithms: [\"HS256\"],\n });\n const { openId, appId, name } = payload as Record<string, unknown>;\n if (\n !isNonEmptyString(openId) ||\n !isNonEmptyString(appId) ||\n !isNonEmptyString(name)\n ) {\n console.warn(`${tag} Session payload missing required fields`);\n return null;\n }\n return { openId, appId, name };\n } catch (error) {\n console.warn(\n `${tag} Session verification failed:`,\n (error as Error).message\n );\n return null;\n }\n }\n\n // ── Portal API ─────────────────────────────────────────────────\n\n async function getPortalUser(openId: string): Promise<PortalUser | null> {\n try {\n const client = await getPortalClientAsync();\n if (!client) return null;\n // The Portal Service API is under /api/service (base URL is the origin,\n // e.g. https://portal.oplyticsdigital.net). Must include the /api/service\n // prefix — omitting it 404s and silently strands enterprise resolution.\n const { data } = await client.get<PortalUser>(\n `/api/service/users/by-openid/${encodeURIComponent(openId)}`\n );\n return data ?? null;\n } catch (err: unknown) {\n const status = (err as any)?.response?.status;\n if (status === 401) {\n console.error(\n `${tag} Portal API key revoked (401) — enrichment disabled`\n );\n } else if (status === 404) {\n // User not found in portal — normal for first-time users\n } else {\n console.warn(\n `${tag} Portal user lookup failed:`,\n (err as Error).message\n );\n }\n return null;\n }\n }\n\n /** Portal user lookup with a short-TTL cache (portal mode). */\n async function getPortalUserCached(\n openId: string\n ): Promise<PortalUser | null> {\n if (portalUserCacheTtlMs > 0) {\n const hit = portalUserCache.get(openId);\n if (hit && Date.now() - hit.at < portalUserCacheTtlMs) {\n return hit.user;\n }\n }\n const user = await getPortalUser(openId);\n if (user && portalUserCacheTtlMs > 0) {\n portalUserCache.set(openId, { user, at: Date.now() });\n }\n return user;\n }\n\n /**\n * Build ctx.user entirely from the Portal record — no local persistence.\n * The single source of truth. ctx.user.id is the PORTAL user id. Roles and\n * enterprise come from the Portal verbatim (no mapping).\n *\n * On a genuine Portal outage (the by-openid lookup returns null) we keep the\n * user authenticated using their verified session claims, with role/enterprise\n * unresolved, rather than throwing. Throwing here would make createContext (in\n * every subdomain) swallow the error into `user = null`, which the AuthGate\n * renders as the Sign In screen — i.e. a transient Portal blip would log the\n * user out. Their session cookie is already valid; keep them in, degraded,\n * until the Portal recovers. (Enterprise-scoped procedures will 403 during the\n * outage; client array-guards keep pages from crashing.)\n */\n async function resolvePortalUser(\n openId: string,\n session: SessionPayload\n ): Promise<TUser> {\n const portalUser = await getPortalUserCached(openId);\n\n if (!portalUser) {\n console.warn(\n `${tag} Portal user unavailable for ${openId} — keeping session (role/enterprise unresolved until Portal recovers)`\n );\n return {\n id: 0,\n openId,\n name: session.name ?? null,\n email: null,\n role: \"\",\n enterpriseId: null,\n portalUserId: null,\n } as unknown as TUser;\n }\n\n const enterpriseId =\n portalUser.enterpriseId ?? portalUser.companyId ?? null;\n return {\n id: portalUser.id,\n openId: portalUser.openId ?? openId,\n name: portalUser.name ?? session.name ?? null,\n email: portalUser.email ?? null,\n role: portalUser.role,\n enterpriseId,\n portalUserId: portalUser.id,\n } as unknown as TUser;\n }\n\n // ── Authenticate Request ───────────────────────────────────────\n\n async function authenticateRequest(req: Request): Promise<TUser> {\n const cookie = await getCookie();\n const cookies = req.headers.cookie\n ? new Map(Object.entries(cookie.parse(req.headers.cookie)))\n : new Map<string, string>();\n\n const sessionCookie = cookies.get(cookieName);\n const session = await verifySession(sessionCookie);\n\n if (!session) {\n throw Object.assign(new Error(\"Invalid session cookie\"), {\n statusCode: 403,\n });\n }\n\n const sessionUserId = session.openId;\n const signedInAt = new Date();\n\n // ── Portal mode: single source of truth, no local mirror ────\n if (userSource === \"portal\") {\n return await resolvePortalUser(sessionUserId, session);\n }\n\n let user = (await db!.getUserByOpenId(sessionUserId)) ?? null;\n\n // ── Auto-create user from verified JWT ──────────────────────\n if (!user) {\n try {\n // Try portal API first for richer user info\n const portalUser = await getPortalUser(sessionUserId);\n\n if (portalUser) {\n console.log(\n `${tag} Cross-subdomain SSO: creating user from portal (${portalUser.email || sessionUserId})`\n );\n // Portal returns `enterpriseId`; `companyId` is a legacy alias.\n const portalEnterpriseId =\n portalUser.enterpriseId ?? portalUser.companyId ?? null;\n await db!.upsertUser({\n openId: sessionUserId,\n name: portalUser.name || session.name || null,\n email: portalUser.email ?? null,\n loginMethod: \"portal-sso\",\n role: mapRole(portalUser.role),\n lastSignedIn: signedInAt,\n ...(portalEnterpriseId != null\n ? {\n enterpriseId: portalEnterpriseId,\n portalUserId: portalUser.id,\n }\n : {}),\n });\n user = (await db!.getUserByOpenId(sessionUserId)) ?? null;\n } else {\n // Portal unavailable — create from JWT payload\n console.log(\n `${tag} Cross-subdomain SSO: creating user from JWT payload (${sessionUserId})`\n );\n await db!.upsertUser({\n openId: sessionUserId,\n name: session.name || null,\n loginMethod: \"portal-sso\",\n lastSignedIn: signedInAt,\n });\n user = (await db!.getUserByOpenId(sessionUserId)) ?? null;\n }\n } catch (error) {\n console.error(`${tag} Failed to sync user info:`, error);\n throw Object.assign(new Error(\"Failed to sync user info\"), {\n statusCode: 403,\n });\n }\n }\n\n if (!user) {\n throw Object.assign(new Error(\"User not found\"), { statusCode: 403 });\n }\n\n // ── Enterprise backfill ─────────────────────────────────────\n if (user.enterpriseId === null) {\n try {\n const portalUser = await getPortalUser(user.openId);\n // Portal returns `enterpriseId`; `companyId` is a legacy alias.\n const portalEnterpriseId =\n portalUser?.enterpriseId ?? portalUser?.companyId ?? null;\n if (portalEnterpriseId != null) {\n console.log(\n `${tag} Backfilling enterpriseId=${portalEnterpriseId} for user ${user.id}`\n );\n await db!.updateUserFields(user.id, {\n enterpriseId: portalEnterpriseId,\n ...(portalUser?.id ? { portalUserId: portalUser.id } : {}),\n });\n user = (await db!.getUserByOpenId(user.openId)) ?? user;\n }\n } catch (backfillError) {\n console.warn(\n `${tag} Failed to backfill enterpriseId for user ${user.id}:`,\n backfillError\n );\n // Non-fatal — user can still proceed\n }\n }\n\n // Touch lastSignedIn\n await db!.upsertUser({\n openId: user.openId,\n lastSignedIn: signedInAt,\n });\n\n return user;\n }\n\n // ── Return public API ──────────────────────────────────────────\n\n return {\n verifySession,\n createSessionToken,\n authenticateRequest,\n getPortalUser,\n getSessionCookieOptions,\n isValidReturnUrl,\n };\n}\n"],"mappings":"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","names":[]}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { HierarchyService, HierarchyServiceConfig, createHierarchyService } from './hierarchy.js';
2
- export { AuthEnv, BaseUser, DbAdapter, ExchangeTokenResponse, GetUserInfoResponse, GetUserInfoWithJwtResponse, PortalUser, RoleMapper, SessionPayload, SubdomainAuth, SubdomainAuthConfig, UpsertUserPayload, createSubdomainAuth } from './auth.js';
2
+ export { AuthEnv, BaseUser, DbAdapter, PortalUser, RoleMapper, SessionPayload, SubdomainAuth, SubdomainAuthConfig, UpsertUserPayload, createSubdomainAuth } from './auth.js';
3
3
  import '@pablo2410/shared-ui/hierarchy/core';
4
4
  import 'express';
5
5
 
@@ -12,44 +12,13 @@ declare const SQDCP_PILLAR_LABELS: Record<SqdcpPillar, string>;
12
12
  * Minimal environment interface required by core-server utilities.
13
13
  * Each subdomain provides its own ENV object that satisfies this shape.
14
14
  *
15
- * `forgeApiUrl`/`forgeApiKey` back the Manus "Forge" gateway, used by the
16
- * data API, map, image generation, notification, and transcription helpers.
17
15
  * `geminiApiUrl`/`geminiApiKey` back the LLM chat client (llm.ts) directly
18
- * against Google's Gemini OpenAI-compatible endpoint — separate credential,
19
- * separate provider, not a Forge passthrough.
16
+ * against Google's Gemini OpenAI-compatible endpoint.
20
17
  */
21
18
  interface CoreEnv {
22
- forgeApiUrl: string;
23
- forgeApiKey: string;
24
19
  geminiApiUrl: string;
25
20
  geminiApiKey: string;
26
21
  }
27
- /**
28
- * Storage upload function signature.
29
- * Subdomains provide their own storagePut implementation.
30
- */
31
- type StoragePutFn = (key: string, data: Buffer | Uint8Array | string, contentType?: string) => Promise<{
32
- key: string;
33
- url: string;
34
- }>;
35
-
36
- /**
37
- * Data API helper — calls the Manus built-in Data API Hub.
38
- *
39
- * Quick example:
40
- * const callDataApi = createDataApiClient(ENV);
41
- * await callDataApi("Youtube/search", {
42
- * query: { gl: "US", hl: "en", q: "manus" },
43
- * });
44
- */
45
-
46
- type DataApiCallOptions = {
47
- query?: Record<string, unknown>;
48
- body?: Record<string, unknown>;
49
- pathParams?: Record<string, unknown>;
50
- formData?: Record<string, unknown>;
51
- };
52
- declare function createDataApiClient(env: CoreEnv): (apiId: string, options?: DataApiCallOptions) => Promise<unknown>;
53
22
 
54
23
  /**
55
24
  * Metering + cost-control primitives for the AI layer.
@@ -66,8 +35,11 @@ interface TokenCostRate {
66
35
  outputPer1k: number;
67
36
  }
68
37
  /**
69
- * Default rate for gemini-2.5-flash (USD per 1k tokens).
70
- * Override per app via createLLMClient options if Forge pricing changes.
38
+ * Default rate (USD per 1k tokens). Was priced for gemini-2.5-flash;
39
+ * llm.ts's MODEL moved to gemini-3.5-flash (2.5-flash was deprecated for new
40
+ * API keys) but this rate was NOT re-verified against 3.5-flash's actual
41
+ * published pricing — treat cost figures as stale until confirmed.
42
+ * Override per app via createLLMClient options once real pricing is known.
71
43
  */
72
44
  declare const DEFAULT_COST_RATE: TokenCostRate;
73
45
  /** Per-call context that varies request-to-request (set via InvokeParams.meta). */
@@ -212,7 +184,7 @@ type InvokeParams = {
212
184
  type LLMClientOptions = {
213
185
  /** App identifier recorded on every usage event, e.g. "marketing-site". */
214
186
  app?: string;
215
- /** Pricing used to estimate cost from tokens. Defaults to gemini-2.5-flash. */
187
+ /** Pricing used to estimate cost from tokens. Defaults to gemini-3.5-flash (rate unverified, see metering.ts). */
216
188
  costRate?: TokenCostRate;
217
189
  /** Metering sink — called after each successful call. Errors are swallowed. */
218
190
  onUsage?: (event: UsageEvent) => void | Promise<void>;
@@ -248,102 +220,6 @@ type InvokeResult = {
248
220
  };
249
221
  declare function createLLMClient(env: CoreEnv, options?: LLMClientOptions): (params: InvokeParams) => Promise<InvokeResult>;
250
222
 
251
- /**
252
- * Image generation helper using internal ImageService.
253
- *
254
- * Example:
255
- * import { storagePut } from "../storage";
256
- * const generateImage = createImageGenerator(ENV, storagePut);
257
- * const { url } = await generateImage({ prompt: "A serene landscape" });
258
- */
259
-
260
- type GenerateImageOptions = {
261
- prompt: string;
262
- originalImages?: Array<{
263
- url?: string;
264
- b64Json?: string;
265
- mimeType?: string;
266
- }>;
267
- };
268
- type GenerateImageResponse = {
269
- url?: string;
270
- };
271
- declare function createImageGenerator(env: CoreEnv, storagePut: StoragePutFn): (options: GenerateImageOptions) => Promise<GenerateImageResponse>;
272
-
273
- /**
274
- * Google Maps API Integration for Manus WebDev Templates.
275
- *
276
- * Main function: createMapClient(env) returns makeRequest<T>(endpoint, params)
277
- * All credentials are automatically injected. Array parameters use | as separator.
278
- *
279
- * Example:
280
- * const makeRequest = createMapClient(ENV);
281
- * const result = await makeRequest<GeocodeResult>(
282
- * "/maps/api/geocode/json",
283
- * { address: "1600 Amphitheatre Parkway" }
284
- * );
285
- */
286
-
287
- type MapRequestParams = Record<string, string | number | boolean | string[] | undefined>;
288
- declare function createMapClient(env: CoreEnv): <T = unknown>(endpoint: string, params?: MapRequestParams) => Promise<T>;
289
-
290
- /**
291
- * Owner notification helper — pushes operational updates to the Manus project owner.
292
- *
293
- * Example:
294
- * const notifyOwner = createNotificationClient(ENV);
295
- * await notifyOwner({ title: "New submission", content: "User X submitted form Y" });
296
- */
297
-
298
- type NotificationPayload = {
299
- title: string;
300
- content: string;
301
- };
302
- declare function createNotificationClient(env: CoreEnv): (payload: NotificationPayload) => Promise<boolean>;
303
-
304
- /**
305
- * Voice transcription helper using internal Speech-to-Text service (Whisper API).
306
- *
307
- * Example:
308
- * const transcribeAudio = createTranscriptionClient(ENV);
309
- * const result = await transcribeAudio({
310
- * audioUrl: "https://storage.example.com/audio/recording.mp3",
311
- * language: "en",
312
- * });
313
- */
314
-
315
- type TranscribeOptions = {
316
- audioUrl: string;
317
- language?: string;
318
- prompt?: string;
319
- };
320
- type WhisperSegment = {
321
- id: number;
322
- seek: number;
323
- start: number;
324
- end: number;
325
- text: string;
326
- tokens: number[];
327
- temperature: number;
328
- avg_logprob: number;
329
- compression_ratio: number;
330
- no_speech_prob: number;
331
- };
332
- type WhisperResponse = {
333
- task: string;
334
- language: string;
335
- duration: number;
336
- text: string;
337
- segments: WhisperSegment[];
338
- };
339
- type TranscriptionError = {
340
- error: string;
341
- code: string;
342
- details: string;
343
- };
344
- type TranscriptionResult = WhisperResponse | TranscriptionError;
345
- declare function createTranscriptionClient(env: CoreEnv): (options: TranscribeOptions) => Promise<TranscriptionResult>;
346
-
347
223
  /**
348
224
  * Personas for the shared Oplytics AI assistant.
349
225
  *
@@ -454,4 +330,4 @@ declare function createSupportEngine(env: CoreEnv, options?: SupportEngineOption
454
330
  };
455
331
  type SupportEngine = ReturnType<typeof createSupportEngine>;
456
332
 
457
- export { type AssistantMode, type BuildSystemPromptParams, type CallMeta, type ChatMessage, type ChatParams, type ChatResult, type CoreEnv, DEFAULT_COST_RATE, type DataApiCallOptions, type FileContent, type GenerateImageOptions, type GenerateImageResponse, type GuardRequest, type GuardResult, type ImageContent, type InvokeParams, type InvokeResult, KNOWLEDGE, type KnowledgeItem, LLMBudgetError, type LLMClientOptions, type MapRequestParams, type Message, type MessageContent, type NotificationPayload, type OutputSchema, type ResponseFormat, type RetrieveOptions, type Retriever, type Role, SQDCP_PILLARS, SQDCP_PILLAR_COLORS, SQDCP_PILLAR_LABELS, type SqdcpPillar, type StoragePutFn, type SupportContext, type SupportEngine, type SupportEngineOptions, type TextContent, type TokenCostRate, type Tool, type ToolChoice, type TranscribeOptions, type TranscriptionError, type TranscriptionResult, type UsageEvent, type WhisperResponse, type WhisperSegment, buildSystemPrompt, computeCostUsd, createDataApiClient, createImageGenerator, createLLMClient, createMapClient, createNotificationClient, createStaticRetriever, createSupportEngine, createTranscriptionClient, estimateMessageTokens, estimateTokens, formatKnowledge };
333
+ export { type AssistantMode, type BuildSystemPromptParams, type CallMeta, type ChatMessage, type ChatParams, type ChatResult, type CoreEnv, DEFAULT_COST_RATE, type FileContent, type GuardRequest, type GuardResult, type ImageContent, type InvokeParams, type InvokeResult, KNOWLEDGE, type KnowledgeItem, LLMBudgetError, type LLMClientOptions, type Message, type MessageContent, type OutputSchema, type ResponseFormat, type RetrieveOptions, type Retriever, type Role, SQDCP_PILLARS, SQDCP_PILLAR_COLORS, SQDCP_PILLAR_LABELS, type SqdcpPillar, type SupportContext, type SupportEngine, type SupportEngineOptions, type TextContent, type TokenCostRate, type Tool, type ToolChoice, type UsageEvent, buildSystemPrompt, computeCostUsd, createLLMClient, createStaticRetriever, createSupportEngine, estimateMessageTokens, estimateTokens, formatKnowledge };