added login

This commit is contained in:
2026-08-07 17:07:49 +05:30
parent ad44402232
commit 4a2474ee6c
8 changed files with 908 additions and 383 deletions

View File

@@ -0,0 +1,83 @@
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import '../../domain/entities/pos_session.dart';
/// Where the signed-in session lives between launches.
///
/// The platform keystore, not SQLite — Keychain on macOS, Credential Manager
/// on Windows, the Android Keystore on a tablet. The response carries a bearer
/// token and every staff PIN in the clear, and the SQLite file sits on a
/// machine behind a shop counter readable by anything that can open it.
///
/// Stored as one blob rather than field by field so [clear] is a single
/// delete. A sign-out that leaves half a session behind is worse than one that
/// leaves none.
class SessionStore {
SessionStore({FlutterSecureStorage? secureStorage})
: _secure = secureStorage ?? const FlutterSecureStorage();
final FlutterSecureStorage _secure;
static const String _key = 'pos.session';
/// The stored session, or null if there is none, it cannot be read, or it
/// has expired.
///
/// An expired token is treated as absent and swept: carrying it forward only
/// moves the failure to the first call that uses it, which is a cashier
/// discovering it mid-sale rather than at the login screen.
Future<PosSession?> read() async {
String? raw;
try {
raw = await _secure.read(key: _key);
} on Object catch (e) {
// No keystore — a headless test host, or a Linux box with no secret
// service. The terminal still runs, it just asks for credentials.
debugPrint('SessionStore: keystore unavailable ($e)');
return null;
}
if (raw == null || raw.isEmpty) return null;
PosSession session;
try {
session = PosSession.fromJson(jsonDecode(raw) as Map<String, Object?>);
} on Object catch (e) {
// A blob this build cannot parse — an upgrade that changed the shape.
// Drop it rather than failing every launch from here on.
debugPrint('SessionStore: unreadable session dropped ($e)');
await clear();
return null;
}
if (session.token.isEmpty || session.isExpired) {
await clear();
return null;
}
return session;
}
Future<void> save(PosSession session) async {
try {
await _secure.write(key: _key, value: jsonEncode(session.toJson()));
} on Object catch (e) {
// Not fatal: the session is live in memory and this shift carries on.
// The next launch just asks for credentials again.
debugPrint('SessionStore: could not persist session ($e)');
}
}
/// Removes the session. Called on every sign-out, and on an expired or
/// rejected token.
Future<void> clear() async {
try {
await _secure.delete(key: _key);
} on Object catch (e) {
debugPrint('SessionStore: could not clear session ($e)');
}
}
}