import 'dart:convert'; import 'package:http/http.dart' as http; import '../api/marianumcloud/nextcloud_ocs.dart'; /// Result of a successful Nextcloud push-v2 registration. class NextcloudPushRegistration { /// Per-user server public key (PEM). Forwarded to MarianumConnect as /// `userPublicKey` and used device-side to verify push signatures. final String publicKey; /// Opaque device identifier assigned by Nextcloud. final String deviceIdentifier; /// Signature over [deviceIdentifier], forwarded as `deviceIdentifierSignature`. final String signature; /// True when Nextcloud created a new subscription (HTTP 201); false when the /// existing one was already up to date (HTTP 200). final bool created; const NextcloudPushRegistration({ required this.publicKey, required this.deviceIdentifier, required this.signature, required this.created, }); } /// Thin client for Nextcloud's push-v2 device endpoints under /// `/ocs/v2.php/apps/notifications/api/v2/push`. Uses the shared OCS headers, /// which authenticate with the app password once available (the push /// registration binds to it). class NextcloudPushApi { static const _path = 'apps/notifications/api/v2/push'; final http.Client _client; NextcloudPushApi({http.Client? client}) : _client = client ?? http.Client(); /// Registers (or refreshes) this device. [devicePublicKeyPem] must be the /// 64-column SPKI PEM. [proxyServer] is the MarianumConnect push-proxy base /// URL (with trailing slash). Future register({ required String pushTokenHash, required String devicePublicKeyPem, required String proxyServer, }) async { final response = await _client.post( NextcloudOcs.uri(_path), headers: NextcloudOcs.headers(), body: { 'pushTokenHash': pushTokenHash, 'devicePublicKey': devicePublicKeyPem, 'proxyServer': proxyServer, }, ); if (response.statusCode < 200 || response.statusCode >= 300) { throw Exception('NC push register HTTP ${response.statusCode}'); } final json = jsonDecode(utf8.decode(response.bodyBytes)); final data = (json as Map)['ocs']?['data']; if (data is! Map) throw Exception('NC push register: malformed response'); final publicKey = data['publicKey'] as String?; final deviceIdentifier = data['deviceIdentifier'] as String?; final signature = data['signature'] as String?; if (publicKey == null || deviceIdentifier == null || signature == null) { throw Exception('NC push register: missing fields'); } return NextcloudPushRegistration( publicKey: publicKey, deviceIdentifier: deviceIdentifier, signature: signature, created: response.statusCode == 201, ); } /// Unregisters this device from Nextcloud push. Returns true when the server /// responded 202, meaning the proxy subscription should also be removed. Future unregister() async { final response = await _client.delete( NextcloudOcs.uri(_path), headers: NextcloudOcs.headers(), ); return response.statusCode == 202; } }