128 lines
3.3 KiB
TypeScript

"use server"
import { cookies } from "next/headers"
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,
expiresIn: number = DEFAULT_EXPIRES_IN,
) {
const cookieStore = await cookies()
const expires = new Date(Date.now() + expiresIn * 1000)
cookieStore.set(TOKEN_COOKIE, token, {
expires,
path: "/",
sameSite: "strict",
httpOnly: true,
secure: isProd,
})
cookieStore.set(USER_COOKIE, JSON.stringify(user), {
expires,
path: "/",
sameSite: "strict",
httpOnly: true,
secure: isProd,
})
}
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<{
token: string | undefined
user: AuthUser | undefined
}> {
const cookieStore = await cookies()
const token = cookieStore.get(TOKEN_COOKIE)?.value
const rawUser = cookieStore.get(USER_COOKIE)?.value
let user: AuthUser | undefined
if (rawUser) {
try {
user = JSON.parse(rawUser) as AuthUser
} catch {
user = undefined
}
}
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)
}