Compare commits
10 Commits
main
...
brokerServ
Author | SHA1 | Date | |
---|---|---|---|
7c310d21f7 | |||
80b002f12a | |||
|
a4dae6211f | ||
|
5d7beec9c1 | ||
78aea1397e | |||
c4899d4a11 | |||
33c3a074da | |||
|
0bf5823652 | ||
|
0c94c926a5 | ||
|
c58d07731c |
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"dotnet.defaultSolution": "Cloud.sln"
|
||||
}
|
22
Cloud.sln
Normal file
22
Cloud.sln
Normal file
@ -0,0 +1,22 @@
|
||||
|
||||
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}") = "Cloud", "Cloud\Cloud.csproj", "{D279AAA9-D4F8-4C7B-B34D-44A7429C87AA}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{D279AAA9-D4F8-4C7B-B34D-44A7429C87AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D279AAA9-D4F8-4C7B-B34D-44A7429C87AA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D279AAA9-D4F8-4C7B-B34D-44A7429C87AA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D279AAA9-D4F8-4C7B-B34D-44A7429C87AA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
30
Cloud/ApplicationContext.cs
Normal file
30
Cloud/ApplicationContext.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using Cloud.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Cloud;
|
||||
public class ApplicationContext : DbContext
|
||||
{
|
||||
public DbSet<User> Users { get; set; } = null!;
|
||||
public DbSet<Farm> Farms { get; set; } = null!;
|
||||
|
||||
public ApplicationContext(DbContextOptions<ApplicationContext> options)
|
||||
: base(options)
|
||||
{
|
||||
}
|
||||
|
||||
public ApplicationContext()
|
||||
: base(new DbContextOptionsBuilder<ApplicationContext>()
|
||||
.UseNpgsql("Host=localhost;Port=5438;Database=main_database;Username=postgres;Password=12345")
|
||||
.Options)
|
||||
{
|
||||
Database.EnsureCreated();
|
||||
}
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
if (!optionsBuilder.IsConfigured)
|
||||
{
|
||||
optionsBuilder.UseNpgsql("Host=localhost;Port=5438;Database=main_database;Username=postgres;Password=12345");
|
||||
}
|
||||
}
|
||||
}
|
24
Cloud/Cloud.csproj
Normal file
24
Cloud/Cloud.csproj
Normal file
@ -0,0 +1,24 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Confluent.Kafka" Version="2.6.0" />
|
||||
<PackageReference Include="FluentValidation.AspNetCore" Version="11.3.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.14" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.6">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.7" />
|
||||
<PackageReference Include="StackExchange.Redis" Version="2.8.16" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.35.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
104
Cloud/Controllers/AuthController.cs
Normal file
104
Cloud/Controllers/AuthController.cs
Normal file
@ -0,0 +1,104 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Cloud.Models;
|
||||
using Cloud.Requests;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Text;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace Cloud.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class AuthController : ControllerBase
|
||||
{
|
||||
private PasswordHasher<User> _passwordHasher;
|
||||
private IConfiguration _config;
|
||||
private ApplicationContext _context;
|
||||
|
||||
public AuthController(IConfiguration config, ApplicationContext context)
|
||||
{
|
||||
_passwordHasher = new PasswordHasher<User>();
|
||||
_config = config;
|
||||
_context = context;
|
||||
}
|
||||
|
||||
[HttpPost("register")]
|
||||
public async Task<IActionResult> Register([FromBody] RegisterRequest request)
|
||||
{
|
||||
var existUser = await _context.Users.SingleOrDefaultAsync(u => u.Email == request.Email);
|
||||
|
||||
if (existUser != null) {
|
||||
return BadRequest("Пользователь с такой эл. почтой уже существует");
|
||||
}
|
||||
|
||||
var user = new User
|
||||
{
|
||||
Name = request.Name,
|
||||
Email = request.Email,
|
||||
Password = _passwordHasher.HashPassword(null, request.Password)
|
||||
};
|
||||
|
||||
_context.Users.Add(user);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return Ok("Пользователь успешно зарегистрирован");
|
||||
}
|
||||
|
||||
[HttpPost("login")]
|
||||
public async Task<IActionResult> Login([FromBody] LoginRequest request)
|
||||
{
|
||||
var user = await _context.Users.SingleOrDefaultAsync(u => u.Email == request.Email);
|
||||
|
||||
if (user == null) {
|
||||
return Unauthorized("Пользователя с такой эл. почтой не существует");
|
||||
}
|
||||
|
||||
var verificationResult = _passwordHasher.VerifyHashedPassword(null, user.Password, request.Password);
|
||||
|
||||
if (verificationResult == PasswordVerificationResult.Failed) {
|
||||
return Unauthorized("Неверный пароль");
|
||||
}
|
||||
|
||||
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
|
||||
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
|
||||
|
||||
var claims = new[]
|
||||
{
|
||||
new Claim(ClaimTypes.Name, user.Email),
|
||||
};
|
||||
|
||||
var Sectoken = new JwtSecurityToken(_config["Jwt:Issuer"],
|
||||
_config["Jwt:Issuer"],
|
||||
claims: claims,
|
||||
expires: DateTime.Now.AddMinutes(120),
|
||||
signingCredentials: credentials);
|
||||
|
||||
var token = new JwtSecurityTokenHandler().WriteToken(Sectoken);
|
||||
|
||||
return Ok(token);
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpGet("user")]
|
||||
public async Task<IActionResult> GetAuthUser()
|
||||
{
|
||||
var userEmail = User.Identity.Name;
|
||||
|
||||
var user = await _context.Users.SingleOrDefaultAsync(u => u.Email == userEmail);
|
||||
|
||||
if (user == null) {
|
||||
return NotFound("Пользователь не найден");
|
||||
}
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
user.Id,
|
||||
user.Name,
|
||||
user.Email
|
||||
});
|
||||
}
|
||||
}
|
128
Cloud/Controllers/FarmController.cs
Normal file
128
Cloud/Controllers/FarmController.cs
Normal file
@ -0,0 +1,128 @@
|
||||
using Cloud.Models;
|
||||
using Cloud.Requests;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Cloud.Controllers
|
||||
{
|
||||
[Authorize]
|
||||
[ApiController]
|
||||
[Route("api/user")]
|
||||
public class FarmController : ControllerBase
|
||||
{
|
||||
private IConfiguration _config;
|
||||
private ApplicationContext _context;
|
||||
|
||||
public FarmController(IConfiguration config, ApplicationContext context)
|
||||
{
|
||||
_config = config;
|
||||
_context = context;
|
||||
}
|
||||
|
||||
[HttpGet("{userId}/farm")]
|
||||
public async Task<ActionResult<List<Farm>>> Index (int userId)
|
||||
{
|
||||
try
|
||||
{
|
||||
List<Farm> farms = await
|
||||
_context.Farms.Where(x => x.UserId == userId).AsNoTracking().ToListAsync();
|
||||
if (!farms.Any())
|
||||
return NotFound("Farms is not found");
|
||||
|
||||
return Ok(farms);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("{userId}/farm/{farmId}")]
|
||||
public async Task<ActionResult<Farm>> Show(int userId, int farmId)
|
||||
{
|
||||
try
|
||||
{
|
||||
Farm? farm = await
|
||||
_context.Farms.FirstOrDefaultAsync(x => x.UserId == userId && x.Id == farmId);
|
||||
|
||||
if (farm == null)
|
||||
return NotFound("Farm is not found");
|
||||
|
||||
return Ok(farm);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("{userId}/farm")]
|
||||
public async Task<ActionResult<Farm>> Create([FromBody] FarmRequest farmRequest, int userId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var farm = new Farm {
|
||||
Name = farmRequest.Name,
|
||||
UserId = userId,
|
||||
RaspberryMacAddr = farmRequest.RaspberryMacAddr,
|
||||
};
|
||||
|
||||
Farm? farmCreated = _context.Farms.Add(farm).Entity;
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return Ok(farmCreated);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPut("{userId}/farm/{farmId}")]
|
||||
public async Task<ActionResult<Farm>> Update([FromBody] FarmRequest farmRequest, int userId, int farmId)
|
||||
{
|
||||
try
|
||||
{
|
||||
Farm? farm = await _context.Farms.FirstOrDefaultAsync(x => x.Id == farmId && x.UserId == userId);
|
||||
|
||||
if (farm == null)
|
||||
return NotFound("Farm is not found");
|
||||
|
||||
farm.Name = farmRequest.Name;
|
||||
farm.RaspberryMacAddr = farmRequest.RaspberryMacAddr;
|
||||
|
||||
_context.Farms.Update(farm);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return Ok(farm);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpDelete("{userId}/farm/{farmId}")]
|
||||
public async Task<ActionResult> Delete(int userId, int farmId)
|
||||
{
|
||||
try
|
||||
{
|
||||
Farm? farm = await _context.Farms.FirstOrDefaultAsync(x => x.Id == farmId && x.UserId == userId);
|
||||
|
||||
if (farm == null)
|
||||
return NotFound("Farm is not found");
|
||||
|
||||
_context.Farms.Remove(farm);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return Ok("Farm deleted successfully");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
41
Cloud/Controllers/ValveController.cs
Normal file
41
Cloud/Controllers/ValveController.cs
Normal file
@ -0,0 +1,41 @@
|
||||
using Cloud.Requests;
|
||||
using Cloud.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Cloud.Controllers
|
||||
{
|
||||
[Authorize]
|
||||
[ApiController]
|
||||
[Route("api")]
|
||||
public class ValveController : ControllerBase
|
||||
{
|
||||
//Контроллер вентиля
|
||||
|
||||
private readonly ProducerService _producerService;
|
||||
|
||||
public ValveController(ProducerService producerService)
|
||||
{
|
||||
_producerService = producerService;
|
||||
}
|
||||
|
||||
[HttpPost("farm/{farmId}/greenhouse/{greenhouseId}/watering")]
|
||||
public async Task<IActionResult> interactValve([FromBody] ValveRequest request, int farmId, int ghId)
|
||||
{
|
||||
var kafkaRequest = new
|
||||
{
|
||||
FarmId = farmId,
|
||||
GreenHouseId = ghId,
|
||||
SomeAction = request.Action,
|
||||
};
|
||||
|
||||
var message = JsonSerializer.Serialize(kafkaRequest);
|
||||
|
||||
await _producerService.ProduceMessageAsync("InventoryUpdates", message);
|
||||
|
||||
return Ok($"Valve status is {request.Action}");
|
||||
}
|
||||
}
|
||||
}
|
32
Cloud/Controllers/WeatherForecastController.cs
Normal file
32
Cloud/Controllers/WeatherForecastController.cs
Normal file
@ -0,0 +1,32 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Cloud.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("[controller]")]
|
||||
public class WeatherForecastController : ControllerBase
|
||||
{
|
||||
private static readonly string[] Summaries = new[]
|
||||
{
|
||||
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
|
||||
};
|
||||
|
||||
private readonly ILogger<WeatherForecastController> _logger;
|
||||
|
||||
public WeatherForecastController(ILogger<WeatherForecastController> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
[HttpGet(Name = "GetWeatherForecast")]
|
||||
public IEnumerable<WeatherForecast> Get()
|
||||
{
|
||||
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
|
||||
{
|
||||
Date = DateTime.Now.AddDays(index),
|
||||
TemperatureC = Random.Shared.Next(-20, 55),
|
||||
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
|
||||
})
|
||||
.ToArray();
|
||||
}
|
||||
}
|
9
Cloud/Enums/ValveEnum.cs
Normal file
9
Cloud/Enums/ValveEnum.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace Cloud.Enums
|
||||
{
|
||||
public enum ValveEnum
|
||||
{
|
||||
Open,
|
||||
Close,
|
||||
Auto
|
||||
}
|
||||
}
|
53
Cloud/Migrations/20241027220558_CreateUsersTable.Designer.cs
generated
Normal file
53
Cloud/Migrations/20241027220558_CreateUsersTable.Designer.cs
generated
Normal file
@ -0,0 +1,53 @@
|
||||
// <auto-generated />
|
||||
using Cloud;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Cloud.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationContext))]
|
||||
[Migration("20241027220558_CreateUsersTable")]
|
||||
partial class CreateUsersTable
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "6.0.14")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("Cloud.Models.User", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("Email")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Password")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Users");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
34
Cloud/Migrations/20241027220558_CreateUsersTable.cs
Normal file
34
Cloud/Migrations/20241027220558_CreateUsersTable.cs
Normal file
@ -0,0 +1,34 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Cloud.Migrations
|
||||
{
|
||||
public partial class CreateUsersTable : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Users",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Name = table.Column<string>(type: "text", nullable: false),
|
||||
Email = table.Column<string>(type: "text", nullable: false),
|
||||
Password = table.Column<string>(type: "text", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Users", x => x.Id);
|
||||
});
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "Users");
|
||||
}
|
||||
}
|
||||
}
|
95
Cloud/Migrations/20241028192806_CreateFarmsTable.Designer.cs
generated
Normal file
95
Cloud/Migrations/20241028192806_CreateFarmsTable.Designer.cs
generated
Normal file
@ -0,0 +1,95 @@
|
||||
// <auto-generated />
|
||||
using Cloud;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Cloud.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationContext))]
|
||||
[Migration("20241028192806_CreateFarmsTable")]
|
||||
partial class CreateFarmsTable
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "6.0.14")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("Cloud.Models.Farm", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("RaspberryMacAddr")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int>("UserId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("Farms");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Cloud.Models.User", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("Email")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Password")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Users");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Cloud.Models.Farm", b =>
|
||||
{
|
||||
b.HasOne("Cloud.Models.User", "User")
|
||||
.WithMany("Farms")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Cloud.Models.User", b =>
|
||||
{
|
||||
b.Navigation("Farms");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
45
Cloud/Migrations/20241028192806_CreateFarmsTable.cs
Normal file
45
Cloud/Migrations/20241028192806_CreateFarmsTable.cs
Normal file
@ -0,0 +1,45 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Cloud.Migrations
|
||||
{
|
||||
public partial class CreateFarmsTable : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Farms",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Name = table.Column<string>(type: "text", nullable: false),
|
||||
UserId = table.Column<int>(type: "integer", nullable: false),
|
||||
RaspberryMacAddr = table.Column<string>(type: "text", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Farms", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Farms_Users_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "Users",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Farms_UserId",
|
||||
table: "Farms",
|
||||
column: "UserId");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "Farms");
|
||||
}
|
||||
}
|
||||
}
|
95
Cloud/Migrations/20241030111034_front.Designer.cs
generated
Normal file
95
Cloud/Migrations/20241030111034_front.Designer.cs
generated
Normal file
@ -0,0 +1,95 @@
|
||||
// <auto-generated />
|
||||
using Cloud;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Cloud.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationContext))]
|
||||
[Migration("20241030111034_front")]
|
||||
partial class front
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "6.0.14")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("Cloud.Models.Farm", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("RaspberryMacAddr")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int>("UserId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("Farms");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Cloud.Models.User", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("Email")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Password")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Users");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Cloud.Models.Farm", b =>
|
||||
{
|
||||
b.HasOne("Cloud.Models.User", "User")
|
||||
.WithMany("Farms")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Cloud.Models.User", b =>
|
||||
{
|
||||
b.Navigation("Farms");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
19
Cloud/Migrations/20241030111034_front.cs
Normal file
19
Cloud/Migrations/20241030111034_front.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Cloud.Migrations
|
||||
{
|
||||
public partial class front : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
93
Cloud/Migrations/ApplicationContextModelSnapshot.cs
Normal file
93
Cloud/Migrations/ApplicationContextModelSnapshot.cs
Normal file
@ -0,0 +1,93 @@
|
||||
// <auto-generated />
|
||||
using Cloud;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Cloud.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationContext))]
|
||||
partial class ApplicationContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "6.0.14")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("Cloud.Models.Farm", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("RaspberryMacAddr")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int>("UserId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("Farms");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Cloud.Models.User", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("Email")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Password")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Users");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Cloud.Models.Farm", b =>
|
||||
{
|
||||
b.HasOne("Cloud.Models.User", "User")
|
||||
.WithMany("Farms")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Cloud.Models.User", b =>
|
||||
{
|
||||
b.Navigation("Farms");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
12
Cloud/Models/Farm.cs
Normal file
12
Cloud/Models/Farm.cs
Normal file
@ -0,0 +1,12 @@
|
||||
namespace Cloud.Models
|
||||
{
|
||||
public class Farm
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public int UserId { get; set; }
|
||||
public User? User { get; set; }
|
||||
public string RaspberryMacAddr { get; set; }
|
||||
|
||||
}
|
||||
}
|
13
Cloud/Models/User.cs
Normal file
13
Cloud/Models/User.cs
Normal file
@ -0,0 +1,13 @@
|
||||
namespace Cloud.Models;
|
||||
public class User
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public string Email { get; set; }
|
||||
|
||||
public string Password { get; set; }
|
||||
|
||||
public List<Farm> Farms { get; set; } = new();
|
||||
}
|
127
Cloud/Program.cs
Normal file
127
Cloud/Program.cs
Normal file
@ -0,0 +1,127 @@
|
||||
using Cloud;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System.Text;
|
||||
using FluentValidation;
|
||||
using FluentValidation.AspNetCore;
|
||||
using Cloud.Validation;
|
||||
using StackExchange.Redis;
|
||||
using Cloud.Services;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Add services to the container.
|
||||
|
||||
//Redis configuration
|
||||
builder.Services.AddSingleton<IConnectionMultiplexer>(sp =>
|
||||
{
|
||||
var configuration = ConfigurationOptions.Parse("localhost:6379");
|
||||
return ConnectionMultiplexer.Connect(configuration);
|
||||
});
|
||||
|
||||
//Kafka producer service
|
||||
builder.Services.AddSingleton<ProducerService, ProducerService>();
|
||||
|
||||
//Kafka consumer service
|
||||
builder.Services.AddSingleton<ConsumerService, ConsumerService>();
|
||||
|
||||
//Add the BackgroundWorkerService
|
||||
builder.Services.AddHostedService<BackgroundWorkerService>();
|
||||
|
||||
//Jwt configuration
|
||||
var jwtIssuer = builder.Configuration.GetSection("Jwt:Issuer").Get<string>();
|
||||
var jwtKey = builder.Configuration.GetSection("Jwt:Key").Get<string>();
|
||||
|
||||
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||
.AddJwtBearer(options =>
|
||||
{
|
||||
options.TokenValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
ValidateIssuer = true,
|
||||
ValidateAudience = true,
|
||||
ValidateLifetime = true,
|
||||
ValidateIssuerSigningKey = true,
|
||||
ValidIssuer = jwtIssuer,
|
||||
ValidAudience = jwtIssuer,
|
||||
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtKey))
|
||||
};
|
||||
});
|
||||
|
||||
builder.Services.AddDbContext<ApplicationContext>(options =>
|
||||
options.UseNpgsql("Host=localhost;Port=5438;Database=main_database;Username=postgres;Password=12345"));
|
||||
|
||||
// Настройка CORS
|
||||
builder.Services.AddCors(options =>
|
||||
{
|
||||
options.AddPolicy("AllowFrontendLocalhost", builder =>
|
||||
{
|
||||
builder.WithOrigins("http://localhost:3000") // фронтенд
|
||||
.AllowAnyHeader()
|
||||
.AllowAnyMethod();
|
||||
});
|
||||
});
|
||||
|
||||
builder.Services.AddControllers();
|
||||
builder.Services.AddFluentValidationAutoValidation();
|
||||
builder.Services.AddFluentValidationClientsideAdapters();
|
||||
builder.Services.AddValidatorsFromAssemblyContaining<LoginValidator>();
|
||||
builder.Services.AddValidatorsFromAssemblyContaining<RegisterValidator>();
|
||||
builder.Services.AddValidatorsFromAssemblyContaining<FarmValidator>();
|
||||
builder.Services.AddValidatorsFromAssemblyContaining<ValveValidator>();
|
||||
|
||||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen(c =>
|
||||
{
|
||||
c.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo { Title = "Cloud API", Version = "v1" });
|
||||
|
||||
c.AddSecurityDefinition("Bearer", new Microsoft.OpenApi.Models.OpenApiSecurityScheme
|
||||
{
|
||||
Description = "Введите ваш Bearer токен",
|
||||
Name = "Authorization",
|
||||
In = Microsoft.OpenApi.Models.ParameterLocation.Header,
|
||||
Type = Microsoft.OpenApi.Models.SecuritySchemeType.ApiKey
|
||||
});
|
||||
|
||||
c.AddSecurityRequirement(new Microsoft.OpenApi.Models.OpenApiSecurityRequirement
|
||||
{
|
||||
{
|
||||
new Microsoft.OpenApi.Models.OpenApiSecurityScheme
|
||||
{
|
||||
Reference = new Microsoft.OpenApi.Models.OpenApiReference
|
||||
{
|
||||
Type = Microsoft.OpenApi.Models.ReferenceType.SecurityScheme,
|
||||
Id = "Bearer"
|
||||
}
|
||||
},
|
||||
new string[] {}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI(c =>
|
||||
{
|
||||
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Cloud API V1");
|
||||
c.RoutePrefix = string.Empty;
|
||||
});
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
// Включение CORS
|
||||
app.UseCors("AllowFrontendLocalhost");
|
||||
|
||||
app.UseAuthentication();
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
app.MapControllers();
|
||||
|
||||
app.Run();
|
31
Cloud/Properties/launchSettings.json
Normal file
31
Cloud/Properties/launchSettings.json
Normal file
@ -0,0 +1,31 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:43967",
|
||||
"sslPort": 44304
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"Cloud": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"applicationUrl": "https://localhost:7113;http://localhost:5124",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
8
Cloud/Requests/FarmRequest.cs
Normal file
8
Cloud/Requests/FarmRequest.cs
Normal file
@ -0,0 +1,8 @@
|
||||
namespace Cloud.Requests
|
||||
{
|
||||
public class FarmRequest
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string RaspberryMacAddr { get; set; }
|
||||
}
|
||||
}
|
13
Cloud/Requests/LoginRequest.cs
Normal file
13
Cloud/Requests/LoginRequest.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Cloud.Requests;
|
||||
|
||||
public class LoginRequest
|
||||
{
|
||||
public string Email { get; set; }
|
||||
|
||||
public string Password { get; set; }
|
||||
}
|
15
Cloud/Requests/RegisterRequest.cs
Normal file
15
Cloud/Requests/RegisterRequest.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Cloud.Requests;
|
||||
|
||||
public class RegisterRequest
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public string Email { get; set; }
|
||||
|
||||
public string Password { get; set; }
|
||||
}
|
7
Cloud/Requests/ValveRequest.cs
Normal file
7
Cloud/Requests/ValveRequest.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace Cloud.Requests
|
||||
{
|
||||
public class ValveRequest
|
||||
{
|
||||
public string Action { get; set; }
|
||||
}
|
||||
}
|
39
Cloud/Services/BackgroundWorkerService.cs
Normal file
39
Cloud/Services/BackgroundWorkerService.cs
Normal file
@ -0,0 +1,39 @@
|
||||
namespace Cloud.Services
|
||||
{
|
||||
public class BackgroundWorkerService : BackgroundService
|
||||
{
|
||||
public readonly ILogger<BackgroundWorkerService> _logger;
|
||||
private readonly ConsumerService _consumerService;
|
||||
|
||||
public BackgroundWorkerService(ILogger<BackgroundWorkerService> logger, ConsumerService consumer)
|
||||
{
|
||||
_logger = logger;
|
||||
_consumerService = consumer;
|
||||
}
|
||||
|
||||
//Backghround Service, This will run continuously
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
//_logger.LogInformation("Background Service is Runing at : {time}", DateTimeOffset.Now);
|
||||
|
||||
string request = await _consumerService.WaitMessage("ValvesHeatersRequest"); //Consume the Kafka Message
|
||||
|
||||
//After Consume the Order Request Can process the order
|
||||
if (!string.IsNullOrEmpty(request))
|
||||
_logger.LogInformation("Valves-Heaters Request : {value}", request);
|
||||
|
||||
|
||||
await Task.Delay(1000, stoppingToken);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError($"BackgroundWorkerService - Exception {ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
51
Cloud/Services/ConsumerService.cs
Normal file
51
Cloud/Services/ConsumerService.cs
Normal file
@ -0,0 +1,51 @@
|
||||
using Confluent.Kafka;
|
||||
|
||||
namespace Cloud.Services
|
||||
{
|
||||
public class ConsumerService
|
||||
{
|
||||
private IConsumer<string, string> _consumer;
|
||||
private ConsumerConfig consumerConfig;
|
||||
public ConsumerService(IConfiguration configuration)
|
||||
{
|
||||
consumerConfig = new ConsumerConfig
|
||||
{
|
||||
BootstrapServers = configuration["Kafka:BootstrapServers"],
|
||||
GroupId = configuration["Kafka:GroupId"],
|
||||
AutoOffsetReset = AutoOffsetReset.Earliest,
|
||||
};
|
||||
|
||||
_consumer = new ConsumerBuilder<string, string>(consumerConfig).Build();
|
||||
}
|
||||
|
||||
//Consume Method
|
||||
public async Task<string>WaitMessage(string topic)
|
||||
{
|
||||
try
|
||||
{
|
||||
_consumer.Subscribe(topic);
|
||||
|
||||
var consumeResult = _consumer.Consume(TimeSpan.FromMilliseconds(1000));
|
||||
|
||||
if (consumeResult != null)
|
||||
{
|
||||
return consumeResult.Message.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
//No message received from Kafka within the specified timeout.
|
||||
}
|
||||
return "";
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
finally
|
||||
{
|
||||
_consumer.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
33
Cloud/Services/ProducerService.cs
Normal file
33
Cloud/Services/ProducerService.cs
Normal file
@ -0,0 +1,33 @@
|
||||
using Confluent.Kafka;
|
||||
|
||||
namespace Cloud.Services
|
||||
{
|
||||
public class ProducerService
|
||||
{
|
||||
private readonly IProducer<string, string> _producer;
|
||||
|
||||
public ProducerService(IConfiguration configuration)
|
||||
{
|
||||
var producerConfig = new ProducerConfig
|
||||
{
|
||||
BootstrapServers = configuration["Kafka:BootstrapServers"]
|
||||
};
|
||||
|
||||
//Build the Producer
|
||||
_producer = new ProducerBuilder<string, string>(producerConfig).Build();
|
||||
}
|
||||
|
||||
//Method for Produce the Message to Kafka Topic
|
||||
public async Task ProduceMessageAsync(string topic, string value)
|
||||
{
|
||||
var kafkaMessage = new Message<string, string>
|
||||
{
|
||||
Key = Guid.NewGuid().ToString(),
|
||||
Value = value
|
||||
};
|
||||
|
||||
//Produce the Message
|
||||
await _producer.ProduceAsync(topic, kafkaMessage);
|
||||
}
|
||||
}
|
||||
}
|
18
Cloud/Validation/FarmValidator.cs
Normal file
18
Cloud/Validation/FarmValidator.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using Cloud.Requests;
|
||||
using FluentValidation;
|
||||
|
||||
namespace Cloud.Validation
|
||||
{
|
||||
public class FarmValidator : AbstractValidator<FarmRequest>
|
||||
{
|
||||
public FarmValidator()
|
||||
{
|
||||
RuleFor(request => request.RaspberryMacAddr)
|
||||
.NotEmpty().WithMessage("MAC address can't be empty")
|
||||
.Matches("^([0-9A-Fa-f]{2}[:-]?){5}([0-9A-Fa-f]{2})$").WithMessage("MAC address is not valid");
|
||||
|
||||
RuleFor(request => request.Name)
|
||||
.NotEmpty().WithMessage("Name can't be empty");
|
||||
}
|
||||
}
|
||||
}
|
18
Cloud/Validation/LoginValidator.cs
Normal file
18
Cloud/Validation/LoginValidator.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using Cloud.Requests;
|
||||
using FluentValidation;
|
||||
|
||||
namespace Cloud.Validation;
|
||||
|
||||
public class LoginValidator : AbstractValidator<LoginRequest>
|
||||
{
|
||||
public LoginValidator()
|
||||
{
|
||||
RuleFor(request => request.Email)
|
||||
.NotEmpty().WithMessage("Email обязателен для заполнения")
|
||||
.EmailAddress().WithMessage("Некорректный формат Email");
|
||||
|
||||
RuleFor(request => request.Password)
|
||||
.NotEmpty().WithMessage("Пароль обязателен для заполнения")
|
||||
.MinimumLength(8).WithMessage("Пароль должен быть не менее 8 символов");
|
||||
}
|
||||
}
|
22
Cloud/Validation/RegisterValidator.cs
Normal file
22
Cloud/Validation/RegisterValidator.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using Cloud.Requests;
|
||||
using FluentValidation;
|
||||
|
||||
namespace Cloud.Validation;
|
||||
|
||||
public class RegisterValidator : AbstractValidator<RegisterRequest>
|
||||
{
|
||||
public RegisterValidator()
|
||||
{
|
||||
RuleFor(user => user.Name)
|
||||
.NotEmpty().WithMessage("Имя обязательно для заполнения")
|
||||
.MaximumLength(50).WithMessage("Имя должно быть не более 50 символов");
|
||||
|
||||
RuleFor(user => user.Email)
|
||||
.NotEmpty().WithMessage("Email обязателен для заполнения")
|
||||
.EmailAddress().WithMessage("Некорректный формат Email");
|
||||
|
||||
RuleFor(user => user.Password)
|
||||
.NotEmpty().WithMessage("Пароль обязателен для заполнения")
|
||||
.MinimumLength(8).WithMessage("Пароль должен быть не менее 8 символов");
|
||||
}
|
||||
}
|
16
Cloud/Validation/ValveValidator.cs
Normal file
16
Cloud/Validation/ValveValidator.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using Cloud.Enums;
|
||||
using Cloud.Requests;
|
||||
using FluentValidation;
|
||||
|
||||
namespace Cloud.Validation
|
||||
{
|
||||
public class ValveValidator : AbstractValidator<ValveRequest>
|
||||
{
|
||||
public ValveValidator() {
|
||||
|
||||
RuleFor(request => request.Action)
|
||||
.NotEmpty().WithMessage("Action can't be empty").
|
||||
IsEnumName(typeof (ValveEnum)).WithMessage("Action is not correct");
|
||||
}
|
||||
}
|
||||
}
|
12
Cloud/WeatherForecast.cs
Normal file
12
Cloud/WeatherForecast.cs
Normal file
@ -0,0 +1,12 @@
|
||||
namespace Cloud;
|
||||
|
||||
public class WeatherForecast
|
||||
{
|
||||
public DateTime Date { get; set; }
|
||||
|
||||
public int TemperatureC { get; set; }
|
||||
|
||||
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
|
||||
|
||||
public string? Summary { get; set; }
|
||||
}
|
8
Cloud/appsettings.Development.json
Normal file
8
Cloud/appsettings.Development.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
17
Cloud/appsettings.json
Normal file
17
Cloud/appsettings.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"Kafka": {
|
||||
"BootstrapServers": "localhost:9092",
|
||||
"GroupId": "ValvesHeaters"
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"Jwt": {
|
||||
"Key": "m7TyhE20s0dVtUDAr9EnFdPZnAG8maxgBTaiW5j6kO6RQhWDAGxYmXyu0suDnE0o",
|
||||
"Issuer": "localhost"
|
||||
}
|
||||
}
|
31
docker-compose.yml
Normal file
31
docker-compose.yml
Normal file
@ -0,0 +1,31 @@
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:14
|
||||
container_name: cucumber_database
|
||||
environment:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: 12345
|
||||
POSTGRES_DB: main_database
|
||||
ports:
|
||||
- "5438:5432"
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
|
||||
redis:
|
||||
image: 'redis:latest'
|
||||
ports:
|
||||
- '6379:6379'
|
||||
volumes:
|
||||
- 'cloud-redis:/data'
|
||||
healthcheck:
|
||||
test:
|
||||
- CMD
|
||||
- redis-cli
|
||||
- ping
|
||||
retries: 3
|
||||
timeout: 5s
|
||||
volumes:
|
||||
postgres_data:
|
||||
driver: local
|
||||
cloud-redis:
|
||||
driver: local
|
Loading…
Reference in New Issue
Block a user