chore(chat): remover WS dedicado e usar apenas Convex

This commit is contained in:
esdrasrenan 2025-12-09 01:08:59 -03:00
parent f8a472ee46
commit a7d9803c97
7 changed files with 0 additions and 161 deletions

View file

@ -12,8 +12,6 @@ NEXT_PUBLIC_CONVEX_URL=http://127.0.0.1:3210
CONVEX_INTERNAL_URL=http://127.0.0.1:3210
# Intervalo (ms) para aceitar token revogado ao sincronizar acessos remotos (opcional)
REMOTE_ACCESS_TOKEN_GRACE_MS=900000
# Porta do servidor WebSocket de chat (processo dedicado iniciado no container)
CHAT_WS_PORT=3030
# SQLite database (local dev)
DATABASE_URL=file:./prisma/db.dev.sqlite

View file

@ -180,8 +180,3 @@ export async function uploadToConvexStorage(uploadUrl: string, file: Blob | Arra
const json = await response.json().catch(() => ({}))
return json.storageId || json.storage_id
}
const FN_CHECK_UPDATES = "liveChat.checkMachineUpdates"
const FN_LIST_MESSAGES = "liveChat.listMachineMessages"
const FN_POST_MESSAGE = "liveChat.postMachineMessage"
const FN_MARK_READ = "liveChat.markMachineMessagesRead"
const FN_UPLOAD_URL = "liveChat.generateMachineUploadUrl"

View file

@ -64,7 +64,6 @@
"tippy.js": "^6.3.7",
"unicornstudio-react": "^1.4.31",
"vaul": "^1.1.2",
"ws": "^8.18.0",
"zod": "^4.1.9",
},
"devDependencies": {

View file

@ -84,7 +84,6 @@
"three": "0.181.2",
"tippy.js": "^6.3.7",
"unicornstudio-react": "^1.4.31",
"ws": "^8.18.0",
"vaul": "^1.1.2",
"zod": "^4.1.9"
},

View file

@ -1,130 +0,0 @@
#!/usr/bin/env node
/**
* Servidor WebSocket dedicado para notificações de chat (máquinas).
*
* Por enquanto ele replica a lógica de streaming via SSE/poll:
* - autentica via machineToken (query ?token=)
* - consulta checkMachineUpdates a cada 1s
* - envia eventos "connected", "update" e "heartbeat"
* - fecha em caso de erro de autenticação
*
* Isso permite remover SSE/poll no cliente, mantendo compatibilidade com o
* backend Convex existente.
*/
import { WebSocketServer } from "ws"
import { ConvexHttpClient } from "convex/browser"
import { api } from "../convex/_generated/api.js"
const PORT = Number(process.env.CHAT_WS_PORT ?? process.env.PORT_WS ?? 3030)
const POLL_MS = Number(process.env.CHAT_WS_POLL_MS ?? 1000)
const HEARTBEAT_MS = Number(process.env.CHAT_WS_HEARTBEAT_MS ?? 30000)
const convexUrl =
process.env.CONVEX_INTERNAL_URL ??
process.env.NEXT_PUBLIC_CONVEX_URL ??
process.env.CONVEX_URL ??
null
if (!convexUrl) {
console.error("[chat-ws] ERRO: defina CONVEX_INTERNAL_URL ou NEXT_PUBLIC_CONVEX_URL")
process.exit(1)
}
const wss = new WebSocketServer({ port: PORT })
console.log(`[chat-ws] Servidor WebSocket iniciado na porta ${PORT}`)
function buildClient() {
return new ConvexHttpClient(convexUrl)
}
function parseToken(urlString) {
try {
const url = new URL(urlString, "http://localhost")
return url.searchParams.get("token")
} catch {
return null
}
}
wss.on("connection", (ws, req) => {
const token = parseToken(req.url ?? "")
if (!token) {
ws.close(1008, "Missing token")
return
}
const client = buildClient()
let previousState = null
let closed = false
const send = (event, data) => {
if (ws.readyState === ws.OPEN) {
ws.send(JSON.stringify({ event, data }))
}
}
// Heartbeat
const heartbeat = setInterval(() => {
if (closed) return
send("heartbeat", { ts: Date.now() })
}, HEARTBEAT_MS)
// Poll
const poll = setInterval(async () => {
if (closed) return
try {
const result = await client.query(api.liveChat.checkMachineUpdates, {
machineToken: token,
})
const currentState = JSON.stringify({
hasActiveSessions: result.hasActiveSessions,
totalUnread: result.totalUnread,
sessions: result.sessions,
})
if (currentState !== previousState) {
previousState = currentState
send("update", { ...result, ts: Date.now() })
}
} catch (error) {
console.error("[chat-ws] Poll error:", error?.message ?? error)
send("error", { message: "Poll failed" })
ws.close(1011, "Poll failed")
}
}, POLL_MS)
// Primeira validação + evento inicial
client
.query(api.liveChat.checkMachineUpdates, { machineToken: token })
.then((result) => {
previousState = JSON.stringify({
hasActiveSessions: result.hasActiveSessions,
totalUnread: result.totalUnread,
sessions: result.sessions,
})
send("connected", { ts: Date.now(), ...result })
})
.catch((error) => {
console.error("[chat-ws] Token inválido:", error?.message ?? error)
send("error", { message: "Token inválido" })
ws.close(1008, "Invalid token")
})
ws.on("close", () => {
closed = true
clearInterval(poll)
clearInterval(heartbeat)
})
ws.on("error", (err) => {
console.error("[chat-ws] WS erro:", err?.message ?? err)
closed = true
clearInterval(poll)
clearInterval(heartbeat)
})
})
wss.on("error", (err) => {
console.error("[chat-ws] Erro no servidor:", err?.message ?? err)
})

View file

@ -16,7 +16,6 @@ echo "[start-web] Using bun cache dir: $BUN_INSTALL_CACHE_DIR"
echo "[start-web] Using APP_DIR=$(pwd)"
echo "[start-web] NEXT_PUBLIC_APP_URL=${NEXT_PUBLIC_APP_URL:-}"
echo "[start-web] NEXT_PUBLIC_CONVEX_URL=${NEXT_PUBLIC_CONVEX_URL:-}"
echo "[start-web] CHAT_WS_PORT=${CHAT_WS_PORT:-3030}"
ensure_db_writable() {
mkdir -p "$(dirname "$DB_PATH")"
@ -204,19 +203,6 @@ else
echo "[start-web] skipping auth seed (SKIP_AUTH_SEED=true)"
fi
# Iniciar servidor WebSocket de chat (processo dedicado)
CHAT_WS_PORT="${CHAT_WS_PORT:-3030}"
CHAT_WS_SCRIPT="/app/scripts/chat-ws-server.mjs"
if [ -f "$CHAT_WS_SCRIPT" ]; then
echo "[start-web] iniciando chat-ws-server em :$CHAT_WS_PORT"
node "$CHAT_WS_SCRIPT" &
CHAT_WS_PID=$!
# Garantir cleanup
trap "kill $CHAT_WS_PID 2>/dev/null || true" EXIT
else
echo "[start-web] chat-ws-server não encontrado em $CHAT_WS_SCRIPT" >&2
fi
echo "[start-web] launching Next.js"
PORT=${PORT:-3000}
NODE_MAJOR=$(command -v node >/dev/null 2>&1 && node -v | sed -E 's/^v([0-9]+).*/\1/' || echo "")

View file

@ -29,7 +29,6 @@ services:
BETTER_AUTH_SECRET: "${BETTER_AUTH_SECRET}"
REPORTS_CRON_SECRET: "${REPORTS_CRON_SECRET}"
REPORTS_CRON_BASE_URL: "${REPORTS_CRON_BASE_URL}"
CHAT_WS_PORT: "${CHAT_WS_PORT:-3030}"
# Mantém o SQLite fora do repositório
DATABASE_URL: "file:/app/data/db.sqlite"
# Evita apt-get na inicialização porque a imagem já vem com toolchain pronta
@ -68,13 +67,6 @@ services:
- traefik.http.routers.sistema_web.tls=true
- traefik.http.routers.sistema_web.tls.certresolver=le
- traefik.http.services.sistema_web.loadbalancer.server.port=3000
# Roteador dedicado para WebSocket do chat
- traefik.http.routers.sistema_web_ws.rule=Host(`tickets.esdrasrenan.com.br`) && PathPrefix(`/chat-ws`)
- traefik.http.routers.sistema_web_ws.entrypoints=websecure
- traefik.http.routers.sistema_web_ws.tls=true
- traefik.http.routers.sistema_web_ws.tls.certresolver=le
- traefik.http.routers.sistema_web_ws.service=sistema_web_ws
- traefik.http.services.sistema_web_ws.loadbalancer.server.port=3030
networks:
- traefik_public
healthcheck: