mirror of
https://github.com/PepperDash/Essentials.git
synced 2026-04-19 23:46:49 +00:00
feat: Implement login request handler with token generation and error handling
This commit is contained in:
parent
d1501d2dab
commit
dc656bddf1
5 changed files with 241 additions and 6 deletions
|
|
@ -75,6 +75,8 @@ public class WebApiServer : IKeyName
|
||||||
|
|
||||||
if (_server == null) _server = new HttpCwsServer(BasePath);
|
if (_server == null) _server = new HttpCwsServer(BasePath);
|
||||||
|
|
||||||
|
_server.AuthenticateAllRoutes = false;
|
||||||
|
|
||||||
_server.setProcessName(Key);
|
_server.setProcessName(Key);
|
||||||
_server.HttpRequestHandler = new DefaultRequestHandler();
|
_server.HttpRequestHandler = new DefaultRequestHandler();
|
||||||
_server.ReceivedRequestEvent += ReceivedRequestEventHandler;
|
_server.ReceivedRequestEvent += ReceivedRequestEventHandler;
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,7 @@ public class EssentialsWebApi : EssentialsDevice
|
||||||
|
|
||||||
_server = new WebApiServer(Key, Name, BasePath);
|
_server = new WebApiServer(Key, Name, BasePath);
|
||||||
|
|
||||||
|
|
||||||
_debugServer = new WebApiServer($"{key}-debug-app", $"{name} Debug App", "/debug");
|
_debugServer = new WebApiServer($"{key}-debug-app", $"{name} Debug App", "/debug");
|
||||||
_debugServer.SetFallbackHandler(new ServeDebugAppRequestHandler());
|
_debugServer.SetFallbackHandler(new ServeDebugAppRequestHandler());
|
||||||
|
|
||||||
|
|
@ -79,6 +80,11 @@ public class EssentialsWebApi : EssentialsDevice
|
||||||
{
|
{
|
||||||
var routes = new List<HttpCwsRoute>
|
var routes = new List<HttpCwsRoute>
|
||||||
{
|
{
|
||||||
|
new HttpCwsRoute("login")
|
||||||
|
{
|
||||||
|
Name = "Root",
|
||||||
|
RouteHandler = new LoginRequestHandler()
|
||||||
|
},
|
||||||
new HttpCwsRoute("versions")
|
new HttpCwsRoute("versions")
|
||||||
{
|
{
|
||||||
Name = "ReportVersions",
|
Name = "ReportVersions",
|
||||||
|
|
|
||||||
|
|
@ -40,12 +40,18 @@ public static class EssentialsWebApiHelpers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static object MapToDeviceListObject(IKeyed device)
|
public static object MapToDeviceListObject(IKeyed device)
|
||||||
{
|
{
|
||||||
|
var interfaces = device.GetType()
|
||||||
|
.GetInterfaces()
|
||||||
|
.Select(i => i.Name)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
return new
|
return new
|
||||||
{
|
{
|
||||||
device.Key,
|
device.Key,
|
||||||
Name = (device is IKeyName)
|
Name = (device is IKeyName)
|
||||||
? (device as IKeyName).Name
|
? (device as IKeyName).Name
|
||||||
: "---"
|
: "---",
|
||||||
|
Interfaces = interfaces
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,26 +40,26 @@ namespace PepperDash.Essentials.Core.Web.RequestHandlers
|
||||||
if (device is IRoutingInputs inputDevice)
|
if (device is IRoutingInputs inputDevice)
|
||||||
{
|
{
|
||||||
deviceInfo.HasInputs = true;
|
deviceInfo.HasInputs = true;
|
||||||
deviceInfo.InputPorts = inputDevice.InputPorts.Select(p => new PortInfo
|
deviceInfo.InputPorts = [.. inputDevice.InputPorts.Select(p => new PortInfo
|
||||||
{
|
{
|
||||||
Key = p.Key,
|
Key = p.Key,
|
||||||
SignalType = p.Type.ToString(),
|
SignalType = p.Type.ToString(),
|
||||||
ConnectionType = p.ConnectionType.ToString(),
|
ConnectionType = p.ConnectionType.ToString(),
|
||||||
IsInternal = p.IsInternal
|
IsInternal = p.IsInternal
|
||||||
}).ToList();
|
})];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if device implements IRoutingOutputs
|
// Check if device implements IRoutingOutputs
|
||||||
if (device is IRoutingOutputs outputDevice)
|
if (device is IRoutingOutputs outputDevice)
|
||||||
{
|
{
|
||||||
deviceInfo.HasOutputs = true;
|
deviceInfo.HasOutputs = true;
|
||||||
deviceInfo.OutputPorts = outputDevice.OutputPorts.Select(p => new PortInfo
|
deviceInfo.OutputPorts = [.. outputDevice.OutputPorts.Select(p => new PortInfo
|
||||||
{
|
{
|
||||||
Key = p.Key,
|
Key = p.Key,
|
||||||
SignalType = p.Type.ToString(),
|
SignalType = p.Type.ToString(),
|
||||||
ConnectionType = p.ConnectionType.ToString(),
|
ConnectionType = p.ConnectionType.ToString(),
|
||||||
IsInternal = p.IsInternal
|
IsInternal = p.IsInternal
|
||||||
}).ToList();
|
})];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if device implements IRoutingInputsOutputs
|
// Check if device implements IRoutingInputsOutputs
|
||||||
|
|
@ -86,10 +86,31 @@ namespace PepperDash.Essentials.Core.Web.RequestHandlers
|
||||||
IsInternal = tl.IsInternal
|
IsInternal = tl.IsInternal
|
||||||
}).ToList();
|
}).ToList();
|
||||||
|
|
||||||
|
// Get current active routes from DefaultCollection, grouped by signal type
|
||||||
|
var currentRoutes = RouteDescriptorCollection.DefaultCollection.Descriptors
|
||||||
|
.GroupBy(d => d.SignalType.ToString())
|
||||||
|
.Select(g => new CurrentRouteGroupInfo
|
||||||
|
{
|
||||||
|
SignalType = g.Key,
|
||||||
|
Routes = [.. g.Select(d => new ActiveRouteInfo
|
||||||
|
{
|
||||||
|
SourceDeviceKey = d.Source.Key,
|
||||||
|
DestinationDeviceKey = d.Destination.Key,
|
||||||
|
DestinationInputPortKey = d.InputPort?.Key,
|
||||||
|
Steps = [.. d.Routes.Select(r => new RouteSwitchStepInfo
|
||||||
|
{
|
||||||
|
SwitchingDeviceKey = r.SwitchingDevice?.Key,
|
||||||
|
InputPortKey = r.InputPort?.Key,
|
||||||
|
OutputPortKey = r.OutputPort?.Key
|
||||||
|
})]
|
||||||
|
})]
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
var response = new RoutingSystemInfo
|
var response = new RoutingSystemInfo
|
||||||
{
|
{
|
||||||
Devices = devices,
|
Devices = devices,
|
||||||
TieLines = tielines
|
TieLines = tielines,
|
||||||
|
CurrentRoutes = currentRoutes
|
||||||
};
|
};
|
||||||
|
|
||||||
var jsonResponse = JsonConvert.SerializeObject(response, Formatting.Indented);
|
var jsonResponse = JsonConvert.SerializeObject(response, Formatting.Indented);
|
||||||
|
|
@ -121,6 +142,12 @@ namespace PepperDash.Essentials.Core.Web.RequestHandlers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("tieLines")]
|
[JsonProperty("tieLines")]
|
||||||
public List<TieLineInfo> TieLines { get; set; }
|
public List<TieLineInfo> TieLines { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the current active routes in the system, grouped by signal type
|
||||||
|
/// </summary>
|
||||||
|
[JsonProperty("currentRoutes")]
|
||||||
|
public List<CurrentRouteGroupInfo> CurrentRoutes { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -238,4 +265,77 @@ namespace PepperDash.Essentials.Core.Web.RequestHandlers
|
||||||
[JsonProperty("isInternal")]
|
[JsonProperty("isInternal")]
|
||||||
public bool IsInternal { get; set; }
|
public bool IsInternal { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a group of active routes for a given signal type
|
||||||
|
/// </summary>
|
||||||
|
public class CurrentRouteGroupInfo
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the signal type for the group of active routes (e.g., AudioVideo, Audio, Video, etc.)
|
||||||
|
/// </summary>
|
||||||
|
[JsonProperty("signalType")]
|
||||||
|
public string SignalType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the list of active routes for the given signal type
|
||||||
|
/// </summary>
|
||||||
|
[JsonProperty("routes")]
|
||||||
|
public List<ActiveRouteInfo> Routes { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a single active route from a source to a destination
|
||||||
|
/// </summary>
|
||||||
|
public class ActiveRouteInfo
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the key of the source device for the active route
|
||||||
|
/// </summary>
|
||||||
|
[JsonProperty("sourceDeviceKey")]
|
||||||
|
public string SourceDeviceKey { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the key of the destination device for the active route
|
||||||
|
/// </summary>
|
||||||
|
[JsonProperty("destinationDeviceKey")]
|
||||||
|
public string DestinationDeviceKey { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the key of the destination input port for the active route, if applicable
|
||||||
|
/// </summary>
|
||||||
|
[JsonProperty("destinationInputPortKey", NullValueHandling = NullValueHandling.Ignore)]
|
||||||
|
public string DestinationInputPortKey { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the list of switching steps for the active route
|
||||||
|
/// </summary>
|
||||||
|
[JsonProperty("steps")]
|
||||||
|
public List<RouteSwitchStepInfo> Steps { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a single switching step within a route
|
||||||
|
/// </summary>
|
||||||
|
public class RouteSwitchStepInfo
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the key of the switching device for the route step
|
||||||
|
/// </summary>
|
||||||
|
[JsonProperty("switchingDeviceKey", NullValueHandling = NullValueHandling.Ignore)]
|
||||||
|
public string SwitchingDeviceKey { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the key of the input port for the route step, if applicable
|
||||||
|
/// </summary>
|
||||||
|
[JsonProperty("inputPortKey", NullValueHandling = NullValueHandling.Ignore)]
|
||||||
|
public string InputPortKey { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the key of the output port for the route step, if applicable
|
||||||
|
/// </summary>
|
||||||
|
[JsonProperty("outputPortKey", NullValueHandling = NullValueHandling.Ignore)]
|
||||||
|
public string OutputPortKey { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,121 @@
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using Crestron.SimplSharp.CrestronAuthentication;
|
||||||
|
using Crestron.SimplSharp.WebScripting;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using PepperDash.Core.Web.RequestHandlers;
|
||||||
|
|
||||||
|
namespace PepperDash.Essentials.Core.Web.RequestHandlers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a LoginRequestHandler
|
||||||
|
/// </summary>
|
||||||
|
public class LoginRequestHandler : WebApiBaseRequestHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// base(true) enables CORS support by default
|
||||||
|
/// </remarks>
|
||||||
|
public LoginRequestHandler()
|
||||||
|
: base(true)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles POST method requests for user login and token generation
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">The HTTP context for the request.</param>
|
||||||
|
protected override void HandlePost(HttpCwsContext context)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (context.Request.ContentLength < 0)
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 400;
|
||||||
|
context.Response.StatusDescription = "Bad Request";
|
||||||
|
context.Response.End();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var data = context.Request.GetRequestBody();
|
||||||
|
if (string.IsNullOrEmpty(data))
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 400;
|
||||||
|
context.Response.StatusDescription = "Bad Request";
|
||||||
|
context.Response.End();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var loginRequest = JsonConvert.DeserializeObject<LoginRequest>(data);
|
||||||
|
|
||||||
|
if (loginRequest == null || string.IsNullOrEmpty(loginRequest.Username) || string.IsNullOrEmpty(loginRequest.Password))
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 400;
|
||||||
|
context.Response.StatusDescription = "Bad Request";
|
||||||
|
context.Response.End();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Authentication.UserToken token;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
token = Authentication.GetAuthenticationToken(loginRequest.Username, loginRequest.Password);
|
||||||
|
}
|
||||||
|
catch (ArgumentException)
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 401;
|
||||||
|
context.Response.StatusDescription = "Bad Request";
|
||||||
|
context.Response.End();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!token.Valid)
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 401;
|
||||||
|
context.Response.StatusDescription = "Unauthorized";
|
||||||
|
context.Response.End();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
context.Response.StatusCode = 200;
|
||||||
|
context.Response.StatusDescription = "OK";
|
||||||
|
context.Response.ContentType = "application/json";
|
||||||
|
context.Response.ContentEncoding = System.Text.Encoding.UTF8;
|
||||||
|
context.Response.Write(JsonConvert.SerializeObject(new { Token = token }, Formatting.Indented), false);
|
||||||
|
context.Response.End();
|
||||||
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 500;
|
||||||
|
context.Response.StatusDescription = "Internal Server Error";
|
||||||
|
context.Response.ContentType = "application/json";
|
||||||
|
context.Response.ContentEncoding = System.Text.Encoding.UTF8;
|
||||||
|
context.Response.Write(JsonConvert.SerializeObject(new { Error = ex.Message }, Formatting.Indented), false);
|
||||||
|
context.Response.End();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a LoginRequest
|
||||||
|
/// </summary>
|
||||||
|
public class LoginRequest
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the username.
|
||||||
|
/// </summary>
|
||||||
|
public string Username { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the password.
|
||||||
|
/// </summary>
|
||||||
|
public string Password { get; set; }
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue