smooth-app
전 세계 식품의 성분·영양·정보를 자유롭게 협업하고 공개하는 데이터베이스
Private period & cycle tracker: end-to-end encrypted, charts, symptom patterns, phase-based health tips. Web + Android.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
Private period and cycle tracker focused on cycle tracking and health optimisation (not pregnancy).
Web app + Android app, one account, data synced between devices and end-to-end encrypted.
Live: https://shark-week.shark-week-server.workers.dev — landing page at /, the app at /app,
Android APK at /download/SharkWeek.apk (also attached to every GitHub release).README 전문 보기 ↗Private period and cycle tracker focused on cycle tracking and health optimisation (not pregnancy). Web app + Android app, one account, data synced between devices and end-to-end encrypted.
Live: https://shark-week.shark-week-server.workers.dev — landing page at /, the app at /app,
Android APK at /download/SharkWeek.apk (also attached to every GitHub release).
app.html — the whole app (single page, no external scripts or fonts), served at /applanding.html — the promo page at / (no scripts); screenshots in web/img/legal/ — Privacy Policy and Terms (filled in from site.json)server/ — Cloudflare Worker + D1 database (accounts, encrypted sync, security headers)android/ — WebView shell (reminders, privacy screen, file export); ./build.sh → SharkWeek.apksite.json — app name, public host, operator name, contact email, stateToday ring (cycle day, phase, next period, fertile window), quick "period started/ended", daily log (flow, 20 symptoms, pain 0–10, mood, energy, stress, sleep, BBT, cervical mucus, ovulation tests, sex drive, exercise, water/alcohol/caffeine, meds, notes), calendar with predictions and an "edit period days" mode, Insights (cycle & period length charts, symptom-by-cycle-day heatmap, energy/sleep/stress/pain by cycle day, BBT chart with three-over-six ovulation detection, plain-English pattern notes), phase-based health tips, "worth a chat with your GP" flags (healthdirect/Jean Hailes thresholds), printable doctor summary, JSON/CSV export, account deletion, devices list, Android reminders (optionally discreet).
Sleep report (Android): with Usage access, Sleep.java reads only screen on/off events (no app names)
and takes each morning's longest screen-off stretch between 6 pm and 2 pm (short checks under 6 min count as
"woke in the night"). The result goes into that day's encrypted log (sleepAuto) and fills in hours slept
unless typed manually. Today shows last night, a 7-night chart, bedtime consistency and sleep by cycle phase.
Partner heads-up (opt-in): /partner#ID.KEY shows a playful forecast (status level, countdown, likely
symptoms with "how to help", survival kit, golden rules). The owner's device encrypts it with KEY, which exists
only in the link; the server stores ciphertext (partner_shares). A nudge (in-app card + Android notification)
before days out sends a pre-written message via the share sheet. Turning it off deletes the forecast.
Password resets: (1) recovery code, keeps data; (2) a signed-in device resets it (Me → Forgot your password?), keeps data, and must prove it holds the data key, so a stolen session token isn't enough; (3) email link (needs a sending domain + Workers Paid; see below) starts the account fresh, since old logs can't be decrypted.
Add a domain to Cloudflare, onboard it in Email Service, upgrade to Workers Paid, then in wrangler.jsonc add
"send_email": [{ "name": "EMAIL" }] and "vars": { "EMAIL_FROM": "reset@yourdomain" } and deploy. The app shows
the email option automatically once /api/config reports emailReset: true.
./release.sh "What changed" builds a new APK (version code = minutes since 1970), deploys the site/API,
publishes the APK and /version.json, pushes to GitHub and creates a release.
/version.json on start, on resume (every 6 h) and from Me → Check for updates.
A newer version code shows an Update banner that downloads the APK; Android installs it over the old one
(same signing key).UpdateCheck runs once a day (≈10:30) and posts one notification per new version;
tapping it downloads the APK. Android always asks before installing (sideloaded apps can't update silently)./version.json shows Reload./qr.svg (built from site.json host).PBKDF2-SHA256(password, salt, 600k) → HKDF → login key (sent) and wrap key (never sent).day:YYYY-MM-DD, so the server can't see which dates exist.HMAC(AUTH_PEPPER, login key). Lose password and recovery code = data unrecoverable (by design).D1 is SQLite, which has no built-in RLS, so it is enforced in one place: rowsFor(env, userId) in
server/src/worker.js is the only code that touches a user's rows, and the user id comes from the verified
session. test/rls.test.mjs (also run before every deploy) fails if any SQL on users/docs/sessions isn't
pinned to a user; test/api.test.mjs attacks cross-account reads/writes against a running server.
On top of that, the data is ciphertext, so even a bypass would leak nothing readable.
TERMS_VERSION changes.--location oc (Oceania). Cloudflare is disclosed as an overseas recipient (APP 8).cd server && npm install
echo "AUTH_PEPPER=$(node -e 'console.log(require("crypto").randomBytes(32).toString("base64url"))')" > .dev.vars
npm run dev # http://127.0.0.1:8787
node test/api.test.mjs # in another terminal
The Cloudflare login for this project lives in server/.cf-home (gitignored), separate from any other
wrangler login on the machine. To log in again: cd server && XDG_CONFIG_HOME=$PWD/.cf-home npx wrangler login.
cd server
npx wrangler d1 create shark-week --location oc # put the database_id into wrangler.jsonc
node -e 'console.log(require("crypto").randomBytes(32).toString("base64url"))' | npx wrangler secret put AUTH_PEPPER
npm run deploy # builds, RLS check, migrations, deploy
Then set host in site.json to the deployed address, npm run deploy again, and cd ../android && ./build.sh.
Never change or lose AUTH_PEPPER (everyone would be locked out) or android/sharkweek.keystore.