feat: habilitar provisionamento desktop e rotas CORS

This commit is contained in:
Esdras Renan 2025-10-08 23:07:49 -03:00
parent 7569986ffc
commit 152550a9a0
19 changed files with 1806 additions and 211 deletions

38
src/server/cors.ts Normal file
View file

@ -0,0 +1,38 @@
import { NextResponse } from "next/server"
const DEFAULT_ALLOWED_ORIGINS = [
process.env.NEXT_PUBLIC_APP_URL ?? null,
"https://tickets.esdrasrenan.com.br",
"http://localhost:1420",
"http://localhost:3000",
"tauri://localhost",
].filter((value): value is string => Boolean(value))
export function resolveCorsOrigin(requestOrigin: string | null): string {
if (!requestOrigin) return "*"
const allowed = new Set(DEFAULT_ALLOWED_ORIGINS)
if (allowed.has(requestOrigin)) {
return requestOrigin
}
return "*"
}
export function applyCorsHeaders(response: NextResponse, origin: string | null, methods = "POST, OPTIONS") {
const resolvedOrigin = resolveCorsOrigin(origin)
response.headers.set("Access-Control-Allow-Origin", resolvedOrigin)
response.headers.set("Access-Control-Allow-Methods", methods)
response.headers.set("Access-Control-Allow-Headers", "Content-Type, Authorization")
response.headers.set("Access-Control-Max-Age", "86400")
response.headers.set("Vary", "Origin")
return response
}
export function createCorsPreflight(origin: string | null, methods = "POST, OPTIONS") {
const response = new NextResponse(null, { status: 204 })
return applyCorsHeaders(response, origin, methods)
}
export function jsonWithCors<T>(data: T, init: number | ResponseInit, origin: string | null, methods = "POST, OPTIONS") {
const response = NextResponse.json(data, typeof init === "number" ? { status: init } : init)
return applyCorsHeaders(response, origin, methods)
}

View file

@ -0,0 +1,81 @@
import { ConvexHttpClient } from "convex/browser"
import { api } from "@/convex/_generated/api"
import type { Id } from "@/convex/_generated/dataModel"
import { DEFAULT_TENANT_ID } from "@/lib/constants"
import { env } from "@/lib/env"
import { ensureMachineAccount } from "@/server/machines-auth"
import { auth } from "@/lib/auth"
export type MachineSessionContext = {
machine: {
id: Id<"machines">
hostname: string
osName: string | null
osVersion: string | null
architecture: string | null
status: string | null
lastHeartbeatAt: number | null
companyId: Id<"companies"> | null
companySlug: string | null
metadata: Record<string, unknown> | null
}
headers: Headers
response: unknown
}
export async function createMachineSession(machineToken: string, rememberMe = true): Promise<MachineSessionContext> {
const convexUrl = env.NEXT_PUBLIC_CONVEX_URL
if (!convexUrl) {
throw new Error("Convex não configurado")
}
const client = new ConvexHttpClient(convexUrl)
const resolved = await client.mutation(api.machines.resolveToken, { machineToken })
let machineEmail = resolved.machine.authEmail ?? null
if (!machineEmail) {
const account = await ensureMachineAccount({
machineId: resolved.machine._id,
tenantId: resolved.machine.tenantId ?? DEFAULT_TENANT_ID,
hostname: resolved.machine.hostname,
machineToken,
})
await client.mutation(api.machines.linkAuthAccount, {
machineId: resolved.machine._id as Id<"machines">,
authUserId: account.authUserId,
authEmail: account.authEmail,
})
machineEmail = account.authEmail
}
const signIn = await auth.api.signInEmail({
body: {
email: machineEmail,
password: machineToken,
rememberMe,
},
returnHeaders: true,
})
return {
machine: {
id: resolved.machine._id as Id<"machines">,
hostname: resolved.machine.hostname,
osName: resolved.machine.osName,
osVersion: resolved.machine.osVersion,
architecture: resolved.machine.architecture,
status: resolved.machine.status,
lastHeartbeatAt: resolved.machine.lastHeartbeatAt,
companyId: (resolved.machine.companyId ?? null) as Id<"companies"> | null,
companySlug: resolved.machine.companySlug ?? null,
metadata: (resolved.machine.metadata ?? null) as Record<string, unknown> | null,
},
headers: signIn.headers,
response: signIn.response,
}
}