Добавьте файлы проекта.
This commit is contained in:
79
Yuna.Website/Yuna.Website.Server/API/AuthEndpoints.cs
Normal file
79
Yuna.Website/Yuna.Website.Server/API/AuthEndpoints.cs
Normal file
@ -0,0 +1,79 @@
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Security.Claims;
|
||||
using Yuna.Website.Server.Infrastructure;
|
||||
using System.Text.Json.Serialization;
|
||||
using Yuna.Website.Server.Services.TokenService;
|
||||
using Yuna.Website.Server.Model;
|
||||
using Yuna.Website.Server.Services.UserService;
|
||||
|
||||
namespace Yuna.Website.Server.API
|
||||
{
|
||||
public class AuthEndpoints
|
||||
{
|
||||
public void Define(WebApplication app)
|
||||
{
|
||||
app.MapPost("/api/auth/login", Login)
|
||||
.WithTags("auth")
|
||||
.Produces(200)
|
||||
.Produces(401)
|
||||
.Produces(400);
|
||||
|
||||
app.MapPost("/api/user/register", RegisterUser)
|
||||
.WithTags("auth")
|
||||
.Produces(200)
|
||||
.Produces(400);
|
||||
|
||||
}
|
||||
|
||||
public class LoginRequest
|
||||
{
|
||||
[JsonPropertyName("password")]
|
||||
public string RawPassword { get; set; } = null!;
|
||||
|
||||
[JsonPropertyName("username")]
|
||||
public string UserName { get; set; } = null!;
|
||||
}
|
||||
public async Task<IResult> Login(HttpContext context, [FromBody] LoginRequest request, IUserService userService, ITokenService tokenService)
|
||||
{
|
||||
var userFromDb = await userService.GetByUsername(request.UserName);
|
||||
if (userFromDb is null) return Results.Unauthorized();
|
||||
|
||||
var hashedPassword = Encrypter.HashPassword(request.RawPassword, request.UserName);
|
||||
if (!hashedPassword.Equals(userFromDb.HashedPassword)) return Results.Unauthorized();
|
||||
await SetAccessToken(context, tokenService, userFromDb);
|
||||
return Results.Ok();
|
||||
}
|
||||
|
||||
|
||||
private static async Task SetAccessToken(HttpContext context, ITokenService tokenService, User userFromDb)
|
||||
{
|
||||
var identity = tokenService.CreateAccessToken(userFromDb);
|
||||
|
||||
await context.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(identity));
|
||||
}
|
||||
|
||||
public class RegisterUserRequest
|
||||
{
|
||||
public string username { get; set; } = null!;
|
||||
public string password { get; set; } = null!;
|
||||
public string referalCode { get; set; } = null!;
|
||||
}
|
||||
|
||||
public async Task<IResult> RegisterUser([FromBody] RegisterUserRequest dto, IUserService userService)
|
||||
{
|
||||
|
||||
if (!dto.referalCode.Equals(Settings.ReferalCode)) return Results.BadRequest();
|
||||
|
||||
var hashedPassword = Encrypter.HashPassword(dto.password, dto.username);
|
||||
|
||||
var userToRegister = new User(dto.username, hashedPassword);
|
||||
|
||||
var result = await userService.Create(userToRegister);
|
||||
if (result is null) return Results.BadRequest();
|
||||
|
||||
return Results.Ok();
|
||||
}
|
||||
}
|
||||
}
|
87
Yuna.Website/Yuna.Website.Server/API/DeviceEndpoints.cs
Normal file
87
Yuna.Website/Yuna.Website.Server/API/DeviceEndpoints.cs
Normal file
@ -0,0 +1,87 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Text.Json.Serialization;
|
||||
using Yuna.Website.Server.Services.DeviceSkillService;
|
||||
using Yuna.Website.Server.Services.DeviceService;
|
||||
using Yuna.Website.Server.Model;
|
||||
|
||||
namespace Yuna.Website.Server.API
|
||||
{
|
||||
public class DeviceEndpoints
|
||||
{
|
||||
public void Define(WebApplication app)
|
||||
{
|
||||
app.MapPost("/api/device", CreateDevice)
|
||||
.WithTags("device");
|
||||
|
||||
app.MapDelete("/api/device/{id:long}", () => { })
|
||||
.WithTags("device");
|
||||
|
||||
app.MapGet("/api/device/{id:long}", () => { })
|
||||
.WithTags("device");
|
||||
|
||||
app.MapGet("/api/device", GetAll)
|
||||
.WithTags("device");
|
||||
|
||||
app.MapPut("/api/device/{deviceId:long}", AddSkillsToDevice)
|
||||
.WithTags("device");
|
||||
}
|
||||
|
||||
|
||||
public class CreateDeviceResult
|
||||
{
|
||||
[JsonPropertyName("name")]
|
||||
public string Name { get; set; } = null!;
|
||||
|
||||
[JsonPropertyName("description")]
|
||||
public string Description { get; set; } = "";
|
||||
|
||||
[JsonPropertyName("deviceUrl")]
|
||||
public string DeviceUrl { get; set; } = null!;
|
||||
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
public async Task<IResult> CreateDevice([FromBody] CreateDeviceResult request, IDeviceService deviceService)
|
||||
{
|
||||
var device = new Device()
|
||||
{
|
||||
Description = request.Description,
|
||||
DeviceUrl = request.DeviceUrl,
|
||||
Name = request.Name
|
||||
};
|
||||
|
||||
var result = await deviceService.Create(device);
|
||||
if (result is null) return Results.BadRequest();
|
||||
|
||||
|
||||
return Results.Ok(result);
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
public async Task<IResult> GetAll(IDeviceService deviceService)
|
||||
{
|
||||
var result = await deviceService.GetList();
|
||||
return Results.Ok(result);
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
public async Task<IResult> Delete(IDeviceService deviceService, long id)
|
||||
{
|
||||
var result = await deviceService.Delete(id);
|
||||
if (result is null) return Results.NotFound();
|
||||
return Results.Ok(result);
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
public async Task<IResult> AddSkillsToDevice([FromBody] long[] skillsIds, long deviceId, IDeviceService deviceService, IPropService skillService)
|
||||
{
|
||||
var skills = await skillService.GetByIds(skillsIds);
|
||||
if (skills is null) return Results.NotFound("not all skills exist");
|
||||
|
||||
var result = await deviceService.AddProps(skills, deviceId);
|
||||
if (result is null) return Results.NotFound("device");
|
||||
return Results.Ok(result);
|
||||
}
|
||||
}
|
||||
}
|
71
Yuna.Website/Yuna.Website.Server/API/SkillsEndpoints.cs
Normal file
71
Yuna.Website/Yuna.Website.Server/API/SkillsEndpoints.cs
Normal file
@ -0,0 +1,71 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Text.Json.Serialization;
|
||||
using Yuna.Website.Server.Model;
|
||||
using Yuna.Website.Server.Services.DeviceSkillService;
|
||||
|
||||
namespace Yuna.Website.Server.API
|
||||
{
|
||||
public class SkillsEndpoints
|
||||
{
|
||||
|
||||
public void Define(WebApplication app)
|
||||
{
|
||||
app.MapPost("/api/skill", CreateSkill)
|
||||
.Produces(200)
|
||||
.WithTags("skill");
|
||||
|
||||
app.MapDelete("/api/skill/{id:long}", () => { })
|
||||
.Produces(200)
|
||||
.WithTags("skill");
|
||||
|
||||
app.MapGet("/api/skill/{id:long}", () => { })
|
||||
.Produces(200)
|
||||
.WithTags("skill");
|
||||
|
||||
app.MapGet("/api/skill", GetAllSkills)
|
||||
.Produces(200)
|
||||
.WithTags("skill");
|
||||
}
|
||||
|
||||
|
||||
public class CreateSkillRequest
|
||||
{
|
||||
public string type { get; init; } = null!;
|
||||
|
||||
[JsonPropertyName("measureName")]
|
||||
public string? MeasureName { get; init; } = null!;
|
||||
|
||||
[JsonPropertyName("jsonValueName")]
|
||||
public string JsonValueName { get; init; } = null!;
|
||||
|
||||
[JsonPropertyName("name")]
|
||||
public String Name { get; init; } = null!;
|
||||
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
public async Task<IResult> CreateSkill([FromBody] CreateSkillRequest request, IPropService skillService)
|
||||
{
|
||||
Prop prop = new Prop()
|
||||
{
|
||||
JsonValueName = request.JsonValueName,
|
||||
MeasureName = request.MeasureName ?? "",
|
||||
Name = request.Name
|
||||
};
|
||||
|
||||
var result = await skillService.Create(prop);
|
||||
if (result is null) return Results.BadRequest();
|
||||
|
||||
|
||||
return Results.Ok(result);
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
public async Task<IResult> GetAllSkills(IPropService skillService)
|
||||
{
|
||||
var result = await skillService.GetList();
|
||||
return Results.Ok(result);
|
||||
}
|
||||
}
|
||||
}
|
32
Yuna.Website/Yuna.Website.Server/Dockerfile
Normal file
32
Yuna.Website/Yuna.Website.Server/Dockerfile
Normal file
@ -0,0 +1,32 @@
|
||||
#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
|
||||
USER app
|
||||
WORKDIR /app
|
||||
EXPOSE 8080
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS with-node
|
||||
RUN apt-get update
|
||||
RUN apt-get install curl
|
||||
RUN curl -sL https://deb.nodesource.com/setup_20.x | bash
|
||||
RUN apt-get -y install nodejs
|
||||
|
||||
|
||||
FROM with-node AS build
|
||||
ARG BUILD_CONFIGURATION=Release
|
||||
WORKDIR /src
|
||||
COPY ["Yuna.Website/Yuna.Website.Server/Yuna.Website.Server.csproj", "Yuna.Website/Yuna.Website.Server/"]
|
||||
COPY ["yuna.website.client/yuna.website.client.esproj", "yuna.website.client/"]
|
||||
RUN dotnet restore "./Yuna.Website/Yuna.Website.Server/Yuna.Website.Server.csproj"
|
||||
COPY . .
|
||||
WORKDIR "/src/Yuna.Website/Yuna.Website.Server"
|
||||
RUN dotnet build "./Yuna.Website.Server.csproj" -c $BUILD_CONFIGURATION -o /app/build
|
||||
|
||||
FROM build AS publish
|
||||
ARG BUILD_CONFIGURATION=Release
|
||||
RUN dotnet publish "./Yuna.Website.Server.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
|
||||
|
||||
FROM base AS final
|
||||
WORKDIR /app
|
||||
COPY --from=publish /app/publish .
|
||||
ENTRYPOINT ["dotnet", "Yuna.Website.Server.dll"]
|
18
Yuna.Website/Yuna.Website.Server/GlobalSettings.json
Normal file
18
Yuna.Website/Yuna.Website.Server/GlobalSettings.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"AppVariables": {
|
||||
"AccessTokenLifeTimeMinutes": 1,
|
||||
"RefreshTokenLifeTimeDays": 365,
|
||||
"ReferalCode": "#I_@m_g00d_guy_07_07_2024"
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"BarcodeService": "http://localhost:7799/barcode",
|
||||
"Db": "User ID=developer;Password=magazinchik_forever;Server=localhost;Port=5432;Database=Yuna;Pooling=true;IncludeErrorDetail=true;",
|
||||
"UserDb": "User ID=developer;Password=magazinchik_forever;Server=localhost;Port=5432;Database=UserKeeper;Pooling=true;",
|
||||
"RabbitMq": "host=localhost;username=guest;password=guest",
|
||||
"Redis": "localhost:5002"
|
||||
},
|
||||
"ExternalLinks": {
|
||||
"BookInfo": "https://m.books.ru/ajax/search_autocomplete.php",
|
||||
"BookPictures": "https://book24.ru"
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
using System.Collections;
|
||||
|
||||
namespace Yuna.Website.Server.Infrastructure
|
||||
{
|
||||
public static class CollectionExtensions
|
||||
{
|
||||
public static bool IsNullOrEmpty<T>(this T? collection) where T : IList
|
||||
{
|
||||
if (collection is null) return true;
|
||||
if (collection.Count == 0) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
namespace Yuna.Website.Server.Infrastructure
|
||||
{
|
||||
public static class CookieExtensions
|
||||
{
|
||||
public static T LoadFromCookies<T>(this HttpContext context, string key)
|
||||
{
|
||||
if (context.Request.Cookies.TryGetValue(key, out var value))
|
||||
{
|
||||
try
|
||||
{
|
||||
return System.Text.Json.JsonSerializer.Deserialize<T>(value)
|
||||
?? throw new Exception();
|
||||
}
|
||||
|
||||
catch
|
||||
{
|
||||
return default!;
|
||||
}
|
||||
}
|
||||
|
||||
return default!;
|
||||
}
|
||||
|
||||
public static void SaveToCookies<T>(this HttpContext context, string key, T value, TimeSpan? maxAge = null)
|
||||
{
|
||||
var dataStr = System.Text.Json.JsonSerializer.Serialize<T>(value);
|
||||
|
||||
if (context.Request.Cookies.ContainsKey(key)) context.Response.Cookies.Delete(key);
|
||||
|
||||
context.Response.Cookies.Append(key, dataStr, new CookieOptions() { HttpOnly = true, MaxAge = maxAge ?? TimeSpan.FromDays(365) });
|
||||
}
|
||||
|
||||
}
|
||||
}
|
31
Yuna.Website/Yuna.Website.Server/Infrastructure/Encrypter.cs
Normal file
31
Yuna.Website/Yuna.Website.Server/Infrastructure/Encrypter.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace Yuna.Website.Server.Infrastructure
|
||||
{
|
||||
public class Encrypter
|
||||
{
|
||||
private static HMACSHA256 _passwordHasher = new() { Key = [2, 2, 2, 2, 2, 2, 22, 2, 2, 2, 2, 2, 2, 2] };
|
||||
private static HMACSHA256 _tokenHasher = new() { Key = [21, 12, 21, 21, 2, 11, 111, 2, 2, 21, 2, 21, 2, 2] };
|
||||
public static string HashPassword(string password, string username)
|
||||
{
|
||||
|
||||
var loweredUsername = username.ToLower();
|
||||
|
||||
var hashedPassword = Convert.ToBase64String(_passwordHasher.ComputeHash(Encoding.UTF8.GetBytes(password + loweredUsername)))
|
||||
?? throw new ArgumentNullException("didnt manage to generate password");
|
||||
return hashedPassword;
|
||||
}
|
||||
|
||||
public static byte[] CreateTokenSalt(long userId, DateTime creationTime)
|
||||
{
|
||||
return Encoding.UTF8.GetBytes(userId.ToString() + creationTime.ToString("dd_mm_yy___hh_ss"));
|
||||
}
|
||||
|
||||
public static string HashTokenSalt(byte[] bytesStr)
|
||||
{
|
||||
var hashedSalt = _tokenHasher.ComputeHash(bytesStr);
|
||||
return Convert.ToBase64String(hashedSalt);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using System.Net;
|
||||
|
||||
namespace Yuna.Website.Server.Infrastructure
|
||||
{
|
||||
public class HttpHelper
|
||||
{
|
||||
public static Func<RedirectContext<CookieAuthenticationOptions>, Task> BypassWithStatusCode(HttpStatusCode statusCode)
|
||||
{
|
||||
return context =>
|
||||
{
|
||||
context.Response.StatusCode = (int)statusCode;
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
}
|
||||
|
||||
public static Func<RedirectContext<CookieAuthenticationOptions>, Task> BypassWithStatusCode(int statusCode)
|
||||
{
|
||||
return context =>
|
||||
{
|
||||
context.Response.StatusCode = statusCode;
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
53
Yuna.Website/Yuna.Website.Server/Infrastructure/Settings.cs
Normal file
53
Yuna.Website/Yuna.Website.Server/Infrastructure/Settings.cs
Normal file
@ -0,0 +1,53 @@
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Yuna.Website.Server.Infrastructure
|
||||
{
|
||||
public static class Settings
|
||||
{
|
||||
public static TimeSpan AccessTokenLifeTime { get; private set; }
|
||||
public static TimeSpan RefreshTokenLifeTime { get; private set; }
|
||||
public static string ReferalCode { get; private set; } = null!;
|
||||
public static string DbConnectionStr { get; private set; } = null!;
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
var jsonText = File.ReadAllText("globalSettings.json");
|
||||
using JsonDocument document = JsonDocument.Parse(jsonText);
|
||||
|
||||
var root = document.RootElement;
|
||||
|
||||
|
||||
var connectionStrs = root.GetProperty("ConnectionStrings");
|
||||
LoadConnectionStrs(connectionStrs);
|
||||
|
||||
var externalLinkStrs = root.GetProperty("ExternalLinks");
|
||||
LoadExternalLinks(externalLinkStrs);
|
||||
|
||||
var appVariablesStr = root.GetProperty("AppVariables");
|
||||
LoadAppVariables(appVariablesStr);
|
||||
|
||||
|
||||
string appName = AppDomain.CurrentDomain.FriendlyName;
|
||||
//DistributedCacheExtensions.Init(appName + "_");
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static void LoadConnectionStrs(JsonElement connectionStrs)
|
||||
{
|
||||
DbConnectionStr = connectionStrs.GetProperty("Db").GetString()!;
|
||||
}
|
||||
|
||||
private static void LoadExternalLinks(JsonElement externalLinksStr)
|
||||
{
|
||||
}
|
||||
|
||||
private static void LoadAppVariables(JsonElement appVariablesStr)
|
||||
{
|
||||
ReferalCode = appVariablesStr.GetProperty("ReferalCode").GetString() ?? throw new Exception("No ref code");
|
||||
AccessTokenLifeTime = TimeSpan.FromSeconds(appVariablesStr.GetProperty("AccessTokenLifeTimeMinutes").GetInt32());
|
||||
RefreshTokenLifeTime = TimeSpan.FromDays(appVariablesStr.GetProperty("RefreshTokenLifeTimeDays").GetInt32());
|
||||
}
|
||||
}
|
||||
}
|
21
Yuna.Website/Yuna.Website.Server/Model/Device.cs
Normal file
21
Yuna.Website/Yuna.Website.Server/Model/Device.cs
Normal file
@ -0,0 +1,21 @@
|
||||
namespace Yuna.Website.Server.Model
|
||||
{
|
||||
public class Device
|
||||
{
|
||||
public Device(string name, string description)
|
||||
{
|
||||
Name = name;
|
||||
Description = description;
|
||||
}
|
||||
public Device()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public long Id { get; set; }
|
||||
public string Name { get; set; } = null!;
|
||||
public string Description { get; set; } = null!;
|
||||
public List<Prop> Props { get; set; } = [];
|
||||
public string DeviceUrl { get; set; } = null!;
|
||||
}
|
||||
}
|
10
Yuna.Website/Yuna.Website.Server/Model/Prop.cs
Normal file
10
Yuna.Website/Yuna.Website.Server/Model/Prop.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace Yuna.Website.Server.Model
|
||||
{
|
||||
public class Prop
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public string Name { get; set; } = null!;
|
||||
public string MeasureName { get; set; } = null!;
|
||||
public string JsonValueName { get; set; } = null!;
|
||||
}
|
||||
}
|
19
Yuna.Website/Yuna.Website.Server/Model/User.cs
Normal file
19
Yuna.Website/Yuna.Website.Server/Model/User.cs
Normal file
@ -0,0 +1,19 @@
|
||||
namespace Yuna.Website.Server.Model
|
||||
{
|
||||
public class User
|
||||
{
|
||||
public User(string userName, string hashedPassword)
|
||||
{
|
||||
UserName = userName;
|
||||
HashedPassword = hashedPassword;
|
||||
}
|
||||
|
||||
User()
|
||||
{ }
|
||||
|
||||
public long Id { get; init; }
|
||||
public string UserName { get; set; } = null!;
|
||||
public string HashedPassword { get; set; } = null!;
|
||||
public bool IsAdmin { get; set; } = false;
|
||||
}
|
||||
}
|
15
Yuna.Website/Yuna.Website.Server/Program.cs
Normal file
15
Yuna.Website/Yuna.Website.Server/Program.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using Yuna.Website.Server;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
Starter.RegisterServices(builder);
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
Starter.Configure(app);
|
||||
|
||||
Starter.DefineEndpoints(app);
|
||||
|
||||
Starter.ApplyMigrations(app);
|
||||
|
||||
app.Run();
|
@ -0,0 +1,42 @@
|
||||
{
|
||||
"profiles": {
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development",
|
||||
"ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.SpaProxy"
|
||||
},
|
||||
"dotnetRunMessages": true,
|
||||
"applicationUrl": "http://localhost:5227"
|
||||
},
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development",
|
||||
"ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.SpaProxy"
|
||||
}
|
||||
},
|
||||
"Container (Dockerfile)": {
|
||||
"commandName": "Docker",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_HTTP_PORTS": "8080"
|
||||
},
|
||||
"publishAllPorts": true
|
||||
}
|
||||
},
|
||||
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:52505",
|
||||
"sslPort": 0
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
|
||||
using Yuna.Website.Server.Storage;
|
||||
using Yuna.Website.Server.Model;
|
||||
|
||||
using Yuna.Website.Server.Services.DeviceSkillService;
|
||||
using Yuna.Website.Server.Storage.Repositories.Device;
|
||||
|
||||
namespace Yuna.Website.Server.Services.DeviceService
|
||||
{
|
||||
public class DeviceService : IDeviceService
|
||||
{
|
||||
private readonly IPropService _propService;
|
||||
private readonly IDeviceRepository _deviceRepository;
|
||||
public DeviceService(IPropService propService, IDeviceRepository repository)
|
||||
{
|
||||
_propService = propService;
|
||||
_deviceRepository = repository;
|
||||
}
|
||||
|
||||
public async Task<Device?> AddProps(IReadOnlyList<Prop> props, long deviceId)
|
||||
{
|
||||
var device = await _deviceRepository.GetById(deviceId);
|
||||
if (device is null) return null;
|
||||
|
||||
await _deviceRepository.AddProps(props, deviceId);
|
||||
|
||||
device.Props.AddRange(props);
|
||||
return device;
|
||||
}
|
||||
|
||||
public async Task<Device?> Create(Device device)
|
||||
{
|
||||
var result = await _deviceRepository.Create(device);
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<Device?> Delete(long id)
|
||||
{
|
||||
var prop = await _deviceRepository.GetById(id);
|
||||
if (prop is null) return null;
|
||||
|
||||
var result = await _deviceRepository.Delete(id);
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<Device?> GetById(long id)
|
||||
{
|
||||
var result = await _deviceRepository.GetById(id);
|
||||
return result;
|
||||
}
|
||||
public async Task<IReadOnlyList<Device>> GetList()
|
||||
{
|
||||
var result = await _deviceRepository.GetList();
|
||||
return result ?? [];
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
using Yuna.Website.Server.Model;
|
||||
|
||||
namespace Yuna.Website.Server.Services.DeviceService
|
||||
{
|
||||
public interface IDeviceService
|
||||
{
|
||||
public Task<Device?> GetById(long id);
|
||||
public Task<IReadOnlyList<Device>> GetList();
|
||||
public Task<Device?> Create(Device device);
|
||||
//public Task<User?> Update(User user);
|
||||
public Task<Device?> Delete(long id);
|
||||
public Task<Device?> AddProps(IReadOnlyList<Prop> props, long deviceId);
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
using Yuna.Website.Server.Model;
|
||||
|
||||
namespace Yuna.Website.Server.Services.DeviceSkillService
|
||||
{
|
||||
public interface IPropService
|
||||
{
|
||||
public Task<Prop?> GetById(long id);
|
||||
public Task<Prop?> GetByPropName(string value);
|
||||
public Task<IReadOnlyList<Prop>> GetList();
|
||||
public Task<Prop?> Create(Prop value);
|
||||
//public Task<User?> Update(User user);
|
||||
public Task<Prop?> Delete(long id);
|
||||
public Task<IReadOnlyList<Prop>?> GetByIds(long[] ids);
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
using Yuna.Website.Server.Model;
|
||||
using Yuna.Website.Server.Storage.Repositories.Prop;
|
||||
|
||||
|
||||
namespace Yuna.Website.Server.Services.DeviceSkillService
|
||||
{
|
||||
public class PropService : IPropService
|
||||
{
|
||||
private readonly IPropRepository _propRepository;
|
||||
public PropService(IPropRepository propRepository)
|
||||
{
|
||||
_propRepository = propRepository;
|
||||
}
|
||||
|
||||
public async Task<Prop?> Create(Prop prop)
|
||||
{
|
||||
var existingProp = await _propRepository.GetByPropName(prop.Name);
|
||||
if (existingProp is not null) return null;
|
||||
|
||||
var result = await _propRepository.Create(prop);
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<Prop?> Delete(long id)
|
||||
{
|
||||
var prop = await _propRepository.GetById(id);
|
||||
if (prop is null) return null;
|
||||
|
||||
var result = await _propRepository.Delete(id);
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<Prop?> GetById(long id)
|
||||
{
|
||||
var result = await _propRepository.GetById(id);
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<Prop>?> GetByIds(long[] ids)
|
||||
{
|
||||
if (ids.Count() == 0) return [];
|
||||
|
||||
var props = await _propRepository.GetByIds(ids);
|
||||
return props;
|
||||
}
|
||||
|
||||
public async Task<Prop?> GetByPropName(string name)
|
||||
{
|
||||
var result = await _propRepository.GetByPropName(name);
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<Prop>> GetList()
|
||||
{
|
||||
var result = await _propRepository.GetList();
|
||||
return result ?? [];
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
using System.Security.Claims;
|
||||
using Yuna.Website.Server.Model;
|
||||
|
||||
namespace Yuna.Website.Server.Services.TokenService
|
||||
{
|
||||
public interface ITokenService
|
||||
{
|
||||
ClaimsIdentity CreateAccessToken(User user);
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
using System.Security.Claims;
|
||||
using Yuna.Website.Server.Infrastructure;
|
||||
using Yuna.Website.Server.Model;
|
||||
|
||||
namespace Yuna.Website.Server.Services.TokenService
|
||||
{
|
||||
public class TokenService : ITokenService
|
||||
{
|
||||
//private readonly UserKeeperService _userKeeperService;
|
||||
private readonly ILogger<TokenService> _logger;
|
||||
public TokenService(ILogger<TokenService> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public ClaimsIdentity CreateAccessToken(User user)
|
||||
{
|
||||
var claims = new List<Claim>()
|
||||
{
|
||||
new(ClaimTypes.Name, user.UserName),
|
||||
new(ClaimTypes.NameIdentifier, user.Id.ToString()),
|
||||
};
|
||||
|
||||
return new ClaimsIdentity(claims, "Cookies");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
using System.Collections.Generic;
|
||||
using Yuna.Website.Server.Model;
|
||||
|
||||
namespace Yuna.Website.Server.Services.UserService
|
||||
{
|
||||
public interface IUserService
|
||||
{
|
||||
public Task<User?> GetById(long id);
|
||||
public Task<User?> GetByUsername(string username);
|
||||
public Task<IReadOnlyList<User>> GetList();
|
||||
public Task<User?> Create(User user);
|
||||
public Task<User?> Update(User user);
|
||||
public Task<User?> Delete(long id);
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
using Yuna.Website.Server.Model;
|
||||
using Yuna.Website.Server.Storage.Repositories.User;
|
||||
|
||||
namespace Yuna.Website.Server.Services.UserService
|
||||
{
|
||||
public class UserService : IUserService
|
||||
{
|
||||
private readonly IUserRepository _userRepository;
|
||||
public UserService(IUserRepository userRepository)
|
||||
{
|
||||
_userRepository = userRepository;
|
||||
}
|
||||
|
||||
public async Task<User?> Create(User user)
|
||||
{
|
||||
var existingUser = await _userRepository.GetByUsername(user.UserName);
|
||||
if (existingUser is not null) return null;
|
||||
|
||||
var result = await _userRepository.Create(user);
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<User?> Delete(long id)
|
||||
{
|
||||
var user = await _userRepository.GetById(id);
|
||||
if (user is null) return null;
|
||||
|
||||
var result = await _userRepository.Delete(id);
|
||||
return user;
|
||||
}
|
||||
|
||||
public async Task<User?> GetById(long id)
|
||||
{
|
||||
var result = await _userRepository.GetById(id);
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<User?> GetByUsername(string username)
|
||||
{
|
||||
var user = await _userRepository.GetByUsername(username);
|
||||
return user;
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<User>> GetList()
|
||||
{
|
||||
var result = await _userRepository.GetList();
|
||||
return result ?? [];
|
||||
}
|
||||
|
||||
public async Task<User?> Update(User user)
|
||||
{
|
||||
var userFromDb = await _userRepository.GetById(user.Id);
|
||||
if(userFromDb is null) return null;
|
||||
|
||||
return await _userRepository.Update(user);
|
||||
}
|
||||
}
|
||||
}
|
209
Yuna.Website/Yuna.Website.Server/Starter.cs
Normal file
209
Yuna.Website/Yuna.Website.Server/Starter.cs
Normal file
@ -0,0 +1,209 @@
|
||||
using Serilog;
|
||||
using Yuna.Website.Server.API;
|
||||
using Yuna.Website.Server.Infrastructure;
|
||||
using Yuna.Website.Server.Services.DeviceService;
|
||||
using Yuna.Website.Server.Services.DeviceSkillService;
|
||||
using Yuna.Website.Server.Services.TokenService;
|
||||
using Yuna.Website.Server.Services.UserService;
|
||||
using Yuna.Website.Server.Storage;
|
||||
using Yuna.Website.Server.Storage.Repositories.Device;
|
||||
using Yuna.Website.Server.Storage.Repositories.Prop;
|
||||
|
||||
namespace Yuna.Website.Server
|
||||
{
|
||||
public class Starter
|
||||
{
|
||||
public static void LoadConfigs(WebApplicationBuilder builder)
|
||||
{
|
||||
Settings.Init();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Логгирование через Serilog: логгирует все в консоль
|
||||
/// </summary>
|
||||
/// <param name="builder"></param>
|
||||
private static void DefineLogger(WebApplicationBuilder builder)
|
||||
{
|
||||
builder.Logging.ClearProviders();
|
||||
var logger = new LoggerConfiguration()
|
||||
.WriteTo.Console()
|
||||
/* .WriteTo.File($"{DateTime.Now.ToString("dd_MM_yyyy_HH_mm_ss_f")}.txt") */
|
||||
//.Filter.ByIncludingOnly(x => x.Level == Serilog.Events.LogEventLevel.Error)
|
||||
.CreateLogger();
|
||||
|
||||
builder.Logging.AddSerilog(logger);
|
||||
}
|
||||
|
||||
private static void DefineDb(WebApplicationBuilder builder)
|
||||
{
|
||||
builder.Services.AddSingleton<DapperContext>();
|
||||
Console.ForegroundColor = ConsoleColor.DarkGreen;
|
||||
Console.WriteLine($"Added Db host: {Settings.DbConnectionStr}");
|
||||
Console.ResetColor();
|
||||
}
|
||||
|
||||
public static void ApplyMigrations(WebApplication app)
|
||||
{
|
||||
//using var scope = app.Services.CreateScope();
|
||||
//var services = scope.ServiceProvider;
|
||||
|
||||
//var context = services.GetRequiredService<AppDbContext>();
|
||||
//if (context.Database.GetPendingMigrations().Any())
|
||||
//{
|
||||
// Console.ForegroundColor = ConsoleColor.DarkGreen;
|
||||
// Console.WriteLine($"Applying migrations...");
|
||||
// Console.ResetColor();
|
||||
// context.Database.Migrate();
|
||||
//}
|
||||
}
|
||||
|
||||
public static void DefineEndpoints(WebApplication app)
|
||||
{
|
||||
new AuthEndpoints().Define(app);
|
||||
new SkillsEndpoints().Define(app);
|
||||
new DeviceEndpoints().Define(app);
|
||||
//new FacadeEndpoints().Define(app);
|
||||
|
||||
Console.ForegroundColor = ConsoleColor.DarkGreen;
|
||||
Console.WriteLine("Endpoints registered");
|
||||
Console.ResetColor();
|
||||
}
|
||||
|
||||
public static void DefineHttpClient(WebApplicationBuilder builder)
|
||||
{
|
||||
builder.Services.AddHttpClient();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Подключение кеша для журнала состояний
|
||||
/// </summary>
|
||||
/// <param name="builder"></param>
|
||||
public static void DefineCache(WebApplicationBuilder builder)
|
||||
{
|
||||
//builder.Services.AddStackExchangeRedisCache(x =>
|
||||
//{
|
||||
// x.Configuration = builder.Configuration.GetConnectionString("Redis")
|
||||
// ?? throw new Exception("no redis str");
|
||||
// x.InstanceName = "api_";
|
||||
//});
|
||||
|
||||
//Console.ForegroundColor = ConsoleColor.DarkGreen;
|
||||
//Console.WriteLine($"Added redis host: {builder.Configuration.GetConnectionString("Redis")}");
|
||||
//Console.ResetColor();
|
||||
}
|
||||
|
||||
public static void DefineMessaging(WebApplicationBuilder builder)
|
||||
{
|
||||
//builder.Services.RegisterEasyNetQ(Settings.RabbitMQHost, s => s.EnableSystemTextJson());
|
||||
//DefineQueuePublishers(builder);
|
||||
//DefineQueueSubscribers(builder);
|
||||
//DefineRpcClients(builder);
|
||||
//DefineRpcServers(builder);
|
||||
}
|
||||
|
||||
public static void DefineQueuePublishers(WebApplicationBuilder builder)
|
||||
{
|
||||
//builder.Services.AddSingleton<ITimeEventPublisher, TimeEventPublisher>();
|
||||
}
|
||||
|
||||
public static void DefineQueueSubscribers(WebApplicationBuilder builder)
|
||||
{
|
||||
//builder.Services.AddSingleton<ApiRpcConsumer>();
|
||||
}
|
||||
|
||||
public static void DefineRpcServers(WebApplicationBuilder builder)
|
||||
{
|
||||
//builder.Services.AddHostedService<TelegramReplier>();
|
||||
}
|
||||
|
||||
public static void DefineRpcClients(WebApplicationBuilder builder)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public static void DefineCustomServices(WebApplicationBuilder builder)
|
||||
{
|
||||
builder.Services.AddScoped<IUserService, UserService>();
|
||||
builder.Services.AddScoped<IPropService, PropService>();
|
||||
builder.Services.AddScoped<ITokenService, TokenService>();
|
||||
builder.Services.AddScoped<IDeviceService, DeviceService>();
|
||||
|
||||
builder.Services.AddScoped<IDeviceRepository, DeviceRepository>();
|
||||
builder.Services.AddScoped<IPropRepository, PropRepository>();
|
||||
}
|
||||
|
||||
public static void DefineAuth(WebApplicationBuilder builder)
|
||||
{
|
||||
builder.Services.AddAuthorization();
|
||||
builder.Services
|
||||
.AddAuthentication()
|
||||
.AddCookie(options =>
|
||||
{
|
||||
options.Cookie = new()
|
||||
{
|
||||
HttpOnly = true,
|
||||
IsEssential = true,
|
||||
Name = "access_token",
|
||||
MaxAge = TimeSpan.FromDays(180)
|
||||
};
|
||||
options.SlidingExpiration = false;
|
||||
options.ExpireTimeSpan = Settings.RefreshTokenLifeTime;
|
||||
options.Events = new()
|
||||
{
|
||||
OnRedirectToAccessDenied = HttpHelper.BypassWithStatusCode(403),
|
||||
OnRedirectToLogin = HttpHelper.BypassWithStatusCode(401)
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public static void RegisterServices(WebApplicationBuilder builder)
|
||||
{
|
||||
LoadConfigs(builder);
|
||||
|
||||
DefineMessaging(builder);
|
||||
|
||||
DefineCustomServices(builder);
|
||||
|
||||
DefineDb(builder);
|
||||
|
||||
DefineLogger(builder);
|
||||
|
||||
DefineCache(builder);
|
||||
|
||||
DefineHttpClient(builder);
|
||||
|
||||
DefineAuth(builder);
|
||||
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
|
||||
builder.Services.AddSwaggerGen();
|
||||
|
||||
builder.Services.AddCors();
|
||||
|
||||
|
||||
//builder.Services.AddAutoMapper(typeof(ApplicationProfile));
|
||||
}
|
||||
|
||||
|
||||
public static void Configure(WebApplication app)
|
||||
{
|
||||
app.UseCors(x => x.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
|
||||
app.UseDefaultFiles();
|
||||
app.UseStaticFiles();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI();
|
||||
}
|
||||
|
||||
//app.UseHttpsRedirection();
|
||||
|
||||
app.MapFallbackToFile("/index.html");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
19
Yuna.Website/Yuna.Website.Server/Storage/AppDbContext.cs
Normal file
19
Yuna.Website/Yuna.Website.Server/Storage/AppDbContext.cs
Normal file
@ -0,0 +1,19 @@
|
||||
//using Microsoft.EntityFrameworkCore;
|
||||
//using Yuna.Website.Server.Model;
|
||||
//using Yuna.Website.Server.Storage.StorageModel;
|
||||
//using Yuna.Website.Server.Storage.StorageModel.Skill;
|
||||
|
||||
//namespace Yuna.Website.Server.Storage
|
||||
//{
|
||||
// public class AppDbContext : DbContext
|
||||
// {
|
||||
// public AppDbContext()
|
||||
// {
|
||||
// }
|
||||
|
||||
// //public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
|
||||
// //public virtual DbSet<UserInStorage> Users => Set<UserInStorage>();
|
||||
// //public virtual DbSet<SkillInStorage> Skills => Set<SkillInStorage>();
|
||||
// //public virtual DbSet<DeviceInStorage> Devices => Set<DeviceInStorage>();
|
||||
// }
|
||||
//}
|
34
Yuna.Website/Yuna.Website.Server/Storage/DapperContext.cs
Normal file
34
Yuna.Website/Yuna.Website.Server/Storage/DapperContext.cs
Normal file
@ -0,0 +1,34 @@
|
||||
using Microsoft.Data.SqlClient;
|
||||
using Npgsql;
|
||||
using System.Data;
|
||||
using Yuna.Website.Server.Infrastructure;
|
||||
|
||||
namespace Yuna.Website.Server.Storage
|
||||
{
|
||||
public class DapperContext
|
||||
{
|
||||
private readonly IConfiguration _configuration;
|
||||
|
||||
public DapperContext(IConfiguration configuration)
|
||||
{
|
||||
_configuration = configuration;
|
||||
}
|
||||
|
||||
public DapperContext(bool isTest)
|
||||
{
|
||||
_configuration = null!;
|
||||
}
|
||||
|
||||
private IDbConnection? _dbConnection;
|
||||
public IDbConnection Connection
|
||||
{
|
||||
get
|
||||
{
|
||||
|
||||
if (_dbConnection is null) _dbConnection = new NpgsqlConnection(Settings.DbConnectionStr);
|
||||
return _dbConnection;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
using Dapper;
|
||||
using System.Data;
|
||||
|
||||
namespace Yuna.Website.Server.Storage.Repositories.Device
|
||||
{
|
||||
public class DeviceRepository : IDeviceRepository
|
||||
{
|
||||
private readonly DapperContext _context;
|
||||
|
||||
public DeviceRepository(DapperContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task AddProps(IReadOnlyList<Model.Prop> props, long deviceId)
|
||||
{
|
||||
var query =
|
||||
$@"INSERT INTO ""Yuna_Props_In_Devices""
|
||||
(""PropId"", ""DeviceId"")
|
||||
VALUES
|
||||
{string.Join(", ", props.Select(prop => $"({prop.Id}, {deviceId})"))}";
|
||||
|
||||
await _context.Connection.ExecuteAsync(query);
|
||||
}
|
||||
|
||||
public async Task<Model.Device?> Create(Model.Device device)
|
||||
{
|
||||
var query =
|
||||
$@"INSERT INTO ""Yuna_Devices""
|
||||
(""Name"", ""Description"", ""DeviceUrl"")
|
||||
VALUES
|
||||
('{device.Name}', '{device.Description}', '{device.DeviceUrl}')
|
||||
RETURNING *";
|
||||
|
||||
var result = await _context.Connection.QuerySingleAsync<Model.Device?>(query, device);
|
||||
return result;
|
||||
}
|
||||
|
||||
public Task<Model.Device?> Delete(long id)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public async Task<Model.Device> GetById(long id)
|
||||
{
|
||||
var query =
|
||||
$@"SELECT
|
||||
d.""Id"" as {nameof(Model.Device.Id)},
|
||||
d.""Name"" as {nameof(Model.Device.Name)},
|
||||
d.""Description"" as {nameof(Model.Device.Description)},
|
||||
p.""Id"" as {nameof(Model.Prop.Id)},
|
||||
p.""Name"" as {nameof(Model.Prop.Name)},
|
||||
p.""JsonValueName"" as {nameof(Model.Prop.JsonValueName)},
|
||||
p.""MeasureName"" as {nameof(Model.Prop.MeasureName)}
|
||||
FROM ""Yuna_Devices"" d
|
||||
LEFT JOIN ""Yuna_Props_In_Devices"" pd ON d.""Id"" = pd.""DeviceId""
|
||||
LEFT JOIN ""Yuna_Props"" p ON pd.""PropId"" = p.""Id""
|
||||
WHERE d.""Id"" = {id}
|
||||
LIMIT 1";
|
||||
|
||||
var result = await _context.Connection.QuerySingleAsync<Model.Device>(query);
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<Model.Device>> GetList()
|
||||
{
|
||||
var query =
|
||||
$@"SELECT
|
||||
d.""Id"" as {nameof(Model.Device.Id)},
|
||||
d.""Name"" as {nameof(Model.Device.Name)},
|
||||
d.""Description"" as {nameof(Model.Device.Description)},
|
||||
p.""Id"" as {nameof(Model.Prop.Id)},
|
||||
p.""Name"" as {nameof(Model.Prop.Name)},
|
||||
p.""JsonValueName"" as {nameof(Model.Prop.JsonValueName)},
|
||||
p.""MeasureName"" as {nameof(Model.Prop.MeasureName)}
|
||||
FROM ""Yuna_Devices"" d
|
||||
LEFT JOIN ""Yuna_Props_In_Devices"" pd ON d.""Id"" = pd.""DeviceId""
|
||||
LEFT JOIN ""Yuna_Props"" p ON pd.""PropId"" = p.""Id""";
|
||||
|
||||
var result = await _context.Connection.QueryAsync<Model.Device>(query);
|
||||
return result.ToList();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
using Yuna.Website.Server.Model;
|
||||
|
||||
namespace Yuna.Website.Server.Storage.Repositories.Device
|
||||
{
|
||||
public interface IDeviceRepository
|
||||
{
|
||||
public Task<Model.Device> GetById(long id);
|
||||
public Task<IReadOnlyList<Model.Device>> GetList();
|
||||
public Task<Model.Device?> Create(Model.Device device);
|
||||
//public Task<User?> Update(User user);
|
||||
public Task<Model.Device?> Delete(long id);
|
||||
public Task AddProps(IReadOnlyList<Model.Prop> skills, long deviceId);
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
namespace Yuna.Website.Server.Storage.Repositories.Prop
|
||||
{
|
||||
public interface IPropRepository
|
||||
{
|
||||
public Task<Model.Prop?> GetById(long id);
|
||||
public Task<Model.Prop?> GetByPropName(string value);
|
||||
public Task<IReadOnlyList<Model.Prop>> GetList();
|
||||
public Task<Model.Prop?> Create(Model.Prop value);
|
||||
//public Task<User?> Update(User user);
|
||||
public Task<Model.Prop?> Delete(long id);
|
||||
public Task<IReadOnlyList<Model.Prop>?> GetByIds(long[] ids);
|
||||
}
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
using Dapper;
|
||||
|
||||
namespace Yuna.Website.Server.Storage.Repositories.Prop
|
||||
{
|
||||
public class PropRepository : IPropRepository
|
||||
{
|
||||
private readonly DapperContext _context;
|
||||
|
||||
public PropRepository(DapperContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<Model.Prop?> Create(Model.Prop value)
|
||||
{
|
||||
var query =
|
||||
$@"INSERT INTO ""Yuna_Props""
|
||||
(""Name"", ""MeasureName"", ""JsonValueName"")
|
||||
VALUES
|
||||
(@{nameof(Model.Prop.Name)}, @{nameof(Model.Prop.MeasureName)}, @{nameof(Model.Prop.JsonValueName)})
|
||||
RETURNING *";
|
||||
|
||||
var result = await _context.Connection.QuerySingleAsync<Model.Prop?>(query, value);
|
||||
return result;
|
||||
}
|
||||
|
||||
public Task<Model.Prop?> Delete(long id)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public async Task<Model.Prop?> GetById(long id)
|
||||
{
|
||||
var query =
|
||||
$@"SELECT
|
||||
p.""Id"" as {nameof(Model.Prop.Id)},
|
||||
p.""Name"" as {nameof(Model.Prop.Name)},
|
||||
p.""MeasureName"" as {nameof(Model.Prop.MeasureName)},
|
||||
p.""JsonValueName"" as {nameof(Model.Prop.JsonValueName)}
|
||||
FROM ""Yuna_Props"" p
|
||||
WHERE p.""Id"" = {id}
|
||||
LIMIT 1";
|
||||
|
||||
var result = await _context.Connection.QuerySingleOrDefaultAsync<Model.Prop>(query);
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<Model.Prop>?> GetByIds(long[] ids)
|
||||
{
|
||||
var idList = string.Join(", ", ids);
|
||||
var query =
|
||||
$@"SELECT
|
||||
p.""Id"" as {nameof(Model.Prop.Id)},
|
||||
p.""Name"" as {nameof(Model.Prop.Name)},
|
||||
p.""MeasureName"" as {nameof(Model.Prop.MeasureName)},
|
||||
p.""JsonValueName"" as {nameof(Model.Prop.JsonValueName)}
|
||||
FROM ""Yuna_Props"" p
|
||||
WHERE p.""Id"" IN ({idList})";
|
||||
|
||||
var result = await _context.Connection.QueryAsync<Model.Prop>(query);
|
||||
if (result.Count() != ids.Length) return null;
|
||||
return result.ToList();
|
||||
}
|
||||
|
||||
public async Task<Model.Prop?> GetByPropName(string value)
|
||||
{
|
||||
var query =
|
||||
$@"SELECT
|
||||
p.""Id"" as {nameof(Model.Prop.Id)},
|
||||
p.""Name"" as {nameof(Model.Prop.Name)},
|
||||
p.""MeasureName"" as {nameof(Model.Prop.MeasureName)},
|
||||
p.""JsonValueName"" as {nameof(Model.Prop.JsonValueName)}
|
||||
FROM ""Yuna_Props"" p
|
||||
WHERE LOWER(p.""Name"") = '{value.ToLower()}'
|
||||
LIMIT 1";
|
||||
|
||||
var result = await _context.Connection.QuerySingleOrDefaultAsync<Model.Prop>(query);
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<Model.Prop>> GetList()
|
||||
{
|
||||
var query =
|
||||
$@"SELECT
|
||||
p.""Id"" as {nameof(Model.Prop.Id)},
|
||||
p.""Name"" as {nameof(Model.Prop.Name)},
|
||||
p.""MeasureName"" as {nameof(Model.Prop.MeasureName)},
|
||||
p.""JsonValueName"" as {nameof(Model.Prop.JsonValueName)}
|
||||
FROM ""Yuna_Props"" p";
|
||||
|
||||
var result = await _context.Connection.QueryAsync<Model.Prop>(query);
|
||||
return result.ToList();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
namespace Yuna.Website.Server.Storage.Repositories.User
|
||||
{
|
||||
public interface IUserRepository
|
||||
{
|
||||
public Task<Model.User?> GetById(long id);
|
||||
public Task<Model.User?> GetByUsername(string username);
|
||||
public Task<IReadOnlyList<Model.User>> GetList();
|
||||
public Task<Model.User?> Create(Model.User? user);
|
||||
public Task<Model.User?> Update(Model.User? user);
|
||||
public Task<Model.User?> Delete(long id);
|
||||
}
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
|
||||
using Dapper;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Yuna.Website.Server.Model;
|
||||
|
||||
namespace Yuna.Website.Server.Storage.Repositories.User
|
||||
{
|
||||
public class UserRepository : IUserRepository
|
||||
{
|
||||
private readonly DapperContext _context;
|
||||
|
||||
public UserRepository(DapperContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
public async Task<Model.User?> Create(Model.User? user)
|
||||
{
|
||||
var query =
|
||||
$@"INSERT INTO ""Yuna_Users""
|
||||
(""Username"", ""HashedPassword"", ""IsAdmin"")
|
||||
VALUES
|
||||
(@{nameof(Model.User.UserName)}, @{nameof(Model.User.HashedPassword)}, @{nameof(Model.User.IsAdmin)})
|
||||
RETURNING *
|
||||
";
|
||||
|
||||
var result = await _context.Connection.QuerySingleAsync<Model.User?>(query, user);
|
||||
return result;
|
||||
}
|
||||
|
||||
public Task<Model.User?> Delete(long id)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public async Task<Model.User?> GetById(long id)
|
||||
{
|
||||
var query =
|
||||
$@"SELECT
|
||||
u.""Id"" as {nameof(Model.User.Id)},
|
||||
u.""Username"" as {nameof(Model.User.UserName)},
|
||||
u.""HashedPassword"" as {nameof(Model.User.HashedPassword)},
|
||||
u.""IsAdmin"" as {nameof(Model.User.IsAdmin)}
|
||||
FROM ""Yuna_Users"" u
|
||||
WHERE u.""Id"" = {id}
|
||||
LIMIT 1";
|
||||
|
||||
var result = await _context.Connection.QuerySingleAsync<Model.User>(query);
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<Model.User?> GetByUsername(string username)
|
||||
{
|
||||
var query =
|
||||
$@"SELECT
|
||||
u.""Id"" as {nameof(Model.User.Id)},
|
||||
u.""Username"" as {nameof(Model.User.UserName)},
|
||||
u.""HashedPassword"" as {nameof(Model.User.HashedPassword)},
|
||||
u.""IsAdmin"" as {nameof(Model.User.IsAdmin)}
|
||||
FROM ""Yuna_Users"" u
|
||||
WHERE LOWER(u.""Username"") = '{username.ToLower()}'
|
||||
LIMIT 1";
|
||||
|
||||
var result = await _context.Connection.QuerySingleOrDefaultAsync<Model.User>(query);
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<Model.User>> GetList()
|
||||
{
|
||||
var query =
|
||||
$@"SELECT
|
||||
u.""Id"" as {nameof(Model.User.Id)},
|
||||
u.""Username"" as {nameof(Model.User.UserName)},
|
||||
u.""HashedPassword"" as {nameof(Model.User.HashedPassword)},
|
||||
u.""IsAdmin"" as {nameof(Model.User.IsAdmin)}
|
||||
FROM ""Yuna_Users"" u";
|
||||
|
||||
var result = await _context.Connection.QueryAsync<Model.User>(query);
|
||||
return result.ToList();
|
||||
}
|
||||
|
||||
public async Task<Model.User?> Update(Model.User? user)
|
||||
{
|
||||
var query = $@"
|
||||
UPDATE ""Yuna_Users""
|
||||
SET
|
||||
""Username"" = @{nameof(Model.User.UserName)},
|
||||
""HashedPassword"" = @{nameof(Model.User.HashedPassword)},
|
||||
""IsAdmin"" = {nameof(Model.User.IsAdmin)}
|
||||
""WHERE Id"" = @{nameof(Model.User.Id)}
|
||||
RETURNING *
|
||||
";
|
||||
|
||||
var result = await _context.Connection.QuerySingleOrDefaultAsync<Model.User>(query, user);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
35
Yuna.Website/Yuna.Website.Server/Yuna.Website.Server.csproj
Normal file
35
Yuna.Website/Yuna.Website.Server/Yuna.Website.Server.csproj
Normal file
@ -0,0 +1,35 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
<DockerfileContext>..\..</DockerfileContext>
|
||||
<SpaRoot>..\yuna.website.client</SpaRoot>
|
||||
<SpaProxyLaunchCommand>npm run dev</SpaProxyLaunchCommand>
|
||||
<SpaProxyServerUrl>https://localhost:5173</SpaProxyServerUrl>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Dapper" Version="2.1.35" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SpaProxy">
|
||||
<Version>8.*-*</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.2.1" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.6" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="Npgsql" Version="8.0.3" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
|
||||
<PackageReference Include="Serilog" Version="3.1.1" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="8.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\yuna.website.client\yuna.website.client.esproj">
|
||||
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
@ -0,0 +1,6 @@
|
||||
@Yuna.Website.Server_HostAddress = http://localhost:5227
|
||||
|
||||
GET {{Yuna.Website.Server_HostAddress}}/weatherforecast/
|
||||
Accept: application/json
|
||||
|
||||
###
|
@ -0,0 +1,8 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
9
Yuna.Website/Yuna.Website.Server/appsettings.json
Normal file
9
Yuna.Website/Yuna.Website.Server/appsettings.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
Reference in New Issue
Block a user