fix: handle login

This commit is contained in:
shenlong-tanwen
2024-08-25 10:38:24 +05:30
parent 7f83740b35
commit 877c3b028b
27 changed files with 430 additions and 355 deletions
@@ -1,11 +1,11 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/domain/models/asset.model.dart';
class LocalAsset extends Table {
const LocalAsset();
class Asset extends Table {
const Asset();
IntColumn get id => integer().autoIncrement()();
TextColumn get localId => text().unique()();
TextColumn get name => text()();
TextColumn get checksum => text().unique()();
IntColumn get height => integer()();
@@ -0,0 +1,8 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/domain/entities/asset.entity.dart';
class LocalAsset extends Asset {
const LocalAsset();
TextColumn get localId => text().unique()();
}
@@ -0,0 +1,8 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/domain/entities/asset.entity.dart';
class RemoteAsset extends Asset {
const RemoteAsset();
TextColumn get remoteId => text().unique()();
}
+39 -17
View File
@@ -1,5 +1,3 @@
import 'package:flutter/foundation.dart';
enum AssetType {
// do not change this order!
other,
@@ -8,10 +6,8 @@ enum AssetType {
audio,
}
@immutable
class LocalAsset {
class Asset {
final int id;
final String localId;
final String name;
final String checksum;
final int height;
@@ -22,9 +18,8 @@ class LocalAsset {
final int duration;
final bool isLivePhoto;
const LocalAsset({
const Asset({
required this.id,
required this.localId,
required this.name,
required this.checksum,
required this.height,
@@ -36,16 +31,36 @@ class LocalAsset {
required this.isLivePhoto,
});
@override
String toString() {
return 'LocalAsset(id: $id, localId: $localId, name: $name, checksum: $checksum, height: $height, width: $width, type: $type, createdTime: $createdTime, modifiedTime: $modifiedTime, duration: $duration, isLivePhoto: $isLivePhoto)';
Asset copyWith({
int? id,
String? name,
String? checksum,
int? height,
int? width,
AssetType? type,
DateTime? createdTime,
DateTime? modifiedTime,
int? duration,
bool? isLivePhoto,
}) {
return Asset(
id: id ?? this.id,
name: name ?? this.name,
checksum: checksum ?? this.checksum,
height: height ?? this.height,
width: width ?? this.width,
type: type ?? this.type,
createdTime: createdTime ?? this.createdTime,
modifiedTime: modifiedTime ?? this.modifiedTime,
duration: duration ?? this.duration,
isLivePhoto: isLivePhoto ?? this.isLivePhoto,
);
}
String toJSON() {
return """
@override
String toString() => """
{
"id": $id,
"localId": "$localId",
"name": "$name",
"checksum": "$checksum",
"height": $height,
@@ -56,19 +71,26 @@ class LocalAsset {
"duration": "$duration",
"isLivePhoto": "$isLivePhoto",
}""";
}
@override
bool operator ==(covariant LocalAsset other) {
bool operator ==(covariant Asset other) {
if (identical(this, other)) return true;
return other.hashCode == hashCode;
return other.id == id &&
other.name == name &&
other.checksum == checksum &&
other.height == height &&
other.width == width &&
other.type == type &&
other.createdTime == createdTime &&
other.modifiedTime == modifiedTime &&
other.duration == duration &&
other.isLivePhoto == isLivePhoto;
}
@override
int get hashCode {
return id.hashCode ^
localId.hashCode ^
name.hashCode ^
checksum.hashCode ^
height.hashCode ^
@@ -0,0 +1,76 @@
import 'package:flutter/foundation.dart';
import 'package:immich_mobile/domain/models/asset.model.dart';
@immutable
class LocalAsset extends Asset {
final String localId;
const LocalAsset({
required this.localId,
required super.id,
required super.name,
required super.checksum,
required super.height,
required super.width,
required super.type,
required super.createdTime,
required super.modifiedTime,
required super.duration,
required super.isLivePhoto,
});
@override
String toString() => """
{
"id": $id,
"localId": "$localId",
"name": "$name",
"checksum": "$checksum",
"height": $height,
"width": $width,
"type": "$type",
"createdTime": "$createdTime",
"modifiedTime": "$modifiedTime",
"duration": "$duration",
"isLivePhoto": "$isLivePhoto",
}""";
@override
bool operator ==(covariant LocalAsset other) {
if (identical(this, other)) return true;
return super == (other) && other.localId == localId;
}
@override
int get hashCode => super.hashCode ^ localId.hashCode;
@override
LocalAsset copyWith({
int? id,
String? localId,
String? name,
String? checksum,
int? height,
int? width,
AssetType? type,
DateTime? createdTime,
DateTime? modifiedTime,
int? duration,
bool? isLivePhoto,
}) {
return LocalAsset(
id: id ?? this.id,
localId: localId ?? this.localId,
name: name ?? this.name,
checksum: checksum ?? this.checksum,
height: height ?? this.height,
width: width ?? this.width,
type: type ?? this.type,
createdTime: createdTime ?? this.createdTime,
modifiedTime: modifiedTime ?? this.modifiedTime,
duration: duration ?? this.duration,
isLivePhoto: isLivePhoto ?? this.isLivePhoto,
);
}
}
@@ -0,0 +1,76 @@
import 'package:flutter/foundation.dart';
import 'package:immich_mobile/domain/models/asset.model.dart';
@immutable
class RemoteAsset extends Asset {
final String remoteId;
const RemoteAsset({
required this.remoteId,
required super.id,
required super.name,
required super.checksum,
required super.height,
required super.width,
required super.type,
required super.createdTime,
required super.modifiedTime,
required super.duration,
required super.isLivePhoto,
});
@override
String toString() => """
{
"id": $id,
"remoteId": "$remoteId",
"name": "$name",
"checksum": "$checksum",
"height": $height,
"width": $width,
"type": "$type",
"createdTime": "$createdTime",
"modifiedTime": "$modifiedTime",
"duration": "$duration",
"isLivePhoto": "$isLivePhoto",
}""";
@override
bool operator ==(covariant RemoteAsset other) {
if (identical(this, other)) return true;
return super == (other) && other.remoteId == remoteId;
}
@override
int get hashCode => super.hashCode ^ remoteId.hashCode;
@override
RemoteAsset copyWith({
int? id,
String? remoteId,
String? name,
String? checksum,
int? height,
int? width,
AssetType? type,
DateTime? createdTime,
DateTime? modifiedTime,
int? duration,
bool? isLivePhoto,
}) {
return RemoteAsset(
id: id ?? this.id,
remoteId: remoteId ?? this.remoteId,
name: name ?? this.name,
checksum: checksum ?? this.checksum,
height: height ?? this.height,
width: width ?? this.width,
type: type ?? this.type,
createdTime: createdTime ?? this.createdTime,
modifiedTime: modifiedTime ?? this.modifiedTime,
duration: duration ?? this.duration,
isLivePhoto: isLivePhoto ?? this.isLivePhoto,
);
}
}
@@ -1,4 +1,4 @@
import 'package:openapi/openapi.dart';
import 'package:openapi/api.dart';
class ServerConfig {
final String? oauthButtonText;
@@ -1,4 +1,4 @@
import 'package:openapi/openapi.dart';
import 'package:openapi/api.dart';
class ServerFeatures {
final bool hasPasswordLogin;
+1 -1
View File
@@ -1,6 +1,6 @@
import 'dart:ui';
import 'package:openapi/openapi.dart' as api;
import 'package:openapi/api.dart' as api;
class User {
const User({
@@ -6,8 +6,9 @@ import 'package:drift/native.dart';
import 'package:drift_dev/api/migrations.dart';
import 'package:flutter/foundation.dart';
import 'package:immich_mobile/domain/entities/album.entity.dart';
import 'package:immich_mobile/domain/entities/asset.entity.dart';
import 'package:immich_mobile/domain/entities/local_asset.entity.dart';
import 'package:immich_mobile/domain/entities/log.entity.dart';
import 'package:immich_mobile/domain/entities/remote_asset.entity.dart';
import 'package:immich_mobile/domain/entities/store.entity.dart';
import 'package:immich_mobile/domain/entities/user.entity.dart';
import 'package:immich_mobile/domain/interfaces/database.interface.dart';
@@ -18,7 +19,7 @@ import 'package:sqlite3_flutter_libs/sqlite3_flutter_libs.dart';
import 'database.repository.drift.dart';
@DriftDatabase(tables: [Logs, Store, LocalAlbum, LocalAsset, User])
@DriftDatabase(tables: [Logs, Store, LocalAlbum, LocalAsset, User, RemoteAsset])
class DriftDatabaseRepository extends $DriftDatabaseRepository
implements IDatabaseRepository<GeneratedDatabase> {
DriftDatabaseRepository() : super(_openConnection());
@@ -18,32 +18,28 @@ class LogDriftRepository implements ILogRepository {
}
@override
Future<void> truncateLogs({int limit = 250}) {
return db.transaction(() async {
final totalCount = await db.managers.logs.count();
if (totalCount > limit) {
final rowsToDelete = totalCount - limit;
await db.managers.logs
.orderBy((o) => o.createdAt.desc())
.limit(rowsToDelete)
.delete();
}
});
Future<void> truncateLogs({int limit = 250}) async {
final totalCount = await db.managers.logs.count();
if (totalCount > limit) {
final rowsToDelete = totalCount - limit;
await db.managers.logs
.orderBy((o) => o.createdAt.desc())
.limit(rowsToDelete)
.delete();
}
}
@override
FutureOr<bool> add(LogMessage log) async {
try {
await db.transaction(() async {
await db.into(db.logs).insert(LogsCompanion.insert(
content: log.content,
level: log.level,
createdAt: Value(log.createdAt),
error: Value(log.error),
logger: Value(log.logger),
stack: Value(log.stack),
));
});
await db.into(db.logs).insert(LogsCompanion.insert(
content: log.content,
level: log.level,
createdAt: Value(log.createdAt),
error: Value(log.error),
logger: Value(log.logger),
stack: Value(log.stack),
));
return true;
} catch (e) {
debugPrint("Error while adding a log to the DB - $e");
@@ -32,16 +32,14 @@ class StoreDriftRepository with LogContext implements IStoreRepository {
@override
FutureOr<bool> set<T, U>(StoreKey<T, U> key, T value) async {
try {
await db.transaction(() async {
final storeValue = key.converter.toPrimitive(value);
final intValue = (key.type == int) ? storeValue as int : null;
final stringValue = (key.type == String) ? storeValue as String : null;
await db.into(db.store).insertOnConflictUpdate(StoreCompanion.insert(
id: Value(key.id),
intValue: Value(intValue),
stringValue: Value(stringValue),
));
});
final storeValue = key.converter.toPrimitive(value);
final intValue = (key.type == int) ? storeValue as int : null;
final stringValue = (key.type == String) ? storeValue as String : null;
await db.into(db.store).insertOnConflictUpdate(StoreCompanion.insert(
id: Value(key.id),
intValue: Value(intValue),
stringValue: Value(stringValue),
));
return true;
} catch (e, s) {
log.severe("Cannot set store value - ${key.name}; id - ${key.id}", e, s);
@@ -51,9 +49,7 @@ class StoreDriftRepository with LogContext implements IStoreRepository {
@override
FutureOr<void> delete(StoreKey key) async {
return await db.transaction(() async {
await db.managers.store.filter((s) => s.id.equals(key.id)).delete();
});
await db.managers.store.filter((s) => s.id.equals(key.id)).delete();
}
@override
@@ -66,9 +62,8 @@ class StoreDriftRepository with LogContext implements IStoreRepository {
@override
FutureOr<void> clearStore() async {
return await db.transaction(() async {
await db.managers.store.delete();
});
await db.managers.store.delete();
;
}
FutureOr<T?> _getValueFromStoreData<T, U>(
@@ -23,8 +23,8 @@ class UserDriftRepository with LogContext implements IUserRepository {
@override
FutureOr<bool> insertUser(User user) async {
try {
return await db.transaction(() async {
await db.into(db.user).insertOnConflictUpdate(UserCompanion.insert(
await db.into(db.user).insertOnConflictUpdate(
UserCompanion.insert(
id: user.id,
name: user.name,
email: user.email,
@@ -36,9 +36,9 @@ class UserDriftRepository with LogContext implements IUserRepository {
quotaSizeInBytes: Value(user.quotaSizeInBytes),
quotaUsageInBytes: Value(user.quotaSizeInBytes),
updatedAt: Value(user.updatedAt),
));
return true;
});
),
);
return true;
} catch (e, s) {
log.severe("Cannot insert User into table - $user", e, s);
return false;
@@ -1,26 +1,28 @@
import 'dart:convert';
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_web_auth_2/flutter_web_auth_2.dart';
import 'package:http/http.dart';
import 'package:immich_mobile/service_locator.dart';
import 'package:immich_mobile/utils/immich_api_client.dart';
import 'package:immich_mobile/utils/mixins/log_context.mixin.dart';
import 'package:openapi/openapi.dart';
import 'package:openapi/api.dart';
class LoginService with LogContext {
const LoginService();
Future<bool> isEndpointAvailable(Uri uri, {Dio? dio}) async {
Future<bool> isEndpointAvailable(Uri uri, {ImmichApiClient? client}) async {
String baseUrl = uri.toString();
if (!baseUrl.endsWith('/api')) {
baseUrl += '/api';
}
final serverAPI =
Openapi(dio: dio, basePathOverride: baseUrl, interceptors: [])
.getServerApi();
final serverAPI = client?.getServerApi() ??
ImmichApiClient(endpoint: baseUrl).getServerApi();
try {
await serverAPI.pingServer(validateStatus: (status) => status == 200);
await serverAPI.pingServer();
} catch (e) {
log.severe("Exception occured while validating endpoint", e);
return false;
@@ -28,25 +30,24 @@ class LoginService with LogContext {
return true;
}
Future<String> resolveEndpoint(Uri uri, {Dio? dio}) async {
final d = dio ?? Dio();
Future<String> resolveEndpoint(Uri uri, {Client? client}) async {
String baseUrl = uri.toString();
final d = client ?? ImmichApiClient(endpoint: baseUrl).client;
try {
// Check for well-known endpoint
final res = await d.get(
"$baseUrl/.well-known/immich",
options: Options(
headers: {"Accept": "application/json"},
validateStatus: (status) => status == 200,
),
Uri.parse("$baseUrl/.well-known/immich"),
headers: {"Accept": "application/json"},
);
final data = jsonDecode(res.data);
final endpoint = data['api']['endpoint'].toString();
if (res.statusCode == HttpStatus.ok) {
final data = await compute(jsonDecode, res.body);
final endpoint = data['api']['endpoint'].toString();
// Full URL is relative to base
return endpoint.startsWith('/') ? "$baseUrl$endpoint" : endpoint;
// Full URL is relative to base
return endpoint.startsWith('/') ? "$baseUrl$endpoint" : endpoint;
}
} catch (e) {
log.fine("Could not locate /.well-known/immich at $baseUrl", e);
}
@@ -57,14 +58,12 @@ class LoginService with LogContext {
Future<String?> passwordLogin(String email, String password) async {
try {
final loginResponse = await di<Openapi>().getAuthenticationApi().login(
loginCredentialDto: LoginCredentialDto((builder) {
builder.email = email;
builder.password = password;
}),
);
final loginResponse =
await di<ImmichApiClient>().getAuthenticationApi().login(
LoginCredentialDto(email: email, password: password),
);
return loginResponse.data?.accessToken;
return loginResponse?.accessToken;
} catch (e, s) {
log.severe("Exception occured while performing password login", e, s);
}
@@ -74,16 +73,14 @@ class LoginService with LogContext {
Future<String?> oAuthLogin() async {
const String oAuthCallbackSchema = 'app.immich';
final oAuthApi = di<Openapi>().getOAuthApi();
final oAuthApi = di<ImmichApiClient>().getOAuthApi();
try {
final oAuthUrl = await oAuthApi.startOAuth(
oAuthConfigDto: OAuthConfigDto((builder) {
builder.redirectUri = "$oAuthCallbackSchema:/";
}),
OAuthConfigDto(redirectUri: "$oAuthCallbackSchema:/"),
);
final oAuthUrlRes = oAuthUrl.data?.url;
final oAuthUrlRes = oAuthUrl?.url;
if (oAuthUrlRes == null) {
log.severe(
"oAuth Server URL not available. Kindly ensure oAuth login is enabled in the server",
@@ -97,12 +94,10 @@ class LoginService with LogContext {
);
final loginResponse = await oAuthApi.finishOAuth(
oAuthCallbackDto: OAuthCallbackDto((builder) {
builder.url = oAuthCallbackUrl;
}),
OAuthCallbackDto(url: oAuthCallbackUrl),
);
return loginResponse.data?.accessToken;
return loginResponse?.accessToken;
} catch (e) {
log.severe("Exception occured while performing oauth login", e);
}
@@ -1,10 +1,11 @@
import 'package:immich_mobile/domain/models/server-info/server_config.model.dart';
import 'package:immich_mobile/domain/models/server-info/server_features.model.dart';
import 'package:immich_mobile/utils/immich_api_client.dart';
import 'package:immich_mobile/utils/mixins/log_context.mixin.dart';
import 'package:openapi/openapi.dart';
import 'package:openapi/api.dart';
class ServerInfoService with LogContext {
final Openapi _api;
final ImmichApiClient _api;
ServerApi get _serverInfo => _api.getServerApi();
@@ -12,8 +13,7 @@ class ServerInfoService with LogContext {
Future<ServerFeatures?> getServerFeatures() async {
try {
final response = await _serverInfo.getServerFeatures();
final dto = response.data;
final dto = await _serverInfo.getServerFeatures();
if (dto != null) {
return ServerFeatures.fromDto(dto);
}
@@ -25,8 +25,7 @@ class ServerInfoService with LogContext {
Future<ServerConfig?> getServerConfig() async {
try {
final response = await _serverInfo.getServerConfig();
final dto = response.data;
final dto = await _serverInfo.getServerConfig();
if (dto != null) {
return ServerConfig.fromDto(dto);
}
@@ -1,9 +1,10 @@
import 'package:immich_mobile/domain/models/user.model.dart';
import 'package:immich_mobile/utils/immich_api_client.dart';
import 'package:immich_mobile/utils/mixins/log_context.mixin.dart';
import 'package:openapi/openapi.dart';
import 'package:openapi/api.dart';
class UserService with LogContext {
final Openapi _api;
final ImmichApiClient _api;
UsersApi get _userApi => _api.getUsersApi();
@@ -11,15 +12,14 @@ class UserService with LogContext {
Future<User?> getMyUser() async {
try {
final response = await _userApi.getMyUser();
final dto = response.data;
if (dto == null) {
final userDto = await _userApi.getMyUser();
if (userDto == null) {
log.severe("Cannot fetch my user.");
return null;
}
final preferences = await _userApi.getMyPreferences();
return User.fromAdminDto(dto, preferences.data);
final preferencesDto = await _userApi.getMyPreferences();
return User.fromAdminDto(userDto, preferencesDto);
} catch (e, s) {
log.severe("Error while fetching server features", e, s);
}