public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
* [pve-devel] [PATCH flutter-repositories] some improvements/fixes for the app
@ 2024-04-15 10:30 Dominik Csapak
  2024-04-15 10:30 ` [pve-devel] [PATCH proxmox-dart-api-client 1/1] client: correctly set parameter for node actions Dominik Csapak
                   ` (8 more replies)
  0 siblings, 9 replies; 13+ messages in thread
From: Dominik Csapak @ 2024-04-15 10:30 UTC (permalink / raw)
  To: pve-devel

some improvements:
* better error handling for saving the password
* adding node power actions (shutdown/restart)
* change webview so we can show it without a trusted certificate
* improve back button behavior

flutter-frontend patch 1 depends on login-manager patch 2
flutter-frontend patch 4 depends on dart-api-client to work correctly

this is intended on top of my last series:
https://lists.proxmox.com/pipermail/pve-devel/2024-April/062891.html

proxmox-dart-api-client:

Dominik Csapak (1):
  client: correctly set parameter for node actions

 lib/src/client.dart | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

proxmox-login-manager:

Dominik Csapak (2):
  login: show custom alert dialog for password saving errors
  general settings: add trustedFingerprints list

 lib/proxmox_general_settings_model.dart |  6 ++++-
 lib/proxmox_login_form.dart             | 32 +++++++++++++++++++++----
 2 files changed, 33 insertions(+), 5 deletions(-)

pve-flutter-frontend:

Dominik Csapak (5):
  console: use flutter inappwebview as webview
  console: wrap console with appbar
  node overview: use correct color for rrd icons
  nove overview: add power settings menu
  improve back button behavior

 lib/bloc/pve_node_overview_bloc.dart          |  11 ++
 lib/pages/main_layout_slim.dart               |   4 +-
 lib/widgets/pve_console_menu_widget.dart      | 139 ++++++++++++++++--
 lib/widgets/pve_node_overview.dart            |  36 ++++-
 .../pve_node_power_settings_widget.dart       |  59 ++++++++
 pubspec.lock                                  |  42 ++----
 pubspec.yaml                                  |   3 +-
 7 files changed, 240 insertions(+), 54 deletions(-)
 create mode 100644 lib/widgets/pve_node_power_settings_widget.dart

-- 
2.39.2





^ permalink raw reply	[flat|nested] 13+ messages in thread

* [pve-devel] [PATCH proxmox-dart-api-client 1/1] client: correctly set parameter for node actions
  2024-04-15 10:30 [pve-devel] [PATCH flutter-repositories] some improvements/fixes for the app Dominik Csapak
@ 2024-04-15 10:30 ` Dominik Csapak
  2024-04-16 15:07   ` [pve-devel] applied: " Thomas Lamprecht
  2024-04-15 10:30 ` [pve-devel] [PATCH proxmox-login-manager 1/2] login: show custom alert dialog for password saving errors Dominik Csapak
                   ` (7 subsequent siblings)
  8 siblings, 1 reply; 13+ messages in thread
From: Dominik Csapak @ 2024-04-15 10:30 UTC (permalink / raw)
  To: pve-devel

using '?' in the url will be escaped and not used for the get
parameters. Instead add the command to the parameters map.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 lib/src/client.dart | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/lib/src/client.dart b/lib/src/client.dart
index 447335e..f597c28 100644
--- a/lib/src/client.dart
+++ b/lib/src/client.dart
@@ -366,7 +366,9 @@ class ProxmoxApiClient extends http.BaseClient {
             '/api2/json/nodes/$targetNode/$type/$guestId/status/${action.name}';
         break;
       case 'node':
-        url = '/api2/json/nodes/$targetNode/status/?command=${action.name}';
+        parameters ??= {};
+        parameters["command"] = action.name;
+        url = '/api2/json/nodes/$targetNode/status';
         break;
       default:
         throw ArgumentError.value(
-- 
2.39.2





^ permalink raw reply	[flat|nested] 13+ messages in thread

* [pve-devel] [PATCH proxmox-login-manager 1/2] login: show custom alert dialog for password saving errors
  2024-04-15 10:30 [pve-devel] [PATCH flutter-repositories] some improvements/fixes for the app Dominik Csapak
  2024-04-15 10:30 ` [pve-devel] [PATCH proxmox-dart-api-client 1/1] client: correctly set parameter for node actions Dominik Csapak
@ 2024-04-15 10:30 ` Dominik Csapak
  2024-04-16 15:08   ` [pve-devel] applied: " Thomas Lamprecht
  2024-04-15 10:30 ` [pve-devel] [PATCH proxmox-login-manager 2/2] general settings: add trustedFingerprints list Dominik Csapak
                   ` (6 subsequent siblings)
  8 siblings, 1 reply; 13+ messages in thread
From: Dominik Csapak @ 2024-04-15 10:30 UTC (permalink / raw)
  To: pve-devel

in those cases, we sometimes get ugly stack traces/exceptions, so
instead of just showing that and aborting, show a custom dialog with
the basic info that we could not save the password (+details box with
the original error) and continue instead.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 lib/proxmox_login_form.dart | 32 ++++++++++++++++++++++++++++----
 1 file changed, 28 insertions(+), 4 deletions(-)

diff --git a/lib/proxmox_login_form.dart b/lib/proxmox_login_form.dart
index e31d0c0..9c25126 100644
--- a/lib/proxmox_login_form.dart
+++ b/lib/proxmox_login_form.dart
@@ -527,10 +527,34 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
       }
 
       if (id != null) {
-        if (savePW) {
-          await savePassword(id, enteredPassword);
-        } else if (deletePW) {
-          await deletePassword(id);
+        try {
+          if (savePW) {
+            await savePassword(id, enteredPassword);
+          } else if (deletePW) {
+            await deletePassword(id);
+          }
+        } catch (e) {
+          await showDialog(
+              context: context,
+              builder: (context) => AlertDialog(
+                    title: const Text('Password saving error'),
+                    scrollable: true,
+                    content: Column(
+                      mainAxisSize: MainAxisSize.min,
+                      children: [
+                        Text('Could not save or delete password.'),
+                        ExpansionTile(
+                          title: const Text('Details'),
+                          children: [Text(e.toString())],
+                        )
+                      ],
+                    ),
+                    actions: [
+                      TextButton(
+                          onPressed: () => Navigator.of(context).pop(),
+                          child: const Text('Continue')),
+                    ],
+                  ));
         }
       }
       await loginStorage.saveToDisk();
-- 
2.39.2





^ permalink raw reply	[flat|nested] 13+ messages in thread

* [pve-devel] [PATCH proxmox-login-manager 2/2] general settings: add trustedFingerprints list
  2024-04-15 10:30 [pve-devel] [PATCH flutter-repositories] some improvements/fixes for the app Dominik Csapak
  2024-04-15 10:30 ` [pve-devel] [PATCH proxmox-dart-api-client 1/1] client: correctly set parameter for node actions Dominik Csapak
  2024-04-15 10:30 ` [pve-devel] [PATCH proxmox-login-manager 1/2] login: show custom alert dialog for password saving errors Dominik Csapak
@ 2024-04-15 10:30 ` Dominik Csapak
  2024-04-15 10:30 ` [pve-devel] [PATCH pve-flutter-frontend 1/5] console: use flutter inappwebview as webview Dominik Csapak
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 13+ messages in thread
From: Dominik Csapak @ 2024-04-15 10:30 UTC (permalink / raw)
  To: pve-devel

where we can save the fingerprints the user wants to trust

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 lib/proxmox_general_settings_model.dart | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/lib/proxmox_general_settings_model.dart b/lib/proxmox_general_settings_model.dart
index 767bdaf..4c8305b 100644
--- a/lib/proxmox_general_settings_model.dart
+++ b/lib/proxmox_general_settings_model.dart
@@ -1,5 +1,6 @@
 import 'dart:convert';
 
+import 'package:built_collection/built_collection.dart';
 import 'package:built_value/built_value.dart';
 import 'package:built_value/serializer.dart';
 import 'package:proxmox_login_manager/serializers.dart';
@@ -11,6 +12,7 @@ abstract class ProxmoxGeneralSettingsModel
     implements
         Built<ProxmoxGeneralSettingsModel, ProxmoxGeneralSettingsModelBuilder> {
   bool? get sslValidation;
+  BuiltList<String>? get trustedFingerprints;
 
   ProxmoxGeneralSettingsModel._();
   factory ProxmoxGeneralSettingsModel(
@@ -18,7 +20,9 @@ abstract class ProxmoxGeneralSettingsModel
       _$ProxmoxGeneralSettingsModel;
 
   factory ProxmoxGeneralSettingsModel.defaultValues() =>
-      ProxmoxGeneralSettingsModel((b) => b..sslValidation = true);
+      ProxmoxGeneralSettingsModel((b) => b
+        ..sslValidation = true
+        ..trustedFingerprints = ListBuilder());
 
   Object toJson() {
     return serializers.serializeWith(
-- 
2.39.2





^ permalink raw reply	[flat|nested] 13+ messages in thread

* [pve-devel] [PATCH pve-flutter-frontend 1/5] console: use flutter inappwebview as webview
  2024-04-15 10:30 [pve-devel] [PATCH flutter-repositories] some improvements/fixes for the app Dominik Csapak
                   ` (2 preceding siblings ...)
  2024-04-15 10:30 ` [pve-devel] [PATCH proxmox-login-manager 2/2] general settings: add trustedFingerprints list Dominik Csapak
@ 2024-04-15 10:30 ` Dominik Csapak
  2024-04-15 10:30 ` [pve-devel] [PATCH pve-flutter-frontend 2/5] console: wrap console with appbar Dominik Csapak
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 13+ messages in thread
From: Dominik Csapak @ 2024-04-15 10:30 UTC (permalink / raw)
  To: pve-devel

instead of flutter webview. This new dependency does not have the
limitations of the "official" flutter webview. We now can ignore
https errors and even get some info on the certificate.

We use that to now show a 'do you want to trust this cert' dialog with
the fingerprint of the server, which the user can then either trust or
abort.

When pressing yes, we save that fingerprint in the shared preferences so
that we don't have to ask the next time.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
this depends on the login-manager patch to add the 'trustedFingerprints' list

 lib/widgets/pve_console_menu_widget.dart | 128 ++++++++++++++++++++---
 pubspec.lock                             |  42 ++------
 pubspec.yaml                             |   3 +-
 3 files changed, 126 insertions(+), 47 deletions(-)

diff --git a/lib/widgets/pve_console_menu_widget.dart b/lib/widgets/pve_console_menu_widget.dart
index 767a51c..243baf1 100644
--- a/lib/widgets/pve_console_menu_widget.dart
+++ b/lib/widgets/pve_console_menu_widget.dart
@@ -6,7 +6,9 @@ import 'package:flutter/material.dart';
 import 'package:flutter/services.dart';
 import 'package:path_provider/path_provider.dart';
 import 'package:proxmox_dart_api_client/proxmox_dart_api_client.dart';
-import 'package:webview_flutter/webview_flutter.dart';
+import 'package:proxmox_login_manager/proxmox_general_settings_model.dart';
+import 'package:flutter_inappwebview/flutter_inappwebview.dart';
+import 'package:crypto/crypto.dart';
 
 class PveConsoleMenu extends StatelessWidget {
   static const platform =
@@ -104,8 +106,7 @@ class PveConsoleMenu extends StatelessWidget {
                   "noVNC Console", // xterm.js doesn't work that well on mobile
                   style: TextStyle(fontWeight: FontWeight.bold),
                 ),
-                subtitle: const Text(
-                    "Open console view (requires trusted SSL certificate)"),
+                subtitle: const Text("Open console view"),
                 onTap: () async {
                   if (Platform.isAndroid) {
                     if (['qemu', 'lxc'].contains(type)) {
@@ -209,6 +210,8 @@ class PVEWebConsole extends StatefulWidget {
 }
 
 class PVEWebConsoleState extends State<PVEWebConsole> {
+  InAppWebViewController? webViewController;
+
   @override
   Widget build(BuildContext context) {
     final ticket = widget.apiClient.credentials.ticket!;
@@ -222,24 +225,123 @@ class PVEWebConsoleState extends State<PVEWebConsole> {
     } else {
       consoleUrl += "&console=shell";
     }
-
-    final controller = WebViewController()
-      ..setJavaScriptMode(JavaScriptMode.unrestricted)
-      ..setBackgroundColor(Theme.of(context).colorScheme.background);
+    WidgetsFlutterBinding.ensureInitialized();
 
     return FutureBuilder(
-        future: WebViewCookieManager().setCookie(WebViewCookie(
+        future: CookieManager.instance().setCookie(
+          url: Uri.parse(consoleUrl),
           name: 'PVEAuthCookie',
           value: ticket,
-          domain: baseUrl.origin,
-        )),
+        ),
         builder: (context, snapshot) {
-          controller.loadRequest(Uri.parse(consoleUrl));
           return SafeArea(
-            child: WebViewWidget(
-              controller: controller,
+            child: InAppWebView(
+              onReceivedServerTrustAuthRequest: (controller, challenge) async {
+                final cert = challenge.protectionSpace.sslCertificate;
+                final certBytes = cert?.x509Certificate?.encoded;
+                final sslError = challenge.protectionSpace.sslError?.message;
+
+                String? issuedTo = cert?.issuedTo?.CName.toString();
+                String? hash = certBytes != null
+                    ? sha256.convert(certBytes).toString()
+                    : null;
+
+                final settings =
+                    await ProxmoxGeneralSettingsModel.fromLocalStorage();
+
+                bool trust = false;
+                if (hash != null && settings.trustedFingerprints != null) {
+                  trust = settings.trustedFingerprints!.contains(hash);
+                }
+
+                if (!trust) {
+                  // format hash to '01:23:...' format
+                  String? formattedHash = hash?.toUpperCase().replaceAllMapped(
+                      RegExp(r"[a-zA-Z0-9]{2}"),
+                      (match) => "${match.group(0)}:");
+                  formattedHash = formattedHash?.substring(
+                      0, formattedHash.length - 1); // remove last ':'
+
+                  if (context.mounted) {
+                    trust = await showTLSWarning(
+                        context,
+                        sslError ?? 'An unknown TLS error has occurred',
+                        issuedTo ?? 'unknown',
+                        formattedHash ?? 'unknown');
+                  }
+                }
+
+                // save Fingerprint
+                if (trust && hash != null) {
+                  await settings
+                      .rebuild((b) => b..trustedFingerprints.add(hash))
+                      .toLocalStorage();
+                  print(settings.toJson());
+                }
+
+                final action = trust
+                    ? ServerTrustAuthResponseAction.PROCEED
+                    : ServerTrustAuthResponseAction.CANCEL;
+                return ServerTrustAuthResponse(action: action);
+              },
+              onWebViewCreated: (controller) {
+                webViewController = controller;
+                controller.loadUrl(
+                    urlRequest: URLRequest(url: Uri.parse(consoleUrl)));
+              },
             ),
           );
         });
   }
+
+  Future<bool> showTLSWarning(BuildContext context, String sslError,
+      String issuedTo, String hash) async {
+    return await showDialog(
+        context: context,
+        barrierDismissible: false,
+        builder: (BuildContext context) {
+          return PopScope(
+            // prevent back button from canceling this callback
+            canPop: false,
+            child: AlertDialog(
+                title: const Text('An TLS error has occurred:'),
+                content: Column(
+                  crossAxisAlignment: CrossAxisAlignment.start,
+                  mainAxisSize: MainAxisSize.min,
+                  children: [
+                    ListTile(
+                      title: const Text('Error Message:'),
+                      subtitle: Text(sslError),
+                    ),
+                    const ListTile(
+                      title: Text('Certificate Information:'),
+                    ),
+                    ListTile(
+                      title: const Text('Issued to:'),
+                      subtitle: Text(issuedTo),
+                    ),
+                    ListTile(
+                      title: const Text('Fingerprint:'),
+                      subtitle: Text(hash),
+                    ),
+                    const Text(''), // spacer
+                    const Text('Do you want to continue?'),
+                  ],
+                ),
+                actions: <Widget>[
+                  TextButton(
+                      onPressed: () {
+                        Navigator.of(context).pop(false);
+                        Navigator.of(context).pop();
+                      },
+                      child: const Text('No')),
+                  TextButton(
+                      onPressed: () {
+                        Navigator.of(context).pop(true);
+                      },
+                      child: const Text('Yes'))
+                ]),
+          );
+        });
+  }
 }
diff --git a/pubspec.lock b/pubspec.lock
index 7e39fba..94c0656 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -170,7 +170,7 @@ packages:
     source: hosted
     version: "3.1.1"
   crypto:
-    dependency: transitive
+    dependency: "direct main"
     description:
       name: crypto
       sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
@@ -230,6 +230,14 @@ packages:
     description: flutter
     source: sdk
     version: "0.0.0"
+  flutter_inappwebview:
+    dependency: "direct main"
+    description:
+      name: flutter_inappwebview
+      sha256: d198297060d116b94048301ee6749cd2e7d03c1f2689783f52d210a6b7aba350
+      url: "https://pub.dev"
+    source: hosted
+    version: "5.8.0"
   flutter_lints:
     dependency: "direct dev"
     description:
@@ -819,38 +827,6 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "2.4.5"
-  webview_flutter:
-    dependency: "direct main"
-    description:
-      name: webview_flutter
-      sha256: "25e1b6e839e8cbfbd708abc6f85ed09d1727e24e08e08c6b8590d7c65c9a8932"
-      url: "https://pub.dev"
-    source: hosted
-    version: "4.7.0"
-  webview_flutter_android:
-    dependency: transitive
-    description:
-      name: webview_flutter_android
-      sha256: f038ee2fae73b509dde1bc9d2c5a50ca92054282de17631a9a3d515883740934
-      url: "https://pub.dev"
-    source: hosted
-    version: "3.16.0"
-  webview_flutter_platform_interface:
-    dependency: transitive
-    description:
-      name: webview_flutter_platform_interface
-      sha256: d937581d6e558908d7ae3dc1989c4f87b786891ab47bb9df7de548a151779d8d
-      url: "https://pub.dev"
-    source: hosted
-    version: "2.10.0"
-  webview_flutter_wkwebview:
-    dependency: transitive
-    description:
-      name: webview_flutter_wkwebview
-      sha256: f12f8d8a99784b863e8b85e4a9a5e3cf1839d6803d2c0c3e0533a8f3c5a992a7
-      url: "https://pub.dev"
-    source: hosted
-    version: "3.13.0"
   win32:
     dependency: transitive
     description:
diff --git a/pubspec.yaml b/pubspec.yaml
index 065749c..976dbb1 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -34,7 +34,6 @@ dependencies:
   url_launcher: ^6.0.17
   intl: ^0.19.0
   path_provider: ^2.0.8
-  webview_flutter: ^4.2.0
   proxmox_dart_api_client:
     path: ../proxmox_dart_api_client
   proxmox_login_manager:
@@ -42,6 +41,8 @@ dependencies:
   collection: ^1.15.0-nullsafety.4
 
   shared_preferences: any
+  flutter_inappwebview: ^5.8.0
+  crypto: ^3.0.3
 dev_dependencies:
   flutter_test:
     sdk: flutter
-- 
2.39.2





^ permalink raw reply	[flat|nested] 13+ messages in thread

* [pve-devel] [PATCH pve-flutter-frontend 2/5] console: wrap console with appbar
  2024-04-15 10:30 [pve-devel] [PATCH flutter-repositories] some improvements/fixes for the app Dominik Csapak
                   ` (3 preceding siblings ...)
  2024-04-15 10:30 ` [pve-devel] [PATCH pve-flutter-frontend 1/5] console: use flutter inappwebview as webview Dominik Csapak
@ 2024-04-15 10:30 ` Dominik Csapak
  2024-04-15 10:30 ` [pve-devel] [PATCH pve-flutter-frontend 3/5] node overview: use correct color for rrd icons Dominik Csapak
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 13+ messages in thread
From: Dominik Csapak @ 2024-04-15 10:30 UTC (permalink / raw)
  To: pve-devel

so one can return to the previous view without having to use the back
button of the device.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 lib/widgets/pve_console_menu_widget.dart | 107 ++++++++++++-----------
 1 file changed, 57 insertions(+), 50 deletions(-)

diff --git a/lib/widgets/pve_console_menu_widget.dart b/lib/widgets/pve_console_menu_widget.dart
index 243baf1..9c91d30 100644
--- a/lib/widgets/pve_console_menu_widget.dart
+++ b/lib/widgets/pve_console_menu_widget.dart
@@ -234,63 +234,70 @@ class PVEWebConsoleState extends State<PVEWebConsole> {
           value: ticket,
         ),
         builder: (context, snapshot) {
-          return SafeArea(
-            child: InAppWebView(
-              onReceivedServerTrustAuthRequest: (controller, challenge) async {
-                final cert = challenge.protectionSpace.sslCertificate;
-                final certBytes = cert?.x509Certificate?.encoded;
-                final sslError = challenge.protectionSpace.sslError?.message;
+          return Scaffold(
+              appBar: AppBar(
+                title: const Text("Console"),
+              ),
+              body: SafeArea(
+                child: InAppWebView(
+                  onReceivedServerTrustAuthRequest:
+                      (controller, challenge) async {
+                    final cert = challenge.protectionSpace.sslCertificate;
+                    final certBytes = cert?.x509Certificate?.encoded;
+                    final sslError =
+                        challenge.protectionSpace.sslError?.message;
 
-                String? issuedTo = cert?.issuedTo?.CName.toString();
-                String? hash = certBytes != null
-                    ? sha256.convert(certBytes).toString()
-                    : null;
+                    String? issuedTo = cert?.issuedTo?.CName.toString();
+                    String? hash = certBytes != null
+                        ? sha256.convert(certBytes).toString()
+                        : null;
 
-                final settings =
-                    await ProxmoxGeneralSettingsModel.fromLocalStorage();
+                    final settings =
+                        await ProxmoxGeneralSettingsModel.fromLocalStorage();
 
-                bool trust = false;
-                if (hash != null && settings.trustedFingerprints != null) {
-                  trust = settings.trustedFingerprints!.contains(hash);
-                }
+                    bool trust = false;
+                    if (hash != null && settings.trustedFingerprints != null) {
+                      trust = settings.trustedFingerprints!.contains(hash);
+                    }
 
-                if (!trust) {
-                  // format hash to '01:23:...' format
-                  String? formattedHash = hash?.toUpperCase().replaceAllMapped(
-                      RegExp(r"[a-zA-Z0-9]{2}"),
-                      (match) => "${match.group(0)}:");
-                  formattedHash = formattedHash?.substring(
-                      0, formattedHash.length - 1); // remove last ':'
+                    if (!trust) {
+                      // format hash to '01:23:...' format
+                      String? formattedHash = hash
+                          ?.toUpperCase()
+                          .replaceAllMapped(RegExp(r"[a-zA-Z0-9]{2}"),
+                              (match) => "${match.group(0)}:");
+                      formattedHash = formattedHash?.substring(
+                          0, formattedHash.length - 1); // remove last ':'
 
-                  if (context.mounted) {
-                    trust = await showTLSWarning(
-                        context,
-                        sslError ?? 'An unknown TLS error has occurred',
-                        issuedTo ?? 'unknown',
-                        formattedHash ?? 'unknown');
-                  }
-                }
+                      if (context.mounted) {
+                        trust = await showTLSWarning(
+                            context,
+                            sslError ?? 'An unknown TLS error has occurred',
+                            issuedTo ?? 'unknown',
+                            formattedHash ?? 'unknown');
+                      }
+                    }
 
-                // save Fingerprint
-                if (trust && hash != null) {
-                  await settings
-                      .rebuild((b) => b..trustedFingerprints.add(hash))
-                      .toLocalStorage();
-                  print(settings.toJson());
-                }
+                    // save Fingerprint
+                    if (trust && hash != null) {
+                      await settings
+                          .rebuild((b) => b..trustedFingerprints.add(hash))
+                          .toLocalStorage();
+                      print(settings.toJson());
+                    }
 
-                final action = trust
-                    ? ServerTrustAuthResponseAction.PROCEED
-                    : ServerTrustAuthResponseAction.CANCEL;
-                return ServerTrustAuthResponse(action: action);
-              },
-              onWebViewCreated: (controller) {
-                webViewController = controller;
-                controller.loadUrl(
-                    urlRequest: URLRequest(url: Uri.parse(consoleUrl)));
-              },
-            ),
-          );
+                    final action = trust
+                        ? ServerTrustAuthResponseAction.PROCEED
+                        : ServerTrustAuthResponseAction.CANCEL;
+                    return ServerTrustAuthResponse(action: action);
+                  },
+                  onWebViewCreated: (controller) {
+                    webViewController = controller;
+                    controller.loadUrl(
+                        urlRequest: URLRequest(url: Uri.parse(consoleUrl)));
+                  },
+                ),
+              ));
         });
   }
 
-- 
2.39.2





^ permalink raw reply	[flat|nested] 13+ messages in thread

* [pve-devel] [PATCH pve-flutter-frontend 3/5] node overview: use correct color for rrd icons
  2024-04-15 10:30 [pve-devel] [PATCH flutter-repositories] some improvements/fixes for the app Dominik Csapak
                   ` (4 preceding siblings ...)
  2024-04-15 10:30 ` [pve-devel] [PATCH pve-flutter-frontend 2/5] console: wrap console with appbar Dominik Csapak
@ 2024-04-15 10:30 ` Dominik Csapak
  2024-04-15 10:30 ` [pve-devel] [PATCH pve-flutter-frontend 4/5] nove overview: add power settings menu Dominik Csapak
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 13+ messages in thread
From: Dominik Csapak @ 2024-04-15 10:30 UTC (permalink / raw)
  To: pve-devel

same as the guests use for that.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 lib/widgets/pve_node_overview.dart | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/lib/widgets/pve_node_overview.dart b/lib/widgets/pve_node_overview.dart
index 020eeba..219d498 100644
--- a/lib/widgets/pve_node_overview.dart
+++ b/lib/widgets/pve_node_overview.dart
@@ -46,6 +46,8 @@ class PveNodeOverview extends StatelessWidget {
       builder: (context, state) {
         final status = state.status;
         final rrd = state.rrdData.where((e) => e.time != null);
+        final fgColor =
+            Theme.of(context).colorScheme.onPrimary.withOpacity(0.75);
         return SafeArea(
           child: Scaffold(
             appBar: AppBar(
@@ -97,7 +99,7 @@ class PveNodeOverview extends StatelessWidget {
                                           (e) => Point(
                                               e.time!.millisecondsSinceEpoch,
                                               (e.cpu ?? 0) * 100.0)),
-                                      icon: const Icon(Icons.memory),
+                                      icon: Icon(Icons.memory, color: fgColor),
                                       bottomRight: pageIndicator,
                                       dataRenderer: (data) =>
                                           '${data.toStringAsFixed(2)} %',
@@ -112,7 +114,8 @@ class PveNodeOverview extends StatelessWidget {
                                       data: rrd.map((e) => Point(
                                           e.time!.millisecondsSinceEpoch,
                                           e.memused ?? 0)),
-                                      icon: const Icon(FontAwesomeIcons.memory),
+                                      icon: Icon(FontAwesomeIcons.memory,
+                                          color: fgColor),
                                       bottomRight: pageIndicator,
                                       dataRenderer: (data) =>
                                           Renderers.formatSize(data),
@@ -128,7 +131,7 @@ class PveNodeOverview extends StatelessWidget {
                                       data: rrd.map((e) => Point(
                                           e.time!.millisecondsSinceEpoch,
                                           e.iowait ?? 0)),
-                                      icon: const Icon(Icons.timer),
+                                      icon: Icon(Icons.timer, color: fgColor),
                                       bottomRight: pageIndicator,
                                       dataRenderer: (data) =>
                                           data.toStringAsFixed(3),
@@ -144,7 +147,8 @@ class PveNodeOverview extends StatelessWidget {
                                       data: rrd.map((e) => Point(
                                           e.time!.millisecondsSinceEpoch,
                                           e.loadavg ?? 0)),
-                                      icon: const Icon(Icons.show_chart),
+                                      icon: Icon(Icons.show_chart,
+                                          color: fgColor),
                                       bottomRight: pageIndicator,
                                       dataRenderer: (data) =>
                                           data.toStringAsFixed(2),
-- 
2.39.2





^ permalink raw reply	[flat|nested] 13+ messages in thread

* [pve-devel] [PATCH pve-flutter-frontend 4/5] nove overview: add power settings menu
  2024-04-15 10:30 [pve-devel] [PATCH flutter-repositories] some improvements/fixes for the app Dominik Csapak
                   ` (5 preceding siblings ...)
  2024-04-15 10:30 ` [pve-devel] [PATCH pve-flutter-frontend 3/5] node overview: use correct color for rrd icons Dominik Csapak
@ 2024-04-15 10:30 ` Dominik Csapak
  2024-04-16 15:11   ` Thomas Lamprecht
  2024-04-15 10:30 ` [pve-devel] [PATCH pve-flutter-frontend 5/5] improve back button behavior Dominik Csapak
  2024-04-16 15:11 ` [pve-devel] applied: [PATCH flutter-repositories] some improvements/fixes for the app Thomas Lamprecht
  8 siblings, 1 reply; 13+ messages in thread
From: Dominik Csapak @ 2024-04-15 10:30 UTC (permalink / raw)
  To: pve-devel

similar to how it works for qemu

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
this depends on the dart-api-client patch, otherwise the url is wrong

 lib/bloc/pve_node_overview_bloc.dart          | 11 ++++
 lib/widgets/pve_node_overview.dart            | 24 ++++++++
 .../pve_node_power_settings_widget.dart       | 59 +++++++++++++++++++
 3 files changed, 94 insertions(+)
 create mode 100644 lib/widgets/pve_node_power_settings_widget.dart

diff --git a/lib/bloc/pve_node_overview_bloc.dart b/lib/bloc/pve_node_overview_bloc.dart
index caeb1c4..2f07ef9 100644
--- a/lib/bloc/pve_node_overview_bloc.dart
+++ b/lib/bloc/pve_node_overview_bloc.dart
@@ -52,9 +52,20 @@ class PveNodeOverviewBloc
       final disks = await apiClient.getNodeDisksList(nodeID);
       yield latestState.rebuild((b) => b..disks.replace(disks));
     }
+    if (event is PerformNodeAction) {
+      await apiClient.doResourceAction(nodeID, '', 'node', event.action,
+          parameters: <String, String>{});
+      yield latestState;
+    }
   }
 }
 
 abstract class PveNodeOverviewEvent {}
 
 class UpdateNodeStatus extends PveNodeOverviewEvent {}
+
+class PerformNodeAction extends PveNodeOverviewEvent {
+  final PveClusterResourceAction action;
+
+  PerformNodeAction(this.action);
+}
diff --git a/lib/widgets/pve_node_overview.dart b/lib/widgets/pve_node_overview.dart
index 219d498..a5d79a3 100644
--- a/lib/widgets/pve_node_overview.dart
+++ b/lib/widgets/pve_node_overview.dart
@@ -8,6 +8,7 @@ import 'package:pve_flutter_frontend/states/pve_node_overview_state.dart';
 import 'package:pve_flutter_frontend/states/pve_task_log_state.dart';
 import 'package:pve_flutter_frontend/utils/renderers.dart';
 import 'package:pve_flutter_frontend/utils/utils.dart';
+import 'package:pve_flutter_frontend/widgets/pve_node_power_settings_widget.dart';
 import 'package:pve_flutter_frontend/widgets/proxmox_capacity_indicator.dart';
 import 'package:pve_flutter_frontend/widgets/proxmox_stream_builder_widget.dart';
 import 'package:pve_flutter_frontend/widgets/pve_action_card_widget.dart';
@@ -189,6 +190,16 @@ class PveNodeOverview extends StatelessWidget {
                       child: Row(
                         mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                         children: <Widget>[
+                          ActionCard(
+                            icon: const Icon(
+                              Icons.power_settings_new,
+                              size: 55,
+                              color: Colors.white24,
+                            ),
+                            title: 'Power Settings',
+                            onTap: () =>
+                                showPowerMenuBottomSheet(context, nBloc),
+                          ),
                           ActionCard(
                             icon: const Icon(
                               Icons.queue_play_next,
@@ -439,4 +450,17 @@ class PveNodeOverview extends StatelessWidget {
       },
     );
   }
+
+  Future<T?> showPowerMenuBottomSheet<T>(
+      BuildContext context, PveNodeOverviewBloc nodeBloc) async {
+    return showModalBottomSheet(
+      shape: const RoundedRectangleBorder(
+          borderRadius: BorderRadius.vertical(top: Radius.circular(10))),
+      context: context,
+      builder: (context) => Provider.value(
+        value: nodeBloc,
+        child: const PveNodePowerSettings(),
+      ),
+    );
+  }
 }
diff --git a/lib/widgets/pve_node_power_settings_widget.dart b/lib/widgets/pve_node_power_settings_widget.dart
new file mode 100644
index 0000000..f8d35d1
--- /dev/null
+++ b/lib/widgets/pve_node_power_settings_widget.dart
@@ -0,0 +1,59 @@
+import 'package:flutter/material.dart';
+import 'package:provider/provider.dart';
+import 'package:proxmox_dart_api_client/proxmox_dart_api_client.dart';
+import 'package:pve_flutter_frontend/bloc/pve_node_overview_bloc.dart';
+import 'package:pve_flutter_frontend/states/pve_node_overview_state.dart';
+import 'package:pve_flutter_frontend/widgets/proxmox_stream_builder_widget.dart';
+
+class PveNodePowerSettings extends StatelessWidget {
+  const PveNodePowerSettings({
+    super.key,
+  });
+  @override
+  Widget build(BuildContext context) {
+    final bloc = Provider.of<PveNodeOverviewBloc>(context);
+    return ProxmoxStreamBuilder<PveNodeOverviewBloc, PveNodeOverviewState>(
+        bloc: bloc,
+        builder: (context, state) {
+          return SafeArea(
+            child: SingleChildScrollView(
+              child: Container(
+                constraints: BoxConstraints(
+                    minHeight: MediaQuery.of(context).size.height / 3),
+                child: Column(
+                  mainAxisSize: MainAxisSize.min,
+                  children: <Widget>[
+                    ListTile(
+                      leading: const Icon(Icons.autorenew),
+                      title: const Text(
+                        "Reboot",
+                        style: TextStyle(fontWeight: FontWeight.bold),
+                      ),
+                      subtitle: const Text("Reboot Node"),
+                      onTap: () => action(
+                          context, PveClusterResourceAction.reboot, bloc),
+                    ),
+                    ListTile(
+                      leading: const Icon(Icons.power_settings_new),
+                      title: const Text(
+                        "Shutdown",
+                        style: TextStyle(fontWeight: FontWeight.bold),
+                      ),
+                      subtitle: const Text("Shutdown Node"),
+                      onTap: () => action(
+                          context, PveClusterResourceAction.shutdown, bloc),
+                    ),
+                  ],
+                ),
+              ),
+            ),
+          );
+        });
+  }
+
+  void action(BuildContext context, PveClusterResourceAction action,
+      PveNodeOverviewBloc bloc) {
+    bloc.events.add(PerformNodeAction(action));
+    Navigator.of(context).pop();
+  }
+}
-- 
2.39.2





^ permalink raw reply	[flat|nested] 13+ messages in thread

* [pve-devel] [PATCH pve-flutter-frontend 5/5] improve back button behavior
  2024-04-15 10:30 [pve-devel] [PATCH flutter-repositories] some improvements/fixes for the app Dominik Csapak
                   ` (6 preceding siblings ...)
  2024-04-15 10:30 ` [pve-devel] [PATCH pve-flutter-frontend 4/5] nove overview: add power settings menu Dominik Csapak
@ 2024-04-15 10:30 ` Dominik Csapak
  2024-04-16 15:11 ` [pve-devel] applied: [PATCH flutter-repositories] some improvements/fixes for the app Thomas Lamprecht
  8 siblings, 0 replies; 13+ messages in thread
From: Dominik Csapak @ 2024-04-15 10:30 UTC (permalink / raw)
  To: pve-devel

by using 'SystemNavigator.pop()' on the top level page when pressing the
back button. On Android this minimizes/closes the app an presents
whatever was shown before.

This fixes an issue where we would show a blank screen when pressing the
back button on the dashboard.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
not sure if this is the fix that we want, but i did not find any other
way. It seems that using PopScope does not really handle popping the
last route correctly. Also, this will not work for iOS (but since we
don't provide the app there, probably irrelevant)

 lib/pages/main_layout_slim.dart | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/lib/pages/main_layout_slim.dart b/lib/pages/main_layout_slim.dart
index ec80dba..1761b26 100644
--- a/lib/pages/main_layout_slim.dart
+++ b/lib/pages/main_layout_slim.dart
@@ -1,5 +1,6 @@
 import 'package:built_collection/built_collection.dart';
 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';
@@ -74,7 +75,8 @@ class _MainLayoutSlimState extends State<MainLayoutSlim> {
             if (pageSelector.value != 0) pageSelector.add(0);
             return;
           }
-          if (mounted) Navigator.of(context).pop();
+          // minimize/return from the app
+          SystemNavigator.pop();
         },
         child: StreamBuilder<int>(
           stream: pageSelector.stream,
-- 
2.39.2





^ permalink raw reply	[flat|nested] 13+ messages in thread

* [pve-devel] applied: [PATCH proxmox-dart-api-client 1/1] client: correctly set parameter for node actions
  2024-04-15 10:30 ` [pve-devel] [PATCH proxmox-dart-api-client 1/1] client: correctly set parameter for node actions Dominik Csapak
@ 2024-04-16 15:07   ` Thomas Lamprecht
  0 siblings, 0 replies; 13+ messages in thread
From: Thomas Lamprecht @ 2024-04-16 15:07 UTC (permalink / raw)
  To: Proxmox VE development discussion, Dominik Csapak

Am 15/04/2024 um 12:30 schrieb Dominik Csapak:
> using '?' in the url will be escaped and not used for the get
> parameters. Instead add the command to the parameters map.
> 
> Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
> ---
>  lib/src/client.dart | 4 +++-
>  1 file changed, 3 insertions(+), 1 deletion(-)
> 
>

applied, thanks!




^ permalink raw reply	[flat|nested] 13+ messages in thread

* [pve-devel] applied: [PATCH proxmox-login-manager 1/2] login: show custom alert dialog for password saving errors
  2024-04-15 10:30 ` [pve-devel] [PATCH proxmox-login-manager 1/2] login: show custom alert dialog for password saving errors Dominik Csapak
@ 2024-04-16 15:08   ` Thomas Lamprecht
  0 siblings, 0 replies; 13+ messages in thread
From: Thomas Lamprecht @ 2024-04-16 15:08 UTC (permalink / raw)
  To: Proxmox VE development discussion, Dominik Csapak

Am 15/04/2024 um 12:30 schrieb Dominik Csapak:
> in those cases, we sometimes get ugly stack traces/exceptions, so
> instead of just showing that and aborting, show a custom dialog with
> the basic info that we could not save the password (+details box with
> the original error) and continue instead.
> 
> Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
> ---
>  lib/proxmox_login_form.dart | 32 ++++++++++++++++++++++++++++----
>  1 file changed, 28 insertions(+), 4 deletions(-)
> 
>

applied, with a follow-up to fix the unsafe context access over an async
gap, that `dart analyze` reported, thanks!




^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [pve-devel] [PATCH pve-flutter-frontend 4/5] nove overview: add power settings menu
  2024-04-15 10:30 ` [pve-devel] [PATCH pve-flutter-frontend 4/5] nove overview: add power settings menu Dominik Csapak
@ 2024-04-16 15:11   ` Thomas Lamprecht
  0 siblings, 0 replies; 13+ messages in thread
From: Thomas Lamprecht @ 2024-04-16 15:11 UTC (permalink / raw)
  To: Proxmox VE development discussion, Dominik Csapak

Am 15/04/2024 um 12:30 schrieb Dominik Csapak:
> similar to how it works for qemu

the one for virtual guests is already borderline dangerous without having
a confirmation prompt, but this one here is far from being borderline here
IMO, as for guests one can at least start them for sure again through, e.g.,
this App, but for a node one might not be easily able to without out-of-band
management.

I would not mind being a bit on the scare-mongering side in the prompt.





^ permalink raw reply	[flat|nested] 13+ messages in thread

* [pve-devel] applied: [PATCH flutter-repositories] some improvements/fixes for the app
  2024-04-15 10:30 [pve-devel] [PATCH flutter-repositories] some improvements/fixes for the app Dominik Csapak
                   ` (7 preceding siblings ...)
  2024-04-15 10:30 ` [pve-devel] [PATCH pve-flutter-frontend 5/5] improve back button behavior Dominik Csapak
@ 2024-04-16 15:11 ` Thomas Lamprecht
  8 siblings, 0 replies; 13+ messages in thread
From: Thomas Lamprecht @ 2024-04-16 15:11 UTC (permalink / raw)
  To: Proxmox VE development discussion, Dominik Csapak

Am 15/04/2024 um 12:30 schrieb Dominik Csapak:
> some improvements:
> * better error handling for saving the password
> * adding node power actions (shutdown/restart)
> * change webview so we can show it without a trusted certificate
> * improve back button behavior
> 
> flutter-frontend patch 1 depends on login-manager patch 2
> flutter-frontend patch 4 depends on dart-api-client to work correctly
> 
> this is intended on top of my last series:
> https://lists.proxmox.com/pipermail/pve-devel/2024-April/062891.html
> 
> proxmox-dart-api-client:
> 
> Dominik Csapak (1):
>   client: correctly set parameter for node actions
> 
>  lib/src/client.dart | 4 +++-
>  1 file changed, 3 insertions(+), 1 deletion(-)
> 
> proxmox-login-manager:
> 
> Dominik Csapak (2):
>   login: show custom alert dialog for password saving errors
>   general settings: add trustedFingerprints list
> 
>  lib/proxmox_general_settings_model.dart |  6 ++++-
>  lib/proxmox_login_form.dart             | 32 +++++++++++++++++++++----
>  2 files changed, 33 insertions(+), 5 deletions(-)
> 
> pve-flutter-frontend:
> 
> Dominik Csapak (5):
>   console: use flutter inappwebview as webview
>   console: wrap console with appbar
>   node overview: use correct color for rrd icons
>   nove overview: add power settings menu
>   improve back button behavior
> 
>  lib/bloc/pve_node_overview_bloc.dart          |  11 ++
>  lib/pages/main_layout_slim.dart               |   4 +-
>  lib/widgets/pve_console_menu_widget.dart      | 139 ++++++++++++++++--
>  lib/widgets/pve_node_overview.dart            |  36 ++++-
>  .../pve_node_power_settings_widget.dart       |  59 ++++++++
>  pubspec.lock                                  |  42 ++----
>  pubspec.yaml                                  |   3 +-
>  7 files changed, 240 insertions(+), 54 deletions(-)
>  create mode 100644 lib/widgets/pve_node_power_settings_widget.dart
> 


applied all but the patch adding a power menu for the nodes, thanks!




^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2024-04-16 15:12 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-04-15 10:30 [pve-devel] [PATCH flutter-repositories] some improvements/fixes for the app Dominik Csapak
2024-04-15 10:30 ` [pve-devel] [PATCH proxmox-dart-api-client 1/1] client: correctly set parameter for node actions Dominik Csapak
2024-04-16 15:07   ` [pve-devel] applied: " Thomas Lamprecht
2024-04-15 10:30 ` [pve-devel] [PATCH proxmox-login-manager 1/2] login: show custom alert dialog for password saving errors Dominik Csapak
2024-04-16 15:08   ` [pve-devel] applied: " Thomas Lamprecht
2024-04-15 10:30 ` [pve-devel] [PATCH proxmox-login-manager 2/2] general settings: add trustedFingerprints list Dominik Csapak
2024-04-15 10:30 ` [pve-devel] [PATCH pve-flutter-frontend 1/5] console: use flutter inappwebview as webview Dominik Csapak
2024-04-15 10:30 ` [pve-devel] [PATCH pve-flutter-frontend 2/5] console: wrap console with appbar Dominik Csapak
2024-04-15 10:30 ` [pve-devel] [PATCH pve-flutter-frontend 3/5] node overview: use correct color for rrd icons Dominik Csapak
2024-04-15 10:30 ` [pve-devel] [PATCH pve-flutter-frontend 4/5] nove overview: add power settings menu Dominik Csapak
2024-04-16 15:11   ` Thomas Lamprecht
2024-04-15 10:30 ` [pve-devel] [PATCH pve-flutter-frontend 5/5] improve back button behavior Dominik Csapak
2024-04-16 15:11 ` [pve-devel] applied: [PATCH flutter-repositories] some improvements/fixes for the app Thomas Lamprecht

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal