mudular code ready to fork 2

master
gitea 2 months ago
parent 8c6bf598f5
commit 3debb6ad7d

@ -0,0 +1,156 @@
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:firebase_core/firebase_core.dart';
import '../config/config_loader.dart';
import '../data/local/local_storage_service.dart';
import '../data/nostr/nostr_service.dart';
import '../data/sync/sync_engine.dart';
import '../data/firebase/firebase_service.dart';
import '../data/session/session_service.dart';
import '../data/immich/immich_service.dart';
import 'app_services.dart';
import 'service_locator.dart';
import 'logger.dart';
/// Initializes all application services.
///
/// Handles the complete initialization sequence:
/// 1. Load environment configuration
/// 2. Initialize Firebase (if enabled)
/// 3. Initialize local storage
/// 4. Initialize Nostr service and sync engine
/// 5. Initialize Immich service
/// 6. Initialize Firebase service (if enabled)
/// 7. Initialize session service
/// 8. Register all services with ServiceLocator
class AppInitializer {
/// Initializes all application services.
///
/// [environment] - The environment to use ('dev' or 'prod').
///
/// Returns an [AppServices] instance with all initialized services.
///
/// Throws if initialization fails.
static Future<AppServices> initialize({
String environment = 'dev',
}) async {
Logger.info('Starting application initialization...');
// Load .env file (optional - falls back to defaults if not found)
try {
await dotenv.load(fileName: '.env');
Logger.debug('.env file loaded successfully');
} catch (e) {
Logger.warning('.env file not found, using default values: $e');
}
// Load configuration based on environment
final config = ConfigLoader.load(environment);
Logger.setEnabled(config.enableLogging);
Logger.info('Configuration loaded for environment: $environment');
// Initialize Firebase if enabled
if (config.firebaseConfig.enabled) {
try {
await Firebase.initializeApp();
Logger.info('Firebase initialized successfully');
} catch (e) {
Logger.error(
'Firebase initialization failed: $e',
e,
);
Logger.warning(
'Note: Firebase requires google-services.json (Android) and GoogleService-Info.plist (iOS)',
);
}
}
// Initialize local storage service
Logger.debug('Initializing local storage service...');
final storageService = LocalStorageService();
try {
await storageService.initialize();
Logger.info('Local storage service initialized');
} catch (e) {
Logger.error('Failed to initialize storage: $e', e);
rethrow;
}
// Initialize Nostr service and sync engine
Logger.debug('Initializing Nostr service...');
final nostrService = NostrService();
final nostrKeyPair = nostrService.generateKeyPair();
final syncEngine = SyncEngine(
localStorage: storageService,
nostrService: nostrService,
nostrKeyPair: nostrKeyPair,
);
Logger.info('Nostr service and sync engine initialized');
// Load relays from config
for (final relayUrl in config.nostrRelays) {
nostrService.addRelay(relayUrl);
}
Logger.debug('Loaded ${config.nostrRelays.length} relay(s) from config');
// Initialize Immich service
Logger.debug('Initializing Immich service...');
final immichService = ImmichService(
baseUrl: config.immichBaseUrl,
apiKey: config.immichApiKey,
localStorage: storageService,
);
Logger.info('Immich service initialized');
// Initialize Firebase service if enabled
FirebaseService? firebaseService;
if (config.firebaseConfig.enabled) {
try {
Logger.debug('Initializing Firebase service...');
firebaseService = FirebaseService(
config: config.firebaseConfig,
localStorage: storageService,
);
await firebaseService.initialize();
Logger.info('Firebase service initialized: ${firebaseService.isEnabled}');
} catch (e) {
Logger.error('Firebase service initialization failed: $e', e);
firebaseService = null;
}
}
// Initialize SessionService with Firebase and Nostr integration
Logger.debug('Initializing session service...');
final sessionService = SessionService(
localStorage: storageService,
syncEngine: syncEngine,
firebaseService: firebaseService,
nostrService: nostrService,
);
Logger.info('Session service initialized');
// Create AppServices container
final appServices = AppServices(
localStorageService: storageService,
nostrService: nostrService,
syncEngine: syncEngine,
firebaseService: firebaseService,
sessionService: sessionService,
immichService: immichService,
);
// Register all services with ServiceLocator
ServiceLocator.instance.registerServices(
localStorageService: storageService,
nostrService: nostrService,
syncEngine: syncEngine,
firebaseService: firebaseService,
sessionService: sessionService,
immichService: immichService,
);
Logger.info('Application initialization completed successfully');
return appServices;
}
}

@ -0,0 +1,55 @@
import '../data/local/local_storage_service.dart';
import '../data/nostr/nostr_service.dart';
import '../data/sync/sync_engine.dart';
import '../data/firebase/firebase_service.dart';
import '../data/session/session_service.dart';
import '../data/immich/immich_service.dart';
/// Container for all application services.
///
/// Holds references to all initialized services and provides
/// a convenient way to dispose of them.
class AppServices {
/// Local storage service.
final LocalStorageService localStorageService;
/// Nostr service.
final NostrService? nostrService;
/// Sync engine.
final SyncEngine? syncEngine;
/// Firebase service.
final FirebaseService? firebaseService;
/// Session service.
final SessionService? sessionService;
/// Immich service.
final ImmichService? immichService;
/// Creates an [AppServices] instance.
AppServices({
required this.localStorageService,
this.nostrService,
this.syncEngine,
this.firebaseService,
this.sessionService,
this.immichService,
});
/// Disposes of all services that need cleanup.
Future<void> dispose() async {
syncEngine?.dispose();
nostrService?.dispose();
firebaseService?.dispose();
// Close storage service
try {
await localStorageService.close();
} catch (e) {
// Ignore errors during cleanup
}
}
}

@ -0,0 +1,24 @@
/// Application-wide constants.
class AppConstants {
/// Application name.
static const String appName = 'App Boilerplate';
/// Default connection timeout duration.
static const Duration connectionTimeout = Duration(seconds: 3);
/// Default health check timeout duration.
static const Duration healthCheckTimeout = Duration(seconds: 2);
/// Default retry delay for operations.
static const Duration retryDelay = Duration(seconds: 1);
/// Maximum number of retries for failed operations.
static const int maxRetries = 3;
/// Maximum queue size for sync operations.
static const int maxQueueSize = 100;
/// Private constructor to prevent instantiation.
AppConstants._();
}

@ -0,0 +1,24 @@
/// Route names for navigation.
class RouteNames {
/// Home route.
static const String home = '/home';
/// Immich route.
static const String immich = '/immich';
/// Nostr Events route.
static const String nostrEvents = '/nostr-events';
/// Session route.
static const String session = '/session';
/// Settings route.
static const String settings = '/settings';
/// Relay Management route.
static const String relayManagement = '/relay-management';
/// Private constructor to prevent instantiation.
RouteNames._();
}

@ -0,0 +1,10 @@
/// Barrel file for all exceptions.
///
/// Import this file to get access to all exception classes.
export 'firebase_exception.dart' show FirebaseServiceException;
export 'immich_exception.dart';
export 'invalid_environment_exception.dart';
export 'nostr_exception.dart';
export 'session_exception.dart';
export 'sync_exception.dart';

@ -0,0 +1,15 @@
/// Exception thrown when Firebase service operations fail.
///
/// Note: This is different from Firebase SDK's FirebaseException.
/// This exception is used for our FirebaseService wrapper.
class FirebaseServiceException implements Exception {
/// Error message.
final String message;
/// Creates a [FirebaseServiceException] with the provided message.
FirebaseServiceException(this.message);
@override
String toString() => 'FirebaseServiceException: $message';
}

@ -0,0 +1,20 @@
/// Exception thrown when Immich API operations fail.
class ImmichException implements Exception {
/// Error message.
final String message;
/// HTTP status code if available.
final int? statusCode;
/// Creates an [ImmichException] with the provided message.
ImmichException(this.message, [this.statusCode]);
@override
String toString() {
if (statusCode != null) {
return 'ImmichException: $message (Status: $statusCode)';
}
return 'ImmichException: $message';
}
}

@ -0,0 +1,15 @@
/// Exception thrown when an invalid environment is provided to [ConfigLoader].
class InvalidEnvironmentException implements Exception {
/// The invalid environment that was provided.
final String environment;
/// Creates an [InvalidEnvironmentException] with the provided environment.
InvalidEnvironmentException(this.environment);
@override
String toString() {
return 'InvalidEnvironmentException: Invalid environment "$environment". '
'Valid environments are: dev, prod';
}
}

@ -0,0 +1,12 @@
/// Exception thrown when Nostr operations fail.
class NostrException implements Exception {
/// Error message.
final String message;
/// Creates a [NostrException] with the provided message.
NostrException(this.message);
@override
String toString() => 'NostrException: $message';
}

@ -0,0 +1,12 @@
/// Exception thrown when session operations fail.
class SessionException implements Exception {
/// Error message.
final String message;
/// Creates a [SessionException] with the provided message.
SessionException(this.message);
@override
String toString() => 'SessionException: $message';
}

@ -0,0 +1,12 @@
/// Exception thrown when sync operations fail.
class SyncException implements Exception {
/// Error message.
final String message;
/// Creates a [SyncException] with the provided message.
SyncException(this.message);
@override
String toString() => 'SyncException: $message';
}

@ -0,0 +1,82 @@
import 'package:flutter/foundation.dart';
/// Log levels for different types of messages.
enum LogLevel {
/// Debug messages (only in debug mode).
debug,
/// Informational messages.
info,
/// Warning messages.
warning,
/// Error messages (always shown).
error,
}
/// Centralized logging service for the application.
///
/// Provides consistent logging across the app with different log levels.
/// In production, only errors are logged unless explicitly enabled.
class Logger {
/// Whether logging is enabled (can be configured via config).
static bool _enabled = kDebugMode;
/// Enable or disable logging.
static void setEnabled(bool enabled) {
_enabled = enabled;
}
/// Logs a message with the specified level.
///
/// [level] - The log level (debug, info, warning, error).
/// [message] - The message to log.
/// [error] - Optional error object.
/// [stackTrace] - Optional stack trace.
static void log(
LogLevel level,
String message, [
Object? error,
StackTrace? stackTrace,
]) {
// Always log errors, respect enabled flag for others
if (!_enabled && level != LogLevel.error) {
return;
}
final prefix = '[${level.name.toUpperCase()}]';
final timestamp = DateTime.now().toIso8601String();
debugPrint('$timestamp $prefix $message');
if (error != null) {
debugPrint('$timestamp $prefix Error: $error');
}
if (stackTrace != null) {
debugPrint('$timestamp $prefix Stack trace: $stackTrace');
}
}
/// Logs a debug message (only in debug mode).
static void debug(String message) {
log(LogLevel.debug, message);
}
/// Logs an informational message.
static void info(String message) {
log(LogLevel.info, message);
}
/// Logs a warning message.
static void warning(String message, [Object? error]) {
log(LogLevel.warning, message, error);
}
/// Logs an error message (always shown).
static void error(String message, [Object? error, StackTrace? stackTrace]) {
log(LogLevel.error, message, error, stackTrace);
}
}

@ -0,0 +1,85 @@
/// Service locator for dependency injection.
///
/// Provides a centralized registry for all application services,
/// making it easy to access services throughout the app
/// and swap implementations for testing.
class ServiceLocator {
/// Private constructor to prevent instantiation.
ServiceLocator._();
/// Singleton instance.
static final ServiceLocator instance = ServiceLocator._();
/// Local storage service.
dynamic _localStorageService;
/// Nostr service.
dynamic _nostrService;
/// Sync engine.
dynamic _syncEngine;
/// Firebase service.
dynamic _firebaseService;
/// Session service.
dynamic _sessionService;
/// Immich service.
dynamic _immichService;
/// Registers all services with the locator.
///
/// All services are optional and can be null if not configured.
void registerServices({
dynamic localStorageService,
dynamic nostrService,
dynamic syncEngine,
dynamic firebaseService,
dynamic sessionService,
dynamic immichService,
}) {
_localStorageService = localStorageService;
_nostrService = nostrService;
_syncEngine = syncEngine;
_firebaseService = firebaseService;
_sessionService = sessionService;
_immichService = immichService;
}
/// Gets the local storage service.
///
/// Throws [StateError] if service is not registered.
dynamic get localStorageService {
if (_localStorageService == null) {
throw StateError('LocalStorageService not registered');
}
return _localStorageService;
}
/// Gets the Nostr service (nullable).
dynamic get nostrService => _nostrService;
/// Gets the sync engine (nullable).
dynamic get syncEngine => _syncEngine;
/// Gets the Firebase service (nullable).
dynamic get firebaseService => _firebaseService;
/// Gets the session service (nullable).
dynamic get sessionService => _sessionService;
/// Gets the Immich service (nullable).
dynamic get immichService => _immichService;
/// Clears all registered services (useful for testing).
void reset() {
_localStorageService = null;
_nostrService = null;
_syncEngine = null;
_firebaseService = null;
_sessionService = null;
_immichService = null;
}
}
Loading…
Cancel
Save

Powered by TurnKey Linux.