GymCoach
근력 트레이닝 기록 및 개인 AI 코치
AI 칼로리 트래커는 사진으로 식단의 칼로리·단백질을 계산하고 하루 목표와 비교해 결과를 알려줍니다
Want to eat better? Photograph the plate and get its calories, protein and a verdict against your day. No card for the first 15. Photos stay with the meal.
건강TypeScript · ShellGitHub @kirmalyshev온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기eait.fit — the backend, the contract its clients implement, and the web application
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
The backend of eait.fit — a photo-first nutrition coach — the contract its
clients implement, and the web application at app.eait.fit.
The iOS app and the landing page are separate products that consume this repository; they are not
in it. Comments and rulebooks here occasionally name files from that
side (src/mobile/…, docs/…, scripts/…, deploy/…) or an issue number — those refer to the
private repository this backend is developed alongside.README 전문 보기 ↗The backend of eait.fit — a photo-first nutrition coach — the contract its clients implement, and the web application at app.eait.fit.
One line, on any machine with git, curl and Docker (Compose v2) running:
curl -fsSL https://raw.githubusercontent.com/kirmalyshev/eait/main/src/scripts/self-host.sh | sh
It clones this repo to ~/eait, asks for a model key (OpenRouter, typed
with echo off, or exported as EAIT__BACKEND__LLM_API_KEY) and an optional Telegram bot token,
generates the database passwords, and starts the stack in docker-compose.prod.yml — Postgres,
backend, web app, Caddy. When it ends the app answers on http://localhost:8080, reachable from that
machine only. Run the same line again to upgrade: it pulls, rebuilds and keeps your .env.prod.
For a public server, add EAIT_DOMAIN=eait.example.com [email protected] in front of
sh — the domain's DNS must already point at the machine; ports 80 and 443 are used and Caddy gets
the certificate. Host it yourself below says what that does not do for you.
https://github.com/user-attachments/assets/3969bf68-972d-4076-98ff-e36211031c9a
Three workspaces:
| Workspace | What it is |
|---|---|
src/shared/ | The contract: HTTP routes and types (contract.ts), the health arithmetic, the calorie floor (targets.ts), onboarding, chat orchestration. No renderer, no server — both clients and the backend import it. |
openapi.json | The HTTP surface as OpenAPI 3.1, generated from src/shared/openapi.ts and the types it names by bun run openapi. Committed, and bun run check fails when it is stale. |
src/frontend/ | The web application: a dependency-free browser client and the small bun server that serves it under a per-response CSP nonce. It calls the backend on one origin with relative paths only. |
src/backend/ | The server. One handler per route, all product logic in engine/, a store port with a Postgres implementation and an in-memory one the tests run against, an LLM port with an OpenRouter implementation and a canned one. |
The iOS app and the landing page are separate products that consume this repository; they are not
in it. Comments and rulebooks here occasionally name files from that
side (src/mobile/…, docs/…, scripts/…, deploy/…) or an issue number — those refer to the
private repository this backend is developed alongside.
./dev install # every tool this needs, on macOS or Linux (--check reports, changes nothing)
./dev up --demo --web
Open the web application on the port it prints — no database, no model key: canned analyses, seeded fixtures, and the app talking to the demo backend through its dev proxy.
Against Postgres and a real model, put EAIT__BACKEND__LLM_API_KEY in .env (.env.example is
the full inventory of what the server reads, with every default). To run on your own model
server instead of OpenRouter (Ollama, vLLM, LM Studio, llama.cpp, Together, Groq), set
EAIT__BACKEND__LLM_PROVIDER=openai-compatible with its base URL and models; .env.example
carries a commented Ollama setup. Then:
./dev up --all # Postgres, the backend, the web app — detached, on this worktree's ports
./dev seed # the development accounts, including the one that holds the admin role
./dev runs the stack detached and per worktree, so several checkouts coexist: each gets its own
ports and its own database inside one shared Postgres, derived from a slot number.
./dev up [--backend|--demo] [--web] [--all] | start, detached |
./dev down / ./dev restart | stop this worktree's stack; restart what was last up |
./dev status / ./dev ls | this worktree; every worktree on the machine |
./dev logs [service] | follow |
./dev seed / ./dev db … | fixtures; the shared Postgres and this worktree's database |
./dev env / ./dev url | what this worktree derives; its API URL, for substitution |
Without it, the plain commands still work — bun run demo on :8484, bun run web:build then
EAIT__FRONTEND__BACKEND_ORIGIN=http://127.0.0.1:8484 bun run web on :8485, and bun run start
against a real database. migrate() creates the tables; it never creates the database.
src/iac/ is how eait runs in production: an image for the backend, one for the web application,
Postgres, and TLS in front of all of it on ONE hostname. That single origin is load-bearing — the
browser client calls its API with relative paths under connect-src 'self' and the backend carries
no CORS header, so splitting the two across two names breaks the security model, not just the
routing.
It ships the application, not the machine. Bring your own server with Docker on it, a domain
whose A record already points at that server, and a key from a model provider — or your own
OpenAI-compatible model server (.env.prod.example, EAIT__BACKEND__LLM_PROVIDER). The one-liner above does the steps below for you; by hand, on the server:
cp .env.prod.example .env.prod && chmod 600 .env.prod # then fill it in
docker compose -f docker-compose.prod.yml --env-file .env.prod up -d --build
That is the whole of it. Caddy gets a Let's Encrypt certificate on the first request for the name;
the Postgres image creates the database and migrate() creates the tables inside it.
.env.prod.example is the inventory of every setting, with the shipped default and a sentence on
each, and src/scripts/prod-env.test.ts fails if it ever stops matching what the server reads.
Nothing but Caddy publishes a port — reach the database with docker compose exec db psql.
Prove it answers, on the one origin:
curl -s https://<your domain>/health # {"ok":true,"demo":false} the backend
curl -s https://<your domain>/ | head -3 # the shell the web application
What this does NOT do for you. It does not touch your server: no provisioning, no hardening, no firewall rules, no SSH policy, no unattended upgrades — securing and patching the box is yours, and the containers here are not a substitute for it. There are no backups either, so a bad migration or a deleted volume loses every user's data; that is the largest thing left to build. Nothing monitors the instance between deploys, so one that dies at 02:00 is down until you notice. It does not buy the server, register the domain, create the DNS record or install Docker, and it does not register OAuth clients, obtain a model key, or pay the bill for what that key spends.
The backend also runs a Telegram bot, in the same process, when EAIT__BACKEND__TELEGRAM_BOT_TOKEN
is set. Get a token from @BotFather, and use a separate bot for
development. The bot never runs under --demo, so it needs the Postgres-backed server:
EAIT__BACKEND__TELEGRAM_BOT_TOKEN=… bun run start # logs "telegram connector on as @<bot>"
A person connects their account with Connect Telegram on their plan (/start/plan) or in the
web application. It opens the bot with a one-time code. The rules are in src/backend/telegram/AGENTS.md.
bun run check # typecheck, the web build, the unit suites, and that openapi.json is current
bun run web:e2e # the browser suite in the Chrome already installed, against the demo model
./dev test # the same unit suites PLUS the store contract suite, against real Postgres
bun run check never needs Docker: the store contract suite skips its Postgres half, loudly, when
TEST_DATABASE_URL is unset (CI sets it too, at a Postgres of its own, using the same
eait__test). ./dev test is the opt-in
that sets it here — to this worktree's own test database, eait__test in a single checkout and
eait_<branch>__test in a linked worktree, created by ./dev db up alongside the dev one. Do not
set that variable by hand and do not create a test database yourself: that suite MIGRATES and
WRITES, so a fixed name shared by several checkouts is several test runs writing each other's rows.
It is also a database of its own rather than the one you develop against, because ./dev seed
writes an admin and two of its assertions are about a database with none. The double underscore is
load-bearing — a branch name cannot produce one, so no branch's dev database is another branch's
test database.
AGENTS.md at the root, then the one in each workspace. They are current state: what holds, why,
and what would falsify it.
CONTRIBUTING.md — setup, the rules, what a PR owes. Vulnerabilities go to SECURITY.md, not
the issue tracker. CODE_OF_CONDUCT.md is the Contributor Covenant.
AGPL-3.0 — see LICENSE.
eait is independent and self-funded. If it is useful to you, you can support its development: