healthchecks
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,5 +1,5 @@
|
|||||||
*.db
|
*.db
|
||||||
back/data/
|
back/.program_data/
|
||||||
## Ignore Visual Studio temporary files, build results, and
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
## files generated by popular Visual Studio add-ons.
|
## files generated by popular Visual Studio add-ons.
|
||||||
##
|
##
|
||||||
|
Binary file not shown.
Binary file not shown.
@@ -1 +1,5 @@
|
|||||||
{"Email":"@system","Key":"caeae1bc-3761-4b30-8627-d86af99b0a4f","Password":"M8I^7b,UF!)PIQ.A"}
|
{
|
||||||
|
"email": "sys@t.em",
|
||||||
|
"key": "c1d6bd4e-ac32-4859-b2f5-fcda1c190934",
|
||||||
|
"password": "Tx,bA%8KPn_dç8v["
|
||||||
|
}
|
9
back/DTO/UserDto.cs
Normal file
9
back/DTO/UserDto.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using back.DataModels;
|
||||||
|
|
||||||
|
namespace back.DTO;
|
||||||
|
|
||||||
|
public class UserDto
|
||||||
|
{
|
||||||
|
public string Id { get; set; } = null!;
|
||||||
|
public ICollection<Role> Roles { get; set; } = [];
|
||||||
|
}
|
@@ -1,11 +1,12 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
using Transactional.Abstractions;
|
using Transactional.Abstractions;
|
||||||
|
using Transactional.Abstractions.Interfaces;
|
||||||
|
|
||||||
namespace back.DataModels;
|
namespace back.DataModels;
|
||||||
|
|
||||||
[Table("Persons")]
|
[Table("Persons")]
|
||||||
public partial class Person: IEquatable<Person>, ISoftDeletable
|
public partial class Person: IEntity<Person>, ISoftDeletable
|
||||||
{
|
{
|
||||||
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
public string Id { get; set; } = null!;
|
public string Id { get; set; } = null!;
|
||||||
@@ -26,6 +27,7 @@ public partial class Person: IEquatable<Person>, ISoftDeletable
|
|||||||
public virtual User? User { get; set; }
|
public virtual User? User { get; set; }
|
||||||
public virtual ICollection<Photo> PhotosNavigation { get; set; } = [];
|
public virtual ICollection<Photo> PhotosNavigation { get; set; } = [];
|
||||||
|
|
||||||
|
|
||||||
public override int GetHashCode() => HashCode.Combine(Id, Name);
|
public override int GetHashCode() => HashCode.Combine(Id, Name);
|
||||||
|
|
||||||
public override bool Equals(object? obj)
|
public override bool Equals(object? obj)
|
||||||
@@ -38,6 +40,23 @@ public partial class Person: IEquatable<Person>, ISoftDeletable
|
|||||||
return
|
return
|
||||||
Id == other.Id || GetHashCode() == other.GetHashCode();
|
Id == other.Id || GetHashCode() == other.GetHashCode();
|
||||||
}
|
}
|
||||||
|
public bool IsNull => this is null;
|
||||||
|
|
||||||
|
public object Clone() => (Person)MemberwiseClone();
|
||||||
|
|
||||||
|
public int CompareTo(object? obj)
|
||||||
|
{
|
||||||
|
if(obj is null) return 1;
|
||||||
|
if (obj is not Person other) throw new ArgumentException("Object is not a Person");
|
||||||
|
return CompareTo(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int CompareTo(Person? other)
|
||||||
|
{
|
||||||
|
if (other is null) return 1;
|
||||||
|
if (ReferenceEquals(this, other)) return 0;
|
||||||
|
return string.Compare(Id, other.Id, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
public const string SystemPersonId = "00000000-0000-0000-0000-000000000001";
|
public const string SystemPersonId = "00000000-0000-0000-0000-000000000001";
|
||||||
|
|
||||||
|
@@ -1,10 +1,11 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
using Transactional.Abstractions.Interfaces;
|
||||||
|
|
||||||
namespace back.DataModels;
|
namespace back.DataModels;
|
||||||
|
|
||||||
[Table("Photos")]
|
[Table("Photos")]
|
||||||
public partial class Photo : IEquatable<Photo>
|
public partial class Photo : IEntity<Photo>
|
||||||
{
|
{
|
||||||
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
public string Id { get; set; } = null!;
|
public string Id { get; set; } = null!;
|
||||||
@@ -44,4 +45,22 @@ public partial class Photo : IEquatable<Photo>
|
|||||||
return
|
return
|
||||||
Id == other.Id || GetHashCode() == other.GetHashCode();
|
Id == other.Id || GetHashCode() == other.GetHashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsNull => this is null;
|
||||||
|
|
||||||
|
public object Clone() => (Photo)MemberwiseClone();
|
||||||
|
|
||||||
|
public int CompareTo(object? obj)
|
||||||
|
{
|
||||||
|
if (obj is null) return 1;
|
||||||
|
if (obj is not Photo other) throw new ArgumentException("Object is not a Person");
|
||||||
|
return CompareTo(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int CompareTo(Photo? other)
|
||||||
|
{
|
||||||
|
if (other is null) return 1;
|
||||||
|
if (ReferenceEquals(this, other)) return 0;
|
||||||
|
return string.Compare(Id, other.Id, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
}
|
}
|
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
public class SystemKey
|
public class SystemKey
|
||||||
{
|
{
|
||||||
public string Email { get; set; } = "@system";
|
public string Email { get; set; } = User.SystemUser.Email;
|
||||||
public string Key { get; set; } = Guid.NewGuid().ToString();
|
public string Key { get; set; } = Guid.NewGuid().ToString();
|
||||||
public required string Password { get; set; }
|
public required string Password { get; set; }
|
||||||
|
|
||||||
|
@@ -1,10 +1,12 @@
|
|||||||
|
using back.DTO;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
using Transactional.Abstractions.Interfaces;
|
||||||
|
|
||||||
namespace back.DataModels;
|
namespace back.DataModels;
|
||||||
|
|
||||||
[Table("Users")]
|
[Table("Users")]
|
||||||
public class User : IEquatable<User>
|
public class User : IEntity<User>
|
||||||
{
|
{
|
||||||
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
public string Id { get; set; } = null!;
|
public string Id { get; set; } = null!;
|
||||||
@@ -31,6 +33,12 @@ public class User : IEquatable<User>
|
|||||||
CreatedAt = createdAt.ToString("dd-MM-yyyy HH:mm:ss zz");
|
CreatedAt = createdAt.ToString("dd-MM-yyyy HH:mm:ss zz");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public UserDto ToDto() => new()
|
||||||
|
{
|
||||||
|
Id = Id,
|
||||||
|
Roles = Roles
|
||||||
|
};
|
||||||
|
|
||||||
public bool IsAdmin() => Roles.Any(r => r.IsAdmin());
|
public bool IsAdmin() => Roles.Any(r => r.IsAdmin());
|
||||||
public bool IsContentManager() => Roles.Any(r => r.IsContentManager());
|
public bool IsContentManager() => Roles.Any(r => r.IsContentManager());
|
||||||
public bool IsUser() => Roles.Any(r => r.IsUser());
|
public bool IsUser() => Roles.Any(r => r.IsUser());
|
||||||
@@ -46,12 +54,33 @@ public class User : IEquatable<User>
|
|||||||
return Id == other.Id && Email == other.Email;
|
return Id == other.Id && Email == other.Email;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsNull => this is null;
|
||||||
|
|
||||||
|
public object Clone() => (User)MemberwiseClone();
|
||||||
|
|
||||||
|
public int CompareTo(object? obj)
|
||||||
|
{
|
||||||
|
if (obj is null) return 1;
|
||||||
|
if (obj is not User other) throw new ArgumentException("Object is not a Person");
|
||||||
|
return CompareTo(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int CompareTo(User? other)
|
||||||
|
{
|
||||||
|
if (other is null) return 1;
|
||||||
|
if (ReferenceEquals(this, other)) return 0;
|
||||||
|
return string.Compare(Id, other.Id, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
public const string SystemUserId = "00000000-0000-0000-0000-000000000001";
|
public const string SystemUserId = "00000000-0000-0000-0000-000000000001";
|
||||||
|
|
||||||
public static readonly User SystemUser = new(
|
public static readonly User SystemUser = new(
|
||||||
id: SystemUserId,
|
id: SystemUserId,
|
||||||
email: "@system",
|
email: "sys@t.em",
|
||||||
password: "",
|
password: "",
|
||||||
createdAt: DateTime.UtcNow
|
createdAt: DateTime.UtcNow
|
||||||
);
|
)
|
||||||
|
{
|
||||||
|
Roles = [Role.AdminRole, Role.ContentManagerRole, Role.UserRole]
|
||||||
|
};
|
||||||
}
|
}
|
@@ -1,4 +1,5 @@
|
|||||||
using back.ServicesExtensions;
|
using back.ServicesExtensions;
|
||||||
|
using healthchecks;
|
||||||
|
|
||||||
namespace back;
|
namespace back;
|
||||||
|
|
||||||
@@ -11,6 +12,13 @@ public class Program
|
|||||||
builder.Services.UseExtensions();
|
builder.Services.UseExtensions();
|
||||||
|
|
||||||
builder.Services.AddControllers();
|
builder.Services.AddControllers();
|
||||||
|
|
||||||
|
builder.Services.AddHealthChecks(options => {
|
||||||
|
options.CacheDuration = TimeSpan.FromMinutes(30);
|
||||||
|
options.Timeout = TimeSpan.FromSeconds(5);
|
||||||
|
options.AssembliesToScan = [typeof(Program).Assembly];
|
||||||
|
}).DiscoverHealthChecks();
|
||||||
|
|
||||||
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
|
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
|
||||||
builder.Services.AddSwaggerGen();
|
builder.Services.AddSwaggerGen();
|
||||||
|
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
using back.persistance.data;
|
using back.persistance.data;
|
||||||
using back.persistance.data.repositories;
|
using System.Text.Json.Serialization;
|
||||||
using back.persistance.data.repositories.Abstracts;
|
|
||||||
using back.services.engine.SystemUser;
|
using back.services.engine.SystemUser;
|
||||||
using DependencyInjector;
|
using DependencyInjector;
|
||||||
|
using System.Text.Json;
|
||||||
using Transactional.Abstractions.Interfaces;
|
using Transactional.Abstractions.Interfaces;
|
||||||
using Transactional.Implementations.EntityFramework;
|
using Transactional.Implementations.EntityFramework;
|
||||||
|
|
||||||
@@ -21,6 +21,23 @@ public static partial class ServicesExtensions
|
|||||||
services.AddServices();
|
services.AddServices();
|
||||||
|
|
||||||
services.AddScoped<ITransactionalService<DataContext>, EntityFrameworkTransactionalService<DataContext>>();
|
services.AddScoped<ITransactionalService<DataContext>, EntityFrameworkTransactionalService<DataContext>>();
|
||||||
|
|
||||||
|
services.AddSingleton(new JsonSerializerOptions {
|
||||||
|
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
|
||||||
|
AllowTrailingCommas = true,
|
||||||
|
PropertyNameCaseInsensitive = true,
|
||||||
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||||
|
WriteIndented = true,
|
||||||
|
Converters = {
|
||||||
|
new JsonStringEnumConverter(JsonNamingPolicy.CamelCase),
|
||||||
|
},
|
||||||
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||||
|
DictionaryKeyPolicy = JsonNamingPolicy.CamelCase,
|
||||||
|
NumberHandling = JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString,
|
||||||
|
ReadCommentHandling = JsonCommentHandling.Skip,
|
||||||
|
UnmappedMemberHandling = JsonUnmappedMemberHandling.Skip,
|
||||||
|
UnknownTypeHandling = JsonUnknownTypeHandling.JsonElement,
|
||||||
|
});
|
||||||
|
|
||||||
using var scope = services.BuildServiceProvider().CreateScope();
|
using var scope = services.BuildServiceProvider().CreateScope();
|
||||||
scope.ServiceProvider
|
scope.ServiceProvider
|
||||||
|
@@ -7,8 +7,11 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Azure.Identity" Version="1.14.2" />
|
<PackageReference Include="Azure.Identity" Version="1.15.0" />
|
||||||
<PackageReference Include="MailKit" Version="4.13.0" />
|
<PackageReference Include="MailKit" Version="4.13.0" />
|
||||||
|
<PackageReference Include="Mapster" Version="7.4.0" />
|
||||||
|
<PackageReference Include="Mapster.DependencyInjection" Version="1.0.1" />
|
||||||
|
<PackageReference Include="Mapster.EFCore" Version="5.1.1" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.8" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.8" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.8" />
|
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.8" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.8" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.8" />
|
||||||
@@ -31,9 +34,10 @@
|
|||||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="9.0.8" />
|
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="9.0.8" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.8" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.8" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="9.0.8" />
|
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="9.0.8" />
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
|
||||||
<PackageReference Include="Oracle.EntityFrameworkCore" Version="9.23.90" />
|
<PackageReference Include="Oracle.EntityFrameworkCore" Version="9.23.90" />
|
||||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.3" />
|
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="9.0.0" />
|
||||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.11" />
|
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.11" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="9.0.3" />
|
<PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="9.0.3" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="9.0.3" />
|
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="9.0.3" />
|
||||||
@@ -44,6 +48,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\nuget\DependencyInjector\DependencyInjector.csproj" />
|
<ProjectReference Include="..\..\nuget\DependencyInjector\DependencyInjector.csproj" />
|
||||||
|
<ProjectReference Include="..\..\nuget\healthchecks\healthchecks.csproj" />
|
||||||
<ProjectReference Include="..\..\nuget\Transactional\Transactional.csproj" />
|
<ProjectReference Include="..\..\nuget\Transactional\Transactional.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
@@ -9,6 +9,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Transactional", "..\..\nuge
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DependencyInjector", "..\..\nuget\DependencyInjector\DependencyInjector.csproj", "{DBDF84A4-235C-4F29-8626-5BD1DC255970}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DependencyInjector", "..\..\nuget\DependencyInjector\DependencyInjector.csproj", "{DBDF84A4-235C-4F29-8626-5BD1DC255970}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "healthchecks", "..\..\nuget\healthchecks\healthchecks.csproj", "{B21E2BEF-17B7-4981-9843-C0CC36D67010}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -27,6 +29,10 @@ Global
|
|||||||
{DBDF84A4-235C-4F29-8626-5BD1DC255970}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{DBDF84A4-235C-4F29-8626-5BD1DC255970}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{DBDF84A4-235C-4F29-8626-5BD1DC255970}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{DBDF84A4-235C-4F29-8626-5BD1DC255970}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{DBDF84A4-235C-4F29-8626-5BD1DC255970}.Release|Any CPU.Build.0 = Release|Any CPU
|
{DBDF84A4-235C-4F29-8626-5BD1DC255970}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{B21E2BEF-17B7-4981-9843-C0CC36D67010}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{B21E2BEF-17B7-4981-9843-C0CC36D67010}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{B21E2BEF-17B7-4981-9843-C0CC36D67010}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{B21E2BEF-17B7-4981-9843-C0CC36D67010}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
using back.DataModels;
|
using back.DataModels;
|
||||||
|
using back.DTO;
|
||||||
using back.services.bussines;
|
using back.services.bussines;
|
||||||
using back.services.bussines.UserService;
|
using back.services.bussines.UserService;
|
||||||
|
using Mapster;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace back.controllers;
|
namespace back.controllers;
|
||||||
@@ -44,14 +46,14 @@ public class UsersController(IUserService user) : ControllerBase
|
|||||||
if (user == null || string.IsNullOrEmpty(user.Email) || string.IsNullOrEmpty(user.Password))
|
if (user == null || string.IsNullOrEmpty(user.Email) || string.IsNullOrEmpty(user.Password))
|
||||||
return BadRequest(Errors.BadRequest.Description);
|
return BadRequest(Errors.BadRequest.Description);
|
||||||
|
|
||||||
if (user.Email.Equals("@system", StringComparison.InvariantCultureIgnoreCase))
|
if (user.Email.Equals(DataModels.User.SystemUser.Email, StringComparison.InvariantCultureIgnoreCase))
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(user.SystemKey))
|
if (string.IsNullOrEmpty(user.SystemKey))
|
||||||
return Unauthorized(Errors.Unauthorized.Description);
|
return Unauthorized(Errors.Unauthorized.Description);
|
||||||
var systemUser = await _user.ValidateSystemUser(user.Email, user.Password, user.SystemKey, clientId);
|
var systemUser = await _user.ValidateSystemUser(user.Email, user.Password, user.SystemKey, clientId);
|
||||||
if (systemUser == null)
|
if (systemUser == null)
|
||||||
return Unauthorized(Errors.Unauthorized.Description);
|
return Unauthorized(Errors.Unauthorized.Description);
|
||||||
return Ok(systemUser);
|
return Ok(systemUser.Adapt<UserDto>());
|
||||||
}
|
}
|
||||||
|
|
||||||
var existingUser = await _user.Login(user.Email, user.Password, clientId);
|
var existingUser = await _user.Login(user.Email, user.Password, clientId);
|
||||||
|
50
back/healthchecks/sqlite.cs
Normal file
50
back/healthchecks/sqlite.cs
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
using back.Options;
|
||||||
|
using healthchecks;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
namespace back.healthchecks;
|
||||||
|
|
||||||
|
[HealthCheckExecutionOptions(retryAttempts: 2, timeout: "00:00:05", retryDelay: "00:00:01", severity: HealthCheckSeverity.Critical)]
|
||||||
|
public class SqliteHealthCheck : IHealthCheck
|
||||||
|
{
|
||||||
|
private readonly DatabaseConfig config;
|
||||||
|
public SqliteHealthCheck(IOptionsMonitor<DatabaseConfig> optionsSnapshot)
|
||||||
|
{
|
||||||
|
config = optionsSnapshot.Get(DatabaseConfig.DataStorage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<HealthCheckResult> CheckAsync(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
// check if can connect to sqlite database
|
||||||
|
// then run a query to Users table to see if User.SystemUser exists
|
||||||
|
var isHealthy = false;
|
||||||
|
var details = string.Empty;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var connection = new Microsoft.Data.Sqlite.SqliteConnection(config.ConnectionString);
|
||||||
|
connection.Open();
|
||||||
|
using var command = connection.CreateCommand();
|
||||||
|
command.CommandText = $"SELECT COUNT(1) FROM Users WHERE Id = '{DataModels.User.SystemUserId}';";
|
||||||
|
var result = command.ExecuteScalar();
|
||||||
|
if (result != null && Convert.ToInt32(result) == 1)
|
||||||
|
{
|
||||||
|
isHealthy = true;
|
||||||
|
details = "Connection to SQLite database successful and SystemUser exists.";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
details = "Connection to SQLite database successful but SystemUser does not exist.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
details = $"Failed to connect to SQLite database: {ex.Message}";
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.FromResult(new HealthCheckResult(isHealthy, null)
|
||||||
|
{
|
||||||
|
Details = details,
|
||||||
|
Severity = isHealthy ? HealthCheckSeverity.Info : HealthCheckSeverity.Critical
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -87,8 +87,15 @@ public class FileSystemImageStorageService(
|
|||||||
{
|
{
|
||||||
throw new InvalidOperationException($"File {fileName} already exists. Use Update for updating file info.");
|
throw new InvalidOperationException($"File {fileName} already exists. Use Update for updating file info.");
|
||||||
}
|
}
|
||||||
using var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read);
|
using var fileStream = new FileStream(path, options: new FileStreamOptions {
|
||||||
|
Access = FileAccess.Write,
|
||||||
|
BufferSize = 4096,
|
||||||
|
Mode = FileMode.OpenOrCreate,
|
||||||
|
Share = FileShare.Read,
|
||||||
|
});
|
||||||
|
blobStream.Seek(0, SeekOrigin.Begin);
|
||||||
await blobStream.CopyToAsync(fileStream);
|
await blobStream.CopyToAsync(fileStream);
|
||||||
|
blobStream.Seek(0, SeekOrigin.Begin);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Update(Stream blobStream, string fileName)
|
public async Task Update(Stream blobStream, string fileName)
|
||||||
|
@@ -4,6 +4,7 @@ using Transactional.Abstractions.Interfaces;
|
|||||||
|
|
||||||
namespace back.persistance.data.repositories.Abstracts;
|
namespace back.persistance.data.repositories.Abstracts;
|
||||||
|
|
||||||
public interface IPersonRepository : IRepository<Person, string>, IScoped
|
public interface IPersonRepository : IRepository<Person>, IScoped
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
@@ -4,5 +4,5 @@ using Transactional.Abstractions.Interfaces;
|
|||||||
|
|
||||||
namespace back.persistance.data.repositories.Abstracts;
|
namespace back.persistance.data.repositories.Abstracts;
|
||||||
|
|
||||||
public interface IPhotoRepository : IRepository<Photo, string>, IScoped
|
public interface IPhotoRepository : IRepository<Photo>, IScoped
|
||||||
{ }
|
{ }
|
||||||
|
@@ -4,7 +4,7 @@ using Transactional.Abstractions.Interfaces;
|
|||||||
|
|
||||||
namespace back.persistance.data.repositories.Abstracts;
|
namespace back.persistance.data.repositories.Abstracts;
|
||||||
|
|
||||||
public interface IUserRepository : IRepository<User, string>, IScoped
|
public interface IUserRepository : IRepository<User>, IScoped
|
||||||
{
|
{
|
||||||
Task<User?> GetByEmail(string email);
|
Task<User?> GetByEmail(string email);
|
||||||
Task<string?> GetUserSaltByEmail(string email);
|
Task<string?> GetUserSaltByEmail(string email);
|
||||||
|
@@ -4,7 +4,7 @@ using Transactional.Implementations.EntityFramework;
|
|||||||
|
|
||||||
namespace back.persistance.data.repositories;
|
namespace back.persistance.data.repositories;
|
||||||
|
|
||||||
public class PersonRepository(DataContext context) : ReadWriteRepository<Person, string>(context), IPersonRepository
|
public class PersonRepository(DataContext context) : ReadWriteRepository<Person>(context), IPersonRepository
|
||||||
{
|
{
|
||||||
// Implement methods specific to Photo repository if needed
|
// Implement methods specific to Photo repository if needed
|
||||||
}
|
}
|
@@ -4,7 +4,7 @@ using Transactional.Implementations.EntityFramework;
|
|||||||
|
|
||||||
namespace back.persistance.data.repositories;
|
namespace back.persistance.data.repositories;
|
||||||
|
|
||||||
public class PhotoRepository(DataContext context) : ReadWriteRepository<Photo, string>(context), IPhotoRepository
|
public class PhotoRepository(DataContext context) : ReadWriteRepository<Photo>(context), IPhotoRepository
|
||||||
{
|
{
|
||||||
// Implement methods specific to Photo repository if needed
|
// Implement methods specific to Photo repository if needed
|
||||||
}
|
}
|
||||||
|
@@ -5,14 +5,14 @@ using Transactional.Implementations.EntityFramework;
|
|||||||
|
|
||||||
namespace back.persistance.data.repositories;
|
namespace back.persistance.data.repositories;
|
||||||
|
|
||||||
public class UserRepository(DataContext context) : ReadWriteRepository<User, string>(context), IUserRepository
|
public class UserRepository(DataContext context) : ReadWriteRepository<User>(context), IUserRepository
|
||||||
{
|
{
|
||||||
public async Task<User?> GetByEmail(string email)
|
public async Task<User?> GetByEmail(string email)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(email)) return null;
|
if (string.IsNullOrEmpty(email)) return null;
|
||||||
return await Entity.FirstOrDefaultAsync(u => u.Email == email);
|
return await Entities.FirstOrDefaultAsync(u => u.Email == email);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@@ -25,7 +25,7 @@ public class UserRepository(DataContext context) : ReadWriteRepository<User, str
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(email)) return string.Empty;
|
if (string.IsNullOrEmpty(email)) return string.Empty;
|
||||||
var user = await Entity.FirstOrDefaultAsync(u => u.Email == email);
|
var user = await Entities.FirstOrDefaultAsync(u => u.Email == email);
|
||||||
return user?.Salt ?? string.Empty;
|
return user?.Salt ?? string.Empty;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
@@ -39,7 +39,7 @@ public class UserRepository(DataContext context) : ReadWriteRepository<User, str
|
|||||||
if (string.IsNullOrEmpty(email) || string.IsNullOrEmpty(password)) return null;
|
if (string.IsNullOrEmpty(email) || string.IsNullOrEmpty(password)) return null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return await Entity.FirstOrDefaultAsync(u => u.Email == email && u.Password == password);
|
return await Entities.FirstOrDefaultAsync(u => u.Email == email && u.Password == password);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@@ -52,7 +52,7 @@ public class UserRepository(DataContext context) : ReadWriteRepository<User, str
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(email)) return false;
|
if (string.IsNullOrEmpty(email)) return false;
|
||||||
return await Entity.AnyAsync(u => u.Email == email);
|
return await Entities.AnyAsync(u => u.Email == email);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
@@ -4,13 +4,15 @@ using back.persistance.data.repositories.Abstracts;
|
|||||||
using back.services.engine.Crypto;
|
using back.services.engine.Crypto;
|
||||||
using back.services.engine.mailing;
|
using back.services.engine.mailing;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
namespace back.services.bussines.UserService;
|
namespace back.services.bussines.UserService;
|
||||||
|
|
||||||
public class UserService(
|
public class UserService(
|
||||||
IUserRepository userRepository, ICryptoService cryptoService,
|
IUserRepository userRepository, ICryptoService cryptoService,
|
||||||
IEmailService emailService,
|
IEmailService emailService,
|
||||||
IBlobStorageService blobStorageService
|
IBlobStorageService blobStorageService,
|
||||||
|
JsonSerializerOptions jsonSerializerOptions
|
||||||
) : IUserService
|
) : IUserService
|
||||||
{
|
{
|
||||||
private readonly IUserRepository _repository = userRepository ?? throw new ArgumentNullException(nameof(userRepository));
|
private readonly IUserRepository _repository = userRepository ?? throw new ArgumentNullException(nameof(userRepository));
|
||||||
@@ -66,6 +68,14 @@ public class UserService(
|
|||||||
return existingUser;
|
return existingUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<User?> Login(string email, string decryptedPass)
|
||||||
|
{
|
||||||
|
var salt = await _repository.GetUserSaltByEmail(email);
|
||||||
|
var hashedPassword = _cryptoService.HashPassword(decryptedPass, salt);
|
||||||
|
var user = await _repository.Login(email, hashedPassword ?? string.Empty);
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<User?> Login(string email, string password, string clientId)
|
public async Task<User?> Login(string email, string password, string clientId)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(email) || string.IsNullOrEmpty(password)) return null;
|
if (string.IsNullOrEmpty(email) || string.IsNullOrEmpty(password)) return null;
|
||||||
@@ -73,9 +83,7 @@ public class UserService(
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var decryptedPass = _cryptoService.Decrypt(clientId, password);
|
var decryptedPass = _cryptoService.Decrypt(clientId, password);
|
||||||
var salt = await _repository.GetUserSaltByEmail(email);
|
var user = await Login(email, decryptedPass ?? string.Empty);
|
||||||
var hashedPassword = _cryptoService.HashPassword(decryptedPass, salt);
|
|
||||||
var user = await _repository.Login(email, hashedPassword ?? string.Empty);
|
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
@@ -101,21 +109,21 @@ public class UserService(
|
|||||||
|
|
||||||
public async Task<User?> ValidateSystemUser(string email, string password, string systemKey, string clientId)
|
public async Task<User?> ValidateSystemUser(string email, string password, string systemKey, string clientId)
|
||||||
{
|
{
|
||||||
password = _cryptoService.Decrypt(clientId, password) ?? string.Empty;
|
var decryptedPassword = _cryptoService.Decrypt(clientId, password) ?? string.Empty;
|
||||||
systemKey = _cryptoService.Decrypt(clientId, systemKey) ?? string.Empty;
|
var decryptedsystemKey = _cryptoService.Decrypt(clientId, systemKey) ?? string.Empty;
|
||||||
if (string.IsNullOrEmpty(email) || string.IsNullOrEmpty(password) || string.IsNullOrEmpty(systemKey))
|
if (string.IsNullOrEmpty(email) || string.IsNullOrEmpty(decryptedPassword) || string.IsNullOrEmpty(decryptedsystemKey))
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (!email.Equals("@system", StringComparison.InvariantCultureIgnoreCase))
|
if (!email.Equals(User.SystemUser.Email, StringComparison.InvariantCultureIgnoreCase))
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var systemKeyBytes = await _blobStorageService.GetBytes("systemkey.lock");
|
var systemKeyBytes = await _blobStorageService.GetBytes("systemkey.lock");
|
||||||
var systemKeyString = Encoding.UTF8.GetString(systemKeyBytes ?? []);
|
var systemKeyString = Encoding.UTF8.GetString(systemKeyBytes ?? []);
|
||||||
var systemKeyObject = System.Text.Json.JsonSerializer.Deserialize<SystemKey>(systemKeyString);
|
var systemKeyObject = JsonSerializer.Deserialize<SystemKey>(systemKeyString, jsonSerializerOptions);
|
||||||
if (systemKeyObject == null || !systemKeyObject.IsValid(email, password, systemKey))
|
if (systemKeyObject == null || !systemKeyObject.IsValid(email, decryptedPassword, decryptedsystemKey))
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -128,6 +136,6 @@ public class UserService(
|
|||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return await Login(user.Email!, user.Password!, clientId);
|
return await Login(user.Email!, decryptedPassword);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -5,8 +5,8 @@ public class PasswordGenerator : IPasswordGenerator
|
|||||||
public string Generate(int length, bool includeNumbers = true, bool includeMayus = true, bool includeMinus = true, bool includeSpecials = true)
|
public string Generate(int length, bool includeNumbers = true, bool includeMayus = true, bool includeMinus = true, bool includeSpecials = true)
|
||||||
{
|
{
|
||||||
const string numbers = "0123456789";
|
const string numbers = "0123456789";
|
||||||
const string mayus = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
const string mayus = "ABCÇDEFGHIJKLMNÑOPQRSTUVWXYZ";
|
||||||
const string minus = "abcdefghijklmnopqrstuvwxyz";
|
const string minus = "abcçdefghijklmnñopqrstuvwxyz";
|
||||||
const string specials = "!@#$%^&*()_+[]{}|;:,.<>?";
|
const string specials = "!@#$%^&*()_+[]{}|;:,.<>?";
|
||||||
var characters = minus;
|
var characters = minus;
|
||||||
if (includeNumbers) characters += numbers;
|
if (includeNumbers) characters += numbers;
|
||||||
@@ -19,6 +19,22 @@ public class PasswordGenerator : IPasswordGenerator
|
|||||||
{
|
{
|
||||||
password[i] = characters[random.Next(characters.Length)];
|
password[i] = characters[random.Next(characters.Length)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var positionPool = new List<int>();
|
||||||
|
for (int i = 0; i < length; i++) positionPool.Add(i);
|
||||||
|
var forcedRandomNumber = random.Next(0, positionPool.Count);
|
||||||
|
positionPool.RemoveAt(forcedRandomNumber);
|
||||||
|
var forcedRandomMayus = random.Next(0, positionPool.Count);
|
||||||
|
positionPool.RemoveAt(forcedRandomMayus);
|
||||||
|
var forcedRandomMinus = random.Next(0, positionPool.Count);
|
||||||
|
positionPool.RemoveAt(forcedRandomMinus);
|
||||||
|
var forcedRandomSpecial = random.Next(0, positionPool.Count);
|
||||||
|
positionPool.RemoveAt(forcedRandomSpecial);
|
||||||
|
|
||||||
|
password[forcedRandomNumber] = numbers[random.Next(numbers.Length)];
|
||||||
|
password[forcedRandomMayus] = mayus[random.Next(mayus.Length)];
|
||||||
|
password[forcedRandomMinus] = minus[random.Next(minus.Length)];
|
||||||
|
password[forcedRandomSpecial] = specials[random.Next(specials.Length)];
|
||||||
return new string(password);
|
return new string(password);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -4,12 +4,14 @@ using back.persistance.data;
|
|||||||
using back.persistance.data.repositories.Abstracts;
|
using back.persistance.data.repositories.Abstracts;
|
||||||
using back.services.engine.Crypto;
|
using back.services.engine.Crypto;
|
||||||
using back.services.engine.PasswordGenerator;
|
using back.services.engine.PasswordGenerator;
|
||||||
|
using System.Text.Json;
|
||||||
using Transactional.Abstractions.Interfaces;
|
using Transactional.Abstractions.Interfaces;
|
||||||
|
|
||||||
namespace back.services.engine.SystemUser;
|
namespace back.services.engine.SystemUser;
|
||||||
|
|
||||||
public class SystemUserGenerator(
|
public class SystemUserGenerator(
|
||||||
ITransactionalService<DataContext> transactional,
|
ITransactionalService<DataContext> transactional,
|
||||||
|
JsonSerializerOptions jsonSerializerOptions,
|
||||||
IUserRepository userRepository,
|
IUserRepository userRepository,
|
||||||
IPersonRepository personRepository,
|
IPersonRepository personRepository,
|
||||||
ICryptoService cryptoService,
|
ICryptoService cryptoService,
|
||||||
@@ -21,9 +23,9 @@ public class SystemUserGenerator(
|
|||||||
var systemKey = new SystemKey() {
|
var systemKey = new SystemKey() {
|
||||||
Password = passwordGenerator.Generate(16),
|
Password = passwordGenerator.Generate(16),
|
||||||
};
|
};
|
||||||
var systemKeyJson = System.Text.Json.JsonSerializer.Serialize(systemKey);
|
var systemKeyJson = JsonSerializer.Serialize(systemKey, options: jsonSerializerOptions);
|
||||||
|
|
||||||
using Stream stream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(systemKeyJson));
|
using Stream stream = new MemoryStream(new System.Text.UTF8Encoding(true).GetBytes(systemKeyJson));
|
||||||
|
|
||||||
await blobStorageService.Delete("systemkey.lock");
|
await blobStorageService.Delete("systemkey.lock");
|
||||||
|
|
||||||
@@ -38,10 +40,16 @@ public class SystemUserGenerator(
|
|||||||
|
|
||||||
if (!await userRepository.Exists(User.SystemUser.Id!))
|
if (!await userRepository.Exists(User.SystemUser.Id!))
|
||||||
{
|
{
|
||||||
await transactional.DoTransaction(async () => {
|
await transactional.DoTransaction(async () =>
|
||||||
|
{
|
||||||
await personRepository.Insert(Person.SystemPerson);
|
await personRepository.Insert(Person.SystemPerson);
|
||||||
await userRepository.Insert(User.SystemUser);
|
await userRepository.Insert(User.SystemUser);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await userRepository.Update(User.SystemUser);
|
||||||
|
await userRepository.SaveChanges();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
34
front/v2/.vscode/launch.json
vendored
34
front/v2/.vscode/launch.json
vendored
@@ -1,20 +1,38 @@
|
|||||||
{
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"configurations": [
|
"configurations": [
|
||||||
{
|
{
|
||||||
"name": "ng serve",
|
"name": "FRONT: DEBUG(Edge)",
|
||||||
"type": "chrome",
|
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"preLaunchTask": "npm: start",
|
"type": "msedge",
|
||||||
"url": "http://localhost:4200/"
|
"url": "http://localhost:4200",
|
||||||
|
"webRoot": "${workspaceFolder}",
|
||||||
|
"preLaunchTask": "Start Node server with nvs latest"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "ng test",
|
"name": "Attach Edge",
|
||||||
"type": "chrome",
|
"type": "msedge",
|
||||||
|
"request": "attach",
|
||||||
|
"url": "http://localhost:4200/#",
|
||||||
|
"webRoot": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Launch Edge (Test)",
|
||||||
|
"type": "msedge",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"preLaunchTask": "npm: test",
|
"url": "http://localhost:9876/debug.html",
|
||||||
"url": "http://localhost:9876/debug.html"
|
"webRoot": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Launch Edge (E2E)",
|
||||||
|
"type": "node",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${workspaceFolder}/node_modules/protractor/bin/protractor",
|
||||||
|
"protocol": "inspector",
|
||||||
|
"args": ["${workspaceFolder}/protractor.conf.js"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
45
front/v2/.vscode/tasks.json
vendored
45
front/v2/.vscode/tasks.json
vendored
@@ -1,41 +1,20 @@
|
|||||||
{
|
{
|
||||||
// For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
|
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"tasks": [
|
"tasks": [
|
||||||
{
|
{
|
||||||
"type": "npm",
|
"label": "Start Node server with nvs latest",
|
||||||
"script": "start",
|
"type": "shell",
|
||||||
|
"command": "nvs use latest && npm run start",
|
||||||
|
"options": {
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
"isBackground": true,
|
"isBackground": true,
|
||||||
"problemMatcher": {
|
"problemMatcher": [],
|
||||||
"owner": "typescript",
|
"presentation": {
|
||||||
"pattern": "$tsc",
|
"echo": true,
|
||||||
"background": {
|
"reveal": "always",
|
||||||
"activeOnStart": true,
|
"focus": false,
|
||||||
"beginsPattern": {
|
"panel": "shared"
|
||||||
"regexp": "(.*?)"
|
|
||||||
},
|
|
||||||
"endsPattern": {
|
|
||||||
"regexp": "bundle generation complete"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "npm",
|
|
||||||
"script": "test",
|
|
||||||
"isBackground": true,
|
|
||||||
"problemMatcher": {
|
|
||||||
"owner": "typescript",
|
|
||||||
"pattern": "$tsc",
|
|
||||||
"background": {
|
|
||||||
"activeOnStart": true,
|
|
||||||
"beginsPattern": {
|
|
||||||
"regexp": "(.*?)"
|
|
||||||
},
|
|
||||||
"endsPattern": {
|
|
||||||
"regexp": "bundle generation complete"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@@ -5,15 +5,15 @@
|
|||||||
<events-link></events-link>
|
<events-link></events-link>
|
||||||
<tags-link></tags-link>
|
<tags-link></tags-link>
|
||||||
<services-link></services-link>
|
<services-link></services-link>
|
||||||
@if (user.isLoggedIn) {
|
@if (currentUser().isLoggedIn) {
|
||||||
<user-galleries-link></user-galleries-link>
|
<user-galleries-link></user-galleries-link>
|
||||||
} @if (user.isContentManager) {
|
} @if (currentUser().isContentManager) {
|
||||||
<content-manager-panel-link></content-manager-panel-link>
|
<content-manager-panel-link></content-manager-panel-link>
|
||||||
} @if (user.isAdmin) {
|
} @if (currentUser().isAdmin) {
|
||||||
<admin-panel-link></admin-panel-link>
|
<admin-panel-link></admin-panel-link>
|
||||||
}
|
}
|
||||||
<div class="user-profile">
|
<div class="user-profile">
|
||||||
@if (user.isLoggedIn) {
|
@if (currentUser().isLoggedIn) {
|
||||||
<user-profile-link></user-profile-link>
|
<user-profile-link></user-profile-link>
|
||||||
} @else {
|
} @else {
|
||||||
<login-link></login-link>
|
<login-link></login-link>
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { Component, inject } from '@angular/core';
|
import { Component, inject, signal } from '@angular/core';
|
||||||
import { Logo } from '../logo/logo';
|
import { Logo } from '../logo/logo';
|
||||||
import { EventsLink } from '../events-link/events-link';
|
import { EventsLink } from '../events-link/events-link';
|
||||||
import { UserGalleriesLink } from '../user-galleries-link/user-galleries-link';
|
import { UserGalleriesLink } from '../user-galleries-link/user-galleries-link';
|
||||||
@@ -35,12 +35,12 @@ export class Header implements OnInit {
|
|||||||
|
|
||||||
constructor(private userService: userService) {}
|
constructor(private userService: userService) {}
|
||||||
|
|
||||||
private currentUser: userModel = userModel.DefaultUser;
|
currentUser = signal<userModel>(userModel.DefaultUser);
|
||||||
|
|
||||||
hideButtons = false;
|
hideButtons = false;
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.userService.getUser().subscribe((user) => {
|
this.userService.getUser().subscribe((user) => {
|
||||||
this.currentUser = user;
|
this.currentUser.set(user);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Escucha cambios de ruta
|
// Escucha cambios de ruta
|
||||||
@@ -51,8 +51,4 @@ export class Header implements OnInit {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get user() {
|
|
||||||
return this.currentUser;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -30,18 +30,21 @@ export class userService {
|
|||||||
if (email == null || password == null || systemKey == null) {
|
if (email == null || password == null || systemKey == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const encryptedPassword = this.cryptoService.encryptData(password);
|
const encryptedPassword = await this.cryptoService.encryptData(password);
|
||||||
const encryptedSystemKey = this.cryptoService.encryptData(systemKey);
|
const encryptedSystemKey = await this.cryptoService.encryptData(systemKey);
|
||||||
const response = await axios.post('/users/login', {
|
return axios
|
||||||
email,
|
.post('/users/login', {
|
||||||
password: await encryptedPassword,
|
email,
|
||||||
systemKey: await encryptedSystemKey,
|
password: encryptedPassword,
|
||||||
});
|
systemKey: encryptedSystemKey,
|
||||||
const { jwt, refresh, usermodel } = response.data;
|
})
|
||||||
localStorage.setItem('jwt', jwt);
|
.then((response) => {
|
||||||
localStorage.setItem('refresh', refresh);
|
const { jwt, refresh, usermodel } = response.data;
|
||||||
this.setUser(usermodel);
|
localStorage.setItem('jwt', jwt);
|
||||||
return usermodel;
|
localStorage.setItem('refresh', refresh);
|
||||||
|
this.setUser(usermodel);
|
||||||
|
return usermodel;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async login(
|
async login(
|
||||||
|
@@ -80,7 +80,7 @@ export class LoginView {
|
|||||||
|
|
||||||
isLoginSystem(): boolean {
|
isLoginSystem(): boolean {
|
||||||
const emailValue = this.email?.value;
|
const emailValue = this.email?.value;
|
||||||
if (emailValue && emailValue == '@system') {
|
if (emailValue && emailValue == 'sys@t.em') {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -119,19 +119,16 @@ export class LoginView {
|
|||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
onSubmit() {
|
async onSubmit() {
|
||||||
const email = this.loginForm.value.email;
|
const email = this.loginForm.value.email;
|
||||||
const password = this.loginForm.value.password;
|
const password = this.loginForm.value.password;
|
||||||
if (this.isLoginSystem()) {
|
if (this.isLoginSystem()) {
|
||||||
const systemKey = this.systemKey?.value;
|
const systemKey = this.systemKey?.value;
|
||||||
from(this.userService.systemLogin(email, password, systemKey)).subscribe({
|
await this.userService
|
||||||
next: (user) => {
|
.systemLogin(email, password, systemKey)
|
||||||
|
.finally(() => {
|
||||||
this.router.navigate(['/']);
|
this.router.navigate(['/']);
|
||||||
},
|
});
|
||||||
error: (error) => {
|
|
||||||
this.router.navigate(['/']);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else if (this.loginForm.valid) {
|
} else if (this.loginForm.valid) {
|
||||||
this.entrando.set(true);
|
this.entrando.set(true);
|
||||||
from(this.userService.login(email, password)).subscribe({
|
from(this.userService.login(email, password)).subscribe({
|
||||||
|
@@ -54,17 +54,6 @@ class userModel extends personModel {
|
|||||||
new Date(),
|
new Date(),
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
public static readonly TestAdminUser: userModel = new userModel(
|
|
||||||
'-1',
|
|
||||||
'testadmin@example.com',
|
|
||||||
'password',
|
|
||||||
'Test Admin User',
|
|
||||||
[roleModel.AdminRole],
|
|
||||||
new Date(),
|
|
||||||
new Date(),
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export { SigningMethods, userModel };
|
export { SigningMethods, userModel };
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
@use "./styles/variables";
|
@use "./styles/variables";
|
||||||
@use "./styles/buttons";
|
@use "./styles/buttons";
|
||||||
@use "./styles/inputs";
|
@use "./styles/inputs";
|
||||||
@use "./styles/themes/light";
|
// @use "./styles/themes/light";
|
||||||
@use "./styles/themes/dark";
|
// @use "./styles/themes/dark";
|
||||||
@use "./styles/loaders";
|
@use "./styles/loaders";
|
||||||
|
|
||||||
/* You can add global styles to this file, and also import other style files */
|
/* You can add global styles to this file, and also import other style files */
|
||||||
|
Reference in New Issue
Block a user