From 255fc9337cdf047748774f5094e539b030e58863 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Schw=C3=B6rer?= Date: Sun, 11 May 2025 15:43:06 +0200 Subject: [PATCH] =?UTF-8?q?Add=20confirm=3D=3F=20query-param=20to=20delete?= =?UTF-8?q?-user=20route=20and=20confirm=20dialog=20in=20flutter=20[skip-t?= =?UTF-8?q?ests]?= --- flutter/lib/api/api_client.dart | 3 + flutter/lib/pages/account/account.dart | 155 ++++++++- .../keytoken_list/keytoken_list_item.dart | 1 - .../pages/keytoken_view/keytoken_view.dart | 1 - .../pages/message_list/message_list_item.dart | 1 - .../lib/pages/message_view/message_view.dart | 1 - flutter/lib/pages/settings/settings_view.dart | 4 +- .../subscription_view/subscription_view.dart | 1 - flutter/lib/state/app_auth.dart | 4 + flutter/lib/state/app_theme.dart | 1 - flutter/pubspec.lock | 8 + flutter/pubspec.yaml | 3 +- scnserver/db/dbtools/logger.go | 24 +- scnserver/db/dbtools/preprocessor.go | 24 +- scnserver/swagger/swagger.json | 319 ++++++------------ scnserver/swagger/swagger.yaml | 296 ++++++---------- scnserver/test/user_test.go | 8 +- 17 files changed, 417 insertions(+), 437 deletions(-) diff --git a/flutter/lib/api/api_client.dart b/flutter/lib/api/api_client.dart index 3d53769..4ed7d9f 100644 --- a/flutter/lib/api/api_client.dart +++ b/flutter/lib/api/api_client.dart @@ -190,6 +190,9 @@ class APIClient { relURL: 'users/$uid', fn: User.fromJson, authToken: auth.getToken(), + query: { + 'confirm': ['true'] + }, ); } diff --git a/flutter/lib/pages/account/account.dart b/flutter/lib/pages/account/account.dart index 4234b55..510d294 100644 --- a/flutter/lib/pages/account/account.dart +++ b/flutter/lib/pages/account/account.dart @@ -1,5 +1,7 @@ +import 'dart:async'; import 'dart:io'; +import 'package:action_slider/action_slider.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; @@ -527,17 +529,30 @@ class _AccountRootPageState extends State { final acc = AppAuth(); if (!acc.isAuth()) return; - try { - TODO ASK BEFORE DELETING TEH FUCKING USER !!!!!!! + final r1 = await UIDialogs.showConfirmDialog(context, "Delete Account?", text: "Are you sure you want to delete your account?", okText: "Delete", cancelText: "Cancel"); + if (!r1) return; + final r2 = await UIDialogs.showConfirmDialog(context, "Really sure?", text: "Are you really sure you want to delete your account?.\n\nThis includes all your messages and channels etc.\nThis action cannot be undone!", okText: "Delete", cancelText: "Cancel"); + if (!r2) return; + + final r3 = await UIDialogs.showTextInput(context, "Please input 'Delete' to delete your Account.", ""); + if (r3 == null) return; + if (r3.trim().toLowerCase() != 'delete') return; + + final r4 = await this._showDeleteDialog(context); + if (!r4) return; + + final r5 = await this._showDeleteAccountWaitDialog(context); + if (!r5) return; + + try { await APIClient.deleteUser(acc, acc.userID!); Toaster.info('Logout', 'Successfully logged out'); + //TODO clear messages/channels/etc in open views acc.clear(); await acc.save(); - - //TODO clear messages/channels/etc in open views } catch (exc, trace) { Toaster.error("Error", 'Failed to delete user'); ApplicationLog.error('Failed to delete user: ' + exc.toString(), trace: trace); @@ -570,4 +585,136 @@ class _AccountRootPageState extends State { ApplicationLog.error('Failed to update username: ' + exc.toString(), trace: trace); } } + + Future _showDeleteDialog(BuildContext context) { + return showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text("Delete Account?"), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + child: ActionSlider.standard( + sliderBehavior: SliderBehavior.stretch, + width: 300, + backgroundColor: Colors.white, + toggleColor: Colors.red, + action: (controller) => Navigator.of(context).pop(true), + child: const Text('Slide to delete'), + ), + width: 300, + height: 65, + ), + ], + ), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(false), + child: Text('Cancel'), + ), + ], + ), + ).then((value) => value ?? false); + } + + Future _showDeleteAccountWaitDialog(BuildContext context) { + final completer = Completer(); + + bool isTimerActive = true; + + const int totalSeconds = 20; + int secondsRemaining = totalSeconds; + double percentageRemaining = 1.0; + + late Timer timer; + + showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext dialogContext) { + void Function(void Function())? setStateInner = null; + + final t0 = DateTime.now(); + + timer = Timer.periodic(const Duration(milliseconds: 50), (t) { + setStateInner?.call(() { + percentageRemaining = 1 - (DateTime.now().millisecondsSinceEpoch - t0.millisecondsSinceEpoch) / (totalSeconds * 1000.0); + secondsRemaining = (totalSeconds * percentageRemaining).ceil(); + + if (secondsRemaining <= 0) { + t.cancel(); + isTimerActive = false; + + // Close the dialog and return true for successful deletion + Navigator.of(dialogContext).pop(); + if (!completer.isCompleted) { + completer.complete(true); + } + } + }); + }); + + return StatefulBuilder( + builder: (context, setState) { + setStateInner = setState; + return AlertDialog( + title: const Text('Delete Account'), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + 'Your account will be deleted in $secondsRemaining seconds.', + textAlign: TextAlign.center, + ), + const SizedBox(height: 20), + SizedBox( + height: 100, + width: 100, + child: CircularProgressIndicator( + value: 1 - percentageRemaining, + strokeWidth: 8.0, + valueColor: const AlwaysStoppedAnimation(Colors.red), + backgroundColor: Colors.grey[300], + ), + ), + ], + ), + actions: [ + TextButton( + onPressed: () { + // Cancel the timer + if (timer.isActive) { + timer.cancel(); + } + isTimerActive = false; + + // Close the dialog and return false for cancellation + Navigator.of(dialogContext).pop(); + if (!completer.isCompleted) { + completer.complete(false); + } + }, + child: const Text('CANCEL'), + ), + ], + ); + }, + ); + }, + ).then((_) { + // Ensure timer is cancelled if dialog is dismissed + if (timer.isActive) { + timer.cancel(); + } + + // If the completer hasn't been completed yet (e.g., if the dialog is dismissed), + // complete it with false + if (!completer.isCompleted && isTimerActive) { + completer.complete(false); + } + }); + + return completer.future; + } } diff --git a/flutter/lib/pages/keytoken_list/keytoken_list_item.dart b/flutter/lib/pages/keytoken_list/keytoken_list_item.dart index be53a8d..bf84f44 100644 --- a/flutter/lib/pages/keytoken_list/keytoken_list_item.dart +++ b/flutter/lib/pages/keytoken_list/keytoken_list_item.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; -import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; import 'package:simplecloudnotifier/api/api_client.dart'; import 'package:simplecloudnotifier/models/keytoken.dart'; diff --git a/flutter/lib/pages/keytoken_view/keytoken_view.dart b/flutter/lib/pages/keytoken_view/keytoken_view.dart index 467fd37..c149b51 100644 --- a/flutter/lib/pages/keytoken_view/keytoken_view.dart +++ b/flutter/lib/pages/keytoken_view/keytoken_view.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; -import 'package:intl/intl.dart'; import 'package:simplecloudnotifier/api/api_client.dart'; import 'package:simplecloudnotifier/components/error_display/error_display.dart'; import 'package:simplecloudnotifier/components/layout/scaffold.dart'; diff --git a/flutter/lib/pages/message_list/message_list_item.dart b/flutter/lib/pages/message_list/message_list_item.dart index 4b0739d..03765ad 100644 --- a/flutter/lib/pages/message_list/message_list_item.dart +++ b/flutter/lib/pages/message_list/message_list_item.dart @@ -5,7 +5,6 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:provider/provider.dart'; import 'package:simplecloudnotifier/models/channel.dart'; import 'package:simplecloudnotifier/models/scn_message.dart'; -import 'package:intl/intl.dart'; import 'package:simplecloudnotifier/state/app_settings.dart'; import 'package:simplecloudnotifier/utils/ui.dart'; diff --git a/flutter/lib/pages/message_view/message_view.dart b/flutter/lib/pages/message_view/message_view.dart index 7a23cf1..dd7a386 100644 --- a/flutter/lib/pages/message_view/message_view.dart +++ b/flutter/lib/pages/message_view/message_view.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; -import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; import 'package:share_plus/share_plus.dart'; import 'package:simplecloudnotifier/api/api_client.dart'; diff --git a/flutter/lib/pages/settings/settings_view.dart b/flutter/lib/pages/settings/settings_view.dart index a6902ae..8e127e5 100644 --- a/flutter/lib/pages/settings/settings_view.dart +++ b/flutter/lib/pages/settings/settings_view.dart @@ -186,8 +186,8 @@ class _SettingsRootPageState extends State { SettingsTile.navigation( leading: Icon(FontAwesomeIcons.solidBell), title: Text('FCM Token'), - value: Text(AppAuth().getToken()), - onPressed: (context) => _clipboardCopy(AppAuth().getToken()), + value: Text(AppAuth().getTokenOrNull() ?? 'N/A'), + onPressed: (context) => _clipboardCopy(AppAuth().getTokenOrNull() ?? ''), ), ], ), diff --git a/flutter/lib/pages/subscription_view/subscription_view.dart b/flutter/lib/pages/subscription_view/subscription_view.dart index 87f7afc..2cd98b8 100644 --- a/flutter/lib/pages/subscription_view/subscription_view.dart +++ b/flutter/lib/pages/subscription_view/subscription_view.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; -import 'package:intl/intl.dart'; import 'package:simplecloudnotifier/api/api_client.dart'; import 'package:simplecloudnotifier/components/error_display/error_display.dart'; import 'package:simplecloudnotifier/components/layout/scaffold.dart'; diff --git a/flutter/lib/state/app_auth.dart b/flutter/lib/state/app_auth.dart index f41b1c9..6a389e8 100644 --- a/flutter/lib/state/app_auth.dart +++ b/flutter/lib/state/app_auth.dart @@ -225,6 +225,10 @@ class AppAuth extends ChangeNotifier implements TokenSource { return _tokenAdmin!; } + String? getTokenOrNull() { + return _tokenAdmin; + } + @override String getUserID() { return _userID!; diff --git a/flutter/lib/state/app_theme.dart b/flutter/lib/state/app_theme.dart index 62166bb..00ab3ae 100644 --- a/flutter/lib/state/app_theme.dart +++ b/flutter/lib/state/app_theme.dart @@ -1,4 +1,3 @@ -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:simplecloudnotifier/state/globals.dart'; diff --git a/flutter/pubspec.lock b/flutter/pubspec.lock index 0095d9e..7774411 100644 --- a/flutter/pubspec.lock +++ b/flutter/pubspec.lock @@ -22,6 +22,14 @@ packages: description: dart source: sdk version: "0.3.3" + action_slider: + dependency: "direct main" + description: + name: action_slider + sha256: fad0720cde9bf06c12594c15da17dba087556a3285875a91aee3d3a64a3072e2 + url: "https://pub.dev" + source: hosted + version: "0.7.0" analyzer: dependency: transitive description: diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index efc8e7b..960bfe0 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -2,7 +2,7 @@ name: simplecloudnotifier description: "Receive push messages" publish_to: 'none' -version: 2.0.0+474 +version: 2.0.0+479 environment: sdk: '>=3.2.6 <4.0.0' @@ -40,6 +40,7 @@ dependencies: mobile_scanner: ^6.0.1 settings_ui: ^2.0.2 git_stamp: ^5.10.0 + action_slider: ^0.7.0 dependency_overrides: font_awesome_flutter: path: deps/font_awesome_flutter diff --git a/scnserver/db/dbtools/logger.go b/scnserver/db/dbtools/logger.go index 18516da..ea127ca 100644 --- a/scnserver/db/dbtools/logger.go +++ b/scnserver/db/dbtools/logger.go @@ -16,31 +16,31 @@ type DBLogger struct { Ident string } -func (l DBLogger) PrePing(ctx context.Context) error { +func (l DBLogger) PrePing(ctx context.Context, meta sq.PrePingMeta) error { log.Debug().Msg("[SQL-PING]") return nil } -func (l DBLogger) PreTxBegin(ctx context.Context, txid uint16) error { +func (l DBLogger) PreTxBegin(ctx context.Context, txid uint16, meta sq.PreTxBeginMeta) error { log.Debug().Msg(fmt.Sprintf("[SQL-TX<%s|%d>-START]", l.Ident, txid)) return nil } -func (l DBLogger) PreTxCommit(txid uint16) error { +func (l DBLogger) PreTxCommit(txid uint16, meta sq.PreTxCommitMeta) error { log.Debug().Msg(fmt.Sprintf("[SQL-TX<%s|%d>-COMMIT]", l.Ident, txid)) return nil } -func (l DBLogger) PreTxRollback(txid uint16) error { +func (l DBLogger) PreTxRollback(txid uint16, meta sq.PreTxRollbackMeta) error { log.Debug().Msg(fmt.Sprintf("[SQL-TX<%s|%d>-ROLLBACK]", l.Ident, txid)) return nil } -func (l DBLogger) PreQuery(ctx context.Context, txID *uint16, sql *string, params *sq.PP) error { +func (l DBLogger) PreQuery(ctx context.Context, txID *uint16, sql *string, params *sq.PP, meta sq.PreQueryMeta) error { if txID == nil { log.Debug().Msg(fmt.Sprintf("[SQL<%s>-QUERY] %s", l.Ident, fmtSQLPrint(*sql))) } else { @@ -50,7 +50,7 @@ func (l DBLogger) PreQuery(ctx context.Context, txID *uint16, sql *string, param return nil } -func (l DBLogger) PreExec(ctx context.Context, txID *uint16, sql *string, params *sq.PP) error { +func (l DBLogger) PreExec(ctx context.Context, txID *uint16, sql *string, params *sq.PP, meta sq.PreExecMeta) error { if txID == nil { log.Debug().Msg(fmt.Sprintf("[SQL-<%s>-EXEC] %s", l.Ident, fmtSQLPrint(*sql))) } else { @@ -60,27 +60,27 @@ func (l DBLogger) PreExec(ctx context.Context, txID *uint16, sql *string, params return nil } -func (l DBLogger) PostPing(result error) { +func (l DBLogger) PostPing(result error, meta sq.PostPingMeta) { // } -func (l DBLogger) PostTxBegin(txid uint16, result error) { +func (l DBLogger) PostTxBegin(txid uint16, result error, meta sq.PostTxBeginMeta) { // } -func (l DBLogger) PostTxCommit(txid uint16, result error) { +func (l DBLogger) PostTxCommit(txid uint16, result error, meta sq.PostTxCommitMeta) { // } -func (l DBLogger) PostTxRollback(txid uint16, result error) { +func (l DBLogger) PostTxRollback(txid uint16, result error, meta sq.PostTxRollbackMeta) { // } -func (l DBLogger) PostQuery(txID *uint16, sqlOriginal string, sqlReal string, params sq.PP) { +func (l DBLogger) PostQuery(txID *uint16, sqlOriginal string, sqlReal string, params sq.PP, result error, meta sq.PostQueryMeta) { // } -func (l DBLogger) PostExec(txID *uint16, sqlOriginal string, sqlReal string, params sq.PP) { +func (l DBLogger) PostExec(txID *uint16, sqlOriginal string, sqlReal string, params sq.PP, result error, meta sq.PostExecMeta) { // } diff --git a/scnserver/db/dbtools/preprocessor.go b/scnserver/db/dbtools/preprocessor.go index 4d493d9..818bc3c 100644 --- a/scnserver/db/dbtools/preprocessor.go +++ b/scnserver/db/dbtools/preprocessor.go @@ -101,23 +101,23 @@ func (pp *DBPreprocessor) Init(ctx context.Context) error { return nil } -func (pp *DBPreprocessor) PrePing(ctx context.Context) error { +func (pp *DBPreprocessor) PrePing(ctx context.Context, meta sq.PrePingMeta) error { return nil } -func (pp *DBPreprocessor) PreTxBegin(ctx context.Context, txid uint16) error { +func (pp *DBPreprocessor) PreTxBegin(ctx context.Context, txid uint16, meta sq.PreTxBeginMeta) error { return nil } -func (pp *DBPreprocessor) PreTxCommit(txid uint16) error { +func (pp *DBPreprocessor) PreTxCommit(txid uint16, meta sq.PreTxCommitMeta) error { return nil } -func (pp *DBPreprocessor) PreTxRollback(txid uint16) error { +func (pp *DBPreprocessor) PreTxRollback(txid uint16, meta sq.PreTxRollbackMeta) error { return nil } -func (pp *DBPreprocessor) PreQuery(ctx context.Context, txID *uint16, sql *string, params *sq.PP) error { +func (pp *DBPreprocessor) PreQuery(ctx context.Context, txID *uint16, sql *string, params *sq.PP, meta sq.PreQueryMeta) error { sqlOriginal := *sql pp.lock.Lock() @@ -223,30 +223,30 @@ func (pp *DBPreprocessor) PreQuery(ctx context.Context, txID *uint16, sql *strin return nil } -func (pp *DBPreprocessor) PreExec(ctx context.Context, txID *uint16, sql *string, params *sq.PP) error { +func (pp *DBPreprocessor) PreExec(ctx context.Context, txID *uint16, sql *string, params *sq.PP, meta sq.PreExecMeta) error { return nil } -func (pp *DBPreprocessor) PostPing(result error) { +func (pp *DBPreprocessor) PostPing(result error, meta sq.PostPingMeta) { // } -func (pp *DBPreprocessor) PostTxBegin(txid uint16, result error) { +func (pp *DBPreprocessor) PostTxBegin(txid uint16, result error, meta sq.PostTxBeginMeta) { // } -func (pp *DBPreprocessor) PostTxCommit(txid uint16, result error) { +func (pp *DBPreprocessor) PostTxCommit(txid uint16, result error, meta sq.PostTxCommitMeta) { // } -func (pp *DBPreprocessor) PostTxRollback(txid uint16, result error) { +func (pp *DBPreprocessor) PostTxRollback(txid uint16, result error, meta sq.PostTxRollbackMeta) { // } -func (pp *DBPreprocessor) PostQuery(txID *uint16, sqlOriginal string, sqlReal string, params sq.PP) { +func (pp *DBPreprocessor) PostQuery(txID *uint16, sqlOriginal string, sqlReal string, params sq.PP, result error, meta sq.PostQueryMeta) { // } -func (pp *DBPreprocessor) PostExec(txID *uint16, sqlOriginal string, sqlReal string, params sq.PP) { +func (pp *DBPreprocessor) PostExec(txID *uint16, sqlOriginal string, sqlReal string, params sq.PP, result error, meta sq.PostExecMeta) { // } diff --git a/scnserver/swagger/swagger.json b/scnserver/swagger/swagger.json index b5b0fde..a836676 100644 --- a/scnserver/swagger/swagger.json +++ b/scnserver/swagger/swagger.json @@ -19,63 +19,39 @@ "parameters": [ { "type": "string", - "example": "test", - "name": "channel", - "in": "query" - }, - { - "type": "string", - "example": "This is a message", "name": "content", "in": "query" }, { "type": "string", - "example": "P3TNH8mvv14fm", - "name": "key", - "in": "query" - }, - { - "type": "string", - "example": "db8b0e6a-a08c-4646", "name": "msg_id", "in": "query" }, { - "enum": [ - 0, - 1, - 2 - ], "type": "integer", - "example": 1, "name": "priority", "in": "query" }, - { - "type": "string", - "example": "example-server", - "name": "sender_name", - "in": "query" - }, { "type": "number", - "example": 1669824037, "name": "timestamp", "in": "query" }, { "type": "string", - "example": "Hello World", "name": "title", "in": "query" }, { - "type": "string", - "example": "7725", + "type": "integer", "name": "user_id", "in": "query" }, + { + "type": "string", + "name": "user_key", + "in": "query" + }, { "description": " ", "name": "post_body", @@ -86,62 +62,38 @@ }, { "type": "string", - "example": "test", - "name": "channel", - "in": "formData" - }, - { - "type": "string", - "example": "This is a message", "name": "content", "in": "formData" }, { "type": "string", - "example": "P3TNH8mvv14fm", - "name": "key", - "in": "formData" - }, - { - "type": "string", - "example": "db8b0e6a-a08c-4646", "name": "msg_id", "in": "formData" }, { - "enum": [ - 0, - 1, - 2 - ], "type": "integer", - "example": 1, "name": "priority", "in": "formData" }, - { - "type": "string", - "example": "example-server", - "name": "sender_name", - "in": "formData" - }, { "type": "number", - "example": 1669824037, "name": "timestamp", "in": "formData" }, { "type": "string", - "example": "Hello World", "name": "title", "in": "formData" }, { - "type": "string", - "example": "7725", + "type": "integer", "name": "user_id", "in": "formData" + }, + { + "type": "string", + "name": "user_key", + "in": "formData" } ], "responses": { @@ -1372,7 +1324,7 @@ } } }, - "patch": { + "delete": { "tags": [ "API-v2" ], @@ -1419,6 +1371,71 @@ } } } + }, + "patch": { + "description": "The body-values are optional, only send the ones you want to update", + "tags": [ + "API-v2" + ], + "summary": "(Partially) update a user", + "operationId": "api-user-update", + "parameters": [ + { + "type": "string", + "description": "UserID", + "name": "uid", + "in": "path", + "required": true + }, + { + "description": "Change the username (send an empty string to clear it)", + "name": "username", + "in": "body", + "schema": { + "type": "string" + } + }, + { + "description": "Send a verification of premium purchase", + "name": "pro_token", + "in": "body", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.User" + } + }, + "400": { + "description": "supplied values/parameters cannot be parsed / are invalid", + "schema": { + "$ref": "#/definitions/ginresp.apiError" + } + }, + "401": { + "description": "user is not authorized / has missing permissions", + "schema": { + "$ref": "#/definitions/ginresp.apiError" + } + }, + "404": { + "description": "user not found", + "schema": { + "$ref": "#/definitions/ginresp.apiError" + } + }, + "500": { + "description": "internal server error", + "schema": { + "$ref": "#/definitions/ginresp.apiError" + } + } + } } }, "/api/v2/users/{uid}/channels": { @@ -2795,63 +2812,39 @@ "parameters": [ { "type": "string", - "example": "test", - "name": "channel", - "in": "query" - }, - { - "type": "string", - "example": "This is a message", "name": "content", "in": "query" }, { "type": "string", - "example": "P3TNH8mvv14fm", - "name": "key", - "in": "query" - }, - { - "type": "string", - "example": "db8b0e6a-a08c-4646", "name": "msg_id", "in": "query" }, { - "enum": [ - 0, - 1, - 2 - ], "type": "integer", - "example": 1, "name": "priority", "in": "query" }, - { - "type": "string", - "example": "example-server", - "name": "sender_name", - "in": "query" - }, { "type": "number", - "example": 1669824037, "name": "timestamp", "in": "query" }, { "type": "string", - "example": "Hello World", "name": "title", "in": "query" }, { - "type": "string", - "example": "7725", + "type": "integer", "name": "user_id", "in": "query" }, + { + "type": "string", + "name": "user_key", + "in": "query" + }, { "description": " ", "name": "post_body", @@ -2862,62 +2855,38 @@ }, { "type": "string", - "example": "test", - "name": "channel", - "in": "formData" - }, - { - "type": "string", - "example": "This is a message", "name": "content", "in": "formData" }, { "type": "string", - "example": "P3TNH8mvv14fm", - "name": "key", - "in": "formData" - }, - { - "type": "string", - "example": "db8b0e6a-a08c-4646", "name": "msg_id", "in": "formData" }, { - "enum": [ - 0, - 1, - 2 - ], "type": "integer", - "example": 1, "name": "priority", "in": "formData" }, - { - "type": "string", - "example": "example-server", - "name": "sender_name", - "in": "formData" - }, { "type": "number", - "example": 1669824037, "name": "timestamp", "in": "formData" }, { "type": "string", - "example": "Hello World", "name": "title", "in": "formData" }, { - "type": "string", - "example": "7725", + "type": "integer", "name": "user_id", "in": "formData" + }, + { + "type": "string", + "name": "user_key", + "in": "formData" } ], "responses": { @@ -2965,121 +2934,73 @@ "parameters": [ { "type": "string", - "example": "test", - "name": "channel", - "in": "query" - }, - { - "type": "string", - "example": "This is a message", "name": "content", "in": "query" }, { "type": "string", - "example": "P3TNH8mvv14fm", - "name": "key", - "in": "query" - }, - { - "type": "string", - "example": "db8b0e6a-a08c-4646", "name": "msg_id", "in": "query" }, { - "enum": [ - 0, - 1, - 2 - ], "type": "integer", - "example": 1, "name": "priority", "in": "query" }, - { - "type": "string", - "example": "example-server", - "name": "sender_name", - "in": "query" - }, { "type": "number", - "example": 1669824037, "name": "timestamp", "in": "query" }, { "type": "string", - "example": "Hello World", "name": "title", "in": "query" }, { - "type": "string", - "example": "7725", + "type": "integer", "name": "user_id", "in": "query" }, { "type": "string", - "example": "test", - "name": "channel", - "in": "formData" + "name": "user_key", + "in": "query" }, { "type": "string", - "example": "This is a message", "name": "content", "in": "formData" }, { "type": "string", - "example": "P3TNH8mvv14fm", - "name": "key", - "in": "formData" - }, - { - "type": "string", - "example": "db8b0e6a-a08c-4646", "name": "msg_id", "in": "formData" }, { - "enum": [ - 0, - 1, - 2 - ], "type": "integer", - "example": 1, "name": "priority", "in": "formData" }, - { - "type": "string", - "example": "example-server", - "name": "sender_name", - "in": "formData" - }, { "type": "number", - "example": 1669824037, "name": "timestamp", "in": "formData" }, { "type": "string", - "example": "Hello World", "name": "title", "in": "formData" }, { - "type": "string", - "example": "7725", + "type": "integer", "name": "user_id", "in": "formData" + }, + { + "type": "string", + "name": "user_key", + "in": "formData" } ], "responses": { @@ -3135,6 +3056,7 @@ 1153, 1152, 1161, + 1162, 1171, 1201, 1202, @@ -3181,6 +3103,7 @@ "BINDFAIL_URI_PARAM", "BINDFAIL_HEADER_PARAM", "INVALID_BODY_PARAM", + "INVALID_QUERY_PARAM", "INVALID_ENUM_VALUE", "NO_TITLE", "TITLE_TOO_LONG", @@ -3591,46 +3514,26 @@ "handler.SendMessage.combined": { "type": "object", "properties": { - "channel": { - "type": "string", - "example": "test" - }, "content": { - "type": "string", - "example": "This is a message" - }, - "key": { - "type": "string", - "example": "P3TNH8mvv14fm" + "type": "string" }, "msg_id": { - "type": "string", - "example": "db8b0e6a-a08c-4646" + "type": "string" }, "priority": { - "type": "integer", - "enum": [ - 0, - 1, - 2 - ], - "example": 1 - }, - "sender_name": { - "type": "string", - "example": "example-server" + "type": "integer" }, "timestamp": { - "type": "number", - "example": 1669824037 + "type": "number" }, "title": { - "type": "string", - "example": "Hello World" + "type": "string" }, "user_id": { - "type": "string", - "example": "7725" + "type": "integer" + }, + "user_key": { + "type": "string" } } }, @@ -3659,7 +3562,7 @@ "type": "integer" }, "scn_msg_id": { - "type": "string" + "type": "integer" }, "success": { "type": "boolean" diff --git a/scnserver/swagger/swagger.yaml b/scnserver/swagger/swagger.yaml index 703bb3f..7ed4140 100644 --- a/scnserver/swagger/swagger.yaml +++ b/scnserver/swagger/swagger.yaml @@ -16,6 +16,7 @@ definitions: - 1153 - 1152 - 1161 + - 1162 - 1171 - 1201 - 1202 @@ -62,6 +63,7 @@ definitions: - BINDFAIL_URI_PARAM - BINDFAIL_HEADER_PARAM - INVALID_BODY_PARAM + - INVALID_QUERY_PARAM - INVALID_ENUM_VALUE - NO_TITLE - TITLE_TOO_LONG @@ -338,36 +340,19 @@ definitions: type: object handler.SendMessage.combined: properties: - channel: - example: test - type: string content: - example: This is a message - type: string - key: - example: P3TNH8mvv14fm type: string msg_id: - example: db8b0e6a-a08c-4646 type: string priority: - enum: - - 0 - - 1 - - 2 - example: 1 type: integer - sender_name: - example: example-server - type: string timestamp: - example: 1669824037 type: number title: - example: Hello World type: string user_id: - example: "7725" + type: integer + user_key: type: string type: object handler.SendMessage.response: @@ -387,7 +372,7 @@ definitions: quota_max: type: integer scn_msg_id: - type: string + type: integer success: type: boolean suppress_send: @@ -867,90 +852,52 @@ paths: description: All parameter can be set via query-parameter or the json body. Only UserID, UserKey and Title are required parameters: - - example: test - in: query - name: channel - type: string - - example: This is a message - in: query + - in: query name: content type: string - - example: P3TNH8mvv14fm - in: query - name: key - type: string - - example: db8b0e6a-a08c-4646 - in: query + - in: query name: msg_id type: string - - enum: - - 0 - - 1 - - 2 - example: 1 - in: query + - in: query name: priority type: integer - - example: example-server - in: query - name: sender_name - type: string - - example: 1669824037 - in: query + - in: query name: timestamp type: number - - example: Hello World - in: query + - in: query name: title type: string - - example: "7725" - in: query + - in: query name: user_id + type: integer + - in: query + name: user_key type: string - description: ' ' in: body name: post_body schema: $ref: '#/definitions/handler.SendMessage.combined' - - example: test - in: formData - name: channel - type: string - - example: This is a message - in: formData + - in: formData name: content type: string - - example: P3TNH8mvv14fm - in: formData - name: key - type: string - - example: db8b0e6a-a08c-4646 - in: formData + - in: formData name: msg_id type: string - - enum: - - 0 - - 1 - - 2 - example: 1 - in: formData + - in: formData name: priority type: integer - - example: example-server - in: formData - name: sender_name - type: string - - example: 1669824037 - in: formData + - in: formData name: timestamp type: number - - example: Hello World - in: formData + - in: formData name: title type: string - - example: "7725" - in: formData + - in: formData name: user_id + type: integer + - in: formData + name: user_key type: string responses: "200": @@ -1760,6 +1707,39 @@ paths: tags: - API-v2 /api/v2/users/{uid}: + delete: + operationId: api-user-delete + parameters: + - description: UserID + in: path + name: uid + required: true + type: string + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.User' + "400": + description: supplied values/parameters cannot be parsed / are invalid + schema: + $ref: '#/definitions/ginresp.apiError' + "401": + description: user is not authorized / has missing permissions + schema: + $ref: '#/definitions/ginresp.apiError' + "404": + description: user not found + schema: + $ref: '#/definitions/ginresp.apiError' + "500": + description: internal server error + schema: + $ref: '#/definitions/ginresp.apiError' + summary: (Self-)Deletes a user (including all entities - all messages, channels, + clients, .....) + tags: + - API-v2 get: operationId: api-user-get parameters: @@ -1793,13 +1773,24 @@ paths: tags: - API-v2 patch: - operationId: api-user-delete + description: The body-values are optional, only send the ones you want to update + operationId: api-user-update parameters: - description: UserID in: path name: uid required: true type: string + - description: Change the username (send an empty string to clear it) + in: body + name: username + schema: + type: string + - description: Send a verification of premium purchase + in: body + name: pro_token + schema: + type: string responses: "200": description: OK @@ -1821,8 +1812,7 @@ paths: description: internal server error schema: $ref: '#/definitions/ginresp.apiError' - summary: (Self-)Deletes a user (including all entities - all messages, channels, - clients, .....) + summary: (Partially) update a user tags: - API-v2 /api/v2/users/{uid}/channels: @@ -2772,90 +2762,52 @@ paths: description: All parameter can be set via query-parameter or the json body. Only UserID, UserKey and Title are required parameters: - - example: test - in: query - name: channel - type: string - - example: This is a message - in: query + - in: query name: content type: string - - example: P3TNH8mvv14fm - in: query - name: key - type: string - - example: db8b0e6a-a08c-4646 - in: query + - in: query name: msg_id type: string - - enum: - - 0 - - 1 - - 2 - example: 1 - in: query + - in: query name: priority type: integer - - example: example-server - in: query - name: sender_name - type: string - - example: 1669824037 - in: query + - in: query name: timestamp type: number - - example: Hello World - in: query + - in: query name: title type: string - - example: "7725" - in: query + - in: query name: user_id + type: integer + - in: query + name: user_key type: string - description: ' ' in: body name: post_body schema: $ref: '#/definitions/handler.SendMessage.combined' - - example: test - in: formData - name: channel - type: string - - example: This is a message - in: formData + - in: formData name: content type: string - - example: P3TNH8mvv14fm - in: formData - name: key - type: string - - example: db8b0e6a-a08c-4646 - in: formData + - in: formData name: msg_id type: string - - enum: - - 0 - - 1 - - 2 - example: 1 - in: formData + - in: formData name: priority type: integer - - example: example-server - in: formData - name: sender_name - type: string - - example: 1669824037 - in: formData + - in: formData name: timestamp type: number - - example: Hello World - in: formData + - in: formData name: title type: string - - example: "7725" - in: formData + - in: formData name: user_id + type: integer + - in: formData + name: user_key type: string responses: "200": @@ -2888,85 +2840,47 @@ paths: description: All parameter can be set via query-parameter or form-data body. Only UserID, UserKey and Title are required parameters: - - example: test - in: query - name: channel - type: string - - example: This is a message - in: query + - in: query name: content type: string - - example: P3TNH8mvv14fm - in: query - name: key - type: string - - example: db8b0e6a-a08c-4646 - in: query + - in: query name: msg_id type: string - - enum: - - 0 - - 1 - - 2 - example: 1 - in: query + - in: query name: priority type: integer - - example: example-server - in: query - name: sender_name - type: string - - example: 1669824037 - in: query + - in: query name: timestamp type: number - - example: Hello World - in: query + - in: query name: title type: string - - example: "7725" - in: query + - in: query name: user_id + type: integer + - in: query + name: user_key type: string - - example: test - in: formData - name: channel - type: string - - example: This is a message - in: formData + - in: formData name: content type: string - - example: P3TNH8mvv14fm - in: formData - name: key - type: string - - example: db8b0e6a-a08c-4646 - in: formData + - in: formData name: msg_id type: string - - enum: - - 0 - - 1 - - 2 - example: 1 - in: formData + - in: formData name: priority type: integer - - example: example-server - in: formData - name: sender_name - type: string - - example: 1669824037 - in: formData + - in: formData name: timestamp type: number - - example: Hello World - in: formData + - in: formData name: title type: string - - example: "7725" - in: formData + - in: formData name: user_id + type: integer + - in: formData + name: user_key type: string responses: "200": diff --git a/scnserver/test/user_test.go b/scnserver/test/user_test.go index 427c971..1a2af0f 100644 --- a/scnserver/test/user_test.go +++ b/scnserver/test/user_test.go @@ -203,10 +203,16 @@ func TestDeleteUser(t *testing.T) { tt.RequestAuthGet[gin.H](t, admintok, baseUrl, "/api/v2/users/"+uid) tt.RequestAuthDeleteShouldFail(t, readtok, baseUrl, "/api/v2/users/"+uid, nil, 401, apierr.USER_AUTH_FAILED) + tt.RequestAuthDeleteShouldFail(t, readtok, baseUrl, "/api/v2/users/"+uid+"?confirm=false", nil, 401, apierr.USER_AUTH_FAILED) + tt.RequestAuthDeleteShouldFail(t, readtok, baseUrl, "/api/v2/users/"+uid+"?confirm=true", nil, 401, apierr.USER_AUTH_FAILED) tt.RequestAuthDeleteShouldFail(t, sendtok, baseUrl, "/api/v2/users/"+uid, nil, 401, apierr.USER_AUTH_FAILED) + tt.RequestAuthDeleteShouldFail(t, sendtok, baseUrl, "/api/v2/users/"+uid+"?confirm=false", nil, 401, apierr.USER_AUTH_FAILED) + tt.RequestAuthDeleteShouldFail(t, sendtok, baseUrl, "/api/v2/users/"+uid+"?confirm=true", nil, 401, apierr.USER_AUTH_FAILED) - tt.RequestAuthDelete[tt.Void](t, admintok, baseUrl, "/api/v2/users/"+uid, nil) + tt.RequestAuthDeleteShouldFail(t, admintok, baseUrl, "/api/v2/users/"+uid, nil, 400, apierr.INVALID_QUERY_PARAM) + tt.RequestAuthDeleteShouldFail(t, admintok, baseUrl, "/api/v2/users/"+uid+"?confirm=false", nil, 400, apierr.INVALID_QUERY_PARAM) + tt.RequestAuthDelete[tt.Void](t, admintok, baseUrl, "/api/v2/users/"+uid+"?confirm=true", nil) tt.RequestAuthGetShouldFail(t, admintok, baseUrl, "/api/v2/users/"+uid, 401, apierr.USER_AUTH_FAILED) }