Merge branch 'FarmCRUD' into dev

mac to ip, dockerfile and docker-compose
This commit is contained in:
mfnefd 2024-11-13 02:08:51 +04:00
commit 49c7d14925
11 changed files with 272 additions and 181 deletions

25
.dockerignore Normal file
View File

@ -0,0 +1,25 @@
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/bin
**/charts
**/docker-compose*
**/compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md

35
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,35 @@
{
"version": "0.2.0",
"configurations": [
{
// Use IntelliSense to find out which attributes exist for C# debugging
// Use hover for the description of the existing attributes
// For further information visit https://github.com/dotnet/vscode-csharp/blob/main/debugger-launchjson.md.
"name": ".NET Core Launch (web)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/Cloud/bin/Debug/net6.0/Cloud.dll",
"args": [],
"cwd": "${workspaceFolder}/Cloud",
"stopAtEntry": false,
// Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser
"serverReadyAction": {
"action": "openExternally",
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
},
"env": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"sourceFileMap": {
"/Views": "${workspaceFolder}/Views"
}
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach"
}
]
}

41
.vscode/tasks.json vendored Normal file
View File

@ -0,0 +1,41 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/Cloud.sln",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary;ForceNoAlign"
],
"problemMatcher": "$msCompile"
},
{
"label": "publish",
"command": "dotnet",
"type": "process",
"args": [
"publish",
"${workspaceFolder}/Cloud.sln",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary;ForceNoAlign"
],
"problemMatcher": "$msCompile"
},
{
"label": "watch",
"command": "dotnet",
"type": "process",
"args": [
"watch",
"run",
"--project",
"${workspaceFolder}/Cloud.sln"
],
"problemMatcher": "$msCompile"
}
]
}

View File

@ -6,123 +6,123 @@ using Microsoft.EntityFrameworkCore;
namespace Cloud.Controllers namespace Cloud.Controllers
{ {
[Authorize] [Authorize]
[ApiController] [ApiController]
[Route("api/user")] [Route("api/user")]
public class FarmController : ControllerBase public class FarmController : ControllerBase
{ {
private IConfiguration _config; private IConfiguration _config;
private ApplicationContext _context; private ApplicationContext _context;
public FarmController(IConfiguration config, ApplicationContext context) public FarmController(IConfiguration config, ApplicationContext context)
{ {
_config = config; _config = config;
_context = context; _context = context;
} }
[HttpGet("{userId}/farm")] [HttpGet("{userId}/farm")]
public async Task<ActionResult<List<Farm>>> Index (int userId) public async Task<ActionResult<List<Farm>>> Index(int userId)
{ {
try try
{ {
List<Farm> farms = await List<Farm> farms = await
_context.Farms.Where(x => x.UserId == userId).AsNoTracking().ToListAsync(); _context.Farms.Where(x => x.UserId == userId).AsNoTracking().ToListAsync();
if (!farms.Any()) if (!farms.Any())
return NotFound("Farms is not found"); return NotFound("Farms is not found");
return Ok(farms); return Ok(farms);
} }
catch (Exception ex) catch (Exception ex)
{ {
return BadRequest(ex.Message); return BadRequest(ex.Message);
} }
} }
[HttpGet("{userId}/farm/{farmId}")] [HttpGet("{userId}/farm/{farmId}")]
public async Task<ActionResult<Farm>> Show(int userId, int farmId) public async Task<ActionResult<Farm>> Show(int userId, int farmId)
{ {
try try
{ {
Farm? farm = await Farm? farm = await
_context.Farms.FirstOrDefaultAsync(x => x.UserId == userId && x.Id == farmId); _context.Farms.FirstOrDefaultAsync(x => x.UserId == userId && x.Id == farmId);
if (farm == null) if (farm == null)
return NotFound("Farm is not found"); return NotFound("Farm is not found");
return Ok(farm); return Ok(farm);
} }
catch (Exception ex) catch (Exception ex)
{ {
return BadRequest(ex.Message); return BadRequest(ex.Message);
} }
} }
[HttpPost("{userId}/farm")] [HttpPost("{userId}/farm")]
public async Task<ActionResult<Farm>> Create([FromBody] FarmRequest farmRequest, int userId) public async Task<ActionResult<Farm>> Create([FromBody] FarmRequest farmRequest, int userId)
{ {
try try
{ {
var farm = new Farm { var farm = new Farm
Name = farmRequest.Name, {
UserId = userId, Name = farmRequest.Name,
RaspberryMacAddr = farmRequest.RaspberryMacAddr, UserId = userId,
}; RaspberryIP = farmRequest.RaspberryIP,
};
Farm? farmCreated = _context.Farms.Add(farm).Entity; Farm? farmCreated = _context.Farms.Add(farm).Entity;
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
return Ok(farmCreated); return Ok(farmCreated);
} }
catch (Exception ex) catch (Exception ex)
{ {
return BadRequest(ex.Message); return BadRequest(ex.Message);
} }
} }
[HttpPut("{userId}/farm/{farmId}")] [HttpPut("{userId}/farm/{farmId}")]
public async Task<ActionResult<Farm>> Update([FromBody] FarmRequest farmRequest, int userId, int farmId) public async Task<ActionResult<Farm>> Update([FromBody] FarmRequest farmRequest, int userId, int farmId)
{ {
try try
{ {
Farm? farm = await _context.Farms.FirstOrDefaultAsync(x => x.Id == farmId && x.UserId == userId); Farm? farm = await _context.Farms.FirstOrDefaultAsync(x => x.Id == farmId && x.UserId == userId);
if (farm == null) if (farm == null)
return NotFound("Farm is not found"); return NotFound("Farm is not found");
farm.Name = farmRequest.Name; farm.Name = farmRequest.Name;
farm.RaspberryMacAddr = farmRequest.RaspberryMacAddr; farm.RaspberryIP = farmRequest.RaspberryIP;
_context.Farms.Update(farm); _context.Farms.Update(farm);
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
return Ok(farm); return Ok(farm);
} }
catch (Exception ex) catch (Exception ex)
{ {
return BadRequest(ex.Message); return BadRequest(ex.Message);
} }
} }
[HttpDelete("{userId}/farm/{farmId}")] [HttpDelete("{userId}/farm/{farmId}")]
public async Task<ActionResult> Delete(int userId, int farmId) public async Task<ActionResult> Delete(int userId, int farmId)
{ {
try try
{ {
Farm? farm = await _context.Farms.FirstOrDefaultAsync(x => x.Id == farmId && x.UserId == userId); Farm? farm = await _context.Farms.FirstOrDefaultAsync(x => x.Id == farmId && x.UserId == userId);
if (farm == null) if (farm == null)
return NotFound("Farm is not found"); return NotFound("Farm is not found");
_context.Farms.Remove(farm); _context.Farms.Remove(farm);
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
return Ok("Farm deleted successfully"); return Ok("Farm deleted successfully");
} }
catch (Exception ex) catch (Exception ex)
{ {
return BadRequest(ex.Message); return BadRequest(ex.Message);
} }
} }
}
} }
}

View File

@ -1,32 +0,0 @@
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();
}
}

29
Cloud/Dockerfile Normal file
View File

@ -0,0 +1,29 @@
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 5124
ENV ASPNETCORE_URLS=http://+:5124
# Creates a non-root user with an explicit UID and adds permission to access the /app folder
# For more info, please refer to https://aka.ms/vscode-docker-dotnet-configure-containers
RUN adduser -u 5678 --disabled-password --gecos "" appuser && chown -R appuser /app
USER appuser
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
ARG configuration=Release
WORKDIR /src
COPY ["Cloud.csproj", "."]
RUN dotnet restore "./Cloud.csproj"
COPY . .
WORKDIR "/src/."
RUN dotnet build "./Cloud.csproj" -c $configuration -o /app/build
FROM build AS publish
ARG configuration=Release
RUN dotnet publish "./Cloud.csproj" -c $configuration -o /app/publish /p:UseAppHost=false
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "Cloud.dll"]

View File

@ -1,12 +1,11 @@
namespace Cloud.Models namespace Cloud.Models
{ {
public class Farm public class Farm
{ {
public int Id { get; set; } public int Id { get; set; }
public string Name { get; set; } public string Name { get; set; }
public int UserId { get; set; } public int UserId { get; set; }
public User? User { get; set; } public User? User { get; set; }
public string RaspberryMacAddr { get; set; } public string RaspberryIP { get; set; }
}
} }
}

View File

@ -1,8 +1,8 @@
namespace Cloud.Requests namespace Cloud.Requests
{ {
public class FarmRequest public class FarmRequest
{ {
public string Name { get; set; } public string Name { get; set; }
public string RaspberryMacAddr { get; set; } public string RaspberryIP { get; set; }
} }
} }

View File

@ -3,16 +3,16 @@ using FluentValidation;
namespace Cloud.Validation namespace Cloud.Validation
{ {
public class FarmValidator : AbstractValidator<FarmRequest> public class FarmValidator : AbstractValidator<FarmRequest>
{ {
public FarmValidator() public FarmValidator()
{ {
RuleFor(request => request.RaspberryMacAddr) RuleFor(request => request.RaspberryIP)
.NotEmpty().WithMessage("MAC address can't be empty") .NotEmpty().WithMessage("IP address can't be empty")
.Matches("^([0-9A-Fa-f]{2}[:-]?){5}([0-9A-Fa-f]{2})$").WithMessage("MAC address is not valid"); .Matches(@"^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$").WithMessage("IP address is not valid");
RuleFor(request => request.Name) RuleFor(request => request.Name)
.NotEmpty().WithMessage("Name can't be empty"); .NotEmpty().WithMessage("Name can't be empty");
} }
} }
} }

View File

@ -1,12 +0,0 @@
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; }
}

View File

@ -1,16 +1,22 @@
services: services:
cloud:
build: ./Cloud/
ports:
- "5124:5124"
depends_on:
- postgres
- redis
postgres: postgres:
image: postgres:14 image: postgres:14
container_name: cucumber_database container_name: cucumber_database
environment: environment:
POSTGRES_USER: postgres POSTGRES_USER: postgres
POSTGRES_PASSWORD: 12345 POSTGRES_PASSWORD: 12345
POSTGRES_DB: main_database POSTGRES_DB: main_database
ports: ports:
- "5438:5432" - "5438:5432"
volumes: volumes:
- postgres_data:/var/lib/postgresql/data - postgres_data:/var/lib/postgresql/data
redis: redis:
image: 'redis:latest' image: 'redis:latest'
ports: ports:
@ -28,4 +34,4 @@ volumes:
postgres_data: postgres_data:
driver: local driver: local
cloud-redis: cloud-redis:
driver: local driver: local