Finish Alerts API.

This commit is contained in:
Jared Goodwin 2020-03-26 20:04:57 -07:00
parent 78607a2b82
commit f3e38ed2ed
10 changed files with 117 additions and 25 deletions

View File

@ -13,6 +13,7 @@ Multi-Tenant Demo Server: https://app.remotely.one
* Endpoint devices require the .NET Core runtime to be installed.
* For Windows, the Desktop Runtime is required.
* Download Link: https://dotnet.microsoft.com/download/dotnet-core/current/runtime
* The installer will automatically download and install the runtime if missing.
## Build Instructions (Windows 10)
The following steps will configure your Windows 10 machine for building the Remotely server and clients.
@ -98,14 +99,6 @@ Ideally, you'd be doing remote control from an actual computer or laptop. Howev
* Click-and-drag: Tap and hold with one finger, tap and release a second finger (without pinch-zooming)
* The click-and-drag operation will begin where finger one is held.
## Shortcut Keys
There are a few shortcut keys available when using the console.
* / : Slash will open the autocomplete for selecting the current command mode. The names are configurable in the Account - Options page.
* Up/Down: Use arrow up/down to cycle through input history.
* Ctrl + Up/Down: Scroll the console output window.
* Ctrl + Q: Clear the output window.
* Esc: Close the autocomplete window.
## Configuration
The following settings are available in appsettings.json.
@ -131,6 +124,24 @@ Note: To retain your settings between upgrades, copy your settings to appsetting
* Only works on Windows agents.
* Session recording will not work if a WebRTC connection is made.
## .NET Core Deployments
* .NET Core has two methods of deployment: framework-dependent and self-contained.
* Framework-dependent deployments require the .NET Core runtime to be installed on the target computers. It must be the same version that was used to build the app.
* Self-contained deployments include a copy of the runtime, so you don't need to install it on the target computers. As a result, the total file size is much larger.
* .NET Core uses runtime identifiers that are targeted when building.
* Link: https://docs.microsoft.com/en-us/dotnet/core/rid-catalog
## Shortcut Keys
There are a few shortcut keys available when using the console.
* / : Slash will open the autocomplete for selecting the current command mode. The names are configurable in the Account - Options page.
* Up/Down: Use arrow up/down to cycle through input history.
* Ctrl + Up/Down: Scroll the console output window.
* Ctrl + Q: Clear the output window.
* Esc: Close the autocomplete window.
## API and Integrations
Remotely has a basic API, which can be browsed at https://app.remotely.one/swagger (or your own server instance). Most endpoints require authentication via an API access token, which can be created by going to Account - API Access.
@ -153,10 +164,39 @@ Below is an example API request:
Get-Location
## Alerts
The Alerts API gives you the ability to add monitoring and alerting functionality to your device endpoints. This feature is intended to add basic RMM-type functionality without diverging too far from Remotely's primary purpose.
## .NET Core Deployments
* .NET Core has two methods of deployment: framework-dependent and self-contained.
* Framework-dependent deployments require the .NET Core runtime to be installed on the target computers. It must be the same version or higher that was used to build the app.
* Self-contained deployments include a copy of the runtime, so you don't need to install it on the target computers. As a result, the total file size is much larger.
* .NET Core uses runtime identifiers that are targeted when building.
* Link: https://docs.microsoft.com/en-us/dotnet/core/rid-catalog
Alerts can be set up to show a notification on the Remotely website, send an email, and/or perform a separate API request.
To use Alerts, you'd first need to make an API token (or multiple tokens) for your devices to use. Then create a scheduled task or some other recurring script to do the work. Below is an example of how to use PowerShell to create a Scheduled Job that checks the disk space on a daily schedule.
```
$Trigger = New-JobTrigger -Daily -At "5 AM"
$Option = New-ScheduledJobOption -RequireNetwork
Register-ScheduledJob -ScriptBlock {
$OsDrive = Get-PSDrive -Name C
$FreeSpace = $OsDrive.Free / ($OsDrive.Used + $OsDrive.Free)
if ($FreeSpace -lt .1) {
Invoke-WebRequest -Uri "https://localhost:5001/api/Alerts/Create/" -Method Post -Headers @{
Authorization="3e9d8273-1dc1-4303-bd50-7a133e36b9b7:S+82XKZdvg278pSFHWtUklqHENuO5IhH"
} -Body @"
{
"AlertDeviceID": "f2b0a595-5ea8-471b-975f-12e70e0f3497",
"AlertMessage": "Low hard drive space. Free Space: $([Math]::Round($FreeSpace * 100))%",
"ApiRequestBody": null,
"ApiRequestHeaders": null,
"ApiRequestMethod": null,
"ApiRequestUrl": null,
"EmailBody": "Low hard drive space for device Maker.",
"EmailSubject": "Hard Drive Space Alert",
"EmailTo": "translucency_software@outlook.com",
"ShouldAlert": true,
"ShouldEmail": true,
"ShouldSendApiRequest": false
}
"@ -ContentType "application/json"
}
} -Name "Check OS Drive Space" -Trigger $Trigger -ScheduledJobOption $Option
```

View File

@ -92,7 +92,7 @@ namespace Remotely.Server.API
return Ok();
}
[HttpPost("Delete/{alertID}")]
[HttpDelete("Delete/{alertID}")]
public async Task<IActionResult> Delete(string alertID)
{
Request.Headers.TryGetValue("OrganizationID", out var orgID);

View File

@ -101,7 +101,7 @@ namespace Remotely.Server.API
var stopWatch = Stopwatch.StartNew();
Func<bool> remoteControlStarted = () => RCDeviceSocketHub.SessionInfoList.Values.Any(x => x.DeviceID == targetDevice.Value.ID && !existingSessions.Any(y => y.Key != x.RCDeviceSocketID));
bool remoteControlStarted() => RCDeviceSocketHub.SessionInfoList.Values.Any(x => x.DeviceID == targetDevice.Value.ID && !existingSessions.Any(y => y.Key != x.RCDeviceSocketID));
if (!await TaskHelper.DelayUntil(remoteControlStarted, TimeSpan.FromSeconds(15)))
{

View File

@ -3,18 +3,23 @@
<button id="alertsButton" class="btn btn-info">
<span class="fa fa-bell"></span>
@Model.Alerts.Count
<span id="alertsCount">
@Model.Alerts.Count
</span>
</button>
<div id="alertsFrame" class="bg-secondary">
<div class="text-right">
<span id="closeAlertsFrameButton" class="fa fa-times pointer mt-2 mr-2"></span>
<button id="closeAlertsFrameButton" class="btn btn-secondary mt-2 mr-2 btn-sm">
<span class="fa fa-times pointer"></span>
</button>
</div>
<div class="mt-3">
@foreach (var alert in Model.Alerts)
{
<div class="card border-primary mb-3">
<div id="@alert.ID" class="card border-primary mb-3 mr-2 ml-2">
<div class="card-header">@alert.CreatedOn.ToString()</div>
<div class="card-body">
@if (!string.IsNullOrWhiteSpace(alert.Device?.DeviceName))
@ -22,6 +27,12 @@
<h6 class="card-title">@alert.Device.DeviceName</h6>
}
<p class="card-text">@alert.Message</p>
<div class="text-right">
<button alert="@alert.ID" class="btn btn-sm btn-secondary alert-dismiss-button"
type="button">
Dismiss
</button>
</div>
</div>
</div>
}

View File

@ -15,7 +15,7 @@ export function ApplyInputEventHandlers() {
clickStartRemoteControlButton();
consoleTabSelected();
deviceGroupSelectChanged();
clickAlertsButtons();
addAlertHandlers();
window.addEventListener("resize", ev => {
PositionCommandCompletionWindow();
});
@ -157,13 +157,32 @@ function consoleTabSelected() {
UI.ConsoleFrame.scrollTop = UI.ConsoleFrame.scrollHeight;
});
}
function clickAlertsButtons() {
function addAlertHandlers() {
UI.AlertsButton.addEventListener("click", ev => {
UI.AlertsFrame.classList.toggle("open");
});
UI.CloseAlertsButton.addEventListener("click", ev => {
UI.AlertsFrame.classList.toggle("open");
});
document.querySelectorAll(".alert-dismiss-button").forEach(element => {
element.addEventListener("click", ev => {
var alertID = ev.currentTarget.getAttribute("alert");
var xhr = new XMLHttpRequest();
xhr.open("delete", location.origin + "/api/Alerts/Delete/" + alertID);
xhr.onload = function () {
if (xhr.status == 200) {
document.getElementById(alertID).remove();
var currentCount = Number(UI.AlertsCount.innerText);
currentCount--;
UI.AlertsCount.innerText = String(currentCount);
}
else {
UI.ShowModal("API Error", "There was an error deleting the alert.");
}
};
xhr.send();
});
});
}
function clickToggleAllDevices() {
document.getElementById("toggleAllDevices").addEventListener("click", function (e) {

File diff suppressed because one or more lines are too long

View File

@ -17,7 +17,7 @@ export function ApplyInputEventHandlers() {
clickStartRemoteControlButton();
consoleTabSelected();
deviceGroupSelectChanged();
clickAlertsButtons();
addAlertHandlers();
window.addEventListener("resize", ev => {
PositionCommandCompletionWindow();
@ -164,13 +164,33 @@ function consoleTabSelected() {
UI.ConsoleFrame.scrollTop = UI.ConsoleFrame.scrollHeight;
});
}
function clickAlertsButtons() {
function addAlertHandlers() {
UI.AlertsButton.addEventListener("click", ev => {
UI.AlertsFrame.classList.toggle("open");
});
UI.CloseAlertsButton.addEventListener("click", ev => {
UI.AlertsFrame.classList.toggle("open");
});
document.querySelectorAll(".alert-dismiss-button").forEach(element => {
element.addEventListener("click", ev => {
var alertID = (ev.currentTarget as HTMLButtonElement).getAttribute("alert");
var xhr = new XMLHttpRequest();
xhr.open("delete", location.origin + "/api/Alerts/Delete/" + alertID);
xhr.onload = function () {
if (xhr.status == 200) {
document.getElementById(alertID).remove();
var currentCount = Number(UI.AlertsCount.innerText);
currentCount--;
UI.AlertsCount.innerText = String(currentCount);
}
else {
UI.ShowModal("API Error", "There was an error deleting the alert.");
}
};
xhr.send();
})
});
}
function clickToggleAllDevices() {
document.getElementById("toggleAllDevices").addEventListener("click", function (e) {

View File

@ -19,6 +19,7 @@ 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 var AlertsCount = document.getElementById("alertsCount");
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

@ -21,6 +21,7 @@ export var GridFilter = document.getElementById("gridFilter") as HTMLInputElemen
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 var AlertsCount = document.getElementById("alertsCount") as HTMLSpanElement;
export function PopupMessage(message: string) {