Kate
2 years ago
committed by
GitHub
41 changed files with 738 additions and 1439 deletions
@ -1,4 +0,0 @@
|
||||
apiVersion: "v1" |
||||
name: nextcloud-neon |
||||
version: 1.0.0 |
||||
description: nextcloud-neon |
@ -1,73 +0,0 @@
|
||||
--- |
||||
kind: Deployment |
||||
apiVersion: apps/v1 |
||||
metadata: |
||||
name: common-proxies |
||||
spec: |
||||
selector: |
||||
matchLabels: |
||||
app: common-proxies |
||||
template: |
||||
metadata: |
||||
labels: |
||||
app: common-proxies |
||||
spec: |
||||
containers: |
||||
- name: common-proxies |
||||
image: "{{ .Values.common_proxies.image.repository }}:{{ .Values.common_proxies.image.tag }}" |
||||
imagePullPolicy: {{ .Values.common_proxies.image.pullPolicy }} |
||||
env: |
||||
{{ toYaml .Values.common_proxies.environment | indent 12 }} |
||||
ports: |
||||
- name: http |
||||
containerPort: 5000 |
||||
readinessProbe: |
||||
httpGet: |
||||
port: http |
||||
path: /health |
||||
livenessProbe: |
||||
httpGet: |
||||
port: http |
||||
path: /health |
||||
|
||||
--- |
||||
kind: Service |
||||
apiVersion: v1 |
||||
metadata: |
||||
name: common-proxies |
||||
spec: |
||||
type: {{ .Values.common_proxies.service.type }} |
||||
ports: |
||||
- port: {{ .Values.common_proxies.service.port }} |
||||
targetPort: http |
||||
selector: |
||||
app: common-proxies |
||||
|
||||
--- |
||||
apiVersion: networking.k8s.io/v1 |
||||
kind: Ingress |
||||
metadata: |
||||
name: common-proxies |
||||
{{ if .Values.common_proxies.ingress.annotations }} |
||||
annotations: |
||||
{{ toYaml .Values.common_proxies.ingress.annotations | indent 4 }} |
||||
{{ end }} |
||||
spec: |
||||
{{ if .Values.common_proxies.ingress.className }} |
||||
ingressClassName: {{ .Values.common_proxies.ingress.className }} |
||||
{{ end }} |
||||
rules: |
||||
- host: {{ .Values.common_proxies.ingress.host }} |
||||
http: |
||||
paths: |
||||
- path: {{ .Values.common_proxies.ingress.path }} |
||||
pathType: {{ .Values.common_proxies.ingress.pathType }} |
||||
backend: |
||||
service: |
||||
name: common-proxies |
||||
port: |
||||
number: {{ .Values.common_proxies.service.port }} |
||||
{{ if .Values.common_proxies.ingress.tls }} |
||||
tls: |
||||
{{ toYaml .Values.common_proxies.ingress.tls | indent 4 }} |
||||
{{ end }} |
@ -1,91 +0,0 @@
|
||||
--- |
||||
kind: Deployment |
||||
apiVersion: apps/v1 |
||||
metadata: |
||||
name: push-proxy |
||||
spec: |
||||
selector: |
||||
matchLabels: |
||||
app: push-proxy |
||||
template: |
||||
metadata: |
||||
labels: |
||||
app: push-proxy |
||||
spec: |
||||
containers: |
||||
- name: push-proxy |
||||
image: "{{ .Values.imageNextcloudPushProxy }}" |
||||
imagePullPolicy: IfNotPresent |
||||
ports: |
||||
- name: http |
||||
containerPort: 8080 |
||||
readinessProbe: |
||||
httpGet: |
||||
port: http |
||||
path: /health |
||||
livenessProbe: |
||||
httpGet: |
||||
port: http |
||||
path: /health |
||||
volumeMounts: |
||||
- mountPath: /data |
||||
name: push-proxy |
||||
volumes: |
||||
- name: push-proxy |
||||
persistentVolumeClaim: |
||||
claimName: push-proxy |
||||
|
||||
--- |
||||
kind: Service |
||||
apiVersion: v1 |
||||
metadata: |
||||
name: push-proxy |
||||
spec: |
||||
type: {{ .Values.push_proxy.service.type }} |
||||
ports: |
||||
- port: {{ .Values.push_proxy.service.port }} |
||||
targetPort: http |
||||
selector: |
||||
app: push-proxy |
||||
|
||||
--- |
||||
apiVersion: networking.k8s.io/v1 |
||||
kind: Ingress |
||||
metadata: |
||||
name: push-proxy |
||||
{{ if .Values.push_proxy.ingress.annotations }} |
||||
annotations: |
||||
{{ toYaml .Values.push_proxy.ingress.annotations | indent 4 }} |
||||
{{ end }} |
||||
spec: |
||||
{{ if .Values.push_proxy.ingress.className }} |
||||
ingressClassName: {{ .Values.push_proxy.ingress.className }} |
||||
{{ end }} |
||||
rules: |
||||
- host: {{ .Values.push_proxy.ingress.host }} |
||||
http: |
||||
paths: |
||||
- path: {{ .Values.push_proxy.ingress.path }} |
||||
pathType: {{ .Values.push_proxy.ingress.pathType }} |
||||
backend: |
||||
service: |
||||
name: push-proxy |
||||
port: |
||||
number: {{ .Values.push_proxy.service.port }} |
||||
{{ if .Values.push_proxy.ingress.tls }} |
||||
tls: |
||||
{{ toYaml .Values.push_proxy.ingress.tls | indent 4 }} |
||||
{{ end }} |
||||
|
||||
|
||||
--- |
||||
apiVersion: v1 |
||||
kind: PersistentVolumeClaim |
||||
metadata: |
||||
name: push-proxy |
||||
spec: |
||||
resources: |
||||
requests: |
||||
storage: 1Gi |
||||
accessModes: |
||||
- ReadWriteOnce |
@ -0,0 +1,30 @@
|
||||
part of '../neon.dart'; |
||||
|
||||
abstract class FirstLaunchBlocEvents {} |
||||
|
||||
abstract class FirstLaunchBlocStates { |
||||
BehaviorSubject get onFirstLaunch; |
||||
} |
||||
|
||||
class FirstLaunchBloc extends Bloc implements FirstLaunchBlocEvents, FirstLaunchBlocStates { |
||||
FirstLaunchBloc( |
||||
this._sharedPreferences, { |
||||
final bool disabled = false, |
||||
}) { |
||||
if (!disabled && !_sharedPreferences.containsKey(_keyFirstLaunch)) { |
||||
onFirstLaunch.add(null); |
||||
unawaited(_sharedPreferences.setBool(_keyFirstLaunch, false)); |
||||
} |
||||
} |
||||
|
||||
final SharedPreferences _sharedPreferences; |
||||
final _keyFirstLaunch = 'first-launch'; |
||||
|
||||
@override |
||||
void dispose() { |
||||
unawaited(onFirstLaunch.close()); |
||||
} |
||||
|
||||
@override |
||||
BehaviorSubject onFirstLaunch = BehaviorSubject(); |
||||
} |
@ -0,0 +1,25 @@
|
||||
import 'package:json_annotation/json_annotation.dart'; |
||||
import 'package:nextcloud/nextcloud.dart'; |
||||
|
||||
part 'push_notification.g.dart'; |
||||
|
||||
@JsonSerializable() |
||||
class PushNotification { |
||||
PushNotification({ |
||||
required this.accountID, |
||||
required this.priority, |
||||
required this.type, |
||||
required this.subject, |
||||
}); |
||||
|
||||
factory PushNotification.fromJson(final Map<String, dynamic> json) => _$PushNotificationFromJson(json); |
||||
Map<String, dynamic> toJson() => _$PushNotificationToJson(this); |
||||
|
||||
final String accountID; |
||||
|
||||
final String priority; |
||||
|
||||
final String type; |
||||
|
||||
final NextcloudNotificationsNotificationDecryptedSubject subject; |
||||
} |
@ -0,0 +1,21 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND |
||||
|
||||
part of 'push_notification.dart'; |
||||
|
||||
// ************************************************************************** |
||||
// JsonSerializableGenerator |
||||
// ************************************************************************** |
||||
|
||||
PushNotification _$PushNotificationFromJson(Map<String, dynamic> json) => PushNotification( |
||||
accountID: json['accountID'] as String, |
||||
priority: json['priority'] as String, |
||||
type: json['type'] as String, |
||||
subject: NextcloudNotificationsNotificationDecryptedSubject.fromJson(json['subject'] as Map<String, dynamic>), |
||||
); |
||||
|
||||
Map<String, dynamic> _$PushNotificationToJson(PushNotification instance) => <String, dynamic>{ |
||||
'accountID': instance.accountID, |
||||
'priority': instance.priority, |
||||
'type': instance.type, |
||||
'subject': instance.subject, |
||||
}; |
@ -1,20 +0,0 @@
|
||||
import 'package:json_annotation/json_annotation.dart'; |
||||
import 'package:nextcloud/nextcloud.dart'; |
||||
|
||||
part 'push_notification_with_account.g.dart'; |
||||
|
||||
@JsonSerializable() |
||||
class PushNotificationWithAccountID { |
||||
PushNotificationWithAccountID({ |
||||
required this.notification, |
||||
required this.accountID, |
||||
}); |
||||
|
||||
factory PushNotificationWithAccountID.fromJson(final Map<String, dynamic> json) => |
||||
_$PushNotificationWithAccountIDFromJson(json); |
||||
Map<String, dynamic> toJson() => _$PushNotificationWithAccountIDToJson(this); |
||||
|
||||
final NextcloudNotificationsPushNotification notification; |
||||
|
||||
final String accountID; |
||||
} |
@ -1,18 +0,0 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND |
||||
|
||||
part of 'push_notification_with_account.dart'; |
||||
|
||||
// ************************************************************************** |
||||
// JsonSerializableGenerator |
||||
// ************************************************************************** |
||||
|
||||
PushNotificationWithAccountID _$PushNotificationWithAccountIDFromJson(Map<String, dynamic> json) => |
||||
PushNotificationWithAccountID( |
||||
notification: NextcloudNotificationsPushNotification.fromJson(json['notification'] as Map<String, dynamic>), |
||||
accountID: json['accountID'] as String, |
||||
); |
||||
|
||||
Map<String, dynamic> _$PushNotificationWithAccountIDToJson(PushNotificationWithAccountID instance) => <String, dynamic>{ |
||||
'notification': instance.notification, |
||||
'accountID': instance.accountID, |
||||
}; |
@ -1,12 +0,0 @@
|
||||
# Files and directories created by pub. |
||||
.dart_tool/ |
||||
.packages |
||||
|
||||
# Conventional directory for build outputs. |
||||
build/ |
||||
|
||||
# Omit committing pubspec.lock for library packages; see |
||||
# https://dart.dev/guides/libraries/private-files#pubspeclock. |
||||
pubspec.lock |
||||
|
||||
tmp/ |
@ -1,17 +0,0 @@
|
||||
FROM dart:stable as builder |
||||
|
||||
WORKDIR /app |
||||
|
||||
ADD pubspec.yaml . |
||||
RUN dart pub get |
||||
|
||||
|
||||
ADD lib ./lib |
||||
ADD bin ./bin |
||||
RUN dart compile exe bin/unified_push.dart -o bin/nextcloud-push-proxy |
||||
|
||||
FROM debian:bullseye-slim |
||||
|
||||
COPY --from=builder /app/bin/nextcloud-push-proxy /usr/local/bin/ |
||||
|
||||
CMD ["nextcloud-push-proxy", "/data/devices.json"] |
@ -1 +0,0 @@
|
||||
include: package:nit_picking/dart.yaml |
@ -1,68 +0,0 @@
|
||||
import 'dart:convert'; |
||||
import 'dart:io'; |
||||
|
||||
import 'package:crypto/crypto.dart'; |
||||
import 'package:nextcloud_push_proxy/nextcloud_push_proxy.dart'; |
||||
|
||||
Future main(final List<String> args) async { |
||||
if (args.length != 1) { |
||||
throw Exception('Provide the file where to store devices'); |
||||
} |
||||
final devices = <PushProxyDevice>[]; |
||||
final devicesFile = File(args[0]); |
||||
if (devicesFile.existsSync()) { |
||||
devices.addAll( |
||||
(json.decode(devicesFile.readAsStringSync()) as List) |
||||
.map((final d) => PushProxyDevice.fromJson(d as Map<String, dynamic>)), |
||||
); |
||||
} |
||||
|
||||
final server = NextcloudPushProxy(); |
||||
|
||||
watchSignals((final signal) async { |
||||
print('Got exit signal, shutting down'); |
||||
await server.close(); |
||||
exit(1); |
||||
}); |
||||
|
||||
await server.create(); |
||||
|
||||
server.onNewDevice.listen((final device) { |
||||
if (!devices.map((final d) => d.pushToken).contains(device.pushToken)) { |
||||
devices.add(device); |
||||
devicesFile |
||||
..createSync(recursive: true) |
||||
..writeAsString(json.encode(devices.map((final d) => d.toJson()).toList())); |
||||
} |
||||
}); |
||||
|
||||
server.onNewNotification.listen((final notification) async { |
||||
for (final device in devices) { |
||||
if (notification.pushTokenHash == sha512.convert(utf8.encode(device.pushToken)).toString()) { |
||||
final request = await HttpClient().postUrl(Uri.parse(device.pushToken)) |
||||
..followRedirects = false |
||||
..persistentConnection = true |
||||
..add(utf8.encode(json.encode(notification.toPushNotificationData()))); |
||||
|
||||
final response = await request.close(); |
||||
if (response.statusCode > 299) { |
||||
print('Failed to send notification'); |
||||
} |
||||
} |
||||
} |
||||
}); |
||||
|
||||
print('Listening on *:8080'); |
||||
} |
||||
|
||||
void watchSignals(final Function(ProcessSignal signal) callback) { |
||||
for (final signal in [ |
||||
ProcessSignal.sighup, |
||||
ProcessSignal.sigint, |
||||
ProcessSignal.sigterm, |
||||
ProcessSignal.sigusr1, |
||||
ProcessSignal.sigusr2, |
||||
]) { |
||||
signal.watch().listen(callback); |
||||
} |
||||
} |
@ -1,163 +0,0 @@
|
||||
// ignore_for_file: public_member_api_docs |
||||
|
||||
import 'dart:async'; |
||||
import 'dart:convert'; |
||||
import 'dart:io'; |
||||
|
||||
import 'package:shelf/shelf.dart'; |
||||
import 'package:shelf/shelf_io.dart'; |
||||
import 'package:shelf_router/shelf_router.dart'; |
||||
|
||||
/// Implements the listening part of a Nextcloud push proxy |
||||
class NextcloudPushProxy { |
||||
HttpServer? _server; |
||||
|
||||
late StreamController<PushProxyDevice> _onNewDeviceController; |
||||
late StreamController<PushProxyNotification> _onNewNotificationController; |
||||
|
||||
Stream<PushProxyDevice>? _onNewDeviceStream; |
||||
Stream<PushProxyNotification>? _onNewNotificationStream; |
||||
|
||||
/// Listens for new devices |
||||
Stream<PushProxyDevice> get onNewDevice { |
||||
if (_onNewDeviceStream == null) { |
||||
throw Exception('Server not created'); |
||||
} |
||||
return _onNewDeviceStream!; |
||||
} |
||||
|
||||
/// Listens for new notifications |
||||
Stream<PushProxyNotification> get onNewNotification { |
||||
if (_onNewNotificationStream == null) { |
||||
throw Exception('Server not created'); |
||||
} |
||||
return _onNewNotificationStream!; |
||||
} |
||||
|
||||
late final _router = Router() |
||||
..post('/devices', _devicesHandler) |
||||
..post('/notifications', _notificationsHandler) |
||||
..get('/health', (final _) async => Response.ok('')); |
||||
|
||||
Future<Response> _devicesHandler(final Request request) async { |
||||
final data = Uri(query: await request.readAsString()).queryParameters; |
||||
_onNewDeviceController.add( |
||||
PushProxyDevice( |
||||
pushToken: data['pushToken']!, |
||||
deviceIdentifier: data['deviceIdentifier']!, |
||||
deviceIdentifierSignature: data['deviceIdentifierSignature']!, |
||||
userPublicKey: data['userPublicKey']!, |
||||
), |
||||
); |
||||
return Response.ok(''); |
||||
} |
||||
|
||||
Future<Response> _notificationsHandler(final Request request) async { |
||||
final data = Uri(query: await request.readAsString()).queryParameters; |
||||
for (final notification in data.values) { |
||||
final notificationData = json.decode(notification) as Map<String, dynamic>; |
||||
_onNewNotificationController.add( |
||||
PushProxyNotification( |
||||
deviceIdentifier: notificationData['deviceIdentifier']! as String, |
||||
pushTokenHash: notificationData['pushTokenHash']! as String, |
||||
subject: notificationData['subject']! as String, |
||||
signature: notificationData['signature']! as String, |
||||
priority: notificationData['priority']! as String, |
||||
type: notificationData['type']! as String, |
||||
), |
||||
); |
||||
} |
||||
return Response.ok(''); |
||||
} |
||||
|
||||
/// Creates a server listening on the [port] |
||||
Future create({ |
||||
final bool logging = true, |
||||
final int port = 8080, |
||||
}) async { |
||||
if (_server != null) { |
||||
throw Exception('Server already created'); |
||||
} |
||||
|
||||
_onNewDeviceController = StreamController<PushProxyDevice>(); |
||||
_onNewNotificationController = StreamController<PushProxyNotification>(); |
||||
_onNewDeviceStream = _onNewDeviceController.stream.asBroadcastStream(); |
||||
_onNewNotificationStream = _onNewNotificationController.stream.asBroadcastStream(); |
||||
|
||||
var handler = Cascade().add(_router.call).handler; |
||||
if (logging) { |
||||
handler = logRequests().addHandler(handler); |
||||
} |
||||
final server = await serve( |
||||
handler, |
||||
InternetAddress.anyIPv4, |
||||
port, |
||||
); |
||||
server.autoCompress = true; |
||||
|
||||
_server = server; |
||||
} |
||||
|
||||
/// Closes the server |
||||
Future close() async { |
||||
if (_server != null) { |
||||
await _server!.close(); |
||||
_server = null; |
||||
await _onNewDeviceController.close(); |
||||
await _onNewNotificationController.close(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
class PushProxyDevice { |
||||
PushProxyDevice({ |
||||
required this.pushToken, |
||||
required this.deviceIdentifier, |
||||
required this.deviceIdentifierSignature, |
||||
required this.userPublicKey, |
||||
}); |
||||
|
||||
factory PushProxyDevice.fromJson(final Map<String, dynamic> data) => PushProxyDevice( |
||||
pushToken: data['pushToken'] as String, |
||||
deviceIdentifier: data['deviceIdentifier'] as String, |
||||
deviceIdentifierSignature: data['deviceIdentifierSignature'] as String, |
||||
userPublicKey: data['userPublicKey'] as String, |
||||
); |
||||
|
||||
Map<String, dynamic> toJson() => { |
||||
'pushToken': pushToken, |
||||
'deviceIdentifier': deviceIdentifier, |
||||
'deviceIdentifierSignature': deviceIdentifierSignature, |
||||
'userPublicKey': userPublicKey, |
||||
}; |
||||
|
||||
final String pushToken; |
||||
final String deviceIdentifier; |
||||
final String deviceIdentifierSignature; |
||||
final String userPublicKey; |
||||
} |
||||
|
||||
class PushProxyNotification { |
||||
PushProxyNotification({ |
||||
required this.deviceIdentifier, |
||||
required this.pushTokenHash, |
||||
required this.subject, |
||||
required this.signature, |
||||
required this.priority, |
||||
required this.type, |
||||
}); |
||||
|
||||
final String deviceIdentifier; |
||||
final String pushTokenHash; |
||||
final String subject; |
||||
final String signature; |
||||
final String priority; |
||||
final String type; |
||||
|
||||
Map<String, String> toPushNotificationData() => { |
||||
'subject': subject, |
||||
'signature': signature, |
||||
'priority': priority, |
||||
'type': type, |
||||
}; |
||||
} |
@ -1,7 +0,0 @@
|
||||
sdk: |
||||
- stable |
||||
|
||||
stages: |
||||
- all: |
||||
- analyze: --fatal-infos . |
||||
- format: --output=none --set-exit-if-changed --line-length 120 . |
@ -1,16 +0,0 @@
|
||||
name: nextcloud_push_proxy |
||||
version: 1.0.0 |
||||
|
||||
environment: |
||||
sdk: '>=2.19.0 <3.0.0' |
||||
|
||||
dependencies: |
||||
crypto: ^3.0.2 |
||||
shelf: ^1.3.1 |
||||
shelf_router: ^1.1.3 |
||||
|
||||
dev_dependencies: |
||||
nit_picking: |
||||
git: |
||||
url: https://github.com/stack11/dart_nit_picking |
||||
ref: 0b2ee0d |
@ -1,23 +0,0 @@
|
||||
kind: Config |
||||
apiVersion: skaffold/v2beta27 |
||||
build: |
||||
artifacts: |
||||
- image: provokateurin/nextcloud-push-proxy |
||||
context: packages/nextcloud_push_proxy |
||||
local: |
||||
useBuildkit: true |
||||
concurrency: 0 |
||||
push: true |
||||
tagPolicy: |
||||
sha256: {} |
||||
deploy: |
||||
helm: |
||||
releases: |
||||
- name: nextcloud-neon |
||||
chartPath: helm/nextcloud-neon |
||||
namespace: nextcloud-neon |
||||
valuesFiles: |
||||
- helm/nextcloud-neon/values.yaml |
||||
artifactOverrides: |
||||
imageNextcloudPushProxy: provokateurin/nextcloud-push-proxy |
||||
statusCheckDeadlineSeconds: 300 |
Loading…
Reference in new issue