From f599d52ce88dff9565cad9fb9065c18c55ba7b20 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Sun, 11 Aug 2024 12:54:29 +0000 Subject: [PATCH] Add shared token support --- lib/api/client.dart | 3 +++ lib/api/client.g.dart | 32 +++++++++++++++++++++++++++ lib/api/token.dart | 50 +++++++++++++++++++++++++++++++++++++++++++ lib/api/token.g.dart | 12 +++++++++++ lib/auth.dart | 17 +++++++++++++++ lib/globals.dart | 19 +++++++++++++++- lib/main.dart | 1 + 7 files changed, 133 insertions(+), 1 deletion(-) diff --git a/lib/api/client.dart b/lib/api/client.dart index fefb032..cf2556f 100644 --- a/lib/api/client.dart +++ b/lib/api/client.dart @@ -189,6 +189,9 @@ abstract class _EHApi { @GET('/token') Future> getToken( {@Query("token") String? token, @CancelRequest() CancelToken? cancel}); + @GET('/shared_token') + Future> getSharedToken( + {@CancelRequest() CancelToken? cancel}); @GET('/file/{id}') @DioResponseType(ResponseType.bytes) diff --git a/lib/api/client.g.dart b/lib/api/client.g.dart index 3a7f6db..65723b0 100644 --- a/lib/api/client.g.dart +++ b/lib/api/client.g.dart @@ -655,6 +655,38 @@ class __EHApi implements _EHApi { return _value; } + @override + Future> getSharedToken({CancelToken? cancel}) async { + final _extra = {}; + final queryParameters = {}; + queryParameters.removeWhere((k, v) => v == null); + final _headers = {}; + const Map? _data = null; + final _result = await _dio.fetch>( + _setStreamType>(Options( + method: 'GET', + headers: _headers, + extra: _extra, + ) + .compose( + _dio.options, + '/shared_token', + queryParameters: queryParameters, + data: _data, + cancelToken: cancel, + ) + .copyWith( + baseUrl: _combineBaseUrls( + _dio.options.baseUrl, + baseUrl, + )))); + final _value = ApiResult.fromJson( + _result.data!, + (json) => SharedToken.fromJson(json as Map), + ); + return _value; + } + @override Future>> getFile( int id, { diff --git a/lib/api/token.dart b/lib/api/token.dart index e393519..caf9a9d 100644 --- a/lib/api/token.dart +++ b/lib/api/token.dart @@ -58,3 +58,53 @@ class TokenWithUserInfo { _$TokenWithUserInfoFromJson(json); Map toJson() => _$TokenWithUserInfoToJson(this); } + +enum SharedTokenType { + @JsonValue(0) + gallery, +} + +sealed class SharedTokenInfoBase {} + +@JsonSerializable() +class GallerySharedTokenInfo implements SharedTokenInfoBase { + const GallerySharedTokenInfo({required this.gid}); + final int gid; + factory GallerySharedTokenInfo.fromJson(Map json) => + _$GallerySharedTokenInfoFromJson(json); + Map toJson() => _$GallerySharedTokenInfoToJson(this); +} + +class SharedToken { + const SharedToken({ + required this.id, + required this.token, + this.expired, + required this.type, + required this.info, + }); + final int id; + final String token; + final DateTime? expired; + final SharedTokenType type; + final SharedTokenInfoBase info; + factory SharedToken.fromJson(Map json) { + final int id = (json["id"] as num).toInt(); + final String token = json["token"] as String; + final tmp = json["expired"] as String?; + final expired = tmp != null ? DateTime.parse(tmp) : null; + final int type = (json["type"] as num).toInt(); + final info = json["info"] as Map; + switch (type) { + case 0: + return SharedToken( + id: id, + token: token, + expired: expired, + type: SharedTokenType.gallery, + info: GallerySharedTokenInfo.fromJson(info)); + default: + throw ArgumentError.value(type, 'type', 'Invalid task type'); + } + } +} diff --git a/lib/api/token.g.dart b/lib/api/token.g.dart index 7d8a9c8..fd3cf60 100644 --- a/lib/api/token.g.dart +++ b/lib/api/token.g.dart @@ -50,3 +50,15 @@ Map _$TokenWithUserInfoToJson(TokenWithUserInfo instance) => 'is_admin': instance.isAdmin, 'permissions': UserPermissions.toJson2(instance.permissions), }; + +GallerySharedTokenInfo _$GallerySharedTokenInfoFromJson( + Map json) => + GallerySharedTokenInfo( + gid: (json['gid'] as num).toInt(), + ); + +Map _$GallerySharedTokenInfoToJson( + GallerySharedTokenInfo instance) => + { + 'gid': instance.gid, + }; diff --git a/lib/auth.dart b/lib/auth.dart index 7da46d4..28ec8ea 100644 --- a/lib/auth.dart +++ b/lib/auth.dart @@ -15,6 +15,8 @@ class AuthInfo { ServerStatus? get status => _status; Token? _token; Token? get token => _token; + SharedToken? _sharedToken; + SharedToken? get sharedToken => _sharedToken; bool get isAuthed => (_user != null); bool _checked = false; bool get checked => _checked; @@ -81,6 +83,21 @@ class AuthInfo { Future checkAuth() async { _isChecking = true; + if (shareToken != null) { + try { + final re = await api.getSharedToken(); + if (re.ok) { + _sharedToken = re.unwrap(); + _checked = true; + await getServerStatus(); + return true; + } else { + shareToken = null; + } + } catch (e) { + _log.warning("Failed to check shareToken.", e); + } + } try { final re = await api.getUser(); if (re.ok) { diff --git a/lib/globals.dart b/lib/globals.dart index c07e547..571fc22 100644 --- a/lib/globals.dart +++ b/lib/globals.dart @@ -35,12 +35,26 @@ final dio = Dio() ..options.validateStatus = (int? _) { return true; } - ..options.extra['withCredentials'] = true; + ..options.extra['withCredentials'] = true + ..interceptors.add(_TokenInterceptor()); Config? _prefs; EHApi? _api; PersistCookieJar? _jar; ImageCaches? _imageCaches; String? queryBaseUrl; +String? shareToken; + +class _TokenInterceptor extends Interceptor { + @override + void onRequest(RequestOptions options, RequestInterceptorHandler handler) { + if (shareToken != null && + _api != null && + options.uri.toString().startsWith(_api!.baseUrl!)) { + options.headers["X-Token"] = shareToken; + } + super.onRequest(options, handler); + } +} Future prepareJar() async { final jar = PersistCookieJar(storage: FileStorage(await getJarPath())); @@ -86,6 +100,9 @@ bool get isImageCacheEnabled => prefs.getBool("enableImageCache") ?? true; void initApi(String baseUrl) { _api = EHApi(dio, baseUrl: baseUrl); + if (shareToken != null) { + dio.options.extra['withCredentials'] = false; + } } bool tryInitApi(BuildContext context) { diff --git a/lib/main.dart b/lib/main.dart index ad89bbb..a85d16b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -44,6 +44,7 @@ class _NavigatorObserver extends NavigatorObserver { try { final args = route.settings.arguments as Map?; queryBaseUrl = args?["base"]; + shareToken = args?["share"]; } catch (e) { _routerLog.warning("Failed to get arguments.", e); }