import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_background_service/flutter_background_service.dart'; import 'enrollment_db.dart'; import 'filter_proxy.dart'; import 'proxy_service.dart'; import 'session_manager.dart'; Future main() async { WidgetsFlutterBinding.ensureInitialized(); await ProxyService.initialize(); runApp(const KPhoneApp()); } class KPhoneApp extends StatelessWidget { const KPhoneApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'k_phone', theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigo), useMaterial3: true, ), home: const ProxyStatusScreen(), ); } } class ProxyStatusScreen extends StatefulWidget { const ProxyStatusScreen({super.key}); @override State createState() => _ProxyStatusScreenState(); } class _ProxyStatusScreenState extends State { bool _serviceRunning = false; bool _cardAttached = false; String _statusMessage = 'Stopped'; final List _log = []; // Debug-only test state — stripped in release builds via kDebugMode. bool _testRunning = false; final _db = EnrollmentDb(); final _session = SessionManager(); @override void initState() { super.initState(); _subscribeToService(); } void _subscribeToService() { final service = FlutterBackgroundService(); // Sync initial running state service.isRunning().then((running) { if (mounted) setState(() => _serviceRunning = running); }); service.on('status').listen((event) { if (event == null) return; if (mounted) { setState(() { _serviceRunning = event['running'] as bool? ?? false; _cardAttached = event['cardAttached'] as bool? ?? false; _statusMessage = event['message'] as String? ?? ''; final log = event['log'] as String?; if (log != null) { _log.insert(0, log); if (_log.length > 200) _log.removeLast(); } }); } }); } void _addLog(String msg) { setState(() { _log.insert(0, msg); if (_log.length > 200) _log.removeLast(); }); } Future _runRegistrationLoginTest() async { if (_testRunning) return; setState(() => _testRunning = true); try { // --- Registrering --- _addLog('[REGISTRATION] Starter: enrolling "testbruger"'); try { final enrollment = await _db.register( username: 'testbruger', displayName: 'Test Bruger', ); _addLog('[REGISTRATION] OK: bruger="${enrollment.username}" ' 'displayName="${enrollment.displayName}"'); } on StateError { _addLog('[REGISTRATION] INFO: "testbruger" allerede enrollet — sletter og prøver igen'); await _db.delete('testbruger'); final enrollment = await _db.register( username: 'testbruger', displayName: 'Test Bruger', ); _addLog('[REGISTRATION] OK: bruger="${enrollment.username}" genregistreret'); } final list = await _db.list(); _addLog('[REGISTRATION] Enrollment-liste: ${list.map((e) => e.username).join(', ')}'); _addLog('[REGISTRATION] Test duplikat afvisning...'); try { await _db.register(username: 'testbruger'); _addLog('[REGISTRATION] FEJL: duplikat burde være afvist!'); } on StateError catch (e) { _addLog('[REGISTRATION] OK: duplikat afvist — ${e.message}'); } // --- Login --- _addLog('[LOGIN] Udsteder session for "testbruger"...'); final token = _session.issue('testbruger'); _addLog('[LOGIN] Token: ${token.substring(0, 8)}... (${token.length} tegn)'); final valid = _session.isValid(token); _addLog('[LOGIN] Session gyldig: $valid'); final entry = _session.getSession(token); _addLog('[LOGIN] Udløber: ${entry?.expires.toLocal().toString().substring(0, 19)}'); _addLog('[LOGIN] Tilbagekalder session...'); _session.revoke(token); _addLog('[LOGIN] Session gyldig efter revoke: ${_session.isValid(token)}'); _addLog('[LOGIN] FÆRDIG — alle flows OK'); } catch (e) { _addLog('[FEJL] $e'); } finally { setState(() => _testRunning = false); } } Future _toggleService() async { final service = FlutterBackgroundService(); final running = await service.isRunning(); if (running) { service.invoke('stop'); setState(() { _serviceRunning = false; _cardAttached = false; _statusMessage = 'Stopped'; }); } else { await service.startService(); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('k_phone — ChromeCard proxy'), backgroundColor: Theme.of(context).colorScheme.inversePrimary, ), body: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _StatusTile( label: 'Filter proxy (Comp 1)', ok: _serviceRunning, value: _serviceRunning ? 'Running on :$kFilterProxyPort' : 'Stopped', ), const SizedBox(height: 8), _StatusTile( label: 'Auth proxy (Comp 2)', ok: _serviceRunning, value: _serviceRunning ? 'Running on :$kProxyPort' : 'Stopped', ), const SizedBox(height: 8), _StatusTile( label: 'ChromeCard (USB)', ok: _cardAttached, value: _cardAttached ? 'Attached' : 'Not detected', ), const SizedBox(height: 8), Text( _statusMessage, style: Theme.of(context).textTheme.bodySmall, ), const SizedBox(height: 16), Row( children: [ FilledButton( onPressed: _toggleService, child: Text(_serviceRunning ? 'Stop proxy' : 'Start proxy'), ), if (kDebugMode) ...[ const SizedBox(width: 12), FilledButton.tonal( onPressed: _testRunning ? null : _runRegistrationLoginTest, child: _testRunning ? const SizedBox( width: 16, height: 16, child: CircularProgressIndicator(strokeWidth: 2), ) : const Text('Kør registrering & login'), ), ], ], ), const Divider(height: 32), const Text('Log', style: TextStyle(fontWeight: FontWeight.bold)), Expanded( child: ListView.builder( itemCount: _log.length, itemBuilder: (_, i) => Text( _log[i], style: const TextStyle(fontSize: 11, fontFamily: 'monospace'), ), ), ), ], ), ), ); } } class _StatusTile extends StatelessWidget { final String label; final bool ok; final String value; const _StatusTile({ required this.label, required this.ok, required this.value, }); @override Widget build(BuildContext context) { return Row( children: [ Icon( ok ? Icons.check_circle : Icons.radio_button_unchecked, color: ok ? Colors.green : Colors.grey, size: 18, ), const SizedBox(width: 8), Text('$label: ', style: const TextStyle(fontWeight: FontWeight.w600)), Text(value), ], ); } }