Skip to content

Latest commit

 

History

2 Commits

Folders and files

Repository files navigation

macro-tracker-backend

Proxy pra Open Food Facts com cache real (Redis) e rate-limit. Fica fora do repo do app (app.macro-tracker) porque não é bundlado pelo Metro. O app hoje não está conectado a esse proxy — FoodApiClient (lib/food-api.ts no app) já está isolado atrás de uma interface, então a troca é só implementar um client que aponte pra cá, quando/se isso for decidido.

Rodando local

cp .env.example .env
docker compose up -d   # sobe Redis em :6379
npm install
npm run dev             # servidor em :3000 (ou $PORT)

Endpoints

  • GET /search?q=<query>&page=<n> — busca produtos, cache 1h.
  • GET /barcode/:code — busca por código de barras, cache 24h (misses cacheiam por 60s).
  • GET /health — healthcheck.
  • POST /auth/google — { idToken } (ID token obtido no client via Google Sign-In) → valida contra o Google, cria/atualiza usuário no SQLite, retorna { token, userId, email } (JWT próprio, 30 dias).
  • GET /sync (autenticado, Authorization: Bearer <token>) — devolve o estado mesclado completo: { goals, settings, dayLogs, favorites, savedMeals }.
  • POST /sync (autenticado) — changeset parcial no mesmo formato (só as entidades que mudaram). Merge é por registro, não no blob inteiro — ver seção abaixo.

Duas camadas de rate-limit

  1. Upstream throttle (src/off-client.ts): serializa requests pra OFF com gap mínimo de 350ms + retry com backoff em 503/429 — protege a OFF de todos os clientes do proxy combinados.
  2. Rate-limit de entrada (src/index.ts, express-rate-limit): 60 req/min por IP — protege o proxy em si de abuso.

Auth + sync entre devices

Login via Google (ID token verificado no servidor com google-auth-library, audience = GOOGLE_CLIENT_ID). Dados ficam em SQLite (better-sqlite3, arquivo em SQLITE_PATH):

  • goals e settings — singletons por usuário, last-write-wins no registro inteiro.
  • day_logs (chave date), favorites (chave food_id), saved_meals (chave meal_id) — coleções, last-write-wins por item. Editar o dia de hoje em dois devices offline não derruba os outros dias já sincronizados.

Exclusão usa tombstone (deletedAt no registro, não DELETE de linha) — assim outros devices aprendem da exclusão em vez de ressuscitar uma cópia local antiga.

src/sync-merge.ts tem a decisão pura de merge (resolveWinner, testável sem DB); src/sync-store.ts é a camada SQLite que aplica isso por registro.

O app RN ainda não chama /auth/google nem /sync — esse wiring (telas de login, chamada de sync no macro-context.tsx, e adaptar o reducer local pra trackear updatedAt por dia/favorito/meal) é uma tarefa separada.

Fora do escopo (v1)

  • Auth/API key na busca de alimento — só espelha dados públicos da OFF, sem dado sensível.
  • Apple Sign In — só Google por agora; Apple entra se/quando publicar na App Store.
  • Deploy/hosting — só ambiente de dev local (docker compose + SQLite) por agora.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages