diff --git a/Azaion.Api/Program.cs b/Azaion.Api/Program.cs index 80f5fb4..9186e1e 100644 --- a/Azaion.Api/Program.cs +++ b/Azaion.Api/Program.cs @@ -8,6 +8,7 @@ using FluentValidation; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Rewrite; using Microsoft.IdentityModel.Tokens; using Microsoft.OpenApi.Models; using Serilog; @@ -117,6 +118,8 @@ if (app.Environment.IsDevelopment()) app.UseAuthentication(); app.UseAuthorization(); +app.UseRewriter(new RewriteOptions().AddRedirect("^$", "/swagger")); + app.MapPost("/login", async (LoginRequest request, IUserService userService, IAuthService authService, CancellationToken cancellationToken) => { @@ -131,6 +134,11 @@ app.MapPost("/users", .RequireAuthorization(apiAdminPolicy) .WithOpenApi(op => new(op){ Summary = "Creates a new user"}); +app.MapGet("/currentuser", + async (IAuthService authService) => await authService.GetCurrentUser()) + .RequireAuthorization() + .WithOpenApi(op => new(op){ Summary = "Get Current User"}); + app.MapGet("/users", async (string? searchEmail, RoleEnum? searchRole, IUserService userService, CancellationToken cancellationToken) => await userService.GetUsers(searchEmail, searchRole, cancellationToken)) @@ -142,6 +150,7 @@ app.MapPost("/resources/{dataFolder?}", => await resourceService.SaveResource(dataFolder, data, cancellationToken)) .Accepts("multipart/form-data") .RequireAuthorization() + //.WithOpenApi(op => new(op){ Summary = "Upload resource"}); //For some reason doesn't work when this is specified. .DisableAntiforgery(); app.MapPost("/resources/get/{dataFolder?}", //Need to have POST method for secure password @@ -166,4 +175,11 @@ app.MapPut("/resources/reset-hardware", .RequireAuthorization(apiAdminPolicy) .WithOpenApi(op => new OpenApiOperation(op){ Summary = "Resets hardware id in case of hardware change"}); +app.MapPut("/users/queue-offsets/{email}", + async ([FromRoute] string email, UserQueueOffsets offsets, IUserService userService, CancellationToken cancellationToken) + => await userService.UpdateQueueOffsets(email, offsets, cancellationToken)) + .RequireAuthorization() + .WithOpenApi(op => new OpenApiOperation(op) { Summary = "Updates user queue offsets" }); + + app.Run(); diff --git a/Azaion.Common/Azaion.Common.csproj b/Azaion.Common/Azaion.Common.csproj index 466dfac..2ca3088 100644 --- a/Azaion.Common/Azaion.Common.csproj +++ b/Azaion.Common/Azaion.Common.csproj @@ -10,6 +10,7 @@ + diff --git a/Azaion.Common/Database/AzaionDbShemaHolder.cs b/Azaion.Common/Database/AzaionDbShemaHolder.cs index 822a67d..24fc31e 100644 --- a/Azaion.Common/Database/AzaionDbShemaHolder.cs +++ b/Azaion.Common/Database/AzaionDbShemaHolder.cs @@ -2,6 +2,7 @@ using Azaion.Common.Extensions; using LinqToDB; using LinqToDB.Mapping; +using Newtonsoft.Json; namespace Azaion.Common.Database; @@ -28,7 +29,12 @@ public static class AzaionDbSchemaHolder .HasDataType(DataType.Guid) .Property(x => x.Role) .HasDataType(DataType.Text) - .HasConversion(v => v.ToString(), v => (RoleEnum)Enum.Parse(typeof(RoleEnum), v)); + .HasConversion(v => v.ToString(), v => (RoleEnum)Enum.Parse(typeof(RoleEnum), v)) + .Property(x => x.UserConfig) + .HasConversion( + v => v == null ? null : JsonConvert.SerializeObject(v), + p => string.IsNullOrEmpty(p) ? new UserConfig() : JsonConvert.DeserializeObject(p)) + .IsNullable(); builder.Build(); diff --git a/Azaion.Common/Entities/User.cs b/Azaion.Common/Entities/User.cs index 44c88fc..67d73fb 100644 --- a/Azaion.Common/Entities/User.cs +++ b/Azaion.Common/Entities/User.cs @@ -8,5 +8,19 @@ public class User public string? Hardware { get; set; } public RoleEnum Role { get; set; } + public UserConfig? UserConfig { get; set; } = null!; + public static string GetCacheKey(string email) => $"{nameof(User)}.{email}"; +} + +public class UserConfig +{ + public UserQueueOffsets? QueueConfig { get; set; } = new(); +} + +public class UserQueueOffsets +{ + public int AnnotationsOffset { get; set; } + public int AnnotationsConfirmOffset { get; set; } + public int AnnotationsCommandsOffset { get; set; } } \ No newline at end of file diff --git a/Azaion.Services/UserService.cs b/Azaion.Services/UserService.cs index 54bf7a5..eca7a65 100644 --- a/Azaion.Services/UserService.cs +++ b/Azaion.Services/UserService.cs @@ -15,6 +15,7 @@ public interface IUserService Task GetById(Guid? id, CancellationToken cancellationToken = default); Task GetByEmail(string email, CancellationToken cancellationToken = default); Task UpdateHardware(string email, HardwareInfo hardwareInfo, CancellationToken cancellationToken = default); + Task UpdateQueueOffsets(string email, UserQueueOffsets queueOffsets, CancellationToken cancellationToken = default); Task> GetUsers(string? searchEmail, RoleEnum? searchRole, CancellationToken cancellationToken); Task CheckHardwareHash(User user, GetResourceRequest request); } @@ -77,6 +78,23 @@ public class UserService(IDbFactory dbFactory, ICache cache) : IUserService cache.Invalidate(User.GetCacheKey(email)); } + public async Task UpdateQueueOffsets(string email, UserQueueOffsets queueOffsets, CancellationToken cancellationToken = default) + { + await dbFactory.RunAdmin(async db => + { + var userConfig = await db.Users.Where(x => x.Email == email).Select(x => x.UserConfig).FirstOrDefaultAsync(token: cancellationToken); + userConfig ??= new UserConfig(); + userConfig.QueueConfig = queueOffsets; + + await db.Users.UpdateAsync(x => x.Email == email, + u => new User + { + UserConfig = userConfig + }, token: cancellationToken); + }); + cache.Invalidate(User.GetCacheKey(email)); + } + public async Task> GetUsers(string? searchEmail, RoleEnum? searchRole, CancellationToken cancellationToken) => await dbFactory.Run(async db => await db.Users diff --git a/env/api/env.ps1 b/env/api/env.ps1 new file mode 100644 index 0000000..40c842a --- /dev/null +++ b/env/api/env.ps1 @@ -0,0 +1,11 @@ +f (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { + $arguments = "-Command `"& { Set-ExecutionPolicy Bypass -Scope Process -Force; & '$PSCommand' $($args -join ' ') }`"" + Start-Process -Verb RunAs -FilePath "powershell.exe" -ArgumentList $arguments + exit # Terminate the current non-elevated script +} + +& "setx" "foo" "bar" + +setx ASPNETCORE_ConnectionStrings__AzaionDb Host=localhost;Database=azaion;Username=azaion_reader;Password=Az@1on_re@d!only@$Az; +setx ASPNETCORE_ConnectionStrings__AzaionDbAdmin Host=localhost;Database=azaion;Username=azaion_admin;Password=Az@1on_admin$$@r; +setx ASPNETCORE_JwtConfig__Secret sdkfjghbsdfklhjgvbsdkljfhbvasklhfgsdfvh \ No newline at end of file diff --git a/env/db/02_structure.sql b/env/db/02_structure.sql index 586a74e..5e392a4 100644 --- a/env/db/02_structure.sql +++ b/env/db/02_structure.sql @@ -7,7 +7,8 @@ create table users password_hash varchar(255) not null, hardware text null, hardware_hash varchar(120) null, - role varchar(20) not null + role varchar(20) not null, + user_config varchar(512) null ); grant select, insert, update, delete on public.users to azaion_admin; grant select on table public.users to azaion_reader;