尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证制作者的介绍仅有上面的一句话。 更多详情请阅读仓库 README。 仓库 README ↗
正在加载完整 README。
查看完整 README ↗A free meal, workout and progress app built on Ethiopian food, named after
Ethiopia’s everyday flatbread. Anyone can create an account. New users answer a
short setup (goal, sex, age, height, weight, activity, fasting days) and get
daily targets (Mifflin-St Jeor) plus a seven-day Ethiopian meal plan scaled to
them, with meat-, egg- and dairy-free meals on their fasting days
(public/plate/plans.js). Accounts that already had history before this
(Benji’s) keep the original hand-written plan until they choose a goal.
The app includes the meal plan, a five-day training schedule with two recovery days, a food log with macro totals, weigh-ins, and walking and sleep check-ins.
It also works out, from what is logged (nothing extra is stored):
claude-opus-5-5,
lib/food/claude.ts) estimates each food, its portion and its macros. The person checks and
edits the estimate before anything is added. Photos are shrunk in the browser, sent once for
analysis and not stored. Each account gets 20 analyses a day (use_ai_quota() in
supabase/migrations/20261028000000_ai_usage.sql).Built with Next.js on Vercel, with Supabase for the database (PostgreSQL) and for email/password accounts. The public home page describes the site only. Every log, weigh-in and backup is private to the signed-in account.
| Part | Where |
|---|---|
| Public home page and account pages (sign in, sign up, reset password) | app/ (Next.js App Router) |
| The meal and workout app: the original pages, kept as they were | shell/index.html, shell/nutrition.html, served only to signed-in users by app/app/route.ts and app/app/nutrition/route.ts |
| App scripts and styles | public/plate/ (fitness.js, plans.js, insights.js, app.js, visuals.js, merge.js, sync.js, importer.js, dates.js, fitness.css, extra.css) |
| Records API | app/api/progress/route.ts (GET/PUT), app/api/progress/validate/route.ts (checks a backup without saving) |
| Validation and save rules | lib/progress/schema.ts, lib/progress/handler.ts |
| Database | supabase/migrations/20261026000000_progress.sql |
| Session refresh and route protection | proxy.ts, lib/supabase/ |
Records. Each account has two records, fitness (profile, daily logs, weigh-ins)
and nutrition (targets and weighed food entries). Both use the same JSON shape as the original site, so old
backups import directly.
Privacy.
Cache-Control: private, no-store.No silent overwrites. Every save is a compare-and-swap on a revision number
(save_progress() in the migration). If another tab or device saved first, the
save is refused with HTTP 409 and the page offers two choices:
In both cases a copy of this tab's version downloads first.
Saving states. The bar at the top of the app shows one of: saving, saved, offline, error, signed out, or conflict. "Saved" appears only after the server confirms the write. Unsaved edits are kept in this browser, per account, until they reach the server.
Version history. Every update copies the previous version into
progress_history (the last 50 per record). This is a safety net for undoing a
mistaken import (see Recovering an earlier version below).
Dates. Calendar days follow Africa/Addis_Ababa, whatever timezone the
device uses. public/plate/dates.js does date maths on plain YYYY-MM-DD
strings so days never shift.
Needs Node 22.13+ and pnpm 11 (corepack enable or npx [email protected]).
pnpm-workspace.yaml only installs package versions that have been public for at
least 7 days.
pnpm install
cp .env.example .env.local # fill in your Supabase URL and publishable key
pnpm dev # http://localhost:3000
Create a project. On supabase.com, create a new project (the free plan is fine). Pick a region close to Addis Ababa, such as Frankfurt (eu-central-1).
Create the tables. Open SQL Editor, paste in all of supabase/migrations/20261026000000_progress.sql, and run it.
Set the sign-in URLs. Go to Authentication → URL Configuration:
https://www.biniyam.site (the bare domain redirects to www)https://biniyam.site/**https://www.biniyam.site/**http://localhost:3000/**https://daily-plate-sooty.vercel.app/**https://*-biniyaminrs-projects.vercel.app/** (Vercel preview deployments)Turn on sign-up. Go to Authentication → Sign In / Providers → Email and keep "Confirm email" on.
Make email links work on any device. In Authentication → Emails → Templates, replace the link in these two templates:
<a href="{{ .RedirectTo }}&token_hash={{ .TokenHash }}&type=email">Confirm your email</a><a href="{{ .RedirectTo }}&token_hash={{ .TokenHash }}&type=recovery">Choose a new password</a>The app already puts ?next=… in the redirect address, which is why these links start with &. Unchanged default templates also work, but only when the link is opened in the same browser that asked for it.
Copy the keys. In Project Settings → API keys, copy the Project URL and the publishable key into Vercel (see below). Never copy the secret or service-role key.
Photo food log (optional).
supabase/migrations/20261028000000_ai_usage.sql (the daily limit).ANTHROPIC_API_KEY (from console.anthropic.com → API keys) for Production, then redeploy. It is read only on the server.Required for public sign-ups: Supabase’s built-in email sender only sends a few emails per hour, so new people would not receive confirmation emails. Set up custom SMTP (for example Resend, Brevo or Amazon SES) under Authentication → Emails → SMTP Settings.
biniyaminr/daily-plate into Vercel. The framework is detected as Next.js and the defaults are fine.NEXT_PUBLIC_SUPABASE_URLNEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEYbiniyam.site and www.biniyam.site. Set one to redirect to the other, then add the DNS records Vercel shows at your registrar. Leave any other DNS records alone.pnpm test # unit: merge, import preview, dates, insights, reports, meal plans and targets
pnpm test:db # PostgreSQL: the real migration + RLS + API handler
pnpm typecheck && pnpm lint && pnpm build
pnpm test:db starts a throwaway local PostgreSQL (15 or newer; set PGBIN if
it isn't on your PATH). It applies a small stand-in for Supabase's auth
schema (tests/db/supabase-stub.sql) and then the real migration. It checks:
For browser testing without Supabase, run
node --experimental-strip-types tests/e2e/local-server.mjs. It serves the real
pages and the real API handler with an in-memory store and a fixed test user.
The old site kept data either in its own database (signed in with ChatGPT) or in that browser only. Neither carries over automatically: a browser keeps saved data separately for each website, so biniyam.site cannot see the old site's.
On the old site, open My progress and choose Download backup. If you used it without signing in, use Download browser backup on the device where you logged things.
Sign in on biniyam.site. Import before logging anything new. Otherwise the new account creates its own profile (with today's start date), and the import keeps the account's values where they differ.
Choose Import backup, either in the top bar or under My progress, and pick the file. It is checked on the server and nothing is saved yet.
Read the preview. It shows how many weigh-ins, logged days and food entries are new, and lists each value that differs from your account.
Keep Add missing records selected (the default) and choose Import. This never removes or changes what is already in your account, and importing the same file again adds nothing.
Replace my account records is only for starting over. You must type REPLACE to confirm, and your current records download first.
The confirmation lists exactly what was added. Check your other devices: the records are there after a refresh.
Keep the old site and its data until you have checked everything on biniyam.site.
In the Supabase SQL Editor (which runs as the project owner):
select id, kind, revision, replaced_at
from progress_history
where user_id = '<your user id>'
order by id desc;
To restore one, copy its payload out to a JSON file shaped like
{ "version": 1, "fitness": … } and import it with Replace.
Keep sign-ups on. The app is open to the public, so leave Allow new users to sign up enabled.