From a5ec789f6805d54247318bf0305358f08b2500c2 Mon Sep 17 00:00:00 2001 From: jaime <819519812@qq.com> Date: Thu, 3 Mar 2022 19:35:40 +0800 Subject: [PATCH 1/2] Support connect redis with username and password --- lib/api/redis/redis_api.dart | 73 ++++++- lib/main.dart | 5 + lib/module/redis/const.dart | 6 +- lib/persistent/persistent_db.dart | 10 +- lib/persistent/persistent_memory.dart | 5 +- lib/persistent/po/redis_instance_po.dart | 12 +- lib/route/route_gen.dart | 10 + lib/ui/redis/redis_page.dart | 16 +- lib/ui/redis/widget/redis_instance_dart.dart | 193 ++++++++++++++++++ lib/ui/util/alert_util.dart | 2 +- .../redis/redis_instance_list_view_model.dart | 4 +- lib/vm/redis/redis_instance_view_model.dart | 61 +++++- test/api/redis/redis_api_set_get_test.dart | 4 +- 13 files changed, 357 insertions(+), 44 deletions(-) create mode 100644 lib/ui/redis/widget/redis_instance_dart.dart diff --git a/lib/api/redis/redis_api.dart b/lib/api/redis/redis_api.dart index 56c0bdb..a8e93f7 100644 --- a/lib/api/redis/redis_api.dart +++ b/lib/api/redis/redis_api.dart @@ -20,15 +20,72 @@ import 'package:redis/redis.dart'; class RedisApi { - static Future set(String host, int port, String username, String password, String key, String value) async { - final conn = RedisConnection(); - Command connect = await conn.connect(host, port); - await connect.send_object(["SET", key, value]); + static Future set(String host, int port, String password, String key, String value) async { + final command = await getCommand(host, port, password); + dynamic rs = await command + .send_object(["SET", key, value]).onError((error, stackTrace) => throw Exception('redis error: $error')); + return rs; } - static Future get(String host, int port, String username, String password, String key) async { - final conn = RedisConnection(); - Command connect = await conn.connect(host, port); - return await connect.send_object(["GET", key]); + static Future get(String host, int port, String password, String key) async { + final command = await getCommand(host, port, password); + dynamic rs = + await command.send_object(["GET", key]).onError((error, stackTrace) => throw Exception('redis error: $error')); + await command.get_connection().close(); + return rs; + } + + static Future delete(String host, int port, String password, String key) async { + final command = await getCommand(host, port, password); + dynamic rs = + await command.send_object(["DEL", key]).onError((error, stackTrace) => throw Exception('redis error: $error')); + return rs; + } + + static dynamic keys(String host, int port, String password, String patten) async { + final command = await getCommand(host, port, password); + dynamic rs = await command + .send_object(["KEYS", patten]).onError((error, stackTrace) => throw Exception('redis error: $error')); + await command.get_connection().close(); + return rs; + } + + static Future hGet(String host, int port, String password, String key, String field) async { + final command = await getCommand(host, port, password); + dynamic rs = await command + .send_object(["HGET", key, field]).onError((error, stackTrace) => throw Exception('redis error: $error')); + return rs; + } + + static Future hSet( + String host, int port, String password, String key, String field, String fieldValue) async { + final command = await getCommand(host, port, password); + dynamic rs = await command.send_object(["HSET", key, field, fieldValue]).onError( + (error, stackTrace) => throw Exception('redis error: $error')); + return rs; + } + + static Future hGetAll(String host, int port, String password, String key) async { + final command = await getCommand(host, port, password); + dynamic rs = await command + .send_object(["HGETALL", key]).onError((error, stackTrace) => throw Exception('redis error: $error')); + return rs; + } + + static Future hDel(String host, int port, String password, String key, String field) async { + final command = await getCommand(host, port, password); + dynamic rs = await command + .send_object(["HDEL", key, field]).onError((error, stackTrace) => throw Exception('redis error: $error')); + return rs; + } + + static Future getCommand(String host, int port, String password) async { + final command = await RedisConnection().connect(host, port); + try { + await command.send_object(["AUTH", password]); + } catch (e) { + throw Exception('Password Error Exception: ${e.toString()}'); + } + return command; } } diff --git a/lib/main.dart b/lib/main.dart index 9e9aead..99aad2f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -60,6 +60,7 @@ import 'package:paas_dashboard_flutter/vm/pulsar/pulsar_source_view_model.dart'; import 'package:paas_dashboard_flutter/vm/pulsar/pulsar_tenant_view_model.dart'; import 'package:paas_dashboard_flutter/vm/pulsar/pulsar_topic_view_model.dart'; import 'package:paas_dashboard_flutter/vm/redis/redis_instance_list_view_model.dart'; +import 'package:paas_dashboard_flutter/vm/redis/redis_instance_view_model.dart'; import 'package:paas_dashboard_flutter/vm/sql/sql_list_view_model.dart'; import 'package:paas_dashboard_flutter/vm/sql/sql_view_model.dart'; import 'package:provider/provider.dart'; @@ -221,6 +222,10 @@ class MyApp extends StatelessWidget { final args = settings.arguments as SqlViewModel; return RouteGen.sqlExecute(args); } + if (settings.name == PageRouteConst.RedisInstance) { + final args = settings.arguments as RedisInstanceViewModel; + return RouteGen.redisInstance(args); + } throw UnimplementedError(); }, ); diff --git a/lib/module/redis/const.dart b/lib/module/redis/const.dart index 370d037..56f1246 100644 --- a/lib/module/redis/const.dart +++ b/lib/module/redis/const.dart @@ -18,9 +18,7 @@ // class RedisConst { - static const String defaultHost = "localhost"; + static const String defaultIp = "localhost"; static const int defaultPort = 6379; - static const String defaultAddr = "localhost:6379"; - static const String defaultUsername = ""; - static const String defaultPassword = ""; + static const String defaultPassword = "123456"; } diff --git a/lib/persistent/persistent_db.dart b/lib/persistent/persistent_db.dart index 132a43e..18088e9 100644 --- a/lib/persistent/persistent_db.dart +++ b/lib/persistent/persistent_db.dart @@ -124,10 +124,10 @@ class PersistentDb implements PersistentApi { 'CREATE TABLE code_list(id INTEGER PRIMARY KEY, name TEXT, code TEXT)', ); await db.execute( - 'CREATE TABLE redis_instances(id INTEGER PRIMARY KEY, name TEXT, addr TEXT, username TEXT, password TEXT)', + 'CREATE TABLE redis_instances(id INTEGER PRIMARY KEY, name TEXT, ip TEXT, port INTEGER, password TEXT)', ); await db.execute( - 'INSERT INTO redis_instances(name, addr, username, password) VALUES ("example", "${RedisConst.defaultAddr}", "${RedisConst.defaultUsername}", "${RedisConst.defaultPassword}")', + 'INSERT INTO redis_instances(name, ip, port, password) VALUES ("example", "${RedisConst.defaultIp}", ${RedisConst.defaultPort}, "${RedisConst.defaultPassword}")', ); } @@ -407,7 +407,7 @@ class PersistentDb implements PersistentApi { Future saveRedis(String name, String addr, String username, String password) async { var aux = await getInstance(); var list = [name, addr, username, password]; - aux.database.execute('INSERT INTO redis_instances(name, addr, username, password) VALUES (?, ?, ?, ?)', list); + aux.database.execute('INSERT INTO redis_instances(name, ip, port, password) VALUES (?, ?, ?, ?)', list); } @override @@ -422,7 +422,7 @@ class PersistentDb implements PersistentApi { final List> maps = await aux.database.query('redis_instances'); return List.generate(maps.length, (i) { var aux = maps[i]; - return RedisInstancePo(aux['id'], aux['name'], aux['addr'], aux['username'], aux['password']); + return RedisInstancePo(aux['id'], aux['name'], aux['ip'], aux['port'], aux['password']); }); } @@ -435,6 +435,6 @@ class PersistentDb implements PersistentApi { return null; } var current = maps[0]; - return RedisInstancePo(current['id'], current['name'], current['addr'], current['username'], current['password']); + return RedisInstancePo(current['id'], current['name'], current['ip'], current['port'], current['password']); } } diff --git a/lib/persistent/persistent_memory.dart b/lib/persistent/persistent_memory.dart index 1f2b684..b64bda1 100644 --- a/lib/persistent/persistent_memory.dart +++ b/lib/persistent/persistent_memory.dart @@ -252,7 +252,7 @@ class PersistentMemory implements PersistentApi { Future> redisInstances() async { return [ - new RedisInstancePo(0, "example", RedisConst.defaultAddr, RedisConst.defaultUsername, RedisConst.defaultPassword) + new RedisInstancePo(0, "example", RedisConst.defaultIp, RedisConst.defaultPort, RedisConst.defaultPassword) ]; } @@ -260,7 +260,6 @@ class PersistentMemory implements PersistentApi { if (name != "example") { return null; } - return new RedisInstancePo( - 0, "example", RedisConst.defaultAddr, RedisConst.defaultUsername, RedisConst.defaultPassword); + return new RedisInstancePo(0, "example", RedisConst.defaultIp, RedisConst.defaultPort, RedisConst.defaultPassword); } } diff --git a/lib/persistent/po/redis_instance_po.dart b/lib/persistent/po/redis_instance_po.dart index a4a1de3..b22c345 100644 --- a/lib/persistent/po/redis_instance_po.dart +++ b/lib/persistent/po/redis_instance_po.dart @@ -20,22 +20,22 @@ class RedisInstancePo { final int id; final String name; - final String addr; - final String username; + final String ip; + final int port; final String password; - RedisInstancePo(this.id, this.name, this.addr, this.username, this.password); + RedisInstancePo(this.id, this.name, this.ip, this.port, this.password); RedisInstancePo deepCopy() { - return new RedisInstancePo(id, name, addr, username, password); + return new RedisInstancePo(id, name, ip, port, password); } Map toMap() { return { 'id': id, 'name': name, - 'addr': addr, - 'username': username, + 'ip': ip, + 'port': port, 'password': password, }; } diff --git a/lib/route/route_gen.dart b/lib/route/route_gen.dart index 1aa2cb1..1881ef8 100644 --- a/lib/route/route_gen.dart +++ b/lib/route/route_gen.dart @@ -35,6 +35,7 @@ import 'package:paas_dashboard_flutter/ui/pulsar/screen/pulsar_sink.dart'; import 'package:paas_dashboard_flutter/ui/pulsar/screen/pulsar_source.dart'; import 'package:paas_dashboard_flutter/ui/pulsar/screen/pulsar_tenant.dart'; import 'package:paas_dashboard_flutter/ui/pulsar/screen/pulsar_topic.dart'; +import 'package:paas_dashboard_flutter/ui/redis/widget/redis_instance_dart.dart'; import 'package:paas_dashboard_flutter/ui/sql/screen/sql_execute_screen.dart'; import 'package:paas_dashboard_flutter/vm/code/code_view_model.dart'; import 'package:paas_dashboard_flutter/vm/mongo/mongo_database_view_model.dart'; @@ -53,6 +54,7 @@ import 'package:paas_dashboard_flutter/vm/pulsar/pulsar_sink_view_model.dart'; import 'package:paas_dashboard_flutter/vm/pulsar/pulsar_source_view_model.dart'; import 'package:paas_dashboard_flutter/vm/pulsar/pulsar_tenant_view_model.dart'; import 'package:paas_dashboard_flutter/vm/pulsar/pulsar_topic_view_model.dart'; +import 'package:paas_dashboard_flutter/vm/redis/redis_instance_view_model.dart'; import 'package:paas_dashboard_flutter/vm/sql/sql_view_model.dart'; import 'package:provider/provider.dart'; @@ -195,6 +197,14 @@ class RouteGen { )); } + static Route redisInstance(RedisInstanceViewModel viewModel) { + return MaterialPageRoute( + builder: (context) => ChangeNotifierProvider( + create: (context) => viewModel, + child: RedisInstanceWidget(), + )); + } + static Route sqlExecute(SqlViewModel viewModel) { // deep copy view model return MaterialPageRoute( diff --git a/lib/ui/redis/redis_page.dart b/lib/ui/redis/redis_page.dart index 1d9a465..d3559a4 100644 --- a/lib/ui/redis/redis_page.dart +++ b/lib/ui/redis/redis_page.dart @@ -69,8 +69,8 @@ class _RedisPageState extends State { columns: [ DataColumn(label: Text('Id')), DataColumn(label: Text('Name')), - DataColumn(label: Text('Addr')), - DataColumn(label: Text('Username')), + DataColumn(label: Text('IP')), + DataColumn(label: Text('Port')), DataColumn(label: Text('Delete instance')), ], rows: vm.instances @@ -81,8 +81,8 @@ class _RedisPageState extends State { cells: [ DataCell(Text(itemRow.id.toString())), DataCell(Text(itemRow.name)), - DataCell(Text(itemRow.addr)), - DataCell(Text(itemRow.username)), + DataCell(Text(itemRow.ip)), + DataCell(Text(itemRow.port.toString())), DataCellUtil.newDelDataCell(() { vm.deleteRedis(itemRow.id); }), @@ -103,12 +103,12 @@ class _RedisPageState extends State { final vm = Provider.of(context, listen: false); var list = [ FormFieldDef('Instance Name'), - FormFieldDef('Addr'), - FormFieldDef('Username'), + FormFieldDef('IP'), + FormFieldDef('Port'), FormFieldDef('Password'), ]; - return FormUtil.createButton4("Redis Instance", list, context, (name, addr, username, password) { - vm.createRedis(name, addr, username, password); + return FormUtil.createButton4("Redis Instance", list, context, (name, ip, port, password) { + vm.createRedis(name, ip, port, password); }); } } diff --git a/lib/ui/redis/widget/redis_instance_dart.dart b/lib/ui/redis/widget/redis_instance_dart.dart new file mode 100644 index 0000000..334cdaa --- /dev/null +++ b/lib/ui/redis/widget/redis_instance_dart.dart @@ -0,0 +1,193 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +import 'package:flutter/material.dart'; +import 'package:paas_dashboard_flutter/generated/l10n.dart'; +import 'package:paas_dashboard_flutter/ui/util/exception_util.dart'; +import 'package:paas_dashboard_flutter/vm/redis/redis_instance_view_model.dart'; +import 'package:provider/provider.dart'; + +class RedisInstanceWidget extends StatefulWidget { + RedisInstanceWidget(); + + @override + State createState() { + return new _RedisInstanceWidgetState(); + } +} + +class _RedisInstanceWidgetState extends State { + _RedisInstanceWidgetState(); + + DropdownButton? opButton; + OP opButtonValue = OP.KEYS; + TextButton? executeButton; + Row? inputRow; + Map inputValues = {0: ""}; + + @override + Widget build(BuildContext context) { + final vm = Provider.of(context); + ExceptionUtil.processOpException(vm, context); + var dbsFuture = SingleChildScrollView( + child: DataTable( + showCheckboxColumn: false, + columns: [ + DataColumn( + label: SelectableText( + S.of(context).result, + style: new TextStyle(color: Colors.red, fontSize: 20), + )) + ], + rows: [ + DataRow(cells: [new DataCell(SelectableText(vm.executeResult))]) + ], + ), + ); + + opButton = DropdownButton( + items: getOpMenu(), + underline: Container(), + onChanged: (value) { + setState(() { + opButtonValue = value; + if (value == OP.HSET) { + inputValues = {0: "", 1: "", 2: ""}; + } else if (value == OP.SET || value == OP.HGET || value == OP.HDEL) { + inputValues = {0: "", 1: ""}; + } else { + inputValues = {0: ""}; + } + }); + }, + value: opButtonValue, + isExpanded: true, + ); + + inputRow = new Row( + children: createInput(inputValues.length), + ); + + executeButton = TextButton( + onPressed: () { + if (opButtonValue == OP.DELETE || opButtonValue == OP.HDEL) { + showDialog( + context: context, + builder: (BuildContext context) { + return new AlertDialog( + title: Text( + S.of(context).confirmDeleteQuestion, + textAlign: TextAlign.center, + ), + actions: [ + new TextButton( + child: new Text(S.of(context).cancel), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + new TextButton( + child: new Text(S.of(context).confirm), + onPressed: () { + vm.execute(opButtonValue, List.from(inputValues.values)); + Navigator.of(context).pop(); + }, + ), + ], + ); + }); + } else { + vm.execute(opButtonValue, List.from(inputValues.values)); + } + }, + child: Text(S.of(context).execute)); + + var row1 = new Row( + children: [ + Expanded(flex: 1, child: opButton!), + Expanded(flex: 3, child: inputRow!), + Expanded(flex: 1, child: executeButton!) + ], + ); + + Expanded row2 = new Expanded(child: dbsFuture); + + var body = ListView( + shrinkWrap: true, + children: [ + Container( + child: ListView( + scrollDirection: Axis.vertical, + shrinkWrap: true, + children: [row1, row2], + ), + ), + ], + ); + + return DefaultTabController( + length: 1, + child: Scaffold( + appBar: AppBar( + title: Text('Redis ${vm.name} Dashboard'), + bottom: TabBar( + tabs: [ + Tab( + text: "Redis", + ), + ], + ), + ), + body: TabBarView( + children: [body], + ), + ), + ); + } + + List> getOpMenu() { + List> rs = []; + rs.add(new DropdownMenuItem(child: Text("KEYS"), value: OP.KEYS)); + rs.add(new DropdownMenuItem(child: Text("DELETE"), value: OP.DELETE)); + rs.add(new DropdownMenuItem(child: Text("GET"), value: OP.GET)); + rs.add(new DropdownMenuItem(child: Text("SET"), value: OP.SET)); + rs.add(new DropdownMenuItem(child: Text("HSET"), value: OP.HSET)); + rs.add(new DropdownMenuItem(child: Text("HGET"), value: OP.HGET)); + rs.add(new DropdownMenuItem(child: Text("HGETALL"), value: OP.HGETALL)); + rs.add(new DropdownMenuItem(child: Text("HDEL"), value: OP.HDEL)); + return rs; + } + + List createInput(int length) { + List rs = []; + for (int i = 0; i < length; i++) { + rs.add(Expanded( + child: new TextField( + decoration: InputDecoration(labelText: "param" + (i + 1).toString()), + controller: new TextEditingController(text: inputValues[i]), + onChanged: (text) { + inputValues[i] = text; + }, + ))); + } + return rs; + } +} + +enum OP { KEYS, DELETE, GET, SET, HSET, HGET, HDEL, HGETALL } diff --git a/lib/ui/util/alert_util.dart b/lib/ui/util/alert_util.dart index 35691a6..69f4dd4 100644 --- a/lib/ui/util/alert_util.dart +++ b/lib/ui/util/alert_util.dart @@ -38,7 +38,7 @@ class AlertUtil { color: Colors.redAccent, ), ), - content: Text( + content: SelectableText( "$error", style: TextStyle( color: Colors.blueAccent, diff --git a/lib/vm/redis/redis_instance_list_view_model.dart b/lib/vm/redis/redis_instance_list_view_model.dart index 8d5f75f..13019c9 100644 --- a/lib/vm/redis/redis_instance_list_view_model.dart +++ b/lib/vm/redis/redis_instance_list_view_model.dart @@ -30,8 +30,8 @@ class RedisInstanceListViewModel extends ChangeNotifier { notifyListeners(); } - Future createRedis(String name, String addr, String username, String password) async { - Persistent.saveRedis(name, addr, username, password); + Future createRedis(String name, String ip, String port, String password) async { + Persistent.saveRedis(name, ip, port, password); fetchRedisInstances(); } diff --git a/lib/vm/redis/redis_instance_view_model.dart b/lib/vm/redis/redis_instance_view_model.dart index 3f40e13..955b14e 100644 --- a/lib/vm/redis/redis_instance_view_model.dart +++ b/lib/vm/redis/redis_instance_view_model.dart @@ -17,13 +17,18 @@ // under the License. // +import 'package:paas_dashboard_flutter/api/redis/redis_api.dart'; import 'package:paas_dashboard_flutter/persistent/po/redis_instance_po.dart'; +import 'package:paas_dashboard_flutter/ui/redis/widget/redis_instance_dart.dart'; +import 'package:paas_dashboard_flutter/vm/base_load_view_model.dart'; -class RedisInstanceViewModel { +class RedisInstanceViewModel extends BaseLoadViewModel { final RedisInstancePo redisInstancePo; RedisInstanceViewModel(this.redisInstancePo); + dynamic result = ""; + RedisInstanceViewModel deepCopy() { return new RedisInstanceViewModel(redisInstancePo.deepCopy()); } @@ -36,11 +41,57 @@ class RedisInstanceViewModel { return this.redisInstancePo.name; } - String get addr { - return this.redisInstancePo.addr; + String get ip { + return this.redisInstancePo.ip; + } + + String get password { + return this.redisInstancePo.password; + } + + int get port { + return this.redisInstancePo.port; + } + + String get executeResult { + return this.result.toString(); } - String get username { - return this.redisInstancePo.username; + Future execute(OP op, List value) async { + dynamic reply; + try { + switch (op) { + case OP.KEYS: + reply = await RedisApi.keys(ip, port, password, value[0]); + break; + case OP.GET: + reply = await RedisApi.get(ip, port, password, value[0]); + break; + case OP.SET: + reply = await RedisApi.set(ip, port, password, value[0], value[1]); + break; + case OP.DELETE: + reply = await RedisApi.delete(ip, port, password, value[0]); + break; + case OP.HSET: + reply = await RedisApi.hSet(ip, port, password, value[0], value[1], value[2]); + break; + case OP.HGET: + reply = await RedisApi.hGet(ip, port, password, value[0], value[1]); + break; + case OP.HGETALL: + reply = await RedisApi.hGetAll(ip, port, password, value[0]); + break; + case OP.HDEL: + reply = await RedisApi.hDel(ip, port, password, value[0], value[1]); + break; + } + result = reply; + loading = true; + } on Exception catch (e) { + loading = false; + opException = e; + } + notifyListeners(); } } diff --git a/test/api/redis/redis_api_set_get_test.dart b/test/api/redis/redis_api_set_get_test.dart index 43f7e7d..8d057e2 100644 --- a/test/api/redis/redis_api_set_get_test.dart +++ b/test/api/redis/redis_api_set_get_test.dart @@ -3,8 +3,8 @@ import 'package:paas_dashboard_flutter/api/redis/redis_api.dart'; void main() { test("test_set_get", () async { - await RedisApi.set("localhost", 6379, "", "", "key", "value"); - var val = await RedisApi.get("localhost", 6379, "", "", "key"); + await RedisApi.set("localhost", 6379, "", "key", "value"); + var val = await RedisApi.get("localhost", 6379, "", "key"); print(val); }); } From 3435eb679be3b428b370046b35c485500f5fa0e6 Mon Sep 17 00:00:00 2001 From: LeKe <95280282+goflutterjava@users.noreply.github.com> Date: Thu, 3 Mar 2022 20:46:35 +0800 Subject: [PATCH 2/2] Update main.dart --- lib/main.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 99aad2f..24c1aec 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -218,14 +218,14 @@ class MyApp extends StatelessWidget { final args = settings.arguments as PulsarSinkViewModel; return RouteGen.pulsarSink(args); } - if (settings.name == PageRouteConst.SqlExecute) { - final args = settings.arguments as SqlViewModel; - return RouteGen.sqlExecute(args); - } if (settings.name == PageRouteConst.RedisInstance) { final args = settings.arguments as RedisInstanceViewModel; return RouteGen.redisInstance(args); } + if (settings.name == PageRouteConst.SqlExecute) { + final args = settings.arguments as SqlViewModel; + return RouteGen.sqlExecute(args); + } throw UnimplementedError(); }, );