mirror of
https://github.com/lifegpc/eh_downloader_flutter.git
synced 2026-07-08 01:30:28 +08:00
Update
This commit is contained in:
@@ -1,19 +1,75 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'package:cryptography/cryptography.dart';
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:retrofit/retrofit.dart';
|
import 'package:retrofit/retrofit.dart';
|
||||||
import 'api_result.dart';
|
import 'api_result.dart';
|
||||||
|
import 'status.dart';
|
||||||
|
import 'token.dart';
|
||||||
import 'user.dart';
|
import 'user.dart';
|
||||||
|
|
||||||
part 'client.g.dart';
|
part 'client.g.dart';
|
||||||
|
|
||||||
@RestApi(parser: Parser.FlutterCompute)
|
final _pbkdf2a = Pbkdf2(
|
||||||
|
macAlgorithm: Hmac.sha512(),
|
||||||
|
iterations: 210000,
|
||||||
|
bits: 512,
|
||||||
|
);
|
||||||
|
|
||||||
|
final _pbkdf2b = Pbkdf2(
|
||||||
|
macAlgorithm: Hmac.sha512(),
|
||||||
|
iterations: 1000,
|
||||||
|
bits: 512,
|
||||||
|
);
|
||||||
|
|
||||||
|
const _utf8Encoder = Utf8Encoder();
|
||||||
|
final _salt = _utf8Encoder.convert("eh-downloader-salt");
|
||||||
|
|
||||||
|
@RestApi()
|
||||||
abstract class EHApi {
|
abstract class EHApi {
|
||||||
factory EHApi(Dio dio, {String baseUrl}) = _EHApi;
|
factory EHApi(Dio dio, {String baseUrl}) = _EHApi;
|
||||||
|
|
||||||
|
@PUT('/user')
|
||||||
|
Future<ApiResult<int>> createUser(
|
||||||
|
@Query("name") String name, @Query("password") String password,
|
||||||
|
{@Query("is_admin") bool? isAdmin,
|
||||||
|
@Query("permissions") int? permissions});
|
||||||
@GET('/user')
|
@GET('/user')
|
||||||
Future<ApiResult<BUser>> getUser();
|
Future<ApiResult<BUser>> getUser(
|
||||||
@GET('/user')
|
{@Query("id") int? id, @Query("username") String? username});
|
||||||
Future<ApiResult<BUser>> getUserById(@Query("id") int id);
|
|
||||||
@GET('/user')
|
@GET('/status')
|
||||||
Future<ApiResult<BUser>> getUserByUsername(
|
Future<ApiResult<ServerStatus>> getStatus();
|
||||||
@Query("username") String username);
|
|
||||||
|
@PUT('/token')
|
||||||
|
Future<ApiResult<Token>> _createToken(
|
||||||
|
{@Query("username") String username,
|
||||||
|
@Query("password") String password,
|
||||||
|
@Query("t") int t,
|
||||||
|
@Query("set_cookie") bool? setCookie,
|
||||||
|
@Query("http_only") bool? httpOnly,
|
||||||
|
@Query("secure") bool? secure});
|
||||||
|
Future<ApiResult<Token>> createToken(
|
||||||
|
{required String username,
|
||||||
|
required String password,
|
||||||
|
bool? setCookie,
|
||||||
|
bool? httpOnly,
|
||||||
|
bool? secure}) async {
|
||||||
|
int t = DateTime.now().millisecondsSinceEpoch;
|
||||||
|
final p =
|
||||||
|
await _pbkdf2a.deriveKeyFromPassword(password: password, nonce: _salt);
|
||||||
|
final p2 = await _pbkdf2b.deriveKey(
|
||||||
|
secretKey: p, nonce: _utf8Encoder.convert(t.toString()));
|
||||||
|
final p3 = base64Encode(await p2.extractBytes());
|
||||||
|
return await _createToken(
|
||||||
|
username: username,
|
||||||
|
password: p3,
|
||||||
|
t: t,
|
||||||
|
setCookie: setCookie,
|
||||||
|
httpOnly: httpOnly,
|
||||||
|
secure: secure);
|
||||||
|
}
|
||||||
|
@DELETE('/token')
|
||||||
|
Future<ApiResult<bool>> deleteToken({@Query("token") String? token});
|
||||||
|
@GET('/token')
|
||||||
|
Future<ApiResult<Token>> getToken({@Query("token") String? token});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,20 +19,98 @@ class _EHApi implements EHApi {
|
|||||||
String? baseUrl;
|
String? baseUrl;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<ApiResult<BUser>> getUser() async {
|
Future<ApiResult<int>> createUser(
|
||||||
|
String name,
|
||||||
|
String password, {
|
||||||
|
bool? isAdmin,
|
||||||
|
int? permissions,
|
||||||
|
}) async {
|
||||||
|
const _extra = <String, dynamic>{};
|
||||||
|
final queryParameters = <String, dynamic>{
|
||||||
|
r'name': name,
|
||||||
|
r'password': password,
|
||||||
|
r'is_admin': isAdmin,
|
||||||
|
r'permissions': permissions,
|
||||||
|
};
|
||||||
|
queryParameters.removeWhere((k, v) => v == null);
|
||||||
|
final _headers = <String, dynamic>{};
|
||||||
|
final Map<String, dynamic>? _data = null;
|
||||||
|
final _result = await _dio
|
||||||
|
.fetch<Map<String, dynamic>>(_setStreamType<ApiResult<int>>(Options(
|
||||||
|
method: 'PUT',
|
||||||
|
headers: _headers,
|
||||||
|
extra: _extra,
|
||||||
|
)
|
||||||
|
.compose(
|
||||||
|
_dio.options,
|
||||||
|
'/user',
|
||||||
|
queryParameters: queryParameters,
|
||||||
|
data: _data,
|
||||||
|
)
|
||||||
|
.copyWith(
|
||||||
|
baseUrl: _combineBaseUrls(
|
||||||
|
_dio.options.baseUrl,
|
||||||
|
baseUrl,
|
||||||
|
))));
|
||||||
|
final value = ApiResult<int>.fromJson(
|
||||||
|
_result.data!,
|
||||||
|
(json) => json as int,
|
||||||
|
);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<ApiResult<BUser>> getUser({
|
||||||
|
int? id,
|
||||||
|
String? username,
|
||||||
|
}) async {
|
||||||
|
const _extra = <String, dynamic>{};
|
||||||
|
final queryParameters = <String, dynamic>{
|
||||||
|
r'id': id,
|
||||||
|
r'username': username,
|
||||||
|
};
|
||||||
|
queryParameters.removeWhere((k, v) => v == null);
|
||||||
|
final _headers = <String, dynamic>{};
|
||||||
|
final Map<String, dynamic>? _data = null;
|
||||||
|
final _result = await _dio
|
||||||
|
.fetch<Map<String, dynamic>>(_setStreamType<ApiResult<BUser>>(Options(
|
||||||
|
method: 'GET',
|
||||||
|
headers: _headers,
|
||||||
|
extra: _extra,
|
||||||
|
)
|
||||||
|
.compose(
|
||||||
|
_dio.options,
|
||||||
|
'/user',
|
||||||
|
queryParameters: queryParameters,
|
||||||
|
data: _data,
|
||||||
|
)
|
||||||
|
.copyWith(
|
||||||
|
baseUrl: _combineBaseUrls(
|
||||||
|
_dio.options.baseUrl,
|
||||||
|
baseUrl,
|
||||||
|
))));
|
||||||
|
final value = ApiResult<BUser>.fromJson(
|
||||||
|
_result.data!,
|
||||||
|
(json) => BUser.fromJson(json as Map<String, dynamic>),
|
||||||
|
);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<ApiResult<ServerStatus>> getStatus() async {
|
||||||
const _extra = <String, dynamic>{};
|
const _extra = <String, dynamic>{};
|
||||||
final queryParameters = <String, dynamic>{};
|
final queryParameters = <String, dynamic>{};
|
||||||
final _headers = <String, dynamic>{};
|
final _headers = <String, dynamic>{};
|
||||||
final Map<String, dynamic>? _data = null;
|
final Map<String, dynamic>? _data = null;
|
||||||
final _result = await _dio
|
final _result = await _dio.fetch<Map<String, dynamic>>(
|
||||||
.fetch<Map<String, dynamic>>(_setStreamType<ApiResult<BUser>>(Options(
|
_setStreamType<ApiResult<ServerStatus>>(Options(
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: _headers,
|
headers: _headers,
|
||||||
extra: _extra,
|
extra: _extra,
|
||||||
)
|
)
|
||||||
.compose(
|
.compose(
|
||||||
_dio.options,
|
_dio.options,
|
||||||
'/user',
|
'/status',
|
||||||
queryParameters: queryParameters,
|
queryParameters: queryParameters,
|
||||||
data: _data,
|
data: _data,
|
||||||
)
|
)
|
||||||
@@ -41,25 +119,43 @@ class _EHApi implements EHApi {
|
|||||||
_dio.options.baseUrl,
|
_dio.options.baseUrl,
|
||||||
baseUrl,
|
baseUrl,
|
||||||
))));
|
))));
|
||||||
final value = await compute(deserializeApiResult<BUser>, _result.data!);
|
final value = ApiResult<ServerStatus>.fromJson(
|
||||||
|
_result.data!,
|
||||||
|
(json) => ServerStatus.fromJson(json as Map<String, dynamic>),
|
||||||
|
);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<ApiResult<BUser>> getUserById(int id) async {
|
Future<ApiResult<Token>> _createToken({
|
||||||
|
required String username,
|
||||||
|
required String password,
|
||||||
|
required int t,
|
||||||
|
bool? setCookie,
|
||||||
|
bool? httpOnly,
|
||||||
|
bool? secure,
|
||||||
|
}) async {
|
||||||
const _extra = <String, dynamic>{};
|
const _extra = <String, dynamic>{};
|
||||||
final queryParameters = <String, dynamic>{r'id': id};
|
final queryParameters = <String, dynamic>{
|
||||||
|
r'username': username,
|
||||||
|
r'password': password,
|
||||||
|
r't': t,
|
||||||
|
r'set_cookie': setCookie,
|
||||||
|
r'http_only': httpOnly,
|
||||||
|
r'secure': secure,
|
||||||
|
};
|
||||||
|
queryParameters.removeWhere((k, v) => v == null);
|
||||||
final _headers = <String, dynamic>{};
|
final _headers = <String, dynamic>{};
|
||||||
final Map<String, dynamic>? _data = null;
|
final Map<String, dynamic>? _data = null;
|
||||||
final _result = await _dio
|
final _result = await _dio
|
||||||
.fetch<Map<String, dynamic>>(_setStreamType<ApiResult<BUser>>(Options(
|
.fetch<Map<String, dynamic>>(_setStreamType<ApiResult<Token>>(Options(
|
||||||
method: 'GET',
|
method: 'PUT',
|
||||||
headers: _headers,
|
headers: _headers,
|
||||||
extra: _extra,
|
extra: _extra,
|
||||||
)
|
)
|
||||||
.compose(
|
.compose(
|
||||||
_dio.options,
|
_dio.options,
|
||||||
'/user',
|
'/token',
|
||||||
queryParameters: queryParameters,
|
queryParameters: queryParameters,
|
||||||
data: _data,
|
data: _data,
|
||||||
)
|
)
|
||||||
@@ -68,25 +164,29 @@ class _EHApi implements EHApi {
|
|||||||
_dio.options.baseUrl,
|
_dio.options.baseUrl,
|
||||||
baseUrl,
|
baseUrl,
|
||||||
))));
|
))));
|
||||||
final value = await compute(deserializeApiResult<BUser>, _result.data!);
|
final value = ApiResult<Token>.fromJson(
|
||||||
|
_result.data!,
|
||||||
|
(json) => Token.fromJson(json as Map<String, dynamic>),
|
||||||
|
);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<ApiResult<BUser>> getUserByUsername(String username) async {
|
Future<ApiResult<bool>> deleteToken({String? token}) async {
|
||||||
const _extra = <String, dynamic>{};
|
const _extra = <String, dynamic>{};
|
||||||
final queryParameters = <String, dynamic>{r'username': username};
|
final queryParameters = <String, dynamic>{r'token': token};
|
||||||
|
queryParameters.removeWhere((k, v) => v == null);
|
||||||
final _headers = <String, dynamic>{};
|
final _headers = <String, dynamic>{};
|
||||||
final Map<String, dynamic>? _data = null;
|
final Map<String, dynamic>? _data = null;
|
||||||
final _result = await _dio
|
final _result = await _dio
|
||||||
.fetch<Map<String, dynamic>>(_setStreamType<ApiResult<BUser>>(Options(
|
.fetch<Map<String, dynamic>>(_setStreamType<ApiResult<bool>>(Options(
|
||||||
method: 'GET',
|
method: 'DELETE',
|
||||||
headers: _headers,
|
headers: _headers,
|
||||||
extra: _extra,
|
extra: _extra,
|
||||||
)
|
)
|
||||||
.compose(
|
.compose(
|
||||||
_dio.options,
|
_dio.options,
|
||||||
'/user',
|
'/token',
|
||||||
queryParameters: queryParameters,
|
queryParameters: queryParameters,
|
||||||
data: _data,
|
data: _data,
|
||||||
)
|
)
|
||||||
@@ -95,7 +195,41 @@ class _EHApi implements EHApi {
|
|||||||
_dio.options.baseUrl,
|
_dio.options.baseUrl,
|
||||||
baseUrl,
|
baseUrl,
|
||||||
))));
|
))));
|
||||||
final value = await compute(deserializeApiResult<BUser>, _result.data!);
|
final value = ApiResult<bool>.fromJson(
|
||||||
|
_result.data!,
|
||||||
|
(json) => json as bool,
|
||||||
|
);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<ApiResult<Token>> getToken({String? token}) async {
|
||||||
|
const _extra = <String, dynamic>{};
|
||||||
|
final queryParameters = <String, dynamic>{r'token': token};
|
||||||
|
queryParameters.removeWhere((k, v) => v == null);
|
||||||
|
final _headers = <String, dynamic>{};
|
||||||
|
final Map<String, dynamic>? _data = null;
|
||||||
|
final _result = await _dio
|
||||||
|
.fetch<Map<String, dynamic>>(_setStreamType<ApiResult<Token>>(Options(
|
||||||
|
method: 'GET',
|
||||||
|
headers: _headers,
|
||||||
|
extra: _extra,
|
||||||
|
)
|
||||||
|
.compose(
|
||||||
|
_dio.options,
|
||||||
|
'/token',
|
||||||
|
queryParameters: queryParameters,
|
||||||
|
data: _data,
|
||||||
|
)
|
||||||
|
.copyWith(
|
||||||
|
baseUrl: _combineBaseUrls(
|
||||||
|
_dio.options.baseUrl,
|
||||||
|
baseUrl,
|
||||||
|
))));
|
||||||
|
final value = ApiResult<Token>.fromJson(
|
||||||
|
_result.data!,
|
||||||
|
(json) => Token.fromJson(json as Map<String, dynamic>),
|
||||||
|
);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
37
lib/api/status.dart
Normal file
37
lib/api/status.dart
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
|
part 'status.g.dart';
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class MeilisearchInfo {
|
||||||
|
const MeilisearchInfo({
|
||||||
|
required this.host,
|
||||||
|
required this.key,
|
||||||
|
});
|
||||||
|
final String host;
|
||||||
|
final String key;
|
||||||
|
factory MeilisearchInfo.fromJson(Map<String, dynamic> json) => _$MeilisearchInfoFromJson(json);
|
||||||
|
Map<String, dynamic> toJson() => _$MeilisearchInfoToJson(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class ServerStatus {
|
||||||
|
const ServerStatus({
|
||||||
|
required this.ffmpegApiEnabled,
|
||||||
|
required this.ffmpegBinaryEnabled,
|
||||||
|
required this.meilisearchEnabled,
|
||||||
|
this.meilisearch,
|
||||||
|
required this.noUser,
|
||||||
|
});
|
||||||
|
@JsonKey(name: 'ffmpeg_api_enabled')
|
||||||
|
final bool ffmpegApiEnabled;
|
||||||
|
@JsonKey(name: 'ffmpeg_binary_enabled')
|
||||||
|
final bool ffmpegBinaryEnabled;
|
||||||
|
@JsonKey(name: 'meilisearch_enabled')
|
||||||
|
final bool meilisearchEnabled;
|
||||||
|
final MeilisearchInfo? meilisearch;
|
||||||
|
@JsonKey(name: 'no_user')
|
||||||
|
final bool noUser;
|
||||||
|
factory ServerStatus.fromJson(Map<String, dynamic> json) => _$ServerStatusFromJson(json);
|
||||||
|
Map<String, dynamic> toJson() => _$ServerStatusToJson(this);
|
||||||
|
}
|
||||||
39
lib/api/status.g.dart
Normal file
39
lib/api/status.g.dart
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'status.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
MeilisearchInfo _$MeilisearchInfoFromJson(Map<String, dynamic> json) =>
|
||||||
|
MeilisearchInfo(
|
||||||
|
host: json['host'] as String,
|
||||||
|
key: json['key'] as String,
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$MeilisearchInfoToJson(MeilisearchInfo instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'host': instance.host,
|
||||||
|
'key': instance.key,
|
||||||
|
};
|
||||||
|
|
||||||
|
ServerStatus _$ServerStatusFromJson(Map<String, dynamic> json) => ServerStatus(
|
||||||
|
ffmpegApiEnabled: json['ffmpeg_api_enabled'] as bool,
|
||||||
|
ffmpegBinaryEnabled: json['ffmpeg_binary_enabled'] as bool,
|
||||||
|
meilisearchEnabled: json['meilisearch_enabled'] as bool,
|
||||||
|
meilisearch: json['meilisearch'] == null
|
||||||
|
? null
|
||||||
|
: MeilisearchInfo.fromJson(
|
||||||
|
json['meilisearch'] as Map<String, dynamic>),
|
||||||
|
noUser: json['no_user'] as bool,
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$ServerStatusToJson(ServerStatus instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'ffmpeg_api_enabled': instance.ffmpegApiEnabled,
|
||||||
|
'ffmpeg_binary_enabled': instance.ffmpegBinaryEnabled,
|
||||||
|
'meilisearch_enabled': instance.meilisearchEnabled,
|
||||||
|
'meilisearch': instance.meilisearch,
|
||||||
|
'no_user': instance.noUser,
|
||||||
|
};
|
||||||
27
lib/api/token.dart
Normal file
27
lib/api/token.dart
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
|
part 'token.g.dart';
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class Token {
|
||||||
|
const Token ({
|
||||||
|
required this.id,
|
||||||
|
required this.uid,
|
||||||
|
required this.token,
|
||||||
|
required this.expired,
|
||||||
|
required this.httpOnly,
|
||||||
|
required this.secure,
|
||||||
|
});
|
||||||
|
final int id;
|
||||||
|
final int uid;
|
||||||
|
final String token;
|
||||||
|
@JsonKey(fromJson: _fromJson, toJson: _toJson)
|
||||||
|
final DateTime expired;
|
||||||
|
@JsonKey(name: 'http_only')
|
||||||
|
final bool httpOnly;
|
||||||
|
final bool secure;
|
||||||
|
static DateTime _fromJson(String d) => DateTime.parse(d);
|
||||||
|
static String _toJson(DateTime d) => d.toIso8601String();
|
||||||
|
factory Token.fromJson(Map<String, dynamic> json) => _$TokenFromJson(json);
|
||||||
|
Map<String, dynamic> toJson() => _$TokenToJson(this);
|
||||||
|
}
|
||||||
25
lib/api/token.g.dart
Normal file
25
lib/api/token.g.dart
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'token.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
Token _$TokenFromJson(Map<String, dynamic> json) => Token(
|
||||||
|
id: json['id'] as int,
|
||||||
|
uid: json['uid'] as int,
|
||||||
|
token: json['token'] as String,
|
||||||
|
expired: Token._fromJson(json['expired'] as String),
|
||||||
|
httpOnly: json['http_only'] as bool,
|
||||||
|
secure: json['secure'] as bool,
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$TokenToJson(Token instance) => <String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'uid': instance.uid,
|
||||||
|
'token': instance.token,
|
||||||
|
'expired': Token._toJson(instance.expired),
|
||||||
|
'http_only': instance.httpOnly,
|
||||||
|
'secure': instance.secure,
|
||||||
|
};
|
||||||
@@ -18,12 +18,13 @@ class BUser {
|
|||||||
const BUser({
|
const BUser({
|
||||||
required this.id,
|
required this.id,
|
||||||
required this.username,
|
required this.username,
|
||||||
required this.is_admin,
|
required this.isAdmin,
|
||||||
required this.permissions,
|
required this.permissions,
|
||||||
});
|
});
|
||||||
final int id;
|
final int id;
|
||||||
final String username;
|
final String username;
|
||||||
final bool is_admin;
|
@JsonKey(name: 'is_admin')
|
||||||
|
final bool isAdmin;
|
||||||
final UserPermission permissions;
|
final UserPermission permissions;
|
||||||
factory BUser.fromJson(Map<String, dynamic> json) => _$BUserFromJson(json);
|
factory BUser.fromJson(Map<String, dynamic> json) => _$BUserFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$BUserToJson(this);
|
Map<String, dynamic> toJson() => _$BUserToJson(this);
|
||||||
|
|||||||
@@ -9,14 +9,14 @@ part of 'user.dart';
|
|||||||
BUser _$BUserFromJson(Map<String, dynamic> json) => BUser(
|
BUser _$BUserFromJson(Map<String, dynamic> json) => BUser(
|
||||||
id: json['id'] as int,
|
id: json['id'] as int,
|
||||||
username: json['username'] as String,
|
username: json['username'] as String,
|
||||||
is_admin: json['is_admin'] as bool,
|
isAdmin: json['is_admin'] as bool,
|
||||||
permissions: $enumDecode(_$UserPermissionEnumMap, json['permissions']),
|
permissions: $enumDecode(_$UserPermissionEnumMap, json['permissions']),
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> _$BUserToJson(BUser instance) => <String, dynamic>{
|
Map<String, dynamic> _$BUserToJson(BUser instance) => <String, dynamic>{
|
||||||
'id': instance.id,
|
'id': instance.id,
|
||||||
'username': instance.username,
|
'username': instance.username,
|
||||||
'is_admin': instance.is_admin,
|
'is_admin': instance.isAdmin,
|
||||||
'permissions': _$UserPermissionEnumMap[instance.permissions]!,
|
'permissions': _$UserPermissionEnumMap[instance.permissions]!,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,11 @@ import 'package:dio/dio.dart';
|
|||||||
import 'package:dio_cookie_manager/dio_cookie_manager.dart';
|
import 'package:dio_cookie_manager/dio_cookie_manager.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
import 'api/client.dart';
|
||||||
|
|
||||||
final dio = Dio();
|
final dio = Dio()..options.validateStatus = (int? _) {return true;};
|
||||||
SharedPreferences? _prefs;
|
SharedPreferences? _prefs;
|
||||||
|
EHApi? _api;
|
||||||
|
|
||||||
Future<void> prepareJar() async {
|
Future<void> prepareJar() async {
|
||||||
final Directory appDocDir = await getApplicationDocumentsDirectory();
|
final Directory appDocDir = await getApplicationDocumentsDirectory();
|
||||||
@@ -27,3 +29,18 @@ SharedPreferences get prefs {
|
|||||||
}
|
}
|
||||||
return _prefs!;
|
return _prefs!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void initApi(String baseUrl) {
|
||||||
|
_api = EHApi(dio, baseUrl: baseUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get apiInited {
|
||||||
|
return _api != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
EHApi get api {
|
||||||
|
if (_api == null) {
|
||||||
|
throw Exception('EHApi not initialized');
|
||||||
|
}
|
||||||
|
return _api!;
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ class HomePage extends HookWidget {
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
initApi(baseUrl);
|
||||||
return;
|
return;
|
||||||
}, []);
|
}, []);
|
||||||
return const Scaffold(
|
return const Scaffold(
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'globals.dart';
|
||||||
|
|
||||||
class SetServerPage extends StatefulWidget {
|
class SetServerPage extends StatefulWidget {
|
||||||
const SetServerPage({Key? key}) : super(key: key);
|
const SetServerPage({Key? key}) : super(key: key);
|
||||||
@@ -10,29 +12,92 @@ class SetServerPage extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _SetServerPageState extends State<SetServerPage> {
|
class _SetServerPageState extends State<SetServerPage> {
|
||||||
|
String _serverUrl = "";
|
||||||
|
String _apiPath = "/api/";
|
||||||
|
bool _isValid = false;
|
||||||
|
final _formKey = GlobalKey<FormState>();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
String? baseUrl = prefs.getString('baseUrl');
|
||||||
|
if (baseUrl != null) {
|
||||||
|
try {
|
||||||
|
Uri url = Uri.parse(baseUrl);
|
||||||
|
_serverUrl = url.origin;
|
||||||
|
_apiPath = url.path;
|
||||||
|
_isValid = true;
|
||||||
|
} catch (e) {
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool _checkIsValid(String serverUrl, String apiPath) {
|
||||||
|
try {
|
||||||
|
Uri url = Uri.parse(serverUrl + apiPath);
|
||||||
|
return url.isAbsolute;
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _serverUrlChanged(String value) {
|
||||||
|
bool isValid = _checkIsValid(value, _apiPath);
|
||||||
|
setState(() {
|
||||||
|
_serverUrl = value;
|
||||||
|
_isValid = isValid;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _apiPathChanged(String value) {
|
||||||
|
bool isValid = _checkIsValid(_serverUrl, value);
|
||||||
|
setState(() {
|
||||||
|
_apiPath = value;
|
||||||
|
_isValid = isValid;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: Center(
|
body: Container(
|
||||||
child: Column(
|
padding: const EdgeInsets.symmetric(horizontal: 100),
|
||||||
children: [
|
child: Form(
|
||||||
const Text('EH Downloader server url:'),
|
key: _formKey,
|
||||||
const TextField(
|
child: Column(
|
||||||
decoration: InputDecoration(
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
border: OutlineInputBorder(),
|
children: [
|
||||||
labelText: 'Server URL',
|
Container(
|
||||||
),
|
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||||
),
|
child: TextFormField(
|
||||||
const Text('API path'),
|
decoration: const InputDecoration(
|
||||||
TextField(
|
border: OutlineInputBorder(),
|
||||||
decoration: const InputDecoration(
|
labelText: 'EH Downloader Server URL',
|
||||||
border: OutlineInputBorder(),
|
),
|
||||||
labelText: 'API Path',
|
initialValue: _serverUrl,
|
||||||
),
|
onChanged: _serverUrlChanged,
|
||||||
controller: TextEditingController(text: "/api"),
|
)),
|
||||||
),
|
Container(
|
||||||
],
|
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||||
)),
|
child: TextFormField(
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
labelText: 'API Path',
|
||||||
|
),
|
||||||
|
initialValue: _apiPath,
|
||||||
|
onChanged: _apiPathChanged,
|
||||||
|
)),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: _isValid
|
||||||
|
? () {
|
||||||
|
prefs.setString('baseUrl', _serverUrl + _apiPath);
|
||||||
|
context.go('/');
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
child: const Text("Save")),
|
||||||
|
],
|
||||||
|
))),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,10 +5,12 @@
|
|||||||
import FlutterMacOS
|
import FlutterMacOS
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
import cryptography_flutter
|
||||||
import path_provider_foundation
|
import path_provider_foundation
|
||||||
import shared_preferences_foundation
|
import shared_preferences_foundation
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
|
CryptographyFlutterPlugin.register(with: registry.registrar(forPlugin: "CryptographyFlutterPlugin"))
|
||||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||||
}
|
}
|
||||||
|
|||||||
16
pubspec.lock
16
pubspec.lock
@@ -169,6 +169,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.3"
|
version: "3.0.3"
|
||||||
|
cryptography:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: cryptography
|
||||||
|
sha256: df156c5109286340817d21fa7b62f9140f17915077127dd70f8bd7a2a0997a35
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.5.0"
|
||||||
|
cryptography_flutter:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: cryptography_flutter
|
||||||
|
sha256: a66ce021e0b600688c2d51b0594cb156ee677ce9bfbc981b62219ad577dd302e
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.0"
|
||||||
dart_style:
|
dart_style:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ environment:
|
|||||||
sdk: '>=3.0.5 <4.0.0'
|
sdk: '>=3.0.5 <4.0.0'
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
|
cryptography: ^2.5.0
|
||||||
|
cryptography_flutter: ^2.3.0
|
||||||
dio: ^5.3.2
|
dio: ^5.3.2
|
||||||
dio_cookie_manager: ^3.1.0+1
|
dio_cookie_manager: ^3.1.0+1
|
||||||
flutter:
|
flutter:
|
||||||
|
|||||||
Reference in New Issue
Block a user