feat(convex): add internal url and remote access fixes
This commit is contained in:
parent
feb31d48c1
commit
da46fa448b
17 changed files with 73 additions and 92 deletions
|
|
@ -9,6 +9,7 @@ BETTER_AUTH_SECRET=change-me-in-prod
|
||||||
|
|
||||||
# Convex (dev server URL)
|
# Convex (dev server URL)
|
||||||
NEXT_PUBLIC_CONVEX_URL=http://127.0.0.1:3210
|
NEXT_PUBLIC_CONVEX_URL=http://127.0.0.1:3210
|
||||||
|
CONVEX_INTERNAL_URL=http://127.0.0.1:3210
|
||||||
|
|
||||||
# SQLite database (local dev)
|
# SQLite database (local dev)
|
||||||
DATABASE_URL=file:./prisma/db.dev.sqlite
|
DATABASE_URL=file:./prisma/db.dev.sqlite
|
||||||
|
|
|
||||||
|
|
@ -966,6 +966,7 @@ export const listByTenant = query({
|
||||||
inventory,
|
inventory,
|
||||||
postureAlerts,
|
postureAlerts,
|
||||||
lastPostureAt,
|
lastPostureAt,
|
||||||
|
remoteAccess: machine.remoteAccess ?? null,
|
||||||
customFields: machine.customFields ?? [],
|
customFields: machine.customFields ?? [],
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ Arquivo base: `.env` na raiz do projeto. Exemplo mínimo (substitua domínios/se
|
||||||
NEXT_PUBLIC_APP_URL=https://tickets.esdrasrenan.com.br
|
NEXT_PUBLIC_APP_URL=https://tickets.esdrasrenan.com.br
|
||||||
BETTER_AUTH_URL=https://tickets.esdrasrenan.com.br
|
BETTER_AUTH_URL=https://tickets.esdrasrenan.com.br
|
||||||
NEXT_PUBLIC_CONVEX_URL=https://convex.esdrasrenan.com.br
|
NEXT_PUBLIC_CONVEX_URL=https://convex.esdrasrenan.com.br
|
||||||
|
CONVEX_INTERNAL_URL=http://convex_backend:3210
|
||||||
BETTER_AUTH_SECRET=<hex forte gerado por `openssl rand -hex 32`>
|
BETTER_AUTH_SECRET=<hex forte gerado por `openssl rand -hex 32`>
|
||||||
DATABASE_URL=file:./prisma/db.sqlite
|
DATABASE_URL=file:./prisma/db.sqlite
|
||||||
|
|
||||||
|
|
@ -77,6 +78,9 @@ MACHINE_PROVISIONING_SECRET=<hex forte>
|
||||||
MACHINE_TOKEN_TTL_MS=2592000000
|
MACHINE_TOKEN_TTL_MS=2592000000
|
||||||
FLEET_SYNC_SECRET=<hex forte ou igual ao de provisionamento>
|
FLEET_SYNC_SECRET=<hex forte ou igual ao de provisionamento>
|
||||||
|
|
||||||
|
# Conexões internas (Next.js -> Convex)
|
||||||
|
# CONVEX_INTERNAL_URL deve apontar para o hostname/porta do serviço no Swarm.
|
||||||
|
|
||||||
# Outros
|
# Outros
|
||||||
CONVEX_SYNC_SECRET=dev-sync-secret
|
CONVEX_SYNC_SECRET=dev-sync-secret
|
||||||
ALERTS_LOCAL_HOUR=8
|
ALERTS_LOCAL_HOUR=8
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import { prisma } from "@/lib/prisma"
|
||||||
import { DEFAULT_TENANT_ID } from "@/lib/constants"
|
import { DEFAULT_TENANT_ID } from "@/lib/constants"
|
||||||
import { assertStaffSession } from "@/lib/auth-server"
|
import { assertStaffSession } from "@/lib/auth-server"
|
||||||
import { ROLE_OPTIONS, type RoleOption, isAdmin } from "@/lib/authz"
|
import { ROLE_OPTIONS, type RoleOption, isAdmin } from "@/lib/authz"
|
||||||
|
import { requireConvexUrl } from "@/server/convex-client"
|
||||||
|
|
||||||
function normalizeRole(input: string | null | undefined): RoleOption {
|
function normalizeRole(input: string | null | undefined): RoleOption {
|
||||||
const candidate = (input ?? "agent").toLowerCase() as RoleOption
|
const candidate = (input ?? "agent").toLowerCase() as RoleOption
|
||||||
|
|
@ -256,10 +257,8 @@ export async function PATCH(request: Request, { params }: { params: Promise<{ id
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const convexUrl = process.env.NEXT_PUBLIC_CONVEX_URL
|
try {
|
||||||
if (convexUrl) {
|
const convex = new ConvexHttpClient(requireConvexUrl())
|
||||||
try {
|
|
||||||
const convex = new ConvexHttpClient(convexUrl)
|
|
||||||
let managerConvexId: Id<"users"> | undefined
|
let managerConvexId: Id<"users"> | undefined
|
||||||
if (hasManagerField && managerRecord?.email) {
|
if (hasManagerField && managerRecord?.email) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -299,10 +298,9 @@ export async function PATCH(request: Request, { params }: { params: Promise<{ id
|
||||||
if (hasManagerField) {
|
if (hasManagerField) {
|
||||||
ensurePayload.managerId = managerConvexId
|
ensurePayload.managerId = managerConvexId
|
||||||
}
|
}
|
||||||
await convex.mutation(api.users.ensureUser, ensurePayload)
|
await convex.mutation(api.users.ensureUser, ensurePayload)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn("Falha ao sincronizar usuário no Convex", error)
|
console.warn("Falha ao sincronizar usuário no Convex", error)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const updatedDomain = await prisma.user.findUnique({
|
const updatedDomain = await prisma.user.findUnique({
|
||||||
|
|
@ -363,12 +361,10 @@ export async function DELETE(_: Request, { params }: { params: Promise<{ id: str
|
||||||
return NextResponse.json({ error: "Você não pode remover o usuário atualmente autenticado." }, { status: 400 })
|
return NextResponse.json({ error: "Você não pode remover o usuário atualmente autenticado." }, { status: 400 })
|
||||||
}
|
}
|
||||||
|
|
||||||
const convexUrl = process.env.NEXT_PUBLIC_CONVEX_URL
|
|
||||||
const tenantId = target.tenantId ?? session.user.tenantId ?? DEFAULT_TENANT_ID
|
const tenantId = target.tenantId ?? session.user.tenantId ?? DEFAULT_TENANT_ID
|
||||||
|
|
||||||
if (convexUrl) {
|
try {
|
||||||
try {
|
const convex = new ConvexHttpClient(requireConvexUrl())
|
||||||
const convex = new ConvexHttpClient(convexUrl)
|
|
||||||
const ensured = await convex.mutation(api.users.ensureUser, {
|
const ensured = await convex.mutation(api.users.ensureUser, {
|
||||||
tenantId,
|
tenantId,
|
||||||
email: session.user.email,
|
email: session.user.email,
|
||||||
|
|
@ -393,10 +389,9 @@ export async function DELETE(_: Request, { params }: { params: Promise<{ id: str
|
||||||
actorId,
|
actorId,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = error instanceof Error ? error.message : "Falha ao remover usuário na base de dados"
|
const message = error instanceof Error ? error.message : "Falha ao remover usuário na base de dados"
|
||||||
return NextResponse.json({ error: message }, { status: 400 })
|
return NextResponse.json({ error: message }, { status: 400 })
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await prisma.authUser.delete({ where: { id: target.id } })
|
await prisma.authUser.delete({ where: { id: target.id } })
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import { prisma } from "@/lib/prisma"
|
||||||
import { DEFAULT_TENANT_ID } from "@/lib/constants"
|
import { DEFAULT_TENANT_ID } from "@/lib/constants"
|
||||||
import type { Id } from "@/convex/_generated/dataModel"
|
import type { Id } from "@/convex/_generated/dataModel"
|
||||||
import { api } from "@/convex/_generated/api"
|
import { api } from "@/convex/_generated/api"
|
||||||
|
import { requireConvexUrl } from "@/server/convex-client"
|
||||||
|
|
||||||
export const runtime = "nodejs"
|
export const runtime = "nodejs"
|
||||||
|
|
||||||
|
|
@ -21,9 +22,12 @@ export async function POST(request: Request) {
|
||||||
return NextResponse.json({ error: "Informe e-mail e empresa" }, { status: 400 })
|
return NextResponse.json({ error: "Informe e-mail e empresa" }, { status: 400 })
|
||||||
}
|
}
|
||||||
|
|
||||||
const convexUrl = process.env.NEXT_PUBLIC_CONVEX_URL
|
let client: ConvexHttpClient
|
||||||
if (!convexUrl) return NextResponse.json({ error: "Convex não configurado" }, { status: 500 })
|
try {
|
||||||
const client = new ConvexHttpClient(convexUrl)
|
client = new ConvexHttpClient(requireConvexUrl())
|
||||||
|
} catch {
|
||||||
|
return NextResponse.json({ error: "Convex não configurado" }, { status: 500 })
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const tenantId = session.user.tenantId ?? DEFAULT_TENANT_ID
|
const tenantId = session.user.tenantId ?? DEFAULT_TENANT_ID
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { ConvexHttpClient } from "convex/browser"
|
||||||
|
|
||||||
import { api } from "@/convex/_generated/api"
|
import { api } from "@/convex/_generated/api"
|
||||||
import { env } from "@/lib/env"
|
import { env } from "@/lib/env"
|
||||||
|
import { requireConvexUrl } from "@/server/convex-client"
|
||||||
|
|
||||||
const fleetHostSchema = z.object({
|
const fleetHostSchema = z.object({
|
||||||
host: z
|
host: z
|
||||||
|
|
@ -92,11 +93,6 @@ export async function POST(request: Request) {
|
||||||
return NextResponse.json({ error: "Não autorizado" }, { status: 401 })
|
return NextResponse.json({ error: "Não autorizado" }, { status: 401 })
|
||||||
}
|
}
|
||||||
|
|
||||||
const convexUrl = env.NEXT_PUBLIC_CONVEX_URL
|
|
||||||
if (!convexUrl) {
|
|
||||||
return NextResponse.json({ error: "Convex não configurado" }, { status: 500 })
|
|
||||||
}
|
|
||||||
|
|
||||||
let parsed
|
let parsed
|
||||||
try {
|
try {
|
||||||
const raw = await request.json()
|
const raw = await request.json()
|
||||||
|
|
@ -159,7 +155,7 @@ export async function POST(request: Request) {
|
||||||
cpuLogicalCores: host.cpu_logical_cores,
|
cpuLogicalCores: host.cpu_logical_cores,
|
||||||
}
|
}
|
||||||
|
|
||||||
const client = new ConvexHttpClient(convexUrl)
|
const client = new ConvexHttpClient(requireConvexUrl())
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await client.mutation(api.devices.upsertInventory, {
|
const result = await client.mutation(api.devices.upsertInventory, {
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import { ConvexHttpClient } from "convex/browser"
|
||||||
|
|
||||||
import { api } from "@/convex/_generated/api"
|
import { api } from "@/convex/_generated/api"
|
||||||
import { DEFAULT_TENANT_ID } from "@/lib/constants"
|
import { DEFAULT_TENANT_ID } from "@/lib/constants"
|
||||||
import { env } from "@/lib/env"
|
|
||||||
import { prisma } from "@/lib/prisma"
|
import { prisma } from "@/lib/prisma"
|
||||||
import {
|
import {
|
||||||
computeInviteStatus,
|
computeInviteStatus,
|
||||||
|
|
@ -14,6 +13,7 @@ import {
|
||||||
normalizeRoleOption,
|
normalizeRoleOption,
|
||||||
type NormalizedInvite,
|
type NormalizedInvite,
|
||||||
} from "@/server/invite-utils"
|
} from "@/server/invite-utils"
|
||||||
|
import { requireConvexUrl } from "@/server/convex-client"
|
||||||
|
|
||||||
type AcceptInvitePayload = {
|
type AcceptInvitePayload = {
|
||||||
name?: string
|
name?: string
|
||||||
|
|
@ -25,9 +25,8 @@ function validatePassword(password: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function syncInvite(invite: NormalizedInvite) {
|
async function syncInvite(invite: NormalizedInvite) {
|
||||||
const convexUrl = env.NEXT_PUBLIC_CONVEX_URL
|
const url = requireConvexUrl()
|
||||||
if (!convexUrl) return
|
const client = new ConvexHttpClient(url)
|
||||||
const client = new ConvexHttpClient(convexUrl)
|
|
||||||
await client.mutation(api.invites.sync, {
|
await client.mutation(api.invites.sync, {
|
||||||
tenantId: invite.tenantId,
|
tenantId: invite.tenantId,
|
||||||
inviteId: invite.id,
|
inviteId: invite.id,
|
||||||
|
|
@ -178,20 +177,17 @@ export async function POST(request: Request, context: { params: Promise<{ token:
|
||||||
const normalized = normalizeInvite({ ...updatedInvite, events: [...invite.events, event] }, now)
|
const normalized = normalizeInvite({ ...updatedInvite, events: [...invite.events, event] }, now)
|
||||||
await syncInvite(normalized)
|
await syncInvite(normalized)
|
||||||
|
|
||||||
const convexUrl = env.NEXT_PUBLIC_CONVEX_URL
|
try {
|
||||||
if (convexUrl) {
|
const convex = new ConvexHttpClient(requireConvexUrl())
|
||||||
try {
|
await convex.mutation(api.users.ensureUser, {
|
||||||
const convex = new ConvexHttpClient(convexUrl)
|
tenantId,
|
||||||
await convex.mutation(api.users.ensureUser, {
|
email: invite.email,
|
||||||
tenantId,
|
name,
|
||||||
email: invite.email,
|
avatarUrl: undefined,
|
||||||
name,
|
role: role.toUpperCase(),
|
||||||
avatarUrl: undefined,
|
})
|
||||||
role: role.toUpperCase(),
|
} catch (error) {
|
||||||
})
|
console.warn("Falha ao sincronizar usuário no Convex", error)
|
||||||
} catch (error) {
|
|
||||||
console.warn("Falha ao sincronizar usuário no Convex", error)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return NextResponse.json({ success: true })
|
return NextResponse.json({ success: true })
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,10 @@ import { ConvexHttpClient } from "convex/browser"
|
||||||
|
|
||||||
import { prisma } from "@/lib/prisma"
|
import { prisma } from "@/lib/prisma"
|
||||||
import { requireAuthenticatedSession } from "@/lib/auth-server"
|
import { requireAuthenticatedSession } from "@/lib/auth-server"
|
||||||
import { env } from "@/lib/env"
|
|
||||||
import { DEFAULT_TENANT_ID } from "@/lib/constants"
|
import { DEFAULT_TENANT_ID } from "@/lib/constants"
|
||||||
import { api } from "@/convex/_generated/api"
|
import { api } from "@/convex/_generated/api"
|
||||||
import { ensureCollaboratorAccount } from "@/server/machines-auth"
|
import { ensureCollaboratorAccount } from "@/server/machines-auth"
|
||||||
|
import { requireConvexUrl } from "@/server/convex-client"
|
||||||
|
|
||||||
const updateSchema = z.object({
|
const updateSchema = z.object({
|
||||||
email: z.string().email().optional(),
|
email: z.string().email().optional(),
|
||||||
|
|
@ -167,18 +167,16 @@ export async function PATCH(request: Request) {
|
||||||
role: effectiveRole,
|
role: effectiveRole,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (env.NEXT_PUBLIC_CONVEX_URL) {
|
try {
|
||||||
try {
|
const client = new ConvexHttpClient(requireConvexUrl())
|
||||||
const client = new ConvexHttpClient(env.NEXT_PUBLIC_CONVEX_URL)
|
await client.mutation(api.users.ensureUser, {
|
||||||
await client.mutation(api.users.ensureUser, {
|
tenantId,
|
||||||
tenantId,
|
email: effectiveEmail,
|
||||||
email: effectiveEmail,
|
name,
|
||||||
name,
|
role: effectiveRole,
|
||||||
role: effectiveRole,
|
})
|
||||||
})
|
} catch (error) {
|
||||||
} catch (error) {
|
console.warn("[portal.profile] Falha ao sincronizar usuário no Convex", error)
|
||||||
console.warn("[portal.profile] Falha ao sincronizar usuário no Convex", error)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return NextResponse.json({ ok: true, email: effectiveEmail })
|
return NextResponse.json({ ok: true, email: effectiveEmail })
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,11 @@ import { NextResponse } from "next/server"
|
||||||
import { ConvexHttpClient } from "convex/browser"
|
import { ConvexHttpClient } from "convex/browser"
|
||||||
|
|
||||||
import { api } from "@/convex/_generated/api"
|
import { api } from "@/convex/_generated/api"
|
||||||
import { env } from "@/lib/env"
|
|
||||||
import { assertAuthenticatedSession } from "@/lib/auth-server"
|
import { assertAuthenticatedSession } from "@/lib/auth-server"
|
||||||
import { DEFAULT_TENANT_ID } from "@/lib/constants"
|
import { DEFAULT_TENANT_ID } from "@/lib/constants"
|
||||||
import { buildMachinesInventoryWorkbook, type MachineInventoryRecord } from "@/server/machines/inventory-export"
|
import { buildMachinesInventoryWorkbook, type MachineInventoryRecord } from "@/server/machines/inventory-export"
|
||||||
import type { DeviceInventoryColumnConfig } from "@/lib/device-inventory-columns"
|
import type { DeviceInventoryColumnConfig } from "@/lib/device-inventory-columns"
|
||||||
|
import { requireConvexUrl } from "@/server/convex-client"
|
||||||
|
|
||||||
export const runtime = "nodejs"
|
export const runtime = "nodejs"
|
||||||
|
|
||||||
|
|
@ -14,11 +14,6 @@ export async function GET(request: Request) {
|
||||||
const session = await assertAuthenticatedSession()
|
const session = await assertAuthenticatedSession()
|
||||||
if (!session) return NextResponse.json({ error: "Não autorizado" }, { status: 401 })
|
if (!session) return NextResponse.json({ error: "Não autorizado" }, { status: 401 })
|
||||||
|
|
||||||
const convexUrl = env.NEXT_PUBLIC_CONVEX_URL
|
|
||||||
if (!convexUrl) {
|
|
||||||
return NextResponse.json({ error: "Convex não configurado" }, { status: 500 })
|
|
||||||
}
|
|
||||||
|
|
||||||
const { searchParams } = new URL(request.url)
|
const { searchParams } = new URL(request.url)
|
||||||
const companyId = searchParams.get("companyId") ?? undefined
|
const companyId = searchParams.get("companyId") ?? undefined
|
||||||
const machineIdParams = searchParams.getAll("machineId").filter(Boolean)
|
const machineIdParams = searchParams.getAll("machineId").filter(Boolean)
|
||||||
|
|
@ -49,7 +44,7 @@ export async function GET(request: Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const client = new ConvexHttpClient(convexUrl)
|
const client = new ConvexHttpClient(requireConvexUrl())
|
||||||
const tenantId = session.user.tenantId ?? DEFAULT_TENANT_ID
|
const tenantId = session.user.tenantId ?? DEFAULT_TENANT_ID
|
||||||
|
|
||||||
let viewerId: string | null = null
|
let viewerId: string | null = null
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,11 @@ import { ConvexHttpClient } from "convex/browser"
|
||||||
|
|
||||||
import { api } from "@/convex/_generated/api"
|
import { api } from "@/convex/_generated/api"
|
||||||
import type { Id } from "@/convex/_generated/dataModel"
|
import type { Id } from "@/convex/_generated/dataModel"
|
||||||
import { env } from "@/lib/env"
|
|
||||||
import { assertAuthenticatedSession } from "@/lib/auth-server"
|
import { assertAuthenticatedSession } from "@/lib/auth-server"
|
||||||
import { mapTicketWithDetailsFromServer } from "@/lib/mappers/ticket"
|
import { mapTicketWithDetailsFromServer } from "@/lib/mappers/ticket"
|
||||||
import { DEFAULT_TENANT_ID } from "@/lib/constants"
|
import { DEFAULT_TENANT_ID } from "@/lib/constants"
|
||||||
import { renderTicketPdfBuffer } from "@/server/pdf/ticket-pdf-template"
|
import { renderTicketPdfBuffer } from "@/server/pdf/ticket-pdf-template"
|
||||||
|
import { requireConvexUrl } from "@/server/convex-client"
|
||||||
|
|
||||||
export const runtime = "nodejs"
|
export const runtime = "nodejs"
|
||||||
|
|
||||||
|
|
@ -32,12 +32,7 @@ export async function GET(_request: Request, context: { params: Promise<{ id: st
|
||||||
return NextResponse.json({ error: "Não autorizado" }, { status: 401 })
|
return NextResponse.json({ error: "Não autorizado" }, { status: 401 })
|
||||||
}
|
}
|
||||||
|
|
||||||
const convexUrl = env.NEXT_PUBLIC_CONVEX_URL
|
const client = new ConvexHttpClient(requireConvexUrl())
|
||||||
if (!convexUrl) {
|
|
||||||
return NextResponse.json({ error: "Convex não configurado" }, { status: 500 })
|
|
||||||
}
|
|
||||||
|
|
||||||
const client = new ConvexHttpClient(convexUrl)
|
|
||||||
const tenantId = session.user.tenantId ?? DEFAULT_TENANT_ID
|
const tenantId = session.user.tenantId ?? DEFAULT_TENANT_ID
|
||||||
|
|
||||||
let viewerId: string | null = null
|
let viewerId: string | null = null
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@ import { NextResponse } from "next/server"
|
||||||
import { ConvexHttpClient } from "convex/browser"
|
import { ConvexHttpClient } from "convex/browser"
|
||||||
import { api } from "@/convex/_generated/api"
|
import { api } from "@/convex/_generated/api"
|
||||||
import type { Id } from "@/convex/_generated/dataModel"
|
import type { Id } from "@/convex/_generated/dataModel"
|
||||||
import { env } from "@/lib/env"
|
|
||||||
import { assertAuthenticatedSession } from "@/lib/auth-server"
|
import { assertAuthenticatedSession } from "@/lib/auth-server"
|
||||||
import { DEFAULT_TENANT_ID } from "@/lib/constants"
|
import { DEFAULT_TENANT_ID } from "@/lib/constants"
|
||||||
|
import { requireConvexUrl } from "@/server/convex-client"
|
||||||
|
|
||||||
export const runtime = "nodejs"
|
export const runtime = "nodejs"
|
||||||
|
|
||||||
|
|
@ -42,11 +42,6 @@ export async function GET(request: Request) {
|
||||||
return NextResponse.json({ items: [] }, { status: 401 })
|
return NextResponse.json({ items: [] }, { status: 401 })
|
||||||
}
|
}
|
||||||
|
|
||||||
const convexUrl = env.NEXT_PUBLIC_CONVEX_URL
|
|
||||||
if (!convexUrl) {
|
|
||||||
return NextResponse.json({ items: [] }, { status: 500 })
|
|
||||||
}
|
|
||||||
|
|
||||||
const normalizedRole = normalizeRole(session.user.role)
|
const normalizedRole = normalizeRole(session.user.role)
|
||||||
const isAgentOrAdmin = normalizedRole === "admin" || normalizedRole === "agent"
|
const isAgentOrAdmin = normalizedRole === "admin" || normalizedRole === "agent"
|
||||||
const canLinkOwnTickets = normalizedRole === "collaborator"
|
const canLinkOwnTickets = normalizedRole === "collaborator"
|
||||||
|
|
@ -59,7 +54,7 @@ export async function GET(request: Request) {
|
||||||
const rawQuery = url.searchParams.get("q") ?? ""
|
const rawQuery = url.searchParams.get("q") ?? ""
|
||||||
const query = rawQuery.trim()
|
const query = rawQuery.trim()
|
||||||
|
|
||||||
const client = new ConvexHttpClient(convexUrl)
|
const client = new ConvexHttpClient(requireConvexUrl())
|
||||||
|
|
||||||
// Garantir que o usuário exista no Convex para obter viewerId
|
// Garantir que o usuário exista no Convex para obter viewerId
|
||||||
let viewerId: string | null = null
|
let viewerId: string | null = null
|
||||||
|
|
|
||||||
|
|
@ -3922,7 +3922,7 @@ export function DeviceDetails({ device }: DeviceDetailsProps) {
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
{entry.url ? (
|
{entry.url && !isRustDesk ? (
|
||||||
<a
|
<a
|
||||||
href={entry.url}
|
href={entry.url}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ const envSchema = z.object({
|
||||||
BETTER_AUTH_SECRET: z.string().min(1, "Missing BETTER_AUTH_SECRET"),
|
BETTER_AUTH_SECRET: z.string().min(1, "Missing BETTER_AUTH_SECRET"),
|
||||||
BETTER_AUTH_URL: z.string().url().optional(),
|
BETTER_AUTH_URL: z.string().url().optional(),
|
||||||
NEXT_PUBLIC_CONVEX_URL: z.string().url().optional(),
|
NEXT_PUBLIC_CONVEX_URL: z.string().url().optional(),
|
||||||
|
CONVEX_INTERNAL_URL: z.string().url().optional(),
|
||||||
DATABASE_URL: z.string().min(1).optional(),
|
DATABASE_URL: z.string().min(1).optional(),
|
||||||
NEXT_PUBLIC_APP_URL: z.string().url().optional(),
|
NEXT_PUBLIC_APP_URL: z.string().url().optional(),
|
||||||
MACHINE_PROVISIONING_SECRET: z.string().optional(),
|
MACHINE_PROVISIONING_SECRET: z.string().optional(),
|
||||||
|
|
@ -33,6 +34,7 @@ export const env = {
|
||||||
BETTER_AUTH_SECRET: parsed.data.BETTER_AUTH_SECRET,
|
BETTER_AUTH_SECRET: parsed.data.BETTER_AUTH_SECRET,
|
||||||
BETTER_AUTH_URL: parsed.data.BETTER_AUTH_URL ?? parsed.data.NEXT_PUBLIC_APP_URL ?? "http://localhost:3000",
|
BETTER_AUTH_URL: parsed.data.BETTER_AUTH_URL ?? parsed.data.NEXT_PUBLIC_APP_URL ?? "http://localhost:3000",
|
||||||
NEXT_PUBLIC_CONVEX_URL: parsed.data.NEXT_PUBLIC_CONVEX_URL,
|
NEXT_PUBLIC_CONVEX_URL: parsed.data.NEXT_PUBLIC_CONVEX_URL,
|
||||||
|
CONVEX_INTERNAL_URL: parsed.data.CONVEX_INTERNAL_URL,
|
||||||
DATABASE_URL: parsed.data.DATABASE_URL,
|
DATABASE_URL: parsed.data.DATABASE_URL,
|
||||||
NEXT_PUBLIC_APP_URL: parsed.data.NEXT_PUBLIC_APP_URL,
|
NEXT_PUBLIC_APP_URL: parsed.data.NEXT_PUBLIC_APP_URL,
|
||||||
MACHINE_PROVISIONING_SECRET: parsed.data.MACHINE_PROVISIONING_SECRET,
|
MACHINE_PROVISIONING_SECRET: parsed.data.MACHINE_PROVISIONING_SECRET,
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,14 @@ export class ConvexConfigurationError extends Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isServerSide() {
|
||||||
|
return typeof window === "undefined"
|
||||||
|
}
|
||||||
|
|
||||||
export function requireConvexUrl(): string {
|
export function requireConvexUrl(): string {
|
||||||
const url = env.NEXT_PUBLIC_CONVEX_URL
|
const url = isServerSide()
|
||||||
|
? env.CONVEX_INTERNAL_URL ?? env.NEXT_PUBLIC_CONVEX_URL
|
||||||
|
: env.NEXT_PUBLIC_CONVEX_URL
|
||||||
if (!url) {
|
if (!url) {
|
||||||
throw new ConvexConfigurationError()
|
throw new ConvexConfigurationError()
|
||||||
}
|
}
|
||||||
|
|
@ -21,4 +27,3 @@ export function createConvexClient(): ConvexHttpClient {
|
||||||
const url = requireConvexUrl()
|
const url = requireConvexUrl()
|
||||||
return new ConvexHttpClient(url)
|
return new ConvexHttpClient(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,9 @@ import { ConvexHttpClient } from "convex/browser"
|
||||||
import { api } from "@/convex/_generated/api"
|
import { api } from "@/convex/_generated/api"
|
||||||
import type { Id } from "@/convex/_generated/dataModel"
|
import type { Id } from "@/convex/_generated/dataModel"
|
||||||
import { DEFAULT_TENANT_ID } from "@/lib/constants"
|
import { DEFAULT_TENANT_ID } from "@/lib/constants"
|
||||||
import { env } from "@/lib/env"
|
|
||||||
import { ensureMachineAccount } from "@/server/machines-auth"
|
import { ensureMachineAccount } from "@/server/machines-auth"
|
||||||
import { auth } from "@/lib/auth"
|
import { auth } from "@/lib/auth"
|
||||||
|
import { requireConvexUrl } from "@/server/convex-client"
|
||||||
|
|
||||||
export type MachineSessionContext = {
|
export type MachineSessionContext = {
|
||||||
machine: {
|
machine: {
|
||||||
|
|
@ -38,11 +38,7 @@ export class MachineInactiveError extends Error {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createMachineSession(machineToken: string, rememberMe = true): Promise<MachineSessionContext> {
|
export async function createMachineSession(machineToken: string, rememberMe = true): Promise<MachineSessionContext> {
|
||||||
const convexUrl = env.NEXT_PUBLIC_CONVEX_URL
|
const convexUrl = requireConvexUrl()
|
||||||
if (!convexUrl) {
|
|
||||||
throw new Error("Convex não configurado")
|
|
||||||
}
|
|
||||||
|
|
||||||
const client = new ConvexHttpClient(convexUrl)
|
const client = new ConvexHttpClient(convexUrl)
|
||||||
|
|
||||||
const resolved = await client.mutation(api.devices.resolveToken, { machineToken })
|
const resolved = await client.mutation(api.devices.resolveToken, { machineToken })
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,9 @@ import { ConvexHttpClient } from "convex/browser"
|
||||||
|
|
||||||
import { api } from "@/convex/_generated/api"
|
import { api } from "@/convex/_generated/api"
|
||||||
import type { Id } from "@/convex/_generated/dataModel"
|
import type { Id } from "@/convex/_generated/dataModel"
|
||||||
import { env } from "@/lib/env"
|
|
||||||
import { buildXlsxWorkbook } from "@/lib/xlsx"
|
import { buildXlsxWorkbook } from "@/lib/xlsx"
|
||||||
import { REPORT_EXPORT_DEFINITIONS, type ReportExportKey } from "@/lib/report-definitions"
|
import { REPORT_EXPORT_DEFINITIONS, type ReportExportKey } from "@/lib/report-definitions"
|
||||||
|
import { requireConvexUrl } from "@/server/convex-client"
|
||||||
export type { ReportExportKey }
|
export type { ReportExportKey }
|
||||||
|
|
||||||
type ViewerIdentity = {
|
type ViewerIdentity = {
|
||||||
|
|
@ -24,11 +24,7 @@ export type ConvexReportContext = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createConvexContext(identity: ViewerIdentity): Promise<ConvexReportContext> {
|
export async function createConvexContext(identity: ViewerIdentity): Promise<ConvexReportContext> {
|
||||||
const convexUrl = env.NEXT_PUBLIC_CONVEX_URL
|
const client = new ConvexHttpClient(requireConvexUrl())
|
||||||
if (!convexUrl) {
|
|
||||||
throw new Error("Convex URL não configurada para exportações")
|
|
||||||
}
|
|
||||||
const client = new ConvexHttpClient(convexUrl)
|
|
||||||
const ensuredUser = await client.mutation(api.users.ensureUser, {
|
const ensuredUser = await client.mutation(api.users.ensureUser, {
|
||||||
tenantId: identity.tenantId,
|
tenantId: identity.tenantId,
|
||||||
name: identity.name,
|
name: identity.name,
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,8 @@ services:
|
||||||
# IMPORTANTE: "NEXT_PUBLIC_*" é consumida pelo navegador (cliente). Use a URL pública do Convex.
|
# IMPORTANTE: "NEXT_PUBLIC_*" é consumida pelo navegador (cliente). Use a URL pública do Convex.
|
||||||
# Não use o hostname interno do Swarm aqui, pois o browser não consegue resolvê-lo.
|
# Não use o hostname interno do Swarm aqui, pois o browser não consegue resolvê-lo.
|
||||||
NEXT_PUBLIC_CONVEX_URL: "${NEXT_PUBLIC_CONVEX_URL}"
|
NEXT_PUBLIC_CONVEX_URL: "${NEXT_PUBLIC_CONVEX_URL}"
|
||||||
|
# URLs consumidas apenas pelo backend/SSR podem usar o hostname interno
|
||||||
|
CONVEX_INTERNAL_URL: "http://convex_backend:3210"
|
||||||
# URLs públicas do app (evita fallback para localhost)
|
# URLs públicas do app (evita fallback para localhost)
|
||||||
NEXT_PUBLIC_APP_URL: "${NEXT_PUBLIC_APP_URL}"
|
NEXT_PUBLIC_APP_URL: "${NEXT_PUBLIC_APP_URL}"
|
||||||
BETTER_AUTH_URL: "${BETTER_AUTH_URL}"
|
BETTER_AUTH_URL: "${BETTER_AUTH_URL}"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue