IoTDameon:1.0.0.20090101

Former-commit-id: 031dde85daa45a579e2cefef876396978f40e1f8
Former-commit-id: 393344018b0ea29532bd77448632005bc72daa3a
TSXN
wanggang 5 years ago
parent 99c237f45f
commit 788a5be7e3

@ -1,33 +1,62 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using System;
using System.Reflection;
using System.Threading.Tasks;
namespace IoTDameon.Controllers
{
public class HomeController : Controller
{
private readonly IWebHostEnvironment _env;
private readonly UpdateIoTNodeService _service;
private readonly IHubContext<DefaultHub> _hub;
public HomeController(IWebHostEnvironment env)
public HomeController(IWebHostEnvironment env, UpdateIoTNodeService service, IHubContext<DefaultHub> hub)
{
this._env = env;
this._service = service;
this._hub = hub;
}
public IActionResult Index(string command)
public IActionResult Index()
{
return View();
}
public IActionResult Command(string command)
{
if (!string.IsNullOrEmpty(command))
{
try
{
ViewBag.Output = command.Bash();
this._hub.Clients.All.SendAsync("ServerToClient", command.Bash());
}
catch (Exception ex)
{
ViewBag.Output = ex.ToString();
this._hub.Clients.All.SendAsync("ServerToClient", ex.ToString());
return Problem(ex.Message);
}
}
return View(model: command);
return Ok();
}
public IActionResult Update()
{
try
{
Task.Run(() =>
{
this._service.Update();
});
}
catch (Exception ex)
{
this._hub.Clients.All.SendAsync("ServerToClient", ex.ToString());
return Problem(ex.Message);
}
return Ok();
}
public IActionResult GetVersion()

@ -0,0 +1,18 @@
using Microsoft.AspNetCore.SignalR;
using System.Threading.Tasks;
namespace IoTDameon
{
public class DefaultHub : Hub
{
public override Task OnConnectedAsync()
{
var group = Context.GetHttpContext().Request.Query["group"].ToString();
if (!string.IsNullOrEmpty(group))
{
this.Groups.AddToGroupAsync(Context.ConnectionId, group);
}
return base.OnConnectedAsync();
}
}
}

@ -1,4 +1,4 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
@ -25,9 +25,10 @@ namespace IoTDameon
builder.SetIsOriginAllowed(o => true).AllowAnyMethod().AllowAnyHeader().AllowCredentials();
})
);
services.AddHostedService<UpdateIoTNodeService>();
services.AddSingleton<UpdateIoTNodeService>();
services.AddHostedService<UpdateIoTNodeService>(o => o.GetRequiredService<UpdateIoTNodeService>());
services.AddHttpClient();
services.AddSignalR(o => o.EnableDetailedErrors = true).AddJsonProtocol();
services.AddControllersWithViews();
}
@ -54,6 +55,15 @@ namespace IoTDameon
app.UseEndpoints(endpoints =>
{
endpoints.MapHub<DefaultHub>("/hub", o =>
{
o.ApplicationMaxBufferSize = long.MaxValue;
o.TransportMaxBufferSize = long.MaxValue;
});
endpoints.MapControllerRoute(
name: "areas",
pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}");
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");

@ -1,5 +1,6 @@
using CookComputing.XmlRpc;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
@ -20,29 +21,34 @@ namespace IoTDameon
private readonly ILogger<UpdateIoTNodeService> _logger;
private readonly IWebHostEnvironment _env;
private readonly IHttpClientFactory _httpClientFactory;
private readonly IHubContext<DefaultHub> _hub;
public bool IsUpdating { get; set; }
public UpdateIoTNodeService(ILogger<UpdateIoTNodeService> logger, IWebHostEnvironment env, IHttpClientFactory httpClientFactory)
public UpdateIoTNodeService(ILogger<UpdateIoTNodeService> logger,
IWebHostEnvironment env,
IHttpClientFactory httpClientFactory,
IHubContext<DefaultHub> hub)
{
this._logger = logger;
this._env = env;
this._httpClientFactory = httpClientFactory;
this._hub = hub;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogDebug($"update service start");
stoppingToken.Register(() => _logger.LogDebug("update service stop"));
this.Log($"update service start");
stoppingToken.Register(() => this.Log("update service stop"));
while (!stoppingToken.IsCancellationRequested)
{
this.Update();
await Task.Delay(60 * 10 * 1000, stoppingToken);
await Task.Delay(10 * 60 * 1000, stoppingToken);
}
await Task.CompletedTask;
}
private void Update()
public void Update()
{
if (this.IsUpdating)
{
@ -58,7 +64,7 @@ namespace IoTDameon
}
catch (Exception ex)
{
this._logger.LogError(ex, "update error");
this.LogError(ex.ToString());
}
this.IsUpdating = false;
}
@ -74,29 +80,49 @@ namespace IoTDameon
var name = $"{appFolder}.zip";
var backupPath = Path.Combine(root, $"{appFolder}_bk");
var file = Path.Combine(root, name);
//检查是否有更新
this._logger.LogInformation("check last version");
var currentVersion = this._httpClientFactory.CreateClient().GetAsync($"http://localhost:{port}/Home/GetVersion").Result.Content.ReadAsStringAsync().Result;
string currentVersion;
try
{
currentVersion = this._httpClientFactory.CreateClient().GetAsync($"http://localhost:{port}/Home/GetVersion").Result.Content.ReadAsStringAsync().Result;
this.Log($"节点的当前版本:{currentVersion}");
}
catch (Exception ex)
{
this.LogError("获取当前版本失败");
this.LogError(ex.ToString());
throw ex;
}
var server = this._httpClientFactory.CreateClient().GetAsync($"http://localhost:{port}/Home/GetServer").Result.Content.ReadAsStringAsync().Result;
var serverUrl = $"{server}/{processName}.xml";
this._logger.LogInformation($"request url:{serverUrl}");
var info = this._httpClientFactory.CreateClient().GetAsync(serverUrl).Result.Content.ReadAsStringAsync().Result;
this.Log($"request url:{serverUrl}");
string info;
try
{
info = this._httpClientFactory.CreateClient().GetAsync(serverUrl).Result.Content.ReadAsStringAsync().Result;
this.Log("最新版本信息:");
this.Log(info);
}
catch (Exception ex)
{
this.LogError("获取最新版本失败");
this.LogError(ex.ToString());
throw ex;
}
var doc = new XmlDocument();
doc.LoadXml(info);
var lastVersion = doc.GetElementsByTagName("version")[0].InnerText.Trim();
var lastCheckSum = doc.GetElementsByTagName("checksum")[0].InnerText.Trim();
if (currentVersion != lastVersion)
{
this._logger.LogInformation($"current version {currentVersion} does not equals {lastVersion}");
this.Log($"当前版本 {currentVersion} 不同于最新版本 {lastVersion}");
//查看是否已下载更新并删除旧的更新文件
if (File.Exists(file))
{
var currentCheckSum = GetCheckSum(file);
if (currentCheckSum != lastCheckSum)
{
this._logger.LogWarning($"current file hash {currentCheckSum} does not equals last file hash {lastCheckSum}");
this.Log($"已下载更新hash {currentCheckSum} 不匹配最新更新的hash {lastCheckSum}");
File.Delete(file);
this._logger.LogWarning($"delete old file {file}");
this.Log($"删除过时的文件 {file}");
}
}
//下载更新并校验
@ -110,14 +136,14 @@ namespace IoTDameon
var currentCheckSum = GetCheckSum(file);
if (currentCheckSum != lastCheckSum)
{
this._logger.LogWarning($"download file hash {currentCheckSum} does not math hash in xml {lastCheckSum}");
this.Log($"已下载更新hash {currentCheckSum} 不匹配最新更新的hash {lastCheckSum}");
File.Delete(file);
this._logger.LogWarning($"delete old file {file}");
this.Log($"删除过时的文件 {file}");
}
}
if (File.Exists(file))
{
this._logger.LogWarning($"begin update");
this.Log($"开始更新");
//关闭要更新的程序
var proxy = XmlRpcProxyGen.Create<ISupervisorService>();
proxy.Url = proxyUrl;
@ -125,7 +151,7 @@ namespace IoTDameon
try
{
proxy.stopProcess(processName);
this._logger.LogWarning($"close process {processName}");
this.Log($"关闭进程 {processName}");
}
catch (XmlRpcFaultException ex)
{
@ -135,7 +161,7 @@ namespace IoTDameon
}
else
{
this._logger.LogWarning($"close error{processName} is alreday stoped");
this.Log($"关闭失败,进程 {processName} 已经关闭");
}
}
//备份要更新的程序
@ -146,15 +172,16 @@ namespace IoTDameon
Directory.Delete(backupPath, true);
}
Directory.Move(appPath, backupPath);
this._logger.LogInformation($"back up {appPath}");
this.Log($"备份 {appPath}");
}
catch (Exception ex)
{
this._logger.LogError(ex, ex.Message);
throw new Exception("backup error", ex);
this.Log($"备份失败:");
this.LogError(ex.ToString());
throw ex;
}
Directory.CreateDirectory(appPath);
this._logger.LogInformation($"mkdir {appPath}");
this.Log($"创建目录 {appPath}");
foreach (var item in Directory.GetFiles(backupPath))
{
if (item.EndsWith(".db") || item == "appsettings.json")
@ -162,32 +189,36 @@ namespace IoTDameon
File.Copy(item, Path.Combine(appPath, Path.GetFileName(item)));
}
}
this._logger.LogInformation($"copy db files to {appPath}");
this.Log($"还原数据库db文件 {appPath}");
//更新程序
try
{
ZipFile.ExtractToDirectory(file, root, true);
this._logger.LogInformation($"unzip {file} to {appPath}");
this.Log($"解压更新文件 {file} 到 {appPath}");
}
catch (Exception ex)
{
this._logger.LogError(ex, ex.Message);
this.LogError("解压失败,开始还原");
this.LogError(ex.ToString());
Directory.Delete(appPath, true);
this.LogError("删除失败的更新目录");
Directory.Move(backupPath, appPath);
throw new Exception("upzip errorrestore old files", ex);
this.LogError("还原备份的程序");
throw ex;
}
//设置权限
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
var updateScript = Path.Combine(appPath, "update.sh");
var command = $"chmod 755 {updateScript}";
Console.WriteLine(command.Bash());
Console.WriteLine(updateScript.Bash());
this._logger.LogInformation($"chmod and run {updateScript}");
this.Log(command.Bash());
this.Log($"修改权限:{updateScript}");
this.Log(updateScript.Bash());
this.Log($"执行更细脚本:{updateScript}");
}
//启动更新程序
proxy.startProcess(processName);
this._logger.LogInformation($"{appPath} start");
this.Log($"启动进程:{processName}");
}
}
}
@ -203,5 +234,31 @@ namespace IoTDameon
}
}
}
private void Log(string message)
{
try
{
this._logger.LogInformation(message);
this._hub.Clients.All.SendAsync("ServerToClient", message);
}
catch (Exception ex)
{
this.LogError(ex.ToString());
}
}
private void LogError(string message)
{
try
{
this._logger.LogError(message);
this._hub.Clients.All.SendAsync("ServerToClient", message);
}
catch (Exception ex)
{
this._logger.LogError(ex, ex.Message);
}
}
}
}

@ -1,23 +1,65 @@
@model string
@using System.Reflection
@{
Layout = null;
var version = Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion;
}
@using System.Reflection
@{ Layout = null;
var version = Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion; }
<html lang="en">
<head>
<meta content="text/html; charset=utf-8" http-equiv="content-type" />
<title>更新程序|@version</title>
</head>
<body>
<div style="margin: 0 auto; width: 1000px;">
<form action="/" method="post" style="margin:0;padding:0;">
<input type="text" name="command" value="@Model" placeholder="#" style="width:100%;background:#666;color:#ddd;border:none;height:1.5em;line-height:1.5em;outline:none;" />
</form>
<div style="background:#666;color:#ddd;line-height:1.5em;">
<pre>@Html.Raw(ViewBag.Output)</pre>
</div>
<div style="text-align:center">v @version</div>
<div>
<input type="text" id="command" name="command" value="@Model" placeholder="ssh#" />
<button id="run">run</button>
<button id="update">update</button>
<button id="clear">clear</button>
</div>
<pre id="log" style="background: #666; color: #ddd; line-height: 1.5em;"></pre>
<div style="text-align:center">v @version</div>
<script src="~/lib/jquery/jquery.min.js"></script>
<script src="~/lib/signalr/signalr.min.js"></script>
<script>
var connection = new signalR.HubConnectionBuilder().withUrl('/hub').build();
function log(message) {
$('#log').append(message + '\n');
};
function connect() {
log('check connection state :' + connection.state);
if (connection.state === signalR.HubConnectionState.Disconnected) {
connection.start().then(function () {
log('connect successful');
}).catch(function () {
'connect closed'
log('connect failed');
setTimeout(connect, 5000);
});
}
}
connection.onclose(function () {
log('connect closed');
connect();
});
connection.on("ServerToClient", function (message) {
log(message);
});
connect();
$('#run').click(function () {
$.get('/Home/Command', { command: $('#command').val() }, function (data) {
if (data) {
log(data);
}
});
});
$('#update').click(function () {
$.get('/Home/Update', function (data) {
if (data) {
log(data);
}
});
});
$('#clear').click(function () {
$('#log').html('');
});
</script>
</body>
</html>
</html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long
Loading…
Cancel
Save