mirror of
https://github.com/farcasclaudiu/LanBackup.git
synced 2026-06-29 09:01:08 +03:00
Add project files.
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2" />
|
||||
</startup>
|
||||
<appSettings>
|
||||
<add key="webApiUrl" value="http://localhost:5000" />
|
||||
</appSettings>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Data.SQLite" publicKeyToken="db937bc2d44ff139" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-1.0.104.0" newVersion="1.0.104.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
||||
@@ -0,0 +1,198 @@
|
||||
using Hangfire;
|
||||
using Hangfire.SQLite;
|
||||
using LanBackupAgent.WebApi;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Timers;
|
||||
using TinyMessenger;
|
||||
using LanBackupAgent.Models;
|
||||
using LanBackupAgent.Message;
|
||||
|
||||
namespace LanBackupAgent.Utils
|
||||
{
|
||||
public class BackgroundRefreshController : IDisposable
|
||||
{
|
||||
|
||||
private ILogger logger;
|
||||
private ITinyMessengerHub messenger;
|
||||
private WebApiService webapi;
|
||||
|
||||
public const int WEBAPI_REFRESH_BACKUPS_TIME = 120000; //miliseconds for backups refresh from webapi
|
||||
|
||||
|
||||
#region WebApi
|
||||
private System.Timers.Timer timerRefreshFromWebApi = new System.Timers.Timer();
|
||||
#endregion
|
||||
|
||||
#region Hangfire
|
||||
private BackgroundJobServer hangfireServer;
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
public BackgroundRefreshController(
|
||||
ILogger mlogger,
|
||||
ITinyMessengerHub mmessenger,
|
||||
WebApiService mwebapi
|
||||
)
|
||||
{
|
||||
this.logger = mlogger;
|
||||
this.messenger = mmessenger;
|
||||
this.webapi = mwebapi;
|
||||
|
||||
this.messenger.Subscribe<StopMessage>(Stop);
|
||||
logger.Trace("BackgroundRefreshController instance initialized");
|
||||
}
|
||||
|
||||
internal void Start()
|
||||
{
|
||||
ConfigureHangfire();
|
||||
ConfigureRefresWebApiTimer();
|
||||
}
|
||||
|
||||
private void ConfigureHangfire()
|
||||
{
|
||||
string file = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "local_backups.db3");
|
||||
string sqlconn = $"Data Source={file};Version=3;";
|
||||
|
||||
GlobalConfiguration.Configuration.UseSQLiteStorage(sqlconn);
|
||||
|
||||
var options = new BackgroundJobServerOptions
|
||||
{
|
||||
// This is the default value
|
||||
SchedulePollingInterval = TimeSpan.FromSeconds(5),
|
||||
WorkerCount = 1// FORCE ONE BACKUP at one time - so they will be performed serial one by one
|
||||
};
|
||||
hangfireServer = new BackgroundJobServer(options);
|
||||
|
||||
JobStorage.Current?.GetMonitoringApi()?.PurgeJobs();
|
||||
TimerRefreshFromWebApi_Elapsed(null, null);
|
||||
}
|
||||
|
||||
private void ConfigureRefresWebApiTimer()
|
||||
{
|
||||
//start retry timer for WebAPi refresh
|
||||
timerRefreshFromWebApi = new System.Timers.Timer();
|
||||
timerRefreshFromWebApi.Interval = WEBAPI_REFRESH_BACKUPS_TIME;
|
||||
timerRefreshFromWebApi.Elapsed += TimerRefreshFromWebApi_Elapsed; ;
|
||||
timerRefreshFromWebApi.Start();
|
||||
|
||||
logger.Trace("WebApi timer configured");
|
||||
}
|
||||
|
||||
private async void TimerRefreshFromWebApi_Elapsed(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
logger.Trace(string.Format("WebApi refresh at {0}", DateTime.Now.ToString("o")));
|
||||
|
||||
List<string> activeJobs = new List<string>();
|
||||
var result = await webapi.LoadBackupConfigurations();
|
||||
if (result != null)
|
||||
{
|
||||
foreach (var item in result)
|
||||
{
|
||||
try
|
||||
{
|
||||
string jobId = $"{item.Id}";// _{Convert.ToBase64String(item.RowVersion)}";
|
||||
if (item.IsActive.Value)
|
||||
{
|
||||
//RecurringJob.
|
||||
var recJob = JobStorage.Current.GetMonitoringApi().JobDetails(jobId);
|
||||
//RecurringJob.AddOrUpdate(jobId, () => LanCopyFileJob(item), item.Crontab);
|
||||
if (recJob == null)
|
||||
{
|
||||
RecurringJob.AddOrUpdate(jobId, () => DoCopyJob(item), item.Crontab);
|
||||
}
|
||||
activeJobs.Add(jobId);
|
||||
RecurringJob.Trigger(jobId);
|
||||
}
|
||||
else
|
||||
{
|
||||
//stopping inactive
|
||||
var recJob = JobStorage.Current.GetMonitoringApi().JobDetails(jobId);
|
||||
if (recJob != null)
|
||||
{
|
||||
RecurringJob.RemoveIfExists(jobId);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error($"Reccuring jobs refresh failed: {ex}");
|
||||
//report error to webAPI
|
||||
webapi.LogActivity(item.Id, $"Reccuring jobs refresh failed -({item.Id})- see Err", ex.Message, "ERR");
|
||||
}
|
||||
}
|
||||
}
|
||||
//TODO - purge ofphan jobs
|
||||
//JobStorage.Current.GetMonitoringApi().PurgeOrfanJobsExceptList(activeJobs);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error($"ERR WebApi refresh: {ex}");
|
||||
//report error to webAPI
|
||||
webapi.LogActivity(string.Empty, "Reccuring jobs refresh final failed - see Err", ex.Message, "ERR");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//MUST BE STATIC so it can be called from Hangfire
|
||||
public static string DoCopyJob(BackupConfiguration item)
|
||||
{
|
||||
string result = string.Empty;
|
||||
try
|
||||
{
|
||||
result = DI.Container.GetInstance<LanCopyController>().StartLanCopyFileJob(item);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var logger = DI.Container.GetInstance<ILogger>();
|
||||
logger.Error(ex, "DoCopyJoberror:");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void Stop(StopMessage msg)
|
||||
{
|
||||
//cleanup disposable resources
|
||||
if (timerRefreshFromWebApi != null)
|
||||
{
|
||||
timerRefreshFromWebApi.Stop();
|
||||
timerRefreshFromWebApi.Dispose();
|
||||
}
|
||||
if (hangfireServer != null)
|
||||
{
|
||||
hangfireServer.SendStop();
|
||||
hangfireServer.Dispose();
|
||||
}
|
||||
logger.Trace("LanCopyController Stopped");
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
if (timerRefreshFromWebApi != null)
|
||||
{
|
||||
timerRefreshFromWebApi.Dispose();
|
||||
}
|
||||
if (hangfireServer != null)
|
||||
{
|
||||
hangfireServer.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,437 @@
|
||||
using System;
|
||||
using LanBackupAgent.Models;
|
||||
using NLog;
|
||||
using LanBackupAgent.WebApi;
|
||||
using TinyMessenger;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
using System.IO;
|
||||
using System.Security.Principal;
|
||||
using SimpleImpersonation;
|
||||
using LanBackupAgent.Message;
|
||||
using LanBackup.Models;
|
||||
using System.Linq;
|
||||
using LanBackupAgent.Controllers;
|
||||
|
||||
namespace LanBackupAgent.Utils
|
||||
{
|
||||
public class LanCopyController
|
||||
{
|
||||
|
||||
private ILogger logger;
|
||||
private ITinyMessengerHub messenger;
|
||||
private WebApiService webapi;
|
||||
private Exception externalError;
|
||||
private Network networkUtil;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// used to signal a forced exception
|
||||
/// </summary>
|
||||
private bool forceExceptionalStop;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// used to queue and sync copy jobs between threads
|
||||
/// </summary>
|
||||
ConcurrentQueue<CopyOperation> myCollection;
|
||||
|
||||
/// <summary>
|
||||
/// maximum items in the copy queue - must test to find the best value
|
||||
/// </summary>
|
||||
const int queueTHRESHOLD = 30;
|
||||
|
||||
/// <summary>
|
||||
/// buffer size for copy operations - must test to find the best value
|
||||
/// </summary>
|
||||
const int bufferSize = 5*65536;
|
||||
|
||||
|
||||
|
||||
private long dirCount = 0; long fileCount = 0;
|
||||
|
||||
|
||||
|
||||
public LanCopyController(
|
||||
ILogger mlogger,
|
||||
ITinyMessengerHub mmessenger,
|
||||
WebApiService mwebapi,
|
||||
Network networkUtil
|
||||
)
|
||||
{
|
||||
this.logger = mlogger;
|
||||
this.messenger = mmessenger;
|
||||
this.webapi = mwebapi;
|
||||
this.networkUtil = networkUtil;
|
||||
|
||||
this.messenger.Subscribe<StopMessage>(Stop);
|
||||
logger.Trace("LanCopyController instance initialized");
|
||||
}
|
||||
|
||||
public string StartLanCopyFileJob(BackupConfiguration item)
|
||||
{
|
||||
string jobId = $"{item.Id}";// _{Convert.ToBase64String(item.RowVersion)}";
|
||||
Exception localEx = null;
|
||||
try
|
||||
{
|
||||
var msg = $"({item.Id}) - START";
|
||||
logger.Debug(msg);
|
||||
webapi.LogActivity(item.Id, msg, string.Empty, "OK");
|
||||
ReportAgentStatus(StatusType.Starting, $"Starting copy operation - ({item.Id})", 0, item.Id);
|
||||
|
||||
if (item.SrcUser.IndexOf(@"@") < 0)
|
||||
throw new ArgumentException("Source user must be in format john@computer");
|
||||
if (item.DestUser.IndexOf(@"@") < 0)
|
||||
throw new ArgumentException("Destination user must be in format john@computer");
|
||||
|
||||
|
||||
string domainSrc = item.SrcUser.Substring(item.SrcUser.IndexOf(@"@") + 1);
|
||||
string usernameSrc = item.SrcUser.Substring(0, item.SrcUser.IndexOf(@"@"));
|
||||
string domainDest = item.DestUser.Substring(item.DestUser.IndexOf(@"@") + 1);
|
||||
string usernameDest = item.DestUser.Substring(0, item.DestUser.IndexOf(@"@"));
|
||||
|
||||
Exception outEx;
|
||||
if (!CopyFolderAndFilesWithImpersonation(item.Id, item.SrcFolder, domainSrc, usernameSrc, item.SrcPass,
|
||||
item.DestLanFolder, domainDest, usernameDest, item.DestPass, out outEx))
|
||||
{
|
||||
if (outEx != null)
|
||||
{
|
||||
localEx = outEx;
|
||||
logger.Error($"CopyFolderAndFilesWithImpersonation ERR : {outEx}");
|
||||
ReportAgentStatus(StatusType.Error, "Error... " + outEx.Message, 100);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ReportAgentStatus(StatusType.Idle, $"Finished - ({item.Id})...", 100);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
localEx = ex;
|
||||
logger.Error($"Background job ERR : {ex}");
|
||||
ReportAgentStatus(StatusType.Error, $"Error - ({item.Id}) - " + ex.Message, 0);
|
||||
}
|
||||
finally
|
||||
{
|
||||
var msg = $"({item.Id}) - END";
|
||||
logger.Debug(msg);
|
||||
webapi.LogActivity(item.Id, msg, localEx != null ? localEx.Message : string.Empty, localEx != null ? "ERR" : "OK");
|
||||
}
|
||||
|
||||
|
||||
return jobId;
|
||||
}
|
||||
|
||||
private void ReportAgentStatus(StatusType type, string desc, int percent, string configId = "")
|
||||
{
|
||||
var obj = new StatusReport("", new StatusReportInfo()
|
||||
{
|
||||
IP = networkUtil.GetLocalIPAddress(),
|
||||
ConfigurationId = configId,
|
||||
StatusType = type,
|
||||
StatusDateTime = DateTime.UtcNow,
|
||||
StatusPercent = percent,
|
||||
StatusDescription = desc
|
||||
});
|
||||
ThreadPool.QueueUserWorkItem((msg) => {
|
||||
this.messenger.Publish(msg as StatusReport);
|
||||
}, obj);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Copy the source folder content into a destination folder
|
||||
/// </summary>
|
||||
/// <param name="srcPath">source folder</param>
|
||||
/// <param name="destPath">destination folder</param>
|
||||
/// <returns>true if success</returns>
|
||||
private bool CopyFolderAndFilesWithImpersonation(string configID, string srcPath, string domainSrc, string userSrc, string passSrc,
|
||||
string destPath, string domainDest, string userDest, string passDest, out Exception outEx)
|
||||
{
|
||||
outEx = null;
|
||||
myCollection = new ConcurrentQueue<CopyOperation>();
|
||||
|
||||
byte[] buffer = new byte[bufferSize];
|
||||
bool finished = false;
|
||||
Exception srcError = null;
|
||||
Exception destError = null;
|
||||
Thread thSource = new Thread(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
//FileLogging.Log($"begin thSrc.", LogLevel.Info);
|
||||
using (Impersonation.LogonUser(domainSrc, userSrc, passSrc, LogonType.Network))//LogonType.Interactive
|
||||
{
|
||||
WindowsIdentity wid_current = WindowsIdentity.GetCurrent();
|
||||
//FileLogging.Log($"impersonated thSrc. {wid_current.Name}", LogLevel.Info);
|
||||
//scan all of the directories
|
||||
var lstDirectories = Directory.GetDirectories(srcPath, "*", SearchOption.AllDirectories);
|
||||
dirCount = lstDirectories.LongCount();
|
||||
foreach (string dirPath in lstDirectories)
|
||||
{
|
||||
CopyOperation coFolder = new CopyOperation
|
||||
{
|
||||
Operation = OperationType.WriteFolder,
|
||||
Argument = dirPath.Replace(srcPath, string.Empty)
|
||||
};
|
||||
FillQueue(myCollection, queueTHRESHOLD, destError, coFolder);
|
||||
if (forceExceptionalStop)
|
||||
return;
|
||||
//FileLogging.Log($"q-d: {coFolder.Argument}", LogLevel.Info);
|
||||
}
|
||||
//scan all files
|
||||
var lstFiles = Directory.GetFiles(srcPath, "*.*", SearchOption.AllDirectories);
|
||||
fileCount = lstFiles.LongCount();
|
||||
foreach (string newPath in lstFiles)
|
||||
{
|
||||
using (FileStream fs = File.Open(newPath, FileMode.Open, FileAccess.Read, FileShare.Delete | FileShare.Read))
|
||||
{
|
||||
int readB;
|
||||
long offset = 0;
|
||||
while ((readB = fs.Read(buffer, 0, bufferSize)) > 0)
|
||||
{
|
||||
CopyOperation coFile = new CopyOperation
|
||||
{
|
||||
Operation = OperationType.WriteFile,
|
||||
Argument = newPath.Replace(srcPath, string.Empty),
|
||||
Offset = offset,
|
||||
Content = buffer.SubArray(0, readB)
|
||||
};
|
||||
FillQueue(myCollection, queueTHRESHOLD, destError, coFile);
|
||||
if (forceExceptionalStop)
|
||||
return;
|
||||
//FileLogging.Log($"q-f: {coFile.Argument} - {coFile.Offset}", LogLevel.Info);
|
||||
offset += readB;
|
||||
}
|
||||
}
|
||||
}
|
||||
//YEY!
|
||||
finished = true;
|
||||
|
||||
//wic.Undo();
|
||||
}
|
||||
}
|
||||
catch (Exception err)
|
||||
{
|
||||
srcError = err;
|
||||
forceExceptionalStop = true;
|
||||
finished = true;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Thread thDest = new Thread(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
//FileLogging.Log($"begin thDest.", LogLevel.Info);
|
||||
Thread.Sleep(200);
|
||||
|
||||
long dirIndex = 0, fileIndex = 0;
|
||||
string lastFile = string.Empty;
|
||||
DateTime lastSent = DateTime.MinValue;
|
||||
int copyPercent;
|
||||
using (Impersonation.LogonUser(domainDest, userDest, passDest, LogonType.NewCredentials))//LogonType.NewCredentials
|
||||
{
|
||||
WindowsIdentity wid_current = WindowsIdentity.GetCurrent();
|
||||
//FileLogging.Log($"impersonated thDest. {wid_current.Name}", LogLevel.Info);
|
||||
while (!finished || myCollection.Count > 0)
|
||||
{
|
||||
while (myCollection.Count == 0 && !forceExceptionalStop)
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
if (forceExceptionalStop)
|
||||
return;
|
||||
|
||||
CopyOperation item = null;
|
||||
if (myCollection.TryDequeue(out item))
|
||||
{
|
||||
switch (item.Operation)
|
||||
{
|
||||
case OperationType.WriteFolder:
|
||||
string newFolder = UncCombine(destPath, item.Argument);
|
||||
Directory.CreateDirectory(newFolder);
|
||||
dirIndex++;
|
||||
break;
|
||||
case OperationType.WriteFile:
|
||||
string destFile = UncCombine(destPath, item.Argument);
|
||||
using (var fs = ((item.Offset == 0) && File.Exists(destFile)) ? File.Create(destFile) : File.OpenWrite(destFile))
|
||||
{
|
||||
fs.Seek(item.Offset, SeekOrigin.Begin);
|
||||
fs.Write(item.Content, 0, item.Content.Length);
|
||||
}
|
||||
if (destFile != lastFile)
|
||||
{
|
||||
fileIndex++;
|
||||
lastFile = destFile;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
//update report status
|
||||
DateTime now = DateTime.UtcNow;
|
||||
if ( now > lastSent)
|
||||
{
|
||||
copyPercent = Convert.ToInt32( 20 * (dirCount > 0 ? (double)dirIndex / dirCount : 1) + 80 * (fileCount > 0 ? (double)fileIndex / fileCount : 1) );
|
||||
lastSent = now + TimeSpan.FromMilliseconds(SignalRController.SIGNALR_REFRESH_INTERVAL);
|
||||
ReportAgentStatus(item.Operation == OperationType.WriteFolder ? StatusType.CopyFolders : StatusType.CopyFiles,
|
||||
item.Operation == OperationType.WriteFolder ? $"Copy folders in progress {dirIndex}/{dirCount} ..." : $"Copy files in progress {fileIndex}/{fileCount} ..."
|
||||
, copyPercent, configID);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException("Could not extract item from queue");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
catch (Exception err)
|
||||
{
|
||||
destError = err;
|
||||
forceExceptionalStop = true;
|
||||
}
|
||||
});
|
||||
|
||||
//clear exception
|
||||
forceExceptionalStop = false;
|
||||
|
||||
thDest.Start();
|
||||
thSource.Start();
|
||||
|
||||
//wait to complete or fail
|
||||
thSource.Join();
|
||||
thDest.Join();
|
||||
|
||||
|
||||
myCollection = new ConcurrentQueue<CopyOperation>();
|
||||
|
||||
|
||||
if (srcError != null)
|
||||
{
|
||||
outEx = srcError;
|
||||
logger.Error($"srcErr: {srcError}");
|
||||
return false;
|
||||
}
|
||||
if (destError != null)
|
||||
{
|
||||
outEx = destError;
|
||||
logger.Error($"destErr: {destError}");
|
||||
return false;
|
||||
}
|
||||
if (externalError != null)
|
||||
{
|
||||
outEx = externalError;
|
||||
logger.Debug($"externalError: {destError}");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
logger.Error($"LAN Copy completed!");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// helper function for adding items in the copy queue
|
||||
/// </summary>
|
||||
/// <param name="myCollection"></param>
|
||||
/// <param name="queueTHRESHOLD"></param>
|
||||
/// <param name="destError"></param>
|
||||
/// <param name="coFolder"></param>
|
||||
private void FillQueue(ConcurrentQueue<CopyOperation> myCollection, int queueTHRESHOLD, Exception destError, CopyOperation coFolder)
|
||||
{
|
||||
if (destError != null)
|
||||
throw new ArgumentException("Thread Copy Destination has failed", destError);
|
||||
|
||||
while (myCollection.Count > queueTHRESHOLD)
|
||||
{
|
||||
if (forceExceptionalStop)
|
||||
return;
|
||||
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
|
||||
myCollection.Enqueue(coFolder);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Helper function to combine Unc paths
|
||||
/// </summary>
|
||||
/// <param name="startPath"></param>
|
||||
/// <param name="endPath"></param>
|
||||
/// <returns></returns>
|
||||
private string UncCombine(string startPath, string endPath)
|
||||
{
|
||||
while (startPath.EndsWith(@"\"))
|
||||
startPath = startPath.Substring(0, startPath.Length - 1);
|
||||
while (endPath.StartsWith(@"\"))
|
||||
endPath = endPath.Substring(1);
|
||||
return startPath + @"\" + endPath;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private void Stop(StopMessage obj)
|
||||
{
|
||||
//signal stop
|
||||
externalError = new ApplicationException("Stop processing invoked by the service");
|
||||
forceExceptionalStop = true;
|
||||
//cleanup disposable resources
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// helper class used in quing and sync copy operation between threads
|
||||
/// </summary>
|
||||
internal class CopyOperation
|
||||
{
|
||||
/// <summary>
|
||||
/// Operation type - can be either Write Folder or Write File
|
||||
/// </summary>
|
||||
public OperationType Operation { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// partial folder or file path
|
||||
/// </summary>
|
||||
public string Argument { get; internal set; }
|
||||
public byte[] Content { get; internal set; }
|
||||
public long Offset { get; internal set; }
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// helper enumeration for copy operation type
|
||||
/// </summary>
|
||||
internal enum OperationType
|
||||
{
|
||||
WriteFolder,
|
||||
WriteFile
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,293 @@
|
||||
using LanBackup.Models;
|
||||
using LanBackupAgent.Message;
|
||||
using LanBackupAgent.Utils;
|
||||
using Microsoft.AspNet.SignalR.Client;
|
||||
using Newtonsoft.Json;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Configuration;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.WebSockets;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Timers;
|
||||
using TinyMessenger;
|
||||
|
||||
namespace LanBackupAgent.Controllers
|
||||
{
|
||||
public class SignalRController : IDisposable
|
||||
{
|
||||
|
||||
//TODO - move these in config file
|
||||
const string SIGNALR_HUBNAME = "backupslan";
|
||||
public const int SIGNALR_REFRESH_INTERVAL = 1500;
|
||||
public const int SIGNALR_REFRESH_INTERVAL_IDLE = 10000; //used for retry and ping (when idle)
|
||||
|
||||
|
||||
private ILogger logger;
|
||||
private ITinyMessengerHub messenger;
|
||||
private string webapiUrlBase;
|
||||
private Network networkUtil;
|
||||
private bool isWorkInProgress;
|
||||
|
||||
#region SignalR Hub
|
||||
HubConnection connSignalR;
|
||||
private DateTime connSignalrClosedOn;
|
||||
IHubProxy hubSignalr;
|
||||
private System.Timers.Timer timerRetryPingSignalR = new System.Timers.Timer();
|
||||
#endregion
|
||||
|
||||
|
||||
public SignalRController(
|
||||
string mwebapiUrlBase,
|
||||
ILogger mlogger,
|
||||
ITinyMessengerHub mmessenger,
|
||||
Network networkUtil
|
||||
)
|
||||
{
|
||||
this.webapiUrlBase = mwebapiUrlBase;
|
||||
this.logger = mlogger;
|
||||
this.messenger = mmessenger;
|
||||
this.networkUtil = networkUtil;
|
||||
this.messenger.Subscribe<StopMessage>(DoStop);
|
||||
this.messenger.Subscribe<StatusReport>(DoStatus);
|
||||
logger.Trace("SignalRController instance initialized");
|
||||
}
|
||||
|
||||
public async Task<bool> Start()
|
||||
{
|
||||
logger.Trace("SignalRController Starting");
|
||||
await ConfigureSignalR();
|
||||
ConfigureTimers();
|
||||
logger.Trace("SignalRController Started");
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task ConfigureSignalR()
|
||||
{
|
||||
string webApiUrl = ConfigurationManager.AppSettings["webApiUrl"];
|
||||
string hubAddress = webApiUrl + "/signalr";
|
||||
logger.Trace($"Starting SignalR Client on: {hubAddress}");
|
||||
connSignalR = new HubConnection(hubAddress, useDefaultUrl: false);
|
||||
connSignalR.Error += (err) =>
|
||||
{
|
||||
if (err is WebSocketException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
logger.Error($"Err: {err}");
|
||||
};
|
||||
connSignalR.Closed += () =>
|
||||
{
|
||||
logger.Info($"Connection closed.");
|
||||
connSignalrClosedOn = DateTime.Now;
|
||||
};
|
||||
connSignalR.ConnectionSlow += () =>
|
||||
{
|
||||
//TODO - log the issue
|
||||
};
|
||||
connSignalR.Reconnected += () =>
|
||||
{
|
||||
//TODO - check for overdue/due backups on the server
|
||||
};
|
||||
|
||||
hubSignalr = connSignalR.CreateHubProxy(SIGNALR_HUBNAME);
|
||||
hubSignalr.On<string>("controlAgent", data => //Waiting control signal from server signalr
|
||||
{
|
||||
logger.Trace($"controlAgent data: {data}");
|
||||
//scheduler
|
||||
ThreadPool.QueueUserWorkItem((par) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
DateTime recDate = (DateTime)par;
|
||||
//TODO - something to do by the server command
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error($"ERR scheduler: {ex}");
|
||||
}
|
||||
}, DateTime.Now);
|
||||
});
|
||||
|
||||
await TryStartSignalR();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#region Timer for retry
|
||||
|
||||
private void ConfigureTimers()
|
||||
{
|
||||
//start retry timer for SignalR
|
||||
timerRetryPingSignalR.Interval = SIGNALR_REFRESH_INTERVAL_IDLE;
|
||||
timerRetryPingSignalR.Elapsed += TimerRetryPingSignalR_Elapsed;
|
||||
timerRetryPingSignalR.Start();
|
||||
|
||||
logger.Trace("Timers configured");
|
||||
}
|
||||
|
||||
|
||||
private async void TimerRetryPingSignalR_Elapsed(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (connSignalR != null)
|
||||
{
|
||||
//retry
|
||||
if (connSignalR.State == ConnectionState.Disconnected)
|
||||
{
|
||||
if((DateTime.Now - connSignalrClosedOn).TotalSeconds > 5) //retry after 5 seconds
|
||||
await TryStartSignalR();
|
||||
}
|
||||
else if (connSignalR.State == ConnectionState.Connected)
|
||||
{
|
||||
//send ping signal when IDLE
|
||||
if(!isWorkInProgress)
|
||||
{
|
||||
// IDLE ping signalr - i'm online!
|
||||
ReadyIdlePing();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error($"ERR TimerRetrySignalR: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// signal thet agent is online
|
||||
/// </summary>
|
||||
private void ReadyIdlePing()
|
||||
{
|
||||
this.messenger.Publish(new StatusReport("", new StatusReportInfo()
|
||||
{
|
||||
IP = networkUtil.GetLocalIPAddress(),
|
||||
ConfigurationId = string.Empty,
|
||||
StatusType = StatusType.Idle,
|
||||
StatusDateTime = DateTime.UtcNow,
|
||||
StatusPercent = 0,
|
||||
StatusDescription = $"Ready ..."
|
||||
}));
|
||||
}
|
||||
|
||||
public async Task TryStartSignalR()
|
||||
{
|
||||
try
|
||||
{
|
||||
logger.Info($"Trying ro connect ...");
|
||||
await connSignalR.Start();
|
||||
// hello to signalr - i'm online!
|
||||
ReadyIdlePing();
|
||||
}
|
||||
catch (HttpRequestException hex)
|
||||
{
|
||||
if (hex.InnerException is WebException)
|
||||
{
|
||||
WebException wex = hex.InnerException as WebException;
|
||||
if (wex.Status == WebExceptionStatus.ConnectFailure)
|
||||
{
|
||||
//ignore this err
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Error($"ERR WebException HResult: {wex.HResult}");
|
||||
logger.Error($"ERR WebException Status: {wex.Status}");
|
||||
logger.Error($"ERR WebException Message: {wex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error($"ERR Start SignalR: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Timer for retry
|
||||
|
||||
|
||||
|
||||
#region SignalR actions
|
||||
private void DoStop(StopMessage obj)
|
||||
{
|
||||
//cleanup disposable resources
|
||||
if (timerRetryPingSignalR != null)
|
||||
{
|
||||
timerRetryPingSignalR.Stop();
|
||||
timerRetryPingSignalR.Dispose();
|
||||
}
|
||||
if (connSignalR != null)
|
||||
{
|
||||
connSignalR.Stop();
|
||||
connSignalR.Dispose();
|
||||
}
|
||||
|
||||
logger.Trace("SignalRController Stopped");
|
||||
}
|
||||
|
||||
|
||||
private void DoStatus(StatusReport obj)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.isWorkInProgress = (obj.Info.StatusType != StatusType.Idle);
|
||||
|
||||
if (connSignalR != null)
|
||||
{
|
||||
logger.Warn($"DoStatus {JsonConvert.SerializeObject(obj)}");
|
||||
if (connSignalR != null && connSignalR.State == ConnectionState.Connected)
|
||||
{
|
||||
hubSignalr.Invoke("StatusReport", obj.Info);
|
||||
}
|
||||
else
|
||||
{
|
||||
//connection not open
|
||||
}
|
||||
}else
|
||||
{
|
||||
logger.Warn($"Warn SignalR DoStatus: connection null");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error($"ERR SignalR DoStatus: {ex}");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endregion SignalR actions
|
||||
|
||||
|
||||
|
||||
#region IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
if (timerRetryPingSignalR != null)
|
||||
{
|
||||
timerRetryPingSignalR.Dispose();
|
||||
}
|
||||
if (connSignalR != null)
|
||||
{
|
||||
connSignalR.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion IDisposable
|
||||
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,202 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{CB7EE867-93E8-43E3-9EEE-9068AED1DE6C}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>LanBackupAgent</RootNamespace>
|
||||
<AssemblyName>LanBackupAgent</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
<PublishUrl>publish\</PublishUrl>
|
||||
<Install>true</Install>
|
||||
<InstallFrom>Disk</InstallFrom>
|
||||
<UpdateEnabled>false</UpdateEnabled>
|
||||
<UpdateMode>Foreground</UpdateMode>
|
||||
<UpdateInterval>7</UpdateInterval>
|
||||
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
|
||||
<UpdatePeriodically>false</UpdatePeriodically>
|
||||
<UpdateRequired>false</UpdateRequired>
|
||||
<MapFileExtensions>true</MapFileExtensions>
|
||||
<ApplicationRevision>0</ApplicationRevision>
|
||||
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
|
||||
<IsWebBootstrapper>false</IsWebBootstrapper>
|
||||
<UseApplicationTrust>false</UseApplicationTrust>
|
||||
<BootstrapperEnabled>true</BootstrapperEnabled>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ApplicationIcon>app_icon.png.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="ColoredConsole, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\ColoredConsole.0.5.0\lib\net45\ColoredConsole.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Dapper, Version=1.40.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Dapper.1.42\lib\net45\Dapper.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Hangfire.Core, Version=1.6.8.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Hangfire.Core.1.6.8\lib\net45\Hangfire.Core.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Hangfire.SQLite, Version=1.1.1.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Hangfire.SQLite.1.1.1.0\lib\net45\Hangfire.SQLite.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.AspNet.SignalR.Client, Version=2.2.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.AspNet.SignalR.Client.2.2.1\lib\net45\Microsoft.AspNet.SignalR.Client.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Rest.ClientRuntime, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.Rest.ClientRuntime.2.3.4\lib\net45\Microsoft.Rest.ClientRuntime.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Newtonsoft.Json.6.0.8\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\NLog.4.4.1\lib\net45\NLog.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Owin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0ebd12fd5e55cc5, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="SimpleImpersonation, Version=2.0.1.27158, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\SimpleImpersonation.2.0.1\lib\net40-Client\SimpleImpersonation.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="SimpleInjector, Version=3.3.2.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\SimpleInjector.3.3.2\lib\net45\SimpleInjector.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Data.SQLite, Version=1.0.104.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.Data.SQLite.Core.1.0.104.0\lib\net46\System.Data.SQLite.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Net" />
|
||||
<Reference Include="System.Net.Http.WebRequest" />
|
||||
<Reference Include="System.Runtime.Serialization" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="Topshelf, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b800c4cfcdeea87b, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Topshelf.4.0.3\lib\net452\Topshelf.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Topshelf.NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b800c4cfcdeea87b, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Topshelf.NLog.4.0.3\lib\net452\Topshelf.NLog.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\LanBackup.WebApp\Models\DTO\StatusReportInfo.cs">
|
||||
<Link>Models\StatusReportInfo.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="Controllers\SignalRController.cs" />
|
||||
<Compile Include="GlobalSuppressions.cs" />
|
||||
<Compile Include="LanBackupAgentService.cs" />
|
||||
<Compile Include="LanBackups API\ILanBackupsAPI.cs" />
|
||||
<Compile Include="LanBackups API\LanBackupsAPI.cs" />
|
||||
<Compile Include="LanBackups API\LanBackupsAPIExtensions.cs" />
|
||||
<Compile Include="LanBackups API\Models\BackupConfiguration.cs" />
|
||||
<Compile Include="LanBackups API\Models\BackupLog.cs" />
|
||||
<Compile Include="LanBackups API\Models\IdentityError.cs" />
|
||||
<Compile Include="LanBackups API\Models\IdentityResult.cs" />
|
||||
<Compile Include="LanBackups API\Models\PaginatedListBackupConfigurationString.cs" />
|
||||
<Compile Include="LanBackups API\Models\PaginatedListBackupLogDateTime.cs" />
|
||||
<Compile Include="LanBackups API\Models\User.cs" />
|
||||
<Compile Include="Message\StatusReport.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Message\StopMessage.cs" />
|
||||
<Compile Include="Utils\Extensions.cs" />
|
||||
<Compile Include="Utils\HangfireExtensions.cs" />
|
||||
<Compile Include="Utils\NativeMethods.cs" />
|
||||
<Compile Include="Utils\TinyMessenger.cs" />
|
||||
<Compile Include="Utils\DI.cs" />
|
||||
<Compile Include="Controllers\BackgroundRefreshController.cs" />
|
||||
<Compile Include="Controllers\LanCopyController.cs" />
|
||||
<Compile Include="Utils\Network.cs" />
|
||||
<Compile Include="WebApi\WebApiService.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
<Content Include="nlog.config">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<None Include="nlog.Debug.config">
|
||||
<DependentUpon>nlog.config</DependentUpon>
|
||||
</None>
|
||||
<None Include="nlog.Release.config">
|
||||
<DependentUpon>nlog.config</DependentUpon>
|
||||
</None>
|
||||
<None Include="nlog.xsd">
|
||||
<SubType>Designer</SubType>
|
||||
</None>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>.NET Framework 3.5 SP1</ProductName>
|
||||
<Install>false</Install>
|
||||
</BootstrapperPackage>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="app_icon.png.ico" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="..\..\packages\System.Data.SQLite.Core.1.0.104.0\build\net46\System.Data.SQLite.Core.targets" Condition="Exists('..\..\packages\System.Data.SQLite.Core.1.0.104.0\build\net46\System.Data.SQLite.Core.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\packages\System.Data.SQLite.Core.1.0.104.0\build\net46\System.Data.SQLite.Core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\System.Data.SQLite.Core.1.0.104.0\build\net46\System.Data.SQLite.Core.targets'))" />
|
||||
</Target>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" />
|
||||
<Target Name="AfterBuild" Condition="Exists('nlog.$(Configuration).config')">
|
||||
<!--Generate transformed config in the output directory-->
|
||||
<TransformXml Source="nlog.config" Destination="$(OutputPath)nlog.config" Transform="nlog.$(Configuration).config" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -0,0 +1,51 @@
|
||||
using LanBackupAgent.Controllers;
|
||||
using LanBackupAgent.Message;
|
||||
using LanBackupAgent.Utils;
|
||||
using Microsoft.AspNet.SignalR.Client;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using System.Timers;
|
||||
using TinyMessenger;
|
||||
|
||||
namespace LanBackupAgent
|
||||
{
|
||||
public class LanBackupAgentService
|
||||
{
|
||||
|
||||
private ILogger logger;
|
||||
ITinyMessengerHub messenger;
|
||||
|
||||
|
||||
public LanBackupAgentService(ILogger mlogger, ITinyMessengerHub mmessenger)
|
||||
{
|
||||
this.logger = mlogger;
|
||||
this.messenger = mmessenger;
|
||||
}
|
||||
|
||||
public async Task<bool> Start()
|
||||
{
|
||||
logger.Info("Started");
|
||||
|
||||
//START LOGIC
|
||||
DI.Container.GetInstance<BackgroundRefreshController>().Start();
|
||||
await DI.Container.GetInstance<SignalRController>().Start();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public bool Stop()
|
||||
{
|
||||
//STOP LOGIC
|
||||
logger.Info("Stopping signal received ...");
|
||||
messenger.Publish(new StopMessage("STOP"));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
// Code generated by Microsoft (R) AutoRest Code Generator 0.16.0.0
|
||||
// Changes may cause incorrect behavior and will be lost if the code is
|
||||
// regenerated.
|
||||
|
||||
namespace LanBackupAgent
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using Microsoft.Rest;
|
||||
using Models;
|
||||
|
||||
/// <summary>
|
||||
/// ASP.NET Core Web API for LAN Backup service
|
||||
/// </summary>
|
||||
public partial interface ILanBackupsAPI : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// The base URI of the service.
|
||||
/// </summary>
|
||||
Uri BaseUri { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets json serialization settings.
|
||||
/// </summary>
|
||||
JsonSerializerSettings SerializationSettings { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets json deserialization settings.
|
||||
/// </summary>
|
||||
JsonSerializerSettings DeserializationSettings { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Subscription credentials which uniquely identify client
|
||||
/// subscription.
|
||||
/// </summary>
|
||||
ServiceClientCredentials Credentials { get; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// retrieve all backup configurations
|
||||
/// </summary>
|
||||
/// <param name='idx'>
|
||||
/// </param>
|
||||
/// <param name='siz'>
|
||||
/// </param>
|
||||
/// <param name='customHeaders'>
|
||||
/// The headers that will be added to request.
|
||||
/// </param>
|
||||
/// <param name='cancellationToken'>
|
||||
/// The cancellation token.
|
||||
/// </param>
|
||||
Task<HttpOperationResponse<PaginatedListBackupConfigurationString>> ApiBackupConfigGetWithHttpMessagesAsync(string idx = default(string), string siz = default(string), Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// create backup configuration
|
||||
/// </summary>
|
||||
/// <param name='backup'>
|
||||
/// </param>
|
||||
/// <param name='customHeaders'>
|
||||
/// The headers that will be added to request.
|
||||
/// </param>
|
||||
/// <param name='cancellationToken'>
|
||||
/// The cancellation token.
|
||||
/// </param>
|
||||
Task<HttpOperationResponse<BackupConfiguration>> ApiBackupConfigPostWithHttpMessagesAsync(BackupConfiguration backup = default(BackupConfiguration), Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// retrieve backup configuration by ID
|
||||
/// </summary>
|
||||
/// <param name='id'>
|
||||
/// </param>
|
||||
/// <param name='customHeaders'>
|
||||
/// The headers that will be added to request.
|
||||
/// </param>
|
||||
/// <param name='cancellationToken'>
|
||||
/// The cancellation token.
|
||||
/// </param>
|
||||
Task<HttpOperationResponse<BackupConfiguration>> ApiBackupConfigByIdGetWithHttpMessagesAsync(string id, Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// Update backup configuration
|
||||
/// </summary>
|
||||
/// <param name='id'>
|
||||
/// </param>
|
||||
/// <param name='backup'>
|
||||
/// </param>
|
||||
/// <param name='customHeaders'>
|
||||
/// The headers that will be added to request.
|
||||
/// </param>
|
||||
/// <param name='cancellationToken'>
|
||||
/// The cancellation token.
|
||||
/// </param>
|
||||
Task<HttpOperationResponse<BackupConfiguration>> ApiBackupConfigByIdPutWithHttpMessagesAsync(string id, BackupConfiguration backup = default(BackupConfiguration), Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// delete backup configuration by id
|
||||
/// </summary>
|
||||
/// <param name='id'>
|
||||
/// </param>
|
||||
/// <param name='customHeaders'>
|
||||
/// The headers that will be added to request.
|
||||
/// </param>
|
||||
/// <param name='cancellationToken'>
|
||||
/// The cancellation token.
|
||||
/// </param>
|
||||
Task<HttpOperationResponse<object>> ApiBackupConfigByIdDeleteWithHttpMessagesAsync(string id, Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// retrieve backup configuration by ID
|
||||
/// </summary>
|
||||
/// <param name='clientid'>
|
||||
/// </param>
|
||||
/// <param name='customHeaders'>
|
||||
/// The headers that will be added to request.
|
||||
/// </param>
|
||||
/// <param name='cancellationToken'>
|
||||
/// The cancellation token.
|
||||
/// </param>
|
||||
Task<HttpOperationResponse<IList<BackupConfiguration>>> ApiBackupConfigClientByClientidGetWithHttpMessagesAsync(string clientid, Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// retrieves paginated logs from DB
|
||||
/// </summary>
|
||||
/// <param name='idx'>
|
||||
/// page index
|
||||
/// </param>
|
||||
/// <param name='siz'>
|
||||
/// page size
|
||||
/// </param>
|
||||
/// <param name='customHeaders'>
|
||||
/// The headers that will be added to request.
|
||||
/// </param>
|
||||
/// <param name='cancellationToken'>
|
||||
/// The cancellation token.
|
||||
/// </param>
|
||||
Task<HttpOperationResponse<PaginatedListBackupLogDateTime>> ApiLogsGetWithHttpMessagesAsync(string idx = default(string), string siz = default(string), Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// Create an entry in the logs
|
||||
/// </summary>
|
||||
/// <param name='log'>
|
||||
/// </param>
|
||||
/// <param name='customHeaders'>
|
||||
/// The headers that will be added to request.
|
||||
/// </param>
|
||||
/// <param name='cancellationToken'>
|
||||
/// The cancellation token.
|
||||
/// </param>
|
||||
Task<HttpOperationResponse<object>> ApiLogsPostWithHttpMessagesAsync(BackupLog log = default(BackupLog), Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// retrieves alog record by ID
|
||||
/// </summary>
|
||||
/// <param name='id'>
|
||||
/// </param>
|
||||
/// <param name='customHeaders'>
|
||||
/// The headers that will be added to request.
|
||||
/// </param>
|
||||
/// <param name='cancellationToken'>
|
||||
/// The cancellation token.
|
||||
/// </param>
|
||||
Task<HttpOperationResponse<BackupLog>> ApiLogsByIdGetWithHttpMessagesAsync(int id, Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// retrieve all logs of a clientIP
|
||||
/// </summary>
|
||||
/// <param name='clientid'>
|
||||
/// </param>
|
||||
/// <param name='customHeaders'>
|
||||
/// The headers that will be added to request.
|
||||
/// </param>
|
||||
/// <param name='cancellationToken'>
|
||||
/// The cancellation token.
|
||||
/// </param>
|
||||
Task<HttpOperationResponse<IList<BackupLog>>> ApiLogsClientByClientidGetWithHttpMessagesAsync(string clientid, Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// retrieves all logs of a specific configuration
|
||||
/// </summary>
|
||||
/// <param name='configurationid'>
|
||||
/// </param>
|
||||
/// <param name='customHeaders'>
|
||||
/// The headers that will be added to request.
|
||||
/// </param>
|
||||
/// <param name='cancellationToken'>
|
||||
/// The cancellation token.
|
||||
/// </param>
|
||||
Task<HttpOperationResponse<IList<BackupLog>>> ApiLogsConfigByConfigurationidGetWithHttpMessagesAsync(string configurationid, Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <param name='dtouser'>
|
||||
/// </param>
|
||||
/// <param name='customHeaders'>
|
||||
/// The headers that will be added to request.
|
||||
/// </param>
|
||||
/// <param name='cancellationToken'>
|
||||
/// The cancellation token.
|
||||
/// </param>
|
||||
Task<HttpOperationResponse<IdentityResult>> ApiUsersRegisterPostWithHttpMessagesAsync(User dtouser = default(User), Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <param name='dtouser'>
|
||||
/// </param>
|
||||
/// <param name='customHeaders'>
|
||||
/// The headers that will be added to request.
|
||||
/// </param>
|
||||
/// <param name='cancellationToken'>
|
||||
/// The cancellation token.
|
||||
/// </param>
|
||||
Task<HttpOperationResponse> ApiUsersLoginPostWithHttpMessagesAsync(User dtouser = default(User), Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <param name='dtouser'>
|
||||
/// </param>
|
||||
/// <param name='customHeaders'>
|
||||
/// The headers that will be added to request.
|
||||
/// </param>
|
||||
/// <param name='cancellationToken'>
|
||||
/// The cancellation token.
|
||||
/// </param>
|
||||
Task<HttpOperationResponse> ApiUsersPwchangePostWithHttpMessagesAsync(User dtouser = default(User), Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <param name='customHeaders'>
|
||||
/// The headers that will be added to request.
|
||||
/// </param>
|
||||
/// <param name='cancellationToken'>
|
||||
/// The cancellation token.
|
||||
/// </param>
|
||||
Task<HttpOperationResponse> ApiUsersListGetWithHttpMessagesAsync(Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <param name='user'>
|
||||
/// </param>
|
||||
/// <param name='customHeaders'>
|
||||
/// The headers that will be added to request.
|
||||
/// </param>
|
||||
/// <param name='cancellationToken'>
|
||||
/// The cancellation token.
|
||||
/// </param>
|
||||
Task<HttpOperationResponse> ApiUsersListPostWithHttpMessagesAsync(User user = default(User), Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,503 @@
|
||||
// Code generated by Microsoft (R) AutoRest Code Generator 0.16.0.0
|
||||
// Changes may cause incorrect behavior and will be lost if the code is
|
||||
// regenerated.
|
||||
|
||||
namespace LanBackupAgent
|
||||
{
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Rest;
|
||||
using Models;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for LanBackupsAPI.
|
||||
/// </summary>
|
||||
public static partial class LanBackupsAPIExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// retrieve all backup configurations
|
||||
/// </summary>
|
||||
/// <param name='operations'>
|
||||
/// The operations group for this extension method.
|
||||
/// </param>
|
||||
/// <param name='idx'>
|
||||
/// </param>
|
||||
/// <param name='siz'>
|
||||
/// </param>
|
||||
public static PaginatedListBackupConfigurationString ApiBackupConfigGet(this ILanBackupsAPI operations, string idx = default(string), string siz = default(string))
|
||||
{
|
||||
return Task.Factory.StartNew(s => ((ILanBackupsAPI)s).ApiBackupConfigGetAsync(idx, siz), operations, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).Unwrap().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// retrieve all backup configurations
|
||||
/// </summary>
|
||||
/// <param name='operations'>
|
||||
/// The operations group for this extension method.
|
||||
/// </param>
|
||||
/// <param name='idx'>
|
||||
/// </param>
|
||||
/// <param name='siz'>
|
||||
/// </param>
|
||||
/// <param name='cancellationToken'>
|
||||
/// The cancellation token.
|
||||
/// </param>
|
||||
public static async Task<PaginatedListBackupConfigurationString> ApiBackupConfigGetAsync(this ILanBackupsAPI operations, string idx = default(string), string siz = default(string), CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
using (var _result = await operations.ApiBackupConfigGetWithHttpMessagesAsync(idx, siz, null, cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
return _result.Body;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// create backup configuration
|
||||
/// </summary>
|
||||
/// <param name='operations'>
|
||||
/// The operations group for this extension method.
|
||||
/// </param>
|
||||
/// <param name='backup'>
|
||||
/// </param>
|
||||
public static BackupConfiguration ApiBackupConfigPost(this ILanBackupsAPI operations, BackupConfiguration backup = default(BackupConfiguration))
|
||||
{
|
||||
return Task.Factory.StartNew(s => ((ILanBackupsAPI)s).ApiBackupConfigPostAsync(backup), operations, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).Unwrap().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// create backup configuration
|
||||
/// </summary>
|
||||
/// <param name='operations'>
|
||||
/// The operations group for this extension method.
|
||||
/// </param>
|
||||
/// <param name='backup'>
|
||||
/// </param>
|
||||
/// <param name='cancellationToken'>
|
||||
/// The cancellation token.
|
||||
/// </param>
|
||||
public static async Task<BackupConfiguration> ApiBackupConfigPostAsync(this ILanBackupsAPI operations, BackupConfiguration backup = default(BackupConfiguration), CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
using (var _result = await operations.ApiBackupConfigPostWithHttpMessagesAsync(backup, null, cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
return _result.Body;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// retrieve backup configuration by ID
|
||||
/// </summary>
|
||||
/// <param name='operations'>
|
||||
/// The operations group for this extension method.
|
||||
/// </param>
|
||||
/// <param name='id'>
|
||||
/// </param>
|
||||
public static BackupConfiguration ApiBackupConfigByIdGet(this ILanBackupsAPI operations, string id)
|
||||
{
|
||||
return Task.Factory.StartNew(s => ((ILanBackupsAPI)s).ApiBackupConfigByIdGetAsync(id), operations, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).Unwrap().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// retrieve backup configuration by ID
|
||||
/// </summary>
|
||||
/// <param name='operations'>
|
||||
/// The operations group for this extension method.
|
||||
/// </param>
|
||||
/// <param name='id'>
|
||||
/// </param>
|
||||
/// <param name='cancellationToken'>
|
||||
/// The cancellation token.
|
||||
/// </param>
|
||||
public static async Task<BackupConfiguration> ApiBackupConfigByIdGetAsync(this ILanBackupsAPI operations, string id, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
using (var _result = await operations.ApiBackupConfigByIdGetWithHttpMessagesAsync(id, null, cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
return _result.Body;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update backup configuration
|
||||
/// </summary>
|
||||
/// <param name='operations'>
|
||||
/// The operations group for this extension method.
|
||||
/// </param>
|
||||
/// <param name='id'>
|
||||
/// </param>
|
||||
/// <param name='backup'>
|
||||
/// </param>
|
||||
public static BackupConfiguration ApiBackupConfigByIdPut(this ILanBackupsAPI operations, string id, BackupConfiguration backup = default(BackupConfiguration))
|
||||
{
|
||||
return Task.Factory.StartNew(s => ((ILanBackupsAPI)s).ApiBackupConfigByIdPutAsync(id, backup), operations, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).Unwrap().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update backup configuration
|
||||
/// </summary>
|
||||
/// <param name='operations'>
|
||||
/// The operations group for this extension method.
|
||||
/// </param>
|
||||
/// <param name='id'>
|
||||
/// </param>
|
||||
/// <param name='backup'>
|
||||
/// </param>
|
||||
/// <param name='cancellationToken'>
|
||||
/// The cancellation token.
|
||||
/// </param>
|
||||
public static async Task<BackupConfiguration> ApiBackupConfigByIdPutAsync(this ILanBackupsAPI operations, string id, BackupConfiguration backup = default(BackupConfiguration), CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
using (var _result = await operations.ApiBackupConfigByIdPutWithHttpMessagesAsync(id, backup, null, cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
return _result.Body;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// delete backup configuration by id
|
||||
/// </summary>
|
||||
/// <param name='operations'>
|
||||
/// The operations group for this extension method.
|
||||
/// </param>
|
||||
/// <param name='id'>
|
||||
/// </param>
|
||||
public static object ApiBackupConfigByIdDelete(this ILanBackupsAPI operations, string id)
|
||||
{
|
||||
return Task.Factory.StartNew(s => ((ILanBackupsAPI)s).ApiBackupConfigByIdDeleteAsync(id), operations, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).Unwrap().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// delete backup configuration by id
|
||||
/// </summary>
|
||||
/// <param name='operations'>
|
||||
/// The operations group for this extension method.
|
||||
/// </param>
|
||||
/// <param name='id'>
|
||||
/// </param>
|
||||
/// <param name='cancellationToken'>
|
||||
/// The cancellation token.
|
||||
/// </param>
|
||||
public static async Task<object> ApiBackupConfigByIdDeleteAsync(this ILanBackupsAPI operations, string id, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
using (var _result = await operations.ApiBackupConfigByIdDeleteWithHttpMessagesAsync(id, null, cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
return _result.Body;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// retrieve backup configuration by ID
|
||||
/// </summary>
|
||||
/// <param name='operations'>
|
||||
/// The operations group for this extension method.
|
||||
/// </param>
|
||||
/// <param name='clientid'>
|
||||
/// </param>
|
||||
public static IList<BackupConfiguration> ApiBackupConfigClientByClientidGet(this ILanBackupsAPI operations, string clientid)
|
||||
{
|
||||
return Task.Factory.StartNew(s => ((ILanBackupsAPI)s).ApiBackupConfigClientByClientidGetAsync(clientid), operations, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).Unwrap().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// retrieve backup configuration by ID
|
||||
/// </summary>
|
||||
/// <param name='operations'>
|
||||
/// The operations group for this extension method.
|
||||
/// </param>
|
||||
/// <param name='clientid'>
|
||||
/// </param>
|
||||
/// <param name='cancellationToken'>
|
||||
/// The cancellation token.
|
||||
/// </param>
|
||||
public static async Task<IList<BackupConfiguration>> ApiBackupConfigClientByClientidGetAsync(this ILanBackupsAPI operations, string clientid, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
using (var _result = await operations.ApiBackupConfigClientByClientidGetWithHttpMessagesAsync(clientid, null, cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
return _result.Body;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// retrieves paginated logs from DB
|
||||
/// </summary>
|
||||
/// <param name='operations'>
|
||||
/// The operations group for this extension method.
|
||||
/// </param>
|
||||
/// <param name='idx'>
|
||||
/// page index
|
||||
/// </param>
|
||||
/// <param name='siz'>
|
||||
/// page size
|
||||
/// </param>
|
||||
public static PaginatedListBackupLogDateTime ApiLogsGet(this ILanBackupsAPI operations, string idx = default(string), string siz = default(string))
|
||||
{
|
||||
return Task.Factory.StartNew(s => ((ILanBackupsAPI)s).ApiLogsGetAsync(idx, siz), operations, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).Unwrap().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// retrieves paginated logs from DB
|
||||
/// </summary>
|
||||
/// <param name='operations'>
|
||||
/// The operations group for this extension method.
|
||||
/// </param>
|
||||
/// <param name='idx'>
|
||||
/// page index
|
||||
/// </param>
|
||||
/// <param name='siz'>
|
||||
/// page size
|
||||
/// </param>
|
||||
/// <param name='cancellationToken'>
|
||||
/// The cancellation token.
|
||||
/// </param>
|
||||
public static async Task<PaginatedListBackupLogDateTime> ApiLogsGetAsync(this ILanBackupsAPI operations, string idx = default(string), string siz = default(string), CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
using (var _result = await operations.ApiLogsGetWithHttpMessagesAsync(idx, siz, null, cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
return _result.Body;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an entry in the logs
|
||||
/// </summary>
|
||||
/// <param name='operations'>
|
||||
/// The operations group for this extension method.
|
||||
/// </param>
|
||||
/// <param name='log'>
|
||||
/// </param>
|
||||
public static object ApiLogsPost(this ILanBackupsAPI operations, BackupLog log = default(BackupLog))
|
||||
{
|
||||
return Task.Factory.StartNew(s => ((ILanBackupsAPI)s).ApiLogsPostAsync(log), operations, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).Unwrap().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an entry in the logs
|
||||
/// </summary>
|
||||
/// <param name='operations'>
|
||||
/// The operations group for this extension method.
|
||||
/// </param>
|
||||
/// <param name='log'>
|
||||
/// </param>
|
||||
/// <param name='cancellationToken'>
|
||||
/// The cancellation token.
|
||||
/// </param>
|
||||
public static async Task<object> ApiLogsPostAsync(this ILanBackupsAPI operations, BackupLog log = default(BackupLog), CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
using (var _result = await operations.ApiLogsPostWithHttpMessagesAsync(log, null, cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
return _result.Body;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// retrieves alog record by ID
|
||||
/// </summary>
|
||||
/// <param name='operations'>
|
||||
/// The operations group for this extension method.
|
||||
/// </param>
|
||||
/// <param name='id'>
|
||||
/// </param>
|
||||
public static BackupLog ApiLogsByIdGet(this ILanBackupsAPI operations, int id)
|
||||
{
|
||||
return Task.Factory.StartNew(s => ((ILanBackupsAPI)s).ApiLogsByIdGetAsync(id), operations, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).Unwrap().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// retrieves alog record by ID
|
||||
/// </summary>
|
||||
/// <param name='operations'>
|
||||
/// The operations group for this extension method.
|
||||
/// </param>
|
||||
/// <param name='id'>
|
||||
/// </param>
|
||||
/// <param name='cancellationToken'>
|
||||
/// The cancellation token.
|
||||
/// </param>
|
||||
public static async Task<BackupLog> ApiLogsByIdGetAsync(this ILanBackupsAPI operations, int id, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
using (var _result = await operations.ApiLogsByIdGetWithHttpMessagesAsync(id, null, cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
return _result.Body;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// retrieve all logs of a clientIP
|
||||
/// </summary>
|
||||
/// <param name='operations'>
|
||||
/// The operations group for this extension method.
|
||||
/// </param>
|
||||
/// <param name='clientid'>
|
||||
/// </param>
|
||||
public static IList<BackupLog> ApiLogsClientByClientidGet(this ILanBackupsAPI operations, string clientid)
|
||||
{
|
||||
return Task.Factory.StartNew(s => ((ILanBackupsAPI)s).ApiLogsClientByClientidGetAsync(clientid), operations, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).Unwrap().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// retrieve all logs of a clientIP
|
||||
/// </summary>
|
||||
/// <param name='operations'>
|
||||
/// The operations group for this extension method.
|
||||
/// </param>
|
||||
/// <param name='clientid'>
|
||||
/// </param>
|
||||
/// <param name='cancellationToken'>
|
||||
/// The cancellation token.
|
||||
/// </param>
|
||||
public static async Task<IList<BackupLog>> ApiLogsClientByClientidGetAsync(this ILanBackupsAPI operations, string clientid, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
using (var _result = await operations.ApiLogsClientByClientidGetWithHttpMessagesAsync(clientid, null, cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
return _result.Body;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// retrieves all logs of a specific configuration
|
||||
/// </summary>
|
||||
/// <param name='operations'>
|
||||
/// The operations group for this extension method.
|
||||
/// </param>
|
||||
/// <param name='configurationid'>
|
||||
/// </param>
|
||||
public static IList<BackupLog> ApiLogsConfigByConfigurationidGet(this ILanBackupsAPI operations, string configurationid)
|
||||
{
|
||||
return Task.Factory.StartNew(s => ((ILanBackupsAPI)s).ApiLogsConfigByConfigurationidGetAsync(configurationid), operations, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).Unwrap().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// retrieves all logs of a specific configuration
|
||||
/// </summary>
|
||||
/// <param name='operations'>
|
||||
/// The operations group for this extension method.
|
||||
/// </param>
|
||||
/// <param name='configurationid'>
|
||||
/// </param>
|
||||
/// <param name='cancellationToken'>
|
||||
/// The cancellation token.
|
||||
/// </param>
|
||||
public static async Task<IList<BackupLog>> ApiLogsConfigByConfigurationidGetAsync(this ILanBackupsAPI operations, string configurationid, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
using (var _result = await operations.ApiLogsConfigByConfigurationidGetWithHttpMessagesAsync(configurationid, null, cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
return _result.Body;
|
||||
}
|
||||
}
|
||||
|
||||
/// <param name='operations'>
|
||||
/// The operations group for this extension method.
|
||||
/// </param>
|
||||
/// <param name='dtouser'>
|
||||
/// </param>
|
||||
public static IdentityResult ApiUsersRegisterPost(this ILanBackupsAPI operations, User dtouser = default(User))
|
||||
{
|
||||
return Task.Factory.StartNew(s => ((ILanBackupsAPI)s).ApiUsersRegisterPostAsync(dtouser), operations, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).Unwrap().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <param name='operations'>
|
||||
/// The operations group for this extension method.
|
||||
/// </param>
|
||||
/// <param name='dtouser'>
|
||||
/// </param>
|
||||
/// <param name='cancellationToken'>
|
||||
/// The cancellation token.
|
||||
/// </param>
|
||||
public static async Task<IdentityResult> ApiUsersRegisterPostAsync(this ILanBackupsAPI operations, User dtouser = default(User), CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
using (var _result = await operations.ApiUsersRegisterPostWithHttpMessagesAsync(dtouser, null, cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
return _result.Body;
|
||||
}
|
||||
}
|
||||
|
||||
/// <param name='operations'>
|
||||
/// The operations group for this extension method.
|
||||
/// </param>
|
||||
/// <param name='dtouser'>
|
||||
/// </param>
|
||||
public static void ApiUsersLoginPost(this ILanBackupsAPI operations, User dtouser = default(User))
|
||||
{
|
||||
Task.Factory.StartNew(s => ((ILanBackupsAPI)s).ApiUsersLoginPostAsync(dtouser), operations, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).Unwrap().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <param name='operations'>
|
||||
/// The operations group for this extension method.
|
||||
/// </param>
|
||||
/// <param name='dtouser'>
|
||||
/// </param>
|
||||
/// <param name='cancellationToken'>
|
||||
/// The cancellation token.
|
||||
/// </param>
|
||||
public static async Task ApiUsersLoginPostAsync(this ILanBackupsAPI operations, User dtouser = default(User), CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
await operations.ApiUsersLoginPostWithHttpMessagesAsync(dtouser, null, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <param name='operations'>
|
||||
/// The operations group for this extension method.
|
||||
/// </param>
|
||||
/// <param name='dtouser'>
|
||||
/// </param>
|
||||
public static void ApiUsersPwchangePost(this ILanBackupsAPI operations, User dtouser = default(User))
|
||||
{
|
||||
Task.Factory.StartNew(s => ((ILanBackupsAPI)s).ApiUsersPwchangePostAsync(dtouser), operations, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).Unwrap().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <param name='operations'>
|
||||
/// The operations group for this extension method.
|
||||
/// </param>
|
||||
/// <param name='dtouser'>
|
||||
/// </param>
|
||||
/// <param name='cancellationToken'>
|
||||
/// The cancellation token.
|
||||
/// </param>
|
||||
public static async Task ApiUsersPwchangePostAsync(this ILanBackupsAPI operations, User dtouser = default(User), CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
await operations.ApiUsersPwchangePostWithHttpMessagesAsync(dtouser, null, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <param name='operations'>
|
||||
/// The operations group for this extension method.
|
||||
/// </param>
|
||||
public static void ApiUsersListGet(this ILanBackupsAPI operations)
|
||||
{
|
||||
Task.Factory.StartNew(s => ((ILanBackupsAPI)s).ApiUsersListGetAsync(), operations, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).Unwrap().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <param name='operations'>
|
||||
/// The operations group for this extension method.
|
||||
/// </param>
|
||||
/// <param name='cancellationToken'>
|
||||
/// The cancellation token.
|
||||
/// </param>
|
||||
public static async Task ApiUsersListGetAsync(this ILanBackupsAPI operations, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
await operations.ApiUsersListGetWithHttpMessagesAsync(null, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <param name='operations'>
|
||||
/// The operations group for this extension method.
|
||||
/// </param>
|
||||
/// <param name='user'>
|
||||
/// </param>
|
||||
public static void ApiUsersListPost(this ILanBackupsAPI operations, User user = default(User))
|
||||
{
|
||||
Task.Factory.StartNew(s => ((ILanBackupsAPI)s).ApiUsersListPostAsync(user), operations, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).Unwrap().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <param name='operations'>
|
||||
/// The operations group for this extension method.
|
||||
/// </param>
|
||||
/// <param name='user'>
|
||||
/// </param>
|
||||
/// <param name='cancellationToken'>
|
||||
/// The cancellation token.
|
||||
/// </param>
|
||||
public static async Task ApiUsersListPostAsync(this ILanBackupsAPI operations, User user = default(User), CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
await operations.ApiUsersListPostWithHttpMessagesAsync(user, null, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
// Code generated by Microsoft (R) AutoRest Code Generator 0.16.0.0
|
||||
// Changes may cause incorrect behavior and will be lost if the code is
|
||||
// regenerated.
|
||||
|
||||
namespace LanBackupAgent.Models
|
||||
{
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using Microsoft.Rest;
|
||||
using Microsoft.Rest.Serialization;
|
||||
|
||||
public partial class BackupConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the BackupConfiguration class.
|
||||
/// </summary>
|
||||
public BackupConfiguration() { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the BackupConfiguration class.
|
||||
/// </summary>
|
||||
public BackupConfiguration(string clientIP, string id = default(string), byte[] rowVersion = default(byte[]), string srcFolder = default(string), string srcUser = default(string), string srcPass = default(string), string destLanFolder = default(string), string destUser = default(string), string destPass = default(string), bool? isActive = default(bool?), string crontab = default(string))
|
||||
{
|
||||
Id = id;
|
||||
RowVersion = rowVersion;
|
||||
ClientIP = clientIP;
|
||||
SrcFolder = srcFolder;
|
||||
SrcUser = srcUser;
|
||||
SrcPass = srcPass;
|
||||
DestLanFolder = destLanFolder;
|
||||
DestUser = destUser;
|
||||
DestPass = destPass;
|
||||
IsActive = isActive;
|
||||
Crontab = crontab;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "rowVersion")]
|
||||
public byte[] RowVersion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "clientIP")]
|
||||
public string ClientIP { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "srcFolder")]
|
||||
public string SrcFolder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "srcUser")]
|
||||
public string SrcUser { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "srcPass")]
|
||||
public string SrcPass { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "destLanFolder")]
|
||||
public string DestLanFolder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "destUser")]
|
||||
public string DestUser { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "destPass")]
|
||||
public string DestPass { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "isActive")]
|
||||
public bool? IsActive { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "crontab")]
|
||||
public string Crontab { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Validate the object. Throws ValidationException if validation fails.
|
||||
/// </summary>
|
||||
public virtual void Validate()
|
||||
{
|
||||
if (ClientIP == null)
|
||||
{
|
||||
throw new ValidationException(ValidationRules.CannotBeNull, "ClientIP");
|
||||
}
|
||||
if (this.ClientIP != null)
|
||||
{
|
||||
if (!System.Text.RegularExpressions.Regex.IsMatch(this.ClientIP, "^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$"))
|
||||
{
|
||||
throw new ValidationException(ValidationRules.Pattern, "ClientIP", "^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
// Code generated by Microsoft (R) AutoRest Code Generator 0.16.0.0
|
||||
// Changes may cause incorrect behavior and will be lost if the code is
|
||||
// regenerated.
|
||||
|
||||
namespace LanBackupAgent.Models
|
||||
{
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using Microsoft.Rest;
|
||||
using Microsoft.Rest.Serialization;
|
||||
|
||||
public partial class BackupLog
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the BackupLog class.
|
||||
/// </summary>
|
||||
public BackupLog() { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the BackupLog class.
|
||||
/// </summary>
|
||||
public BackupLog(int? id = default(int?), byte[] rowVersion = default(byte[]), string clientIP = default(string), string configurationID = default(string), string description = default(string), string logError = default(string), string status = default(string), DateTime? dateTime = default(DateTime?))
|
||||
{
|
||||
Id = id;
|
||||
RowVersion = rowVersion;
|
||||
ClientIP = clientIP;
|
||||
ConfigurationID = configurationID;
|
||||
Description = description;
|
||||
LogError = logError;
|
||||
Status = status;
|
||||
DateTime = dateTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "id")]
|
||||
public int? Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "rowVersion")]
|
||||
public byte[] RowVersion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "clientIP")]
|
||||
public string ClientIP { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "configurationID")]
|
||||
public string ConfigurationID { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "description")]
|
||||
public string Description { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "logError")]
|
||||
public string LogError { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "status")]
|
||||
public string Status { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "dateTime")]
|
||||
public DateTime? DateTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Validate the object. Throws ValidationException if validation fails.
|
||||
/// </summary>
|
||||
public virtual void Validate()
|
||||
{
|
||||
if (this.ClientIP != null)
|
||||
{
|
||||
if (!System.Text.RegularExpressions.Regex.IsMatch(this.ClientIP, "^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$"))
|
||||
{
|
||||
throw new ValidationException(ValidationRules.Pattern, "ClientIP", "^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// Code generated by Microsoft (R) AutoRest Code Generator 0.16.0.0
|
||||
// Changes may cause incorrect behavior and will be lost if the code is
|
||||
// regenerated.
|
||||
|
||||
namespace LanBackupAgent.Models
|
||||
{
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using Microsoft.Rest;
|
||||
using Microsoft.Rest.Serialization;
|
||||
|
||||
public partial class IdentityError
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the IdentityError class.
|
||||
/// </summary>
|
||||
public IdentityError() { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the IdentityError class.
|
||||
/// </summary>
|
||||
public IdentityError(string code = default(string), string description = default(string))
|
||||
{
|
||||
Code = code;
|
||||
Description = description;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "code")]
|
||||
public string Code { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "description")]
|
||||
public string Description { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// Code generated by Microsoft (R) AutoRest Code Generator 0.16.0.0
|
||||
// Changes may cause incorrect behavior and will be lost if the code is
|
||||
// regenerated.
|
||||
|
||||
namespace LanBackupAgent.Models
|
||||
{
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using Microsoft.Rest;
|
||||
using Microsoft.Rest.Serialization;
|
||||
|
||||
public partial class IdentityResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the IdentityResult class.
|
||||
/// </summary>
|
||||
public IdentityResult() { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the IdentityResult class.
|
||||
/// </summary>
|
||||
public IdentityResult(bool? succeeded = default(bool?), IList<IdentityError> errors = default(IList<IdentityError>))
|
||||
{
|
||||
Succeeded = succeeded;
|
||||
Errors = errors;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "succeeded")]
|
||||
public bool? Succeeded { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "errors")]
|
||||
public IList<IdentityError> Errors { get; private set; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
// Code generated by Microsoft (R) AutoRest Code Generator 0.16.0.0
|
||||
// Changes may cause incorrect behavior and will be lost if the code is
|
||||
// regenerated.
|
||||
|
||||
namespace LanBackupAgent.Models
|
||||
{
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using Microsoft.Rest;
|
||||
using Microsoft.Rest.Serialization;
|
||||
|
||||
public partial class PaginatedListBackupConfigurationString
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the
|
||||
/// PaginatedListBackupConfigurationString class.
|
||||
/// </summary>
|
||||
public PaginatedListBackupConfigurationString() { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the
|
||||
/// PaginatedListBackupConfigurationString class.
|
||||
/// </summary>
|
||||
public PaginatedListBackupConfigurationString(int? pi = default(int?), int? tp = default(int?), IList<BackupConfiguration> recs = default(IList<BackupConfiguration>), bool? hp = default(bool?), bool? hn = default(bool?))
|
||||
{
|
||||
Pi = pi;
|
||||
Tp = tp;
|
||||
Recs = recs;
|
||||
Hp = hp;
|
||||
Hn = hn;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "pi")]
|
||||
public int? Pi { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "tp")]
|
||||
public int? Tp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "recs")]
|
||||
public IList<BackupConfiguration> Recs { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "hp")]
|
||||
public bool? Hp { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "hn")]
|
||||
public bool? Hn { get; private set; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
// Code generated by Microsoft (R) AutoRest Code Generator 0.16.0.0
|
||||
// Changes may cause incorrect behavior and will be lost if the code is
|
||||
// regenerated.
|
||||
|
||||
namespace LanBackupAgent.Models
|
||||
{
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using Microsoft.Rest;
|
||||
using Microsoft.Rest.Serialization;
|
||||
|
||||
public partial class PaginatedListBackupLogDateTime
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the PaginatedListBackupLogDateTime
|
||||
/// class.
|
||||
/// </summary>
|
||||
public PaginatedListBackupLogDateTime() { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the PaginatedListBackupLogDateTime
|
||||
/// class.
|
||||
/// </summary>
|
||||
public PaginatedListBackupLogDateTime(int? pi = default(int?), int? tp = default(int?), IList<BackupLog> recs = default(IList<BackupLog>), bool? hp = default(bool?), bool? hn = default(bool?))
|
||||
{
|
||||
Pi = pi;
|
||||
Tp = tp;
|
||||
Recs = recs;
|
||||
Hp = hp;
|
||||
Hn = hn;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "pi")]
|
||||
public int? Pi { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "tp")]
|
||||
public int? Tp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "recs")]
|
||||
public IList<BackupLog> Recs { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "hp")]
|
||||
public bool? Hp { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "hn")]
|
||||
public bool? Hn { get; private set; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
// Code generated by Microsoft (R) AutoRest Code Generator 0.16.0.0
|
||||
// Changes may cause incorrect behavior and will be lost if the code is
|
||||
// regenerated.
|
||||
|
||||
namespace LanBackupAgent.Models
|
||||
{
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using Microsoft.Rest;
|
||||
using Microsoft.Rest.Serialization;
|
||||
|
||||
public partial class User
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the User class.
|
||||
/// </summary>
|
||||
public User() { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the User class.
|
||||
/// </summary>
|
||||
public User(string email = default(string), string password = default(string), string newPassword = default(string), bool? isAdmin = default(bool?), bool? succeeded = default(bool?), bool? isLockedOut = default(bool?))
|
||||
{
|
||||
Email = email;
|
||||
Password = password;
|
||||
NewPassword = newPassword;
|
||||
IsAdmin = isAdmin;
|
||||
Succeeded = succeeded;
|
||||
IsLockedOut = isLockedOut;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "email")]
|
||||
public string Email { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "password")]
|
||||
public string Password { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "newPassword")]
|
||||
public string NewPassword { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "isAdmin")]
|
||||
public bool? IsAdmin { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "succeeded")]
|
||||
public bool? Succeeded { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "isLockedOut")]
|
||||
public bool? IsLockedOut { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using LanBackup.Models;
|
||||
using TinyMessenger;
|
||||
|
||||
namespace LanBackupAgent.Message
|
||||
{
|
||||
public class StatusReport : TinyMessageBase
|
||||
{
|
||||
public StatusReportInfo Info { get; set; }
|
||||
public StatusReport(string sender, StatusReportInfo msg) : base(sender)
|
||||
{
|
||||
this.Info = msg;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using TinyMessenger;
|
||||
|
||||
namespace LanBackupAgent.Message
|
||||
{
|
||||
public class StopMessage : TinyMessageBase
|
||||
{
|
||||
public StopMessage(string msg) : base(msg)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,108 @@
|
||||
using LanBackupAgent.Utils;
|
||||
using LanBackupAgent.WebApi;
|
||||
using LanBackupAgent.Controllers;
|
||||
using NLog;
|
||||
using SimpleInjector;
|
||||
using System;
|
||||
using System.Configuration;
|
||||
using TinyMessenger;
|
||||
using Topshelf;
|
||||
|
||||
namespace LanBackupAgent
|
||||
{
|
||||
class Program
|
||||
{
|
||||
|
||||
static ILogger logger;
|
||||
static TinyMessengerHub messenger;
|
||||
static Network network;
|
||||
|
||||
public static void Main()
|
||||
{
|
||||
|
||||
//conf logger
|
||||
logger = LogManager.GetCurrentClassLogger();
|
||||
messenger = new TinyMessengerHub();
|
||||
|
||||
AppDomain currentDomain = AppDomain.CurrentDomain;
|
||||
currentDomain.UnhandledException += CurrentDomain_UnhandledException;
|
||||
|
||||
|
||||
//setup DI container
|
||||
// Register individual components
|
||||
DI.Container.Register<ILogger>(() => logger, Lifestyle.Singleton);
|
||||
DI.Container.Register<ITinyMessengerHub>(() => messenger, Lifestyle.Singleton);
|
||||
network = new Network(logger);
|
||||
DI.Container.Register<Network>(() => network, Lifestyle.Singleton);
|
||||
DI.Container.Register<BackgroundRefreshController>(Lifestyle.Transient);
|
||||
string webApiUrl = ConfigurationManager.AppSettings["webApiUrl"];
|
||||
WebApiService api = new WebApiService(webApiUrl, logger, messenger);
|
||||
DI.Container.Register<WebApiService>(() => api, Lifestyle.Singleton);
|
||||
SignalRController signarl = new SignalRController(webApiUrl, logger, messenger, network);
|
||||
DI.Container.Register<SignalRController>(() => signarl, Lifestyle.Singleton);
|
||||
DI.Container.Register<LanCopyController>(Lifestyle.Transient);
|
||||
logger.Trace("DI initialized");
|
||||
|
||||
|
||||
|
||||
logger.Trace("Starting service");
|
||||
HostFactory.Run(x =>
|
||||
{
|
||||
//use NLog logger
|
||||
x.UseNLog();
|
||||
|
||||
x.Service<LanBackupAgentService>(s =>
|
||||
{
|
||||
s.ConstructUsing(name => new LanBackupAgentService(logger, messenger));
|
||||
s.WhenStarted(async tc => await tc.Start());
|
||||
s.WhenStopped(tc => tc.Stop());
|
||||
});
|
||||
x.RunAsNetworkService();
|
||||
x.StartAutomatically(); // Start the service automatically
|
||||
|
||||
x.SetDescription("LanBackup Agent Service");
|
||||
x.SetDisplayName("LanBackup Agent");
|
||||
//recommended that service names not contains spaces or other whitespace characters.
|
||||
x.SetServiceName("LanBackupAgent");
|
||||
|
||||
|
||||
x.EnableServiceRecovery(r =>
|
||||
{
|
||||
//you can have up to three of these
|
||||
//r.RestartComputer(5, "message");
|
||||
r.RestartService(1);//minutes for restart
|
||||
//the last one will act for all subsequent failures
|
||||
//r.RunProgram(7, "ping google.com");
|
||||
|
||||
//should this be true for crashed or non-zero exits
|
||||
r.OnCrashOnly();
|
||||
|
||||
//number of days until the error count resets
|
||||
r.SetResetPeriod(1);
|
||||
});
|
||||
|
||||
|
||||
//x.BeforeInstall(() => { ... });
|
||||
//x.AfterInstall(() => { ... });
|
||||
//x.BeforeUninstall(() => { ... });
|
||||
//x.AfterUninstall(() => { ... });
|
||||
|
||||
x.OnException(ex =>
|
||||
{
|
||||
// Do something with the exception
|
||||
logger.Error(ex, "LanBackup Agent service Error: ");
|
||||
});
|
||||
|
||||
});
|
||||
logger.Trace("Program DONE!");
|
||||
}
|
||||
|
||||
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
|
||||
{
|
||||
var logger = DI.Container.GetInstance<ILogger>();
|
||||
logger.Fatal($"Global exception: {e.ExceptionObject}");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("LanBackupAgent")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("LanBackupAgent")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2017")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("cb7ee867-93e8-43e3-9eee-9068aed1de6c")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
@@ -0,0 +1,10 @@
|
||||
using SimpleInjector;
|
||||
|
||||
namespace LanBackupAgent
|
||||
{
|
||||
public static class DI
|
||||
{
|
||||
//the DI container
|
||||
public static Container Container { get; set; } = new Container();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
|
||||
namespace LanBackupAgent
|
||||
{
|
||||
public static class Extensions
|
||||
{
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// extension function for substracting an array
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="data"></param>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="length"></param>
|
||||
/// <returns></returns>
|
||||
public static T[] SubArray<T>(this T[] data, int index, int length)
|
||||
{
|
||||
T[] result = new T[length];
|
||||
Array.Copy(data, index, result, 0, length);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
using Hangfire;
|
||||
using Hangfire.Storage;
|
||||
using Hangfire.Storage.Monitoring;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace LanBackupAgent.Utils
|
||||
{
|
||||
public static class HangfireExtensions
|
||||
{
|
||||
public static void PurgeJobs(this IMonitoringApi monitor)
|
||||
{
|
||||
var toDelete = new List<string>();
|
||||
|
||||
foreach (QueueWithTopEnqueuedJobsDto queue in monitor.Queues())
|
||||
{
|
||||
for (var i = 0; i < Math.Ceiling(queue.Length / 1000d); i++)
|
||||
{
|
||||
monitor.EnqueuedJobs(queue.Name, 1000 * i, 1000)
|
||||
.ForEach(x => toDelete.Add(x.Key));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var jobId in toDelete)
|
||||
{
|
||||
BackgroundJob.Delete(jobId);
|
||||
}
|
||||
}
|
||||
|
||||
public static void PurgeOrfanJobsExceptList(this IMonitoringApi monitor, List<string> exceptions)
|
||||
{
|
||||
var toDelete = new List<string>();
|
||||
|
||||
foreach (QueueWithTopEnqueuedJobsDto queue in monitor.Queues())
|
||||
{
|
||||
for (var i = 0; i < Math.Ceiling(queue.Length / 1000d); i++)
|
||||
{
|
||||
monitor.EnqueuedJobs(queue.Name, 1000 * i, 1000)
|
||||
.ForEach(x =>
|
||||
{
|
||||
if (!exceptions.Contains(x.Key))
|
||||
toDelete.Add(x.Key);
|
||||
else
|
||||
Console.WriteLine("x.Key " + x.Key + " remains.");
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var jobId in toDelete)
|
||||
{
|
||||
BackgroundJob.Delete(jobId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace LanBackupAgent.Utils
|
||||
{
|
||||
public class NativeMethods
|
||||
{
|
||||
|
||||
[DllImport("advapi32.DLL", SetLastError = true, CharSet=CharSet.Unicode)]
|
||||
private static extern int LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
using NLog;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace LanBackupAgent.Utils
|
||||
{
|
||||
public class Network
|
||||
{
|
||||
|
||||
private string localIP = string.Empty;
|
||||
ILogger logger;
|
||||
|
||||
public Network(ILogger mlogger) {
|
||||
this.logger = mlogger;
|
||||
logger.Trace("Network instance initialized");
|
||||
}
|
||||
|
||||
public string GetLocalIPAddress()
|
||||
{
|
||||
if (string.IsNullOrEmpty(localIP))
|
||||
{
|
||||
UnicastIPAddressInformation mostSuitableIp = null;
|
||||
|
||||
var networkInterfaces = NetworkInterface.GetAllNetworkInterfaces();
|
||||
|
||||
foreach (var network in networkInterfaces)
|
||||
{
|
||||
if (network.OperationalStatus != OperationalStatus.Up)
|
||||
continue;
|
||||
|
||||
var properties = network.GetIPProperties();
|
||||
|
||||
if (properties.GatewayAddresses.Count == 0)
|
||||
continue;
|
||||
|
||||
foreach (var address in properties.UnicastAddresses)
|
||||
{
|
||||
if (address.Address.AddressFamily != AddressFamily.InterNetwork)
|
||||
continue;
|
||||
|
||||
if (IPAddress.IsLoopback(address.Address))
|
||||
continue;
|
||||
|
||||
if (!address.IsDnsEligible)
|
||||
{
|
||||
if (mostSuitableIp == null)
|
||||
mostSuitableIp = address;
|
||||
continue;
|
||||
}
|
||||
|
||||
// The best IP is the IP got from DHCP server
|
||||
if (address.PrefixOrigin != PrefixOrigin.Dhcp)
|
||||
{
|
||||
if (mostSuitableIp == null || !mostSuitableIp.IsDnsEligible)
|
||||
mostSuitableIp = address;
|
||||
continue;
|
||||
}
|
||||
|
||||
localIP = address.Address.ToString();
|
||||
}
|
||||
}
|
||||
if (string.IsNullOrEmpty(localIP))
|
||||
{
|
||||
localIP = mostSuitableIp != null
|
||||
? mostSuitableIp.Address.ToString()
|
||||
: string.Empty;
|
||||
}
|
||||
}
|
||||
logger.Trace($"Network instance IP: {localIP}");
|
||||
return localIP;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,793 @@
|
||||
//===============================================================================
|
||||
// TinyIoC - TinyMessenger
|
||||
//
|
||||
// A simple messenger/event aggregator.
|
||||
//
|
||||
// http://hg.grumpydev.com/tinyioc
|
||||
//===============================================================================
|
||||
// Copyright © Steven Robbins. All rights reserved.
|
||||
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
|
||||
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
|
||||
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//===============================================================================
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace TinyMessenger
|
||||
{
|
||||
#region Message Types / Interfaces
|
||||
/// <summary>
|
||||
/// A TinyMessage to be published/delivered by TinyMessenger
|
||||
/// </summary>
|
||||
public interface ITinyMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// The sender of the message, or null if not supported by the message implementation.
|
||||
/// </summary>
|
||||
object Sender { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base class for messages that provides weak refrence storage of the sender
|
||||
/// </summary>
|
||||
public abstract class TinyMessageBase : ITinyMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// Store a WeakReference to the sender just in case anyone is daft enough to
|
||||
/// keep the message around and prevent the sender from being collected.
|
||||
/// </summary>
|
||||
private WeakReference _Sender;
|
||||
public object Sender
|
||||
{
|
||||
get
|
||||
{
|
||||
return (_Sender == null) ? null : _Sender.Target;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the MessageBase class.
|
||||
/// </summary>
|
||||
/// <param name="sender">Message sender (usually "this")</param>
|
||||
public TinyMessageBase(object sender)
|
||||
{
|
||||
if (sender == null)
|
||||
throw new ArgumentNullException("sender");
|
||||
|
||||
_Sender = new WeakReference(sender);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generic message with user specified content
|
||||
/// </summary>
|
||||
/// <typeparam name="TContent">Content type to store</typeparam>
|
||||
public class GenericTinyMessage<TContent> : TinyMessageBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Contents of the message
|
||||
/// </summary>
|
||||
public TContent Content { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new instance of the GenericTinyMessage class.
|
||||
/// </summary>
|
||||
/// <param name="sender">Message sender (usually "this")</param>
|
||||
/// <param name="content">Contents of the message</param>
|
||||
public GenericTinyMessage(object sender, TContent content)
|
||||
: base(sender)
|
||||
{
|
||||
Content = content;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Basic "cancellable" generic message
|
||||
/// </summary>
|
||||
/// <typeparam name="TContent">Content type to store</typeparam>
|
||||
public class CancellableGenericTinyMessage<TContent> : TinyMessageBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Cancel action
|
||||
/// </summary>
|
||||
public Action Cancel { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Contents of the message
|
||||
/// </summary>
|
||||
public TContent Content { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new instance of the CancellableGenericTinyMessage class.
|
||||
/// </summary>
|
||||
/// <param name="sender">Message sender (usually "this")</param>
|
||||
/// <param name="content">Contents of the message</param>
|
||||
/// <param name="cancelAction">Action to call for cancellation</param>
|
||||
public CancellableGenericTinyMessage(object sender, TContent content, Action cancelAction)
|
||||
: base(sender)
|
||||
{
|
||||
if (cancelAction == null)
|
||||
throw new ArgumentNullException("cancelAction");
|
||||
|
||||
Content = content;
|
||||
Cancel = cancelAction;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an active subscription to a message
|
||||
/// </summary>
|
||||
public sealed class TinyMessageSubscriptionToken : IDisposable
|
||||
{
|
||||
private WeakReference _Hub;
|
||||
private Type _MessageType;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the TinyMessageSubscriptionToken class.
|
||||
/// </summary>
|
||||
public TinyMessageSubscriptionToken(ITinyMessengerHub hub, Type messageType)
|
||||
{
|
||||
if (hub == null)
|
||||
throw new ArgumentNullException("hub");
|
||||
|
||||
if (!typeof(ITinyMessage).IsAssignableFrom(messageType))
|
||||
throw new ArgumentOutOfRangeException("messageType");
|
||||
|
||||
_Hub = new WeakReference(hub);
|
||||
_MessageType = messageType;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_Hub.IsAlive)
|
||||
{
|
||||
var hub = _Hub.Target as ITinyMessengerHub;
|
||||
|
||||
if (hub != null)
|
||||
{
|
||||
var unsubscribeMethod = typeof(ITinyMessengerHub).GetMethod("Unsubscribe", new Type[] {typeof(TinyMessageSubscriptionToken)});
|
||||
unsubscribeMethod = unsubscribeMethod.MakeGenericMethod(_MessageType);
|
||||
unsubscribeMethod.Invoke(hub, new object[] {this});
|
||||
}
|
||||
}
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a message subscription
|
||||
/// </summary>
|
||||
public interface ITinyMessageSubscription
|
||||
{
|
||||
/// <summary>
|
||||
/// Token returned to the subscribed to reference this subscription
|
||||
/// </summary>
|
||||
TinyMessageSubscriptionToken SubscriptionToken { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether delivery should be attempted.
|
||||
/// </summary>
|
||||
/// <param name="message">Message that may potentially be delivered.</param>
|
||||
/// <returns>True - ok to send, False - should not attempt to send</returns>
|
||||
bool ShouldAttemptDelivery(ITinyMessage message);
|
||||
|
||||
/// <summary>
|
||||
/// Deliver the message
|
||||
/// </summary>
|
||||
/// <param name="message">Message to deliver</param>
|
||||
void Deliver(ITinyMessage message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Message proxy definition.
|
||||
///
|
||||
/// A message proxy can be used to intercept/alter messages and/or
|
||||
/// marshall delivery actions onto a particular thread.
|
||||
/// </summary>
|
||||
public interface ITinyMessageProxy
|
||||
{
|
||||
void Deliver(ITinyMessage message, ITinyMessageSubscription subscription);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default "pass through" proxy.
|
||||
///
|
||||
/// Does nothing other than deliver the message.
|
||||
/// </summary>
|
||||
public sealed class DefaultTinyMessageProxy : ITinyMessageProxy
|
||||
{
|
||||
private static readonly DefaultTinyMessageProxy _Instance = new DefaultTinyMessageProxy();
|
||||
|
||||
static DefaultTinyMessageProxy()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Singleton instance of the proxy.
|
||||
/// </summary>
|
||||
public static DefaultTinyMessageProxy Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
return _Instance;
|
||||
}
|
||||
}
|
||||
|
||||
private DefaultTinyMessageProxy()
|
||||
{
|
||||
}
|
||||
|
||||
public void Deliver(ITinyMessage message, ITinyMessageSubscription subscription)
|
||||
{
|
||||
subscription.Deliver(message);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Exceptions
|
||||
/// <summary>
|
||||
/// Thrown when an exceptions occurs while subscribing to a message type
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class TinyMessengerSubscriptionException : Exception
|
||||
{
|
||||
private const string ERROR_TEXT = "Unable to add subscription for {0} : {1}";
|
||||
|
||||
public TinyMessengerSubscriptionException(Type messageType, string reason)
|
||||
: base(String.Format(ERROR_TEXT, messageType, reason))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public TinyMessengerSubscriptionException(Type messageType, string reason, Exception innerException)
|
||||
: base(String.Format(ERROR_TEXT, messageType, reason), innerException)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Hub Interface
|
||||
/// <summary>
|
||||
/// Messenger hub responsible for taking subscriptions/publications and delivering of messages.
|
||||
/// </summary>
|
||||
public interface ITinyMessengerHub
|
||||
{
|
||||
/// <summary>
|
||||
/// Subscribe to a message type with the given destination and delivery action.
|
||||
/// All references are held with WeakReferences
|
||||
///
|
||||
/// All messages of this type will be delivered.
|
||||
/// </summary>
|
||||
/// <typeparam name="TMessage">Type of message</typeparam>
|
||||
/// <param name="deliveryAction">Action to invoke when message is delivered</param>
|
||||
/// <returns>TinyMessageSubscription used to unsubscribing</returns>
|
||||
TinyMessageSubscriptionToken Subscribe<TMessage>(Action<TMessage> deliveryAction) where TMessage : class, ITinyMessage;
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe to a message type with the given destination and delivery action.
|
||||
/// Messages will be delivered via the specified proxy.
|
||||
/// All references (apart from the proxy) are held with WeakReferences
|
||||
///
|
||||
/// All messages of this type will be delivered.
|
||||
/// </summary>
|
||||
/// <typeparam name="TMessage">Type of message</typeparam>
|
||||
/// <param name="deliveryAction">Action to invoke when message is delivered</param>
|
||||
/// <param name="proxy">Proxy to use when delivering the messages</param>
|
||||
/// <returns>TinyMessageSubscription used to unsubscribing</returns>
|
||||
TinyMessageSubscriptionToken Subscribe<TMessage>(Action<TMessage> deliveryAction, ITinyMessageProxy proxy) where TMessage : class, ITinyMessage;
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe to a message type with the given destination and delivery action.
|
||||
///
|
||||
/// All messages of this type will be delivered.
|
||||
/// </summary>
|
||||
/// <typeparam name="TMessage">Type of message</typeparam>
|
||||
/// <param name="deliveryAction">Action to invoke when message is delivered</param>
|
||||
/// <param name="useStrongReferences">Use strong references to destination and deliveryAction </param>
|
||||
/// <returns>TinyMessageSubscription used to unsubscribing</returns>
|
||||
TinyMessageSubscriptionToken Subscribe<TMessage>(Action<TMessage> deliveryAction, bool useStrongReferences) where TMessage : class, ITinyMessage;
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe to a message type with the given destination and delivery action.
|
||||
/// Messages will be delivered via the specified proxy.
|
||||
///
|
||||
/// All messages of this type will be delivered.
|
||||
/// </summary>
|
||||
/// <typeparam name="TMessage">Type of message</typeparam>
|
||||
/// <param name="deliveryAction">Action to invoke when message is delivered</param>
|
||||
/// <param name="useStrongReferences">Use strong references to destination and deliveryAction </param>
|
||||
/// <param name="proxy">Proxy to use when delivering the messages</param>
|
||||
/// <returns>TinyMessageSubscription used to unsubscribing</returns>
|
||||
TinyMessageSubscriptionToken Subscribe<TMessage>(Action<TMessage> deliveryAction, bool useStrongReferences, ITinyMessageProxy proxy) where TMessage : class, ITinyMessage;
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe to a message type with the given destination and delivery action with the given filter.
|
||||
/// All references are held with WeakReferences
|
||||
///
|
||||
/// Only messages that "pass" the filter will be delivered.
|
||||
/// </summary>
|
||||
/// <typeparam name="TMessage">Type of message</typeparam>
|
||||
/// <param name="deliveryAction">Action to invoke when message is delivered</param>
|
||||
/// <returns>TinyMessageSubscription used to unsubscribing</returns>
|
||||
TinyMessageSubscriptionToken Subscribe<TMessage>(Action<TMessage> deliveryAction, Func<TMessage, bool> messageFilter) where TMessage : class, ITinyMessage;
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe to a message type with the given destination and delivery action with the given filter.
|
||||
/// Messages will be delivered via the specified proxy.
|
||||
/// All references (apart from the proxy) are held with WeakReferences
|
||||
///
|
||||
/// Only messages that "pass" the filter will be delivered.
|
||||
/// </summary>
|
||||
/// <typeparam name="TMessage">Type of message</typeparam>
|
||||
/// <param name="deliveryAction">Action to invoke when message is delivered</param>
|
||||
/// <param name="proxy">Proxy to use when delivering the messages</param>
|
||||
/// <returns>TinyMessageSubscription used to unsubscribing</returns>
|
||||
TinyMessageSubscriptionToken Subscribe<TMessage>(Action<TMessage> deliveryAction, Func<TMessage, bool> messageFilter, ITinyMessageProxy proxy) where TMessage : class, ITinyMessage;
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe to a message type with the given destination and delivery action with the given filter.
|
||||
/// All references are held with WeakReferences
|
||||
///
|
||||
/// Only messages that "pass" the filter will be delivered.
|
||||
/// </summary>
|
||||
/// <typeparam name="TMessage">Type of message</typeparam>
|
||||
/// <param name="deliveryAction">Action to invoke when message is delivered</param>
|
||||
/// <param name="useStrongReferences">Use strong references to destination and deliveryAction </param>
|
||||
/// <returns>TinyMessageSubscription used to unsubscribing</returns>
|
||||
TinyMessageSubscriptionToken Subscribe<TMessage>(Action<TMessage> deliveryAction, Func<TMessage, bool> messageFilter, bool useStrongReferences) where TMessage : class, ITinyMessage;
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe to a message type with the given destination and delivery action with the given filter.
|
||||
/// Messages will be delivered via the specified proxy.
|
||||
/// All references are held with WeakReferences
|
||||
///
|
||||
/// Only messages that "pass" the filter will be delivered.
|
||||
/// </summary>
|
||||
/// <typeparam name="TMessage">Type of message</typeparam>
|
||||
/// <param name="deliveryAction">Action to invoke when message is delivered</param>
|
||||
/// <param name="useStrongReferences">Use strong references to destination and deliveryAction </param>
|
||||
/// <param name="proxy">Proxy to use when delivering the messages</param>
|
||||
/// <returns>TinyMessageSubscription used to unsubscribing</returns>
|
||||
TinyMessageSubscriptionToken Subscribe<TMessage>(Action<TMessage> deliveryAction, Func<TMessage, bool> messageFilter, bool useStrongReferences, ITinyMessageProxy proxy) where TMessage : class, ITinyMessage;
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribe from a particular message type.
|
||||
///
|
||||
/// Does not throw an exception if the subscription is not found.
|
||||
/// </summary>
|
||||
/// <typeparam name="TMessage">Type of message</typeparam>
|
||||
/// <param name="subscriptionToken">Subscription token received from Subscribe</param>
|
||||
void Unsubscribe<TMessage>(TinyMessageSubscriptionToken subscriptionToken) where TMessage : class, ITinyMessage;
|
||||
|
||||
/// <summary>
|
||||
/// Publish a message to any subscribers
|
||||
/// </summary>
|
||||
/// <typeparam name="TMessage">Type of message</typeparam>
|
||||
/// <param name="message">Message to deliver</param>
|
||||
void Publish<TMessage>(TMessage message) where TMessage : class, ITinyMessage;
|
||||
|
||||
/// <summary>
|
||||
/// Publish a message to any subscribers asynchronously
|
||||
/// </summary>
|
||||
/// <typeparam name="TMessage">Type of message</typeparam>
|
||||
/// <param name="message">Message to deliver</param>
|
||||
void PublishAsync<TMessage>(TMessage message) where TMessage : class, ITinyMessage;
|
||||
|
||||
/// <summary>
|
||||
/// Publish a message to any subscribers asynchronously
|
||||
/// </summary>
|
||||
/// <typeparam name="TMessage">Type of message</typeparam>
|
||||
/// <param name="message">Message to deliver</param>
|
||||
/// <param name="callback">AsyncCallback called on completion</param>
|
||||
void PublishAsync<TMessage>(TMessage message, AsyncCallback callback) where TMessage : class, ITinyMessage;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Hub Implementation
|
||||
/// <summary>
|
||||
/// Messenger hub responsible for taking subscriptions/publications and delivering of messages.
|
||||
/// </summary>
|
||||
public sealed class TinyMessengerHub : ITinyMessengerHub
|
||||
{
|
||||
#region Private Types and Interfaces
|
||||
private class WeakTinyMessageSubscription<TMessage> : ITinyMessageSubscription
|
||||
where TMessage : class, ITinyMessage
|
||||
{
|
||||
protected TinyMessageSubscriptionToken _SubscriptionToken;
|
||||
protected WeakReference _DeliveryAction;
|
||||
protected WeakReference _MessageFilter;
|
||||
|
||||
public TinyMessageSubscriptionToken SubscriptionToken
|
||||
{
|
||||
get { return _SubscriptionToken; }
|
||||
}
|
||||
|
||||
public bool ShouldAttemptDelivery(ITinyMessage message)
|
||||
{
|
||||
if (!(message is TMessage))
|
||||
return false;
|
||||
|
||||
if (!_DeliveryAction.IsAlive)
|
||||
return false;
|
||||
|
||||
if (!_MessageFilter.IsAlive)
|
||||
return false;
|
||||
|
||||
return ((Func<TMessage, bool>)_MessageFilter.Target).Invoke(message as TMessage);
|
||||
}
|
||||
|
||||
public void Deliver(ITinyMessage message)
|
||||
{
|
||||
if (!(message is TMessage))
|
||||
throw new ArgumentException("Message is not the correct type");
|
||||
|
||||
if (!_DeliveryAction.IsAlive)
|
||||
return;
|
||||
|
||||
((Action<TMessage>)_DeliveryAction.Target).Invoke(message as TMessage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the WeakTinyMessageSubscription class.
|
||||
/// </summary>
|
||||
/// <param name="destination">Destination object</param>
|
||||
/// <param name="deliveryAction">Delivery action</param>
|
||||
/// <param name="messageFilter">Filter function</param>
|
||||
public WeakTinyMessageSubscription(TinyMessageSubscriptionToken subscriptionToken, Action<TMessage> deliveryAction, Func<TMessage, bool> messageFilter)
|
||||
{
|
||||
if (subscriptionToken == null)
|
||||
throw new ArgumentNullException("subscriptionToken");
|
||||
|
||||
if (deliveryAction == null)
|
||||
throw new ArgumentNullException("deliveryAction");
|
||||
|
||||
if (messageFilter == null)
|
||||
throw new ArgumentNullException("messageFilter");
|
||||
|
||||
_SubscriptionToken = subscriptionToken;
|
||||
_DeliveryAction = new WeakReference(deliveryAction);
|
||||
_MessageFilter = new WeakReference(messageFilter);
|
||||
}
|
||||
}
|
||||
|
||||
private class StrongTinyMessageSubscription<TMessage> : ITinyMessageSubscription
|
||||
where TMessage : class, ITinyMessage
|
||||
{
|
||||
protected TinyMessageSubscriptionToken _SubscriptionToken;
|
||||
protected Action<TMessage> _DeliveryAction;
|
||||
protected Func<TMessage, bool> _MessageFilter;
|
||||
|
||||
public TinyMessageSubscriptionToken SubscriptionToken
|
||||
{
|
||||
get { return _SubscriptionToken; }
|
||||
}
|
||||
|
||||
public bool ShouldAttemptDelivery(ITinyMessage message)
|
||||
{
|
||||
if (!(message is TMessage))
|
||||
return false;
|
||||
|
||||
return _MessageFilter.Invoke(message as TMessage);
|
||||
}
|
||||
|
||||
public void Deliver(ITinyMessage message)
|
||||
{
|
||||
if (!(message is TMessage))
|
||||
throw new ArgumentException("Message is not the correct type");
|
||||
|
||||
_DeliveryAction.Invoke(message as TMessage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the TinyMessageSubscription class.
|
||||
/// </summary>
|
||||
/// <param name="destination">Destination object</param>
|
||||
/// <param name="deliveryAction">Delivery action</param>
|
||||
/// <param name="messageFilter">Filter function</param>
|
||||
public StrongTinyMessageSubscription(TinyMessageSubscriptionToken subscriptionToken, Action<TMessage> deliveryAction, Func<TMessage, bool> messageFilter)
|
||||
{
|
||||
if (subscriptionToken == null)
|
||||
throw new ArgumentNullException("subscriptionToken");
|
||||
|
||||
if (deliveryAction == null)
|
||||
throw new ArgumentNullException("deliveryAction");
|
||||
|
||||
if (messageFilter == null)
|
||||
throw new ArgumentNullException("messageFilter");
|
||||
|
||||
_SubscriptionToken = subscriptionToken;
|
||||
_DeliveryAction = deliveryAction;
|
||||
_MessageFilter = messageFilter;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Subscription dictionary
|
||||
private class SubscriptionItem
|
||||
{
|
||||
public ITinyMessageProxy Proxy { get; private set; }
|
||||
public ITinyMessageSubscription Subscription { get; private set; }
|
||||
|
||||
public SubscriptionItem(ITinyMessageProxy proxy, ITinyMessageSubscription subscription)
|
||||
{
|
||||
Proxy = proxy;
|
||||
Subscription = subscription;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly object _SubscriptionsPadlock = new object();
|
||||
private readonly Dictionary<Type, List<SubscriptionItem>> _Subscriptions = new Dictionary<Type, List<SubscriptionItem>>();
|
||||
#endregion
|
||||
|
||||
#region Public API
|
||||
/// <summary>
|
||||
/// Subscribe to a message type with the given destination and delivery action.
|
||||
/// All references are held with strong references
|
||||
///
|
||||
/// All messages of this type will be delivered.
|
||||
/// </summary>
|
||||
/// <typeparam name="TMessage">Type of message</typeparam>
|
||||
/// <param name="deliveryAction">Action to invoke when message is delivered</param>
|
||||
/// <returns>TinyMessageSubscription used to unsubscribing</returns>
|
||||
public TinyMessageSubscriptionToken Subscribe<TMessage>(Action<TMessage> deliveryAction) where TMessage : class, ITinyMessage
|
||||
{
|
||||
return AddSubscriptionInternal<TMessage>(deliveryAction, (m) => true, true, DefaultTinyMessageProxy.Instance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe to a message type with the given destination and delivery action.
|
||||
/// Messages will be delivered via the specified proxy.
|
||||
/// All references (apart from the proxy) are held with strong references
|
||||
///
|
||||
/// All messages of this type will be delivered.
|
||||
/// </summary>
|
||||
/// <typeparam name="TMessage">Type of message</typeparam>
|
||||
/// <param name="deliveryAction">Action to invoke when message is delivered</param>
|
||||
/// <param name="proxy">Proxy to use when delivering the messages</param>
|
||||
/// <returns>TinyMessageSubscription used to unsubscribing</returns>
|
||||
public TinyMessageSubscriptionToken Subscribe<TMessage>(Action<TMessage> deliveryAction, ITinyMessageProxy proxy) where TMessage : class, ITinyMessage
|
||||
{
|
||||
return AddSubscriptionInternal<TMessage>(deliveryAction, (m) => true, true, proxy);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe to a message type with the given destination and delivery action.
|
||||
///
|
||||
/// All messages of this type will be delivered.
|
||||
/// </summary>
|
||||
/// <typeparam name="TMessage">Type of message</typeparam>
|
||||
/// <param name="deliveryAction">Action to invoke when message is delivered</param>
|
||||
/// <param name="useStrongReferences">Use strong references to destination and deliveryAction </param>
|
||||
/// <returns>TinyMessageSubscription used to unsubscribing</returns>
|
||||
public TinyMessageSubscriptionToken Subscribe<TMessage>(Action<TMessage> deliveryAction, bool useStrongReferences) where TMessage : class, ITinyMessage
|
||||
{
|
||||
return AddSubscriptionInternal<TMessage>(deliveryAction, (m) => true, useStrongReferences, DefaultTinyMessageProxy.Instance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe to a message type with the given destination and delivery action.
|
||||
/// Messages will be delivered via the specified proxy.
|
||||
///
|
||||
/// All messages of this type will be delivered.
|
||||
/// </summary>
|
||||
/// <typeparam name="TMessage">Type of message</typeparam>
|
||||
/// <param name="deliveryAction">Action to invoke when message is delivered</param>
|
||||
/// <param name="useStrongReferences">Use strong references to destination and deliveryAction </param>
|
||||
/// <param name="proxy">Proxy to use when delivering the messages</param>
|
||||
/// <returns>TinyMessageSubscription used to unsubscribing</returns>
|
||||
public TinyMessageSubscriptionToken Subscribe<TMessage>(Action<TMessage> deliveryAction, bool useStrongReferences, ITinyMessageProxy proxy) where TMessage : class, ITinyMessage
|
||||
{
|
||||
return AddSubscriptionInternal<TMessage>(deliveryAction, (m) => true, useStrongReferences, proxy);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe to a message type with the given destination and delivery action with the given filter.
|
||||
/// All references are held with WeakReferences
|
||||
///
|
||||
/// Only messages that "pass" the filter will be delivered.
|
||||
/// </summary>
|
||||
/// <typeparam name="TMessage">Type of message</typeparam>
|
||||
/// <param name="deliveryAction">Action to invoke when message is delivered</param>
|
||||
/// <returns>TinyMessageSubscription used to unsubscribing</returns>
|
||||
public TinyMessageSubscriptionToken Subscribe<TMessage>(Action<TMessage> deliveryAction, Func<TMessage, bool> messageFilter) where TMessage : class, ITinyMessage
|
||||
{
|
||||
return AddSubscriptionInternal<TMessage>(deliveryAction, messageFilter, true, DefaultTinyMessageProxy.Instance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe to a message type with the given destination and delivery action with the given filter.
|
||||
/// Messages will be delivered via the specified proxy.
|
||||
/// All references (apart from the proxy) are held with WeakReferences
|
||||
///
|
||||
/// Only messages that "pass" the filter will be delivered.
|
||||
/// </summary>
|
||||
/// <typeparam name="TMessage">Type of message</typeparam>
|
||||
/// <param name="deliveryAction">Action to invoke when message is delivered</param>
|
||||
/// <param name="proxy">Proxy to use when delivering the messages</param>
|
||||
/// <returns>TinyMessageSubscription used to unsubscribing</returns>
|
||||
public TinyMessageSubscriptionToken Subscribe<TMessage>(Action<TMessage> deliveryAction, Func<TMessage, bool> messageFilter, ITinyMessageProxy proxy) where TMessage : class, ITinyMessage
|
||||
{
|
||||
return AddSubscriptionInternal<TMessage>(deliveryAction, messageFilter, true, proxy);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe to a message type with the given destination and delivery action with the given filter.
|
||||
/// All references are held with WeakReferences
|
||||
///
|
||||
/// Only messages that "pass" the filter will be delivered.
|
||||
/// </summary>
|
||||
/// <typeparam name="TMessage">Type of message</typeparam>
|
||||
/// <param name="deliveryAction">Action to invoke when message is delivered</param>
|
||||
/// <param name="useStrongReferences">Use strong references to destination and deliveryAction </param>
|
||||
/// <returns>TinyMessageSubscription used to unsubscribing</returns>
|
||||
public TinyMessageSubscriptionToken Subscribe<TMessage>(Action<TMessage> deliveryAction, Func<TMessage, bool> messageFilter, bool useStrongReferences) where TMessage : class, ITinyMessage
|
||||
{
|
||||
return AddSubscriptionInternal<TMessage>(deliveryAction, messageFilter, useStrongReferences, DefaultTinyMessageProxy.Instance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe to a message type with the given destination and delivery action with the given filter.
|
||||
/// Messages will be delivered via the specified proxy.
|
||||
/// All references are held with WeakReferences
|
||||
///
|
||||
/// Only messages that "pass" the filter will be delivered.
|
||||
/// </summary>
|
||||
/// <typeparam name="TMessage">Type of message</typeparam>
|
||||
/// <param name="deliveryAction">Action to invoke when message is delivered</param>
|
||||
/// <param name="useStrongReferences">Use strong references to destination and deliveryAction </param>
|
||||
/// <param name="proxy">Proxy to use when delivering the messages</param>
|
||||
/// <returns>TinyMessageSubscription used to unsubscribing</returns>
|
||||
public TinyMessageSubscriptionToken Subscribe<TMessage>(Action<TMessage> deliveryAction, Func<TMessage, bool> messageFilter, bool useStrongReferences, ITinyMessageProxy proxy) where TMessage : class, ITinyMessage
|
||||
{
|
||||
return AddSubscriptionInternal<TMessage>(deliveryAction, messageFilter, useStrongReferences, proxy);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribe from a particular message type.
|
||||
///
|
||||
/// Does not throw an exception if the subscription is not found.
|
||||
/// </summary>
|
||||
/// <typeparam name="TMessage">Type of message</typeparam>
|
||||
/// <param name="subscriptionToken">Subscription token received from Subscribe</param>
|
||||
public void Unsubscribe<TMessage>(TinyMessageSubscriptionToken subscriptionToken) where TMessage : class, ITinyMessage
|
||||
{
|
||||
RemoveSubscriptionInternal<TMessage>(subscriptionToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Publish a message to any subscribers
|
||||
/// </summary>
|
||||
/// <typeparam name="TMessage">Type of message</typeparam>
|
||||
/// <param name="message">Message to deliver</param>
|
||||
public void Publish<TMessage>(TMessage message) where TMessage : class, ITinyMessage
|
||||
{
|
||||
PublishInternal<TMessage>(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Publish a message to any subscribers asynchronously
|
||||
/// </summary>
|
||||
/// <typeparam name="TMessage">Type of message</typeparam>
|
||||
/// <param name="message">Message to deliver</param>
|
||||
public void PublishAsync<TMessage>(TMessage message) where TMessage : class, ITinyMessage
|
||||
{
|
||||
PublishAsyncInternal<TMessage>(message, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Publish a message to any subscribers asynchronously
|
||||
/// </summary>
|
||||
/// <typeparam name="TMessage">Type of message</typeparam>
|
||||
/// <param name="message">Message to deliver</param>
|
||||
/// <param name="callback">AsyncCallback called on completion</param>
|
||||
public void PublishAsync<TMessage>(TMessage message, AsyncCallback callback) where TMessage : class, ITinyMessage
|
||||
{
|
||||
PublishAsyncInternal<TMessage>(message, callback);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Internal Methods
|
||||
private TinyMessageSubscriptionToken AddSubscriptionInternal<TMessage>(Action<TMessage> deliveryAction, Func<TMessage, bool> messageFilter, bool strongReference, ITinyMessageProxy proxy)
|
||||
where TMessage : class, ITinyMessage
|
||||
{
|
||||
if (deliveryAction == null)
|
||||
throw new ArgumentNullException("deliveryAction");
|
||||
|
||||
if (messageFilter == null)
|
||||
throw new ArgumentNullException("messageFilter");
|
||||
|
||||
if (proxy == null)
|
||||
throw new ArgumentNullException("proxy");
|
||||
|
||||
lock (_SubscriptionsPadlock)
|
||||
{
|
||||
List<SubscriptionItem> currentSubscriptions;
|
||||
|
||||
if (!_Subscriptions.TryGetValue(typeof(TMessage), out currentSubscriptions))
|
||||
{
|
||||
currentSubscriptions = new List<SubscriptionItem>();
|
||||
_Subscriptions[typeof(TMessage)] = currentSubscriptions;
|
||||
}
|
||||
|
||||
var subscriptionToken = new TinyMessageSubscriptionToken(this, typeof(TMessage));
|
||||
|
||||
ITinyMessageSubscription subscription;
|
||||
if (strongReference)
|
||||
subscription = new StrongTinyMessageSubscription<TMessage>(subscriptionToken, deliveryAction, messageFilter);
|
||||
else
|
||||
subscription = new WeakTinyMessageSubscription<TMessage>(subscriptionToken, deliveryAction, messageFilter);
|
||||
|
||||
currentSubscriptions.Add(new SubscriptionItem(proxy, subscription));
|
||||
|
||||
return subscriptionToken;
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveSubscriptionInternal<TMessage>(TinyMessageSubscriptionToken subscriptionToken)
|
||||
where TMessage : class, ITinyMessage
|
||||
{
|
||||
if (subscriptionToken == null)
|
||||
throw new ArgumentNullException("subscriptionToken");
|
||||
|
||||
lock (_SubscriptionsPadlock)
|
||||
{
|
||||
List<SubscriptionItem> currentSubscriptions;
|
||||
if (!_Subscriptions.TryGetValue(typeof(TMessage), out currentSubscriptions))
|
||||
return;
|
||||
|
||||
var currentlySubscribed = (from sub in currentSubscriptions
|
||||
where object.ReferenceEquals(sub.Subscription.SubscriptionToken, subscriptionToken)
|
||||
select sub).ToList();
|
||||
|
||||
currentlySubscribed.ForEach(sub => currentSubscriptions.Remove(sub));
|
||||
}
|
||||
}
|
||||
|
||||
private void PublishInternal<TMessage>(TMessage message)
|
||||
where TMessage : class, ITinyMessage
|
||||
{
|
||||
if (message == null)
|
||||
throw new ArgumentNullException("message");
|
||||
|
||||
List<SubscriptionItem> currentlySubscribed;
|
||||
lock (_SubscriptionsPadlock)
|
||||
{
|
||||
List<SubscriptionItem> currentSubscriptions;
|
||||
if (!_Subscriptions.TryGetValue(typeof(TMessage), out currentSubscriptions))
|
||||
return;
|
||||
|
||||
currentlySubscribed = (from sub in currentSubscriptions
|
||||
where sub.Subscription.ShouldAttemptDelivery(message)
|
||||
select sub).ToList();
|
||||
}
|
||||
|
||||
currentlySubscribed.ForEach(sub =>
|
||||
{
|
||||
try
|
||||
{
|
||||
sub.Proxy.Deliver(message, sub.Subscription);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Ignore any errors and carry on
|
||||
// TODO - add to a list of erroring subs and remove them?
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void PublishAsyncInternal<TMessage>(TMessage message, AsyncCallback callback) where TMessage : class, ITinyMessage
|
||||
{
|
||||
Action publishAction = () => { PublishInternal<TMessage>(message); };
|
||||
|
||||
publishAction.BeginInvoke(callback, null);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
using LanBackupAgent.Message;
|
||||
using LanBackupAgent.Models;
|
||||
using LanBackupAgent.Utils;
|
||||
using Microsoft.Rest;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using TinyMessenger;
|
||||
|
||||
namespace LanBackupAgent.WebApi
|
||||
{
|
||||
public class WebApiService
|
||||
{
|
||||
private ILogger logger;
|
||||
private ITinyMessengerHub messenger;
|
||||
private string webapiUrlBase;
|
||||
|
||||
public WebApiService(string mwebapiUrlBase,
|
||||
ILogger mlogger,
|
||||
ITinyMessengerHub mmessenger
|
||||
)
|
||||
{
|
||||
this.webapiUrlBase = mwebapiUrlBase;
|
||||
this.logger = mlogger;
|
||||
this.messenger = mmessenger;
|
||||
|
||||
this.messenger.Subscribe<StopMessage>(Stop);
|
||||
logger.Trace("WebApiService instance initialized");
|
||||
}
|
||||
|
||||
private void Stop(StopMessage obj)
|
||||
{
|
||||
//TODO - implement cancelation
|
||||
}
|
||||
|
||||
public async Task<IList<BackupConfiguration>> LoadBackupConfigurations()
|
||||
{
|
||||
IList<BackupConfiguration> result = new List<BackupConfiguration>();
|
||||
try
|
||||
{
|
||||
LanBackupsAPI api = getApi();
|
||||
//retrieve own backup configurations
|
||||
string localIp = DI.Container.GetInstance<Network>().GetLocalIPAddress();
|
||||
result = await api.ApiBackupConfigClientByClientidGetAsync(localIp);
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
logger.Info($"loadBackupConfigurations WebApi, response: {result.Count} configurations.");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Info($"loadBackupConfigurations WebApi, response: NO configurations.");
|
||||
}
|
||||
}
|
||||
catch (HttpRequestException hex)
|
||||
{
|
||||
if (hex.InnerException is WebException)
|
||||
{
|
||||
WebException wex = hex.InnerException as WebException;
|
||||
if (wex.Status == WebExceptionStatus.ConnectFailure)
|
||||
{
|
||||
//ignore this err
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Error($"ERR WebException HResult: {wex.HResult}");
|
||||
logger.Error($"ERR WebException Status: {wex.Status}");
|
||||
logger.Error($"ERR WebException Message: {wex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error($"loadBackupConfigurations ERROR: {ex}");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public async void LogActivity(string backupId, string message, string error, string status)
|
||||
{
|
||||
try
|
||||
{
|
||||
LanBackupsAPI api = getApi();
|
||||
// submit activity status
|
||||
var log = new BackupLog()
|
||||
{
|
||||
ClientIP = DI.Container.GetInstance<Network>().GetLocalIPAddress(),
|
||||
Status = status,
|
||||
Description = message,
|
||||
LogError = error,
|
||||
DateTime = DateTime.UtcNow
|
||||
};
|
||||
var result = await api.ApiLogsPostAsync(log);
|
||||
|
||||
logger.Info($"Log sent to WebApi, response ID: {result}");
|
||||
}
|
||||
catch (HttpRequestException hex)
|
||||
{
|
||||
if (hex.InnerException is WebException)
|
||||
{
|
||||
WebException wex = hex.InnerException as WebException;
|
||||
if (wex.Status == WebExceptionStatus.ConnectFailure)
|
||||
{
|
||||
//ignore this err
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Error($"ERR WebException HResult: {wex.HResult}");
|
||||
logger.Error($"ERR WebException Status: {wex.Status}");
|
||||
logger.Error($"ERR WebException Message: {wex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error($"WebApi ERROR: {ex}");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
#region helper
|
||||
|
||||
private LanBackupsAPI getApi()
|
||||
{
|
||||
string webApiUrl = webapiUrlBase;
|
||||
var credentials = new TokenCredentials("<bearer token>");
|
||||
LanBackupsAPI api = new LanBackupsAPI(new Uri(
|
||||
webApiUrl
|
||||
), credentials);
|
||||
return api;
|
||||
}
|
||||
|
||||
#endregion helper
|
||||
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 361 KiB |
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0"?>
|
||||
<!-- For more information on using app.config transformation visit http://go.microsoft.com/fwlink/?LinkId=125889 -->
|
||||
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform"
|
||||
>
|
||||
<rules xdt:Transform="Replace">
|
||||
<logger name="*" minlevel="Trace" writeTo="file" />
|
||||
<logger name="*" minlevel="Trace" writeTo="console" />
|
||||
</rules>
|
||||
</nlog>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0"?>
|
||||
<!-- For more information on using app.config transformation visit http://go.microsoft.com/fwlink/?LinkId=125889 -->
|
||||
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform"
|
||||
>
|
||||
<rules xdt:Transform="Replace">
|
||||
<logger name="*" minlevel="Info" writeTo="file" />
|
||||
<logger name="*" minlevel="Info" writeTo="console" />
|
||||
</rules>
|
||||
</nlog>
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" ?>
|
||||
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<targets>
|
||||
<target xsi:type="File"
|
||||
name="file"
|
||||
filename="${basedir}/log_${shortdate}.log"
|
||||
keepFileOpen ="true"
|
||||
openFileCacheTimeout = "30"
|
||||
></target>
|
||||
<target name="console" xsi:type="ColoredConsole" detectConsoleAvailable="true" />
|
||||
</targets>
|
||||
<rules>
|
||||
<logger name="*" minlevel="Debug" writeTo="file" />
|
||||
<logger name="*" minlevel="Trace" writeTo="console" />
|
||||
</rules>
|
||||
</nlog>
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Autofac" version="4.3.0" targetFramework="net462" />
|
||||
<package id="ColoredConsole" version="0.5.0" targetFramework="net462" />
|
||||
<package id="Dapper" version="1.42" targetFramework="net462" />
|
||||
<package id="Hangfire.Core" version="1.6.8" targetFramework="net462" />
|
||||
<package id="Hangfire.SQLite" version="1.1.1.0" targetFramework="net462" />
|
||||
<package id="Microsoft.AspNet.SignalR.Client" version="2.2.1" targetFramework="net462" />
|
||||
<package id="Microsoft.Rest.ClientRuntime" version="2.3.4" targetFramework="net462" />
|
||||
<package id="Newtonsoft.Json" version="6.0.8" targetFramework="net462" />
|
||||
<package id="NLog" version="4.4.1" targetFramework="net462" />
|
||||
<package id="NLog.Schema" version="4.4.1" targetFramework="net462" />
|
||||
<package id="Owin" version="1.0" targetFramework="net462" />
|
||||
<package id="SimpleImpersonation" version="2.0.1" targetFramework="net462" />
|
||||
<package id="SimpleInjector" version="3.3.2" targetFramework="net462" />
|
||||
<package id="System.Data.SQLite.Core" version="1.0.104.0" targetFramework="net462" />
|
||||
<package id="TinyMessenger" version="1.0" targetFramework="net462" />
|
||||
<package id="Topshelf" version="4.0.3" targetFramework="net462" />
|
||||
<package id="Topshelf.NLog" version="4.0.3" targetFramework="net462" />
|
||||
</packages>
|
||||
Reference in New Issue
Block a user