Add project files.
This commit is contained in:
parent
b41d842114
commit
703c08022b
25
SampleApplication.Tests/SampleApplication.Tests.csproj
Normal file
25
SampleApplication.Tests/SampleApplication.Tests.csproj
Normal file
@ -0,0 +1,25 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.4" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
||||
<PackageReference Include="xunit" Version="2.9.3" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SampleApplication\SampleApplication.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="Xunit" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
101
SampleApplication.Tests/ShapeProcessTest.cs
Normal file
101
SampleApplication.Tests/ShapeProcessTest.cs
Normal file
@ -0,0 +1,101 @@
|
||||
using SampleApplication.Entities;
|
||||
|
||||
namespace SampleApplication.Tests
|
||||
{
|
||||
public class ShapeProcessTest
|
||||
{
|
||||
[Fact]
|
||||
public void Rectangle_Calculation_IsCorrect()
|
||||
{
|
||||
var processor = new SampleApplication.ShapeProcessor();
|
||||
|
||||
var rectangle = new ShapeInput
|
||||
{
|
||||
Id = 1,
|
||||
Type = ShapeType.Rectangle,
|
||||
Dimensions = new[] { 10.0, 5.0 }
|
||||
};
|
||||
|
||||
processor.AddShapes(new[] { rectangle });
|
||||
|
||||
var (area, perimeter) = processor.GetResult(rectangle.Id);
|
||||
|
||||
Assert.Equal(50.0, area, 2);
|
||||
Assert.Equal(30.0, perimeter, 2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Circle_Calculation_IsCorrect()
|
||||
{
|
||||
var processor = new SampleApplication.ShapeProcessor();
|
||||
|
||||
var circle = new ShapeInput
|
||||
{
|
||||
Id = 1,
|
||||
Type = ShapeType.Circle,
|
||||
Dimensions = new[] { 7.0 }
|
||||
};
|
||||
|
||||
processor.AddShapes(new[] { circle });
|
||||
|
||||
var (area, perimeter) = processor.GetResult(circle.Id);
|
||||
|
||||
Assert.Equal(153.94, area, 2);
|
||||
Assert.Equal(43.98, perimeter, 2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Triangle_Calculation_IsCorrect()
|
||||
{
|
||||
var processor = new SampleApplication.ShapeProcessor();
|
||||
|
||||
var triangle = new ShapeInput
|
||||
{
|
||||
Id = 1,
|
||||
Type = ShapeType.Triangle,
|
||||
Dimensions = new[] { 3.0, 4.0 }
|
||||
};
|
||||
|
||||
processor.AddShapes(new[] { triangle });
|
||||
|
||||
var (area, perimeter) = processor.GetResult(triangle.Id);
|
||||
|
||||
Assert.Equal(6.00, area, 2);
|
||||
Assert.Equal(12.00, perimeter, 2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Invalid_Dimensions_Throws()
|
||||
{
|
||||
var processor = new SampleApplication.ShapeProcessor();
|
||||
|
||||
Assert.Throws<ArgumentException>(() =>
|
||||
processor.AddShapes(new[]
|
||||
{
|
||||
new ShapeInput
|
||||
{
|
||||
Id = 1,
|
||||
Type = ShapeType.Rectangle,
|
||||
Dimensions = new[] { -1.0, 2.0 }
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Statistics_AreCorrect()
|
||||
{
|
||||
var processor = new SampleApplication.ShapeProcessor();
|
||||
var shapes = new[]
|
||||
{
|
||||
new ShapeInput { Id = 1, Type = ShapeType.Rectangle, Dimensions = new[] { 2.0, 3.0 } },
|
||||
new ShapeInput { Id = 2, Type = ShapeType.Circle, Dimensions = new[] { 3.0 } }
|
||||
};
|
||||
processor.AddShapes(shapes);
|
||||
var (totalShapes, totalArea, maxPerimeterId) = processor.GetStatistics();
|
||||
Assert.Equal(2, totalShapes);
|
||||
Assert.Equal(34.27,totalArea, 2);
|
||||
Assert.Equal(2, maxPerimeterId);
|
||||
}
|
||||
}
|
||||
}
|
||||
4
SampleApplication.slnx
Normal file
4
SampleApplication.slnx
Normal file
@ -0,0 +1,4 @@
|
||||
<Solution>
|
||||
<Project Path="SampleApplication.Tests/SampleApplication.Tests.csproj" />
|
||||
<Project Path="SampleApplication/SampleApplication.csproj" />
|
||||
</Solution>
|
||||
77
SampleApplication/Controllers/ShapeController.cs
Normal file
77
SampleApplication/Controllers/ShapeController.cs
Normal file
@ -0,0 +1,77 @@
|
||||
using Microsoft.AspNetCore.Http.HttpResults;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SampleApplication.Entities;
|
||||
|
||||
namespace SampleApplication.Controllers
|
||||
{
|
||||
[Route("api/shapes")]
|
||||
public class ShapesController : ControllerBase
|
||||
{
|
||||
|
||||
private readonly SampleDbContext _db;
|
||||
|
||||
private readonly ShapeProcessor _calculator;
|
||||
|
||||
public ShapesController(
|
||||
|
||||
SampleDbContext db,
|
||||
|
||||
ShapeProcessor calculator)
|
||||
|
||||
{
|
||||
|
||||
_db = db;
|
||||
|
||||
_calculator = calculator;
|
||||
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
|
||||
public IActionResult AddShapes(List<ShapeInput> shapes)
|
||||
|
||||
{
|
||||
|
||||
// TODO:
|
||||
|
||||
// - Validate
|
||||
|
||||
// - Ignore duplicate IDs
|
||||
|
||||
// - Save via EF Core
|
||||
|
||||
this._calculator.AddShapes(shapes);
|
||||
|
||||
this._db.Shapes.AddRange(shapes);
|
||||
|
||||
return Ok(shapes);
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
|
||||
public async Task<IActionResult> Get(int id)
|
||||
|
||||
{
|
||||
|
||||
// TODO:
|
||||
|
||||
var shape = await this._db.Shapes.Where(s => s.Id == id).FirstOrDefaultAsync();
|
||||
|
||||
return Ok(shape);
|
||||
}
|
||||
|
||||
[HttpGet("stats")]
|
||||
|
||||
public IActionResult Stats()
|
||||
|
||||
{
|
||||
|
||||
// TODO:
|
||||
|
||||
throw new NotImplementedException();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
13
SampleApplication/Entities/ShapeInput.cs
Normal file
13
SampleApplication/Entities/ShapeInput.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using Microsoft.OpenApi;
|
||||
|
||||
namespace SampleApplication.Entities
|
||||
{
|
||||
public class ShapeInput
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
public ShapeType Type { get; set; }
|
||||
|
||||
public double[] Dimensions { get; set; }
|
||||
}
|
||||
}
|
||||
49
SampleApplication/Migrations/20260119092501_InitialCreate.Designer.cs
generated
Normal file
49
SampleApplication/Migrations/20260119092501_InitialCreate.Designer.cs
generated
Normal file
@ -0,0 +1,49 @@
|
||||
// <auto-generated />
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using SampleApplication;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace SampleApplication.Migrations
|
||||
{
|
||||
[DbContext(typeof(SampleDbContext))]
|
||||
[Migration("20260119092501_InitialCreate")]
|
||||
partial class InitialCreate
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "10.0.2")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("SampleApplication.Entities.ShapeInput", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.PrimitiveCollection<string>("Dimensions")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Shapes");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
35
SampleApplication/Migrations/20260119092501_InitialCreate.cs
Normal file
35
SampleApplication/Migrations/20260119092501_InitialCreate.cs
Normal file
@ -0,0 +1,35 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace SampleApplication.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class InitialCreate : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Shapes",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
Type = table.Column<int>(type: "int", nullable: false),
|
||||
Dimensions = table.Column<string>(type: "nvarchar(max)", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Shapes", x => x.Id);
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "Shapes");
|
||||
}
|
||||
}
|
||||
}
|
||||
46
SampleApplication/Migrations/SampleDbContextModelSnapshot.cs
Normal file
46
SampleApplication/Migrations/SampleDbContextModelSnapshot.cs
Normal file
@ -0,0 +1,46 @@
|
||||
// <auto-generated />
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using SampleApplication;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace SampleApplication.Migrations
|
||||
{
|
||||
[DbContext(typeof(SampleDbContext))]
|
||||
partial class SampleDbContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "10.0.2")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("SampleApplication.Entities.ShapeInput", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.PrimitiveCollection<string>("Dimensions")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Shapes");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
32
SampleApplication/Program.cs
Normal file
32
SampleApplication/Program.cs
Normal file
@ -0,0 +1,32 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SampleApplication;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Add services to the container.
|
||||
|
||||
builder.Services.AddDbContext<SampleDbContext>(options =>
|
||||
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
|
||||
|
||||
builder.Services.AddSingleton<ShapeProcessor>();
|
||||
|
||||
builder.Services.AddControllers();
|
||||
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
|
||||
builder.Services.AddOpenApi();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.MapOpenApi();
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
app.MapControllers();
|
||||
|
||||
app.Run();
|
||||
23
SampleApplication/Properties/launchSettings.json
Normal file
23
SampleApplication/Properties/launchSettings.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||
"profiles": {
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": false,
|
||||
"applicationUrl": "http://localhost:5078",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"https": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": false,
|
||||
"applicationUrl": "https://localhost:7203;http://localhost:5078",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
20
SampleApplication/SampleApplication.csproj
Normal file
20
SampleApplication/SampleApplication.csproj
Normal file
@ -0,0 +1,20 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="10.0.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="10.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
6
SampleApplication/SampleApplication.http
Normal file
6
SampleApplication/SampleApplication.http
Normal file
@ -0,0 +1,6 @@
|
||||
@SampleApplication_HostAddress = http://localhost:5078
|
||||
|
||||
GET {{SampleApplication_HostAddress}}/weatherforecast/
|
||||
Accept: application/json
|
||||
|
||||
###
|
||||
13
SampleApplication/SampleDbContext.cs
Normal file
13
SampleApplication/SampleDbContext.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace SampleApplication
|
||||
{
|
||||
public class SampleDbContext : DbContext
|
||||
{
|
||||
public SampleDbContext(DbContextOptions<SampleDbContext> options) : base(options)
|
||||
{
|
||||
}
|
||||
|
||||
public DbSet<Entities.ShapeInput> Shapes { get; set; }
|
||||
}
|
||||
}
|
||||
102
SampleApplication/ShapeProcessor.cs
Normal file
102
SampleApplication/ShapeProcessor.cs
Normal file
@ -0,0 +1,102 @@
|
||||
using SampleApplication.Entities;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Reflection.Metadata.Ecma335;
|
||||
|
||||
namespace SampleApplication
|
||||
{
|
||||
public class ShapeProcessor
|
||||
{
|
||||
public List<ShapeInput> shapes = new List<ShapeInput>();
|
||||
public void AddShapes(IEnumerable<ShapeInput> shapes)
|
||||
{
|
||||
// TODO:
|
||||
// - Ignore duplicate Ids
|
||||
// - Validate dimensions
|
||||
// - Store shapes in memory
|
||||
|
||||
foreach(var shape in shapes)
|
||||
{
|
||||
if (!ValidateDimensions(shape))
|
||||
{
|
||||
throw new ArgumentException("Invalid shape dimensions");
|
||||
}
|
||||
|
||||
this.shapes.Add(shape);
|
||||
}
|
||||
}
|
||||
|
||||
public (double Area, double Perimeter) GetResult(int id)
|
||||
{
|
||||
// TODO:
|
||||
// - Throw KeyNotFoundException if not found
|
||||
// - Compute area & perimeter
|
||||
|
||||
if(!shapes.Any(s => s.Id == id))
|
||||
{
|
||||
throw new KeyNotFoundException("Shape not found");
|
||||
}
|
||||
|
||||
var shape = shapes.First(s => s.Id == id);
|
||||
|
||||
return this.CalculateAreaPerimeter(shape);
|
||||
}
|
||||
|
||||
public (int TotalShapes, double TotalArea, int MaxPerimeterId) GetStatistics()
|
||||
{
|
||||
// TODO:
|
||||
|
||||
var totalShapes = shapes.Count;
|
||||
|
||||
double totalArea = shapes.Sum(s => this.CalculateAreaPerimeter(s).area);
|
||||
|
||||
var maxPerimeterShape = shapes.OrderByDescending(s => this.CalculateAreaPerimeter(s).perimeter).FirstOrDefault();
|
||||
|
||||
return (totalShapes, totalArea, maxPerimeterShape != null ? maxPerimeterShape.Id : 0);
|
||||
}
|
||||
|
||||
private bool ValidateDimensions(ShapeInput shape)
|
||||
{
|
||||
return shape.Type switch
|
||||
{
|
||||
ShapeType.Circle => shape.Dimensions.Length == 1 && shape.Dimensions[0] > 0,
|
||||
ShapeType.Rectangle => shape.Dimensions.Length == 2 && shape.Dimensions.All(d => d > 0),
|
||||
ShapeType.Triangle => shape.Dimensions.Length == 2,
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
private (double area, double perimeter) CalculateAreaPerimeter(ShapeInput shape)
|
||||
{
|
||||
var perimeter = shape.Type switch
|
||||
{
|
||||
ShapeType.Circle =>
|
||||
2 * Math.PI * shape.Dimensions[0],
|
||||
ShapeType.Rectangle =>
|
||||
2 * (shape.Dimensions[0] + shape.Dimensions[1]),
|
||||
ShapeType.Triangle => this.CalculateTrianglePerimeter(shape),
|
||||
_ => throw new NotSupportedException("Shape type not supported"),
|
||||
};
|
||||
|
||||
var area = shape.Type switch
|
||||
{
|
||||
ShapeType.Circle =>
|
||||
Math.PI * shape.Dimensions[0] * shape.Dimensions[0],
|
||||
ShapeType.Rectangle =>
|
||||
shape.Dimensions[0] * shape.Dimensions[1],
|
||||
ShapeType.Triangle =>
|
||||
0.5 * shape.Dimensions[0] * shape.Dimensions[1],
|
||||
_ => throw new NotSupportedException("Shape type not supported"),
|
||||
};
|
||||
|
||||
return (area, perimeter);
|
||||
}
|
||||
|
||||
private double CalculateTrianglePerimeter(ShapeInput shape)
|
||||
{
|
||||
double @base = shape.Dimensions[0];
|
||||
double height = shape.Dimensions[1];
|
||||
double hypotenuse = Math.Sqrt(@base * @base + height * height);
|
||||
return @base + height + hypotenuse;
|
||||
}
|
||||
}
|
||||
}
|
||||
9
SampleApplication/ShapeType.cs
Normal file
9
SampleApplication/ShapeType.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace SampleApplication
|
||||
{
|
||||
public enum ShapeType
|
||||
{
|
||||
Rectangle,
|
||||
Circle,
|
||||
Triangle
|
||||
}
|
||||
}
|
||||
11
SampleApplication/appsettings.Development.json
Normal file
11
SampleApplication/appsettings.Development.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"ConnectionStrings": {
|
||||
"DefaultConnection": "Server=KBR\\SQLSERVER2022;Database=SampleAppDb;User Id=sa;Password=pass@1234;TrustServerCertificate=True"
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
9
SampleApplication/appsettings.json
Normal file
9
SampleApplication/appsettings.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user