Android-Tablet-Kiosk-App, gedacht für Betrieb hinter WireGuard-VPN. Bearer-Token-Geräte-Auth statt Ed25519-Request-Signing (siehe Backend- Commit f4ff11a im Haupt-Repo). Ersteinrichtung (Server-URL+Token), PIN/ NFC/Listen-Login, Stempeln Ein/Aus/Pause, Heartbeat-Loop. Bekannte Einschränkung: Android-NFC (13,56 MHz) ist inkompatibel zu den 125-kHz-EM4100-Karten der bestehenden PC-Terminals – separate Karten nötig, siehe README. Vollständiger Plan: /home/sysops/.claude/plans/staged-marinating-dove.md Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dzdm3EWeNdYFUuXFRJryA1
63 lines
2.4 KiB
Dart
63 lines
2.4 KiB
Dart
import '../models/kiosk_session.dart';
|
|
import 'api_client.dart';
|
|
|
|
/// Bündelt alle /kiosk/*-Aufrufe. Device-Auth (Bearer-Token) hängt der
|
|
/// Dio-Interceptor in ApiClient automatisch an; session_token (Mitarbeiter-
|
|
/// Login) wird pro Aufruf im Body mitgegeben, siehe
|
|
/// backend/app/schemas/kiosk_auth.py.
|
|
class KioskService {
|
|
final _dio = ApiClient.instance.dio;
|
|
|
|
Future<KioskSession> loginPin(String personnelNumber, String pin) async {
|
|
final r = await _dio.post('/kiosk/auth/pin', data: {
|
|
'personnel_number': personnelNumber,
|
|
'pin': pin,
|
|
});
|
|
return KioskSession.fromJson(r.data as Map<String, dynamic>);
|
|
}
|
|
|
|
Future<KioskSession> loginNfc(String nfcUid, String pin) async {
|
|
final r = await _dio.post('/kiosk/auth/nfc', data: {
|
|
'nfc_uid': nfcUid,
|
|
'pin': pin,
|
|
});
|
|
return KioskSession.fromJson(r.data as Map<String, dynamic>);
|
|
}
|
|
|
|
Future<KioskSession> loginList(String userId) async {
|
|
final r = await _dio.post('/kiosk/auth/list', data: {'user_id': userId});
|
|
return KioskSession.fromJson(r.data as Map<String, dynamic>);
|
|
}
|
|
|
|
Future<List<KioskEmployee>> listEmployees() async {
|
|
final r = await _dio.get('/kiosk/auth/users');
|
|
return KioskEmployee.listFromResponse(r.data as Map<String, dynamic>);
|
|
}
|
|
|
|
Future<void> logout(String sessionToken) async {
|
|
await _dio.post('/kiosk/auth/logout', data: {'session_token': sessionToken});
|
|
}
|
|
|
|
Future<StampStatus> status(String sessionToken) async {
|
|
final r = await _dio.post('/kiosk/stamp/status', data: {'session_token': sessionToken});
|
|
return StampStatus.fromJson(r.data as Map<String, dynamic>);
|
|
}
|
|
|
|
Future<StampResult> stampIn(String sessionToken) => _stamp('in', sessionToken);
|
|
Future<StampResult> stampOut(String sessionToken) => _stamp('out', sessionToken);
|
|
Future<StampResult> breakStart(String sessionToken) => _stamp('break-start', sessionToken);
|
|
Future<StampResult> breakEnd(String sessionToken) => _stamp('break-end', sessionToken);
|
|
|
|
Future<StampResult> _stamp(String action, String sessionToken) async {
|
|
final r = await _dio.post('/kiosk/stamp/$action', data: {'session_token': sessionToken});
|
|
return StampResult.fromJson(r.data as Map<String, dynamic>);
|
|
}
|
|
|
|
Future<void> heartbeat({int queuedOfflineEntries = 0, required String clientVersion}) async {
|
|
await _dio.post('/kiosk/heartbeat', data: {
|
|
'queued_offline_entries': queuedOfflineEntries,
|
|
'client_version': clientVersion,
|
|
});
|
|
}
|
|
}
|