89 lines
3.1 KiB
Dart
89 lines
3.1 KiB
Dart
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<NextcloudPushRegistration> 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<bool> unregister() async {
|
|
final response = await _client.delete(
|
|
NextcloudOcs.uri(_path),
|
|
headers: NextcloudOcs.headers(),
|
|
);
|
|
return response.statusCode == 202;
|
|
}
|
|
}
|