mirror of
https://github.com/MvDevsUnion/WPetition.git
synced 2026-01-13 17:09:28 +00:00
idk
This commit is contained in:
302
README.md
Normal file
302
README.md
Normal file
@@ -0,0 +1,302 @@
|
||||
# Petition Submission API
|
||||
|
||||
A petition signing API built with ASP.NET Core 9.0 that allows users to sign petitions and retrieve petition details. Features rate limiting to prevent spam and duplicate signature detection.
|
||||
|
||||
## Features
|
||||
|
||||
- Sign petitions with digital signatures
|
||||
- Retrieve petition details including author information
|
||||
- Rate limiting (3 signatures per minute per IP)
|
||||
- Duplicate signature prevention (one signature per ID card)
|
||||
- MongoDB backend for data persistence
|
||||
- Docker support for easy deployment
|
||||
- Bilingual support (Dhivehi and English)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- .NET 9.0 SDK
|
||||
- MongoDB instance
|
||||
- Docker (optional, for containerized deployment)
|
||||
|
||||
## Configuration
|
||||
|
||||
### MongoDB Setup
|
||||
|
||||
1. Create a MongoDB database for the petition system
|
||||
2. Create the following collections:
|
||||
- `signatures` - stores petition signatures
|
||||
- `petitions` - stores petition details
|
||||
- `authors` - stores petition author information
|
||||
|
||||
### Application Configuration
|
||||
|
||||
Update `appsettings.json` with your MongoDB connection settings:
|
||||
|
||||
```json
|
||||
{
|
||||
"MongoDbSettings": {
|
||||
"ConnectionString": "mongodb://localhost:27017",
|
||||
"DatabaseName": "petition_database"
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
```
|
||||
|
||||
### Rate Limiting Configuration
|
||||
|
||||
The API is configured with rate limiting to prevent spam. Default settings in `Program.cs`:
|
||||
|
||||
- **Limit**: 3 signatures per minute per IP address
|
||||
- **Window**: Fixed 1-minute window
|
||||
- **Queue**: Disabled (requests over limit receive HTTP 429)
|
||||
|
||||
To modify rate limits, edit `Program.cs`:
|
||||
|
||||
```csharp
|
||||
limiterOptions.PermitLimit = 3; // Change this number
|
||||
limiterOptions.Window = TimeSpan.FromMinutes(1); // Change time window
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
### Local Development
|
||||
|
||||
1. Clone the repository:
|
||||
```bash
|
||||
git clone <repository-url>
|
||||
cd Submission.Api
|
||||
```
|
||||
|
||||
2. Restore dependencies:
|
||||
```bash
|
||||
dotnet restore
|
||||
```
|
||||
|
||||
3. Update `appsettings.json` with your MongoDB connection string
|
||||
|
||||
4. Run the application:
|
||||
```bash
|
||||
dotnet run --project Submission.Api
|
||||
```
|
||||
|
||||
The API will be available at:
|
||||
- HTTPS: `https://localhost:7xxx`
|
||||
- HTTP: `http://localhost:5xxx`
|
||||
|
||||
### Docker Deployment
|
||||
|
||||
1. Build the Docker image:
|
||||
```bash
|
||||
docker build -t petition-api .
|
||||
```
|
||||
|
||||
2. Run the container:
|
||||
```bash
|
||||
docker run -d -p 8080:8080 -p 8081:8081 \
|
||||
-e MongoDbSettings__ConnectionString="mongodb://your-mongo-host:27017" \
|
||||
-e MongoDbSettings__DatabaseName="petition_database" \
|
||||
petition-api
|
||||
```
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Sign a Petition
|
||||
|
||||
Signs a petition with user information and signature.
|
||||
|
||||
**Endpoint**: `POST /api/Sign`
|
||||
|
||||
**Rate Limit**: 3 requests per minute per IP
|
||||
|
||||
**Request Body**:
|
||||
```json
|
||||
{
|
||||
"name": "John Doe",
|
||||
"idCard": "A123456",
|
||||
"signature": "<svg>...</svg>"
|
||||
}
|
||||
```
|
||||
|
||||
**Field Validation**:
|
||||
- `name`: Required, minimum 3 characters
|
||||
- `idCard`: Required, 6-7 characters (typically National ID)
|
||||
- `signature`: Required, SVG signature data
|
||||
|
||||
**Success Response** (200 OK):
|
||||
```json
|
||||
{}
|
||||
```
|
||||
|
||||
**Error Responses**:
|
||||
|
||||
- **400 Bad Request** - Invalid request body or validation failed
|
||||
```json
|
||||
{
|
||||
"errors": {
|
||||
"name": ["The field Name must be a string with a minimum length of 3."],
|
||||
"idCard": ["The field IdCard must be a string with a minimum length of 6 and a maximum length of 7."]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- **429 Too Many Requests** - Rate limit exceeded
|
||||
```json
|
||||
{
|
||||
"status": 429,
|
||||
"title": "Too Many Requests"
|
||||
}
|
||||
```
|
||||
|
||||
- **500 Internal Server Error** - User already signed the petition
|
||||
```json
|
||||
{
|
||||
"title": "You already signed this petition"
|
||||
}
|
||||
```
|
||||
|
||||
### Get Petition Details
|
||||
|
||||
Retrieves details of a specific petition including author information.
|
||||
|
||||
**Endpoint**: `GET /api/Sign/{petition_id}`
|
||||
|
||||
**URL Parameters**:
|
||||
- `petition_id` (GUID) - The unique identifier of the petition
|
||||
|
||||
**Success Response** (200 OK):
|
||||
```json
|
||||
{
|
||||
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"startDate": "2025-01-15",
|
||||
"nameDhiv": "ޕެޓިޝަން ނަން",
|
||||
"nameEng": "Petition Name",
|
||||
"authorDetails": {
|
||||
"name": "Author Name",
|
||||
"nid": "A123456"
|
||||
},
|
||||
"petitionBodyDhiv": "ޕެޓިޝަން ތަފްސީލް...",
|
||||
"petitionBodyEng": "Petition description...",
|
||||
"signatureCount": 0
|
||||
}
|
||||
```
|
||||
|
||||
**Error Response**:
|
||||
|
||||
- **404 Not Found** - Petition does not exist
|
||||
```json
|
||||
{}
|
||||
```
|
||||
|
||||
## Data Models
|
||||
|
||||
### Signature (Widget)
|
||||
```csharp
|
||||
{
|
||||
"id": "ObjectId",
|
||||
"name": "string",
|
||||
"idCard": "string",
|
||||
"signature_SVG": "string",
|
||||
"timestamp": "DateTime"
|
||||
}
|
||||
```
|
||||
|
||||
### Petition Details
|
||||
```csharp
|
||||
{
|
||||
"id": "Guid",
|
||||
"startDate": "DateOnly",
|
||||
"nameDhiv": "string",
|
||||
"nameEng": "string",
|
||||
"petitionBodyDhiv": "string",
|
||||
"petitionBodyEng": "string",
|
||||
"authorId": "Guid",
|
||||
"signatureCount": "int"
|
||||
}
|
||||
```
|
||||
|
||||
### Author
|
||||
```csharp
|
||||
{
|
||||
"id": "Guid",
|
||||
"name": "string",
|
||||
"nid": "string"
|
||||
}
|
||||
```
|
||||
|
||||
## Security Features
|
||||
|
||||
### Rate Limiting
|
||||
- Prevents spam by limiting signature submissions to 3 per minute per IP
|
||||
- Uses ASP.NET Core built-in rate limiting middleware
|
||||
- Returns HTTP 429 when limit is exceeded
|
||||
|
||||
### Duplicate Prevention
|
||||
- Each ID card can only sign a petition once
|
||||
- Checked before inserting into database
|
||||
- Returns error message if duplicate detected
|
||||
|
||||
### Input Validation
|
||||
- Name: Minimum 3 characters
|
||||
- ID Card: Must be 6-7 characters (validates National ID format)
|
||||
- Signature: Required field
|
||||
|
||||
## Development
|
||||
|
||||
### Project Structure
|
||||
```
|
||||
Submission.Api/
|
||||
├── Controllers/
|
||||
│ └── SignController.cs # API endpoints
|
||||
├── Dto/
|
||||
│ ├── WidgetsDto.cs # Signature request DTO
|
||||
│ ├── PetitionDetailsDto.cs # Petition response DTO
|
||||
│ └── Author.cs # Author DTO
|
||||
├── Models/
|
||||
│ ├── Widget.cs # Signature database model
|
||||
│ ├── PetitionDetail.cs # Petition database model
|
||||
│ └── Author.cs # Author database model
|
||||
├── Program.cs # Application configuration
|
||||
├── Dockerfile # Docker configuration
|
||||
└── appsettings.json # Application settings
|
||||
```
|
||||
|
||||
### Dependencies
|
||||
- ASP.NET Core 9.0
|
||||
- MongoDB Driver (via Ashi.MongoInterface)
|
||||
- Microsoft.AspNetCore.OpenApi 9.0.11
|
||||
- Microsoft.AspNetCore.RateLimiting (built-in)
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
**MongoDB Connection Failed**
|
||||
- Verify MongoDB is running
|
||||
- Check connection string in `appsettings.json`
|
||||
- Ensure network connectivity to MongoDB instance
|
||||
|
||||
## Contributing
|
||||
|
||||
When contributing to this project:
|
||||
1. Follow existing code style and conventions
|
||||
2. Test all endpoints thoroughly
|
||||
3. Update documentation for any API changes
|
||||
4. Ensure rate limiting is not disabled in production
|
||||
|
||||
## License
|
||||
|
||||
if you use this you must mention that its powered by Mv Devs Union
|
||||
|
||||
also any forks must be open source
|
||||
|
||||
this must never be used for data collection and profiling people
|
||||
|
||||
## Support
|
||||
|
||||
For issues or questions, please open an issue on the repository.
|
||||
@@ -2,6 +2,13 @@
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Submission.Api", "Submission.Api\Submission.Api.csproj", "{F061EFE3-FFFE-4F0F-825A-9FF8B985912D}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{204A1F7E-0832-4DEF-A64E-EB1A00F559A0}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
compose.yaml = compose.yaml
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ashi.MongoInterface", "external\OtherRepo\Ashi.MongoInterface\Ashi.MongoInterface.csproj", "{6F6B8940-4740-48D1-8790-8CD82A66676B}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -12,5 +19,12 @@ Global
|
||||
{F061EFE3-FFFE-4F0F-825A-9FF8B985912D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F061EFE3-FFFE-4F0F-825A-9FF8B985912D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F061EFE3-FFFE-4F0F-825A-9FF8B985912D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{6F6B8940-4740-48D1-8790-8CD82A66676B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6F6B8940-4740-48D1-8790-8CD82A66676B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6F6B8940-4740-48D1-8790-8CD82A66676B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6F6B8940-4740-48D1-8790-8CD82A66676B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{6F6B8940-4740-48D1-8790-8CD82A66676B} = {204A1F7E-0832-4DEF-A64E-EB1A00F559A0}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
103
Submission.Api/Controllers/SignController.cs
Normal file
103
Submission.Api/Controllers/SignController.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
using System.CodeDom;
|
||||
using System.Runtime.InteropServices;
|
||||
using Ashi.MongoInterface.Service;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.RateLimiting;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Submission.Api.Dto;
|
||||
using Submission.Api.Models;
|
||||
|
||||
namespace Submission.Api.Controllers
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
public class SignController : ControllerBase
|
||||
{
|
||||
|
||||
private readonly IMongoRepository<Author> _authorRepository;
|
||||
private readonly IMongoRepository<PetitionDetail> _detailRepository;
|
||||
private readonly IMongoRepository<Widget> _signatureRepository;
|
||||
private readonly IMemoryCache _cache;
|
||||
|
||||
public SignController(
|
||||
IMongoRepository<Author> authorRepository,
|
||||
IMongoRepository<PetitionDetail> detailRepository,
|
||||
IMongoRepository<Widget> signatureRepository,
|
||||
IMemoryCache cache)
|
||||
{
|
||||
_authorRepository = authorRepository;
|
||||
_detailRepository = detailRepository;
|
||||
_signatureRepository = signatureRepository;
|
||||
_cache = cache;
|
||||
}
|
||||
|
||||
[HttpPost(Name = "petition/{id}")]
|
||||
[EnableRateLimiting("SignPetitionPolicy")]
|
||||
public async Task<IActionResult> SignDisHoe([FromRoute]Guid petition_id,[FromBody] WidgetsDto body)
|
||||
{
|
||||
//check to see if the same person signed the petition already
|
||||
//if dupe send error saying user already signed
|
||||
var dupe = await _signatureRepository.FindOneAsync(x => x.IdCard == body.IdCard);
|
||||
if (dupe != null)
|
||||
return Problem("You already signed this petition");
|
||||
|
||||
//add signature to the db
|
||||
await _signatureRepository.InsertOneAsync(new Widget
|
||||
{
|
||||
IdCard = body.IdCard,
|
||||
Name = body.Name,
|
||||
Signature_SVG = body.Signature,
|
||||
Timestamp = DateTime.Now
|
||||
});
|
||||
|
||||
//update signature count
|
||||
|
||||
return Ok("your signature has been submitted");
|
||||
}
|
||||
|
||||
[HttpGet(Name = "petition/{id}")]
|
||||
public async Task<IActionResult> GetDisHoe([FromRoute] Guid petition_id)
|
||||
{
|
||||
var cacheKey = $"petition_{petition_id}";
|
||||
|
||||
// Try to get from cache
|
||||
if (_cache.TryGetValue(cacheKey, out PetitionDetailsDto cachedDto))
|
||||
{
|
||||
return Ok(cachedDto);
|
||||
}
|
||||
|
||||
// Not in cache, fetch from database
|
||||
var pet = await _detailRepository.FindByIdAsync(petition_id);
|
||||
|
||||
if (pet == null)
|
||||
return NotFound();
|
||||
|
||||
var author = await _authorRepository.FindOneAsync(x => x.Id == pet.AuthorId);
|
||||
|
||||
var dto = new PetitionDetailsDto
|
||||
{
|
||||
Id = petition_id,
|
||||
NameDhiv = pet.NameDhiv,
|
||||
StartDate = pet.StartDate,
|
||||
NameEng = pet.NameEng,
|
||||
PetitionBodyDhiv = pet.PetitionBodyDhiv,
|
||||
PetitionBodyEng = pet.PetitionBodyEng,
|
||||
|
||||
AuthorDetails = new AuthorsDto
|
||||
{
|
||||
Name = author.Name,
|
||||
NID = author.NID,
|
||||
}
|
||||
};
|
||||
|
||||
// Store in cache with 5 minute expiration
|
||||
var cacheOptions = new MemoryCacheEntryOptions()
|
||||
.SetAbsoluteExpiration(TimeSpan.FromHours(12));
|
||||
|
||||
_cache.Set(cacheKey, dto, cacheOptions);
|
||||
|
||||
return Ok(dto);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
namespace Submission.Api.Dto;
|
||||
|
||||
public class Author
|
||||
public class AuthorsDto
|
||||
{
|
||||
|
||||
public string Name { get; set; }
|
||||
public string NID { get; set; }
|
||||
}
|
||||
@@ -2,5 +2,16 @@
|
||||
|
||||
public class PetitionDetailsDto
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public DateOnly StartDate { get; set; }
|
||||
|
||||
public string NameDhiv { get; set; }
|
||||
public string NameEng { get; set; }
|
||||
|
||||
public AuthorsDto AuthorDetails { get; set; }
|
||||
|
||||
public string PetitionBodyDhiv { get; set; }
|
||||
public string PetitionBodyEng { get; set; }
|
||||
|
||||
public int SignatureCount { get; set; }
|
||||
}
|
||||
@@ -1,6 +1,18 @@
|
||||
namespace Submission.Api.Dto;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
public class WidgetDto
|
||||
namespace Submission.Api.Dto;
|
||||
|
||||
public class WidgetsDto
|
||||
{
|
||||
[Required]
|
||||
[MinLength(3)]
|
||||
public string Name { get; set; }
|
||||
|
||||
[Required]
|
||||
[MinLength(6)]
|
||||
[MaxLength(7)]
|
||||
public string IdCard { get; set; }
|
||||
|
||||
[Required]
|
||||
public string Signature { get; set; }
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
namespace Submission.Api.Models;
|
||||
using Ashi.MongoInterface.Helper;
|
||||
|
||||
public class Author
|
||||
namespace Submission.Api.Models;
|
||||
|
||||
[BsonCollection("author")]
|
||||
public class Author : Document
|
||||
{
|
||||
|
||||
public string Name { get; set; }
|
||||
public string NID { get; set; }
|
||||
}
|
||||
@@ -1,6 +1,20 @@
|
||||
namespace Submission.Api.Models;
|
||||
using System.Runtime.InteropServices;
|
||||
using Ashi.MongoInterface.Helper;
|
||||
|
||||
public class PetitionDetail
|
||||
namespace Submission.Api.Models;
|
||||
|
||||
[BsonCollection("petitionDetail")]
|
||||
public class PetitionDetail : Document
|
||||
{
|
||||
public DateOnly StartDate { get; set; }
|
||||
|
||||
public string NameDhiv { get; set; }
|
||||
public string NameEng { get; set; }
|
||||
|
||||
public Guid AuthorId { get; set; }
|
||||
|
||||
public string PetitionBodyDhiv { get; set; }
|
||||
public string PetitionBodyEng { get; set; }
|
||||
|
||||
public int SignatureCount { get; set; }
|
||||
}
|
||||
@@ -1,6 +1,12 @@
|
||||
namespace Submission.Api.Models;
|
||||
using Ashi.MongoInterface.Helper;
|
||||
|
||||
public class Widget
|
||||
namespace Submission.Api.Models;
|
||||
|
||||
[BsonCollection("signatures")]
|
||||
public class Widget : Document
|
||||
{
|
||||
|
||||
public string Name { get; set; }
|
||||
public string IdCard { get; set; }
|
||||
public string Signature_SVG { get; set; }
|
||||
public DateTime Timestamp { get; set; }
|
||||
}
|
||||
@@ -1,11 +1,37 @@
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
using System.Configuration;
|
||||
using Ashi.MongoInterface;
|
||||
using Ashi.MongoInterface.Service;
|
||||
using Microsoft.AspNetCore.RateLimiting;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Add services to the container.
|
||||
builder.Services.Configure<MongoDbSettings>(builder.Configuration.GetSection("MongoDbSettings"));
|
||||
|
||||
builder.Services.AddSingleton<IMongoDbSettings>(serviceProvider =>
|
||||
serviceProvider.GetRequiredService<IOptions<MongoDbSettings>>().Value);
|
||||
|
||||
builder.Services.AddScoped((typeof(IMongoRepository<>)), typeof(MongoRepository<>));
|
||||
|
||||
builder.Services.AddMemoryCache();
|
||||
|
||||
builder.Services.AddControllers();
|
||||
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
|
||||
builder.Services.AddOpenApi();
|
||||
|
||||
// Add rate limiting
|
||||
builder.Services.AddRateLimiter(options =>
|
||||
{
|
||||
options.AddFixedWindowLimiter("SignPetitionPolicy", limiterOptions =>
|
||||
{
|
||||
limiterOptions.PermitLimit = 3;
|
||||
limiterOptions.Window = TimeSpan.FromMinutes(1);
|
||||
limiterOptions.QueueProcessingOrder = System.Threading.RateLimiting.QueueProcessingOrder.OldestFirst;
|
||||
limiterOptions.QueueLimit = 0;
|
||||
});
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
@@ -16,8 +42,10 @@ if (app.Environment.IsDevelopment())
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
app.UseRateLimiter();
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
app.MapControllers();
|
||||
|
||||
app.Run();
|
||||
app.Run();
|
||||
@@ -4,10 +4,22 @@
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.11"/>
|
||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="9.0.11" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="..\.dockerignore">
|
||||
<Link>.dockerignore</Link>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\external\OtherRepo\Ashi.MongoInterface\Ashi.MongoInterface.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
6
Submission.Api/Submission.Api.csproj.user
Normal file
6
Submission.Api/Submission.Api.csproj.user
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ActiveDebugProfile>https</ActiveDebugProfile>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
7
compose.yaml
Normal file
7
compose.yaml
Normal file
@@ -0,0 +1,7 @@
|
||||
services:
|
||||
submission.api:
|
||||
image: submission.api
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Submission.Api/Dockerfile
|
||||
|
||||
Reference in New Issue
Block a user