feat(desktop): add file attachments and native chat window
- Add file upload support in chat (PDF, images, txt, docs, xlsx) - Limited to 10MB max file size - Only allowed extensions for security - Use native Windows decorations for chat window - Remove ChatFloatingWidget (replaced by native window) - Simplify chat event listeners (window managed by Rust) - Fix typo "sessao" -> "sessão" 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
2f89fa33fe
commit
c217a40030
8 changed files with 537 additions and 104 deletions
73
src/app/api/machines/chat/upload/route.ts
Normal file
73
src/app/api/machines/chat/upload/route.ts
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
import { z } from "zod"
|
||||
|
||||
import { api } from "@/convex/_generated/api"
|
||||
import { createCorsPreflight, jsonWithCors } from "@/server/cors"
|
||||
import { createConvexClient, ConvexConfigurationError } from "@/server/convex-client"
|
||||
|
||||
const uploadUrlSchema = z.object({
|
||||
machineToken: z.string().min(1),
|
||||
fileName: z.string().min(1),
|
||||
fileType: z.string().min(1),
|
||||
fileSize: z.number().positive(),
|
||||
})
|
||||
|
||||
const CORS_METHODS = "POST, OPTIONS"
|
||||
|
||||
export async function OPTIONS(request: Request) {
|
||||
return createCorsPreflight(request.headers.get("origin"), CORS_METHODS)
|
||||
}
|
||||
|
||||
// POST /api/machines/chat/upload
|
||||
// Gera URL de upload para anexos do chat
|
||||
export async function POST(request: Request) {
|
||||
const origin = request.headers.get("origin")
|
||||
|
||||
let client
|
||||
try {
|
||||
client = createConvexClient()
|
||||
} catch (error) {
|
||||
if (error instanceof ConvexConfigurationError) {
|
||||
return jsonWithCors({ error: error.message }, 500, origin, CORS_METHODS)
|
||||
}
|
||||
throw error
|
||||
}
|
||||
|
||||
let raw
|
||||
try {
|
||||
raw = await request.json()
|
||||
} catch {
|
||||
return jsonWithCors({ error: "JSON invalido" }, 400, origin, CORS_METHODS)
|
||||
}
|
||||
|
||||
let payload
|
||||
try {
|
||||
payload = uploadUrlSchema.parse(raw)
|
||||
} catch (error) {
|
||||
return jsonWithCors(
|
||||
{ error: "Payload invalido", details: error instanceof Error ? error.message : String(error) },
|
||||
400,
|
||||
origin,
|
||||
CORS_METHODS
|
||||
)
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await client.action(api.liveChat.generateMachineUploadUrl, {
|
||||
machineToken: payload.machineToken,
|
||||
fileName: payload.fileName,
|
||||
fileType: payload.fileType,
|
||||
fileSize: payload.fileSize,
|
||||
})
|
||||
return jsonWithCors(result, 200, origin, CORS_METHODS)
|
||||
} catch (error) {
|
||||
console.error("[machines.chat.upload] Falha ao gerar URL de upload", error)
|
||||
const details = error instanceof Error ? error.message : String(error)
|
||||
|
||||
// Se for erro de validacao, retornar 400
|
||||
if (details.includes("não permitido") || details.includes("muito grande") || details.includes("inválido")) {
|
||||
return jsonWithCors({ error: details }, 400, origin, CORS_METHODS)
|
||||
}
|
||||
|
||||
return jsonWithCors({ error: "Falha ao gerar URL de upload", details }, 500, origin, CORS_METHODS)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue