feat: implement workspace session management and enhance API proxy handling

This commit is contained in:
Najjar\NajjarV02
2026-05-20 16:23:19 +04:00
parent 4f0a2f790f
commit ccb9eacd86
8 changed files with 410 additions and 71 deletions
@@ -5,8 +5,12 @@ import type { AuthUser } from "@garage/api"
const TOKEN_COOKIE = "auth_token"
const USER_COOKIE = "auth_user"
const WORKSPACE_COOKIE = "workspace_uuid"
const API_BASE_COOKIE = "api_base_url"
const DEFAULT_EXPIRES_IN = 60 * 60 * 24 * 7 // 7 days in seconds
const isProd = process.env.NODE_ENV === "production"
export async function setAuthCookies(
token: string,
user: AuthUser,
@@ -19,12 +23,16 @@ export async function setAuthCookies(
expires,
path: "/",
sameSite: "strict",
httpOnly: true,
secure: isProd,
})
cookieStore.set(USER_COOKIE, JSON.stringify(user), {
expires,
path: "/",
sameSite: "strict",
httpOnly: true,
secure: isProd,
})
}
@@ -32,6 +40,8 @@ export async function clearAuthCookies() {
const cookieStore = await cookies()
cookieStore.delete(TOKEN_COOKIE)
cookieStore.delete(USER_COOKIE)
cookieStore.delete(WORKSPACE_COOKIE)
cookieStore.delete(API_BASE_COOKIE)
}
export async function getAuthCookies(): Promise<{
@@ -53,3 +63,65 @@ export async function getAuthCookies(): Promise<{
return { token, user }
}
/**
* Set the per-session workspace context: which garage backend to proxy to,
* and which workspace UUID to send on every upstream call. Written by the
* SaaS → garage-erp activation handoff.
*/
export async function setWorkspaceContext(
apiBase: string,
workspaceUuid: string,
expiresIn: number = DEFAULT_EXPIRES_IN,
) {
const cookieStore = await cookies()
const expires = new Date(Date.now() + expiresIn * 1000)
cookieStore.set(API_BASE_COOKIE, apiBase, {
expires,
path: "/",
sameSite: "strict",
httpOnly: true,
secure: isProd,
})
cookieStore.set(WORKSPACE_COOKIE, workspaceUuid, {
expires,
path: "/",
sameSite: "strict",
httpOnly: true,
secure: isProd,
})
}
export async function getWorkspaceContext(): Promise<{
apiBase: string | undefined
workspaceUuid: string | undefined
}> {
const cookieStore = await cookies()
return {
apiBase: cookieStore.get(API_BASE_COOKIE)?.value,
workspaceUuid: cookieStore.get(WORKSPACE_COOKIE)?.value,
}
}
export async function clearWorkspaceContext() {
const cookieStore = await cookies()
cookieStore.delete(WORKSPACE_COOKIE)
cookieStore.delete(API_BASE_COOKIE)
}
/**
* Server-action used by the SaaS handoff page. Wraps cookie writes for both
* the auth token/user and the workspace context atomically (from the client's
* point of view).
*/
export async function applyHandoff(
token: string,
user: AuthUser,
apiBase: string,
workspaceUuid: string,
) {
await setAuthCookies(token, user)
await setWorkspaceContext(apiBase, workspaceUuid)
}