Ajoute l'endpoint api/sync_api.dart, les use cases de synchronisation (application/sync_use_cases.dart) et l'adapter Postgres (infrastructure/postgres/synced_resource_repository.dart) implémentant un upsert LWW atomique via CTE (INSERT ... ON CONFLICT ... WHERE client_updated_at < EXCLUDED.client_updated_at, avec fallback UNION ALL pour le cas ignoré). dart pub get OK, dart analyze clean, dart test 15/15 vert. SQL d'upsert relu manuellement et jugé correct ; pas de test de bout en bout contre un vrai PostgreSQL faute d'accès Docker dans ce sandbox. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
62 lines
1.4 KiB
Dart
62 lines
1.4 KiB
Dart
import '../domain/domain.dart';
|
|
|
|
abstract interface class UserRepository {
|
|
Future<UserAccount?> findByEmail(String email);
|
|
|
|
Future<UserAccount?> findById(String id);
|
|
|
|
Future<void> insert(UserAccount user);
|
|
|
|
Future<void> updatePasswordHash({
|
|
required String userId,
|
|
required String passwordHash,
|
|
required DateTime updatedAt,
|
|
});
|
|
}
|
|
|
|
abstract interface class AuthSessionRepository {
|
|
Future<void> insert(AuthSession session);
|
|
|
|
Future<AuthSession?> findByTokenHash(String tokenHash);
|
|
|
|
Future<void> revoke({required String sessionId, required DateTime revokedAt});
|
|
}
|
|
|
|
enum SyncWriteStatus { accepted, ignoredOlder }
|
|
|
|
final class SyncWriteResult {
|
|
const SyncWriteResult({required this.status, required this.resource});
|
|
|
|
final SyncWriteStatus status;
|
|
final SyncedResource resource;
|
|
}
|
|
|
|
abstract interface class SyncedResourceRepository {
|
|
Future<SyncWriteResult> upsertWithLww(SyncedResource resource);
|
|
|
|
Future<List<SyncedResource>> findAllForUserSince({
|
|
required String ownerUserId,
|
|
DateTime? since,
|
|
});
|
|
}
|
|
|
|
abstract interface class PasswordHasher {
|
|
Future<String> hash(String password);
|
|
|
|
Future<bool> verify({required String password, required String passwordHash});
|
|
}
|
|
|
|
abstract interface class OpaqueTokenService {
|
|
String generateToken();
|
|
|
|
String hashToken(String token);
|
|
}
|
|
|
|
abstract interface class Clock {
|
|
DateTime now();
|
|
}
|
|
|
|
abstract interface class IdGenerator {
|
|
String newId();
|
|
}
|