128 lines
3.3 KiB
TypeScript
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)
|
|
}
|