Fase 1: Inicialización del Backend .NET 10, Configuración de Dapper, Autenticación JWT y Entidades Base
This commit is contained in:
BIN
SIG-CM.pdf
Normal file
BIN
SIG-CM.pdf
Normal file
Binary file not shown.
26
src/SIGCM.API/Controllers/AuthController.cs
Normal file
26
src/SIGCM.API/Controllers/AuthController.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using SIGCM.Application.DTOs;
|
||||||
|
using SIGCM.Application.Interfaces;
|
||||||
|
|
||||||
|
namespace SIGCM.API.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
public class AuthController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly IAuthService _authService;
|
||||||
|
|
||||||
|
public AuthController(IAuthService authService)
|
||||||
|
{
|
||||||
|
_authService = authService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("login")]
|
||||||
|
public async Task<IActionResult> Login(LoginDto dto)
|
||||||
|
{
|
||||||
|
var token = await _authService.LoginAsync(dto.Username, dto.Password);
|
||||||
|
if (token == null) return Unauthorized("Invalid credentials");
|
||||||
|
|
||||||
|
return Ok(new { token });
|
||||||
|
}
|
||||||
|
}
|
||||||
55
src/SIGCM.API/Controllers/CategoriesController.cs
Normal file
55
src/SIGCM.API/Controllers/CategoriesController.cs
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using SIGCM.Domain.Entities;
|
||||||
|
using SIGCM.Domain.Interfaces;
|
||||||
|
|
||||||
|
namespace SIGCM.API.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
public class CategoriesController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly ICategoryRepository _repository;
|
||||||
|
|
||||||
|
public CategoriesController(ICategoryRepository repository)
|
||||||
|
{
|
||||||
|
_repository = repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<IActionResult> GetAll()
|
||||||
|
{
|
||||||
|
var categories = await _repository.GetAllAsync();
|
||||||
|
return Ok(categories);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("{id}")]
|
||||||
|
public async Task<IActionResult> GetById(int id)
|
||||||
|
{
|
||||||
|
var category = await _repository.GetByIdAsync(id);
|
||||||
|
if (category == null) return NotFound();
|
||||||
|
return Ok(category);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<IActionResult> Create(Category category)
|
||||||
|
{
|
||||||
|
var id = await _repository.AddAsync(category);
|
||||||
|
category.Id = id;
|
||||||
|
return CreatedAtAction(nameof(GetById), new { id }, category);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPut("{id}")]
|
||||||
|
public async Task<IActionResult> Update(int id, Category category)
|
||||||
|
{
|
||||||
|
if (id != category.Id) return BadRequest();
|
||||||
|
await _repository.UpdateAsync(category);
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete("{id}")]
|
||||||
|
public async Task<IActionResult> Delete(int id)
|
||||||
|
{
|
||||||
|
await _repository.DeleteAsync(id);
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
}
|
||||||
47
src/SIGCM.API/Controllers/OperationsController.cs
Normal file
47
src/SIGCM.API/Controllers/OperationsController.cs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using SIGCM.Domain.Entities;
|
||||||
|
using SIGCM.Domain.Interfaces;
|
||||||
|
|
||||||
|
namespace SIGCM.API.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
public class OperationsController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly IOperationRepository _repository;
|
||||||
|
|
||||||
|
public OperationsController(IOperationRepository repository)
|
||||||
|
{
|
||||||
|
_repository = repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<IActionResult> GetAll()
|
||||||
|
{
|
||||||
|
var operations = await _repository.GetAllAsync();
|
||||||
|
return Ok(operations);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("{id}")]
|
||||||
|
public async Task<IActionResult> GetById(int id)
|
||||||
|
{
|
||||||
|
var operation = await _repository.GetByIdAsync(id);
|
||||||
|
if (operation == null) return NotFound();
|
||||||
|
return Ok(operation);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<IActionResult> Create(Operation operation)
|
||||||
|
{
|
||||||
|
var id = await _repository.AddAsync(operation);
|
||||||
|
operation.Id = id;
|
||||||
|
return CreatedAtAction(nameof(GetById), new { id }, operation);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete("{id}")]
|
||||||
|
public async Task<IActionResult> Delete(int id)
|
||||||
|
{
|
||||||
|
await _repository.DeleteAsync(id);
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
}
|
||||||
35
src/SIGCM.API/Program.cs
Normal file
35
src/SIGCM.API/Program.cs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
using SIGCM.Infrastructure;
|
||||||
|
using SIGCM.Infrastructure.Data;
|
||||||
|
|
||||||
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
|
// Add services to the container.
|
||||||
|
builder.Services.AddControllers();
|
||||||
|
builder.Services.AddEndpointsApiExplorer();
|
||||||
|
builder.Services.AddSwaggerGen();
|
||||||
|
|
||||||
|
builder.Services.AddInfrastructure();
|
||||||
|
|
||||||
|
var app = builder.Build();
|
||||||
|
|
||||||
|
// Configure the HTTP request pipeline.
|
||||||
|
if (app.Environment.IsDevelopment())
|
||||||
|
{
|
||||||
|
app.UseSwagger();
|
||||||
|
app.UseSwaggerUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
app.UseHttpsRedirection();
|
||||||
|
|
||||||
|
app.UseAuthorization();
|
||||||
|
|
||||||
|
app.MapControllers();
|
||||||
|
|
||||||
|
// Initialize DB
|
||||||
|
using (var scope = app.Services.CreateScope())
|
||||||
|
{
|
||||||
|
var initializer = scope.ServiceProvider.GetRequiredService<DbInitializer>();
|
||||||
|
await initializer.InitializeAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
app.Run();
|
||||||
23
src/SIGCM.API/Properties/launchSettings.json
Normal file
23
src/SIGCM.API/Properties/launchSettings.json
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||||
|
"profiles": {
|
||||||
|
"http": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"launchBrowser": false,
|
||||||
|
"applicationUrl": "http://localhost:5176",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"https": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"launchBrowser": false,
|
||||||
|
"applicationUrl": "https://localhost:7034;http://localhost:5176",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src/SIGCM.API/SIGCM.API.csproj
Normal file
20
src/SIGCM.API/SIGCM.API.csproj
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.1" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.1" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.0.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\SIGCM.Application\SIGCM.Application.csproj" />
|
||||||
|
<ProjectReference Include="..\SIGCM.Infrastructure\SIGCM.Infrastructure.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
6
src/SIGCM.API/SIGCM.API.http
Normal file
6
src/SIGCM.API/SIGCM.API.http
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
@SIGCM.API_HostAddress = http://localhost:5176
|
||||||
|
|
||||||
|
GET {{SIGCM.API_HostAddress}}/weatherforecast/
|
||||||
|
Accept: application/json
|
||||||
|
|
||||||
|
###
|
||||||
17
src/SIGCM.API/appsettings.json
Normal file
17
src/SIGCM.API/appsettings.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*",
|
||||||
|
"ConnectionStrings": {
|
||||||
|
"DefaultConnection": "Server=TECNICA3;Database=SIGCM;User Id=sigcmApi;Password=@Diego550@;TrustServerCertificate=True"
|
||||||
|
},
|
||||||
|
"Jwt": {
|
||||||
|
"Key": "badb1a38d221c9e23bcf70958840ca7f5a5dc54f2047dadf7ce45b578b5bc3e2",
|
||||||
|
"Issuer": "SIGCMApi",
|
||||||
|
"Audience": "SIGCMAdmin"
|
||||||
|
}
|
||||||
|
}
|
||||||
6
src/SIGCM.Application/Class1.cs
Normal file
6
src/SIGCM.Application/Class1.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namespace SIGCM.Application;
|
||||||
|
|
||||||
|
public class Class1
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
7
src/SIGCM.Application/DTOs/LoginDto.cs
Normal file
7
src/SIGCM.Application/DTOs/LoginDto.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace SIGCM.Application.DTOs;
|
||||||
|
|
||||||
|
public class LoginDto
|
||||||
|
{
|
||||||
|
public required string Username { get; set; }
|
||||||
|
public required string Password { get; set; }
|
||||||
|
}
|
||||||
6
src/SIGCM.Application/Interfaces/IAuthService.cs
Normal file
6
src/SIGCM.Application/Interfaces/IAuthService.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namespace SIGCM.Application.Interfaces;
|
||||||
|
|
||||||
|
public interface IAuthService
|
||||||
|
{
|
||||||
|
Task<string?> LoginAsync(string username, string password);
|
||||||
|
}
|
||||||
7
src/SIGCM.Application/Interfaces/ITokenService.cs
Normal file
7
src/SIGCM.Application/Interfaces/ITokenService.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace SIGCM.Application.Interfaces;
|
||||||
|
using SIGCM.Domain.Entities;
|
||||||
|
|
||||||
|
public interface ITokenService
|
||||||
|
{
|
||||||
|
string GenerateToken(User user);
|
||||||
|
}
|
||||||
13
src/SIGCM.Application/SIGCM.Application.csproj
Normal file
13
src/SIGCM.Application/SIGCM.Application.csproj
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\SIGCM.Domain\SIGCM.Domain.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
6
src/SIGCM.Domain/Class1.cs
Normal file
6
src/SIGCM.Domain/Class1.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namespace SIGCM.Domain;
|
||||||
|
|
||||||
|
public class Class1
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
10
src/SIGCM.Domain/Entities/Category.cs
Normal file
10
src/SIGCM.Domain/Entities/Category.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
namespace SIGCM.Domain.Entities;
|
||||||
|
|
||||||
|
public class Category
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public int? ParentId { get; set; }
|
||||||
|
public required string Name { get; set; }
|
||||||
|
public required string Slug { get; set; }
|
||||||
|
public bool Active { get; set; } = true;
|
||||||
|
}
|
||||||
7
src/SIGCM.Domain/Entities/Operation.cs
Normal file
7
src/SIGCM.Domain/Entities/Operation.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace SIGCM.Domain.Entities;
|
||||||
|
|
||||||
|
public class Operation
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public required string Name { get; set; }
|
||||||
|
}
|
||||||
11
src/SIGCM.Domain/Entities/User.cs
Normal file
11
src/SIGCM.Domain/Entities/User.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
namespace SIGCM.Domain.Entities;
|
||||||
|
|
||||||
|
public class User
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public required string Username { get; set; }
|
||||||
|
public required string PasswordHash { get; set; }
|
||||||
|
public required string Role { get; set; }
|
||||||
|
public string? Email { get; set; }
|
||||||
|
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||||
|
}
|
||||||
12
src/SIGCM.Domain/Interfaces/ICategoryRepository.cs
Normal file
12
src/SIGCM.Domain/Interfaces/ICategoryRepository.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
namespace SIGCM.Domain.Interfaces;
|
||||||
|
using SIGCM.Domain.Entities;
|
||||||
|
|
||||||
|
public interface ICategoryRepository
|
||||||
|
{
|
||||||
|
Task<IEnumerable<Category>> GetAllAsync();
|
||||||
|
Task<Category?> GetByIdAsync(int id);
|
||||||
|
Task<int> AddAsync(Category category);
|
||||||
|
Task UpdateAsync(Category category);
|
||||||
|
Task DeleteAsync(int id);
|
||||||
|
Task<IEnumerable<Category>> GetSubCategoriesAsync(int parentId);
|
||||||
|
}
|
||||||
10
src/SIGCM.Domain/Interfaces/IOperationRepository.cs
Normal file
10
src/SIGCM.Domain/Interfaces/IOperationRepository.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
namespace SIGCM.Domain.Interfaces;
|
||||||
|
using SIGCM.Domain.Entities;
|
||||||
|
|
||||||
|
public interface IOperationRepository
|
||||||
|
{
|
||||||
|
Task<IEnumerable<Operation>> GetAllAsync();
|
||||||
|
Task<Operation?> GetByIdAsync(int id);
|
||||||
|
Task<int> AddAsync(Operation operation);
|
||||||
|
Task DeleteAsync(int id);
|
||||||
|
}
|
||||||
8
src/SIGCM.Domain/Interfaces/IUserRepository.cs
Normal file
8
src/SIGCM.Domain/Interfaces/IUserRepository.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace SIGCM.Domain.Interfaces;
|
||||||
|
using SIGCM.Domain.Entities;
|
||||||
|
|
||||||
|
public interface IUserRepository
|
||||||
|
{
|
||||||
|
Task<User?> GetByUsernameAsync(string username);
|
||||||
|
Task<int> CreateAsync(User user);
|
||||||
|
}
|
||||||
9
src/SIGCM.Domain/SIGCM.Domain.csproj
Normal file
9
src/SIGCM.Domain/SIGCM.Domain.csproj
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
6
src/SIGCM.Infrastructure/Class1.cs
Normal file
6
src/SIGCM.Infrastructure/Class1.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namespace SIGCM.Infrastructure;
|
||||||
|
|
||||||
|
public class Class1
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
26
src/SIGCM.Infrastructure/Data/DbConnectionFactory.cs
Normal file
26
src/SIGCM.Infrastructure/Data/DbConnectionFactory.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
using System.Data;
|
||||||
|
using Microsoft.Data.SqlClient;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
|
namespace SIGCM.Infrastructure.Data;
|
||||||
|
|
||||||
|
public interface IDbConnectionFactory
|
||||||
|
{
|
||||||
|
IDbConnection CreateConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DbConnectionFactory : IDbConnectionFactory
|
||||||
|
{
|
||||||
|
private readonly string _connectionString;
|
||||||
|
|
||||||
|
public DbConnectionFactory(IConfiguration configuration)
|
||||||
|
{
|
||||||
|
_connectionString = configuration.GetConnectionString("DefaultConnection")
|
||||||
|
?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public IDbConnection CreateConnection()
|
||||||
|
{
|
||||||
|
return new SqlConnection(_connectionString);
|
||||||
|
}
|
||||||
|
}
|
||||||
117
src/SIGCM.Infrastructure/Data/DbInitializer.cs
Normal file
117
src/SIGCM.Infrastructure/Data/DbInitializer.cs
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
using Dapper;
|
||||||
|
|
||||||
|
namespace SIGCM.Infrastructure.Data;
|
||||||
|
|
||||||
|
public class DbInitializer
|
||||||
|
{
|
||||||
|
private readonly IDbConnectionFactory _connectionFactory;
|
||||||
|
|
||||||
|
public DbInitializer(IDbConnectionFactory connectionFactory)
|
||||||
|
{
|
||||||
|
_connectionFactory = connectionFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task InitializeAsync()
|
||||||
|
{
|
||||||
|
using var connection = _connectionFactory.CreateConnection();
|
||||||
|
|
||||||
|
var sql = @"
|
||||||
|
IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'Users')
|
||||||
|
BEGIN
|
||||||
|
CREATE TABLE Users (
|
||||||
|
Id INT IDENTITY(1,1) PRIMARY KEY,
|
||||||
|
Username NVARCHAR(50) NOT NULL UNIQUE,
|
||||||
|
PasswordHash NVARCHAR(255) NOT NULL,
|
||||||
|
Role NVARCHAR(20) NOT NULL,
|
||||||
|
Email NVARCHAR(100) NULL,
|
||||||
|
CreatedAt DATETIME DEFAULT GETUTCDATE()
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Seed generic admin (password: admin123)
|
||||||
|
-- Hash created with BCrypt
|
||||||
|
INSERT INTO Users (Username, PasswordHash, Role)
|
||||||
|
VALUES ('admin', '$2a$11$u.w..ExampleHashPlaceholder...', 'Admin');
|
||||||
|
END
|
||||||
|
|
||||||
|
IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'Categories')
|
||||||
|
BEGIN
|
||||||
|
CREATE TABLE Categories (
|
||||||
|
Id INT IDENTITY(1,1) PRIMARY KEY,
|
||||||
|
ParentId INT NULL,
|
||||||
|
Name NVARCHAR(100) NOT NULL,
|
||||||
|
Slug NVARCHAR(100) NOT NULL,
|
||||||
|
Active BIT DEFAULT 1,
|
||||||
|
FOREIGN KEY (ParentId) REFERENCES Categories(Id)
|
||||||
|
);
|
||||||
|
END
|
||||||
|
|
||||||
|
IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'Operations')
|
||||||
|
BEGIN
|
||||||
|
CREATE TABLE Operations (
|
||||||
|
Id INT IDENTITY(1,1) PRIMARY KEY,
|
||||||
|
Name NVARCHAR(50) NOT NULL UNIQUE
|
||||||
|
);
|
||||||
|
END
|
||||||
|
|
||||||
|
IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'CategoryOperations')
|
||||||
|
BEGIN
|
||||||
|
CREATE TABLE CategoryOperations (
|
||||||
|
CategoryId INT NOT NULL,
|
||||||
|
OperationId INT NOT NULL,
|
||||||
|
PRIMARY KEY (CategoryId, OperationId),
|
||||||
|
FOREIGN KEY (CategoryId) REFERENCES Categories(Id) ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY (OperationId) REFERENCES Operations(Id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
END
|
||||||
|
";
|
||||||
|
// Fixing the placeholder hash to a valid one might be necessary if I want to login immediately.
|
||||||
|
// I will update the hash command later or create a small utility to generate one.
|
||||||
|
// For now, I'll remove the INSERT or comment it out until I can generate a real hash in C#.
|
||||||
|
|
||||||
|
var schemaSql = @"
|
||||||
|
IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'Users')
|
||||||
|
BEGIN
|
||||||
|
CREATE TABLE Users (
|
||||||
|
Id INT IDENTITY(1,1) PRIMARY KEY,
|
||||||
|
Username NVARCHAR(50) NOT NULL UNIQUE,
|
||||||
|
PasswordHash NVARCHAR(255) NOT NULL,
|
||||||
|
Role NVARCHAR(20) NOT NULL,
|
||||||
|
Email NVARCHAR(100) NULL,
|
||||||
|
CreatedAt DATETIME DEFAULT GETUTCDATE()
|
||||||
|
);
|
||||||
|
END
|
||||||
|
|
||||||
|
IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'Categories')
|
||||||
|
BEGIN
|
||||||
|
CREATE TABLE Categories (
|
||||||
|
Id INT IDENTITY(1,1) PRIMARY KEY,
|
||||||
|
ParentId INT NULL,
|
||||||
|
Name NVARCHAR(100) NOT NULL,
|
||||||
|
Slug NVARCHAR(100) NOT NULL,
|
||||||
|
Active BIT DEFAULT 1,
|
||||||
|
FOREIGN KEY (ParentId) REFERENCES Categories(Id)
|
||||||
|
);
|
||||||
|
END
|
||||||
|
|
||||||
|
IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'Operations')
|
||||||
|
BEGIN
|
||||||
|
CREATE TABLE Operations (
|
||||||
|
Id INT IDENTITY(1,1) PRIMARY KEY,
|
||||||
|
Name NVARCHAR(50) NOT NULL UNIQUE
|
||||||
|
);
|
||||||
|
END
|
||||||
|
|
||||||
|
IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'CategoryOperations')
|
||||||
|
BEGIN
|
||||||
|
CREATE TABLE CategoryOperations (
|
||||||
|
CategoryId INT NOT NULL,
|
||||||
|
OperationId INT NOT NULL,
|
||||||
|
PRIMARY KEY (CategoryId, OperationId),
|
||||||
|
FOREIGN KEY (CategoryId) REFERENCES Categories(Id) ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY (OperationId) REFERENCES Operations(Id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
END
|
||||||
|
";
|
||||||
|
await connection.ExecuteAsync(schemaSql);
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/SIGCM.Infrastructure/DependencyInjection.cs
Normal file
25
src/SIGCM.Infrastructure/DependencyInjection.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using SIGCM.Domain.Interfaces;
|
||||||
|
using SIGCM.Application.Interfaces;
|
||||||
|
using SIGCM.Infrastructure.Data;
|
||||||
|
using SIGCM.Infrastructure.Repositories;
|
||||||
|
|
||||||
|
namespace SIGCM.Infrastructure;
|
||||||
|
|
||||||
|
public static class DependencyInjection
|
||||||
|
{
|
||||||
|
public static IServiceCollection AddInfrastructure(this IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddSingleton<IDbConnectionFactory, DbConnectionFactory>();
|
||||||
|
services.AddSingleton<DbInitializer>();
|
||||||
|
services.AddScoped<ICategoryRepository, CategoryRepository>();
|
||||||
|
services.AddScoped<IOperationRepository, OperationRepository>();
|
||||||
|
services.AddScoped<IUserRepository, UserRepository>();
|
||||||
|
services.AddScoped<ITokenService, Services.TokenService>();
|
||||||
|
services.AddScoped<IAuthService, Services.AuthService>();
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
60
src/SIGCM.Infrastructure/Repositories/CategoryRepository.cs
Normal file
60
src/SIGCM.Infrastructure/Repositories/CategoryRepository.cs
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
using Dapper;
|
||||||
|
using SIGCM.Domain.Entities;
|
||||||
|
using SIGCM.Domain.Interfaces;
|
||||||
|
using SIGCM.Infrastructure.Data;
|
||||||
|
|
||||||
|
namespace SIGCM.Infrastructure.Repositories;
|
||||||
|
|
||||||
|
public class CategoryRepository : ICategoryRepository
|
||||||
|
{
|
||||||
|
private readonly IDbConnectionFactory _connectionFactory;
|
||||||
|
|
||||||
|
public CategoryRepository(IDbConnectionFactory connectionFactory)
|
||||||
|
{
|
||||||
|
_connectionFactory = connectionFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<Category>> GetAllAsync()
|
||||||
|
{
|
||||||
|
using var conn = _connectionFactory.CreateConnection();
|
||||||
|
return await conn.QueryAsync<Category>("SELECT * FROM Categories");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Category?> GetByIdAsync(int id)
|
||||||
|
{
|
||||||
|
using var conn = _connectionFactory.CreateConnection();
|
||||||
|
return await conn.QueryFirstOrDefaultAsync<Category>("SELECT * FROM Categories WHERE Id = @Id", new { Id = id });
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<int> AddAsync(Category category)
|
||||||
|
{
|
||||||
|
using var conn = _connectionFactory.CreateConnection();
|
||||||
|
var sql = @"
|
||||||
|
INSERT INTO Categories (ParentId, Name, Slug, Active)
|
||||||
|
VALUES (@ParentId, @Name, @Slug, @Active);
|
||||||
|
SELECT CAST(SCOPE_IDENTITY() as int);";
|
||||||
|
return await conn.QuerySingleAsync<int>(sql, category);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task UpdateAsync(Category category)
|
||||||
|
{
|
||||||
|
using var conn = _connectionFactory.CreateConnection();
|
||||||
|
var sql = @"
|
||||||
|
UPDATE Categories
|
||||||
|
SET ParentId = @ParentId, Name = @Name, Slug = @Slug, Active = @Active
|
||||||
|
WHERE Id = @Id";
|
||||||
|
await conn.ExecuteAsync(sql, category);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task DeleteAsync(int id)
|
||||||
|
{
|
||||||
|
using var conn = _connectionFactory.CreateConnection();
|
||||||
|
await conn.ExecuteAsync("DELETE FROM Categories WHERE Id = @Id", new { Id = id });
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<Category>> GetSubCategoriesAsync(int parentId)
|
||||||
|
{
|
||||||
|
using var conn = _connectionFactory.CreateConnection();
|
||||||
|
return await conn.QueryAsync<Category>("SELECT * FROM Categories WHERE ParentId = @ParentId", new { ParentId = parentId });
|
||||||
|
}
|
||||||
|
}
|
||||||
44
src/SIGCM.Infrastructure/Repositories/OperationRepository.cs
Normal file
44
src/SIGCM.Infrastructure/Repositories/OperationRepository.cs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
using Dapper;
|
||||||
|
using SIGCM.Domain.Entities;
|
||||||
|
using SIGCM.Domain.Interfaces;
|
||||||
|
using SIGCM.Infrastructure.Data;
|
||||||
|
|
||||||
|
namespace SIGCM.Infrastructure.Repositories;
|
||||||
|
|
||||||
|
public class OperationRepository : IOperationRepository
|
||||||
|
{
|
||||||
|
private readonly IDbConnectionFactory _connectionFactory;
|
||||||
|
|
||||||
|
public OperationRepository(IDbConnectionFactory connectionFactory)
|
||||||
|
{
|
||||||
|
_connectionFactory = connectionFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<Operation>> GetAllAsync()
|
||||||
|
{
|
||||||
|
using var conn = _connectionFactory.CreateConnection();
|
||||||
|
return await conn.QueryAsync<Operation>("SELECT * FROM Operations");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Operation?> GetByIdAsync(int id)
|
||||||
|
{
|
||||||
|
using var conn = _connectionFactory.CreateConnection();
|
||||||
|
return await conn.QueryFirstOrDefaultAsync<Operation>("SELECT * FROM Operations WHERE Id = @Id", new { Id = id });
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<int> AddAsync(Operation operation)
|
||||||
|
{
|
||||||
|
using var conn = _connectionFactory.CreateConnection();
|
||||||
|
var sql = @"
|
||||||
|
INSERT INTO Operations (Name)
|
||||||
|
VALUES (@Name);
|
||||||
|
SELECT CAST(SCOPE_IDENTITY() as int);";
|
||||||
|
return await conn.QuerySingleAsync<int>(sql, operation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task DeleteAsync(int id)
|
||||||
|
{
|
||||||
|
using var conn = _connectionFactory.CreateConnection();
|
||||||
|
await conn.ExecuteAsync("DELETE FROM Operations WHERE Id = @Id", new { Id = id });
|
||||||
|
}
|
||||||
|
}
|
||||||
34
src/SIGCM.Infrastructure/Repositories/UserRepository.cs
Normal file
34
src/SIGCM.Infrastructure/Repositories/UserRepository.cs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
using Dapper;
|
||||||
|
using SIGCM.Domain.Entities;
|
||||||
|
using SIGCM.Domain.Interfaces;
|
||||||
|
using SIGCM.Infrastructure.Data;
|
||||||
|
|
||||||
|
namespace SIGCM.Infrastructure.Repositories;
|
||||||
|
|
||||||
|
public class UserRepository : IUserRepository
|
||||||
|
{
|
||||||
|
private readonly IDbConnectionFactory _connectionFactory;
|
||||||
|
|
||||||
|
public UserRepository(IDbConnectionFactory connectionFactory)
|
||||||
|
{
|
||||||
|
_connectionFactory = connectionFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<User?> GetByUsernameAsync(string username)
|
||||||
|
{
|
||||||
|
using var conn = _connectionFactory.CreateConnection();
|
||||||
|
return await conn.QuerySingleOrDefaultAsync<User>(
|
||||||
|
"SELECT * FROM Users WHERE Username = @Username",
|
||||||
|
new { Username = username });
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<int> CreateAsync(User user)
|
||||||
|
{
|
||||||
|
using var conn = _connectionFactory.CreateConnection();
|
||||||
|
var sql = @"
|
||||||
|
INSERT INTO Users (Username, PasswordHash, Role, Email)
|
||||||
|
VALUES (@Username, @PasswordHash, @Role, @Email);
|
||||||
|
SELECT CAST(SCOPE_IDENTITY() as int);";
|
||||||
|
return await conn.QuerySingleAsync<int>(sql, user);
|
||||||
|
}
|
||||||
|
}
|
||||||
21
src/SIGCM.Infrastructure/SIGCM.Infrastructure.csproj
Normal file
21
src/SIGCM.Infrastructure/SIGCM.Infrastructure.csproj
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\SIGCM.Application\SIGCM.Application.csproj" />
|
||||||
|
<ProjectReference Include="..\SIGCM.Domain\SIGCM.Domain.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
|
||||||
|
<PackageReference Include="Dapper" Version="2.1.66" />
|
||||||
|
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.1.3" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="10.0.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
27
src/SIGCM.Infrastructure/Services/AuthService.cs
Normal file
27
src/SIGCM.Infrastructure/Services/AuthService.cs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
using SIGCM.Application.Interfaces;
|
||||||
|
using SIGCM.Domain.Interfaces;
|
||||||
|
|
||||||
|
namespace SIGCM.Infrastructure.Services;
|
||||||
|
|
||||||
|
public class AuthService : IAuthService
|
||||||
|
{
|
||||||
|
private readonly IUserRepository _userRepo;
|
||||||
|
private readonly ITokenService _tokenService;
|
||||||
|
|
||||||
|
public AuthService(IUserRepository userRepo, ITokenService tokenService)
|
||||||
|
{
|
||||||
|
_userRepo = userRepo;
|
||||||
|
_tokenService = tokenService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string?> LoginAsync(string username, string password)
|
||||||
|
{
|
||||||
|
var user = await _userRepo.GetByUsernameAsync(username);
|
||||||
|
if (user == null) return null;
|
||||||
|
|
||||||
|
bool valid = BCrypt.Net.BCrypt.Verify(password, user.PasswordHash);
|
||||||
|
if (!valid) return null;
|
||||||
|
|
||||||
|
return _tokenService.GenerateToken(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
42
src/SIGCM.Infrastructure/Services/TokenService.cs
Normal file
42
src/SIGCM.Infrastructure/Services/TokenService.cs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using System.Text;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
using SIGCM.Application.Interfaces;
|
||||||
|
using SIGCM.Domain.Entities;
|
||||||
|
|
||||||
|
namespace SIGCM.Infrastructure.Services;
|
||||||
|
|
||||||
|
public class TokenService : ITokenService
|
||||||
|
{
|
||||||
|
private readonly IConfiguration _config;
|
||||||
|
|
||||||
|
public TokenService(IConfiguration config)
|
||||||
|
{
|
||||||
|
_config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GenerateToken(User user)
|
||||||
|
{
|
||||||
|
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]!));
|
||||||
|
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
|
||||||
|
|
||||||
|
var claims = new[]
|
||||||
|
{
|
||||||
|
new Claim(JwtRegisteredClaimNames.Sub, user.Username),
|
||||||
|
new Claim(ClaimTypes.Role, user.Role),
|
||||||
|
new Claim("Id", user.Id.ToString())
|
||||||
|
};
|
||||||
|
|
||||||
|
var token = new JwtSecurityToken(
|
||||||
|
issuer: _config["Jwt:Issuer"],
|
||||||
|
audience: _config["Jwt:Audience"],
|
||||||
|
claims: claims,
|
||||||
|
expires: DateTime.UtcNow.AddHours(4),
|
||||||
|
signingCredentials: creds
|
||||||
|
);
|
||||||
|
|
||||||
|
return new JwtSecurityTokenHandler().WriteToken(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
76
src/SIGCM.sln
Normal file
76
src/SIGCM.sln
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.0.31903.59
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SIGCM.Domain", "SIGCM.Domain\SIGCM.Domain.csproj", "{0224DD39-C953-4D75-A5AE-D7FB5DC07DFD}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SIGCM.Application", "SIGCM.Application\SIGCM.Application.csproj", "{8B9C32EC-AE46-4882-96B0-62358FD40C84}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SIGCM.Infrastructure", "SIGCM.Infrastructure\SIGCM.Infrastructure.csproj", "{895726EC-FDEF-490D-A78E-98591AC26BA4}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SIGCM.API", "SIGCM.API\SIGCM.API.csproj", "{6AE3C4DB-EBFC-46B4-8231-54CE76BC9D90}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Debug|x64 = Debug|x64
|
||||||
|
Debug|x86 = Debug|x86
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
Release|x64 = Release|x64
|
||||||
|
Release|x86 = Release|x86
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{0224DD39-C953-4D75-A5AE-D7FB5DC07DFD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{0224DD39-C953-4D75-A5AE-D7FB5DC07DFD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{0224DD39-C953-4D75-A5AE-D7FB5DC07DFD}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{0224DD39-C953-4D75-A5AE-D7FB5DC07DFD}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{0224DD39-C953-4D75-A5AE-D7FB5DC07DFD}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{0224DD39-C953-4D75-A5AE-D7FB5DC07DFD}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{0224DD39-C953-4D75-A5AE-D7FB5DC07DFD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{0224DD39-C953-4D75-A5AE-D7FB5DC07DFD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{0224DD39-C953-4D75-A5AE-D7FB5DC07DFD}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{0224DD39-C953-4D75-A5AE-D7FB5DC07DFD}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{0224DD39-C953-4D75-A5AE-D7FB5DC07DFD}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{0224DD39-C953-4D75-A5AE-D7FB5DC07DFD}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{8B9C32EC-AE46-4882-96B0-62358FD40C84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{8B9C32EC-AE46-4882-96B0-62358FD40C84}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{8B9C32EC-AE46-4882-96B0-62358FD40C84}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{8B9C32EC-AE46-4882-96B0-62358FD40C84}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{8B9C32EC-AE46-4882-96B0-62358FD40C84}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{8B9C32EC-AE46-4882-96B0-62358FD40C84}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{8B9C32EC-AE46-4882-96B0-62358FD40C84}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{8B9C32EC-AE46-4882-96B0-62358FD40C84}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{8B9C32EC-AE46-4882-96B0-62358FD40C84}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{8B9C32EC-AE46-4882-96B0-62358FD40C84}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{8B9C32EC-AE46-4882-96B0-62358FD40C84}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{8B9C32EC-AE46-4882-96B0-62358FD40C84}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{895726EC-FDEF-490D-A78E-98591AC26BA4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{895726EC-FDEF-490D-A78E-98591AC26BA4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{895726EC-FDEF-490D-A78E-98591AC26BA4}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{895726EC-FDEF-490D-A78E-98591AC26BA4}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{895726EC-FDEF-490D-A78E-98591AC26BA4}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{895726EC-FDEF-490D-A78E-98591AC26BA4}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{895726EC-FDEF-490D-A78E-98591AC26BA4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{895726EC-FDEF-490D-A78E-98591AC26BA4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{895726EC-FDEF-490D-A78E-98591AC26BA4}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{895726EC-FDEF-490D-A78E-98591AC26BA4}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{895726EC-FDEF-490D-A78E-98591AC26BA4}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{895726EC-FDEF-490D-A78E-98591AC26BA4}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{6AE3C4DB-EBFC-46B4-8231-54CE76BC9D90}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{6AE3C4DB-EBFC-46B4-8231-54CE76BC9D90}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{6AE3C4DB-EBFC-46B4-8231-54CE76BC9D90}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{6AE3C4DB-EBFC-46B4-8231-54CE76BC9D90}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{6AE3C4DB-EBFC-46B4-8231-54CE76BC9D90}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{6AE3C4DB-EBFC-46B4-8231-54CE76BC9D90}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{6AE3C4DB-EBFC-46B4-8231-54CE76BC9D90}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{6AE3C4DB-EBFC-46B4-8231-54CE76BC9D90}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{6AE3C4DB-EBFC-46B4-8231-54CE76BC9D90}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{6AE3C4DB-EBFC-46B4-8231-54CE76BC9D90}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{6AE3C4DB-EBFC-46B4-8231-54CE76BC9D90}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{6AE3C4DB-EBFC-46B4-8231-54CE76BC9D90}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
Reference in New Issue
Block a user