{"ok":true,"logsDir":"/data/logs","accepts":[".xml",".duckdb",".lmur"],"auth":{"enabled":true,"required":true},"limits":{"maxFileBytes":52428800,"maxFilesPerDay":200,"maxAccountBytes":2147483648},"howTo":{"summary":"Envie os logs do Le Mans Ultimate (resultados .xml, telemetria .duckdb e replay já processado .lmur) com POST em /api/ingest. Um arquivo por requisição é o jeito mais simples e confiável.","endpoint":"https://lmu.ferlabs.tech/api/ingest","methods":[{"name":"octet-stream (recomendado)","description":"POST com content-type application/octet-stream, o arquivo como corpo cru e o header x-file-name com o nome do arquivo (pode vir URL-encoded). Um arquivo por requisição."},{"name":"multipart/form-data","description":"POST com vários campos \"files\" (um por arquivo) e, opcional, o campo \"owner\". Bom para poucos arquivos pequenos (o corpo é lido na memória). Dentro de uma mesma requisição o .xml precisa vir antes do .lmur."}],"headers":{"content-type":"application/octet-stream (ou multipart/form-data)","x-file-name":"obrigatório no modo octet-stream; nome do arquivo com extensão","x-owner":"opcional; quem está enviando (só metadado). Com login, vale o nome da conta","x-mtime":"opcional; mtime do arquivo em ms (só serve para casar .Vcr, que você não deve enviar)","authorization":"Bearer <token> (obrigatório neste servidor)"},"accepted":[".xml: resultado da sessão (rFactor XML com <RaceResults>). A pasta no servidor sai do conteúdo (data + pista), não do caminho local",".duckdb: telemetria da sessão (mantenha o nome original do jogo, ex.: \"<pista>_R_2026-07-19T10_06_31Z.duckdb\"; a pasta sai do nome)",".lmur: replay lite já processado no seu computador (gerado do .Vcr por `lmu lite` ou pelo LMU Uploader). O nome do arquivo precisa ser \"<id da sessão>.lmur\""],"refused":[".Vcr bruto (50 a 275 MB, decodificado no servidor): não envie; gere o .lmur local (este servidor o recusa)","qualquer outra extensão (ex.: .json, .cache, .promo): volta em \"skipped\" com reason \"unsupported\"","XML que não é resultado do rFactor/LMU, ou .lmur sem sessão correspondente ou com formato inválido: \"skipped\" com reason \"invalid\""],"pairing":["O id da sessão é o nome do .xml sem a extensão (ex.: 2026_07_19_10_06_31-70R1.xml tem id 2026_07_19_10_06_31-70R1).","O .lmur deve se chamar exatamente \"<id da sessão>.lmur\" (mesmo nome base do .xml, ex.: 2026_07_19_10_06_31-70R1.lmur). A pasta local não importa.","O .xml da sessão precisa já estar no servidor: envie todos os .xml primeiro e os .lmur depois. Um .lmur sem o .xml volta em skipped com reason \"invalid\".",".duckdb não depende de ordem: vai para a pasta da corrida pelo nome, e pilotos diferentes da mesma corrida se juntam na mesma sessão."],"idempotency":"Reenviar é seguro. Arquivo com o mesmo nome, tamanho e sha256 de um que já está no servidor volta em skipped com reason \"duplicate\" e não é gravado de novo. Mesmo nome com conteúdo diferente é guardado como cópia (-1, -2). Um .lmur de uma sessão que já tem replay também vira \"duplicate\".","response":"JSON 200: { imported: [{ name, path, sessionId?, owner?, sha256, bytes }], skipped: [{ name, reason: \"duplicate\"|\"unsupported\"|\"invalid\", sessionId?, sha256?, bytes? }] }. Erros: 400 { error } (nenhum arquivo ou x-file-name ausente) e 401 { error } (login exigido e sem token válido).","login":["Este servidor EXIGE login para enviar: sem \"Authorization: Bearer <token>\" o POST volta 401.","1) POST https://lmu.ferlabs.tech/api/auth/device/code com JSON {\"client_id\":\"lmu-uploader\"} devolve device_code, user_code, verification_uri_complete e interval (segundos).","2) Imprima para uma pessoa o link https://lmu.ferlabs.tech/device?user_code=<user_code> (o mesmo que verification_uri_complete): ela abre no navegador, entra na conta e aprova.","3) A cada interval segundos, POST https://lmu.ferlabs.tech/api/auth/device/token com JSON {\"grant_type\":\"urn:ietf:params:oauth:grant-type:device_code\",\"device_code\":\"<device_code>\",\"client_id\":\"lmu-uploader\"}. Enquanto o erro for authorization_pending, continue (slow_down: aumente o intervalo em 5 s). Ao aprovar vem access_token.","4) Use o access_token em todos os envios: header \"Authorization: Bearer <access_token>\" (nos exemplos, a variável LMU_TOKEN). O token vale 90 dias e renova com o uso; o script pronto está em curl.login."],"curl":{"login":"LMU_URL=https://lmu.ferlabs.tech\n# 1) pede um código de dispositivo\nCODE=$(curl -sS -X POST \"$LMU_URL/api/auth/device/code\" \\\n    -H \"content-type: application/json\" -d '{\"client_id\":\"lmu-uploader\"}')\nDEVICE_CODE=$(printf '%s' \"$CODE\" | sed -n 's/.*\"device_code\":\"\\([^\"]*\\)\".*/\\1/p')\nUSER_CODE=$(printf '%s' \"$CODE\" | sed -n 's/.*\"user_code\":\"\\([^\"]*\\)\".*/\\1/p')\n# 2) uma pessoa abre o link, entra na conta e aprova\necho \"Abra e aprove: $LMU_URL/device?user_code=$USER_CODE\"\n# 3) espera a aprovação (authorization_pending = ainda não aprovou)\nwhile :; do\n  sleep 5\n  R=$(curl -sS -X POST \"$LMU_URL/api/auth/device/token\" -H \"content-type: application/json\" \\\n      -d \"{\\\"grant_type\\\":\\\"urn:ietf:params:oauth:grant-type:device_code\\\",\\\"device_code\\\":\\\"$DEVICE_CODE\\\",\\\"client_id\\\":\\\"lmu-uploader\\\"}\")\n  LMU_TOKEN=$(printf '%s' \"$R\" | sed -n 's/.*\"access_token\":\"\\([^\"]*\\)\".*/\\1/p')\n  [ -n \"$LMU_TOKEN\" ] && break\n  case \"$R\" in *authorization_pending*|*slow_down*) ;; *) echo \"$R\"; break ;; esac\ndone\nexport LMU_TOKEN   # use em \"Authorization: Bearer $LMU_TOKEN\"","single":"# LMU_TOKEN vem do login por dispositivo (exemplo \"login\"); sem ele o envio volta 401\ncurl -sS -X POST https://lmu.ferlabs.tech/api/ingest \\\n    -H \"content-type: application/octet-stream\" \\\n    -H \"x-file-name: 2026_07_19_10_06_31-70R1.xml\" \\\n    -H \"x-owner: meu-nome\" \\\n    -H \"authorization: Bearer $LMU_TOKEN\" \\\n    --data-binary @2026_07_19_10_06_31-70R1.xml","multipart":"# LMU_TOKEN vem do login por dispositivo (exemplo \"login\"); sem ele o envio volta 401\ncurl -sS -X POST https://lmu.ferlabs.tech/api/ingest \\\n    -F \"owner=meu-nome\" \\\n    -F \"files=@2026_07_19_10_06_31-70R1.xml\" \\\n    -F \"files=@2026_07_19_10_06_31-70R1.lmur\" \\\n    -H \"authorization: Bearer $LMU_TOKEN\"","directory":"# LMU_TOKEN vem do login por dispositivo (exemplo \"login\"); sem ele o envio volta 401\nLMU_LOGS=/data/logs   # pasta com os logs do jogo\nLMU_URL=https://lmu.ferlabs.tech\n# a ordem importa: .xml primeiro (o .lmur só entra se o .xml da mesma sessão já estiver no servidor)\nfor ext in xml duckdb lmur; do\n  find \"$LMU_LOGS\" -type f -iname \"*.$ext\" -not -path \"*/.cache/*\" -not -path \"*/.promo/*\" -print0 |\n  sort -z | while IFS= read -r -d '' f; do\n    curl -sS -X POST \"$LMU_URL/api/ingest\" \\\n      -H \"content-type: application/octet-stream\" \\\n      -H \"x-file-name: $(basename \"$f\")\" \\\n      -H \"x-owner: meu-nome\" \\\n      -H \"authorization: Bearer $LMU_TOKEN\" \\\n      --data-binary @\"$f\"; echo\n  done\ndone"}}}