feat(celular): rosto e app usam SSE e token; remove telas legadas
- carinha.html assina /eventos (expressao, app_config, conversa, reacao, recarregar) e volta ao polling de /fila sozinho se o SSE cair; foto dos ajustes a cada (re)conexao; token (?t=) vai em todo fetch ao servidor - APK 0.8-token: token nas preferencias e no menu de toque longo, enviado na pagina, no HTTP do Java e ao llama-server (--api-key); trocar servidor por Intent exige o token salvo; camera/microfone so para o servidor configurado ou a copia local - cerebro/conversa mandam a chave ao llama-server do S10 - servidor: evento SSE 'recarregar'; remove /camera/frame e /app/connect; enderecos antigos redirecionam para a secao do painel novo - remove dashboard, painel, conversa, emojis, treinamento, braco, servos, api, scripts e CSS antigos, audio-robot.js e servir-local.bat - logica pura do painel em web/src/lib/nucleo.ts com testes node --test (substitui test_movimentos.js e test_rastreamento.js) - testes de navegador portados para o painel novo (rtc, rosto, rastreamento) - README, docs/PAINEL.md e docs/SEGURANCA.md reescritos; versao 0.8.0; carinha-pc.service le /etc/carinha.env Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+25
-30
@@ -58,9 +58,15 @@ ORDENS_INTERNAS = {"calma"}
|
||||
APARENCIA = {"rosto": "carinha", "cor": [92, 214, 255]}
|
||||
ARQUIVO_APARENCIA = "aparencia.json"
|
||||
|
||||
# Arquivos servidos como estão: o rosto do celular e o rastreamento do painel.
|
||||
ESTATICOS = {"/carinha.html", "/camera.html", "/reacoes-render.js", "/rastreador-worker.js",
|
||||
"/vendor/mediapipe/vision_bundle.mjs", "/vendor/mediapipe/face.tflite", "/vendor/mediapipe/hands.task",
|
||||
"/vendor/mediapipe/wasm/vision_wasm_internal.js", "/vendor/mediapipe/wasm/vision_wasm_internal.wasm",
|
||||
"/vendor/mediapipe/wasm/vision_wasm_nosimd_internal.js", "/vendor/mediapipe/wasm/vision_wasm_nosimd_internal.wasm"}
|
||||
|
||||
# Rotas que não exigem token: só o necessário para a página do rosto abrir e o app se achar.
|
||||
PUBLICAS_GET = {"/app/health", "/faces", "/gestos", "/config", "/ia/modelo", "/app.apk"}
|
||||
PREFIXOS_API = ("/eventos", "/estado", "/fila", "/rtc/", "/camera/", "/conversa/", "/treinamento/",
|
||||
PREFIXOS_API = ("/eventos", "/estado", "/fila", "/rtc/", "/conversa/", "/treinamento/",
|
||||
"/app/", "/braco/", "/servos/", "/ia/", "/api/", "/gesto/", "/face/", "/diag/")
|
||||
|
||||
|
||||
@@ -290,45 +296,31 @@ class Handler(SimpleHTTPRequestHandler):
|
||||
enfileirar(ordem)
|
||||
return self.json({"ok": True, partes[0]: ordem})
|
||||
|
||||
# Painel novo (Vite/Svelte em web/dist); as telas antigas seguem nos caminhos próprios.
|
||||
# Painel (Vite/Svelte compilado em web/dist) em "/"; o resto é lista branca.
|
||||
painel = Path("web/dist")
|
||||
if caminho in ("/", "") and (painel / "index.html").is_file():
|
||||
if caminho in ("/", "", "/index.html"):
|
||||
if not (painel / "index.html").is_file():
|
||||
return self.json({"erro": "Painel não compilado: rode 'npm run build' em web/ (ou use deploy.ps1)"}, 503)
|
||||
self.path = "/web/dist/index.html"
|
||||
return super().do_GET()
|
||||
if caminho.startswith("/assets/"):
|
||||
elif caminho.startswith("/assets/"):
|
||||
nome = caminho[len("/assets/"):]
|
||||
if "/" in nome or "\\" in nome or nome.startswith(".") or not (painel / "assets" / nome).is_file():
|
||||
return self.json({"erro": "Arquivo não disponível"}, 404)
|
||||
self.path = "/web/dist/assets/" + nome
|
||||
return super().do_GET()
|
||||
|
||||
if caminho in ("/dashboard", "/emojis", "/api"):
|
||||
self.path = caminho + ".html"
|
||||
elif caminho == "/painel":
|
||||
self.path = "/painel.html" # painel de controle
|
||||
elif caminho == '/servos':
|
||||
self.path = '/servos.html'
|
||||
elif caminho == '/conversa':
|
||||
self.path = '/conversa.html'
|
||||
elif caminho == '/treinamento':
|
||||
self.path = '/treinamento.html'
|
||||
elif caminho == '/braco':
|
||||
self.path = '/braco.html'
|
||||
elif caminho in ("/", ""):
|
||||
self.path = "/dashboard.html"
|
||||
elif caminho in ("/dashboard", "/painel", "/conversa", "/emojis", "/treinamento", "/braco", "/servos", "/api"):
|
||||
# Endereços das telas antigas levam à seção equivalente do painel novo.
|
||||
secao = {"/conversa": "conversa", "/emojis": "reacoes", "/treinamento": "reacoes", "/braco": "movimento",
|
||||
"/servos": "movimento", "/api": "ajustes"}.get(caminho, "ao-vivo")
|
||||
self.send_response(302)
|
||||
self.send_header("Location", "/#/" + secao)
|
||||
self.send_header("Content-Length", "0")
|
||||
self.end_headers()
|
||||
return
|
||||
elif caminho == '/qualidade-auto.js':
|
||||
self.path = '/android/app/assets/qualidade-auto.js'
|
||||
elif caminho == '/app.apk':
|
||||
self.path = '/android/carinha-rosto.apk'
|
||||
elif caminho in (
|
||||
'/vendor/mediapipe/vision_bundle.mjs', '/vendor/mediapipe/face.tflite', '/vendor/mediapipe/hands.task',
|
||||
'/vendor/mediapipe/wasm/vision_wasm_internal.js', '/vendor/mediapipe/wasm/vision_wasm_internal.wasm',
|
||||
'/vendor/mediapipe/wasm/vision_wasm_nosimd_internal.js', '/vendor/mediapipe/wasm/vision_wasm_nosimd_internal.wasm'):
|
||||
pass
|
||||
elif caminho in ('/dashboard.html','/emojis.html','/api.html','/dashboard.css','/dashboard.js','/overview.js','/biblioteca.js','/api-painel.js','/painel.css','/conversa.css','/treinamento.css','/servos.css','/braco.css','/audio-robot.js'):
|
||||
pass
|
||||
elif caminho not in ('/conversa.html', '/treinamento.html', '/treinamento.js', '/reacoes-render.js', '/painel-conversa.js', '/braco-calibracao.js', '/movimentos-core.js', '/braco-movimentos.js', '/braco.html', '/braco.js', '/servos.html', '/servos.js', '/carinha.html', '/camera.html', '/painel.html', '/painel-controle.js', '/painel-video.js',
|
||||
'/rastreamento-core.js', '/rastreador-worker.js', '/painel-rastreamento.js'):
|
||||
elif caminho not in ESTATICOS:
|
||||
return self.enviar(json.dumps({'erro': 'Arquivo não disponível'}), codigo=404)
|
||||
return super().do_GET()
|
||||
|
||||
@@ -424,6 +416,7 @@ class Handler(SimpleHTTPRequestHandler):
|
||||
if not online:
|
||||
return self.json({"erro": "Abra o Carinha e conecte ao servidor primeiro"}, 409)
|
||||
FILA.adicionar("recarregar")
|
||||
EVENTOS.publicar("recarregar", {"id": controle.recarga["id"]})
|
||||
return self.json({"ok": True, "mensagem": "Solicitação enviada. Aguardando o celular recarregar a tela."})
|
||||
|
||||
def log_message(self, formato, *args):
|
||||
@@ -465,6 +458,8 @@ def main():
|
||||
servidor.servos = ControleServos(Path.cwd() / 'servos.json')
|
||||
servidor.braco = Braco(Path.cwd() / 'braco.json', servidor.servos)
|
||||
servidor.cerebro = Cerebro(Path.cwd() / 'ia-config.json', servidor.controle, enfileirar)
|
||||
if servidor.token and not servidor.cerebro.chave:
|
||||
servidor.cerebro.chave = servidor.token
|
||||
servidor.conversa = Conversa(Path.cwd() / 'memoria.db', servidor.cerebro)
|
||||
servidor.treinamento = Treinamento(Path.cwd() / 'treinamento.db')
|
||||
servidor.conversa.memoria.on_registrar = servidor.treinamento.registrar
|
||||
|
||||
Reference in New Issue
Block a user