nostr tools added

This commit is contained in:
gitea
2025-11-06 14:46:59 +01:00
parent d8c90cb105
commit f8aa20eb5c
14 changed files with 1146 additions and 141 deletions
+13
View File
@@ -1,3 +1,5 @@
import '../../nostr/models/nostr_profile.dart';
/// Data model representing a user session.
///
/// This model stores user identification and authentication information
@@ -15,17 +17,22 @@ class User {
/// Timestamp when the session was created (milliseconds since epoch).
final int createdAt;
/// Optional Nostr profile data (if logged in via Nostr).
final NostrProfile? nostrProfile;
/// Creates a [User] instance.
///
/// [id] - Unique identifier for the user.
/// [username] - Display name or username.
/// [token] - Optional authentication token.
/// [createdAt] - Session creation timestamp (defaults to current time).
/// [nostrProfile] - Optional Nostr profile data.
User({
required this.id,
required this.username,
this.token,
int? createdAt,
this.nostrProfile,
}) : createdAt = createdAt ?? DateTime.now().millisecondsSinceEpoch;
/// Creates a [User] from a Map (e.g., from database or JSON).
@@ -35,6 +42,9 @@ class User {
username: map['username'] as String,
token: map['token'] as String?,
createdAt: map['created_at'] as int?,
nostrProfile: map['nostr_profile'] != null
? NostrProfile.fromJson(map['nostr_profile'] as Map<String, dynamic>)
: null,
);
}
@@ -45,6 +55,7 @@ class User {
'username': username,
'token': token,
'created_at': createdAt,
'nostr_profile': nostrProfile?.toJson(),
};
}
@@ -54,12 +65,14 @@ class User {
String? username,
String? token,
int? createdAt,
NostrProfile? nostrProfile,
}) {
return User(
id: id ?? this.id,
username: username ?? this.username,
token: token ?? this.token,
createdAt: createdAt ?? this.createdAt,
nostrProfile: nostrProfile ?? this.nostrProfile,
);
}
+74
View File
@@ -5,6 +5,9 @@ import 'package:path_provider/path_provider.dart';
import '../local/local_storage_service.dart';
import '../sync/sync_engine.dart';
import '../firebase/firebase_service.dart';
import '../nostr/nostr_service.dart';
import '../nostr/models/nostr_keypair.dart';
import '../nostr/models/nostr_profile.dart';
import 'models/user.dart';
/// Exception thrown when session operations fail.
@@ -42,6 +45,9 @@ class SessionService {
/// Firebase service for optional cloud sync (optional).
final FirebaseService? _firebaseService;
/// Nostr service for Nostr authentication (optional).
final NostrService? _nostrService;
/// Map of user IDs to their session storage paths.
final Map<String, String> _userDbPaths = {};
@@ -59,17 +65,20 @@ class SessionService {
/// [localStorage] - Local storage service for data persistence.
/// [syncEngine] - Optional sync engine for coordinating sync operations.
/// [firebaseService] - Optional Firebase service for cloud sync.
/// [nostrService] - Optional Nostr service for Nostr authentication.
/// [testDbPath] - Optional database path for testing.
/// [testCacheDir] - Optional cache directory for testing.
SessionService({
required LocalStorageService localStorage,
SyncEngine? syncEngine,
FirebaseService? firebaseService,
NostrService? nostrService,
String? testDbPath,
Directory? testCacheDir,
}) : _localStorage = localStorage,
_syncEngine = syncEngine,
_firebaseService = firebaseService,
_nostrService = nostrService,
_testDbPath = testDbPath,
_testCacheDir = testCacheDir;
@@ -128,6 +137,71 @@ class SessionService {
}
}
/// Logs in a user using Nostr key (nsec or npub).
///
/// [nsecOrNpub] - Nostr key in nsec (private) or npub (public) format.
///
/// Returns the logged-in [User] with fetched profile data.
///
/// Throws [SessionException] if login fails or if user is already logged in.
Future<User> loginWithNostr(String nsecOrNpub) async {
if (_currentUser != null) {
throw SessionException('User already logged in. Logout first.');
}
if (_nostrService == null) {
throw SessionException('Nostr service not available');
}
try {
// Parse the key
NostrKeyPair keyPair;
if (nsecOrNpub.startsWith('nsec')) {
keyPair = NostrKeyPair.fromNsec(nsecOrNpub);
} else if (nsecOrNpub.startsWith('npub')) {
keyPair = NostrKeyPair.fromNpub(nsecOrNpub);
} else {
throw SessionException('Invalid Nostr key format. Expected nsec or npub.');
}
// Fetch profile from relays
NostrProfile? profile;
try {
profile = await _nostrService!.fetchProfile(keyPair.publicKey);
} catch (e) {
debugPrint('Warning: Failed to fetch Nostr profile: $e');
// Continue without profile - offline-first behavior
}
// Create user with Nostr profile
final user = User(
id: keyPair.publicKey,
username: profile?.displayName ?? keyPair.publicKey.substring(0, 16),
nostrProfile: profile,
);
// Create user-specific storage paths
await _setupUserStorage(user);
// Sync with Firebase if enabled
if (_firebaseService != null && _firebaseService!.isEnabled) {
try {
await _firebaseService!.syncItemsFromFirestore(user.id);
} catch (e) {
// Log error but don't fail login - offline-first behavior
debugPrint('Warning: Failed to sync from Firebase on login: $e');
}
}
// Set as current user
_currentUser = user;
return user;
} catch (e) {
throw SessionException('Failed to login with Nostr: $e');
}
}
/// Logs out the current user and clears session data.
///
/// [clearCache] - Whether to clear cached data (default: true).