Create alerts button and frame.

Create cards for alerts.
This commit is contained in:
Jared Goodwin 2020-03-25 19:04:49 -07:00
parent bfc84be732
commit 869fe9aec8
17 changed files with 152 additions and 27 deletions

View File

@ -29,7 +29,6 @@ namespace Remotely.Server.API
private IEmailSenderEx EmailSender { get; }
[HttpPost("Create")]
[ServiceFilter(typeof(ApiAuthorizationFilter))]
public async Task<IActionResult> Create(AlertOptions alertOptions)
{
Request.Headers.TryGetValue("OrganizationID", out var orgID);
@ -94,7 +93,6 @@ namespace Remotely.Server.API
}
[HttpPost("Delete/{alertID}")]
[ServiceFilter(typeof(ApiAuthorizationFilter))]
public async Task<IActionResult> Delete(string alertID)
{
Request.Headers.TryGetValue("OrganizationID", out var orgID);

View File

@ -35,6 +35,12 @@ namespace Remotely.Server.Auth
if (DataService.ValidateApiToken(apiToken, apiSecret, context.HttpContext.Request.Path, context.HttpContext.Connection.RemoteIpAddress.ToString()))
{
var orgID = DataService.GetApiToken(apiToken)?.OrganizationID;
// In case the filter gets run twice.
if (context.HttpContext.Request.Headers.ContainsKey("OrganizationID"))
{
return;
}
context.HttpContext.Request.Headers.Add("OrganizationID", orgID);
return;
}

View File

@ -28,6 +28,12 @@ else
<input type="text" class="form-control" name="device-name" readonly value="@Model.DeviceName" />
</div>
</div>
<div class="form-group row">
<label for="device-name" class="col-sm-2 col-form-label">Device:</label>
<div class="col-sm-10">
<input type="text" class="form-control" name="device-name" readonly value="@Model.DeviceID" />
</div>
</div>
<div class="form-group row">
<label for="agent-version" class="col-sm-2 col-form-label">Agent Version:</label>
<div class="col-sm-10">

View File

@ -22,6 +22,7 @@ namespace Remotely.Server.Pages
public string AgentVersion { get; set; }
public List<SelectListItem> DeviceGroups { get; } = new List<SelectListItem>();
public string DeviceName { get; set; }
public string DeviceID { get; set; }
[BindProperty]
public InputModel Input { get; set; } = new InputModel();
@ -68,6 +69,7 @@ namespace Remotely.Server.Pages
{
var device = DataService.GetDevice(user.OrganizationID, deviceID);
DeviceName = device?.DeviceName;
DeviceID = device?.ID;
AgentVersion = device.AgentVersion;
Input.Alias = device?.Alias;
Input.DeviceGroupID = device?.DeviceGroupID;

View File

@ -24,10 +24,10 @@ namespace Remotely.Server.Pages
public string DefaultPrompt { get; set; }
public List<SelectListItem> DeviceGroups { get; set; } = new List<SelectListItem>();
public List<Alert> Alerts { get; set; } = new List<Alert>();
private ApplicationConfig AppConfig { get; }
private DataService DataService { get; }
private SignInManager<RemotelyUser> SignInManager { get; }
private ApplicationConfig AppConfig { get; }
public async Task<IActionResult> OnGet()
{
if (User?.Identity?.IsAuthenticated == true)
@ -50,6 +50,12 @@ namespace Remotely.Server.Pages
{
DeviceGroups.AddRange(groups.Select(x => new SelectListItem(x.Name, x.ID)));
}
var alerts = DataService.GetAlerts(user.Id);
if (alerts.Any())
{
Alerts.AddRange(alerts);
}
}
else
{

View File

@ -1,5 +1,34 @@
@model IndexModel
<button id="alertsButton" class="btn btn-info">
<span class="fa fa-bell"></span>
@Model.Alerts.Count
</button>
<div id="alertsFrame" class="bg-secondary">
<div class="text-right">
<span id="closeAlertsFrameButton" class="fa fa-times pointer mt-2 mr-2"></span>
</div>
<div class="mt-3">
@foreach (var alert in Model.Alerts)
{
<div class="card border-primary mb-3">
<div class="card-header">@alert.CreatedOn.ToString()</div>
<div class="card-body">
@if (!string.IsNullOrWhiteSpace(alert.Device?.DeviceName))
{
<h6 class="card-title">@alert.Device.DeviceName</h6>
}
<p class="card-text">@alert.Message</p>
</div>
</div>
}
</div>
</div>
<div class="work-area hidden">
<ul class="nav nav-tabs">
<li class="nav-item">
@ -24,8 +53,6 @@
<partial name="_ConsoleFrame" model="Model" />
</div>
</div>
</div>
<script src="~/scripts/Main.js" type="module"></script>

View File

@ -36,20 +36,27 @@ namespace Remotely.Server.Services
public async Task AddAlert(AlertOptions alertOptions, string organizationID)
{
await RemotelyContext.Users
var users = RemotelyContext.Users
.Include(x => x.Alerts)
.Where(x => x.OrganizationID == organizationID)
.ForEachAsync(x => {
var alert = new Alert()
{
CreatedOn = DateTimeOffset.Now,
DeviceID = alertOptions.AlertDeviceID,
Message = alertOptions.AlertMessage,
OrganizationID = organizationID
};
x.Alerts.Add(alert);
});
.Where(x => x.OrganizationID == organizationID);
if (!string.IsNullOrWhiteSpace(alertOptions.AlertDeviceID))
{
var filteredUserIDs = FilterUsersByDevicePermission(users.Select(x => x.Id), alertOptions.AlertDeviceID);
users = users.Where(x => filteredUserIDs.Contains(x.Id));
}
await users.ForEachAsync(x => {
var alert = new Alert()
{
CreatedOn = DateTimeOffset.Now,
DeviceID = alertOptions.AlertDeviceID,
Message = alertOptions.AlertMessage,
OrganizationID = organizationID
};
x.Alerts.Add(alert);
});
await RemotelyContext.SaveChangesAsync();
}
@ -83,11 +90,6 @@ namespace Remotely.Server.Services
return true;
}
public async Task<Alert> GetAlert(string alertID)
{
return await RemotelyContext.Alerts.FirstOrDefaultAsync(x => x.ID == alertID);
}
public InviteLink AddInvite(string orgID, Invite invite)
{
invite.InvitedUser = invite.InvitedUser.ToLower();
@ -485,6 +487,21 @@ namespace Remotely.Server.Services
.ToArray();
}
public async Task<Alert> GetAlert(string alertID)
{
return await RemotelyContext.Alerts
.Include(x => x.Device)
.Include(x => x.User)
.FirstOrDefaultAsync(x => x.ID == alertID);
}
public IEnumerable<Alert> GetAlerts(string userID)
{
return RemotelyContext.Alerts
.Include(x => x.Device)
.Include(x => x.User)
.Where(x => x.UserID == userID);
}
public IEnumerable<ApiToken> GetAllApiTokens(string userID)
{
var user = RemotelyContext.Users.FirstOrDefault(x => x.Id == userID);

View File

@ -112,7 +112,7 @@ button.navbar-toggler:hover {
color: limegreen;
}
.fa-times {
td .fa-times {
color: red;
}

View File

@ -107,7 +107,7 @@ button.navbar-toggler:hover {
color: forestgreen;
}
.fa-times {
td .fa-times {
color: red;
}

View File

@ -236,6 +236,39 @@ span.label.code {
margin-bottom: -5px;
}
#alertsButton {
position: absolute;
top: 75px;
right: 5px;
z-index: 2;
transition: .5s ease left;
user-select: none;
}
#alertsFrame {
position: absolute;
right: 0;
top: 0;
height: 100%;
width: 0;
overflow-x: hidden;
overflow-y: auto;
text-align: center;
opacity: 0;
z-index: 2;
transition: .5s ease all;
}
#alertsFrame.open {
width: 350px;
opacity: 1;
transition: .5s ease all;
}
.float-message {
position: fixed;
top: 20px;

View File

@ -15,6 +15,7 @@ export function ApplyInputEventHandlers() {
clickStartRemoteControlButton();
consoleTabSelected();
deviceGroupSelectChanged();
clickAlertsButtons();
window.addEventListener("resize", ev => {
PositionCommandCompletionWindow();
});
@ -156,6 +157,14 @@ function consoleTabSelected() {
UI.ConsoleFrame.scrollTop = UI.ConsoleFrame.scrollHeight;
});
}
function clickAlertsButtons() {
UI.AlertsButton.addEventListener("click", ev => {
UI.AlertsFrame.classList.toggle("open");
});
UI.CloseAlertsButton.addEventListener("click", ev => {
UI.AlertsFrame.classList.toggle("open");
});
}
function clickToggleAllDevices() {
document.getElementById("toggleAllDevices").addEventListener("click", function (e) {
DataGrid.ToggleSelectAll();

File diff suppressed because one or more lines are too long

View File

@ -17,6 +17,7 @@ export function ApplyInputEventHandlers() {
clickStartRemoteControlButton();
consoleTabSelected();
deviceGroupSelectChanged();
clickAlertsButtons();
window.addEventListener("resize", ev => {
PositionCommandCompletionWindow();
@ -163,6 +164,14 @@ function consoleTabSelected() {
UI.ConsoleFrame.scrollTop = UI.ConsoleFrame.scrollHeight;
});
}
function clickAlertsButtons() {
UI.AlertsButton.addEventListener("click", ev => {
UI.AlertsFrame.classList.toggle("open");
});
UI.CloseAlertsButton.addEventListener("click", ev => {
UI.AlertsFrame.classList.toggle("open");
});
}
function clickToggleAllDevices() {
document.getElementById("toggleAllDevices").addEventListener("click", function (e) {
DataGrid.ToggleSelectAll();

View File

@ -16,6 +16,9 @@ export var ConsoleTab = document.getElementById("consoleTab");
export var ConsoleAlert = document.getElementById("consoleAlert");
export var DeviceGroupSelect = document.getElementById("deviceGroupSelect");
export var GridFilter = document.getElementById("gridFilter");
export var AlertsButton = document.getElementById("alertsButton");
export var CloseAlertsButton = document.getElementById("closeAlertsFrameButton");
export var AlertsFrame = document.getElementById("alertsFrame");
export function PopupMessage(message) {
var messageDiv = document.createElement("div");
messageDiv.classList.add("float-message");

View File

@ -1 +1 @@
{"version":3,"file":"UI.js","sourceRoot":"","sources":["UI.ts"],"names":[],"mappings":"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"}
{"version":3,"file":"UI.js","sourceRoot":"","sources":["UI.ts"],"names":[],"mappings":"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"}

View File

@ -18,6 +18,9 @@ export var ConsoleTab = document.getElementById("consoleTab") as HTMLAnchorEleme
export var ConsoleAlert = document.getElementById("consoleAlert") as HTMLAnchorElement;
export var DeviceGroupSelect = document.getElementById("deviceGroupSelect") as HTMLSelectElement;
export var GridFilter = document.getElementById("gridFilter") as HTMLInputElement;
export var AlertsButton = document.getElementById("alertsButton") as HTMLButtonElement;
export var CloseAlertsButton = document.getElementById("closeAlertsFrameButton") as HTMLButtonElement;
export var AlertsFrame = document.getElementById("alertsFrame") as HTMLDivElement;
export function PopupMessage(message: string) {

View File

@ -163,6 +163,12 @@ namespace Remotely.Tests
ShouldAlert = true
};
await DataService.AddAlert(options, TestData.OrganizationID);
var alerts = DataService.GetAlerts(TestData.Admin1.Id);
var json = System.Text.Json.JsonSerializer.Serialize(options);
Assert.AreEqual("Test Message", alerts.First().Message);
}
}
}