Commit Graph

6 Commits

Author SHA1 Message Date
funman300 fa786bafcf feat(android): wire Android Keystore JNI via OnceLock
Remove the dependency on bevy::android::ANDROID_APP inside
android_keystore.rs. Instead, solitaire_data owns a process-wide
OnceLock<JavaVM> initialised by a new pub fn init_android_jvm().
solitaire_app calls it from android_main before run() so JNI is
ready before any auth-token operation can execute.

- android_keystore: drop ANDROID_APP import; add ANDROID_JVM OnceLock
  and init_android_jvm(vm_ptr: *mut c_void)
- solitaire_data/lib.rs: re-export init_android_jvm for android target
- auth_tokens.rs: update doc comment (Android backend is now complete)
- solitaire_app/lib.rs: call init_android_jvm from android_main

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 11:05:23 -07:00
funman300 6e407a3ea7 fix(engine,server): safe area clamp, analytics batch, achievement save order, daily rollover, replay validation, leaderboard opt-in (#56, #60, #61, #62, #66, #68)
Build and Deploy / build-and-push (push) Successful in 3m54s
- #66: Clamp safe-area insets to 25% of window height with warn!() on excess
- #68: Move fire_flush outside per-event loop in analytics (batch flush once)
- #56: Persist progress before marking reward_granted to prevent XP loss on crash
- #60: Add DateRolloverTimer + check_date_rollover system for midnight seed refresh
- #62: Add validate_header() in replay upload with mode/draw_mode allowlists
- #61: Restore two-query leaderboard opt-in check (SELECT then UPDATE); original
       queries already in .sqlx cache; EXISTS variant would require sqlx prepare

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-28 13:07:22 -07:00
funman300 d5d869a6c8 fix(multi): resolve 16 bugs from comprehensive rules and code review
Build and Deploy / build-and-push (push) Successful in 4m12s
Core (solitaire_core):
- fix(core): auto-complete now requires waste empty to prevent deadlock
- fix(core): reject multi-card moves from waste pile (Klondike rule)
- fix(core): reject foundation-to-foundation moves (score farming exploit)
- fix(core): undo restores score from snapshot baseline, not live score
- feat(scoring): add +5 flip bonus when face-down tableau card is exposed
- feat(scoring): add recycle penalty (Draw-1: -100/pass, Draw-3: -20/pass)

Engine (solitaire_engine):
- fix(engine): remove TokioRuntimeResource::default() panic; degrade gracefully
- fix(engine): add ModalScrim guard to handle_new_game spawn site
- fix(engine): add ModalScrim guard to spawn_restore_prompt spawn site
- fix(engine): add ModalScrim guard to check_no_moves spawn site

Server / Web (solitaire_server):
- fix(web): correct draw_mode casing in replay submission (DrawOne/DrawThree)
- fix(web): correct mode casing in replay submission (Classic) for leaderboard
- fix(web): trim recorded_at to YYYY-MM-DD for NaiveDate deserialization
- fix(server): move /avatars route outside auth middleware (was always 401)

Data / Sync (solitaire_data, solitaire_sync):
- fix(data): namespace Android token file under APP_DIR_NAME with migration
- fix(data): Android token store now multi-user (HashMap); no silent overwrite
- fix(sync): draw_one_wins + draw_three_wins invariant preserved after merge

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 16:27:09 -07:00
funman300 2301cc65d3 fix(data): align android_keystore temp extension with cleanup glob (M-21)
The keystore atomic write used path.with_extension("tmp") producing
auth_tokens.tmp, while cleanup_orphaned_tmp_files only matched *.json.tmp.
A crash after the write but before the rename left an orphaned file
invisible to cleanup.

Fix: use path.with_extension("bin.tmp") to produce auth_tokens.bin.tmp,
and broaden the cleanup glob from ends_with(".json.tmp") to
ends_with(".tmp") so both JSON and binary temp files are caught.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 21:26:23 -07:00
funman300 d761a150d7 chore: rename app from Solitaire Quest to Ferrous Solitaire
Build and Deploy / build-and-push (push) Successful in 4m40s
Updates all in-tree references:
- Android package: com.solitairequest.app → com.ferrousapp.solitaire
- APK name: solitaire-quest → ferrous-solitaire
- Data dir: solitaire_quest → ferrous_solitaire (across all 6 data modules + engine)
- Keyring service: solitaire_quest_server → ferrous_solitaire_server
- Android Keystore key: solitaire_quest_token_key → ferrous_solitaire_token_key
- Gitea repo: Rusty_Solitare → Ferrous-Solitaire (also fixes "Solitare" typo)
- Renamed pkg/solitaire-quest* → pkg/ferrous-solitaire*
- Updated ArgoCD, docker-compose, CI workflow, build script, all docs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 19:23:49 -07:00
funman300 f281425b45 feat(android): Android Keystore AES-GCM token storage via JNI
Replaces the four KeychainUnavailable stubs in auth_tokens.rs with a
real Android Keystore implementation:

- Device-bound AES-256/GCM/NoPadding key under alias
  'solitaire_quest_token_key'; generated on first use, survives
  restarts, destroyed on uninstall.
- Tokens serialised as JSON, encrypted to
  {data_dir}/auth_tokens.bin as [12-byte IV][ciphertext+GCM-tag];
  writes are atomic (tmp → rename).
- Key invalidation (biometric/lock change) surfaces as
  TokenError::KeychainUnavailable, matching desktop fallback semantics.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 21:05:20 -07:00