GoDoctor - Flutter telemedicine app: doctor consultations, prescriptions and chemist fulfilment, backed by Supabase.
翻译准备中
取自仓库 README
正在加载完整 README。 以下为已保存的摘录。
On-demand telemedicine + medicine ordering marketplace for Kenya (patients,
doctors, chemists). See PROJECT_SPEC.md for the full
product/technical spec this build follows.
The download page offers GoDoctor for Android (the latest release's APK) and
GoDoctor for iOS (the app itself at /app/, added to the Home Screen). Its
source is in website/; .github/workflows/website.yml builds the
app for the web from the latest release and publishes both with GitHub Pages
after every release — no action needed.查看完整 README ↗On-demand telemedicine + medicine ordering marketplace for Kenya (patients, doctors, chemists). See PROJECT_SPEC.md for the full product/technical spec this build follows.
The download page offers GoDoctor for Android (the latest release's APK) and
GoDoctor for iOS (the app itself at /app/, added to the Home Screen). Its
source is in website/; .github/workflows/website.yml builds the
app for the web from the latest release and publishes both with GitHub Pages
after every release — no action needed.
app/ Flutter web app (PWA) -- patient, doctor, chemist, admin UI
erp/ GoDoctor HQ -- the company console for staff (React + Vite)
supabase/migrations/ Numbered SQL migrations: schema, RLS, matching logic, storage
supabase/functions/ Edge functions: push, inventory-sync, erp-staff (HQ accounts)
supabase/seed/ Starter KEML drug catalog (~100 rows)
erp/)The back office: patients, doctors and pharmacies (verification, licences),
consultations, orders, payments and payout statements, support tickets,
reviews, announcements, staff with roles and permissions, and an activity log
that can't be edited. It talks to the same Supabase project as the app; every
page reads erp_* views and functions that check the signed-in staff
member's role (erp_can), and opening someone's health information needs a
reason and is logged.
It's published unlisted at /godoctor/hq/ by website.yml whenever erp/
changes. Users whose role is admin are always HQ administrators; they add
everyone else under Staff (the erp-staff function creates the account and
shows a temporary password once). To work on it locally:
cd erp
npm install
npm run dev # reads the Supabase URL + anon key from ../app/.env
npm run build # type-check and build into erp/dist
supabase/migrations/ in order
(0001 through 0009), then supabase/seed/keml_subset.sql.
supabase db push
applies everything in supabase/migrations/ for you.update public.users set role = 'admin' where id = '<that user's auth.users id>';
(There's no self-service admin signup by design.)cd app
cp .env.example .env
# edit .env: paste your Supabase project URL + anon key
.env is git-ignored -- never commit real credentials.
cd app
flutter pub get
flutter run -d chrome
To build the deployable PWA:
flutter build web
web/manifest.json is already set to display: standalone for
Add-to-Home-Screen / browser-install support.
The same codebase builds a native Android app (recommended for reliable push on the 20-30s doctor-offer window):
GitHub builds the signed APK and publishes it as a Release; the download page picks it up automatically. Either:
git tag v1.0.1 && git push origin v1.0.1
or GitHub → Actions → Release Android APK → Run workflow and type the
version. The workflow is .github/workflows/release-apk.yml; it needs the
repository secrets APP_ENV, ANDROID_KEYSTORE_BASE64 and
ANDROID_KEYSTORE_PASSWORD (already set). Each run gets a higher build
number, so phones install it as an update.
Signing key: secrets/godoctor-release.jks (git-ignored; password in
secrets/KEYSTORE-README.txt). Back it up somewhere safe: without it you
can never ship an update to installed apps (or the Play Store listing).
cd app
flutter build apk --debug --target-platform android-arm64 # quick test build
flutter build apk --release # signed with secrets/godoctor-release.jks via android/key.properties
Things that will bite on a fresh machine:
flutter doctor must show the Android toolchain
green: install the SDK command-line tools and run
flutter doctor --android-licenses (you have to accept the licences
yourself).JAVA_HOME must point at a JDK that exists (JDK 17-21 is safest). A
stale JAVA_HOME gives "The supplied javaHome seems to be invalid".android/gradle.properties is sized for a 4 GB machine. The
Flutter default (-Xmx8G) crashes the Gradle JVM with "insufficient
memory" on small machines; raise it on a bigger one.--target-platform android-arm64 skips the emulator/32-bit engines.pubspec.yaml, delete app/.dart_tool/flutter_build
if the web build complains about a package that no longer exists.flutter test tool/generate_icon_test.dart, then
dart run flutter_launcher_icons."About this medicine" in the Order Medicine screen reads the drug_info
table, filled from RxNorm (matching, free, no key) and openFDA drug
labels (free; add OPENFDA_API_KEY=... to app/.env only if you hit rate
limits):
cd app
node tool/import_drug_info.js # writes supabase/seed/drug_info.sql + a report
Then load supabase/seed/drug_info.sql into the database. Only labels for the
same form/route are used (no IV label for eye drops); medicines without a safe
match simply show "information not available yet". These are US labels, so the
app never shows US dosing to patients and always shows the source. To switch to
a paid Kenyan source later, replace the importer; the app only reads
drug_info.
Migrations live in supabase/migrations/. The repo is linked to the live
project with the Supabase CLI (supabase/config.toml), and the local files
and the database's migration history match exactly:
npx supabase migration list --linked # local vs remote, should all match
npx supabase migration new <name> # create a new migration file
npx supabase db push --linked # apply any new ones to the live DB
npx supabase db query --linked -f file.sql # run a one-off SQL/data file
(The Supabase MCP server in .mcp.json works on the same project too.) Data
files that aren't schema, like the medicine information, live in
supabase/seed/ and are loaded with db query -f.
On Supabase's free plan a project pauses after a week without activity, and there are no backups. Two scheduled workflows cover that:
keep-awake.yml, daily) reads one row so the
project never counts as inactive. A failed run (GitHub emails you) usually
means it paused anyway: Supabase dashboard > Restore. It also adds an empty
commit when main has had none for 45 days, because GitHub switches off
scheduled jobs after 60 days without commits.backup.yml, Sundays 02:23 Kenya time) dumps the whole
database (every schema, structure and data, with pg_dump 17) as the
read-only role godoctor_backup, downloads every stored file through the
backup-files function, and locks it all with
age before keeping it as a run
artifact for 90 days (Actions > Back up data > a run > Artifacts).Only secrets/godoctor-backup-key.txt (git-ignored) can open a backup; keep
copies of it off this laptop. Secrets: BACKUP_DB_URL (the role's sign-in,
through the session pooler), BACKUP_FILES_TOKEN (also set on the Supabase
functions) and APP_ENV. If the database is recreated from migrations, the
role exists but needs a new password and a new BACKUP_DB_URL.
To open a backup and see what's in it (tables.txt has the row counts):
age -d -i godoctor-backup-key.txt godoctor-backup-<date>.tar.gz.age | tar -xz
pg_restore --list backup/database/godoctor.dump
pg_restore can bring back anything from the dump: one table's rows, a
schema, or everything. To rebuild in a new Supabase project, create the
structure from this repo (npx supabase db push), empty the tables the
migrations fill (roles, permissions, settings, buckets), then load the data:
pg_restore --list backup/database/godoctor.dump \
| grep -vE 'TABLE DATA (auth schema_migrations|storage migrations) ' > keep.list
pg_restore --data-only -n public -n auth -n storage -L keep.list -f data.sql backup/database/godoctor.dump
psql "<new project's connection string>" --single-transaction -v ON_ERROR_STOP=1 \
-c "SET session_replication_role = replica" -f data.sql
Then upload backup/files/<bucket>/... to the same buckets with overwrite on
(the file records came back with the data), deploy supabase/functions/
with their secrets, and point app/.env and the APP_ENV secret at the new
project.
Everything in the spec's "build now" list is implemented against live Supabase tables/RLS/Realtime: auth, role-based dashboards, doctor matching (rank-by-longest-idle-time with offer/decline/timeout chain), emergency keyword hard-stop, prescription issuing (structured + free-text), chemist inventory, medicine search + nearby-chemist matching, order lifecycle, and the doctor/chemist verification queue.
Deliberately stubbed, per spec's "explicitly deferred" list, with a documented seam for later:
OrderRepository.placeOrder writes a payments row with
is_simulated = true and marks it succeeded immediately -- no real M-Pesa
Daraja call yet. Swap that one method for an Edge Function call.core/widgets/video_call_panel.dart is an isolated
placeholder widget everywhere a call surface is shown. Drop in Agora/Daily.co
there without touching the surrounding in-call layout.services/notifications.dart only drives in-app
banners today (no service-worker Web Push yet -- needs VAPID keys + an Edge
Function sender). SMS fallback is not built (still an open decision per spec).expire_stale_offers() (0008 migration) exists but needs
a scheduler to actually fire -- either enable pg_cron in the Supabase
dashboard and schedule it every ~10s, or call it from a periodically-invoked
Edge Function. Not wired up yet.Commission structure, doctor payout timing, exact dispute/timeout auto-resolution policy, in-consultation text chat, and the SMS-fallback decision are all still open business/product calls -- see PROJECT_SPEC.md's "Known open decisions".