Two callbacks with the same state could both exchange the authorization code upstream (concurrently or by replay); the session is now atomically claimed under the proxy lock before the exchange. GET /posters/ and the /auth/result long-poll had no per-IP limiting or concurrency bound, and every poster lookup serialized through an exclusive store lock; both endpoints now use the established limiter and non-expired hits take a read lock. The limiter also tracks active transfers per IP with caps below the global limits (4 for fetches, 2 for uploads), because one unauthenticated client could otherwise take all 16 global poster-fetch slots and hold them through slow ServeContent reads, starving everyone else with 429s; concurrency checks precede bucket charges so a capped denial consumes no admission tokens. /auth/result gets its own per-IP budget instead of sharing /auth/start's burst-3 bucket, where two concurrent NAT'd sign-ins 429'd on the fourth request. It is charged only after the poll secret validates (bogus requests keep the generic 410 and cost nothing), denials carry an honest Retry-After, and the Dart poller - which treated 429 as terminal and abandoned a valid session - retries them until the session's 10-minute lifetime expires.
31 lines
1.5 KiB
Dart
31 lines
1.5 KiB
Dart
/// Shared constants for tracker integrations.
|
|
class TrackerConstants {
|
|
TrackerConstants._();
|
|
|
|
/// Fallback watched threshold (percent) used only until the active server's
|
|
/// threshold is known. The operative value follows
|
|
/// [MediaServerClient.watchedThreshold] (captured per playback in
|
|
/// [TrackerCoordinator]); this constant just seeds the field before playback.
|
|
static const double watchedThresholdPercent = 80.0;
|
|
|
|
static const Duration requestTimeout = Duration(seconds: 20);
|
|
static const Duration authRequestTimeout = Duration(seconds: 15);
|
|
static const Duration refreshTimeout = Duration(seconds: 15);
|
|
static const Duration revokeTimeout = Duration(seconds: 10);
|
|
static const Duration oauthProxyPollTimeout = Duration(seconds: 65);
|
|
static const Duration oauthProxyRetryDelay = Duration(seconds: 2);
|
|
|
|
/// Mirrors the relay's server-side OAuth session TTL. Rate-limited (429)
|
|
/// polls are retried until this deadline; past it the session can no longer
|
|
/// succeed, so the poller surfaces the failure instead.
|
|
static const Duration oauthProxySessionTimeout = Duration(minutes: 10);
|
|
}
|
|
|
|
/// Identifier used across the app to disambiguate per-service operations.
|
|
/// The enum's `.name` forms part of the persistence key — do not rename
|
|
/// without a migration.
|
|
enum TrackerService { mal, anilist, simkl, trakt, mdblist }
|
|
|
|
/// Blacklist+[] syncs every library (the default); whitelist+[] syncs nothing.
|
|
enum TrackerLibraryFilterMode { blacklist, whitelist }
|