diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..3dbbcf3 --- /dev/null +++ b/.dockerignore @@ -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 diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..e8a7501 --- /dev/null +++ b/.vscode/launch.json @@ -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" + } + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..d410234 --- /dev/null +++ b/.vscode/tasks.json @@ -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" + } + ] +} \ No newline at end of file diff --git a/Cloud/Controllers/FarmController.cs b/Cloud/Controllers/FarmController.cs index a1e7b73..b5cf078 100644 --- a/Cloud/Controllers/FarmController.cs +++ b/Cloud/Controllers/FarmController.cs @@ -6,123 +6,123 @@ using Microsoft.EntityFrameworkCore; namespace Cloud.Controllers { - [Authorize] - [ApiController] - [Route("api/user")] - public class FarmController : ControllerBase - { - private IConfiguration _config; - private ApplicationContext _context; + [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; - } + public FarmController(IConfiguration config, ApplicationContext context) + { + _config = config; + _context = context; + } - [HttpGet("{userId}/farm")] - public async Task>> Index (int userId) - { - try - { - List farms = await - _context.Farms.Where(x => x.UserId == userId).AsNoTracking().ToListAsync(); - if (!farms.Any()) - return NotFound("Farms is not found"); + [HttpGet("{userId}/farm")] + public async Task>> Index(int userId) + { + try + { + List 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); - } - } + return Ok(farms); + } + catch (Exception ex) + { + return BadRequest(ex.Message); + } + } - [HttpGet("{userId}/farm/{farmId}")] - public async Task> Show(int userId, int farmId) - { - try - { - Farm? farm = await - _context.Farms.FirstOrDefaultAsync(x => x.UserId == userId && x.Id == farmId); + [HttpGet("{userId}/farm/{farmId}")] + public async Task> 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"); + if (farm == null) + return NotFound("Farm is not found"); - return Ok(farm); - } - catch (Exception ex) - { - return BadRequest(ex.Message); - } - } + return Ok(farm); + } + catch (Exception ex) + { + return BadRequest(ex.Message); + } + } - [HttpPost("{userId}/farm")] - public async Task> Create([FromBody] FarmRequest farmRequest, int userId) - { - try - { - var farm = new Farm { - Name = farmRequest.Name, - UserId = userId, - RaspberryMacAddr = farmRequest.RaspberryMacAddr, - }; + [HttpPost("{userId}/farm")] + public async Task> Create([FromBody] FarmRequest farmRequest, int userId) + { + try + { + var farm = new Farm + { + Name = farmRequest.Name, + UserId = userId, + RaspberryIP = farmRequest.RaspberryIP, + }; - Farm? farmCreated = _context.Farms.Add(farm).Entity; - await _context.SaveChangesAsync(); + Farm? farmCreated = _context.Farms.Add(farm).Entity; + await _context.SaveChangesAsync(); - return Ok(farmCreated); - } - catch (Exception ex) - { - return BadRequest(ex.Message); - } - } + return Ok(farmCreated); + } + catch (Exception ex) + { + return BadRequest(ex.Message); + } + } - [HttpPut("{userId}/farm/{farmId}")] - public async Task> Update([FromBody] FarmRequest farmRequest, int userId, int farmId) - { - try - { - Farm? farm = await _context.Farms.FirstOrDefaultAsync(x => x.Id == farmId && x.UserId == userId); + [HttpPut("{userId}/farm/{farmId}")] + public async Task> 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"); + if (farm == null) + return NotFound("Farm is not found"); - farm.Name = farmRequest.Name; - farm.RaspberryMacAddr = farmRequest.RaspberryMacAddr; + farm.Name = farmRequest.Name; + farm.RaspberryIP = farmRequest.RaspberryIP; - _context.Farms.Update(farm); - await _context.SaveChangesAsync(); + _context.Farms.Update(farm); + await _context.SaveChangesAsync(); - return Ok(farm); - } - catch (Exception ex) - { - return BadRequest(ex.Message); - } - } + return Ok(farm); + } + catch (Exception ex) + { + return BadRequest(ex.Message); + } + } - [HttpDelete("{userId}/farm/{farmId}")] - public async Task Delete(int userId, int farmId) - { - try - { - Farm? farm = await _context.Farms.FirstOrDefaultAsync(x => x.Id == farmId && x.UserId == userId); + [HttpDelete("{userId}/farm/{farmId}")] + public async Task 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"); + if (farm == null) + return NotFound("Farm is not found"); - _context.Farms.Remove(farm); - await _context.SaveChangesAsync(); + _context.Farms.Remove(farm); + await _context.SaveChangesAsync(); - return Ok("Farm deleted successfully"); - } - catch (Exception ex) - { - return BadRequest(ex.Message); - } - } - - } -} + return Ok("Farm deleted successfully"); + } + catch (Exception ex) + { + return BadRequest(ex.Message); + } + } + } +} \ No newline at end of file diff --git a/Cloud/Controllers/WeatherForecastController.cs b/Cloud/Controllers/WeatherForecastController.cs deleted file mode 100644 index 5f76bcd..0000000 --- a/Cloud/Controllers/WeatherForecastController.cs +++ /dev/null @@ -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 _logger; - - public WeatherForecastController(ILogger logger) - { - _logger = logger; - } - - [HttpGet(Name = "GetWeatherForecast")] - public IEnumerable 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(); - } -} diff --git a/Cloud/Dockerfile b/Cloud/Dockerfile new file mode 100644 index 0000000..084ab62 --- /dev/null +++ b/Cloud/Dockerfile @@ -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"] + diff --git a/Cloud/Models/Farm.cs b/Cloud/Models/Farm.cs index a17d3bc..5cc1ac8 100644 --- a/Cloud/Models/Farm.cs +++ b/Cloud/Models/Farm.cs @@ -1,12 +1,11 @@ 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; } - - } -} + 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 RaspberryIP { get; set; } + } +} \ No newline at end of file diff --git a/Cloud/Requests/FarmRequest.cs b/Cloud/Requests/FarmRequest.cs index c032dfa..3371488 100644 --- a/Cloud/Requests/FarmRequest.cs +++ b/Cloud/Requests/FarmRequest.cs @@ -1,8 +1,8 @@ namespace Cloud.Requests { - public class FarmRequest - { - public string Name { get; set; } - public string RaspberryMacAddr { get; set; } - } -} + public class FarmRequest + { + public string Name { get; set; } + public string RaspberryIP { get; set; } + } +} \ No newline at end of file diff --git a/Cloud/Validation/FarmValidator.cs b/Cloud/Validation/FarmValidator.cs index 4c262fb..c4df29e 100644 --- a/Cloud/Validation/FarmValidator.cs +++ b/Cloud/Validation/FarmValidator.cs @@ -3,16 +3,16 @@ using FluentValidation; namespace Cloud.Validation { - public class FarmValidator : AbstractValidator - { - 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"); + public class FarmValidator : AbstractValidator + { + public FarmValidator() + { + RuleFor(request => request.RaspberryIP) + .NotEmpty().WithMessage("IP address can't be empty") + .Matches(@"^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$").WithMessage("IP address is not valid"); - RuleFor(request => request.Name) - .NotEmpty().WithMessage("Name can't be empty"); - } - } -} + RuleFor(request => request.Name) + .NotEmpty().WithMessage("Name can't be empty"); + } + } +} \ No newline at end of file diff --git a/Cloud/WeatherForecast.cs b/Cloud/WeatherForecast.cs deleted file mode 100644 index d787653..0000000 --- a/Cloud/WeatherForecast.cs +++ /dev/null @@ -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; } -} diff --git a/docker-compose.yml b/docker-compose.yml index f13b1b9..71e50fa 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,16 +1,22 @@ services: + cloud: + build: ./Cloud/ + ports: + - "5124:5124" + depends_on: + - postgres + - redis postgres: - image: postgres:14 + image: postgres:14 container_name: cucumber_database environment: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: 12345 - POSTGRES_DB: main_database + POSTGRES_USER: postgres + POSTGRES_PASSWORD: 12345 + POSTGRES_DB: main_database ports: - - "5438:5432" + - "5438:5432" volumes: - postgres_data:/var/lib/postgresql/data - redis: image: 'redis:latest' ports: @@ -28,4 +34,4 @@ volumes: postgres_data: driver: local cloud-redis: - driver: local \ No newline at end of file + driver: local