using System; using System.IO; using System.Net; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Web.Script.Serialization; namespace FileInterface.Scheduler { public sealed class WebAdminServer : IDisposable { private readonly HttpListener _listener = new HttpListener(); private readonly CronJobRepository _cronRepository; private readonly CronScheduler _scheduler; private readonly WatcherJobRepository _watcherRepository; private readonly FileSystemWatcherManager _watcherManager; private readonly JobLog _log; private readonly SchedulerOptions _options; private readonly JavaScriptSerializer _json = new JavaScriptSerializer(); private CancellationTokenSource _cts; public WebAdminServer( CronJobRepository cronRepository, CronScheduler scheduler, WatcherJobRepository watcherRepository, FileSystemWatcherManager watcherManager, JobLog log, SchedulerOptions options) { _cronRepository = cronRepository; _scheduler = scheduler; _watcherRepository = watcherRepository; _watcherManager = watcherManager; _log = log; _options = options; _listener.Prefixes.Add(options.WebPrefix); _json.MaxJsonLength = Int32.MaxValue; } public void Start() { if (_listener.IsListening) return; _cts = new CancellationTokenSource(); _listener.Start(); Task.Run(() => AcceptLoop(_cts.Token)); } private async Task AcceptLoop(CancellationToken token) { while (!token.IsCancellationRequested) { HttpListenerContext context; try { context = await _listener.GetContextAsync(); } catch when (token.IsCancellationRequested) { break; } catch { continue; } Task.Run(() => Handle(context)); } } private void Handle(HttpListenerContext ctx) { try { if (!IsAuthorized(ctx)) { ctx.Response.StatusCode = 401; ctx.Response.AddHeader("WWW-Authenticate", "Basic realm=FileInterface Admin"); ctx.Response.Close(); return; } string path = ctx.Request.Url.AbsolutePath.TrimEnd('/'); if (path == "") path = "/"; if (ctx.Request.HttpMethod == "GET" && path == "/") { WriteHtml(ctx, Html); return; } if (ctx.Request.HttpMethod == "GET" && (path == "/api/cronjobs" || path == "/api/jobs")) { WriteJson(ctx, _cronRepository.GetAll()); return; } if (ctx.Request.HttpMethod == "POST" && (path == "/api/cronjobs/save" || path == "/api/jobs/save")) { CronJobConfig job = _json.Deserialize(ReadBody(ctx.Request)); WriteJson(ctx, _cronRepository.Save(job)); return; } if (ctx.Request.HttpMethod == "POST" && (path == "/api/cronjobs/delete" || path == "/api/jobs/delete")) { WriteJson(ctx, new { deleted = _cronRepository.Delete(ctx.Request.QueryString["id"] ?? "") }); return; } if (ctx.Request.HttpMethod == "POST" && (path == "/api/cronjobs/run" || path == "/api/jobs/run")) { WriteJson(ctx, new { started = _scheduler.RunNow(ctx.Request.QueryString["id"] ?? "") }); return; } if (ctx.Request.HttpMethod == "GET" && path == "/api/watchers") { WriteJson(ctx, _watcherRepository.GetAll()); return; } if (ctx.Request.HttpMethod == "POST" && path == "/api/watchers/save") { WatcherJobConfig job = _json.Deserialize(ReadBody(ctx.Request)); WatcherJobConfig saved = _watcherRepository.Save(job); _watcherManager.Reload(); WriteJson(ctx, saved); return; } if (ctx.Request.HttpMethod == "POST" && path == "/api/watchers/delete") { bool deleted = _watcherRepository.Delete(ctx.Request.QueryString["id"] ?? ""); _watcherManager.Reload(); WriteJson(ctx, new { deleted = deleted }); return; } if (ctx.Request.HttpMethod == "POST" && path == "/api/watchers/reload") { _watcherManager.Reload(); WriteJson(ctx, new { reloaded = true }); return; } if (ctx.Request.HttpMethod == "POST" && path == "/api/watchers/scan") { WriteJson(ctx, new { started = _watcherManager.ProcessExistingNow(ctx.Request.QueryString["id"] ?? "") }); return; } if (ctx.Request.HttpMethod == "GET" && path == "/api/logs") { int limit = 300; int parsed; if (Int32.TryParse(ctx.Request.QueryString["limit"], out parsed) && parsed > 0) limit = parsed; WriteJson(ctx, _log.GetLatest(limit)); return; } if (ctx.Request.HttpMethod == "GET" && path == "/api/status") { WriteJson(ctx, new { runningCronJobIds = _scheduler.GetRunningJobIds(), activeWatcherIds = _watcherManager.GetActiveWatcherIds(), serverTime = DateTime.Now }); return; } ctx.Response.StatusCode = 404; WriteJson(ctx, new { error = "Not found" }); } catch (Exception ex) { try { ctx.Response.StatusCode = 500; WriteJson(ctx, new { error = ex.Message }); } catch { try { ctx.Response.Close(); } catch { } } } } private bool IsAuthorized(HttpListenerContext ctx) { if (String.IsNullOrWhiteSpace(_options.WebUser)) return true; string header = ctx.Request.Headers["Authorization"]; if (String.IsNullOrWhiteSpace(header) || !header.StartsWith("Basic ", StringComparison.OrdinalIgnoreCase)) return false; try { string decoded = Encoding.UTF8.GetString(Convert.FromBase64String(header.Substring(6).Trim())); int colon = decoded.IndexOf(':'); if (colon < 0) return false; return decoded.Substring(0, colon) == _options.WebUser && decoded.Substring(colon + 1) == (_options.WebPassword ?? ""); } catch { return false; } } private static string ReadBody(HttpListenerRequest request) { using (var reader = new StreamReader(request.InputStream, request.ContentEncoding ?? Encoding.UTF8)) return reader.ReadToEnd(); } private void WriteJson(HttpListenerContext ctx, object value) { byte[] data = Encoding.UTF8.GetBytes(_json.Serialize(value)); ctx.Response.ContentType = "application/json; charset=utf-8"; ctx.Response.ContentLength64 = data.Length; ctx.Response.OutputStream.Write(data, 0, data.Length); ctx.Response.Close(); } private static void WriteHtml(HttpListenerContext ctx, string html) { byte[] data = Encoding.UTF8.GetBytes(html); ctx.Response.ContentType = "text/html; charset=utf-8"; ctx.Response.ContentLength64 = data.Length; ctx.Response.OutputStream.Write(data, 0, data.Length); ctx.Response.Close(); } public void Dispose() { if (_cts != null) _cts.Cancel(); try { _listener.Stop(); } catch { } try { _listener.Close(); } catch { } } private const string Html = @" FileInterface Admin

FileInterface – HTTP Administration

FileSystemWatcher + CronScheduler · iTAC IMS CustomFunctions

Watcher

Watcher bearbeiten

Platzhalter: {{CONTENT}}, {{RAW_CONTENT}}, {{FILE_NAME}}, {{FILE_PATH}}, {{FILE_DIR}}, {{FILE_EXT}}, {{JOB_NAME}}, {{EVENT}}, {{NOW}}

Cronjobs

Cronjob bearbeiten

Platzhalter: {{CONTENT}}, {{RAW_CONTENT}}, {{FILE_NAME}}, {{FILE_PATH}}, {{FILE_DIR}}, {{FILE_EXT}}, {{JOB_NAME}}, {{NOW}}

Runtime / Log

"; } }