summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.idea/dataSources.xml2
-rw-r--r--README.md2
-rw-r--r--src/api/routes/auth/deviceRoutes.js84
-rw-r--r--testFrontend/SafeNSound.Frontend/Pages/Auth.razor92
-rw-r--r--testFrontend/SafeNSound.Sdk/SafeNSoundClient.cs63
5 files changed, 196 insertions, 47 deletions
diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml

index f495692..4dc945a 100644 --- a/.idea/dataSources.xml +++ b/.idea/dataSources.xml
@@ -5,7 +5,7 @@ <driver-ref>mongo.4</driver-ref> <synchronize>true</synchronize> <jdbc-driver>com.dbschema.MongoJdbcDriver</jdbc-driver> - <jdbc-url>mongodb://localhost:27017/nodejs</jdbc-url> + <jdbc-url>mongodb://localhost:27017/nodejs?authSource=admin</jdbc-url> <working-dir>$ProjectFileDir$</working-dir> <driver-properties> <property name="authSource" value="admin" /> diff --git a/README.md b/README.md
index fa2e942..bca91cb 100644 --- a/README.md +++ b/README.md
@@ -13,8 +13,8 @@ Environment variables: | Name | Default | Description | | ---- | ------- | ----------- | | `PORT` | `3000` | The port the server will run on. | -| `LOG_REQUESTS` | `-` | Requests to log to the console by status, `-` to invert. | | `DATABASE_SECRET_PATH` | ` ` | The path to the mongodb connection string. | | `JWT_SECRET_PATH` | ` ` | The path to the JWT secret certificate. | +| `LOG_REQUESTS` | `-` | Requests to log to the console by status, `-` to invert. | | `LOG_QUERIES` | `false` | Whether to enable mongoose debug logs | | `LOG_AUTH` | `false` | Whether to enable authentication debug logs | diff --git a/src/api/routes/auth/deviceRoutes.js b/src/api/routes/auth/deviceRoutes.js
index 551252b..023d02d 100644 --- a/src/api/routes/auth/deviceRoutes.js +++ b/src/api/routes/auth/deviceRoutes.js
@@ -1,7 +1,11 @@ -import { registerUser } from '#db/index.js'; -import { RegisterDto } from '#dto/index.js'; import { requireAuth } from '#api/middlewares/index.js'; import { RouteMethod } from '#api/RouteDescription.js'; +import { SafeNSoundError } from '#util/error.js'; +import Joi from 'joi'; + +const deviceUpdateSchema = Joi.object({ + name: Joi.string().optional().max(100) +}); /** * @type {RouteDescription} @@ -13,9 +17,79 @@ export const getDevicesRoute = { middlewares: [requireAuth], description: 'Get all devices registered to the user', async method(req, res) { - const data = await RegisterDto.create(req.body); - const registerResult = await registerUser(data); - res.send(registerResult); + res.send(req.user.devices); + } + }) + } +}; + +/** + * @type {RouteDescription} + */ +export const manageDeviceRoute = { + path: '/auth/devices/:id', + methods: { + get: new RouteMethod({ + middlewares: [requireAuth], + description: 'Get user device by ID', + async method(req, res) { + const device = req.user.devices.find( + device => device.id === req.params.id + ); + if (!device) { + res.status(404).send( + new SafeNSoundError({ + errCode: 'ENTITY_NOT_FOUND', + message: 'Device not found' + }) + ); + return; + } + res.send(device); + } + }), + delete: new RouteMethod({ + middlewares: [requireAuth], + description: 'Delete user device by ID', + async method(req, res) { + const deviceIndex = req.user.devices.findIndex( + device => device.id === req.params.id + ); + if (deviceIndex === -1) { + res.status(404).send( + new SafeNSoundError({ + errCode: 'ENTITY_NOT_FOUND', + message: 'Device not found' + }) + ); + return; + } + req.user.devices.splice(deviceIndex, 1); + await req.user.save(); + res.status(204).send(); + } + }), + patch: new RouteMethod({ + middlewares: [requireAuth], + description: 'Update user device by ID', + async method(req, res) { + const device = req.user.devices.find( + device => device.id === req.params.id + ); + if (!device) { + res.status(404).send( + new SafeNSoundError({ + errCode: 'ENTITY_NOT_FOUND', + message: 'Device not found' + }) + ); + return; + } + if (req.body.name) { + device.name = req.body.name; + } + await req.user.save(); + res.send(device); } }) } diff --git a/testFrontend/SafeNSound.Frontend/Pages/Auth.razor b/testFrontend/SafeNSound.Frontend/Pages/Auth.razor
index 5540f02..20625f7 100644 --- a/testFrontend/SafeNSound.Frontend/Pages/Auth.razor +++ b/testFrontend/SafeNSound.Frontend/Pages/Auth.razor
@@ -3,13 +3,13 @@ <h1>Auth</h1> <u>User:</u><br/> <span>Username (L?, R): </span> -<FancyTextBox @bind-Value="@Username"/><br/> +<FancyTextBox @bind-Value="@AuthData.Username"/><br/> <span>Email (L? R): </span> -<FancyTextBox @bind-Value="@Email"/><br/> +<FancyTextBox @bind-Value="@AuthData.Email"/><br/> <span>Password (L, R): </span> -<FancyTextBox @bind-Value="@Password" IsPassword="true"/><br/> +<FancyTextBox @bind-Value="@AuthData.Password" IsPassword="true"/><br/> <span>Type (R): </span> -<FancyTextBox @bind-Value="@UserType"/><span> (one of user|monitor|admin)</span><br/> +<FancyTextBox @bind-Value="@AuthData.UserType"/><span> (one of user|monitor|admin)</span><br/> <LinkButton OnClick="@Randomise">Randomise</LinkButton> <LinkButton OnClick="@Register">Register</LinkButton> <LinkButton OnClick="@Login">Login</LinkButton> @@ -24,6 +24,20 @@ <LinkButton OnClick="@GetAssignedUsers">Get</LinkButton> <LinkButton OnClick="@AddAssignedUser">Add</LinkButton> <LinkButton OnClick="@RemoveAssignedUser">Remove</LinkButton> +<br/><br/> + +<u>Devices:</u><br/> +@if (CurrentDevice is not null) { + <span>Device ID: @CurrentDevice.Id</span><br/> + <span>Log in date: @CurrentDevice.CreatedAt</span><br/> + <span>Last seen: @CurrentDevice.LastSeen</span><br/> + <span>Device name: </span> + <FancyTextBox @bind-Value="@CurrentDevice.Name"/><br/> + <LinkButton OnClick="@GetDevice">Get</LinkButton> + <LinkButton OnClick="@UpdateDevice">Update</LinkButton> + <LinkButton OnClick="@DeleteDevice">Delete</LinkButton> + <LinkButton OnClick="@GetAllDevices">Get all</LinkButton> +} @if (Exception != null) { <div class="alert alert-danger"> @@ -44,10 +58,15 @@ } @code { - private string Username { get; set; } = string.Empty; - private string Email { get; set; } = string.Empty; - private string Password { get; set; } = string.Empty; - private string UserType { get; set; } = string.Empty; + + private RegisterDto AuthData { get; set; } = new() { + Username = string.Empty, + UserType = string.Empty, + Email = String.Empty, + Password = string.Empty + }; + + private DeviceDto? CurrentDevice { get; set; } private string TargetUserId { get; set; } = string.Empty; @@ -55,10 +74,10 @@ private object? Result { get; set; } private async Task Randomise() { - Username = Guid.NewGuid().ToString(); - Email = Guid.NewGuid() + "@example.com"; - Password = Guid.NewGuid().ToString(); - UserType = Random.Shared.GetItems(["user", "monitor", "admin"], 1)[0]; + AuthData.Username = Guid.NewGuid().ToString(); + AuthData.Email = Guid.NewGuid() + "@example.com"; + AuthData.Password = Guid.NewGuid().ToString(); + AuthData.UserType = Random.Shared.GetItems(["user", "monitor", "admin"], 1)[0]; StateHasChanged(); } @@ -67,10 +86,10 @@ Exception = null; try { await Authentication.Register(new() { - Username = Username, - Password = Password, - Email = Email, - UserType = UserType + Username = AuthData.Username, + Password = AuthData.Password, + Email = AuthData.Email, + UserType = AuthData.UserType }); } catch (Exception ex) { @@ -86,11 +105,14 @@ try { AuthResult result; Result = result = await Authentication.Login(new() { - Username = Username, - Password = Password, - Email = Email + Username = AuthData.Username, + Password = AuthData.Password, + Email = AuthData.Email }); App.Client = new SafeNSoundClient(Config, result.AccessToken); + CurrentDevice = await App.Client.GetDevice( + (await App.Client.WhoAmI()).DeviceId + ); } catch (Exception ex) { Exception = ex; @@ -104,9 +126,9 @@ Exception = null; try { await Authentication.Delete(new() { - Username = Username, - Password = Password, - Email = Email + Username = AuthData.Username, + Password = AuthData.Password, + Email = AuthData.Email }); } catch (Exception ex) { @@ -125,6 +147,7 @@ catch (Exception ex) { Exception = ex; } + StateHasChanged(); } @@ -137,6 +160,7 @@ catch (Exception ex) { Exception = ex; } + StateHasChanged(); } @@ -150,6 +174,7 @@ catch (Exception ex) { Exception = ex; } + StateHasChanged(); } @@ -163,6 +188,7 @@ catch (Exception ex) { Exception = ex; } + StateHasChanged(); } @@ -185,6 +211,28 @@ catch (Exception ex) { Exception = ex; } + + StateHasChanged(); + } + + private async Task GetDevice() { + Result = CurrentDevice = await App.Client!.GetDevice(CurrentDevice!.Id!); + StateHasChanged(); + } + + private async Task UpdateDevice() { + await App.Client!.UpdateDevice(CurrentDevice!.Id!, new() { + Name = CurrentDevice.Name + }); + await GetDevice(); + } + + private async Task DeleteDevice() { + await App.Client!.DeleteDevice(CurrentDevice!.Id!); + } + + private async Task GetAllDevices() { + Result = await App.Client!.GetDevices(); StateHasChanged(); } diff --git a/testFrontend/SafeNSound.Sdk/SafeNSoundClient.cs b/testFrontend/SafeNSound.Sdk/SafeNSoundClient.cs
index 8291178..9ea8073 100644 --- a/testFrontend/SafeNSound.Sdk/SafeNSoundClient.cs +++ b/testFrontend/SafeNSound.Sdk/SafeNSoundClient.cs
@@ -4,24 +4,20 @@ using System.Text.Json.Serialization; namespace SafeNSound.Sdk; -public class SafeNSoundClient(SafeNSoundConfiguration config, string accessToken) -{ - public WrappedHttpClient HttpClient { get; } = new() - { +public class SafeNSoundClient(SafeNSoundConfiguration config, string accessToken) { + public WrappedHttpClient HttpClient { get; } = new() { BaseAddress = new Uri(config.BaseUri), - DefaultRequestHeaders = - { + DefaultRequestHeaders = { Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken) } }; - - public async Task<WhoAmI> WhoAmI() - { + + public async Task<WhoAmI> WhoAmI() { var res = await HttpClient.GetAsync("/auth/whoami"); res.EnsureSuccessStatusCode(); return (await res.Content.ReadFromJsonAsync<WhoAmI>())!; } - + #region Alarm public async Task<AlarmDto> GetAlarm(string userId = "@me") { @@ -29,25 +25,21 @@ public class SafeNSoundClient(SafeNSoundConfiguration config, string accessToken res.EnsureSuccessStatusCode(); return (await res.Content.ReadFromJsonAsync<AlarmDto>())!; } - + public async Task SetAlarm(AlarmDto alarm, string userId = "@me") { var res = await HttpClient.PutAsJsonAsync("/alarm/@me", alarm); res.EnsureSuccessStatusCode(); } - + public async Task DeleteAlarm(string userId = "@me") { var res = await HttpClient.DeleteAsync($"/alarm/{userId}"); res.EnsureSuccessStatusCode(); } - - #endregion #region Budget - - #endregion public async Task<Dictionary<string, AlarmDto>> GetAllAlarms() { @@ -89,13 +81,48 @@ public class SafeNSoundClient(SafeNSoundConfiguration config, string accessToken var res = await HttpClient.PostAsync("/admin/monitorAllUsers", null); res.EnsureSuccessStatusCode(); } -} + public async Task<List<DeviceDto>> GetDevices() { + var res = await HttpClient.GetAsync("/auth/devices"); + res.EnsureSuccessStatusCode(); + return (await res.Content.ReadFromJsonAsync<List<DeviceDto>>())!; + } + + public async Task<DeviceDto> GetDevice(string deviceId) { + var res = await HttpClient.GetAsync($"/auth/devices/{deviceId}"); + res.EnsureSuccessStatusCode(); + return (await res.Content.ReadFromJsonAsync<DeviceDto>())!; + } + + public async Task DeleteDevice(string deviceId) { + var res = await HttpClient.DeleteAsync($"/auth/devices/{deviceId}"); + res.EnsureSuccessStatusCode(); + } + + public async Task UpdateDevice(string deviceId, DeviceDto device) { + var res = await HttpClient.PatchAsJsonAsync($"/auth/devices/{deviceId}", device); + res.EnsureSuccessStatusCode(); + } +} public class AlarmDto { [JsonPropertyName("reason")] public required string Reason { get; set; } - + [JsonPropertyName("createdAt")] public DateTime CreatedAt { get; set; } +} + +public class DeviceDto { + [JsonPropertyName("_id")] + public string? Id { get; set; } + + [JsonPropertyName("name")] + public string? Name { get; set; } + + [JsonPropertyName("createdAt")] + public DateTime CreatedAt { get; set; } + + [JsonPropertyName("lastSeen")] + public DateTime LastSeen { get; set; } } \ No newline at end of file