mirror of
https://github.com/kuhyx/diet-guard.git
synced 2026-11-10 21:27:11 +01:00
The inline `_DeviceCodeDialog` in `github_mirror_screen.dart` (88 lines) was byte-for-byte identical to wake-alarm's, and `services/github_device_auth.dart` was a fourth copy of the auth client. Both now come from `github_device_auth` in the kuhyx/utils monorepo. This app's copy was the one that mattered most to get right: it took the device-code and token URLs as parameters because GitHub's device-flow endpoints send no CORS headers, so the desktop **web** build has to route both through the local wrapper (`github_client_factory_web.dart` passes them). Extracting todo's simpler hardcoded version would have broken that build outright. The package carries the configurable endpoints instead, defaulting to GitHub's own URLs so the other three consumers are unaffected -- and `flutter build web` here is verified green, which is the check that would have caught it. The dialog also gains a real fix: `on Object` rather than `on Exception`, so an `ArgumentError` shows an error instead of leaving the dialog spinning on "Waiting for authorization…" forever. The unused `token_vault` import in the deleted client confirmed it had no app-specific behaviour left worth keeping. 548 tests pass, analyze clean, web build green, and the release APK is installed on the phone with meal history intact. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U4Rj3s7K2qzrLNgJqqPhno
83 lines
2.9 KiB
Dart
83 lines
2.9 KiB
Dart
/// Wrapper-proxied GitHub client, used by the desktop web build.
|
|
library;
|
|
|
|
import 'package:crdt_sync/crdt_sync.dart';
|
|
import 'package:diet_guard_app/services/desktop_wrapper.dart';
|
|
import 'package:diet_guard_app/services/sync_settings.dart';
|
|
import 'package:github_device_auth/github_device_auth.dart';
|
|
import 'package:http/http.dart' as http;
|
|
|
|
/// This device's id under `diet-guard-sync/devices/`.
|
|
///
|
|
/// Distinct from the phone's `phone` and the Python side's `pc`: the desktop
|
|
/// app is a third device, and sharing an id would make two devices overwrite
|
|
/// each other's pushed log on every tick.
|
|
const syncDeviceId = 'desktop';
|
|
|
|
/// Builds a client whose requests go through the desktop wrapper.
|
|
///
|
|
/// The token is deliberately empty: the wrapper attaches the real one
|
|
/// server-side (see `desktop/github_proxy.dart`), so it never enters the
|
|
/// browser. `settings.token` on web is only ever the stand-in constant.
|
|
GitHubClient createGitHubClient(
|
|
SyncSettings settings, {
|
|
http.Client? httpClient,
|
|
}) => GitHubClient(
|
|
owner: settings.owner,
|
|
repo: settings.repo,
|
|
token: '',
|
|
httpClient: WrapperProxyClient(inner: httpClient),
|
|
);
|
|
|
|
/// Rewrites `https://api.github.com/...` to the wrapper's proxy endpoint.
|
|
///
|
|
/// A decorator rather than a `baseUrl` option on [GitHubClient] so the shared
|
|
/// `crdt_sync` library needs no web-specific knowledge; the rewrite is a
|
|
/// property of how this app is hosted, not of the transport.
|
|
class WrapperProxyClient extends http.BaseClient {
|
|
/// Creates a client delegating to [inner] after rewriting GitHub URLs.
|
|
WrapperProxyClient({http.Client? inner, this.baseUrl = desktopWrapperOrigin})
|
|
: _inner = inner ?? http.Client();
|
|
|
|
final http.Client _inner;
|
|
|
|
/// Origin of the desktop wrapper.
|
|
final String baseUrl;
|
|
|
|
@override
|
|
Future<http.StreamedResponse> send(http.BaseRequest request) {
|
|
if (request.url.host != 'api.github.com' || request is! http.Request) {
|
|
return _inner.send(request);
|
|
}
|
|
final rewritten =
|
|
http.Request(
|
|
request.method,
|
|
Uri.parse(
|
|
'$baseUrl${WrapperPaths.github}api'
|
|
'${request.url.path}',
|
|
).replace(query: request.url.hasQuery ? request.url.query : null),
|
|
)
|
|
..headers.addAll(request.headers)
|
|
..bodyBytes = request.bodyBytes
|
|
..followRedirects = request.followRedirects;
|
|
return _inner.send(rewritten);
|
|
}
|
|
|
|
@override
|
|
void close() => _inner.close();
|
|
}
|
|
|
|
/// Builds the device-flow client, routed through the desktop wrapper.
|
|
///
|
|
/// GitHub's device-flow endpoints send no CORS headers, so the page cannot
|
|
/// call them; the wrapper does, and keeps the resulting token.
|
|
GitHubDeviceAuth createDeviceAuth(
|
|
String clientId, {
|
|
http.Client? httpClient,
|
|
}) => GitHubDeviceAuth(
|
|
clientId: clientId,
|
|
deviceCodeUrl: '$desktopWrapperOrigin${WrapperPaths.github}auth/device/start',
|
|
tokenUrl: '$desktopWrapperOrigin${WrapperPaths.github}auth/device/poll',
|
|
httpClient: httpClient,
|
|
);
|