Skip to content

feat: Google Drive sync — OAuth, upload/download, conflict detection#48

Open
DipandaAser wants to merge 4 commits into
mainfrom
feat/google-drive-sync
Open

feat: Google Drive sync — OAuth, upload/download, conflict detection#48
DipandaAser wants to merge 4 commits into
mainfrom
feat/google-drive-sync

Conversation

@DipandaAser

Copy link
Copy Markdown
Owner

Summary

Adds Google Drive cloud sync for the web app — fully client-side, no backend needed.

Architecture

  • Google Identity Services (GIS) for OAuth 2.0 implicit flow — loaded via <script> tag
  • DRIVE_APPDATA scope — files are private to the app, invisible to the user's Drive
  • REST API via fetch — no SDK dependencies, direct calls to Drive API v3
  • Single file (source-of-truth.jwlibrary) + metadata sidecar (sync-meta.json) in appDataFolder

Features

  • Connect/Disconnect Google Drive from Settings
  • Auto-sync after every library merge (non-blocking, fire-and-forget)
  • Sync on app load — silent re-auth + check if remote is newer
  • Conflict detection — compares local storedAt vs remote modifiedTime
  • Conflict resolution dialog — choose to keep local or remote version
  • Sync status badge on the Library source of truth card
  • Manual "Sync now" button in Settings

New files

  • apps/web/src/lib/gdrive.ts — Core sync module (auth, upload, download, conflict)
  • apps/web/src/lib/components/SyncSection.svelte — Sync UI for Settings
  • apps/web/src/lib/types/google.d.ts — TypeScript declarations for GIS
  • apps/web/.env.example — Documents PUBLIC_GOOGLE_CLIENT_ID

Setup required

To test, you need a Google Cloud project with:

  1. OAuth 2.0 Client ID (Web application type)
  2. Authorized JavaScript origins: http://localhost:5173 (dev) + production URL
  3. Drive API enabled
  4. Set PUBLIC_GOOGLE_CLIENT_ID in .env

Closes #19

Test plan

  • Without .env configured: sync section shows "Connect" button, clicking fails gracefully
  • With valid client ID: OAuth popup appears, grants access
  • After connecting: status shows "Connected to Google Drive"
  • Merge in library mode: auto-uploads to Drive
  • "Sync now" button triggers manual sync
  • Close and reopen app: silent re-auth reconnects
  • Modify backup on another device (or directly in Drive): conflict dialog appears
  • "Keep local" uploads, "Keep remote" downloads
  • "Disconnect" revokes token and clears sync metadata
  • Library truth card shows sync badge when connected
  • Run pnpm --filter @jw-notes-sync/web check — 0 errors

🤖 Generated with Claude Code

…ection

- Google Identity Services (GIS) for client-side OAuth 2.0 implicit flow
- DRIVE_APPDATA scope — files private to app, invisible to user
- Upload source of truth to Google Drive after each library merge
- Download latest backup from Drive on app load if remote is newer
- Conflict detection: compare local vs remote modifiedTime
- Conflict resolution dialog: keep local or keep remote
- SyncSection component in Settings: connect/disconnect, sync now, status
- Sync badge on Library source of truth card (synced/not synced)
- Silent re-auth on return visits (token refresh via prompt:'')
- Generic config helpers exported from storage.ts
- 18 new i18n keys in EN and FR
- GIS script loaded async in app.html
- TypeScript declarations for google.accounts.oauth2

Closes #19
@DipandaAser DipandaAser self-assigned this Mar 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Google Drive sync: OAuth, DRIVE_APPDATA, upload/download

1 participant