├── .gitattributes ├── webredismanager001.png ├── webredismanager002.png ├── webredismanager003.png ├── webredismanager004.png ├── webredismanager005.png ├── webredismanager006.png ├── webredismanager007.png ├── SAEA.WebRedisManager ├── wwwroot │ ├── favicon.ico │ ├── Content │ │ ├── img │ │ │ └── 6139455.png │ │ ├── font │ │ │ └── iconfont.woff │ │ ├── js │ │ │ ├── css │ │ │ │ └── modules │ │ │ │ │ ├── layer │ │ │ │ │ └── default │ │ │ │ │ │ ├── icon.png │ │ │ │ │ │ ├── loading-1.gif │ │ │ │ │ │ └── loading-2.gif │ │ │ │ │ └── code.css │ │ │ ├── npm.js │ │ │ ├── datetimehelper.js │ │ │ ├── lay │ │ │ │ └── modules │ │ │ │ │ ├── code.js │ │ │ │ │ ├── laytpl.js │ │ │ │ │ ├── util.js │ │ │ │ │ ├── laypage.js │ │ │ │ │ ├── element.js │ │ │ │ │ └── form.js │ │ │ ├── requestpamars.js │ │ │ ├── formhelper.js │ │ │ ├── redis.console.js │ │ │ ├── layui.js │ │ │ ├── redis.js │ │ │ └── redis.data.js │ │ └── css │ │ │ └── login.css │ ├── Remove.html │ ├── configs.html │ ├── Console.html │ ├── RedisAdd.html │ ├── ItemsView.html │ ├── Rename.html │ ├── Keys.html │ ├── Index.html │ ├── Chart.html │ ├── AddItem.html │ └── IndexContent.html ├── Controllers │ ├── UpdateController.cs │ ├── ConsoleController.cs │ ├── ConfigController.cs │ ├── VerificationController.cs │ ├── RedisClusterController.cs │ ├── UserController.cs │ └── RedisController.cs ├── Models │ ├── JsonResult.cs │ ├── Config.cs │ ├── RedisData.cs │ ├── KeyType.cs │ ├── User.cs │ └── RedisServerInfo.cs ├── Program.cs ├── Libs │ ├── RedisCmdHelper.cs │ ├── WorkerServiceHelper.cs │ ├── ServerInfoDataHelper.cs │ ├── WebSocketsHelper.cs │ ├── ConfigHelper.cs │ ├── AESHelper.cs │ ├── UserHelper.cs │ └── Verification │ │ └── VerificationCode.cs ├── Services │ ├── UpdateService.cs │ ├── ConsoleService.cs │ ├── ConfigService.cs │ └── RedisClusterService.cs ├── AppService.cs └── Attr │ └── AuthAttribute.cs ├── Dockerfile ├── LICENSE ├── SAEA.WebRedisManager.sln ├── README.md └── .gitignore /.gitattributes: -------------------------------------------------------------------------------- 1 | *.js linguist-language=c# 2 | *.css linguist-language=c# 3 | *.html linguist-language=c# -------------------------------------------------------------------------------- /webredismanager001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yswenli/WebRedisManager/HEAD/webredismanager001.png -------------------------------------------------------------------------------- /webredismanager002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yswenli/WebRedisManager/HEAD/webredismanager002.png -------------------------------------------------------------------------------- /webredismanager003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yswenli/WebRedisManager/HEAD/webredismanager003.png -------------------------------------------------------------------------------- /webredismanager004.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yswenli/WebRedisManager/HEAD/webredismanager004.png -------------------------------------------------------------------------------- /webredismanager005.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yswenli/WebRedisManager/HEAD/webredismanager005.png -------------------------------------------------------------------------------- /webredismanager006.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yswenli/WebRedisManager/HEAD/webredismanager006.png -------------------------------------------------------------------------------- /webredismanager007.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yswenli/WebRedisManager/HEAD/webredismanager007.png -------------------------------------------------------------------------------- /SAEA.WebRedisManager/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yswenli/WebRedisManager/HEAD/SAEA.WebRedisManager/wwwroot/favicon.ico -------------------------------------------------------------------------------- /SAEA.WebRedisManager/wwwroot/Content/img/6139455.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yswenli/WebRedisManager/HEAD/SAEA.WebRedisManager/wwwroot/Content/img/6139455.png -------------------------------------------------------------------------------- /SAEA.WebRedisManager/wwwroot/Content/font/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yswenli/WebRedisManager/HEAD/SAEA.WebRedisManager/wwwroot/Content/font/iconfont.woff -------------------------------------------------------------------------------- /SAEA.WebRedisManager/wwwroot/Content/js/css/modules/layer/default/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yswenli/WebRedisManager/HEAD/SAEA.WebRedisManager/wwwroot/Content/js/css/modules/layer/default/icon.png -------------------------------------------------------------------------------- /SAEA.WebRedisManager/wwwroot/Content/js/css/modules/layer/default/loading-1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yswenli/WebRedisManager/HEAD/SAEA.WebRedisManager/wwwroot/Content/js/css/modules/layer/default/loading-1.gif -------------------------------------------------------------------------------- /SAEA.WebRedisManager/wwwroot/Content/js/css/modules/layer/default/loading-2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yswenli/WebRedisManager/HEAD/SAEA.WebRedisManager/wwwroot/Content/js/css/modules/layer/default/loading-2.gif -------------------------------------------------------------------------------- /SAEA.WebRedisManager/Controllers/UpdateController.cs: -------------------------------------------------------------------------------- 1 | 2 | using SAEA.MVC; 3 | using SAEA.WebRedisManager.Services; 4 | 5 | namespace SAEA.WebRedisManager.Controllers 6 | { 7 | public class UpdateController : Controller 8 | { 9 | public ActionResult GetLatest() 10 | { 11 | return Json(new UpdateService().GetLatest()); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # 准备环境,构建项目 2 | # 指定基础镜像 3 | FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build-env 4 | # 设置工作目录 5 | WORKDIR /app 6 | # 复制所有文件并生成输出 7 | COPY . ./ 8 | RUN dotnet nuget add source https://nuget.cdn.azure.cn/v3/index.json -n azure 9 | RUN dotnet restore 10 | RUN dotnet publish -c Release -o out --os linux 11 | 12 | # 构建镜像 13 | # 指定基础镜像 14 | FROM mcr.microsoft.com/dotnet/runtime:8.0 15 | WORKDIR /app 16 | COPY --from=build-env /app/out . 17 | # 暴露端口 18 | EXPOSE 80 19 | # 设置启动命令 20 | ENTRYPOINT ["dotnet", "SAEA.WebRedisManager.dll"] -------------------------------------------------------------------------------- /SAEA.WebRedisManager/Models/JsonResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SAEA.Redis.WebManager.Models 8 | { 9 | public class JsonResult 10 | { 11 | public int Code 12 | { 13 | get; set; 14 | } 15 | 16 | public string Message 17 | { 18 | get; set; 19 | } 20 | 21 | public T Data 22 | { 23 | get; set; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /SAEA.WebRedisManager/wwwroot/Content/js/npm.js: -------------------------------------------------------------------------------- 1 | // This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment. 2 | require('../../js/transition.js') 3 | require('../../js/alert.js') 4 | require('../../js/button.js') 5 | require('../../js/carousel.js') 6 | require('../../js/collapse.js') 7 | require('../../js/dropdown.js') 8 | require('../../js/modal.js') 9 | require('../../js/tooltip.js') 10 | require('../../js/popover.js') 11 | require('../../js/scrollspy.js') 12 | require('../../js/tab.js') 13 | require('../../js/affix.js') -------------------------------------------------------------------------------- /SAEA.WebRedisManager/Models/Config.cs: -------------------------------------------------------------------------------- 1 | namespace SAEA.Redis.WebManager.Models 2 | { 3 | /// 4 | /// SAEA.Redis.WebManager配置 5 | /// 6 | public class Config 7 | { 8 | public string Name 9 | { 10 | get; set; 11 | } 12 | 13 | public string IP 14 | { 15 | get; set; 16 | } 17 | 18 | public int Port 19 | { 20 | get; set; 21 | } 22 | 23 | public string Password 24 | { 25 | get; set; 26 | } 27 | 28 | public string Creator 29 | { 30 | get; 31 | set; 32 | } 33 | 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /SAEA.WebRedisManager/Models/RedisData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SAEA.Redis.WebManager.Models 8 | { 9 | /// 10 | /// 提交Redis的数据 11 | /// 12 | public class RedisData 13 | { 14 | public string Name 15 | { 16 | get;set; 17 | } 18 | 19 | public int DBIndex 20 | { 21 | get;set; 22 | } 23 | 24 | public int Type 25 | { 26 | get; set; 27 | } 28 | public string ID 29 | { 30 | get; set; 31 | } 32 | 33 | public string Key 34 | { 35 | get; set; 36 | } 37 | 38 | public string Value 39 | { 40 | get; set; 41 | } 42 | 43 | public int TTL 44 | { 45 | get;set; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /SAEA.WebRedisManager/Models/KeyType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SAEA.Redis.WebManager.Models 8 | { 9 | public class KeyType 10 | { 11 | public string Key 12 | { 13 | get; set; 14 | } 15 | 16 | public string Type 17 | { 18 | get; set; 19 | } 20 | } 21 | 22 | public static class Extiontions 23 | { 24 | public static List ToKeyTypes(this Dictionary dic) 25 | { 26 | if (dic == null) return null; 27 | 28 | List result = new List(); 29 | 30 | foreach (var item in dic) 31 | { 32 | result.Add(new KeyType() 33 | { 34 | Key = item.Key, 35 | Type = item.Value 36 | }); 37 | } 38 | 39 | return result; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /SAEA.WebRedisManager/Models/User.cs: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | *项目名称:SAEA.WebRedisManager.Models 3 | *CLR 版本:4.0.30319.42000 4 | *机器名称:WALLE-PC 5 | *命名空间:SAEA.WebRedisManager.Models 6 | *类 名 称:User 7 | *版 本 号:V1.0.0.0 8 | *创建人: yswenli 9 | *电子邮箱:yswenli@outlook.com 10 | *创建时间:2020/1/6 11:13:41 11 | *描述: 12 | *===================================================================== 13 | *修改时间:2020/1/6 11:13:41 14 | *修 改 人: yswenli 15 | *版 本 号: V1.0.0.0 16 | *描 述: 17 | *****************************************************************************/ 18 | 19 | namespace SAEA.WebRedisManager.Models 20 | { 21 | public class User 22 | { 23 | public string ID { get; set; } 24 | 25 | public string UserName { get; set; } 26 | 27 | public string NickName { get; set; } 28 | 29 | public string Password { get; set; } 30 | 31 | public Role Role { get; set; } 32 | } 33 | 34 | public enum Role 35 | { 36 | Admin = 1, 37 | User = 2 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /SAEA.WebRedisManager/Models/RedisServerInfo.cs: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | *项目名称:SAEA.WebRedisManagerForNet.Models 3 | *CLR 版本:4.0.30319.42000 4 | *机器名称:WALLE-PC 5 | *命名空间:SAEA.WebRedisManagerForNet.Models 6 | *类 名 称:RedisServerInfo 7 | *版 本 号:V1.0.0.0 8 | *创建人: yswenli 9 | *电子邮箱:yswenli@outlook.com 10 | *创建时间:2020/9/27 11:02:05 11 | *描述: 12 | *===================================================================== 13 | *修改时间:2020/9/27 11:02:05 14 | *修 改 人: yswenli 15 | *版 本 号: V1.0.0.0 16 | *描 述: 17 | *****************************************************************************/ 18 | 19 | namespace SAEA.Redis.WebManager.Models 20 | { 21 | /// 22 | /// redis 服务器信息 23 | /// 24 | public class RedisServerInfo 25 | { 26 | public string Cpu { get; set; } 27 | 28 | public string Memory { get; set; } 29 | 30 | public string Cmds { get; set; } 31 | 32 | public string Input { get; set; } 33 | 34 | public string Output { get; set; } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /SAEA.WebRedisManager/wwwroot/Content/js/datetimehelper.js: -------------------------------------------------------------------------------- 1 | function datetimehelper_stringToDate(str) { 2 | 3 | var tempStrs = str.split(" "); 4 | 5 | var dateStrs = tempStrs[0].split("-"); 6 | 7 | var year = parseInt(dateStrs[0], 10); 8 | 9 | var month = parseInt(dateStrs[1], 10) - 1; 10 | 11 | var day = parseInt(dateStrs[2], 10); 12 | 13 | var timeStrs = tempStrs[1].split(":"); 14 | 15 | var hour = parseInt(timeStrs[0], 10); 16 | 17 | var minute = parseInt(timeStrs[1], 10); 18 | 19 | var second = parseInt(timeStrs[2], 10); 20 | 21 | var date = new Date(year, month, day, hour, minute, second); 22 | 23 | return date; 24 | } 25 | 26 | function datetimehelper_getSeconds(date) { 27 | return parseInt(date.getTime() / 1000); 28 | } 29 | 30 | function datetimehelper_getSecondsFromNow(dateStr) { 31 | 32 | var d1 = datetimehelper_stringToDate(dateStr); 33 | var d2 = new Date(); 34 | 35 | var s1 = datetimehelper_getSeconds(d1); 36 | var s2 = datetimehelper_getSeconds(d2); 37 | 38 | var s3 = s1 - s2; 39 | 40 | return s3; 41 | } -------------------------------------------------------------------------------- /SAEA.WebRedisManager/Program.cs: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | *项目名称:SAEA.WebRedisManager 3 | *CLR 版本:4.0.30319.42000 4 | *机器名称:WALLE-PC 5 | *命名空间:SAEA.WebRedisManager 6 | *类 名 称:Program 7 | *版 本 号:V1.0.0.0 8 | *创建人: yswenli 9 | *电子邮箱:yswenli@outlook.com 10 | *修改时间:2021/7/8 18:24:22 11 | *描述: 12 | *===================================================================== 13 | *修改时间:2021/7/8 18:24:22 14 | *修 改 人: yswenli 15 | *版 本 号: V1.0.0.0 16 | *描 述: 17 | *****************************************************************************/ 18 | using Microsoft.Extensions.Hosting; 19 | 20 | using SAEA.Common; 21 | using SAEA.WebRedisManager.Libs; 22 | 23 | namespace SAEA.WebRedisManager 24 | { 25 | class Program 26 | { 27 | static void Main(string[] args) 28 | { 29 | try 30 | { 31 | ConsoleHelper.Title = $"SAEA.WebRedisManager {DateTimeHelper.Now}"; 32 | } 33 | catch { } 34 | 35 | WorkerServiceHelper.CreateHostBuilder(args).Build().Run(); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 yswenli 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /SAEA.WebRedisManager.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30011.22 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SAEA.WebRedisManager", "SAEA.WebRedisManager\SAEA.WebRedisManager.csproj", "{BCE122A2-20C5-4CE9-8353-63369347407A}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {BCE122A2-20C5-4CE9-8353-63369347407A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {BCE122A2-20C5-4CE9-8353-63369347407A}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {BCE122A2-20C5-4CE9-8353-63369347407A}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {BCE122A2-20C5-4CE9-8353-63369347407A}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {D5A4CFFA-A42F-4B9A-866B-C4391A645B5B} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /SAEA.WebRedisManager/wwwroot/Content/js/lay/modules/code.js: -------------------------------------------------------------------------------- 1 | /** layui-v2.3.0 MIT License By https://www.layui.com */ 2 | ; layui.define("jquery", function (e) { "use strict"; var a = layui.$, l = "http://www.layui.com/doc/modules/code.html"; e("code", function (e) { var t = []; e = e || {}, e.elem = a(e.elem || ".layui-code"), e.about = !("about" in e) || e.about, e.elem.each(function () { t.push(this) }), layui.each(t.reverse(), function (t, i) { var c = a(i), o = c.html(); (c.attr("lay-encode") || e.encode) && (o = o.replace(/&(?!#?[a-zA-Z0-9]+;)/g, "&").replace(//g, ">").replace(/'/g, "'").replace(/"/g, """)), c.html('
  1. ' + o.replace(/[\r\t\n]+/g, "
  2. ") + "
"), c.find(">.layui-code-h3")[0] || c.prepend('

' + (c.attr("lay-title") || e.title || "code") + (e.about ? 'layui.code' : "") + "

"); var d = c.find(">.layui-code-ol"); c.addClass("layui-box layui-code-view"), (c.attr("lay-skin") || e.skin) && c.addClass("layui-code-" + (c.attr("lay-skin") || e.skin)), (d.find("li").length / 100 | 0) > 0 && d.css("margin-left", (d.find("li").length / 100 | 0) + "px"), (c.attr("lay-height") || e.height) && d.css("max-height", c.attr("lay-height") || e.height) }) }) }).addcss("modules/code.css", "skincodecss"); -------------------------------------------------------------------------------- /SAEA.WebRedisManager/Libs/RedisCmdHelper.cs: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | *项目名称:SAEA.WebRedisManager.Libs 3 | *CLR 版本:4.0.30319.42000 4 | *机器名称:WALLE-PC 5 | *命名空间:SAEA.WebRedisManager.Libs 6 | *类 名 称:RedisCmdHelper 7 | *版 本 号:V1.0.0.0 8 | *创建人: yswenli 9 | *电子邮箱:yswenli@outlook.com 10 | *创建时间:2020/9/7 13:25:07 11 | *描述: 12 | *===================================================================== 13 | *修改时间:2020/9/7 13:25:07 14 | *修 改 人: yswenli 15 | *版 本 号: V1.0.0.0 16 | *描 述: 17 | *****************************************************************************/ 18 | using SAEA.RedisSocket.Model; 19 | using System; 20 | using System.Collections.Generic; 21 | using System.Linq; 22 | using System.Text; 23 | 24 | namespace SAEA.WebRedisManager.Libs 25 | { 26 | public static class RedisCmdHelper 27 | { 28 | static IEnumerable _list; 29 | static RedisCmdHelper() 30 | { 31 | _list = Enum.GetNames(typeof(RequestType)).Select(b => b.Replace("_", " ")); 32 | } 33 | 34 | public static IEnumerable GetList(string input, int max = 10) 35 | { 36 | if (string.IsNullOrEmpty(input)) 37 | 38 | return _list.OrderBy(b => b).Take(max); 39 | 40 | return _list.Where(b => b.IndexOf(input, StringComparison.InvariantCultureIgnoreCase) > -1).OrderBy(b => b).Take(max); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /SAEA.WebRedisManager/Controllers/ConsoleController.cs: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | *项目名称:SAEA.WebRedisManager.Controllers 3 | *CLR 版本:4.0.30319.42000 4 | *机器名称:WENLI-PC 5 | *命名空间:SAEA.WebRedisManager.Controllers 6 | *类 名 称:ConsoleController 7 | *版 本 号:V1.0.0.0 8 | *创建人: yswenli 9 | *电子邮箱:wenguoli_520@qq.com 10 | *创建时间:2019/3/26 11:05:29 11 | *描述: 12 | *===================================================================== 13 | *修改时间:2019/3/26 11:05:29 14 | *修 改 人: yswenli 15 | *版 本 号: V1.0.0.0 16 | *描 述: 17 | *****************************************************************************/ 18 | using SAEA.MVC; 19 | using SAEA.WebRedisManager.Services; 20 | 21 | namespace SAEA.WebRedisManager.Controllers 22 | { 23 | public class ConsoleController : Controller 24 | { 25 | /// 26 | /// 发送命令 27 | /// 28 | /// 29 | /// 30 | /// 31 | public ActionResult SendCmd(string name, string cmd) 32 | { 33 | return Content(new ConsoleService().SendCmd(name, cmd)); 34 | } 35 | 36 | /// 37 | /// 获取输入的命令 38 | /// 39 | /// 40 | /// 41 | public ActionResult GetCMD(string input) 42 | { 43 | return Json(new ConsoleService().GetCMD(input)); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WebRedisManager 2 | WebRedisManager是使用的SAEA.RedisSocket、SAEA.MVC等实现Redis的简便管理功能,使用详情请访问https://www.cnblogs.com/yswenli/p/9460527.html 3 | 4 | QQ群:788260487 5 | 6 | ## WebRedisManager core 版 7 | 安装net core5.0后,输入 dotnet WebRedisManager.dll或点击WebRedisManager.exe 即可运行! 8 |
9 | # 下载地址 10 | https://github.com/yswenli/WebRedisManager/releases 11 | 12 | [![GitHub release](https://img.shields.io/github/release/yswenli/webredismanager.svg)](https://github.com/yswenli/webredismanager/releases) 13 | 14 | ## 引用相关dll 15 | 项目中相关引用可以访问: https://www.nuget.org/packages?q=saea 16 | 17 | ## 运行截图 18 | 19 | WebRedisManager
20 | WebRedisManager
21 | WebRedisManager
22 | WebRedisManager
23 | WebRedisManager
24 | WebRedisManager
25 | WebRedisManager
26 | -------------------------------------------------------------------------------- /SAEA.WebRedisManager/wwwroot/Content/js/css/modules/code.css: -------------------------------------------------------------------------------- 1 | /** layui-v2.3.0 MIT License By https://www.layui.com */ 2 | html #layuicss-skincodecss { 3 | display: none; 4 | position: absolute; 5 | width: 1989px 6 | } 7 | 8 | .layui-code-h3, .layui-code-view { 9 | position: relative; 10 | font-size: 12px 11 | } 12 | 13 | .layui-code-view { 14 | display: block; 15 | margin: 10px 0; 16 | padding: 0; 17 | border: 1px solid #e2e2e2; 18 | border-left-width: 6px; 19 | background-color: #F2F2F2; 20 | color: #333; 21 | font-family: Courier New 22 | } 23 | 24 | .layui-code-h3 { 25 | padding: 0 10px; 26 | height: 32px; 27 | line-height: 32px; 28 | border-bottom: 1px solid #e2e2e2 29 | } 30 | 31 | .layui-code-h3 a { 32 | position: absolute; 33 | right: 10px; 34 | top: 0; 35 | color: #999 36 | } 37 | 38 | .layui-code-view .layui-code-ol { 39 | position: relative; 40 | overflow: auto 41 | } 42 | 43 | .layui-code-view .layui-code-ol li { 44 | position: relative; 45 | margin-left: 45px; 46 | line-height: 20px; 47 | padding: 0 5px; 48 | border-left: 1px solid #e2e2e2; 49 | list-style-type: decimal-leading-zero; 50 | *list-style-type: decimal; 51 | background-color: #fff 52 | } 53 | 54 | .layui-code-view pre { 55 | margin: 0 56 | } 57 | 58 | .layui-code-notepad { 59 | border: 1px solid #0C0C0C; 60 | border-left-color: #3F3F3F; 61 | background-color: #0C0C0C; 62 | color: #C2BE9E 63 | } 64 | 65 | .layui-code-notepad .layui-code-h3 { 66 | border-bottom: none 67 | } 68 | 69 | .layui-code-notepad .layui-code-ol li { 70 | background-color: #3F3F3F; 71 | border-left: none 72 | } 73 | -------------------------------------------------------------------------------- /SAEA.WebRedisManager/Services/UpdateService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using HtmlAgilityPack; 4 | 5 | using SAEA.Common; 6 | using SAEA.Redis.WebManager.Models; 7 | 8 | namespace SAEA.WebRedisManager.Services 9 | { 10 | public class UpdateService 11 | { 12 | public JsonResult GetLatest() 13 | { 14 | JsonResult result = new JsonResult() 15 | { 16 | Data = "" 17 | }; 18 | 19 | try 20 | { 21 | var url = "https://github.com/yswenli/WebRedisManager/releases"; 22 | 23 | HtmlWeb web = new HtmlWeb(); 24 | 25 | HtmlDocument doc = web.Load(url); 26 | 27 | var alinks = doc.DocumentNode.SelectNodes("//div[@class='markdown-body']/p/a"); 28 | 29 | if (alinks == null) 30 | { 31 | result.Message = "找不到相关连接地址"; 32 | result.Code = 2; 33 | return result; 34 | } 35 | 36 | foreach (var item in alinks) 37 | { 38 | var str = item.InnerText; 39 | var ver = str.Replace("SAEA.WebRedisManager v", "").Replace(".zip", ""); 40 | if (new Version(ver) > new Version(SAEAVersion.ToString().Replace("v", ""))) 41 | { 42 | result.Data = item.Attributes["href"].Value; 43 | } 44 | } 45 | result.Code = 1; 46 | } 47 | catch (Exception ex) 48 | { 49 | result.Message = ""; 50 | result.Code = 2; 51 | LogHelper.Error("UpdateService.Update", ex); 52 | } 53 | 54 | return result; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /SAEA.WebRedisManager/AppService.cs: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | *项目名称:SAEA.WebRedisManager 3 | *CLR 版本:4.0.30319.42000 4 | *机器名称:WALLE-PC 5 | *命名空间:SAEA.WebRedisManager 6 | *类 名 称:AppService 7 | *版 本 号:V1.0.0.0 8 | *创建人: yswenli 9 | *电子邮箱:yswenli@outlook.com 10 | *修改时间:2021/7/8 18:24:22 11 | *描述: 12 | *===================================================================== 13 | *修改时间:2021/7/8 18:24:22 14 | *修 改 人: yswenli 15 | *版 本 号: V1.0.0.0 16 | *描 述: 17 | *****************************************************************************/ 18 | using System.Threading; 19 | using System.Threading.Tasks; 20 | 21 | using Microsoft.Extensions.Hosting; 22 | 23 | using SAEA.Common; 24 | using SAEA.MVC; 25 | using SAEA.WebRedisManager.Libs; 26 | 27 | namespace SAEA.WebRedisManager 28 | { 29 | public class AppService : BackgroundService 30 | { 31 | protected override async Task ExecuteAsync(CancellationToken stoppingToken) 32 | { 33 | await Task.Yield(); 34 | 35 | var config = SAEAMvcApplicationConfigBuilder.Read(); 36 | 37 | config.Port = 16379; 38 | 39 | config.IsStaticsCached = false; 40 | 41 | SAEAMvcApplicationConfigBuilder.Write(config); 42 | 43 | //启动api 44 | 45 | SAEAMvcApplication mvcApplication = new(config); 46 | 47 | mvcApplication.Start(); 48 | 49 | //启动websocket 50 | 51 | WebSocketsHelper webSocketsHelper = new WebSocketsHelper(port: 26379); 52 | 53 | webSocketsHelper.Start(); 54 | 55 | try 56 | { 57 | ConsoleHelper.WriteLine("SAEA.WebRedisManager Already started"); 58 | 59 | ConsoleHelper.WriteLine($"Please open on Browser:http://127.0.0.1:{config.Port}/"); 60 | } 61 | catch { } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /SAEA.WebRedisManager/wwwroot/Content/js/requestpamars.js: -------------------------------------------------------------------------------- 1 | //获取url中"?"符后的字串 2 | function GetRequest() { 3 | var url = location.search; //获取url中"?"符后的字串 4 | var theRequest = new Object(); 5 | if (url.indexOf("?") !== -1) { 6 | var str = url.substr(1); 7 | strs = str.split("&"); 8 | for (var i = 0; i < strs.length; i++) { 9 | theRequest[strs[i].split("=")[0]] = decodeURI(strs[i].split("=")[1]); 10 | } 11 | } 12 | return theRequest; 13 | } 14 | 15 | function HttpGet(url, data, success, error) { 16 | const headers = new Headers(); 17 | headers.append('Content-Type', 'application/x-www-form-urlencoded'); 18 | fetch(url, { 19 | method: 'GET', 20 | headers: headers, 21 | cache: 'no-store' 22 | }) 23 | .then(response => response.json()) 24 | .then((result) => { 25 | console.log("Success:", result); 26 | success(result); 27 | }) 28 | .catch(e => { 29 | console.log('Error:', e); 30 | if (error) error(e); 31 | }); 32 | } 33 | 34 | 35 | function HttpPost(url, data, success, error) { 36 | const headers = new Headers(); 37 | headers.append('Content-Type', 'application/x-www-form-urlencoded'); 38 | fetch(url, { 39 | method: 'POST', 40 | headers: headers, 41 | body: data 42 | }) 43 | .then(response => { 44 | if ((response.headers.get("content-type") == "application/json; charset=utf-8")) { 45 | return response.json(); 46 | } 47 | else { 48 | return response.text(); 49 | } 50 | }) 51 | .then((result) => { 52 | console.log("Success:", result); 53 | success(result); 54 | }) 55 | .catch(e => { 56 | console.log('Error:', e); 57 | if (error) error(e); 58 | }); 59 | } -------------------------------------------------------------------------------- /SAEA.WebRedisManager/wwwroot/Remove.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 删除redis服务器 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 |
20 |
21 |
22 | 23 |
24 | 25 |
26 |
27 |
28 |
29 | 30 |
31 |
32 |
33 |
34 |
35 |
36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /SAEA.WebRedisManager/Libs/WorkerServiceHelper.cs: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | *项目名称:SAEA.WebRedisManager.Libs 3 | *CLR 版本:4.0.30319.42000 4 | *机器名称:WALLE-PC 5 | *命名空间:SAEA.WebRedisManager.Libs 6 | *类 名 称:WorkerServiceHelper 7 | *版 本 号:V1.0.0.0 8 | *创建人: yswenli 9 | *电子邮箱:yswenli@outlook.com 10 | *修改时间:2021/7/8 18:24:22 11 | *描述: 12 | *===================================================================== 13 | *修改时间:2021/7/8 18:24:22 14 | *修 改 人: yswenli 15 | *版 本 号: V1.0.0.0 16 | *描 述: 17 | *****************************************************************************/ 18 | using System.Runtime.InteropServices; 19 | 20 | using Microsoft.Extensions.DependencyInjection; 21 | using Microsoft.Extensions.Hosting; 22 | 23 | namespace SAEA.WebRedisManager.Libs 24 | { 25 | public static class WorkerServiceHelper 26 | { 27 | /// 28 | /// 创建传统类型的服务容器 29 | /// 30 | /// 31 | /// 32 | /// 33 | public static IHostBuilder CreateHostBuilder(string[] args) where T : class, IHostedService 34 | { 35 | bool isWinPlantform = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); 36 | 37 | if (isWinPlantform) 38 | { 39 | return Host.CreateDefaultBuilder(args) 40 | .UseWindowsService() 41 | .ConfigureServices((hostContext, services) => 42 | { 43 | services.AddHostedService(); 44 | }); 45 | } 46 | else 47 | { 48 | return Host.CreateDefaultBuilder(args) 49 | .UseSystemd() 50 | .ConfigureServices((hostContext, services) => 51 | { 52 | services.AddHostedService(); 53 | }); 54 | 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /SAEA.WebRedisManager/wwwroot/configs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | redis server configs 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 |
20 |
21 |
22 | 23 |
24 |
25 |
26 | 27 | 28 |
29 |
30 |
31 |
32 |
33 |
34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /SAEA.WebRedisManager/wwwroot/Content/js/lay/modules/laytpl.js: -------------------------------------------------------------------------------- 1 | /** layui-v2.3.0 MIT License By https://www.layui.com */ 2 | ; layui.define(function (e) { "use strict"; var r = { open: "{{", close: "}}" }, c = { exp: function (e) { return new RegExp(e, "g") }, query: function (e, c, t) { var o = ["#([\\s\\S])+?", "([^{#}])*?"][e || 0]; return n((c || "") + r.open + o + r.close + (t || "")) }, escape: function (e) { return String(e || "").replace(/&(?!#?[a-zA-Z0-9]+;)/g, "&").replace(//g, ">").replace(/'/g, "'").replace(/"/g, """) }, error: function (e, r) { var c = "Laytpl Error锛�"; return "object" == typeof console && console.error(c + e + "\n" + (r || "")), c + e } }, n = c.exp, t = function (e) { this.tpl = e }; t.pt = t.prototype, window.errors = 0, t.pt.parse = function (e, t) { var o = this, p = e, a = n("^" + r.open + "#", ""), l = n(r.close + "$", ""); e = e.replace(/\s+|\r|\t|\n/g, " ").replace(n(r.open + "#"), r.open + "# ").replace(n(r.close + "}"), "} " + r.close).replace(/\\/g, "\\\\").replace(n(r.open + "!(.+?)!" + r.close), function (e) { return e = e.replace(n("^" + r.open + "!"), "").replace(n("!" + r.close), "").replace(n(r.open + "|" + r.close), function (e) { return e.replace(/(.)/g, "\\$1") }) }).replace(/(?="|')/g, "\\").replace(c.query(), function (e) { return e = e.replace(a, "").replace(l, ""), '";' + e.replace(/\\/g, "") + ';view+="' }).replace(c.query(1), function (e) { var c = '"+('; return e.replace(/\s/g, "") === r.open + r.close ? "" : (e = e.replace(n(r.open + "|" + r.close), ""), /^=/.test(e) && (e = e.replace(/^=/, ""), c = '"+_escape_('), c + e.replace(/\\/g, "") + ')+"') }), e = '"use strict";var view = "' + e + '";return view;'; try { return o.cache = e = new Function("d, _escape_", e), e(t, c.escape) } catch (u) { return delete o.cache, c.error(u, p) } }, t.pt.render = function (e, r) { var n, t = this; return e ? (n = t.cache ? t.cache(e, c.escape) : t.parse(t.tpl, e), r ? void r(n) : n) : c.error("no data") }; var o = function (e) { return "string" != typeof e ? c.error("Template not found") : new t(e) }; o.config = function (e) { e = e || {}; for (var c in e) r[c] = e[c] }, o.v = "1.2.0", e("laytpl", o) }); -------------------------------------------------------------------------------- /SAEA.WebRedisManager/Libs/ServerInfoDataHelper.cs: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | *项目名称:SAEA.WebRedisManager.Libs 3 | *CLR 版本:4.0.30319.42000 4 | *机器名称:WALLE-PC 5 | *命名空间:SAEA.WebRedisManager.Libs 6 | *类 名 称:ServerInfoDataHelper 7 | *版 本 号:V1.0.0.0 8 | *创建人: yswenli 9 | *电子邮箱:yswenli@outlook.com 10 | *创建时间:2020/6/5 20:09:32 11 | *描述: 12 | *===================================================================== 13 | *修改时间:2020/6/5 20:09:32 14 | *修 改 人: yswenli 15 | *版 本 号: V1.0.0.0 16 | *描 述: 17 | *****************************************************************************/ 18 | using SAEA.Common; 19 | using SAEA.Redis.WebManager.Libs; 20 | using SAEA.Redis.WebManager.Models; 21 | using System; 22 | 23 | namespace SAEA.WebRedisManager.Libs 24 | { 25 | /// 26 | /// redis server info 27 | /// 28 | public static class ServerInfoDataHelper 29 | { 30 | /// 31 | /// GetInfo 32 | /// 33 | /// 34 | /// 35 | public static JsonResult GetInfo(string name) 36 | { 37 | try 38 | { 39 | RedisServerInfo redisServerInfo = new RedisServerInfo(); 40 | 41 | redisServerInfo.Cpu = CurrentRedisClient.GetCpu(name).ToString("f2"); 42 | 43 | redisServerInfo.Memory= CurrentRedisClient.GetUsedMem(name).ToString("f2"); 44 | 45 | redisServerInfo.Cmds = CurrentRedisClient.GetOpsCmd(name).ToString(); 46 | 47 | redisServerInfo.Input = CurrentRedisClient.GetInput(name).ToString("f2"); 48 | 49 | redisServerInfo.Output = CurrentRedisClient.GetOutput(name).ToString("f2"); 50 | 51 | return new JsonResult() { Code = 1, Data = redisServerInfo, Message = "OK" }; 52 | } 53 | catch (Exception ex) 54 | { 55 | LogHelper.Error($"RedisController.GetInfo name:{name}", ex); 56 | return new JsonResult() { Code = 2, Message = ex.Message }; 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /SAEA.WebRedisManager/Services/ConsoleService.cs: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | *项目名称:SAEA.WebRedisManager.Controllers 3 | *CLR 版本:4.0.30319.42000 4 | *机器名称:WALLE-PC 5 | *命名空间:SAEA.WebRedisManager.Services 6 | *类 名 称:ConsoleService 7 | *版 本 号:V1.0.0.0 8 | *创建人: yswenli 9 | *电子邮箱:yswenli@outlook.com 10 | *创建时间:2020/1/6 13:39:36 11 | *描述: 12 | *===================================================================== 13 | *修改时间:2020/1/6 13:39:36 14 | *修 改 人: yswenli 15 | *版 本 号: V1.0.0.0 16 | *描 述: 17 | *****************************************************************************/ 18 | using System; 19 | using System.Collections.Generic; 20 | 21 | using SAEA.Common; 22 | using SAEA.Redis.WebManager.Libs; 23 | using SAEA.Redis.WebManager.Models; 24 | using SAEA.WebRedisManager.Libs; 25 | 26 | namespace SAEA.WebRedisManager.Services 27 | { 28 | class ConsoleService 29 | { 30 | /// 31 | /// 发送命令 32 | /// 33 | /// 34 | /// 35 | /// 36 | public string SendCmd(string name, string cmd) 37 | { 38 | try 39 | { 40 | if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(cmd)) 41 | { 42 | return CurrentRedisClient.Send(name, cmd); 43 | } 44 | return "输入的命令不能为空~"; 45 | } 46 | catch (Exception ex) 47 | { 48 | LogHelper.Error("ConsoleService.SendCmd", ex, name, cmd); 49 | return "请求发生异常:" + ex.Message; 50 | } 51 | } 52 | /// 53 | /// 获取输入的命令 54 | /// 55 | /// 56 | /// 57 | public JsonResult> GetCMD(string input) 58 | { 59 | try 60 | { 61 | return new JsonResult>() { Code = 1, Data = RedisCmdHelper.GetList(input) }; 62 | } 63 | catch (Exception ex) 64 | { 65 | LogHelper.Error("ConsoleService.GetCMD", ex, input); 66 | return new JsonResult>() { Code = 2, Message = ex.Message }; 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /SAEA.WebRedisManager/Attr/AuthAttribute.cs: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | *项目名称:SAEA.WebRedisManager.Attr 3 | *CLR 版本:4.0.30319.42000 4 | *机器名称:WALLE-PC 5 | *命名空间:SAEA.WebRedisManager.Attr 6 | *类 名 称:AuthAttribute 7 | *版 本 号:V1.0.0.0 8 | *创建人: yswenli 9 | *电子邮箱:yswenli@outlook.com 10 | *创建时间:2020/1/8 9:29:01 11 | *描述: 12 | *===================================================================== 13 | *修改时间:2020/1/8 9:29:01 14 | *修 改 人: yswenli 15 | *版 本 号: V1.0.0.0 16 | *描 述: 17 | *****************************************************************************/ 18 | using SAEA.Common; 19 | using SAEA.MVC; 20 | using SAEA.Redis.WebManager.Models; 21 | using SAEA.WebRedisManager.Libs; 22 | using SAEA.WebRedisManager.Models; 23 | 24 | using System.Diagnostics; 25 | 26 | namespace SAEA.WebRedisManager.Attr 27 | { 28 | /// 29 | /// 验证并记录日志 30 | /// 31 | public class AuthAttribute : ActionFilterAttribute 32 | { 33 | Stopwatch _stopwatch; 34 | 35 | bool _isAdmin = false; 36 | 37 | public AuthAttribute(bool isEnabled) 38 | { 39 | 40 | } 41 | 42 | public AuthAttribute(bool isAdmin, bool isEnabled) : this(isEnabled) 43 | { 44 | _isAdmin = isAdmin; 45 | } 46 | 47 | 48 | public override ActionResult OnActionExecuting() 49 | { 50 | _stopwatch = Stopwatch.StartNew(); 51 | 52 | if (!HttpContext.Current.Request.Cookies.ContainsKey("uid")) 53 | { 54 | return new JsonResult(new JsonResult() { Code = 3, Message = "当前操作需要登录!" }); 55 | } 56 | if (_isAdmin) 57 | { 58 | var user = UserHelper.Get(HttpContext.Current.Request.Cookies["uid"].Value); 59 | 60 | if (user.Role != Role.Admin) 61 | { 62 | return new JsonResult(new JsonResult() { Code = 4, Message = "当前操作权限不足,请联系管理员!" }); 63 | } 64 | } 65 | 66 | return EmptyResult.Default; 67 | } 68 | 69 | public override void OnActionExecuted(ref ActionResult result) 70 | { 71 | _stopwatch.Stop(); 72 | 73 | LogHelper.Info(HttpContext.Current.Request.Url, HttpContext.Current.Request.Parmas, result, _stopwatch.ElapsedMilliseconds); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /SAEA.WebRedisManager/wwwroot/Content/js/formhelper.js: -------------------------------------------------------------------------------- 1 | function FormHelper() { 2 | //获取指定form中的所有的对象 3 | function getElements(formId) { 4 | var form = document.getElementById(formId); 5 | var elements = new Array(); 6 | var tagElements = form.getElementsByTagName('input'); 7 | for (var j = 0; j < tagElements.length; j++) { 8 | elements.push(tagElements[j]); 9 | } 10 | tagElements = form.getElementsByTagName('textarea'); 11 | for (j = 0; j < tagElements.length; j++) { 12 | elements.push(tagElements[j]); 13 | } 14 | return elements; 15 | } 16 | 17 | //获取单个input中的【name,value】数组 18 | function inputSelector(element) { 19 | if (element.checked) 20 | return [element.name, element.value]; 21 | } 22 | 23 | function input(element) { 24 | switch (element.type.toLowerCase()) { 25 | case 'submit': 26 | case 'hidden': 27 | case 'password': 28 | case 'text': 29 | case 'textarea': 30 | return [element.name, element.value]; 31 | case 'checkbox': 32 | case 'radio': 33 | return inputSelector(element); 34 | } 35 | return false; 36 | } 37 | 38 | //组合URL 39 | function serializeElement(element) { 40 | var method = element.tagName.toLowerCase(); 41 | var parameter = input(element); 42 | 43 | if (parameter) { 44 | var key = encodeURIComponent(parameter[0]); 45 | if (key.length === 0) return; 46 | 47 | if (parameter[1].constructor !== Array) 48 | parameter[1] = [parameter[1]]; 49 | 50 | var values = parameter[1]; 51 | var results = []; 52 | for (var i = 0; i < values.length; i++) { 53 | results.push(key + '=' + encodeURIComponent(values[i])); 54 | } 55 | return results.join('&'); 56 | } 57 | } 58 | 59 | this.SerializeForm = function (formId) { 60 | 61 | var elements = getElements(formId); 62 | 63 | var queryComponents = new Array(); 64 | 65 | for (var i = 0; i < elements.length; i++) { 66 | var queryComponent = serializeElement(elements[i]); 67 | if (queryComponent) 68 | queryComponents.push(queryComponent); 69 | } 70 | 71 | return queryComponents.join('&'); 72 | }; 73 | } 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /SAEA.WebRedisManager/wwwroot/Console.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Redis command line mode 10 | 11 | 12 | 13 | 14 |
15 |
16 |
17 |
18 | Redis command line mode more redis commands 19 |
20 |
21 |
22 | 23 | 24 |
25 |
26 |
27 |
28 |
29 |
30 | 31 |
32 |
33 |
34 |
35 |
36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /SAEA.WebRedisManager/Controllers/ConfigController.cs: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | *项目名称:SAEA.WebRedisManager.Controllers 3 | *CLR 版本:4.0.30319.42000 4 | *机器名称:WALLE-PC 5 | *命名空间:SAEA.WebRedisManager.Controllers 6 | *类 名 称:ConfigController 7 | *版 本 号:V1.0.0.0 8 | *创建人: yswenli 9 | *电子邮箱:yswenli@outlook.com 10 | *创建时间:2020/1/6 13:39:36 11 | *描述: 12 | *===================================================================== 13 | *修改时间:2020/1/6 13:39:36 14 | *修 改 人: yswenli 15 | *版 本 号: V1.0.0.0 16 | *描 述: 17 | *****************************************************************************/ 18 | using SAEA.MVC; 19 | using SAEA.Redis.WebManager.Models; 20 | using SAEA.WebRedisManager.Attr; 21 | using SAEA.WebRedisManager.Services; 22 | 23 | namespace SAEA.WebRedisManager.Controllers 24 | { 25 | /// 26 | /// 配置处理api 27 | /// 28 | public class ConfigController : Controller 29 | { 30 | /// 31 | /// 设置配置 32 | /// 33 | /// 34 | /// 35 | [HttpPost] 36 | [Auth(false, true)] 37 | public ActionResult Set(Config config) 38 | { 39 | return Json(new ConfigService().Set(config)); 40 | } 41 | 42 | /// 43 | /// 导入配置 44 | /// 45 | /// 46 | /// 47 | [Auth(false, true)] 48 | [HttpPost] 49 | public ActionResult SetConfigs(string configs) 50 | { 51 | return Json(new ConfigService().SetConfigs(configs)); 52 | } 53 | /// 54 | /// 获取全部配置 55 | /// 56 | /// 57 | [Auth(false, true)] 58 | public ActionResult GetList() 59 | { 60 | return Json(new ConfigService().GetList()); 61 | } 62 | 63 | /// 64 | /// 获取配置 65 | /// 66 | /// 67 | /// 68 | [Auth(true)] 69 | public ActionResult Get(string name) 70 | { 71 | return Json(new ConfigService().Get(name)); 72 | } 73 | 74 | /// 75 | /// 删除配置 76 | /// 77 | /// 78 | /// 79 | [Auth(false, true)] 80 | [HttpPost] 81 | public ActionResult Rem(string name) 82 | { 83 | return Json(new ConfigService().Rem(name)); 84 | } 85 | 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /SAEA.WebRedisManager/Controllers/VerificationController.cs: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | *项目名称:SAEA.WebRedisManager.Controllers 3 | *CLR 版本:4.0.30319.42000 4 | *机器名称:WALLE-PC 5 | *命名空间:SAEA.WebRedisManager.Controllers 6 | *类 名 称:VerificationController 7 | *版 本 号:V1.0.0.0 8 | *创建人: yswenli 9 | *电子邮箱:yswenli@outlook.com 10 | *创建时间:2020/5/9 11:38:45 11 | *描述: 12 | *===================================================================== 13 | *修改时间:2020/5/9 11:38:45 14 | *修 改 人: yswenli 15 | *版 本 号: V1.0.0.0 16 | *描 述: 17 | *****************************************************************************/ 18 | using System; 19 | 20 | using SAEA.MVC; 21 | using SAEA.Redis.WebManager.Models; 22 | using SAEA.WebRedisManager.Libs.Verification; 23 | 24 | namespace SAEA.WebRedisManager.Controllers 25 | { 26 | public class VerificationController : Controller 27 | { 28 | /// 29 | /// 获取验证码 30 | /// 31 | /// 32 | /// 33 | public ActionResult Index(int id = 0) 34 | { 35 | try 36 | { 37 | VerificationCode va = new VerificationCode(105, 30, 4, id); 38 | using (var m = va.Create()) 39 | { 40 | string code = va.IdentifyingCode; 41 | HttpContext.Current.Session["code"] = code; 42 | return Data(m); 43 | } 44 | } 45 | catch (Exception ex) 46 | { 47 | return Json(new JsonResult() { Code = 999, Data = false, Message = ex.Message }); 48 | } 49 | } 50 | 51 | /// 52 | /// 验证验证码 53 | /// 54 | /// 55 | /// 56 | public ActionResult Check(string code) 57 | { 58 | try 59 | { 60 | if (!string.IsNullOrEmpty(code)) 61 | { 62 | code = code.ToLower(); 63 | 64 | var rcode = HttpContext.Current.Session["code"]; 65 | 66 | if (rcode != null && rcode.ToString().ToLower() == code) 67 | { 68 | return Json(new JsonResult() { Code = 1, Data = true }); 69 | } 70 | } 71 | 72 | return Json(new JsonResult() { Code = 1, Data = false, Message = "验证码不正确!" }); 73 | } 74 | catch (Exception ex) 75 | { 76 | return Json(new JsonResult() { Code = 1, Data = false, Message = "验证码不正确!" }); 77 | } 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /SAEA.WebRedisManager/wwwroot/Content/js/lay/modules/util.js: -------------------------------------------------------------------------------- 1 | /** layui-v2.3.0 MIT License By https://www.layui.com */ 2 | ; layui.define("jquery", function (e) { "use strict"; var t = layui.$, i = { fixbar: function (e) { var i, a, o = "layui-fixbar", r = "layui-fixbar-top", l = t(document), n = t("body"); e = t.extend({ showHeight: 200 }, e), e.bar1 = e.bar1 === !0 ? "" : e.bar1, e.bar2 = e.bar2 === !0 ? "" : e.bar2, e.bgcolor = e.bgcolor ? "background-color:" + e.bgcolor : ""; var c = [e.bar1, e.bar2, ""], g = t(['
    ', e.bar1 ? '
  • ' + c[0] + "
  • " : "", e.bar2 ? '
  • ' + c[1] + "
  • " : "", '
  • ' + c[2] + "
  • ", "
"].join("")), u = g.find("." + r), s = function () { var t = l.scrollTop(); t >= e.showHeight ? i || (u.show(), i = 1) : i && (u.hide(), i = 0) }; t("." + o)[0] || ("object" == typeof e.css && g.css(e.css), n.append(g), s(), g.find("li").on("click", function () { var i = t(this), a = i.attr("lay-type"); "top" === a && t("html,body").animate({ scrollTop: 0 }, 200), e.click && e.click.call(this, a) }), l.on("scroll", function () { clearTimeout(a), a = setTimeout(function () { s() }, 100) })) }, countdown: function (e, t, i) { var a = this, o = "function" == typeof t, r = new Date(e).getTime(), l = new Date(!t || o ? (new Date).getTime() : t).getTime(), n = r - l, c = [Math.floor(n / 864e5), Math.floor(n / 36e5) % 24, Math.floor(n / 6e4) % 60, Math.floor(n / 1e3) % 60]; o && (i = t); var g = setTimeout(function () { a.countdown(e, l + 1e3, i) }, 1e3); return i && i(n > 0 ? c : [0, 0, 0, 0], t, g), n <= 0 && clearTimeout(g), g }, timeAgo: function (e, t) { var i = this, a = [[], []], o = (new Date).getTime() - new Date(e).getTime(); return o > 6912e5 ? (o = new Date(e), a[0][0] = i.digit(o.getFullYear(), 4), a[0][1] = i.digit(o.getMonth() + 1), a[0][2] = i.digit(o.getDate()), t || (a[1][0] = i.digit(o.getHours()), a[1][1] = i.digit(o.getMinutes()), a[1][2] = i.digit(o.getSeconds())), a[0].join("-") + " " + a[1].join(":")) : o >= 864e5 ? (o / 1e3 / 60 / 60 / 24 | 0) + "澶╁墠" : o >= 36e5 ? (o / 1e3 / 60 / 60 | 0) + "灏忔椂鍓�" : o >= 12e4 ? (o / 1e3 / 60 | 0) + "鍒嗛挓鍓�" : o < 0 ? "鏈潵" : "鍒氬垰" }, digit: function (e, t) { var i = ""; e = String(e), t = t || 2; for (var a = e.length; a < t; a++)i += "0"; return e < Math.pow(10, t) ? i + (0 | e) : e }, toDateString: function (e, t) { var i = this, a = new Date(e || new Date), o = [i.digit(a.getFullYear(), 4), i.digit(a.getMonth() + 1), i.digit(a.getDate())], r = [i.digit(a.getHours()), i.digit(a.getMinutes()), i.digit(a.getSeconds())]; return t = t || "yyyy-MM-dd HH:mm:ss", t.replace(/yyyy/g, o[0]).replace(/MM/g, o[1]).replace(/dd/g, o[2]).replace(/HH/g, r[0]).replace(/mm/g, r[1]).replace(/ss/g, r[2]) }, escape: function (e) { return String(e || "").replace(/&(?!#?[a-zA-Z0-9]+;)/g, "&").replace(//g, ">").replace(/'/g, "'").replace(/"/g, """) } }; e("util", i) }); -------------------------------------------------------------------------------- /SAEA.WebRedisManager/wwwroot/RedisAdd.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 添加redis服务器 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 |
20 |
21 |
22 | 23 |
24 | 25 |
26 |
27 |
28 | 29 |
30 | 31 |
32 |
33 |
34 | 35 |
36 | 37 |
38 |
39 |
40 | 41 |
42 | 43 |
44 |
45 |
46 |
47 | 48 | 49 |
50 |
51 |
52 |
53 |
54 |
55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /SAEA.WebRedisManager/wwwroot/ItemsView.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | ItemsView 10 | 11 | 12 | 22 | 23 | 24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | 35 |
36 |
37 |
38 |
39 | 40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | 55 | 59 |
60 | 61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /SAEA.WebRedisManager/Libs/WebSocketsHelper.cs: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | *项目名称:SAEA.WebRedisManager.Libs 3 | *CLR 版本:4.0.30319.42000 4 | *机器名称:WALLE-PC 5 | *命名空间:SAEA.WebRedisManager.Libs 6 | *类 名 称:WebSocketsHelper 7 | *版 本 号:V1.0.0.0 8 | *创建人: yswenli 9 | *电子邮箱:yswenli@outlook.com 10 | *创建时间:2020/6/5 20:24:16 11 | *描述: 12 | *===================================================================== 13 | *修改时间:2020/6/5 20:24:16 14 | *修 改 人: yswenli 15 | *版 本 号: V1.0.0.0 16 | *描 述: 17 | *****************************************************************************/ 18 | using SAEA.Common; 19 | using SAEA.Common.Serialization; 20 | using SAEA.WebSocket; 21 | using SAEA.WebSocket.Model; 22 | using SAEA.WebSocket.Type; 23 | using System; 24 | using System.Collections.Concurrent; 25 | using System.Linq; 26 | using System.Text; 27 | using System.Threading.Tasks; 28 | 29 | namespace SAEA.WebRedisManager.Libs 30 | { 31 | public class WebSocketsHelper 32 | { 33 | ConcurrentDictionary _dic1 = new ConcurrentDictionary(); 34 | 35 | WSServer _wsServer = null; 36 | 37 | public WebSocketsHelper(int port = 26379) 38 | { 39 | _wsServer = new WSServer(port); 40 | _wsServer.OnConnected += _wsServer_OnConnected; 41 | _wsServer.OnMessage += WsServer_OnMessage; 42 | _wsServer.OnDisconnected += WsServer_OnDisconnected; 43 | } 44 | 45 | private void _wsServer_OnConnected(string cid) 46 | { 47 | _dic1.TryAdd(cid, DateTimeHelper.Now); 48 | } 49 | 50 | private void WsServer_OnDisconnected(string cid) 51 | { 52 | _dic1.TryRemove(cid, out DateTime dt); 53 | } 54 | 55 | private void WsServer_OnMessage(string cid, WebSocket.Model.WSProtocal msg) 56 | { 57 | if (msg != null) 58 | { 59 | if (_dic1.ContainsKey(cid) && msg.Content != null && msg.Content.Any()) 60 | { 61 | var name = Encoding.UTF8.GetString(msg.Content); 62 | 63 | if (string.IsNullOrEmpty(name)) 64 | { 65 | return; 66 | } 67 | 68 | Task.Factory.StartNew(() => 69 | { 70 | while (_dic1.ContainsKey(cid)) 71 | { 72 | try 73 | { 74 | var data = SerializeHelper.Serialize(ServerInfoDataHelper.GetInfo(name)); 75 | 76 | _wsServer.Reply(cid, new WSProtocal(WSProtocalType.Text, Encoding.UTF8.GetBytes(data))); 77 | 78 | } 79 | catch 80 | { 81 | _dic1.TryRemove(cid, out DateTime v); 82 | break; 83 | } 84 | ThreadHelper.Sleep(1000); 85 | } 86 | }); 87 | } 88 | else 89 | { 90 | _wsServer.Disconnect(cid); 91 | } 92 | } 93 | } 94 | 95 | public void Start() 96 | { 97 | _wsServer.Start(); 98 | } 99 | 100 | public void Stop() 101 | { 102 | _wsServer.Stop(); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /SAEA.WebRedisManager/Controllers/RedisClusterController.cs: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | *项目名称:SAEA.WebRedisManager.Controllers 3 | *CLR 版本:4.0.30319.42000 4 | *机器名称:WALLE-PC 5 | *命名空间:SAEA.WebRedisManager.Controllers 6 | *类 名 称:RedisClusterController 7 | *版 本 号:V1.0.0.0 8 | *创建人: yswenli 9 | *电子邮箱:yswenli@outlook.com 10 | *创建时间:2020/1/6 13:39:36 11 | *描述: 12 | *===================================================================== 13 | *修改时间:2020/1/6 13:39:36 14 | *修 改 人: yswenli 15 | *版 本 号: V1.0.0.0 16 | *描 述: 17 | *****************************************************************************/ 18 | using SAEA.MVC; 19 | using SAEA.WebRedisManager.Attr; 20 | using SAEA.WebRedisManager.Services; 21 | 22 | namespace SAEA.WebRedisManager.Controllers 23 | { 24 | /// 25 | /// Redis cluster controller 26 | /// 27 | [Auth(true)] 28 | public class RedisClusterController : Controller 29 | { 30 | /// 31 | /// 获取cluster 节点信息 32 | /// 33 | /// 34 | /// 35 | public ActionResult GetClusterNodes(string name) 36 | { 37 | return Json(new RedisClusterService().GetClusterNodes(name)); 38 | } 39 | 40 | /// 41 | /// 添加节点 42 | /// 43 | /// 44 | /// 45 | /// 46 | public ActionResult AddMaster(string name, string ipPort) 47 | { 48 | return Json(new RedisClusterService().AddMaster(name, ipPort)); 49 | } 50 | 51 | /// 52 | /// 添加从节点 53 | /// 54 | /// 55 | /// 56 | /// 57 | /// 58 | public ActionResult AddSlave(string name, string slaveNodeID, string masterID) 59 | { 60 | return Json(new RedisClusterService().AddSlave(name, slaveNodeID, masterID)); 61 | } 62 | 63 | /// 64 | /// 删除节点 65 | /// 66 | /// 67 | /// 68 | /// 69 | public ActionResult DeleteNode(string name, string nodeID) 70 | { 71 | return Json(new RedisClusterService().DeleteNode(name, nodeID)); 72 | } 73 | 74 | /// 75 | /// 添加槽点 76 | /// 77 | /// 78 | /// 79 | /// 80 | /// 81 | public ActionResult AddSlots(string name, string nodeID, string slotStr) 82 | { 83 | return Json(new RedisClusterService().AddSlots(name, nodeID, slotStr)); 84 | } 85 | 86 | /// 87 | /// 删除槽点 88 | /// 89 | /// 90 | /// 91 | /// 92 | /// 93 | public ActionResult DelSlots(string name, string nodeID, string slotStr) 94 | { 95 | return Json(new RedisClusterService().DelSlots(name, nodeID, slotStr)); 96 | } 97 | 98 | /// 99 | /// 保存配置 100 | /// 101 | /// 102 | /// 103 | /// 104 | public ActionResult SaveConfig(string name, string nodeID) 105 | { 106 | return Json(new RedisClusterService().SaveConfig(name, nodeID)); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /SAEA.WebRedisManager/Libs/ConfigHelper.cs: -------------------------------------------------------------------------------- 1 | using SAEA.Common; 2 | using SAEA.Common.Serialization; 3 | using SAEA.Redis.WebManager.Models; 4 | using System.Collections.Generic; 5 | using System.IO; 6 | using System.Linq; 7 | 8 | namespace SAEA.Redis.WebManager.Libs 9 | { 10 | /// 11 | /// 配置管理 12 | /// 13 | static class ConfigHelper 14 | { 15 | static List _list = new List(); 16 | 17 | 18 | public static string GetCurrentPath(string children) 19 | { 20 | var path = Path.Combine(Path.GetDirectoryName(AssemblyHelper.Current.Location), children); 21 | if (!Directory.Exists(path)) 22 | Directory.CreateDirectory(path); 23 | return path; 24 | } 25 | 26 | /// 27 | /// 添加或更新配置 28 | /// 29 | /// 30 | public static void Set(Config config) 31 | { 32 | var old = _list.Where(b => b.Name == config.Name).FirstOrDefault(); 33 | 34 | if (old == null) 35 | { 36 | _list.Add(config); 37 | } 38 | else 39 | { 40 | _list.Remove(old); 41 | _list.Add(config); 42 | } 43 | 44 | Save(); 45 | } 46 | 47 | /// 48 | /// 设置缓存 49 | /// 50 | /// 51 | public static void Set(List configs) 52 | { 53 | _list = configs; 54 | 55 | Save(); 56 | } 57 | 58 | /// 59 | /// 读取配置列表 60 | /// 61 | /// 62 | public static List ReadList() 63 | { 64 | var filePath = Path.Combine(GetCurrentPath("Config"), "config.json"); 65 | 66 | if (File.Exists(filePath)) 67 | { 68 | var json = File.ReadAllText(filePath); 69 | 70 | if (!string.IsNullOrEmpty(json)) 71 | { 72 | _list = SerializeHelper.Deserialize>(json); 73 | if (_list != null && _list.Count > 0) 74 | return _list; 75 | } 76 | } 77 | 78 | return new List(); 79 | } 80 | 81 | /// 82 | /// 保存到文件 83 | /// 84 | public static void Save() 85 | { 86 | var json = SerializeHelper.Serialize(_list); 87 | 88 | var filePath = Path.Combine(GetCurrentPath("Config"), "config.json"); 89 | 90 | if (File.Exists(filePath)) 91 | { 92 | File.Delete(filePath); 93 | } 94 | File.AppendAllText(filePath, json); 95 | } 96 | 97 | /// 98 | /// 获取配置 99 | /// 100 | /// 101 | /// 102 | public static Config Get(string name) 103 | { 104 | if (_list == null || !_list.Any()) ReadList(); 105 | 106 | if (_list == null || !_list.Any()) return null; 107 | 108 | return _list.Where(b => b.Name == name).FirstOrDefault(); 109 | } 110 | 111 | 112 | /// 113 | /// 移除配置 114 | /// 115 | /// 116 | public static void Rem(string name) 117 | { 118 | if (_list == null || _list.Count < 1) ReadList(); 119 | 120 | var config = _list.Where(b => b.Name == name).FirstOrDefault(); 121 | 122 | if (config != null) 123 | { 124 | _list.Remove(config); 125 | } 126 | 127 | Save(); 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /SAEA.WebRedisManager/wwwroot/Rename.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | rename 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 |
20 |
21 |
22 | 23 |
24 | 25 |
26 |
27 |
28 | 29 |
30 | 31 |
32 |
33 |
34 |
35 | 36 |
37 |
38 |
39 |
40 |
41 |
42 | 43 | 44 | 45 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /SAEA.WebRedisManager/wwwroot/Keys.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Keys 10 | 11 | 12 | 23 | 24 | 25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | 36 |
37 |
38 |
39 |
40 | 41 | 42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 |
typekeyttlaction
74 |
75 |
76 |
77 |
78 |
79 |
80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /SAEA.WebRedisManager/wwwroot/Content/css/login.css: -------------------------------------------------------------------------------- 1 | /** layuiAdmin.std-v1.2.1 LPPL License By http://www.layui.com/admin/ */ 2 | #LAY_app, body, html { 3 | height: 100% 4 | } 5 | 6 | .layui-layout-body { 7 | overflow: auto 8 | } 9 | 10 | #LAY-user-login, .layadmin-user-display-show { 11 | display: block !important 12 | } 13 | 14 | .layadmin-user-login { 15 | position: relative; 16 | left: 0; 17 | top: 0; 18 | padding: 110px 0; 19 | min-height: 100%; 20 | box-sizing: border-box 21 | } 22 | 23 | .layadmin-user-login-main { 24 | width: 375px; 25 | margin: 0 auto; 26 | box-sizing: border-box 27 | } 28 | 29 | .layadmin-user-login-box { 30 | padding: 20px 31 | } 32 | 33 | .layadmin-user-login-header { 34 | text-align: center 35 | } 36 | 37 | .layadmin-user-login-header h2 { 38 | margin-bottom: 10px; 39 | font-weight: 300; 40 | font-size: 30px; 41 | color: #000 42 | } 43 | 44 | .layadmin-user-login-header p { 45 | font-weight: 300; 46 | color: #999 47 | } 48 | 49 | .layadmin-user-login-body .layui-form-item { 50 | position: relative 51 | } 52 | 53 | .layadmin-user-login-icon { 54 | position: absolute; 55 | left: 1px; 56 | top: 1px; 57 | width: 38px; 58 | line-height: 36px; 59 | text-align: center; 60 | color: #d2d2d2 61 | } 62 | 63 | .layadmin-user-login-body .layui-form-item .layui-input { 64 | padding-left: 38px 65 | } 66 | 67 | .layadmin-user-login-codeimg { 68 | max-height: 38px; 69 | width: 100%; 70 | cursor: pointer; 71 | box-sizing: border-box 72 | } 73 | 74 | .layadmin-user-login-other { 75 | position: relative; 76 | font-size: 0; 77 | line-height: 38px; 78 | padding-top: 20px 79 | } 80 | 81 | .layadmin-user-login-other > * { 82 | display: inline-block; 83 | vertical-align: middle; 84 | margin-right: 10px; 85 | font-size: 14px 86 | } 87 | 88 | .layadmin-user-login-other .layui-icon { 89 | position: relative; 90 | top: 2px; 91 | font-size: 26px 92 | } 93 | 94 | .layadmin-user-login-other a:hover { 95 | opacity: .8 96 | } 97 | 98 | .layadmin-user-jump-change { 99 | float: right 100 | } 101 | 102 | .layadmin-user-login-footer { 103 | position: absolute; 104 | left: 0; 105 | bottom: 0; 106 | width: 100%; 107 | line-height: 30px; 108 | padding: 20px; 109 | text-align: center; 110 | box-sizing: border-box; 111 | color: rgba(0,0,0,.5) 112 | } 113 | 114 | .layadmin-user-login-footer span { 115 | padding: 0 5px 116 | } 117 | 118 | .layadmin-user-login-footer a { 119 | padding: 0 5px; 120 | color: rgba(0,0,0,.5) 121 | } 122 | 123 | .layadmin-user-login-footer a:hover { 124 | color: rgba(0,0,0,1) 125 | } 126 | 127 | .layadmin-user-login-main[bgimg] { 128 | background-color: #fff; 129 | box-shadow: 0 0 5px rgba(0,0,0,.05) 130 | } 131 | 132 | .ladmin-user-login-theme { 133 | position: fixed; 134 | bottom: 0; 135 | left: 0; 136 | width: 100%; 137 | text-align: center 138 | } 139 | 140 | .ladmin-user-login-theme ul { 141 | display: inline-block; 142 | padding: 5px; 143 | background-color: #fff 144 | } 145 | 146 | .ladmin-user-login-theme ul li { 147 | display: inline-block; 148 | vertical-align: top; 149 | width: 64px; 150 | height: 43px; 151 | cursor: pointer; 152 | transition: all .3s; 153 | -webkit-transition: all .3s; 154 | background-color: #f2f2f2 155 | } 156 | 157 | .ladmin-user-login-theme ul li:hover { 158 | opacity: .9 159 | } 160 | 161 | @media screen and (max-width: 768px) { 162 | .layadmin-user-login { 163 | padding-top: 60px 164 | } 165 | 166 | .layadmin-user-login-main { 167 | width: 300px 168 | } 169 | 170 | .layadmin-user-login-box { 171 | padding: 10px 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /SAEA.WebRedisManager/Controllers/UserController.cs: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | *项目名称:SAEA.WebRedisManager.Controllers 3 | *CLR 版本:4.0.30319.42000 4 | *机器名称:WALLE-PC 5 | *命名空间:SAEA.WebRedisManager.Controllers 6 | *类 名 称:UserController 7 | *版 本 号:V1.0.0.0 8 | *创建人: yswenli 9 | *电子邮箱:yswenli@outlook.com 10 | *创建时间:2020/1/6 13:39:36 11 | *描述: 12 | *===================================================================== 13 | *修改时间:2020/1/6 13:39:36 14 | *修 改 人: yswenli 15 | *版 本 号: V1.0.0.0 16 | *描 述: 17 | *****************************************************************************/ 18 | using SAEA.MVC; 19 | using SAEA.Redis.WebManager.Models; 20 | using SAEA.WebRedisManager.Attr; 21 | using SAEA.WebRedisManager.Models; 22 | using SAEA.WebRedisManager.Services; 23 | 24 | namespace SAEA.WebRedisManager.Controllers 25 | { 26 | /// 27 | /// 用户控制器 28 | /// 29 | public class UserController : Controller 30 | { 31 | /// 32 | /// 登录 33 | /// 34 | /// 35 | /// 36 | /// 37 | /// 38 | //public ActionResult Login(string userName, string password, string code) 39 | //{ 40 | // if (string.IsNullOrEmpty(userName) || string.IsNullOrEmpty(password)) return Json(new JsonResult() { Code = 2, Message = "用户名或密码不能为空" }); 41 | 42 | // return Json(new UserService().Login(userName, password, code)); 43 | //} 44 | 45 | 46 | /// 47 | /// 登录 48 | /// 49 | /// 50 | /// 51 | /// 52 | public ActionResult Login(string userName, string password) 53 | { 54 | if (string.IsNullOrEmpty(userName) || string.IsNullOrEmpty(password)) return Json(new JsonResult() { Code = 2, Message = "用户名或密码不能为空" }); 55 | 56 | return Json(new UserService().Login(userName, password)); 57 | } 58 | 59 | /// 60 | /// 注销 61 | /// 62 | /// 63 | [Auth(false, true)] 64 | public ActionResult Logout() 65 | { 66 | return Json(new UserService().Logout()); 67 | } 68 | 69 | /// 70 | /// 添加或修改用户 71 | /// 72 | /// 73 | /// 74 | /// 75 | /// 76 | [Auth(true, true)] 77 | public ActionResult SetUser(User user, string confirmPwd, int role) 78 | { 79 | if (string.IsNullOrEmpty(user.UserName) || string.IsNullOrEmpty(user.Password) || string.IsNullOrEmpty(user.NickName)) 80 | 81 | return Json(new JsonResult() { Code = 2, Message = "用户名、密码或昵称不能为空" }); 82 | 83 | if (string.IsNullOrEmpty(confirmPwd) || user.Password != confirmPwd) 84 | 85 | return Json(new JsonResult() { Code = 2, Message = "两次输入的密码不一致" }); 86 | 87 | return Json(new UserService().SetUser(user, confirmPwd, role)); 88 | } 89 | 90 | /// 91 | /// 获取用户列表 92 | /// 93 | /// 94 | [Auth(true, true)] 95 | public ActionResult GetUserList() 96 | { 97 | return Json(new UserService().GetUserList()); 98 | } 99 | 100 | /// 101 | /// 移除用户列表 102 | /// 103 | /// 104 | /// 105 | [Auth(true, true)] 106 | public ActionResult Rem(string uid) 107 | { 108 | return Json(new UserService().Rem(uid)); 109 | } 110 | 111 | /// 112 | /// 是否是空项,是否是首次使用系统 113 | /// 114 | /// 115 | public ActionResult IsEmpty() 116 | { 117 | return Json(new UserService().IsEmpty()); 118 | } 119 | 120 | /// 121 | /// Authenticated 122 | /// 123 | /// 124 | public ActionResult Authenticated() 125 | { 126 | return Json(new UserService().Authenticated()); 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /SAEA.WebRedisManager/wwwroot/Content/js/redis.console.js: -------------------------------------------------------------------------------- 1 | // 对Date的扩展,将 Date 转化为指定格式的String 2 | // 月(M)、日(d)、小时(H)、分(m)、秒(s)、季度(q) 可以用 1-2 个占位符, 3 | // 年(y)可以用 1-4 个占位符,毫秒(S)只能用 1 个占位符(是 1-3 位的数字) 4 | // 例子: 5 | // (new Date()).Format("yyyy-MM-dd HH:mm:ss.S") ==> 2006-07-02 08:09:04.423 6 | // (new Date()).Format("yyyy-M-d H:m:s.S") ==> 2006-7-2 8:9:4.18 7 | Date.prototype.Format = function (fmt) { //author: meizz 8 | var o = { 9 | "M+": this.getMonth() + 1, //月份 10 | "d+": this.getDate(), //日 11 | "h+": this.getHours(), //小时 12 | "m+": this.getMinutes(), //分 13 | "s+": this.getSeconds(), //秒 14 | "q+": Math.floor((this.getMonth() + 3) / 3), //季度 15 | "S": this.getMilliseconds() //毫秒 16 | }; 17 | if (/(y+)/.test(fmt)) 18 | fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length)); 19 | for (var k in o) 20 | if (new RegExp("(" + k + ")").test(fmt)) 21 | fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length))); 22 | return fmt; 23 | } 24 | 25 | // 26 | layui.use(['jquery', 'layer', 'form'], function () { 27 | 28 | var layer = layui.layer, form = layui.form, $ = layui.$; 29 | 30 | var redis_name = encodeURI(GetRequest().name); 31 | 32 | var layerIndex = -1; 33 | 34 | $("#cmdTxt").keypress(function (e) { 35 | if (e.which === 13) { 36 | $("#runBtn").click(); 37 | } 38 | }); 39 | 40 | $("#runBtn").click(function () { 41 | 42 | var cmd = $("#cmdTxt").val(); 43 | 44 | if (cmd !== undefined && cmd !== "") { 45 | 46 | cmd = encodeURI(cmd); 47 | 48 | layerIndex = layer.msg('loading', { 49 | icon: 16 50 | , shade: 0.01 51 | }); 52 | 53 | var t1 = new Date(); 54 | 55 | var input = `${t1.Format("yyyy-MM-dd hh:mm:ss.S")}\r\nCommand:${decodeURI(cmd)},`; 56 | 57 | HttpPost("/console/sendcmd", `name=${redis_name}&cmd=${cmd}`, (result) => { 58 | var t2 = new Date(); 59 | var output = `Cost:${t2.getTime() - t1.getTime()}ms,Result:\r\n${result}\r\n\r\n${$("#resultTxt").val()}`; 60 | 61 | $("#resultTxt").val(input + output); 62 | $("#resultTxt")[0].scrollTop = 0; 63 | 64 | layer.close(layerIndex); 65 | }); 66 | } 67 | 68 | }); 69 | 70 | 71 | $("#clearBtn").click(() => { 72 | $("#resultTxt").val(""); 73 | }); 74 | 75 | $(".autocomplete").keyup(function () { 76 | 77 | var cur = $(this); 78 | 79 | var wordStr = cur.val(); 80 | 81 | if ($("#autocomplete_div").html() === undefined) { 82 | $("body").append("
") 83 | } 84 | 85 | $("#autocomplete_div").css("left", cur.offset().left).css("top", cur.offset().top + 35); 86 | 87 | $("#autocomplete_div").mouseleave(function () { 88 | $("#autocomplete_div").slideUp(300); 89 | }); 90 | 91 | $("#autocomplete_div").html(""); 92 | 93 | HttpPost("/api/console/getcmd?t=" + (new Date().getMilliseconds()), `input=${wordStr}`, function (data) { 94 | if (data.Code === 1) { 95 | var dhtml = ""; 96 | data.Data.forEach(function (element) { 97 | dhtml += `
${element}
`; 98 | }); 99 | $("#autocomplete_div").html(dhtml).slideDown(100); 100 | $("#autocomplete_div div").css({ "cursor": "pointer", "margin": "3px", "padding": "5px", "border": "1px solid #aaa", "border-radius": "4px", "background": "linear-gradient(to bottom, #fff 0%,#e6e6e6 100%)" }); 101 | $("#autocomplete_div div").hover(function () { $(this).css({ "background": "linear-gradient(to bottom, #0773e0 0%,#0563C1 100%)", "color": "#fff" }); }, function () { $(this).css({ "background": "linear-gradient(to bottom, #fff 0%,#e6e6e6 100%)", "color": "#000" }); }); 102 | } 103 | else { 104 | $("#autocomplete_div").slideUp(50); 105 | } 106 | $("#autocomplete_div div").click(function () { 107 | cur.val($(this).html()); 108 | $("#autocomplete_div").slideUp(50); 109 | }); 110 | }); 111 | }); 112 | $(".autocomplete").click(function () { 113 | $(this).keyup(); 114 | }); 115 | }); -------------------------------------------------------------------------------- /SAEA.WebRedisManager/wwwroot/Index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WebRedisManager 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 57 |
58 |
59 | 60 |
61 |
62 | 71 |
72 |
73 |
74 | 75 |
76 |
77 | 78 |
79 |
    80 |
  • RedisDB
  • 81 |
82 |
83 | 84 |
85 |
86 | 87 | 88 | 89 | 95 | 96 | 97 |
98 | 99 | -------------------------------------------------------------------------------- /SAEA.WebRedisManager/wwwroot/Content/js/lay/modules/laypage.js: -------------------------------------------------------------------------------- 1 | /** layui-v2.3.0 MIT License By https://www.layui.com */ 2 | ; layui.define(function (e) { "use strict"; var a = document, t = "getElementById", n = "getElementsByTagName", i = "laypage", r = "layui-disabled", u = function (e) { var a = this; a.config = e || {}, a.config.index = ++s.index, a.render(!0) }; u.prototype.type = function () { var e = this.config; if ("object" == typeof e.elem) return void 0 === e.elem.length ? 2 : 3 }, u.prototype.view = function () { var e = this, a = e.config, t = a.groups = "groups" in a ? 0 | a.groups : 5; a.layout = "object" == typeof a.layout ? a.layout : ["prev", "page", "next"], a.count = 0 | a.count, a.curr = 0 | a.curr || 1, a.limits = "object" == typeof a.limits ? a.limits : [10, 20, 30, 40, 50], a.limit = 0 | a.limit || 10, a.pages = Math.ceil(a.count / a.limit) || 1, a.curr > a.pages && (a.curr = a.pages), t < 0 ? t = 1 : t > a.pages && (t = a.pages), a.prev = "prev" in a ? a.prev : "上一页", a.next = "next" in a ? a.next : "下一页"; var n = a.pages > t ? Math.ceil((a.curr + (t > 1 ? 1 : 0)) / (t > 0 ? t : 1)) : 1, i = { prev: function () { return a.prev ? '' + a.prev + "" : "" }(), page: function () { var e = []; if (a.count < 1) return ""; n > 1 && a.first !== !1 && 0 !== t && e.push('' + (a.first || 1) + ""); var i = Math.floor((t - 1) / 2), r = n > 1 ? a.curr - i : 1, u = n > 1 ? function () { var e = a.curr + (t - i - 1); return e > a.pages ? a.pages : e }() : t; for (u - r < t - 1 && (r = u - t + 1), a.first !== !1 && r > 2 && e.push(''); r <= u; r++)r === a.curr ? e.push('" + r + "") : e.push('' + r + ""); return a.pages > t && a.pages > u && a.last !== !1 && (u + 1 < a.pages && e.push(''), 0 !== t && e.push('' + (a.last || a.pages) + "")), e.join("") }(), next: function () { return a.next ? '' + a.next + "" : "" }(), count: '共 ' + a.count + " 条", limit: function () { var e = ['" }(), refresh: ['', '', ""].join(""), skip: function () { return ['到第', '', '页', ""].join("") }() }; return ['
', function () { var e = []; return layui.each(a.layout, function (a, t) { i[t] && e.push(i[t]) }), e.join("") }(), "
"].join("") }, u.prototype.jump = function (e, a) { if (e) { var t = this, i = t.config, r = e.children, u = e[n]("button")[0], l = e[n]("input")[0], p = e[n]("select")[0], c = function () { var e = 0 | l.value.replace(/\s|\D/g, ""); e && (i.curr = e, t.render()) }; if (a) return c(); for (var o = 0, y = r.length; o < y; o++)"a" === r[o].nodeName.toLowerCase() && s.on(r[o], "click", function () { var e = 0 | this.getAttribute("data-page"); e < 1 || e > i.pages || (i.curr = e, t.render()) }); p && s.on(p, "change", function () { var e = this.value; i.curr * e > i.count && (i.curr = Math.ceil(i.count / e)), i.limit = e, t.render() }), u && s.on(u, "click", function () { c() }) } }, u.prototype.skip = function (e) { if (e) { var a = this, t = e[n]("input")[0]; t && s.on(t, "keyup", function (t) { var n = this.value, i = t.keyCode; /^(37|38|39|40)$/.test(i) || (/\D/.test(n) && (this.value = n.replace(/\D/, "")), 13 === i && a.jump(e, !0)) }) } }, u.prototype.render = function (e) { var n = this, i = n.config, r = n.type(), u = n.view(); 2 === r ? i.elem && (i.elem.innerHTML = u) : 3 === r ? i.elem.html(u) : a[t](i.elem) && (a[t](i.elem).innerHTML = u), i.jump && i.jump(i, e); var s = a[t]("layui-laypage-" + i.index); n.jump(s), i.hash && !e && (location.hash = "!" + i.hash + "=" + i.curr), n.skip(s) }; var s = { render: function (e) { var a = new u(e); return a.index }, index: layui.laypage ? layui.laypage.index + 1e4 : 0, on: function (e, a, t) { return e.attachEvent ? e.attachEvent("on" + a, function (a) { a.target = a.srcElement, t.call(e, a) }) : e.addEventListener(a, t, !1), this } }; e(i, s) }); -------------------------------------------------------------------------------- /SAEA.WebRedisManager/Libs/AESHelper.cs: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | *项目名称:SAEA.WebRedisManager.Libs 3 | *CLR 版本:4.0.30319.42000 4 | *机器名称:WALLE-PC 5 | *命名空间:SAEA.WebRedisManager.Libs 6 | *类 名 称:AESHelper 7 | *版 本 号:V1.0.0.0 8 | *创建人: yswenli 9 | *电子邮箱:yswenli@outlook.com 10 | *创建时间:2020/1/8 18:24:22 11 | *描述: 12 | *===================================================================== 13 | *修改时间:2020/1/8 18:24:22 14 | *修 改 人: yswenli 15 | *版 本 号: V1.0.0.0 16 | *描 述: 17 | *****************************************************************************/ 18 | using SAEA.Common; 19 | using System; 20 | using System.IO; 21 | using System.Security.Cryptography; 22 | using System.Text; 23 | 24 | namespace SAEA.WebRedisManager.Libs 25 | { 26 | /// 27 | /// AES对称加密算法类 28 | /// 29 | public class AESTool 30 | { 31 | /// 32 | /// AES加密 33 | /// 34 | /// 35 | /// 36 | /// 37 | /// 38 | public static string Encrypt(string encryptString, string encryptKey, bool isbase64 = true) 39 | { 40 | string returnValue; 41 | var temp = Convert.FromBase64String("Rkb4jvUy/ye7Cd7k89QQgQ=="); 42 | var AESProvider = Rijndael.Create(); 43 | try 44 | { 45 | var defaultKey = "3B2hb2oYHpmZrFflfdmSon1x"; 46 | if (string.IsNullOrEmpty(encryptKey)) 47 | encryptKey = defaultKey; 48 | if (encryptKey.Length < 24) 49 | encryptKey = encryptKey + defaultKey.Substring(0, 24 - encryptKey.Length); 50 | if (encryptKey.Length > 24) 51 | encryptKey = encryptKey.Substring(0, 24); 52 | var byteEncryptString = Encoding.UTF8.GetBytes(encryptString); 53 | using (var memoryStream = new MemoryStream()) 54 | { 55 | using ( 56 | var cryptoStream = new CryptoStream(memoryStream, 57 | AESProvider.CreateEncryptor(Encoding.UTF8.GetBytes(encryptKey), temp), 58 | CryptoStreamMode.Write)) 59 | { 60 | cryptoStream.Write(byteEncryptString, 0, byteEncryptString.Length); 61 | cryptoStream.FlushFinalBlock(); 62 | if (isbase64) 63 | returnValue = Convert.ToBase64String(memoryStream.ToArray()); 64 | else 65 | returnValue = memoryStream.ToArray().ByteToHexStr(); 66 | } 67 | } 68 | } 69 | catch (Exception ex) 70 | { 71 | throw ex; 72 | } 73 | return returnValue; 74 | } 75 | 76 | /// 77 | /// AES 解密 78 | /// 79 | /// 80 | /// 81 | /// 82 | /// 83 | public static string Decrypt(string decryptString, string decryptKey, bool isbase64 = true) 84 | { 85 | var returnValue = ""; 86 | var temp = Convert.FromBase64String("Rkb4jvUy/ye7Cd7k89QQgQ=="); 87 | var AESProvider = Rijndael.Create(); 88 | try 89 | { 90 | var defaultKey = "3B2hb2oYHpmZrFflfdmSon1x"; 91 | if (string.IsNullOrEmpty(decryptKey)) 92 | decryptKey = defaultKey; 93 | if (decryptKey.Length < 24) 94 | decryptKey = decryptKey + defaultKey.Substring(0, 24 - decryptKey.Length); 95 | if (decryptKey.Length > 24) 96 | decryptKey = decryptKey.Substring(0, 24); 97 | byte[] byteDecryptString = null; 98 | if (isbase64) 99 | { 100 | byteDecryptString = Convert.FromBase64String(decryptString); 101 | } 102 | else 103 | { 104 | byteDecryptString = decryptString.StrToHexByte(); 105 | } 106 | using (var memoryStream = new MemoryStream()) 107 | { 108 | using ( 109 | var cryptoStream = new CryptoStream(memoryStream, 110 | AESProvider.CreateDecryptor(Encoding.UTF8.GetBytes(decryptKey), temp), 111 | CryptoStreamMode.Write)) 112 | { 113 | cryptoStream.Write(byteDecryptString, 0, byteDecryptString.Length); 114 | cryptoStream.FlushFinalBlock(); 115 | returnValue = Encoding.UTF8.GetString(memoryStream.ToArray()); 116 | } 117 | } 118 | } 119 | catch (Exception ex) 120 | { 121 | throw ex; 122 | } 123 | return returnValue; 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /SAEA.WebRedisManager/Libs/UserHelper.cs: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | *项目名称:SAEA.WebRedisManager.Libs 3 | *CLR 版本:4.0.30319.42000 4 | *机器名称:WALLE-PC 5 | *命名空间:SAEA.WebRedisManager.Libs 6 | *类 名 称:UserHelper 7 | *版 本 号:V1.0.0.0 8 | *创建人: yswenli 9 | *电子邮箱:yswenli@outlook.com 10 | *创建时间:2020/1/6 11:26:44 11 | *描述: 12 | *===================================================================== 13 | *修改时间:2020/1/6 11:26:44 14 | *修 改 人: yswenli 15 | *版 本 号: V1.0.0.0 16 | *描 述: 17 | *****************************************************************************/ 18 | using SAEA.Common; 19 | using SAEA.Common.Serialization; 20 | using SAEA.MVC; 21 | using SAEA.WebRedisManager.Models; 22 | using System; 23 | using System.Collections.Generic; 24 | using System.IO; 25 | using System.Linq; 26 | 27 | namespace SAEA.WebRedisManager.Libs 28 | { 29 | /// 30 | /// 配置管理 31 | /// 32 | static class UserHelper 33 | { 34 | static List _list = new List(); 35 | 36 | 37 | public static string GetCurrentPath(string children) 38 | { 39 | var path = Path.Combine(Path.GetDirectoryName(AssemblyHelper.Current.Location), children); 40 | if (!Directory.Exists(path)) 41 | Directory.CreateDirectory(path); 42 | return path; 43 | } 44 | 45 | /// 46 | /// 添加或更新配置 47 | /// 48 | /// 49 | public static void Set(User user) 50 | { 51 | var old = _list.Where(b => b.ID == user.ID).FirstOrDefault(); 52 | 53 | if (old == null) 54 | { 55 | _list.Add(user); 56 | } 57 | else 58 | { 59 | _list.Remove(old); 60 | _list.Add(user); 61 | } 62 | 63 | Save(); 64 | } 65 | 66 | public static void Set(List users) 67 | { 68 | _list = users; 69 | 70 | Save(); 71 | } 72 | 73 | /// 74 | /// 读取配置列表 75 | /// 76 | /// 77 | public static List ReadList() 78 | { 79 | var filePath = Path.Combine(GetCurrentPath("Config"), "userconfig.json"); 80 | 81 | if (File.Exists(filePath)) 82 | { 83 | var json = File.ReadAllText(filePath); 84 | 85 | if (!string.IsNullOrEmpty(json)) 86 | { 87 | var str = AESTool.Decrypt(json, "yswenli", false); 88 | 89 | _list = SerializeHelper.Deserialize>(str); 90 | 91 | if (_list != null && _list.Count > 0) 92 | return _list; 93 | } 94 | } 95 | 96 | return new List(); 97 | } 98 | 99 | /// 100 | /// 保存到文件 101 | /// 102 | public static void Save() 103 | { 104 | try 105 | { 106 | var json = SerializeHelper.Serialize(_list); 107 | 108 | var str = AESTool.Encrypt(json, "yswenli", false); 109 | 110 | var filePath = Path.Combine(GetCurrentPath("Config"), "userconfig.json"); 111 | 112 | if (File.Exists(filePath)) 113 | { 114 | File.Delete(filePath); 115 | } 116 | File.AppendAllText(filePath, str); 117 | } 118 | catch (Exception ex) 119 | { 120 | LogHelper.Error("UserHelper.Save", ex); 121 | } 122 | } 123 | 124 | /// 125 | /// 获取配置 126 | /// 127 | /// 128 | /// 129 | public static User Get(string ID) 130 | { 131 | if (_list == null || _list.Count < 1) ReadList(); 132 | 133 | return _list.Where(b => b.ID == ID).FirstOrDefault(); 134 | } 135 | 136 | /// 137 | /// 获取配置 138 | /// 139 | /// 140 | /// 141 | /// 142 | public static User Login(string userName, string password) 143 | { 144 | if (_list == null || !_list.Any()) ReadList(); 145 | 146 | if (_list == null || !_list.Any()) return null; 147 | 148 | return _list.Where(b => b.UserName == userName && b.Password == password).FirstOrDefault(); 149 | } 150 | 151 | /// 152 | /// 获取配置 153 | /// 154 | /// 155 | /// 156 | public static bool Exists(string userName) 157 | { 158 | if (_list == null || _list.Count < 1) ReadList(); 159 | 160 | return _list.Exists(b => b.UserName == userName); 161 | } 162 | /// 163 | /// 移除配置 164 | /// 165 | /// 166 | public static void Rem(string ID) 167 | { 168 | if (_list == null || _list.Count < 1) ReadList(); 169 | 170 | var config = _list.Where(b => b.ID == ID).FirstOrDefault(); 171 | 172 | if (config != null) 173 | { 174 | _list.Remove(config); 175 | } 176 | 177 | Save(); 178 | } 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /SAEA.WebRedisManager/wwwroot/Chart.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 报表 10 | 11 | 12 | 17 | 18 | 19 |
20 |
21 |
22 |
23 | Redis Info】 【Redis Console】【Redis Clients】【Alter Passwords】 24 |
25 |
26 |
27 |
28 |
29 | CPU 30 | Second 31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | MEM 41 | Second 42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | CMD 52 | Second 53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | NET 63 | Second 64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 | redis cluster nodes 77 |
78 |
79 |
80 |
81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 |
NodeIDIPPortStatusIsMasterMinSlotsMaxSlotsMasterNodeID操作
101 |
102 |
103 |
104 |
105 |
106 |
107 | 108 | 109 | 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /SAEA.WebRedisManager/Controllers/RedisController.cs: -------------------------------------------------------------------------------- 1 | using SAEA.MVC; 2 | using SAEA.Redis.WebManager.Models; 3 | using SAEA.WebRedisManager.Attr; 4 | using SAEA.WebRedisManager.Services; 5 | 6 | namespace SAEA.WebRedisManager.Controllers 7 | { 8 | /// 9 | /// redis相关api 10 | /// 11 | [Auth(true)] 12 | public class RedisController : Controller 13 | { 14 | /// 15 | /// 连接到redis 16 | /// 17 | /// 18 | /// 19 | [HttpPost] 20 | public ActionResult Connect(string name) 21 | { 22 | return Json(new RedisService().Connect(name)); 23 | } 24 | 25 | /// 26 | /// 获取服务器信息 27 | /// 28 | /// 29 | /// 30 | public ActionResult GetInfoString(string name) 31 | { 32 | return Json(new RedisService().GetInfoString(name)); 33 | } 34 | 35 | /// 36 | /// 获取客户端连接信息 37 | /// 38 | /// 39 | /// 40 | public ActionResult GetClients(string name) 41 | { 42 | return Json(new RedisService().GetClients(name)); 43 | } 44 | 45 | 46 | 47 | 48 | /// 49 | /// 获取db中的元素数量 50 | /// 51 | /// 52 | /// 53 | /// 54 | public ActionResult GetDBSize(string name, int dbIndex) 55 | { 56 | return Json(new RedisService().GetDBSize(name, dbIndex)); 57 | } 58 | 59 | /// 60 | /// 获取keys 61 | /// 62 | /// 63 | /// 64 | /// 65 | /// 66 | /// 67 | public ActionResult GetKeyTypes(int offset, string name, int dbIndex, string key) 68 | { 69 | return Json(new RedisService().GetKeyTypes(offset, name, dbIndex, key)); 70 | } 71 | 72 | /// 73 | /// 获取ttls 74 | /// 75 | /// 76 | /// 77 | /// 78 | /// 79 | public ActionResult GetTtls(string name, int dbIndex, string keys) 80 | { 81 | return Json(new RedisService().GetTtls(name, dbIndex, keys)); 82 | } 83 | 84 | /// 85 | /// 批量删除 86 | /// 87 | /// 88 | /// 89 | /// 90 | /// 91 | public ActionResult BatchRemove(string name, int dbIndex, string key) 92 | { 93 | return Json(new RedisService().BatchRemove(name, dbIndex, key)); 94 | } 95 | 96 | /// 97 | /// 提交或修改数据 98 | /// 99 | /// 100 | /// 101 | [HttpPost] 102 | public ActionResult Set(RedisData redisData) 103 | { 104 | return Json(new RedisService().Set(redisData)); 105 | } 106 | 107 | /// 108 | /// 获取数据 109 | /// 110 | /// 111 | /// 112 | public ActionResult GetCount(RedisData redisData) 113 | { 114 | return Json(new RedisService().GetCount(redisData)); 115 | } 116 | 117 | /// 118 | /// 删除项 119 | /// 120 | /// 121 | /// 122 | [HttpPost] 123 | public ActionResult Del(RedisData redisData) 124 | { 125 | return Json(new RedisService().Del(redisData)); 126 | } 127 | 128 | /// 129 | /// 获取string 130 | /// 131 | /// 132 | /// 133 | public ActionResult Get(RedisData redisData) 134 | { 135 | return Json(new RedisService().Get(redisData)); 136 | } 137 | 138 | /// 139 | /// 获取子项 140 | /// 141 | /// 142 | /// 143 | /// 144 | public ActionResult GetItems(int offset, RedisData redisData) 145 | { 146 | return Json(new RedisService().GetItems(offset, redisData)); 147 | } 148 | 149 | /// 150 | /// 修改名称 151 | /// 152 | /// 153 | /// 154 | /// 155 | [HttpPost] 156 | public ActionResult Rename(RedisData redisData, string newID) 157 | { 158 | return Json(new RedisService().Rename(redisData, newID)); 159 | } 160 | /// 161 | /// 修改数据项 162 | /// 163 | /// 164 | /// 165 | [HttpPost] 166 | public ActionResult Edit(RedisData redisData) 167 | { 168 | return Json(new RedisService().Edit(redisData)); 169 | } 170 | 171 | /// 172 | /// 移除项 173 | /// 174 | /// 175 | /// 176 | [HttpPost] 177 | public ActionResult DelItem(RedisData redisData) 178 | { 179 | return Json(new RedisService().DelItem(redisData)); 180 | } 181 | 182 | /// 183 | /// 修改密码 184 | /// 185 | /// 186 | /// 187 | /// 188 | /// 189 | [HttpPost] 190 | public ActionResult AlterPWD(string name, string pwd1, string pwd2) 191 | { 192 | return Json(new RedisService().AlterPWD(name, pwd1, pwd2)); 193 | } 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /SAEA.WebRedisManager/wwwroot/AddItem.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 添加数据 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 |
20 |
21 |
22 | 23 |
24 | 31 |
32 |
33 |
34 | 35 |
36 | 37 |
38 |
39 | 45 |
46 | 47 |
48 | 49 |
50 |
51 |
52 | 53 |
54 | 55 |
56 |
57 |
58 |
59 | 60 | 61 |
62 |
63 |
64 |
65 |
66 |
67 | 68 | 69 | 70 | 71 | 140 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /SAEA.WebRedisManager/wwwroot/IndexContent.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IndexItem 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 |
20 |
项目说明
21 |
22 | 23 |

24 | WebRedisManager是使用的SAEA.RedisSocket、SAEA.MVC等实现Redis及Redis Cluster的可视化、简便管理功能的工具~
25 | 同时它也是一个开源的项目,详情点击:https://www.cnblogs.com/yswenli/p/9460527.html,项目源码地址: 26 | https://github.com/yswenli/WebRedisManager
27 |

28 |

29 | v 7.0.0.2
30 | 1.优化了SAEA.RedisSocket和SAEA.Mvc的性能
31 | 2.调整了SAEA.RedisSocket底层接收队列
32 |

33 |

34 | v 6.2.6.6
35 | 1.大幅度优化了SAEA.RedisSocket和SAEA.Mvc的性能
36 | 2.取消了登录验证码
37 | 3.调整了SAEA.Socket复用相关的细节
38 |

39 |

40 | v 6.2.6.2
41 | 1.修复saea.sockets底层复用池
42 | 2.优化saea.mvc的session处理
43 | 3.调整了一些页面细节
44 | 4.增加SAEA.WebRedisManager新版本检测功能 45 |

46 |

47 | v 6.1.2.9
48 | 1.saea.redissocket优化查询命令
49 | 2.优化项目代码结构
50 | 3.调整了一些页面细节
51 | 4.调整登录页验证码逻辑 52 |

53 |

54 | v 5.3.5.1
55 | 1.saea.redissocket优化相关命令处理
56 | 2.saea.mvc增加远程客户端信息及代理服务器信息
57 | 3.调整了一些页面细节 58 |

59 |

60 | v 5.3.3.8
61 | 1.调整redis服务器资源报表 62 |

63 |

64 | v 5.3.3.7
65 | 1.调整一些细节 66 |

67 |

68 | v 5.3.3.2
69 | 1.优化saea.mvc的解析逻辑
70 | 2.强化saea.redissocket的性能、增加pipeline处理
71 | 3.调整一些细节 72 |

73 |

74 | v 5.3.2.8
75 | 1.将报表状态更新从ajax轮询改为websocket
76 | 2.处理参数中有特殊符号的
77 | 3.调整一些细节 78 |

79 |

80 | v 5.3.2.1
81 | 1.优化慢操作
82 | 2.增加ttl
83 | 3.增加图形验证码
84 | 4.调整一些细节 85 |

86 |

87 | v 5.3.2.0
88 | 1.优化批量删除,改为小批量多批次删除
89 | 2.增加用户相关功能,增加用户的登录、登出、注册、移除
90 | 3.增加角色权限,分成admin和user,admin可以管理所有,user只能管理自已创建的实例
91 | 4.解除控制器级锁 92 |

93 |

94 | v 5.3.1.8
95 | 1.优化配置可批量修改,方便导入导出
96 | 2.优化查询,避免大数据量下的慢命令
97 | 3.优化批量删除,避免大数据量下的慢命令
98 | 4.优化配置项菜单的搜索功能,可针对配置内容搜索 99 |

100 |

101 | v 5.3.1.6
102 | 1.优化集群的连接重定向
103 | 2.优化集群下的命令 104 |

105 |

106 | v 5.2.1.8
107 | 1.优化提交项的特殊符号、中文等 108 |

109 |

110 | v 5.2.1.6
111 | 1.优化配置项的特殊符号、中文等 112 |

113 |

114 | v 5.2.0.1
115 | 1.添加配置项菜单的搜索功能
116 | 2.增加批量删除功能 117 |

118 |
119 |
120 |
121 |
122 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | 56 | # StyleCop 57 | StyleCopReport.xml 58 | 59 | # Files built by Visual Studio 60 | *_i.c 61 | *_p.c 62 | *_h.h 63 | *.ilk 64 | *.meta 65 | *.obj 66 | *.iobj 67 | *.pch 68 | *.pdb 69 | *.ipdb 70 | *.pgc 71 | *.pgd 72 | *.rsp 73 | *.sbr 74 | *.tlb 75 | *.tli 76 | *.tlh 77 | *.tmp 78 | *.tmp_proj 79 | *.log 80 | *.vspscc 81 | *.vssscc 82 | .builds 83 | *.pidb 84 | *.svclog 85 | *.scc 86 | 87 | # Chutzpah Test files 88 | _Chutzpah* 89 | 90 | # Visual C++ cache files 91 | ipch/ 92 | *.aps 93 | *.ncb 94 | *.opendb 95 | *.opensdf 96 | *.sdf 97 | *.cachefile 98 | *.VC.db 99 | *.VC.VC.opendb 100 | 101 | # Visual Studio profiler 102 | *.psess 103 | *.vsp 104 | *.vspx 105 | *.sap 106 | 107 | # Visual Studio Trace Files 108 | *.e2e 109 | 110 | # TFS 2012 Local Workspace 111 | $tf/ 112 | 113 | # Guidance Automation Toolkit 114 | *.gpState 115 | 116 | # ReSharper is a .NET coding add-in 117 | _ReSharper*/ 118 | *.[Rr]e[Ss]harper 119 | *.DotSettings.user 120 | 121 | # JustCode is a .NET coding add-in 122 | .JustCode 123 | 124 | # TeamCity is a build add-in 125 | _TeamCity* 126 | 127 | # DotCover is a Code Coverage Tool 128 | *.dotCover 129 | 130 | # AxoCover is a Code Coverage Tool 131 | .axoCover/* 132 | !.axoCover/settings.json 133 | 134 | # Visual Studio code coverage results 135 | *.coverage 136 | *.coveragexml 137 | 138 | # NCrunch 139 | _NCrunch_* 140 | .*crunch*.local.xml 141 | nCrunchTemp_* 142 | 143 | # MightyMoose 144 | *.mm.* 145 | AutoTest.Net/ 146 | 147 | # Web workbench (sass) 148 | .sass-cache/ 149 | 150 | # Installshield output folder 151 | [Ee]xpress/ 152 | 153 | # DocProject is a documentation generator add-in 154 | DocProject/buildhelp/ 155 | DocProject/Help/*.HxT 156 | DocProject/Help/*.HxC 157 | DocProject/Help/*.hhc 158 | DocProject/Help/*.hhk 159 | DocProject/Help/*.hhp 160 | DocProject/Help/Html2 161 | DocProject/Help/html 162 | 163 | # Click-Once directory 164 | publish/ 165 | 166 | # Publish Web Output 167 | *.[Pp]ublish.xml 168 | *.azurePubxml 169 | # Note: Comment the next line if you want to checkin your web deploy settings, 170 | # but database connection strings (with potential passwords) will be unencrypted 171 | *.pubxml 172 | *.publishproj 173 | 174 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 175 | # checkin your Azure Web App publish settings, but sensitive information contained 176 | # in these scripts will be unencrypted 177 | PublishScripts/ 178 | 179 | # NuGet Packages 180 | *.nupkg 181 | # The packages folder can be ignored because of Package Restore 182 | **/[Pp]ackages/* 183 | # except build/, which is used as an MSBuild target. 184 | !**/[Pp]ackages/build/ 185 | # Uncomment if necessary however generally it will be regenerated when needed 186 | #!**/[Pp]ackages/repositories.config 187 | # NuGet v3's project.json files produces more ignorable files 188 | *.nuget.props 189 | *.nuget.targets 190 | 191 | # Microsoft Azure Build Output 192 | csx/ 193 | *.build.csdef 194 | 195 | # Microsoft Azure Emulator 196 | ecf/ 197 | rcf/ 198 | 199 | # Windows Store app package directories and files 200 | AppPackages/ 201 | BundleArtifacts/ 202 | Package.StoreAssociation.xml 203 | _pkginfo.txt 204 | *.appx 205 | 206 | # Visual Studio cache files 207 | # files ending in .cache can be ignored 208 | *.[Cc]ache 209 | # but keep track of directories ending in .cache 210 | !*.[Cc]ache/ 211 | 212 | # Others 213 | ClientBin/ 214 | ~$* 215 | *~ 216 | *.dbmdl 217 | *.dbproj.schemaview 218 | *.jfm 219 | *.pfx 220 | *.publishsettings 221 | orleans.codegen.cs 222 | 223 | # Including strong name files can present a security risk 224 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 225 | #*.snk 226 | 227 | # Since there are multiple workflows, uncomment next line to ignore bower_components 228 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 229 | #bower_components/ 230 | 231 | # RIA/Silverlight projects 232 | Generated_Code/ 233 | 234 | # Backup & report files from converting an old project file 235 | # to a newer Visual Studio version. Backup files are not needed, 236 | # because we have git ;-) 237 | _UpgradeReport_Files/ 238 | Backup*/ 239 | UpgradeLog*.XML 240 | UpgradeLog*.htm 241 | ServiceFabricBackup/ 242 | *.rptproj.bak 243 | 244 | # SQL Server files 245 | *.mdf 246 | *.ldf 247 | *.ndf 248 | 249 | # Business Intelligence projects 250 | *.rdl.data 251 | *.bim.layout 252 | *.bim_*.settings 253 | *.rptproj.rsuser 254 | 255 | # Microsoft Fakes 256 | FakesAssemblies/ 257 | 258 | # GhostDoc plugin setting file 259 | *.GhostDoc.xml 260 | 261 | # Node.js Tools for Visual Studio 262 | .ntvs_analysis.dat 263 | node_modules/ 264 | 265 | # Visual Studio 6 build log 266 | *.plg 267 | 268 | # Visual Studio 6 workspace options file 269 | *.opt 270 | 271 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 272 | *.vbw 273 | 274 | # Visual Studio LightSwitch build output 275 | **/*.HTMLClient/GeneratedArtifacts 276 | **/*.DesktopClient/GeneratedArtifacts 277 | **/*.DesktopClient/ModelManifest.xml 278 | **/*.Server/GeneratedArtifacts 279 | **/*.Server/ModelManifest.xml 280 | _Pvt_Extensions 281 | 282 | # Paket dependency manager 283 | .paket/paket.exe 284 | paket-files/ 285 | 286 | # FAKE - F# Make 287 | .fake/ 288 | 289 | # JetBrains Rider 290 | .idea/ 291 | *.sln.iml 292 | 293 | # CodeRush 294 | .cr/ 295 | 296 | # Python Tools for Visual Studio (PTVS) 297 | __pycache__/ 298 | *.pyc 299 | 300 | # Cake - Uncomment if you are using it 301 | # tools/** 302 | # !tools/packages.config 303 | 304 | # Tabs Studio 305 | *.tss 306 | 307 | # Telerik's JustMock configuration file 308 | *.jmconfig 309 | 310 | # BizTalk build output 311 | *.btp.cs 312 | *.btm.cs 313 | *.odx.cs 314 | *.xsd.cs 315 | 316 | # OpenCover UI analysis results 317 | OpenCover/ 318 | 319 | # Azure Stream Analytics local run output 320 | ASALocalRun/ 321 | 322 | # MSBuild Binary and Structured Log 323 | *.binlog 324 | 325 | # NVidia Nsight GPU debugger configuration file 326 | *.nvuser 327 | 328 | # MFractors (Xamarin productivity tool) working folder 329 | .mfractor/ 330 | 331 | # Local History for Visual Studio 332 | .localhistory/ 333 | SAEA.WebRedisManager/bin/Debug/netcoreapp2.0/Config/config.json 334 | -------------------------------------------------------------------------------- /SAEA.WebRedisManager/wwwroot/Content/js/layui.js: -------------------------------------------------------------------------------- 1 | /** layui-v2.3.0 MIT License By https://www.layui.com */ 2 | ; !function (e) { "use strict"; var t = document, n = { modules: {}, status: {}, timeout: 10, event: {} }, o = function () { this.v = "2.3.0" }, r = function () { var e = t.currentScript ? t.currentScript.src : function () { for (var e, n = t.scripts, o = n.length - 1, r = o; r > 0; r--)if ("interactive" === n[r].readyState) { e = n[r].src; break } return e || n[o].src }(); return e.substring(0, e.lastIndexOf("/") + 1) }(), a = function (t) { e.console && console.error && console.error("Layui hint: " + t) }, i = "undefined" != typeof opera && "[object Opera]" === opera.toString(), u = { layer: "modules/layer", laydate: "modules/laydate", laypage: "modules/laypage", laytpl: "modules/laytpl", layim: "modules/layim", layedit: "modules/layedit", form: "modules/form", upload: "modules/upload", tree: "modules/tree", table: "modules/table", element: "modules/element", rate: "modules/rate", carousel: "modules/carousel", flow: "modules/flow", util: "modules/util", code: "modules/code", jquery: "modules/jquery", mobile: "modules/mobile", "layui.all": "../layui.all" }; o.prototype.cache = n, o.prototype.define = function (e, t) { var o = this, r = "function" == typeof e, a = function () { var e = function (e, t) { layui[e] = t, n.status[e] = !0 }; return "function" == typeof t && t(function (o, r) { e(o, r), n.callback[o] = function () { t(e) } }), this }; return r && (t = e, e = []), layui["layui.all"] || !layui["layui.all"] && layui["layui.mobile"] ? a.call(o) : (o.use(e, a), o) }, o.prototype.use = function (e, o, l) { function s(e, t) { var o = "PLaySTATION 3" === navigator.platform ? /^complete$/ : /^(complete|loaded)$/; ("load" === e.type || o.test((e.currentTarget || e.srcElement).readyState)) && (n.modules[d] = t, f.removeChild(v), function r() { return ++m > 1e3 * n.timeout / 4 ? a(d + " is not a valid module") : void (n.status[d] ? c() : setTimeout(r, 4)) }()) } function c() { l.push(layui[d]), e.length > 1 ? y.use(e.slice(1), o, l) : "function" == typeof o && o.apply(layui, l) } var y = this, p = n.dir = n.dir ? n.dir : r, f = t.getElementsByTagName("head")[0]; e = "string" == typeof e ? [e] : e, window.jQuery && jQuery.fn.on && (y.each(e, function (t, n) { "jquery" === n && e.splice(t, 1) }), layui.jquery = layui.$ = jQuery); var d = e[0], m = 0; if (l = l || [], n.host = n.host || (p.match(/\/\/([\s\S]+?)\//) || ["//" + location.host + "/"])[0], 0 === e.length || layui["layui.all"] && u[d] || !layui["layui.all"] && layui["layui.mobile"] && u[d]) return c(), y; if (n.modules[d]) !function g() { return ++m > 1e3 * n.timeout / 4 ? a(d + " is not a valid module") : void ("string" == typeof n.modules[d] && n.status[d] ? c() : setTimeout(g, 4)) }(); else { var v = t.createElement("script"), h = (u[d] ? p + "lay/" : /^\{\/\}/.test(y.modules[d]) ? "" : n.base || "") + (y.modules[d] || d) + ".js"; h = h.replace(/^\{\/\}/, ""), v.async = !0, v.charset = "utf-8", v.src = h + function () { var e = n.version === !0 ? n.v || (new Date).getTime() : n.version || ""; return e ? "?v=" + e : "" }(), f.appendChild(v), !v.attachEvent || v.attachEvent.toString && v.attachEvent.toString().indexOf("[native code") < 0 || i ? v.addEventListener("load", function (e) { s(e, h) }, !1) : v.attachEvent("onreadystatechange", function (e) { s(e, h) }), n.modules[d] = h } return y }, o.prototype.getStyle = function (t, n) { var o = t.currentStyle ? t.currentStyle : e.getComputedStyle(t, null); return o[o.getPropertyValue ? "getPropertyValue" : "getAttribute"](n) }, o.prototype.link = function (e, o, r) { var i = this, u = t.createElement("link"), l = t.getElementsByTagName("head")[0]; "string" == typeof o && (r = o); var s = (r || e).replace(/\.|\//g, ""), c = u.id = "layuicss-" + s, y = 0; return u.rel = "stylesheet", u.href = e + (n.debug ? "?v=" + (new Date).getTime() : ""), u.media = "all", t.getElementById(c) || l.appendChild(u), "function" != typeof o ? i : (function p() { return ++y > 1e3 * n.timeout / 100 ? a(e + " timeout") : void (1989 === parseInt(i.getStyle(t.getElementById(c), "width")) ? function () { o() }() : setTimeout(p, 100)) }(), i) }, n.callback = {}, o.prototype.factory = function (e) { if (layui[e]) return "function" == typeof n.callback[e] ? n.callback[e] : null }, o.prototype.addcss = function (e, t, o) { return layui.link(n.dir + "css/" + e, t, o) }, o.prototype.img = function (e, t, n) { var o = new Image; return o.src = e, o.complete ? t(o) : (o.onload = function () { o.onload = null, "function" == typeof t && t(o) }, void (o.onerror = function (e) { o.onerror = null, "function" == typeof n && n(e) })) }, o.prototype.config = function (e) { e = e || {}; for (var t in e) n[t] = e[t]; return this }, o.prototype.modules = function () { var e = {}; for (var t in u) e[t] = u[t]; return e }(), o.prototype.extend = function (e) { var t = this; e = e || {}; for (var n in e) t[n] || t.modules[n] ? a("模块名 " + n + " 已被占用") : t.modules[n] = e[n]; return t }, o.prototype.router = function (e) { var t = this, e = e || location.hash, n = { path: [], search: {}, hash: (e.match(/[^#](#.*$)/) || [])[1] || "" }; return /^#\//.test(e) ? (e = e.replace(/^#\//, ""), n.href = "/" + e, e = e.replace(/([^#])(#.*$)/, "$1").split("/") || [], t.each(e, function (e, t) { /^\w+=/.test(t) ? function () { t = t.split("="), n.search[t[0]] = t[1] }() : n.path.push(t) }), n) : n }, o.prototype.data = function (t, n, o) { if (t = t || "layui", o = o || localStorage, e.JSON && e.JSON.parse) { if (null === n) return delete o[t]; n = "object" == typeof n ? n : { key: n }; try { var r = JSON.parse(o[t]) } catch (a) { var r = {} } return "value" in n && (r[n.key] = n.value), n.remove && delete r[n.key], o[t] = JSON.stringify(r), n.key ? r[n.key] : r } }, o.prototype.sessionData = function (e, t) { return this.data(e, t, sessionStorage) }, o.prototype.device = function (t) { var n = navigator.userAgent.toLowerCase(), o = function (e) { var t = new RegExp(e + "/([^\\s\\_\\-]+)"); return e = (n.match(t) || [])[1], e || !1 }, r = { os: function () { return /windows/.test(n) ? "windows" : /linux/.test(n) ? "linux" : /iphone|ipod|ipad|ios/.test(n) ? "ios" : /mac/.test(n) ? "mac" : void 0 }(), ie: function () { return !!(e.ActiveXObject || "ActiveXObject" in e) && ((n.match(/msie\s(\d+)/) || [])[1] || "11") }(), weixin: o("micromessenger") }; return t && !r[t] && (r[t] = o(t)), r.android = /android/.test(n), r.ios = "ios" === r.os, r }, o.prototype.hint = function () { return { error: a } }, o.prototype.each = function (e, t) { var n, o = this; if ("function" != typeof t) return o; if (e = e || [], e.constructor === Object) { for (n in e) if (t.call(e[n], n, e[n])) break } else for (n = 0; n < e.length && !t.call(e[n], n, e[n]); n++); return o }, o.prototype.sort = function (e, t, n) { var o = JSON.parse(JSON.stringify(e || [])); return t ? (o.sort(function (e, n) { var o = /^-?\d+$/, r = e[t], a = n[t]; return o.test(r) && (r = parseFloat(r)), o.test(a) && (a = parseFloat(a)), r && !a ? 1 : !r && a ? -1 : r > a ? 1 : r < a ? -1 : 0 }), n && o.reverse(), o) : o }, o.prototype.stope = function (t) { t = t || e.event; try { t.stopPropagation() } catch (n) { t.cancelBubble = !0 } }, o.prototype.onevent = function (e, t, n) { return "string" != typeof e || "function" != typeof n ? this : o.event(e, t, null, n) }, o.prototype.event = o.event = function (e, t, o, r) { var a = this, i = null, u = t.match(/\((.*)\)$/) || [], l = (e + "." + t).replace(u[0], ""), s = u[1] || "", c = function (e, t) { var n = t && t.call(a, o); n === !1 && null === i && (i = !1) }; return r ? (n.event[l] = n.event[l] || {}, n.event[l][s] = [r], this) : (layui.each(n.event[l], function (e, t) { return "{*}" === s ? void layui.each(t, c) : ("" === e && layui.each(t, c), void (e === s && layui.each(t, c))) }), i) }, e.layui = new o }(window); -------------------------------------------------------------------------------- /SAEA.WebRedisManager/Services/ConfigService.cs: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | *项目名称:SAEA.WebRedisManager.Controllers 3 | *CLR 版本:4.0.30319.42000 4 | *机器名称:WALLE-PC 5 | *命名空间:SAEA.WebRedisManager.Services 6 | *类 名 称:RedisService 7 | *版 本 号:V1.0.0.0 8 | *创建人: yswenli 9 | *电子邮箱:yswenli@outlook.com 10 | *创建时间:2020/1/6 13:39:36 11 | *描述: 12 | *===================================================================== 13 | *修改时间:2020/1/6 13:39:36 14 | *修 改 人: yswenli 15 | *版 本 号: V1.0.0.0 16 | *描 述: 17 | *****************************************************************************/ 18 | using System; 19 | using System.Collections.Generic; 20 | using System.Linq; 21 | 22 | using SAEA.Common; 23 | using SAEA.Common.Serialization; 24 | using SAEA.MVC; 25 | using SAEA.Redis.WebManager.Libs; 26 | using SAEA.Redis.WebManager.Models; 27 | using SAEA.WebRedisManager.Libs; 28 | using SAEA.WebRedisManager.Models; 29 | 30 | namespace SAEA.WebRedisManager.Services 31 | { 32 | class ConfigService 33 | { 34 | /// 35 | /// 设置配置 36 | /// 37 | /// 38 | /// 39 | public JsonResult Set(Config config) 40 | { 41 | try 42 | { 43 | config.Creator = HttpContext.Current.Request.Cookies["uid"].Value; 44 | 45 | ConfigHelper.Set(config); 46 | 47 | return new JsonResult() { Code = 1, Data = string.Empty, Message = "Ok" }; 48 | } 49 | catch (Exception ex) 50 | { 51 | LogHelper.Error("ConfigService.Set", ex, config); 52 | return new JsonResult() { Code = 2, Data = string.Empty, Message = ex.Message }; 53 | } 54 | } 55 | 56 | /// 57 | /// 导入配置 58 | /// 59 | /// 60 | /// 61 | public JsonResult SetConfigs(string configs) 62 | { 63 | try 64 | { 65 | if (string.IsNullOrWhiteSpace(configs)) 66 | { 67 | return new JsonResult() { Code = 2, Data = string.Empty, Message = "配置不能为空" }; 68 | } 69 | 70 | var confs = SerializeHelper.Deserialize>(configs); 71 | 72 | var user = UserHelper.Get(HttpContext.Current.Request.Cookies["uid"].Value); 73 | 74 | if (user.Role == Role.User) 75 | { 76 | var old = ConfigHelper.ReadList(); 77 | 78 | if (old != null && old.Any()) 79 | { 80 | old.RemoveAll(b => b.Creator == user.ID); 81 | } 82 | 83 | if (confs != null && confs.Any()) 84 | { 85 | confs = confs.Where(b => b.Creator == user.ID).ToList(); 86 | 87 | if (confs != null || confs.Any()) 88 | { 89 | old.AddRange(confs); 90 | } 91 | } 92 | ConfigHelper.Set(old); 93 | } 94 | else 95 | { 96 | ConfigHelper.Set(confs); 97 | } 98 | 99 | return new JsonResult() { Code = 1, Data = string.Empty, Message = "Ok" }; 100 | } 101 | catch (Exception ex) 102 | { 103 | LogHelper.Error("ConfigService.SetConfigs", ex, configs); 104 | return new JsonResult() { Code = 2, Data = string.Empty, Message = ex.Message }; 105 | } 106 | } 107 | 108 | /// 109 | /// 获取全部配置 110 | /// 111 | /// 112 | public JsonResult> GetList() 113 | { 114 | try 115 | { 116 | var user = UserHelper.Get(HttpContext.Current.Request.Cookies["uid"].Value); 117 | 118 | if (user != null) 119 | { 120 | if (user.Role == Role.Admin) 121 | { 122 | return new JsonResult>() { Code = 1, Data = ConfigHelper.ReadList(), Message = "Ok" }; 123 | } 124 | else 125 | { 126 | var list = ConfigHelper.ReadList(); 127 | 128 | if (list != null && list.Any()) 129 | { 130 | list = list.Where(b => b.Creator == user.ID).ToList(); 131 | 132 | return new JsonResult>() { Code = 1, Data = list, Message = "Ok" }; 133 | } 134 | } 135 | } 136 | return new JsonResult>() { Code = 1, Data = new List(), Message = "Ok" }; 137 | } 138 | catch (Exception ex) 139 | { 140 | LogHelper.Error("ConfigService.GetList", ex); 141 | return new JsonResult>() { Code = 2, Message = ex.Message }; 142 | } 143 | } 144 | 145 | /// 146 | /// 获取配置 147 | /// 148 | /// 149 | public JsonResult Get(string name) 150 | { 151 | try 152 | { 153 | return new JsonResult() { Code = 1, Data = ConfigHelper.Get(name), Message = "Ok" }; 154 | } 155 | catch (Exception ex) 156 | { 157 | LogHelper.Error("ConfigService.Get", ex, name); 158 | return new JsonResult() { Code = 2, Message = ex.Message }; 159 | } 160 | } 161 | 162 | /// 163 | /// 删除配置 164 | /// 165 | /// 166 | /// 167 | public JsonResult Rem(string name) 168 | { 169 | try 170 | { 171 | if (string.IsNullOrEmpty(name)) return new JsonResult() { Code = 2, Message = "传入的配置项名称不能为空!" }; 172 | 173 | var user = UserHelper.Get(HttpContext.Current.Request.Cookies["uid"].Value); 174 | 175 | if (user.Role == Role.Admin) 176 | { 177 | ConfigHelper.Rem(name); 178 | return new JsonResult() { Code = 1, Data = true, Message = "Ok" }; 179 | } 180 | else 181 | { 182 | var config = ConfigHelper.Get(name); 183 | 184 | if (config == null) return new JsonResult() { Code = 2, Message = "找不到名称为" + name + "的配置!" }; 185 | 186 | if (config.Creator == user.ID) 187 | { 188 | ConfigHelper.Rem(name); 189 | return new JsonResult() { Code = 1, Data = true, Message = "Ok" }; 190 | } 191 | else 192 | { 193 | return new JsonResult() { Code = 4, Message = "权限不足,请联系管理员!" }; 194 | } 195 | } 196 | 197 | } 198 | catch (Exception ex) 199 | { 200 | LogHelper.Error("ConfigService.Rem", ex, name); 201 | return new JsonResult() { Code = 2, Message = ex.Message }; 202 | } 203 | } 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /SAEA.WebRedisManager/Services/RedisClusterService.cs: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | *项目名称:SAEA.WebRedisManager.Controllers 3 | *CLR 版本:4.0.30319.42000 4 | *机器名称:WALLE-PC 5 | *命名空间:SAEA.WebRedisManager.Services 6 | *类 名 称:RedisClusterService 7 | *版 本 号:V1.0.0.0 8 | *创建人: yswenli 9 | *电子邮箱:yswenli@outlook.com 10 | *创建时间:2020/1/6 13:39:36 11 | *描述: 12 | *===================================================================== 13 | *修改时间:2020/1/6 13:39:36 14 | *修 改 人: yswenli 15 | *版 本 号: V1.0.0.0 16 | *描 述: 17 | *****************************************************************************/ 18 | using System; 19 | using System.Collections.Generic; 20 | using System.Linq; 21 | 22 | using SAEA.Common; 23 | using SAEA.Redis.WebManager.Libs; 24 | using SAEA.Redis.WebManager.Models; 25 | using SAEA.RedisSocket.Model; 26 | 27 | namespace SAEA.WebRedisManager.Services 28 | { 29 | class RedisClusterService 30 | { 31 | /// 32 | /// 获取cluster 节点信息 33 | /// 34 | /// 35 | /// 36 | public JsonResult> GetClusterNodes(string name) 37 | { 38 | try 39 | { 40 | List result = new List(); 41 | 42 | if (CurrentRedisClient.IsCluster(name)) 43 | { 44 | result = CurrentRedisClient.GetClusterNodes(name); 45 | } 46 | 47 | return new JsonResult>() { Code = 1, Data = result, Message = "OK" }; 48 | } 49 | catch (Exception ex) 50 | { 51 | LogHelper.Error("获取cluster 节点信息", ex, name); 52 | return new JsonResult>() { Code = 2, Message = ex.Message }; 53 | } 54 | } 55 | 56 | /// 57 | /// 添加节点 58 | /// 59 | /// 60 | /// 61 | /// 62 | public JsonResult AddMaster(string name, string ipPort) 63 | { 64 | try 65 | { 66 | var result = false; 67 | 68 | if (CurrentRedisClient.IsCluster(name)) 69 | { 70 | var ipp = ipPort.ToIPPort(); 71 | 72 | result = CurrentRedisClient.AddNode(name, ipp.Item1, ipp.Item2); 73 | } 74 | 75 | return new JsonResult() { Code = 1, Data = result, Message = "OK" }; 76 | } 77 | catch (Exception ex) 78 | { 79 | LogHelper.Error("添加节点", ex, name, ipPort); 80 | return new JsonResult() { Code = 2, Message = ex.Message }; 81 | } 82 | } 83 | 84 | /// 85 | /// 添加从节点 86 | /// 87 | /// 88 | /// 89 | /// 90 | /// 91 | public JsonResult AddSlave(string name, string slaveNodeID, string masterID) 92 | { 93 | try 94 | { 95 | var result = false; 96 | 97 | if (CurrentRedisClient.IsCluster(name)) 98 | { 99 | result = CurrentRedisClient.AddSlave(name, slaveNodeID, masterID); 100 | } 101 | 102 | return new JsonResult() { Code = 1, Data = result, Message = "OK" }; 103 | } 104 | catch (Exception ex) 105 | { 106 | LogHelper.Error("添加从节点", ex, name, slaveNodeID, masterID); 107 | return new JsonResult() { Code = 2, Message = ex.Message }; 108 | } 109 | } 110 | 111 | /// 112 | /// 删除节点 113 | /// 114 | /// 115 | /// 116 | /// 117 | public JsonResult DeleteNode(string name, string nodeID) 118 | { 119 | try 120 | { 121 | var result = false; 122 | 123 | if (CurrentRedisClient.IsCluster(name)) 124 | { 125 | result = CurrentRedisClient.DeleteNode(name, nodeID); 126 | } 127 | 128 | return new JsonResult() { Code = 1, Data = result, Message = "OK" }; 129 | } 130 | catch (Exception ex) 131 | { 132 | LogHelper.Error("删除节点", ex, name, nodeID); 133 | return new JsonResult() { Code = 2, Message = ex.Message }; 134 | } 135 | } 136 | 137 | /// 138 | /// 添加槽点 139 | /// 140 | /// 141 | /// 142 | /// 143 | /// 144 | public JsonResult AddSlots(string name, string nodeID, string slotStr) 145 | { 146 | try 147 | { 148 | var result = false; 149 | 150 | if (CurrentRedisClient.IsCluster(name)) 151 | { 152 | var slotList = new List(); 153 | 154 | var index = slotStr.IndexOf("-"); 155 | 156 | if (index > -1) 157 | { 158 | var begin = int.Parse(slotStr.Substring(0, index)); 159 | 160 | var end = int.Parse(slotStr.Substring(index + 1)); 161 | 162 | for (int i = begin; i <= end; i++) 163 | { 164 | slotList.Add(i); 165 | } 166 | } 167 | else 168 | { 169 | var slotArr = slotStr.Split(",", StringSplitOptions.RemoveEmptyEntries); 170 | if (slotArr != null && slotArr.Any()) 171 | { 172 | foreach (var item in slotArr) 173 | { 174 | slotList.Add(int.Parse(item)); 175 | } 176 | } 177 | } 178 | result = CurrentRedisClient.AddSlots(name, nodeID, slotList.ToArray()); 179 | } 180 | 181 | return new JsonResult() { Code = 1, Data = result, Message = "OK" }; 182 | } 183 | catch (Exception ex) 184 | { 185 | LogHelper.Error("添加槽点", ex, name, nodeID,slotStr); 186 | return new JsonResult() { Code = 2, Message = ex.Message }; 187 | } 188 | } 189 | 190 | /// 191 | /// 删除槽点 192 | /// 193 | /// 194 | /// 195 | /// 196 | public JsonResult DelSlots(string name, string nodeID, string slotStr) 197 | { 198 | try 199 | { 200 | var result = false; 201 | 202 | if (CurrentRedisClient.IsCluster(name)) 203 | { 204 | var slotList = new List(); 205 | 206 | var index = slotStr.IndexOf("-"); 207 | 208 | if (index > -1) 209 | { 210 | var begin = int.Parse(slotStr.Substring(0, index)); 211 | 212 | var end = int.Parse(slotStr.Substring(index + 1)); 213 | 214 | for (int i = begin; i <= end; i++) 215 | { 216 | slotList.Add(i); 217 | } 218 | } 219 | else 220 | { 221 | var slotArr = slotStr.Split(",", StringSplitOptions.RemoveEmptyEntries); 222 | if (slotArr != null && slotArr.Any()) 223 | { 224 | foreach (var item in slotArr) 225 | { 226 | slotList.Add(int.Parse(item)); 227 | } 228 | } 229 | } 230 | result = CurrentRedisClient.DelSlots(name, nodeID, slotList.ToArray()); 231 | } 232 | 233 | return new JsonResult() { Code = 1, Data = result, Message = "OK" }; 234 | } 235 | catch (Exception ex) 236 | { 237 | LogHelper.Error("删除槽点", ex, name, nodeID, slotStr); 238 | return new JsonResult() { Code = 2, Message = ex.Message }; 239 | } 240 | } 241 | 242 | /// 243 | /// 保存配置 244 | /// 245 | /// 246 | /// 247 | public JsonResult SaveConfig(string name, string nodeID) 248 | { 249 | try 250 | { 251 | var result = false; 252 | 253 | if (CurrentRedisClient.IsCluster(name)) 254 | { 255 | result = CurrentRedisClient.SaveConfig(name, nodeID); 256 | } 257 | 258 | return new JsonResult() { Code = 1, Data = result, Message = "OK" }; 259 | } 260 | catch (Exception ex) 261 | { 262 | LogHelper.Error("保存配置", ex, name, nodeID); 263 | return new JsonResult() { Code = 2, Message = ex.Message }; 264 | } 265 | } 266 | 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /SAEA.WebRedisManager/wwwroot/Content/js/lay/modules/element.js: -------------------------------------------------------------------------------- 1 | /** layui-v2.3.0 MIT License By https://www.layui.com */ 2 | ; layui.define("jquery", function (t) { "use strict"; var a = layui.$, i = (layui.hint(), layui.device()), e = "element", l = "layui-this", n = "layui-show", s = function () { this.config = {} }; s.prototype.set = function (t) { var i = this; return a.extend(!0, i.config, t), i }, s.prototype.on = function (t, a) { return layui.onevent.call(this, e, t, a) }, s.prototype.tabAdd = function (t, i) { var e = ".layui-tab-title", l = a(".layui-tab[lay-filter=" + t + "]"), n = l.children(e), s = n.children(".layui-tab-bar"), o = l.children(".layui-tab-content"), r = '
  • " + (i.title || "unnaming") + "
  • "; return s[0] ? s.before(r) : n.append(r), o.append('
    ' + (i.content || "") + "
    "), f.hideTabMore(!0), f.tabAuto(), this }, s.prototype.tabDelete = function (t, i) { var e = ".layui-tab-title", l = a(".layui-tab[lay-filter=" + t + "]"), n = l.children(e), s = n.find('>li[lay-id="' + i + '"]'); return f.tabDelete(null, s), this }, s.prototype.tabChange = function (t, i) { var e = ".layui-tab-title", l = a(".layui-tab[lay-filter=" + t + "]"), n = l.children(e), s = n.find('>li[lay-id="' + i + '"]'); return f.tabClick.call(s[0], null, null, s), this }, s.prototype.tab = function (t) { t = t || {}, b.on("click", t.headerElem, function (i) { var e = a(this).index(); f.tabClick.call(this, i, e, null, t) }) }, s.prototype.progress = function (t, i) { var e = "layui-progress", l = a("." + e + "[lay-filter=" + t + "]"), n = l.find("." + e + "-bar"), s = n.find("." + e + "-text"); return n.css("width", i), s.text(i), this }; var o = ".layui-nav", r = "layui-nav-item", c = "layui-nav-bar", u = "layui-nav-tree", d = "layui-nav-child", y = "layui-nav-more", h = "layui-anim layui-anim-upbit", f = { tabClick: function (t, i, s, o) { o = o || {}; var r = s || a(this), i = i || r.parent().children("li").index(r), c = o.headerElem ? r.parent() : r.parents(".layui-tab").eq(0), u = o.bodyElem ? a(o.bodyElem) : c.children(".layui-tab-content").children(".layui-tab-item"), d = r.find("a"), y = c.attr("lay-filter"); "javascript:;" !== d.attr("href") && "_blank" === d.attr("target") || (r.addClass(l).siblings().removeClass(l), u.eq(i).addClass(n).siblings().removeClass(n)), layui.event.call(this, e, "tab(" + y + ")", { elem: c, index: i }) }, tabDelete: function (t, i) { var n = i || a(this).parent(), s = n.index(), o = n.parents(".layui-tab").eq(0), r = o.children(".layui-tab-content").children(".layui-tab-item"), c = o.attr("lay-filter"); n.hasClass(l) && (n.next()[0] ? f.tabClick.call(n.next()[0], null, s + 1) : n.prev()[0] && f.tabClick.call(n.prev()[0], null, s - 1)), n.remove(), r.eq(s).remove(), setTimeout(function () { f.tabAuto() }, 50), layui.event.call(this, e, "tabDelete(" + c + ")", { elem: o, index: s }) }, tabAuto: function () { var t = "layui-tab-more", e = "layui-tab-bar", l = "layui-tab-close", n = this; a(".layui-tab").each(function () { var s = a(this), o = s.children(".layui-tab-title"), r = (s.children(".layui-tab-content").children(".layui-tab-item"), 'lay-stope="tabmore"'), c = a(''); if (n === window && 8 != i.ie && f.hideTabMore(!0), s.attr("lay-allowClose") && o.find("li").each(function () { var t = a(this); if (!t.find("." + l)[0]) { var i = a(''); i.on("click", f.tabDelete), t.append(i) } }), "string" != typeof s.attr("lay-unauto")) if (o.prop("scrollWidth") > o.outerWidth() + 1) { if (o.find("." + e)[0]) return; o.append(c), s.attr("overflow", ""), c.on("click", function (a) { o[this.title ? "removeClass" : "addClass"](t), this.title = this.title ? "" : "鏀剁缉" }) } else o.find("." + e).remove(), s.removeAttr("overflow") }) }, hideTabMore: function (t) { var i = a(".layui-tab-title"); t !== !0 && "tabmore" === a(t.target).attr("lay-stope") || (i.removeClass("layui-tab-more"), i.find(".layui-tab-bar").attr("title", "")) }, clickThis: function () { var t = a(this), i = t.parents(o), n = i.attr("lay-filter"), s = t.parent(), c = t.siblings("." + d), y = "string" == typeof s.attr("lay-unselect"); "javascript:;" !== t.attr("href") && "_blank" === t.attr("target") || y || c[0] || (i.find("." + l).removeClass(l), s.addClass(l)), i.hasClass(u) && (c.removeClass(h), c[0] && (s["none" === c.css("display") ? "addClass" : "removeClass"](r + "ed"), "all" === i.attr("lay-shrink") && s.siblings().removeClass(r + "ed"))), layui.event.call(this, e, "nav(" + n + ")", t) }, collapse: function () { var t = a(this), i = t.find(".layui-colla-icon"), l = t.siblings(".layui-colla-content"), s = t.parents(".layui-collapse").eq(0), o = s.attr("lay-filter"), r = "none" === l.css("display"); if ("string" == typeof s.attr("lay-accordion")) { var c = s.children(".layui-colla-item").children("." + n); c.siblings(".layui-colla-title").children(".layui-colla-icon").html(""), c.removeClass(n) } l[r ? "addClass" : "removeClass"](n), i.html(r ? "" : ""), layui.event.call(this, e, "collapse(" + o + ")", { title: t, content: l, show: r }) } }; s.prototype.init = function (t, e) { var l = function () { return e ? '[lay-filter="' + e + '"]' : "" }(), s = { tab: function () { f.tabAuto.call({}) }, nav: function () { var t = 200, e = {}, s = {}, p = {}, b = function (l, o, r) { var c = a(this), f = c.find("." + d); o.hasClass(u) ? l.css({ top: c.position().top, height: c.children("a").outerHeight(), opacity: 1 }) : (f.addClass(h), l.css({ left: c.position().left + parseFloat(c.css("marginLeft")), top: c.position().top + c.height() - l.height() }), e[r] = setTimeout(function () { l.css({ width: c.width(), opacity: 1 }) }, i.ie && i.ie < 10 ? 0 : t), clearTimeout(p[r]), "block" === f.css("display") && clearTimeout(s[r]), s[r] = setTimeout(function () { f.addClass(n), c.find("." + y).addClass(y + "d") }, 300)) }; a(o + l).each(function (i) { var l = a(this), o = a(''), h = l.find("." + r); l.find("." + c)[0] || (l.append(o), h.on("mouseenter", function () { b.call(this, o, l, i) }).on("mouseleave", function () { l.hasClass(u) || (clearTimeout(s[i]), s[i] = setTimeout(function () { l.find("." + d).removeClass(n), l.find("." + y).removeClass(y + "d") }, 300)) }), l.on("mouseleave", function () { clearTimeout(e[i]), p[i] = setTimeout(function () { l.hasClass(u) ? o.css({ height: 0, top: o.position().top + o.height() / 2, opacity: 0 }) : o.css({ width: 0, left: o.position().left + o.width() / 2, opacity: 0 }) }, t) })), h.find("a").each(function () { var t = a(this), i = (t.parent(), t.siblings("." + d)); i[0] && !t.children("." + y)[0] && t.append(''), t.off("click", f.clickThis).on("click", f.clickThis) }) }) }, breadcrumb: function () { var t = ".layui-breadcrumb"; a(t + l).each(function () { var t = a(this), i = "lay-separator", e = t.attr(i) || "/", l = t.find("a"); l.next("span[" + i + "]")[0] || (l.each(function (t) { t !== l.length - 1 && a(this).after("" + e + "") }), t.css("visibility", "visible")) }) }, progress: function () { var t = "layui-progress"; a("." + t + l).each(function () { var i = a(this), e = i.find(".layui-progress-bar"), l = e.attr("lay-percent"); e.css("width", function () { return /^.+\/.+$/.test(l) ? 100 * new Function("return " + l)() + "%" : l }()), i.attr("lay-showPercent") && setTimeout(function () { e.html('' + l + "") }, 350) }) }, collapse: function () { var t = "layui-collapse"; a("." + t + l).each(function () { var t = a(this).find(".layui-colla-item"); t.each(function () { var t = a(this), i = t.find(".layui-colla-title"), e = t.find(".layui-colla-content"), l = "none" === e.css("display"); i.find(".layui-colla-icon").remove(), i.append('' + (l ? "" : "") + ""), i.off("click", f.collapse).on("click", f.collapse) }) }) } }; return s[t] ? s[t]() : layui.each(s, function (t, a) { a() }) }, s.prototype.render = s.prototype.init; var p = new s, b = a(document); p.render(); var v = ".layui-tab-title li"; b.on("click", v, f.tabClick), b.on("click", f.hideTabMore), a(window).on("resize", f.tabAuto), t(e, p) }); -------------------------------------------------------------------------------- /SAEA.WebRedisManager/Libs/Verification/VerificationCode.cs: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | *项目名称:SAEA.WebRedisManager.Libs.Verification 3 | *CLR 版本:4.0.30319.42000 4 | *机器名称:WALLE-PC 5 | *命名空间:SAEA.WebRedisManager.Libs.Verification 6 | *类 名 称:VerificationCode 7 | *版 本 号:V1.0.0.0 8 | *创建人: yswenli 9 | *电子邮箱:yswenli@outlook.com 10 | *创建时间:2020/5/9 11:33:40 11 | *描述: 12 | *===================================================================== 13 | *修改时间:2020/5/9 11:33:40 14 | *修 改 人: yswenli 15 | *版 本 号: V1.0.0.0 16 | *描 述: 17 | *****************************************************************************/ 18 | using SAEA.MVC; 19 | using System; 20 | using System.Collections.Generic; 21 | using System.Drawing; 22 | using System.IO; 23 | using System.Text; 24 | 25 | namespace SAEA.WebRedisManager.Libs.Verification 26 | { 27 | public class VerificationCode 28 | { 29 | private AnimatedGifEncoder coder = new AnimatedGifEncoder(); 30 | 31 | private char[] _identifyingCode; 32 | 33 | private int _defaultIdentifyingCodeLen = 4; 34 | 35 | const string _availableLetters1 = @"的一是在了不和有大这主中人上为们地个用工时要动国产以我到他会作来分生对于学下级就年阶义发成部民可出能方进同行面说种过命度革而多子后自社加小机也经力线本电高量长党得实家定深法表着水理化争现所二起政三好十战无农使性前等反体合斗路图把结第里正新开论之物从当两些还天资事队批如应形想制心样干都向变关点育重其思与间内去因件日利相由压员气业代全组数果期导平各基或月毛然问比展那它最及外没看治提五解系林者米群头意只明四道马认次文通但条较克又公孔领军流入接席位情运器并飞原油放立题质指建区验活众很教决特此常石强极土少已根共直团统式转别造切九你取西持总料连任志观调七么山程百报更见必真保热委手改管处己将修支识病象几先老光专什六型具示复安带每东增则完风回南广劳轮科北打积车计给节做务被整联步类集号列温装即毫知轴研单色坚据速防史拉世设达尔场织历花受求传口断况采精金界品判参层止边清至万确究书术状厂须离再目海交权且儿青才证低越际八试规斯近注办布门铁需走议县兵固除般引齿千胜细影济白格效置推空配刀叶率述今选养德话查差半敌始片施响收华觉备名红续均药标记难存测士身紧液派准斤角降维板许破述技消底床田势端感往神便贺村构照容非搞亚磨族火段算适讲按值美态黄易彪服早班麦削信排台声该击素张密害侯草何树肥继右属市严径螺检左页抗苏显苦英快称坏移约巴材省黑武培著河帝仅针怎植京助升王眼她抓含苗副杂普谈围食射源例致酸旧却充足短划剂宣环落首尺波承粉践府鱼随考刻靠够满夫失包住促枝局菌杆周护岩师举曲春元超负砂封换太模贫减阳扬江析亩木言球朝医校古呢稻宋听唯输滑站另卫字鼓刚写刘微略范供阿块某功套友限项余倒卷创律雨让骨远帮初皮播优占死毒圈伟季训控激找叫云互跟裂粮粒母练塞钢顶策双留误础吸阻故寸盾晚丝女散焊功株亲院冷彻弹错散商视艺灭版烈零室轻血倍缺厘泵察绝富城冲喷壤简否柱李望盘磁雄似困巩益洲脱投送奴侧润盖挥距触星松送获兴独官混纪依未突架宽冬章湿偏纹吃执阀矿寨责熟稳夺硬价努翻奇甲预职评读背协损棉侵灰虽矛厚罗泥辟告卵箱掌氧恩爱停曾溶营终纲孟钱待尽俄缩沙退陈讨奋械载胞幼哪剥迫旋征槽倒握担仍呀鲜吧卡粗介钻逐弱脚怕盐末阴丰编印蜂急拿扩伤飞露核缘游振操央伍域甚迅辉异序免纸夜乡久隶缸夹念兰映沟乙吗儒杀汽磷艰晶插埃燃欢铁补咱芽永瓦倾阵碳演威附牙芽永瓦斜灌欧献顺猪洋腐请透司危括脉宜笑若尾束壮暴企菜穗楚汉愈绿拖牛份染既秋遍锻玉夏疗尖殖井费州访吹荣铜沿替滚客召旱悟刺脑措贯藏敢令隙炉壳硫煤迎铸粘探临薄旬善福纵择礼愿伏残雷延烟句纯渐耕跑泽慢栽鲁赤繁境潮横掉锥希池败船假亮谓托伙哲怀割摆贡呈劲财仪沉炼麻罪祖息车穿货销齐鼠抽画饲龙库守筑房歌寒喜哥洗蚀废纳腹乎录镜妇恶脂庄擦险赞钟摇典柄辩竹谷卖乱虚桥奥伯赶垂途额壁网截野遗静谋弄挂课镇妄盛耐援扎虑键归符庆聚绕摩忙舞遇索顾胶羊湖钉仁音迹碎伸灯避泛亡答勇频皇柳哈揭甘诺概宪浓岛袭谁洪谢炮浇斑讯懂灵蛋闭孩释乳巨徒私银伊景坦累匀霉杜乐勒隔弯绩招绍胡呼痛峰零柴簧午跳居尚丁秦稍追梁折耗碱殊岗挖氏刃剧堆赫荷胸衡勤膜篇登驻案刊秧缓凸役剪川雪链渔啦脸户洛孢勃盟买杨宗焦赛旗滤硅炭股坐蒸凝竟陷枪黎救冒暗洞犯筒您宋弧爆谬涂味津臂障褐陆啊健尊豆拔莫抵桑坡缝警挑污冰柬嘴啥饭塑寄赵喊垫康遵牧遭幅园腔订香肉弟屋敏恢忘衣孙龄岭骗休借丹渡耳刨虎笔稀昆浪萨茶滴浅拥穴覆伦娘吨浸袖珠雌妈紫戏塔锤震岁貌洁剖牢锋疑霸闪埔猛诉刷狠忽灾闹乔唐漏闻沈熔氯荒茎男凡抢像浆旁玻亦忠唱蒙予纷捕锁尤乘乌智淡允叛畜俘摸锈扫毕璃宝芯爷鉴秘净蒋钙肩腾枯抛轨堂拌爸循诱祝励肯酒绳穷塘燥泡袋朗喂铝软渠颗惯贸粪综墙趋彼届墨碍启逆卸航雾冠丙街莱贝辐肠付吉渗瑞惊顿挤秒悬姆烂森糖圣凹陶词迟蚕亿矩"; 36 | 37 | const string _availableLetters2 = "1234567890"; 38 | 39 | const string _availableLetters3 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; 40 | 41 | int _charType = 1; 42 | 43 | private Random _random = new Random(); 44 | 45 | private Color _color; 46 | 47 | 48 | private int _frameCount = 6; 49 | private int _delay = 500; 50 | private int _noiseCount = 15; 51 | 52 | private int _width = 150, _height = 60; 53 | public int Width 54 | { 55 | get { return _width; } 56 | } 57 | 58 | public int Height 59 | { 60 | get { return _height; } 61 | } 62 | 63 | public string IdentifyingCode 64 | { 65 | get { return new string(_identifyingCode); } 66 | } 67 | 68 | public VerificationCode(int width, int height, int len = 4, int charType = 0) 69 | { 70 | _width = width < 1 ? 1 : width; 71 | _height = height < 1 ? 1 : height; 72 | _defaultIdentifyingCodeLen = len; 73 | _charType = charType; 74 | coder.SetSize(Width, Height); 75 | coder.SetRepeat(0); 76 | } 77 | 78 | private void GenerateIdentifyingCode(int codeLength) 79 | { 80 | if (codeLength < 1) 81 | codeLength = 4; 82 | 83 | List codes = new List(); 84 | 85 | var l1 = _availableLetters1; 86 | var l2 = _availableLetters2; 87 | var l3 = _availableLetters3; 88 | var l4 = l1 + l2; 89 | var l5 = l1 + l3; 90 | var l6 = l2 + l3; 91 | var l7 = l1 + l2 + l3; 92 | 93 | 94 | for (int i = 0; i < codeLength; i++) 95 | { 96 | switch (_charType) 97 | { 98 | case 1: 99 | codes.Add(l1[_random.Next(0, l1.Length)]); 100 | break; 101 | case 2: 102 | codes.Add(l2[_random.Next(0, l2.Length)]); 103 | break; 104 | case 3: 105 | codes.Add(l4[_random.Next(0, l4.Length)]); 106 | break; 107 | case 4: 108 | codes.Add(l3[_random.Next(0, l3.Length)]); 109 | break; 110 | case 5: 111 | codes.Add(l5[_random.Next(0, l5.Length)]); 112 | break; 113 | case 6: 114 | codes.Add(l6[_random.Next(0, l6.Length)]); 115 | break; 116 | default: 117 | codes.Add(l7[_random.Next(0, l7.Length)]); 118 | break; 119 | } 120 | 121 | } 122 | 123 | _identifyingCode = new char[codes.Count]; 124 | 125 | codes.CopyTo(_identifyingCode); 126 | } 127 | 128 | public Stream Create(Stream stream) 129 | { 130 | GenerateIdentifyingCode(_defaultIdentifyingCodeLen); 131 | 132 | coder.Start(stream); 133 | Process(IdentifyingCode); 134 | return stream; 135 | } 136 | 137 | public MemoryStream Create() 138 | { 139 | GenerateIdentifyingCode(_defaultIdentifyingCodeLen); 140 | 141 | MemoryStream stream = new MemoryStream(); 142 | coder.Start(stream); 143 | Process(IdentifyingCode); 144 | return stream; 145 | } 146 | 147 | private void Process(string str) 148 | { 149 | Rectangle rect = new Rectangle(0, 0, Width, Height); 150 | 151 | Font f = new Font(FontFamily.GenericSansSerif, 14, FontStyle.Bold); 152 | 153 | for (int i = 0; i < _frameCount; i++) 154 | { 155 | Image im = new Bitmap(Width, Height); 156 | 157 | _color = Color.FromArgb(_random.Next(128, 256), _random.Next(128, 256), _random.Next(128, 256)); 158 | 159 | var bb = new SolidBrush(_color); 160 | 161 | Graphics ga = Graphics.FromImage(im); 162 | 163 | ga.FillRectangle(bb, rect); 164 | 165 | int fH = (int)f.GetHeight(); 166 | 167 | int fW = (int)ga.MeasureString(str, f).Width; 168 | 169 | _color = Color.FromArgb(_random.Next(0, 128), _random.Next(0, 128), _random.Next(0, 128)); 170 | 171 | var fb = new SolidBrush(_color); 172 | 173 | AddNoise(ga, _color); 174 | 175 | ga.DrawString(str, f, fb, new PointF(_random.Next(1, Width - 1 - fW), _random.Next(1, Height - 1 - fH))); 176 | 177 | ga.Flush(); 178 | 179 | coder.SetDelay(_delay); 180 | 181 | coder.AddFrame(im); 182 | 183 | im.Dispose(); 184 | } 185 | coder.Finish(); 186 | } 187 | 188 | private void AddNoise(Graphics ga, Color color) 189 | { 190 | 191 | Pen pen = new Pen(color); 192 | 193 | Point[] ps = new Point[_noiseCount]; 194 | 195 | for (int i = 0; i < _noiseCount; i++) 196 | { 197 | ps[i] = new Point(_random.Next(1, Width - 1), _random.Next(1, Height - 1)); 198 | } 199 | 200 | ga.DrawLines(pen, ps); 201 | } 202 | 203 | public void Create(string path) 204 | { 205 | FileStream fs = new FileStream(path, FileMode.Create); 206 | coder.Start(fs); 207 | Process(IdentifyingCode); 208 | fs.Close(); 209 | } 210 | public void ProcessRequest(HttpContext context) 211 | { 212 | context.Response.Body = new byte[0]; 213 | context.Response.ContentType = "image/Gif"; 214 | var stream = Create(); 215 | context.Response.BinaryWrite(stream.ToArray()); 216 | stream.Dispose(); 217 | } 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /SAEA.WebRedisManager/wwwroot/Content/js/lay/modules/form.js: -------------------------------------------------------------------------------- 1 | /** layui-v2.3.0 MIT License By https://www.layui.com */ 2 | ; layui.define("layer", function (e) { "use strict"; var i = layui.$, t = layui.layer, a = layui.hint(), n = layui.device(), l = "form", r = ".layui-form", s = "layui-this", o = "layui-hide", c = "layui-disabled", u = function () { this.config = { verify: { required: [/[\S]+/, "蹇呭~椤逛笉鑳戒负绌�"], phone: [/^1\d{10}$/, "璇疯緭鍏ユ纭殑鎵嬫満鍙�"], email: [/^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/, "閭鏍煎紡涓嶆纭�"], url: [/(^#)|(^http(s*):\/\/[^\s]+\.[^\s]+)/, "閾炬帴鏍煎紡涓嶆纭�"], number: function (e) { if (!e || isNaN(e)) return "鍙兘濉啓鏁板瓧" }, date: [/^(\d{4})[-\/](\d{1}|0\d{1}|1[0-2])([-\/](\d{1}|0\d{1}|[1-2][0-9]|3[0-1]))*$/, "鏃ユ湡鏍煎紡涓嶆纭�"], identity: [/(^\d{15}$)|(^\d{17}(x|X|\d)$)/, "璇疯緭鍏ユ纭殑韬唤璇佸彿"] } } }; u.prototype.set = function (e) { var t = this; return i.extend(!0, t.config, e), t }, u.prototype.verify = function (e) { var t = this; return i.extend(!0, t.config.verify, e), t }, u.prototype.on = function (e, i) { return layui.onevent.call(this, l, e, i) }, u.prototype.val = function (e, t) { var a = i(r + '[lay-filter="' + e + '"]'); a.each(function (e, a) { var n = i(this); layui.each(t, function (e, i) { var t, a = n.find('[name="' + e + '"]'); a[0] && (t = a[0].type, "checkbox" === t ? a[0].checked = i : "radio" === t ? a.each(function () { this.value === i && (this.checked = !0) }) : a.val(i)) }) }), f.render(null, e) }, u.prototype.render = function (e, t) { var n = this, u = i(r + function () { return t ? '[lay-filter="' + t + '"]' : "" }()), d = { select: function () { var e, t = "璇烽€夋嫨", a = "layui-form-select", n = "layui-select-title", r = "layui-select-none", d = "", f = u.find("select"), v = function (t, l) { i(t.target).parent().hasClass(n) && !l || (i("." + a).removeClass(a + "ed " + a + "up"), e && d && e.val(d)), e = null }, y = function (t, u, f) { var y, p = i(this), m = t.find("." + n), k = m.find("input"), g = t.find("dl"), x = g.children("dd"), b = this.selectedIndex; if (!u) { var C = function () { var e = t.offset().top + t.outerHeight() + 5 - h.scrollTop(), i = g.outerHeight(); b = p[0].selectedIndex, t.addClass(a + "ed"), x.removeClass(o), y = null, x.eq(b).addClass(s).siblings().removeClass(s), e + i > h.height() && e >= i && t.addClass(a + "up") }, w = function (e) { t.removeClass(a + "ed " + a + "up"), k.blur(), y = null, e || $(k.val(), function (e) { e && (d = g.find("." + s).html(), k && k.val(d)) }) }; m.on("click", function (e) { t.hasClass(a + "ed") ? w() : (v(e, !0), C()), g.find("." + r).remove() }), m.find(".layui-edge").on("click", function () { k.focus() }), k.on("keyup", function (e) { var i = e.keyCode; 9 === i && C() }).on("keydown", function (e) { var i = e.keyCode; 9 === i && w(); var t = function (i, a) { var n, l; if (e.preventDefault(), a = function () { return a && a[0] ? a : y && y[0] ? y : x.eq(b) }(), l = a[i](), n = a[i]("dd"), l[0]) { if (y = a[i](), !n[0] || n.hasClass(c)) return t(i, y); n.addClass(s).siblings().removeClass(s); var r = g.children("dd.layui-this"), o = r.position().top, u = g.height(), d = r.height(); o > u && g.scrollTop(o + g.scrollTop() - u + d - 5), o < 0 && g.scrollTop(o + g.scrollTop()) } }; 38 === i && t("prev"), 40 === i && t("next"), 13 === i && (e.preventDefault(), g.children("dd." + s).trigger("click")) }); var $ = function (e, t, a) { var n = 0; layui.each(x, function () { var t = i(this), l = t.text(), r = l.indexOf(e) === -1; ("" === e || "blur" === a ? e !== l : r) && n++ , "keyup" === a && t[r ? "addClass" : "removeClass"](o) }); var l = n === x.length; return t(l), l }, T = function (e) { var i = this.value, t = e.keyCode; return 9 !== t && 13 !== t && 37 !== t && 38 !== t && 39 !== t && 40 !== t && ($(i, function (e) { e ? g.find("." + r)[0] || g.append('

    鏃犲尮閰嶉」

    ') : g.find("." + r).remove() }, "keyup"), void ("" === i && g.find("." + r).remove())) }; f && k.on("keyup", T).on("blur", function (t) { var a = p[0].selectedIndex; e = k, d = i(p[0].options[a]).html(), setTimeout(function () { $(k.val(), function (e) { d || k.val("") }, "blur") }, 200) }), x.on("click", function () { var e = i(this), a = e.attr("lay-value"), n = p.attr("lay-filter"); return !e.hasClass(c) && (e.hasClass("layui-select-tips") ? k.val("") : (k.val(e.text()), e.addClass(s)), e.siblings().removeClass(s), p.val(a).removeClass("layui-form-danger"), layui.event.call(this, l, "select(" + n + ")", { elem: p[0], value: a, othis: t }), w(!0), !1) }), t.find("dl>dt").on("click", function (e) { return !1 }), i(document).off("click", v).on("click", v) } }; f.each(function (e, l) { var r = i(this), o = r.next("." + a), u = this.disabled, d = l.value, f = i(l.options[l.selectedIndex]), v = l.options[0]; if ("string" == typeof r.attr("lay-ignore")) return r.show(); var h = "string" == typeof r.attr("lay-search"), p = v ? v.value ? t : v.innerHTML || t : t, m = i(['
    ', '
    ', '', '
    ', '
    ', function (e) { var i = []; return layui.each(e, function (e, a) { 0 !== e || a.value ? "optgroup" === a.tagName.toLowerCase() ? i.push("
    " + a.label + "
    ") : i.push('
    ' + a.innerHTML + "
    ") : i.push('
    ' + (a.innerHTML || t) + "
    ") }), 0 === i.length && i.push('
    娌℃湁閫夐」
    '), i.join("") }(r.find("*")) + "
    ", "
    "].join("")); o[0] && o.remove(), r.after(m), y.call(this, m, u, h) }) }, checkbox: function () { var e = { checkbox: ["layui-form-checkbox", "layui-form-checked", "checkbox"], _switch: ["layui-form-switch", "layui-form-onswitch", "switch"] }, t = u.find("input[type=checkbox]"), a = function (e, t) { var a = i(this); e.on("click", function () { var i = a.attr("lay-filter"), n = (a.attr("lay-text") || "").split("|"); a[0].disabled || (a[0].checked ? (a[0].checked = !1, e.removeClass(t[1]).find("em").text(n[1])) : (a[0].checked = !0, e.addClass(t[1]).find("em").text(n[0])), layui.event.call(a[0], l, t[2] + "(" + i + ")", { elem: a[0], value: a[0].value, othis: e })) }) }; t.each(function (t, n) { var l = i(this), r = l.attr("lay-skin"), s = (l.attr("lay-text") || "").split("|"), o = this.disabled; "switch" === r && (r = "_" + r); var u = e[r] || e.checkbox; if ("string" == typeof l.attr("lay-ignore")) return l.show(); var d = l.next("." + u[0]), f = i(['
    ", function () { var e = n.title.replace(/\s/g, ""), i = { checkbox: [e ? "" + n.title + "" : "", ''].join(""), _switch: "" + ((n.checked ? s[0] : s[1]) || "") + "" }; return i[r] || i.checkbox }(), "
    "].join("")); d[0] && d.remove(), l.after(f), a.call(this, f, u) }) }, radio: function () { var e = "layui-form-radio", t = ["", ""], a = u.find("input[type=radio]"), n = function (a) { var n = i(this), s = "layui-anim-scaleSpring"; a.on("click", function () { var o = n[0].name, c = n.parents(r), u = n.attr("lay-filter"), d = c.find("input[name=" + o.replace(/(\.|#|\[|\])/g, "\\$1") + "]"); n[0].disabled || (layui.each(d, function () { var a = i(this).next("." + e); this.checked = !1, a.removeClass(e + "ed"), a.find(".layui-icon").removeClass(s).html(t[1]) }), n[0].checked = !0, a.addClass(e + "ed"), a.find(".layui-icon").addClass(s).html(t[0]), layui.event.call(n[0], l, "radio(" + u + ")", { elem: n[0], value: n[0].value, othis: a })) }) }; a.each(function (a, l) { var r = i(this), s = r.next("." + e), o = this.disabled; if ("string" == typeof r.attr("lay-ignore")) return r.show(); s[0] && s.remove(); var u = i(['
    ', '' + t[l.checked ? 0 : 1] + "", "
    " + function () { var e = l.title || ""; return "string" == typeof r.next().attr("lay-radio") && (e = r.next().html(), r.next().remove()), e }() + "
    ", "
    "].join("")); r.after(u), n.call(this, u) }) } }; return e ? d[e] ? d[e]() : a.error("涓嶆敮鎸佺殑" + e + "琛ㄥ崟娓叉煋") : layui.each(d, function (e, i) { i() }), n }; var d = function () { var e = i(this), a = f.config.verify, s = null, o = "layui-form-danger", c = {}, u = e.parents(r), d = u.find("*[lay-verify]"), v = e.parents("form")[0], h = u.find("input,select,textarea"), y = e.attr("lay-filter"); if (layui.each(d, function (e, l) { var r = i(this), c = r.attr("lay-verify").split("|"), u = r.attr("lay-verType"), d = r.val(); if (r.removeClass(o), layui.each(c, function (e, i) { var c, f = "", v = "function" == typeof a[i]; if (a[i]) { var c = v ? f = a[i](d, l) : !a[i][0].test(d); if (f = f || a[i][1], c) return "tips" === u ? t.tips(f, function () { return "string" == typeof r.attr("lay-ignore") || "select" !== l.tagName.toLowerCase() && !/^checkbox|radio$/.test(l.type) ? r : r.next() }(), { tips: 1 }) : "alert" === u ? t.alert(f, { title: "鎻愮ず", shadeClose: !0 }) : t.msg(f, { icon: 5, shift: 6 }), n.android || n.ios || l.focus(), r.addClass(o), s = !0 } }), s) return s }), s) return !1; var p = {}; return layui.each(h, function (e, i) { if (i.name = (i.name || "").replace(/^\s*|\s*&/, ""), i.name) { if (/^.*\[\]$/.test(i.name)) { var t = i.name.match(/^(.*)\[\]$/g)[0]; p[t] = 0 | p[t], i.name = i.name.replace(/^(.*)\[\]$/, "$1[" + p[t]++ + "]") } /^checkbox|radio$/.test(i.type) && !i.checked || (c[i.name] = i.value) } }), layui.event.call(this, l, "submit(" + y + ")", { elem: this, form: v, field: c }) }, f = new u, v = i(document), h = i(window); f.render(), v.on("reset", r, function () { var e = i(this).attr("lay-filter"); setTimeout(function () { f.render(null, e) }, 50) }), v.on("submit", r, d).on("click", "*[lay-submit]", d), e(l, f) }); -------------------------------------------------------------------------------- /SAEA.WebRedisManager/wwwroot/Content/js/redis.js: -------------------------------------------------------------------------------- 1 | layui.use(['jquery', 'layer', 'form'], function () { 2 | 3 | var layer = layui.layer, form = layui.form, $ = layui.$; 4 | // 5 | var layerIndex = -1; 6 | 7 | //redis列表 8 | $(function () { 9 | 10 | var atimer = setInterval(function () { 11 | HttpGet("/user/authenticated", null, function (adata) { 12 | if (adata.Code === 1 && adata.Data === false) { 13 | layer.msg("当前操作需要登录", { time: 2000 }, function () { 14 | clearInterval(atimer); 15 | location.href = "/login.html"; 16 | }); 17 | } 18 | }); 19 | }, 2000); 20 | 21 | 22 | layerIndex = layer.msg('加载中', { 23 | icon: 16 24 | , shade: 0.01 25 | }); 26 | //默认加载redis列表 27 | HttpGet("/api/config/getlist", null, function (data) { 28 | 29 | layer.close(layerIndex); 30 | 31 | if (data.Code === 1) { 32 | 33 | if (data.Data !== undefined && data.Data.length > 0) { 34 | for (var i = 0; i < data.Data.length; i++) { 35 | var html = `
    36 |    ${data.Data[i].Name} 37 |
    `; 38 | $("dl.redis-dbs").append(html); 39 | } 40 | //搜索 41 | 42 | $("#search_list").keyup(function () { 43 | 44 | var searchText = $(this).val(); 45 | 46 | if (searchText === "") { 47 | $(".redis_link").each(function (index) { 48 | $(this).parent().show(); 49 | }); 50 | } 51 | 52 | $(".redis_link").each(function (index) { 53 | if ($(this).text().indexOf(searchText) === -1 && $(this).attr("title").indexOf(searchText) === -1) { 54 | $(this).parent().hide(); 55 | } 56 | else { 57 | $(this).parent().show(); 58 | } 59 | }); 60 | }); 61 | 62 | //点击redis实例 63 | $("a.redis_link").on("click", function () { 64 | var _parent = $(this).parent(); 65 | var name = encodeURI($(this).attr("data-name")); 66 | 67 | var isLoaded = _parent.attr("data-loaded"); 68 | 69 | if (isLoaded !== undefined) { 70 | _parent.removeAttr("data-loaded"); 71 | _parent.find("dl").remove(); 72 | return; 73 | } 74 | _parent.attr("data-loaded", "1"); 75 | 76 | layerIndex = layer.msg('加载中', { 77 | icon: 16 78 | , shade: 0.01 79 | , time: 30000 80 | }); 81 | HttpPost("/api/redis/connect?name=" + encodeURI(name), null, function (dbData) { 82 | layer.close(layerIndex); 83 | if (dbData.Code === 1) { 84 | if (dbData.Data !== undefined && dbData.Data.length > 0) { 85 | _parent.find("dl").remove(); 86 | _parent.append('
    '); 87 | var db_dl = _parent.find("dl"); 88 | for (var j = 0; j < dbData.Data.length; j++) { 89 | var shtml = `
         db${j}
    `; 90 | db_dl.append(shtml); 91 | } 92 | 93 | $(".layadmin-iframe").attr("src", "/chart.html?name=" + name); 94 | 95 | //点击redus db 96 | $("a.redis_db_link").on("click", function () { 97 | 98 | var sname = $(this).attr("data-name"); 99 | var dbindex = $(this).attr("data-db"); 100 | 101 | $(".layadmin-iframe").attr("src", "/keys.html?name=" + encodeURI(sname) + "&dbindex=" + dbindex); 102 | 103 | }); 104 | } 105 | } 106 | else { 107 | layer.msg("操作失败:" + dbData.Message, { time: 2000 }); 108 | } 109 | }); 110 | }); 111 | // 112 | $("textarea[name='configs']").val(JSON.stringify(data.Data, " ", 4)); 113 | } 114 | } 115 | else if (data.Code === 3) { 116 | layer.msg("操作失败:" + data.Message, { time: 2000 }, function () { 117 | location.href = "/login.html"; 118 | }); 119 | } 120 | else { 121 | if (data == "") return; 122 | layer.msg("操作失败:" + data.Message, { time: 2000 }); 123 | } 124 | }); 125 | 126 | //检查版本 127 | HttpGet("/api/update/getlatest", null, function (res) { 128 | if (res.Code === 1 && res.Data !== "") { 129 | layer.open({ 130 | type: 1, 131 | closeBtn: 0, 132 | area: ['440px', '220px'], 133 | anim: 2, 134 | shadeClose: true, 135 | content: `

    检测到新版本,点击这里下载

    ` 136 | }); 137 | } 138 | }); 139 | 140 | }); 141 | 142 | 143 | //添加redis按钮 144 | $("#add_link").on("click", function () { 145 | layer.open({ 146 | title: 'Set Redis Server', 147 | type: 2, 148 | area: ['580px', '320px'], 149 | fixed: true, 150 | resize: false, 151 | move: false, 152 | maxmin: false, 153 | time: 0, 154 | content: ['/redisadd.html', 'no'], 155 | end: function () { 156 | location.reload(); 157 | } 158 | }); 159 | }); 160 | 161 | //移除redis按钮 162 | $("#rem_link").on("click", function () { 163 | layer.open({ 164 | title: 'Remove Redis Server', 165 | type: 2, 166 | area: ['580px', '200px'], 167 | fixed: true, 168 | resize: false, 169 | move: false, 170 | maxmin: false, 171 | time: 0, 172 | content: ['/remove.html', 'no'] 173 | }); 174 | }); 175 | //redis server configs按钮 176 | $("#conf_link").on("click", function () { 177 | layer.open({ 178 | title: 'Redis Server Configs', 179 | type: 2, 180 | area: ['670px', '560px'], 181 | fixed: true, 182 | resize: false, 183 | move: false, 184 | maxmin: false, 185 | time: 0, 186 | content: ['/configs.html', 'no'] 187 | }); 188 | }); 189 | 190 | //users list按钮 191 | $("#account_link").on("click", function () { 192 | $(".layadmin-iframe").attr("src", "/userlist.html"); 193 | }); 194 | 195 | //提交添加redis表单 196 | $("#add_btn").on("click", function () { 197 | //var str = $("#add_form").serialize(); //layui jquery bug 198 | var name = encodeURIComponent($("input[name='name']").val()); 199 | var ip = encodeURIComponent($("input[name='ip']").val()); 200 | var port = encodeURIComponent($("input[name='port']").val()); 201 | var password = encodeURIComponent($("input[name='password']").val()); 202 | var str = `name=${name}&ip=${ip}&port=${port}&password=${password}`; 203 | HttpPost("/api/config/set", str, function (data) { 204 | if (data.Code === 1) { 205 | parent.location.reload(); 206 | } 207 | else { 208 | layer.msg("操作失败:" + data.Message, { time: 2000 }); 209 | } 210 | }); 211 | }); 212 | 213 | //提交删除redis表单 214 | $("#rem_btn").on("click", function () { 215 | layer.confirm("确认要删除redis服务器么?", { 216 | btn: ['确定', '取消'] 217 | }, 218 | function (index) { 219 | layer.close(index); 220 | var json = $("#add_form").serialize(); 221 | HttpPost("/api/config/rem", json, function (data) { 222 | if (data.Code === 1) { 223 | parent.location.reload(); 224 | } 225 | else { 226 | layer.msg("操作失败:" + data.Message, { time: 2000 }); 227 | } 228 | }); 229 | } 230 | ); 231 | }); 232 | //导入redis表单 233 | $("#conf_btn").on("click", function () { 234 | var configs = encodeURIComponent($("textarea[name='configs']").val()); 235 | var str = `configs=${configs}`; 236 | HttpPost("/api/config/SetConfigs", str, function (data) { 237 | if (data.Code === 1) { 238 | parent.location.reload(); 239 | } 240 | else { 241 | layer.msg("操作失败:" + data.Message, { time: 2000 }); 242 | } 243 | }); 244 | }); 245 | }); 246 | 247 | -------------------------------------------------------------------------------- /SAEA.WebRedisManager/wwwroot/Content/js/redis.data.js: -------------------------------------------------------------------------------- 1 | function htmlEncode(text) { 2 | return text.replace(/&/g, '&').replace(/\"/g, '"').replace(//g, '>'); 3 | } 4 | function htmlDecode(text) { 5 | return text.replace(/>/g, ">").replace(/</g, "<").replace(/"/g, '"').replace(/&/g, "&"); 6 | } 7 | 8 | function isJSON(str) { 9 | if (typeof str === 'string') { 10 | try { 11 | var obj = JSON.parse(str); 12 | if (str.indexOf('{') > -1) { 13 | return true; 14 | } else { 15 | return false; 16 | } 17 | } 18 | catch (e) { 19 | console.log(e); 20 | return false; 21 | } 22 | } 23 | return false; 24 | } 25 | 26 | layui.use(['jquery', 'layer', 'form', 'laypage'], function () { 27 | 28 | var layer = layui.layer, form = layui.form, $ = layui.$, laypage = layui.laypage; 29 | 30 | var layerIndex = -1; 31 | 32 | layerIndex = layer.msg('加载中', { 33 | icon: 16 34 | , shade: 0.01 35 | }); 36 | 37 | var redis_name = GetRequest().name; 38 | 39 | var db_index = GetRequest().dbindex; 40 | 41 | var searchKey = $("#search-key").val(); 42 | 43 | //keys列表 44 | HttpGet(`/api/redis/getdbsize?name=${redis_name}&dbindex=${db_index}`, null, function (gdata) { 45 | 46 | layer.close(layerIndex); 47 | 48 | $(".keys-header").html(`【】 Keys List redisName:${decodeURI(redis_name)} dbIndex:${db_index} dbsize:${gdata.Data}Redis Console】`); 49 | 50 | $("#redis_console").click(() => { 51 | layer.full(layer.open({ 52 | title: '命令行模式', 53 | type: 2, 54 | area: ['580px', '318px'], 55 | fixed: true, 56 | resize: false, 57 | move: false, 58 | maxmin: true, 59 | scrollbar: true, 60 | time: 0, 61 | content: [`/console.html?name=${redis_name}`, 'no'] 62 | })); 63 | }); 64 | }); 65 | 66 | // 67 | 68 | 69 | 70 | //加载列表 71 | 72 | var dataOffset = 0; 73 | 74 | var timer = null; 75 | 76 | var ttlKeys = ""; 77 | 78 | function loadList(searchKey) { 79 | 80 | ttlKeys = ""; 81 | 82 | layerIndex = layer.msg('加载中', { 83 | icon: 16 84 | , shade: 0.3, time: 50000 85 | }); 86 | 87 | var rurl = `/api/redis/getkeytypes?name=${redis_name}&dbindex=${db_index}&key=${searchKey}&offset=${dataOffset}`; 88 | 89 | HttpGet(rurl, null, function (jdata) { 90 | 91 | if (jdata.Code === 1) { 92 | 93 | var redis_data_body = ""; 94 | 95 | for (var i = 0; i < jdata.Data.length; i++) { 96 | 97 | var tkey = jdata.Data[i].Key; 98 | 99 | if (tkey.indexOf("%")) { 100 | tkey = tkey.replace(/%/g, '%25'); 101 | } 102 | var tkey1 = htmlEncode(decodeURIComponent(tkey)); 103 | var tkey2 = encodeURIComponent(tkey); 104 | 105 | var thtml = ` 106 | ${jdata.Data[i].Type} 107 | ${tkey1} 108 | 109 | 110 | view | delete 111 | `; 112 | redis_data_body += thtml; 113 | ttlKeys += tkey2 + ","; 114 | } 115 | 116 | $("#redis-data-body").html(""); 117 | $("#redis-data-body").html(redis_data_body); 118 | $("td").each(function () { 119 | $(this).attr("title", $(this).text()); 120 | }); 121 | //更新ttl 122 | 123 | var getttl = function (req_url) { 124 | HttpGet(req_url, null, function (tddata) { 125 | if (tddata.Code === 1) { 126 | $(".ttl-td").each(function (tdindex) { 127 | $(this).html(tddata.Data[tdindex]); 128 | }); 129 | } 130 | }); 131 | } 132 | 133 | if (timer === null) { 134 | getttl(`/api/redis/getttls?name=${redis_name}&dbindex=${db_index}&keys=${ttlKeys}`); 135 | timer = setInterval(function () { 136 | getttl(`/api/redis/getttls?name=${redis_name}&dbindex=${db_index}&keys=${ttlKeys}`); 137 | }, 3000); 138 | } 139 | 140 | 141 | //查看 142 | $(".view-link").on("click", function () { 143 | var type = $(this).parent().attr("data-type"); 144 | var typeid = 2; 145 | switch (type) { 146 | case "hash": 147 | typeid = 2; 148 | break; 149 | case "set": 150 | typeid = 3; 151 | break; 152 | case "zset": 153 | typeid = 4; 154 | break; 155 | case "list": 156 | typeid = 5; 157 | break; 158 | } 159 | 160 | var key = $(this).parent().attr("data-key"); 161 | 162 | if (type === "string") { 163 | 164 | var info_url = `/api/redis/get?name=${redis_name}&dbindex=${db_index}&key=${key}`; 165 | 166 | HttpGet(info_url, null, function (vdata) { 167 | 168 | if (vdata.Code === 1) { 169 | 170 | if (isJSON(vdata.Data)) { 171 | var jsonMsg = htmlEncode(JSON.stringify(JSON.parse(vdata.Data), " ", 4)); 172 | layer.alert(`
    ${jsonMsg}
    `); 173 | } 174 | else { 175 | layer.alert(htmlEncode(vdata.Data)); 176 | } 177 | } 178 | else { 179 | layer.msg("操作失败:" + data.Message, { time: 2000 }); 180 | } 181 | }); 182 | } 183 | else { 184 | layer.full(layer.open({ 185 | title: '查看数据', 186 | type: 2, 187 | area: ['580px', '318px'], 188 | fixed: true, 189 | resize: false, 190 | move: false, 191 | maxmin: true, 192 | scrollbar: true, 193 | time: 0, 194 | content: [`/itemsview.html?name=${redis_name}&dbindex=${db_index}&id=${key}&type=${typeid}`, 'no'] 195 | })); 196 | } 197 | }); 198 | //移除 199 | $(".del-link").on("click", function () { 200 | 201 | var key = $(this).parent().attr("data-key"); 202 | 203 | layer.confirm("确认要删除此项数据么?", { 204 | btn: ['确定', '取消'] 205 | }, 206 | function (index) { 207 | layer.close(index); 208 | HttpPost(`/api/redis/del?name=${redis_name}&dbindex=${db_index}&key=${key}`, null, function (data) { 209 | if (data.Code === 1) { 210 | $("#search_btn").click(); 211 | } 212 | else { 213 | layer.msg("操作失败:" + data.Message, { time: 2000 }); 214 | } 215 | }); 216 | } 217 | ); 218 | }); 219 | } 220 | else { 221 | layer.msg("操作失败:" + sdata.Message, { time: 2000 }); 222 | } 223 | // 224 | layer.close(layerIndex); 225 | }); 226 | } 227 | 228 | loadList("*"); 229 | 230 | //查询 231 | $("#search_btn").click(function () { 232 | 233 | searchKey = $("#search-key").val(); 234 | 235 | if (searchKey === undefined || searchKey === "") { 236 | loadList("*"); 237 | } 238 | else { 239 | loadList(encodeURIComponent(searchKey)); 240 | } 241 | }); 242 | 243 | $("#search-key").keypress(function (e) { 244 | if (e.which === 13) { 245 | $("#search_btn").click(); 246 | } 247 | }); 248 | 249 | $("#batch_remove_btn").click(() => { 250 | 251 | layer.confirm("此操作为按通配符进行批量删除,确定执行此操作么?", { 252 | btn: ['确定', '取消'], icon: 3 253 | }, 254 | function (index) { 255 | 256 | searchKey = $("#search-key").val(); 257 | 258 | if (searchKey === undefined || searchKey === "") { 259 | layer.msg('输入框内容不能为空!', { 260 | icon: 2, time: 2000 261 | }, function () { 262 | layer.closeAll(); 263 | }); 264 | } 265 | else { 266 | layerIndex = layer.msg('正在批量删除中', { 267 | icon: 16 268 | , shade: 0.3 269 | , time: 60000 270 | }); 271 | 272 | var brurl = `/api/redis/batchremove?name=${redis_name}&dbindex=${db_index}&key=${searchKey}`; 273 | 274 | HttpGet(brurl, null, function (olen) { 275 | layer.msg(`批量删除已完成,已成功删除${olen.Data}条`, { 276 | icon: 1, time: 2000 277 | }, function () { 278 | $("#search_btn").click(); 279 | }); 280 | }); 281 | } 282 | } 283 | ); 284 | }); 285 | 286 | 287 | //添加按钮 288 | $("#add_link").on("click", function () { 289 | layer.open({ 290 | title: '添加redis数据', 291 | type: 2, 292 | area: ['580px', '544px'], 293 | fixed: true, 294 | resize: false, 295 | move: false, 296 | maxmin: false, 297 | time: 0, 298 | content: [`/additem.html?name=${redis_name}&dbindex=${db_index}&type=1&id=`, 'no'] 299 | }); 300 | }); 301 | }); --------------------------------------------------------------------------------