feat: habilitar provisionamento desktop e rotas CORS
This commit is contained in:
parent
7569986ffc
commit
152550a9a0
19 changed files with 1806 additions and 211 deletions
|
|
@ -1,9 +1,9 @@
|
|||
import { NextResponse } from "next/server"
|
||||
import { z } from "zod"
|
||||
import { ConvexHttpClient } from "convex/browser"
|
||||
|
||||
import { api } from "@/convex/_generated/api"
|
||||
import { env } from "@/lib/env"
|
||||
import { createCorsPreflight, jsonWithCors } from "@/server/cors"
|
||||
|
||||
const heartbeatSchema = z.object({
|
||||
machineToken: z.string().min(1),
|
||||
|
|
@ -21,14 +21,20 @@ const heartbeatSchema = z.object({
|
|||
metadata: z.record(z.string(), z.unknown()).optional(),
|
||||
})
|
||||
|
||||
const CORS_METHODS = "POST, OPTIONS"
|
||||
|
||||
export async function OPTIONS(request: Request) {
|
||||
return createCorsPreflight(request.headers.get("origin"), CORS_METHODS)
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
if (request.method !== "POST") {
|
||||
return NextResponse.json({ error: "Método não permitido" }, { status: 405 })
|
||||
return jsonWithCors({ error: "Método não permitido" }, 405, request.headers.get("origin"), CORS_METHODS)
|
||||
}
|
||||
|
||||
const convexUrl = env.NEXT_PUBLIC_CONVEX_URL
|
||||
if (!convexUrl) {
|
||||
return NextResponse.json({ error: "Convex não configurado" }, { status: 500 })
|
||||
return jsonWithCors({ error: "Convex não configurado" }, 500, request.headers.get("origin"), CORS_METHODS)
|
||||
}
|
||||
|
||||
let payload
|
||||
|
|
@ -36,16 +42,21 @@ export async function POST(request: Request) {
|
|||
const raw = await request.json()
|
||||
payload = heartbeatSchema.parse(raw)
|
||||
} catch (error) {
|
||||
return NextResponse.json({ error: "Payload inválido", details: error instanceof Error ? error.message : String(error) }, { status: 400 })
|
||||
return jsonWithCors(
|
||||
{ error: "Payload inválido", details: error instanceof Error ? error.message : String(error) },
|
||||
400,
|
||||
request.headers.get("origin"),
|
||||
CORS_METHODS
|
||||
)
|
||||
}
|
||||
|
||||
const client = new ConvexHttpClient(convexUrl)
|
||||
|
||||
try {
|
||||
const response = await client.mutation(api.machines.heartbeat, payload)
|
||||
return NextResponse.json(response)
|
||||
return jsonWithCors(response, 200, request.headers.get("origin"), CORS_METHODS)
|
||||
} catch (error) {
|
||||
console.error("[machines.heartbeat] Falha ao registrar heartbeat", error)
|
||||
return NextResponse.json({ error: "Falha ao registrar heartbeat" }, { status: 500 })
|
||||
return jsonWithCors({ error: "Falha ao registrar heartbeat" }, 500, request.headers.get("origin"), CORS_METHODS)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { NextResponse } from "next/server"
|
||||
import { z } from "zod"
|
||||
import { ConvexHttpClient } from "convex/browser"
|
||||
|
||||
|
|
@ -7,6 +6,7 @@ import type { Id } from "@/convex/_generated/dataModel"
|
|||
import { env } from "@/lib/env"
|
||||
import { DEFAULT_TENANT_ID } from "@/lib/constants"
|
||||
import { ensureMachineAccount } from "@/server/machines-auth"
|
||||
import { createCorsPreflight, jsonWithCors } from "@/server/cors"
|
||||
|
||||
const registerSchema = z
|
||||
.object({
|
||||
|
|
@ -29,14 +29,20 @@ const registerSchema = z
|
|||
{ message: "Informe ao menos um MAC address ou número de série" }
|
||||
)
|
||||
|
||||
const CORS_METHODS = "POST, OPTIONS"
|
||||
|
||||
export async function OPTIONS(request: Request) {
|
||||
return createCorsPreflight(request.headers.get("origin"), CORS_METHODS)
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
if (request.method !== "POST") {
|
||||
return NextResponse.json({ error: "Método não permitido" }, { status: 405 })
|
||||
return jsonWithCors({ error: "Método não permitido" }, 405, request.headers.get("origin"), CORS_METHODS)
|
||||
}
|
||||
|
||||
const convexUrl = env.NEXT_PUBLIC_CONVEX_URL
|
||||
if (!convexUrl) {
|
||||
return NextResponse.json({ error: "Convex não configurado" }, { status: 500 })
|
||||
return jsonWithCors({ error: "Convex não configurado" }, 500, request.headers.get("origin"), CORS_METHODS)
|
||||
}
|
||||
|
||||
let payload
|
||||
|
|
@ -44,7 +50,12 @@ export async function POST(request: Request) {
|
|||
const raw = await request.json()
|
||||
payload = registerSchema.parse(raw)
|
||||
} catch (error) {
|
||||
return NextResponse.json({ error: "Payload inválido", details: error instanceof Error ? error.message : String(error) }, { status: 400 })
|
||||
return jsonWithCors(
|
||||
{ error: "Payload inválido", details: error instanceof Error ? error.message : String(error) },
|
||||
400,
|
||||
request.headers.get("origin"),
|
||||
CORS_METHODS
|
||||
)
|
||||
}
|
||||
|
||||
const client = new ConvexHttpClient(convexUrl)
|
||||
|
|
@ -75,7 +86,7 @@ export async function POST(request: Request) {
|
|||
authEmail: account.authEmail,
|
||||
})
|
||||
|
||||
return NextResponse.json(
|
||||
return jsonWithCors(
|
||||
{
|
||||
machineId: registration.machineId,
|
||||
tenantId: registration.tenantId,
|
||||
|
|
@ -85,10 +96,12 @@ export async function POST(request: Request) {
|
|||
machineEmail: account.authEmail,
|
||||
expiresAt: registration.expiresAt,
|
||||
},
|
||||
{ status: 201 }
|
||||
{ status: 201 },
|
||||
request.headers.get("origin"),
|
||||
CORS_METHODS
|
||||
)
|
||||
} catch (error) {
|
||||
console.error("[machines.register] Falha no provisionamento", error)
|
||||
return NextResponse.json({ error: "Falha ao provisionar máquina" }, { status: 500 })
|
||||
return jsonWithCors({ error: "Falha ao provisionar máquina" }, 500, request.headers.get("origin"), CORS_METHODS)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,27 +1,22 @@
|
|||
import { NextResponse } from "next/server"
|
||||
import { z } from "zod"
|
||||
import { ConvexHttpClient } from "convex/browser"
|
||||
|
||||
import { api } from "@/convex/_generated/api"
|
||||
import type { Id } from "@/convex/_generated/dataModel"
|
||||
import { env } from "@/lib/env"
|
||||
import { DEFAULT_TENANT_ID } from "@/lib/constants"
|
||||
import { ensureMachineAccount } from "@/server/machines-auth"
|
||||
import { auth } from "@/lib/auth"
|
||||
import { createMachineSession } from "@/server/machines-session"
|
||||
import { applyCorsHeaders, createCorsPreflight, jsonWithCors } from "@/server/cors"
|
||||
|
||||
const sessionSchema = z.object({
|
||||
machineToken: z.string().min(1),
|
||||
rememberMe: z.boolean().optional(),
|
||||
})
|
||||
|
||||
const CORS_METHODS = "POST, OPTIONS"
|
||||
|
||||
export async function OPTIONS(request: Request) {
|
||||
return createCorsPreflight(request.headers.get("origin"), CORS_METHODS)
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
if (request.method !== "POST") {
|
||||
return NextResponse.json({ error: "Método não permitido" }, { status: 405 })
|
||||
}
|
||||
|
||||
const convexUrl = env.NEXT_PUBLIC_CONVEX_URL
|
||||
if (!convexUrl) {
|
||||
return NextResponse.json({ error: "Convex não configurado" }, { status: 500 })
|
||||
return jsonWithCors({ error: "Método não permitido" }, 405, request.headers.get("origin"), CORS_METHODS)
|
||||
}
|
||||
|
||||
let payload
|
||||
|
|
@ -29,68 +24,34 @@ export async function POST(request: Request) {
|
|||
const raw = await request.json()
|
||||
payload = sessionSchema.parse(raw)
|
||||
} catch (error) {
|
||||
return NextResponse.json({ error: "Payload inválido", details: error instanceof Error ? error.message : String(error) }, { status: 400 })
|
||||
return jsonWithCors(
|
||||
{ error: "Payload inválido", details: error instanceof Error ? error.message : String(error) },
|
||||
400,
|
||||
request.headers.get("origin"),
|
||||
CORS_METHODS
|
||||
)
|
||||
}
|
||||
|
||||
const client = new ConvexHttpClient(convexUrl)
|
||||
|
||||
try {
|
||||
const resolved = await client.mutation(api.machines.resolveToken, { machineToken: payload.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: payload.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: payload.machineToken,
|
||||
rememberMe: payload.rememberMe ?? true,
|
||||
},
|
||||
returnHeaders: true,
|
||||
})
|
||||
|
||||
const session = await createMachineSession(payload.machineToken, payload.rememberMe ?? true)
|
||||
const response = NextResponse.json(
|
||||
{
|
||||
ok: true,
|
||||
machine: {
|
||||
id: resolved.machine._id,
|
||||
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,
|
||||
companySlug: resolved.machine.companySlug,
|
||||
metadata: resolved.machine.metadata,
|
||||
},
|
||||
session: signIn.response,
|
||||
machine: session.machine,
|
||||
session: session.response,
|
||||
},
|
||||
{ status: 200 }
|
||||
)
|
||||
|
||||
signIn.headers.forEach((value, key) => {
|
||||
session.headers.forEach((value, key) => {
|
||||
response.headers.set(key, value)
|
||||
})
|
||||
|
||||
applyCorsHeaders(response, request.headers.get("origin"), CORS_METHODS)
|
||||
|
||||
return response
|
||||
} catch (error) {
|
||||
console.error("[machines.sessions] Falha ao criar sessão", error)
|
||||
return NextResponse.json({ error: "Falha ao autenticar máquina" }, { status: 500 })
|
||||
return jsonWithCors({ error: "Falha ao autenticar máquina" }, 500, request.headers.get("origin"), CORS_METHODS)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue