Some checks are pending
Backend CI / test (push) Waiting to run
Backend CI / build (push) Blocked by required conditions
Deploy Mnemo Cards / Deploy Backend (push) Waiting to run
Deploy Mnemo Cards / Deploy Web App (push) Blocked by required conditions
Deploy Mnemo Cards / Final Verification (push) Blocked by required conditions
161 lines
No EOL
4.6 KiB
Dart
161 lines
No EOL
4.6 KiB
Dart
import 'dart:developer';
|
|
|
|
import 'package:injectable/injectable.dart';
|
|
import 'package:drift/drift.dart' as drift;
|
|
import 'package:mnemo_cards_backend/database/database.dart';
|
|
import 'package:mnemo_cards_common_backend/mnemo_cards_common_backend.dart';
|
|
import 'package:mnemo_cards_common/mnemo_cards_common.dart';
|
|
|
|
import '../main.dart';
|
|
import '../packs/free_packs_distributor.dart';
|
|
import '../statistics/session_tracker.dart';
|
|
import '../statistics/statistics_calculator.dart';
|
|
import '../statistics/achievement_manager.dart';
|
|
import 'secure.dart';
|
|
import 'user_drift_extension.dart';
|
|
|
|
Map<int?, DateTime> _onlineUsers = {};
|
|
|
|
@lazySingleton
|
|
class UserManager {
|
|
final AppDatabase _db;
|
|
final FreePacksDistributor _freePacksDistributor;
|
|
// TODO: Re-enable when migrated to PostgreSQL
|
|
// final SessionTracker _sessionTracker;
|
|
// final StatisticsCalculator _statisticsCalculator;
|
|
// final AchievementManager _achievementManager;
|
|
|
|
UserManager(
|
|
this._db,
|
|
this._freePacksDistributor,
|
|
// this._sessionTracker,
|
|
// this._statisticsCalculator,
|
|
// this._achievementManager,
|
|
);
|
|
|
|
Future<UserModel?> fetchUser(int id) async {
|
|
final user = await _db.userDao.getUserById(id);
|
|
if (user == null) return null;
|
|
return await user.toUserModel();
|
|
}
|
|
|
|
DateTime? lastOnline(int id) => _onlineUsers[id];
|
|
|
|
Future<String> createOrGetAuthToken(UserModel user, String externalId) async {
|
|
if (user.id == null) {
|
|
throw Exception('Cant create token for empty id');
|
|
}
|
|
final now = DateTime.now();
|
|
final token = await _db.userDao.getTokenByUserId(user.id!);
|
|
if (token != null) {
|
|
if (token.expires.isAfter(now)) {
|
|
return token.token;
|
|
}
|
|
await _db.userDao.deleteToken(token.id);
|
|
}
|
|
final userToken = Secure.token();
|
|
await _db.userDao.createToken(
|
|
TokensCompanion.insert(
|
|
token: userToken,
|
|
externalUserId: externalId,
|
|
userId: user.id!,
|
|
expires: now.add(const Duration(days: 360)),
|
|
),
|
|
);
|
|
return userToken;
|
|
}
|
|
|
|
Future<UserModel?> getUserByToken(String authToken) async {
|
|
final token = await _db.userDao.getTokenByValue(authToken);
|
|
if (token == null) {
|
|
return null;
|
|
}
|
|
final now = DateTime.now();
|
|
if (token.expires.isBefore(now)) {
|
|
await _db.userDao.deleteToken(token.id);
|
|
return null;
|
|
}
|
|
final user = await fetchUser(token.userId);
|
|
if (user != null) {
|
|
_onlineUsers[token.userId] = now;
|
|
if (_onlineUsers.length > 300) {
|
|
updateOnlineUsers();
|
|
}
|
|
}
|
|
return user;
|
|
}
|
|
|
|
Future<void> updateOnlineUsers() async {
|
|
if (_onlineUsers.isEmpty) {
|
|
return;
|
|
}
|
|
|
|
final userIds = _onlineUsers.keys.whereNotNull().toList();
|
|
|
|
for (final userId in userIds) {
|
|
final lastOnline = _onlineUsers[userId];
|
|
if (lastOnline != null) {
|
|
await _db.userDao.updateUserDataPartial(
|
|
UserDatasCompanion(
|
|
userId: drift.Value(userId),
|
|
lastTimeOnline: drift.Value(lastOnline),
|
|
updatedAt: drift.Value(DateTime.now()),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
_onlineUsers.clear();
|
|
}
|
|
|
|
Future<(UserModel, String)> createOrGetUser({
|
|
required String externalId,
|
|
required String email,
|
|
String? name,
|
|
}) async {
|
|
// Check if user exists by externalId
|
|
final existingUser = await _db.userDao.getUserByExternalId(externalId);
|
|
if (existingUser != null) {
|
|
print('User found $name $email');
|
|
final userModel = await existingUser.toUserModel();
|
|
final token = await createOrGetAuthToken(userModel, externalId);
|
|
return (userModel, token);
|
|
}
|
|
|
|
print('Creating new user $name $email');
|
|
|
|
// Create new user
|
|
final userCompanion = UsersCompanion.insert(
|
|
externalUserId: externalId,
|
|
name: drift.Value(name),
|
|
email: drift.Value(email),
|
|
);
|
|
|
|
final userId = await _db.userDao.createUser(userCompanion);
|
|
|
|
// Create user data
|
|
await _db.userDao.createUserData(
|
|
UserDatasCompanion.insert(
|
|
userId: userId,
|
|
registrationDate: drift.Value(DateTime.now()),
|
|
),
|
|
);
|
|
|
|
final user = await _db.userDao.getUserById(userId);
|
|
if (user == null) {
|
|
throw Exception('Failed to create user');
|
|
}
|
|
|
|
final userModel = await user.toUserModel();
|
|
final token = await createOrGetAuthToken(userModel, externalId);
|
|
|
|
// Give free packs to new user
|
|
await _freePacksDistributor.giveFreePacksToUser(userModel);
|
|
|
|
print('User $name $email created successfully');
|
|
return (userModel, token);
|
|
}
|
|
|
|
// TODO: Implement remaining methods as needed
|
|
// updateUserSettings, addTestStatistics, editUser, deleteUser
|
|
} |