Repository navigation
feat: Android support and the symmetric family model - #7
Merged
Merged
Conversation
Mirror the Apple login flow: a one-time server nonce challenge, Google ID token verification against Google's JWKS (both issuer spellings, audience = Web OAuth client ID), and users.google_subject. GOOGLE_CLIENT_ID left empty keeps both endpoints at 503. Migration 0011 adds the column and the google_login_challenges table.
Devices registered with platform=ANDROID now receive the incoming-call and report-ready signals as FCM HTTP v1 data messages. The access token is minted from a Firebase service account (FCM_SERVICE_ACCOUNT_PATH) and reused; unset keeps Android delivery disabled. iOS delivery is unchanged.
- An empty FCM_SERVICE_ACCOUNT_PATH (compose default) means unset instead of
Path('.') crashing the whole backend on start.
- Without FCM, Android rows are left out of report delivery, and report
retries skip devices that already received the notification.
- The callee receiver check accepts Android devices when FCM is configured.
- Only UNREGISTERED or an INVALID_ARGUMENT naming message.token counts as a
dead token, and clearing it touches that device row only.
- A 401 drops the cached access token; token_uri must be Google's.
- Report messages collapse per call; service-account JSON is git-ignored.
The compose files passed FCM_SERVICE_ACCOUNT_PATH through as a raw path but mounted no key file, so a host path in the deploy env made AppContainer fail at startup and took the API down for every client. Mount FCM_KEY_FILE like the APNs key and fill the container path only when it is set; when it is empty, /dev/null is mounted and Android push stays off.
Android also requests question audio from ElevenLabs directly and signs in with Google, so the iPhone-only wording was not true for Android users. Say phone instead of iPhone, name both login tokens, and bump the document version so the change is recorded as a new consent.
pyjwt 2.15.1, aiohttp 3.14.5, urllib3 2.8.0, certifi 2026.10.23 and multidict 6.9.1 (kept on 6.x). pip-audit reports none afterwards.
The final Figma asks for 건망증 · 호흡기 질환 · 비염 plus a free-text "추가적으로 걱정되는 부분". Health profiles now accept FORGETFULNESS, RESPIRATORY and RHINITIS alongside the earlier codes (the iOS app still sends those) and store an optional `concerns` note of up to 500 chars. - Migration 0012 adds parent_profiles.concerns. - PUT keeps the stored note when a client sends only conditions. - Daily question pools for the three new conditions use open questions, as docs/profile-question-report-design.md §5 asks.
S1 of docs/family-symmetric-plan.md. Server decisions no longer look at
users.role; the column stays for responses and for picking the analysis
subject until S3.
- One family per user. Anyone in a family can create or resend an
invitation. Anyone with no family, or alone in their own family, can
accept (the solo family is removed); someone already in a family with
other people gets 409.
- POST /v1/families returns the caller's family or creates it.
- Member list adds acceptedAt, consentGranted and isCreator, lists the
creator in the same shape (empty relation), sets canInvite for
everyone and shows invitations to every member.
- Calls work between any two members of the same family. calls.parent_id
is the analysis subject: the one PARENT if exactly one participant has
that role (current iOS behaviour), otherwise the callee.
- /parents/{id}/... reads id as the subject user: the subject and
same-family members may read it; same-family members may also edit
the profile once the subject has consented.
- Google login no longer needs a role. Without one the account starts
with no family; clients that send a role (iOS) keep the old flow.
S2 of docs/family-symmetric-plan.md.
- Migration 0013 adds users.nickname (unique), gender, birth_date and
email, all nullable for existing accounts.
- UserView gains the same fields plus signupCompleted (has a nickname).
- GET /v1/account/nickname-availability returns {available, reason}
with reason INVALID or TAKEN. Nicknames are 2-10 Hangul syllables,
Latin letters or digits.
- PATCH /v1/account/profile sets all three fields; a taken nickname is
409 and a malformed one 422.
- Google login stores the verified email from the ID token.
- The member list shows each person's nickname and birth date.
S4 of docs/family-symmetric-plan.md.
- Migration 0014 adds family_shares (owner_id, viewer_id, report,
timeline) and users.marketing_opt_in (default false).
- GET/PUT /v1/account/sharing lists one row per connected member of the
caller's family; with no row everything is shared. Reports, baselines
and signals check `report`; the call list checks `timeline`. Profiles
and daily questions stay open to the family.
- DELETE /v1/families/{familyId}/members/{memberId}: any member can drop
a pending invitation; a connected member can be removed by the family
creator or leave on their own, which leaves them without a family and
clears the sharing rows between them and the rest of the family. The
creator cannot be removed (409).
- The member list gains canRemove.
- PATCH /v1/account/preferences sets marketingOptIn; UserView shows it.
- Account deletion also removes the user's sharing rows.
The family creator showed up to others by their Google/Apple account name. Members list, family list, incoming call push and LiveKit names now prefer the sign-up nickname and fall back to the account name, so accounts without a nickname (iOS) look the same as before.
This was referenced Nov 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
요약
안드로이드 앱을 운영에서 쓰기 위한 서버 변경을 한 번에 합칩니다.
PR #6(
feat/android-port)의 커밋을 모두 포함하므로 이 PR 하나만 합치면 됩니다(합치면 #6 도 병합된 것으로 닫힙니다).concernsbackend/docs/family-symmetric-plan.mdmain 과 충돌 없음, main 뒤 20커밋.
배포 전에 해 주세요 — EC2
/etc/collog/backend.envGOOGLE_CLIENT_ID는 Firebasecollog-2884b의 웹 클라이언트 ID 라 비밀값이 아닙니다.fcm-service-account.json)은 용진이 따로 전달합니다. 위 경로(호스트 절대 경로)에 두고,APPLE_KEY_FILE과 같은 방식으로 컨테이너 사용자(uid 10001)가 읽을 수 있게 해 주세요. compose 가/run/secrets/fcm-service-account.json으로 읽기 전용 bind 합니다.배포 때 일어나는 일
0011_google_login→0012_profile_concerns→0013_user_signup_fields→0014_family_sharing.deploy.sh가 적용 전에pg_dump백업을/opt/collog/backups/에 남깁니다.2026-10-26.v5로 바뀝니다 → 기존 iOS 사용자는 앱에서 한 번 다시 동의하게 됩니다(문구에 휴대폰 두 플랫폼·Google 로그인 추가).확인한 것
ruff check통과 (aebdadf)Collog-iOSmainf738313)을 시뮬레이터에서 이 서버에 붙여 확인: 로그인 → 안드로이드가 만든 초대 코드 수락 → 동의 → 홈(연락처) → 설정 구성원 화면 → 통화 생성(201). 실제 통화는 시뮬레이터에서 CallKit 이 동작하지 않아 아이폰 실기로 배포 뒤 확인합니다.배포 뒤 확인
GET /v1/health200,/openapi.json에/v1/auth/google이 있는지deploy.sh는 실패 시 이전 판을 다시 띄우고, DB 는 위 백업으로 되돌릴 수 있습니다.