33 Commits

Author SHA1 Message Date
8f9ba88b24 Исправил путь к Dockerfile в пайплайне
All checks were successful
continuous-integration/drone/push Build is passing
2025-10-30 14:18:05 +03:00
9df3938d94 Вернул Dockerfile
Some checks failed
continuous-integration/drone/push Build is failing
2025-10-30 14:15:40 +03:00
3eb7072866 Починка сборки образа в CI
Some checks failed
continuous-integration/drone/push Build is failing
2025-10-30 13:37:10 +03:00
a5f54f485c Перенёс Dockerfile
Some checks failed
continuous-integration/drone/push Build is failing
2025-10-30 13:32:48 +03:00
de254806b6 Добавил конфиги CI/CD с сервера в репозиторий
All checks were successful
continuous-integration/drone/push Build is passing
2025-10-30 13:17:55 +03:00
7eca4cdcac Обновить .drone.yml
All checks were successful
continuous-integration/drone/push Build is passing
2025-10-29 20:00:56 +03:00
76ef88e811 Обновить .drone.yml
All checks were successful
continuous-integration/drone/push Build is passing
2025-10-29 19:46:41 +03:00
42ea93ff6a dev-test (#1)
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #1
2025-10-28 14:18:11 +03:00
ee7a15efc2 CD отрабатывает для всех веток
All checks were successful
continuous-integration/drone/push Build is passing
2025-10-28 13:51:45 +03:00
0ec110f45e Добавил отдельного пользователя
All checks were successful
continuous-integration/drone/push Build is passing
2025-10-22 21:31:58 +03:00
03e1778e63 deploy
All checks were successful
continuous-integration/drone/push Build is passing
2025-10-22 21:11:52 +03:00
ccba80f54e Публикация в регистр gitea, притушил тесты
All checks were successful
continuous-integration/drone/push Build is passing
2025-10-21 23:10:09 +03:00
97c83f69eb Вернул тесты
All checks were successful
continuous-integration/drone/push Build is passing
2025-10-06 17:32:04 +03:00
f6029fd89f поправки косяков, ну типа
All checks were successful
continuous-integration/drone/push Build is passing
2025-10-06 15:30:51 +03:00
a256ef99c0 Поправил .dronde.yml
All checks were successful
continuous-integration/drone/push Build is passing
2025-10-05 16:59:05 +03:00
39a748ff0e притушил пока тесты в CI, поправил некоторые вещи для запуска апи, миграцию создал какую-то
Some checks reported errors
continuous-integration/drone/push Build encountered an error
2025-10-05 16:58:08 +03:00
81a01ae4ed Dockerfile и http
All checks were successful
continuous-integration/drone/push Build is passing
2025-10-05 16:46:47 +03:00
4e30f3e7a5 "Пофиксил" тесты
All checks were successful
continuous-integration/drone/push Build is passing
2025-10-05 15:58:08 +03:00
28a1d07e98 Добавил публикацию на Docker Hub
Some checks failed
continuous-integration/drone/push Build is failing
2025-10-05 15:42:19 +03:00
222f90df02 Поменял CI конфиг нормально сделал
All checks were successful
continuous-integration/drone/push Build is passing
2025-10-05 14:40:55 +03:00
aae4b28089 Вытащил солюшен на уровень выше, чтобы прощё было дотнетить
Some checks failed
continuous-integration/drone/push Build is failing
2025-10-05 14:32:06 +03:00
fa87a56ad1 Переименовал файл для CI
Some checks failed
continuous-integration/drone/push Build is failing
2025-10-05 14:25:24 +03:00
5b4ee67c23 Добавил drone.yml для CI 2025-10-05 14:22:06 +03:00
07f5663c75 tests and fixes 2025-05-11 18:28:33 +03:00
6ab5b9f1df задал прослушиваемые url-адреса в appsettings.json 2025-05-09 20:33:04 +03:00
a55eff43fb исправление косяков 2025-05-09 19:25:58 +03:00
99eb10f61d шаблон excel переносится в папку при компиляции 2025-05-07 18:51:46 +03:00
f00595187f Миграция 2025-03-08 22:00:35 +03:00
8c0ad9bc3e Чето поменял 2025-03-08 22:00:08 +03:00
prtsie
b4056fa715 Merge pull request #20 from prtsie/16-refactor
16-refactor
2024-10-04 15:43:41 +03:00
prtsie
b832cd7ecf Merge pull request #19
Excel export
2024-09-29 21:02:29 +03:00
prtsie
09ff31ac82 Merge pull request #18
refactor again
2024-09-23 22:13:35 +03:00
prtsie
cd49f68db1 Merge pull request #17
refactor
2024-09-23 21:49:04 +03:00
253 changed files with 3582 additions and 785 deletions

33
.drone.yml Normal file
View File

@@ -0,0 +1,33 @@
kind: pipeline
type: docker
name: "Backend"
steps:
- name: test
image: mcr.microsoft.com/dotnet/sdk:8.0
commands:
- dotnet build
- dotnet test
- name: publish
image: plugins/docker
settings:
username: prtsie
password:
from_secret: docker_pass
registry: git.prtsie.ru
repo: git.prtsie.ru/prtsie/schengen-visa-${DRONE_BRANCH}
tags:
- latest
- name: deploy
image: appleboy/drone-ssh
settings:
script_stop: true
host: prtsie.ru:1337
username: deploy
key:
from_secret: ssh_key
script:
- cd /deploy
- ./deploy.sh ${DRONE_BRANCH}

2
.gitignore vendored
View File

@@ -33,3 +33,5 @@ _UpgradeReport_Files/
Thumbs.db
Desktop.ini
.DS_Store
.env

View File

@@ -6,7 +6,7 @@ public static class Constants
{
public readonly static Regex EnglishWordRegex = new("^[a-zA-Z']*$");
public readonly static Regex EnglishPhraseRegex = new(@"^[a-zA-Z №0-9;,\-_+=#*']*$");
public readonly static Regex EnglishPhraseRegex = new("""^[a-zA-Z №0-9;,\-_+=#*'\"]*$""");
public readonly static Regex PhoneNumRegex = new(@"^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$");
}

View File

@@ -35,7 +35,7 @@ public class VisaApplicationRequestsHandler(
return mapper.Map<List<VisaApplicationPreview>>(visaApplications);
}
/// <summary> <inheritdoc cref="IVisaApplicationRequestsHandler.GetApplicationForApplicantAsync"/> </summary>
/// <inheritdoc cref="IVisaApplicationRequestsHandler.GetApplicationForApplicantAsync"/>
public async Task<VisaApplicationModel> GetApplicationForApplicantAsync(Guid id, CancellationToken cancellationToken)
{
var applicant = await applicants.FindByUserIdAsync(userIdProvider.GetUserId(), cancellationToken);

View File

@@ -1,5 +1,4 @@
@using BlazorWebAssemblyVisaApiClient.ErrorHandling
<GlobalErrorHandler >
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">

View File

@@ -2,7 +2,6 @@
@using BlazorWebAssemblyVisaApiClient.ErrorHandling
@using BlazorWebAssemblyVisaApiClient.Infrastructure.Services.UserDataProvider
@using VisaApiClient
@code {
public static AuthData? AuthData;

View File

@@ -1,6 +1,5 @@
@using BlazorWebAssemblyVisaApiClient.ErrorHandling
@using VisaApiClient
@code
{
[CascadingParameter] protected GlobalErrorHandler ErrorHandler { get; set; } = null!;

View File

@@ -1,6 +1,5 @@
@using BlazorWebAssemblyVisaApiClient.Infrastructure.Services.DateTimeProvider
@using VisaApiClient
<div>
<div >
<label>

View File

@@ -1,7 +1,7 @@
@using System.Linq.Expressions
@typeparam TItem where TItem : class
@using System.Linq.Expressions
@using System.Reflection
@using BlazorWebAssemblyVisaApiClient.Infrastructure.Helpers
@typeparam TItem where TItem : class
@typeparam TMember where TMember : struct, Enum
<InputSelect TValue="TMember" @bind-Value="selected">

View File

@@ -1,6 +1,5 @@
@using BlazorWebAssemblyVisaApiClient.Infrastructure.Services.DateTimeProvider
@using VisaApiClient
<div>
<label>
Issuer:<br/>

View File

@@ -1,6 +1,5 @@
@using BlazorWebAssemblyVisaApiClient.Infrastructure.Services.DateTimeProvider
@using VisaApiClient
<div>
<label>
Number:<br/>

View File

@@ -1,7 +1,6 @@
@using System.Net
@using BlazorWebAssemblyVisaApiClient.Common.Exceptions
@using VisaApiClient
<CascadingValue Value="this">
<Modal @ref="modal">
<BodyTemplate>

View File

@@ -1,17 +1,17 @@
using System.ComponentModel.DataAnnotations;
namespace BlazorWebAssemblyVisaApiClient.Infrastructure.Helpers;
public static class EnumExtensions
{
public static string GetDisplayName(this Enum value)
{
var enumMembers = value.GetType().GetMembers();
var member = enumMembers.First(info => info.Name == value.ToString());
var displayAttribute = (DisplayAttribute?)member
.GetCustomAttributes(typeof(DisplayAttribute), false)
.FirstOrDefault();
var displayName = displayAttribute?.Name ?? value.ToString();
return displayName;
}
using System.ComponentModel.DataAnnotations;
namespace BlazorWebAssemblyVisaApiClient.Infrastructure.Helpers;
public static class EnumExtensions
{
public static string GetDisplayName(this Enum value)
{
var enumMembers = value.GetType().GetMembers();
var member = enumMembers.First(info => info.Name == value.ToString());
var displayAttribute = (DisplayAttribute?)member
.GetCustomAttributes(typeof(DisplayAttribute), false)
.FirstOrDefault();
var displayName = displayAttribute?.Name ?? value.ToString();
return displayName;
}
}

View File

@@ -1,10 +1,10 @@
@page "/authorities/add"
@using AutoMapper
@using BlazorWebAssemblyVisaApiClient.Validation.Applicants.Models
@using VisaApiClient
@using BlazorWebAssemblyVisaApiClient.Components
@using BlazorWebAssemblyVisaApiClient.Infrastructure.Helpers
@using BlazorWebAssemblyVisaApiClient.Validation.Applicants.Models
@using FluentValidation
@using VisaApiClient
@inherits BlazorWebAssemblyVisaApiClient.Components.Base.VisaClientComponentBase
<EditForm Model="requestModel" class="with-centered-content">

View File

@@ -1,143 +1,143 @@
@page "/applications"
@using BlazorWebAssemblyVisaApiClient.Common.Exceptions
@using BlazorWebAssemblyVisaApiClient.Infrastructure.Helpers
@using BlazorWebAssemblyVisaApiClient.Infrastructure.Services.UserDataProvider
@using BlazorWebAssemblyVisaApiClient.Validation.VisaApplications.Models
@using VisaApiClient
@inherits BlazorWebAssemblyVisaApiClient.Components.Base.VisaClientComponentBase
<PageTitle>Applications</PageTitle>
<table class="table table-bordered table-hover">
<thead>
<tr>
<th>Destination Country</th>
<th>Visa Category</th>
<th>Request date</th>
<th>Days requested</th>
<th>Status</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var application in applications)
{
var rowClass = application.Status switch
{
ApplicationStatus.Pending => "",
ApplicationStatus.Approved => "table-success",
ApplicationStatus.Rejected => "table-danger",
ApplicationStatus.Closed => "table-danger",
_ => throw new ArgumentOutOfRangeException()
};
<tr class="@rowClass">
<td>@application.DestinationCountry</td>
<td>@(((VisaCategoryModel)application.VisaCategory).GetDisplayName())</td>
<td>@application.RequestDate.ToString("d")</td>
<td>@application.ValidDaysRequested</td>
<td>@application.Status.GetDisplayName()</td>
<td>
<NavLink href="@($"/applications/{application.Id}")">
<button class="btn-primary">See</button>
</NavLink>
@if (currentRole == Constants.ApplicantRole)
{
<span> | </span>
<input type="button" class="btn-outline-primary" @onclick="() => DownloadApplication(application)" value="Download"/>
if (application.Status is ApplicationStatus.Pending)
{
<span> | </span>
<input type="button" class="border-danger" @onclick="() => CloseApplication(application)" value="Close"/>
}
}
</td>
</tr>
}
</tbody>
</table >
<script>
window.downloadFileFromStream = async (contentStreamReference) => {
const arrayBuffer = await contentStreamReference.arrayBuffer();
const blob = new Blob([arrayBuffer]);
const url = URL.createObjectURL(blob);
const anchorElement = document.createElement('a');
anchorElement.href = url;
anchorElement.download = 'Application.xlsx';
anchorElement.click();
anchorElement.remove();
URL.revokeObjectURL(url);
}
</script>
@code {
private string currentRole = null!;
private List<VisaApplicationPreview> applications = [];
[Inject] private IUserDataProvider UserDataProvider { get; set; } = null!;
[Inject] private IJSRuntime JavaScriptInterop { get; set; } = null!;
protected override async Task OnInitializedAsync()
{
try
{
currentRole = UserDataProvider.CurrentRole ?? throw new NotLoggedInException();
}
catch (Exception e)
{
ErrorHandler.Handle(e);
}
await Fetch();
}
private async Task Fetch()
{
try
{
applications = currentRole switch
{
Constants.ApplicantRole => (await Client.GetApplicationsForApplicantAsync()).OrderByDescending(a => a.RequestDate).ToList(),
Constants.ApprovingAuthorityRole => (await Client.GetPendingAsync()).OrderByDescending(a => a.RequestDate).ToList(),
_ => throw new NotLoggedInException()
};
}
catch (Exception e)
{
ErrorHandler.Handle(e);
}
}
private async Task CloseApplication(VisaApplicationPreview application)
{
try
{
await Client.CloseApplicationAsync(application.Id);
application.Status = ApplicationStatus.Closed;
StateHasChanged();
}
catch (Exception e)
{
ErrorHandler.Handle(e);
}
}
private async Task DownloadApplication(VisaApplicationPreview application)
{
try
{
var response = await Client.DownloadApplicationForApplicantAsync(application.Id);
using var streamRef = new DotNetStreamReference(stream: response.Stream);
await JavaScriptInterop.InvokeVoidAsync("downloadFileFromStream", streamRef);
}
catch (Exception e)
{
ErrorHandler.Handle(e);
}
}
}
@page "/applications"
@using BlazorWebAssemblyVisaApiClient.Common.Exceptions
@using BlazorWebAssemblyVisaApiClient.Infrastructure.Helpers
@using BlazorWebAssemblyVisaApiClient.Infrastructure.Services.UserDataProvider
@using BlazorWebAssemblyVisaApiClient.Validation.VisaApplications.Models
@using VisaApiClient
@inherits BlazorWebAssemblyVisaApiClient.Components.Base.VisaClientComponentBase
<PageTitle>Applications</PageTitle>
<table class="table table-bordered table-hover">
<thead>
<tr>
<th>Destination Country</th>
<th>Visa Category</th>
<th>Request date</th>
<th>Days requested</th>
<th>Status</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var application in applications)
{
var rowClass = application.Status switch
{
ApplicationStatus.Pending => "",
ApplicationStatus.Approved => "table-success",
ApplicationStatus.Rejected => "table-danger",
ApplicationStatus.Closed => "table-danger",
_ => throw new ArgumentOutOfRangeException()
};
<tr class="@rowClass">
<td>@application.DestinationCountry</td>
<td>@(((VisaCategoryModel)application.VisaCategory).GetDisplayName())</td>
<td>@application.RequestDate.ToString("d")</td>
<td>@application.ValidDaysRequested</td>
<td>@application.Status.GetDisplayName()</td>
<td>
<NavLink href="@($"/applications/{application.Id}")">
<button class="btn-primary">See</button>
</NavLink>
@if (currentRole == Constants.ApplicantRole)
{
<span> | </span>
<input type="button" class="btn-outline-primary" @onclick="() => DownloadApplication(application)" value="Download"/>
if (application.Status is ApplicationStatus.Pending)
{
<span> | </span>
<input type="button" class="border-danger" @onclick="() => CloseApplication(application)" value="Close"/>
}
}
</td>
</tr>
}
</tbody>
</table >
<script>
window.downloadFileFromStream = async (contentStreamReference) => {
const arrayBuffer = await contentStreamReference.arrayBuffer();
const blob = new Blob([arrayBuffer]);
const url = URL.createObjectURL(blob);
const anchorElement = document.createElement('a');
anchorElement.href = url;
anchorElement.download = 'Application.xlsx';
anchorElement.click();
anchorElement.remove();
URL.revokeObjectURL(url);
}
</script>
@code {
private string currentRole = null!;
private List<VisaApplicationPreview> applications = [];
[Inject] private IUserDataProvider UserDataProvider { get; set; } = null!;
[Inject] private IJSRuntime JavaScriptInterop { get; set; } = null!;
protected override async Task OnInitializedAsync()
{
try
{
currentRole = UserDataProvider.CurrentRole ?? throw new NotLoggedInException();
}
catch (Exception e)
{
ErrorHandler.Handle(e);
}
await Fetch();
}
private async Task Fetch()
{
try
{
applications = currentRole switch
{
Constants.ApplicantRole => (await Client.GetApplicationsForApplicantAsync()).OrderByDescending(a => a.RequestDate).ToList(),
Constants.ApprovingAuthorityRole => (await Client.GetPendingAsync()).OrderByDescending(a => a.RequestDate).ToList(),
_ => throw new NotLoggedInException()
};
}
catch (Exception e)
{
ErrorHandler.Handle(e);
}
}
private async Task CloseApplication(VisaApplicationPreview application)
{
try
{
await Client.CloseApplicationAsync(application.Id);
application.Status = ApplicationStatus.Closed;
StateHasChanged();
}
catch (Exception e)
{
ErrorHandler.Handle(e);
}
}
private async Task DownloadApplication(VisaApplicationPreview application)
{
try
{
var response = await Client.DownloadApplicationForApplicantAsync(application.Id);
using var streamRef = new DotNetStreamReference(stream: response.Stream);
await JavaScriptInterop.InvokeVoidAsync("downloadFileFromStream", streamRef);
}
catch (Exception e)
{
ErrorHandler.Handle(e);
}
}
}

View File

@@ -1,8 +1,8 @@
@page "/"
@using BlazorWebAssemblyVisaApiClient.Components.Auth
@using VisaApiClient
@using BlazorWebAssemblyVisaApiClient.Components.FormComponents.Applicants
@using BlazorWebAssemblyVisaApiClient.Components
@using BlazorWebAssemblyVisaApiClient.Components.Auth
@using BlazorWebAssemblyVisaApiClient.Components.FormComponents.Applicants
@using VisaApiClient
@inherits BlazorWebAssemblyVisaApiClient.Components.Base.VisaClientComponentBase
<PageTitle>Authentication</PageTitle>

View File

@@ -1,11 +1,11 @@
@page "/authorities/{authorityId}/{oldEmail}"
@inherits BlazorWebAssemblyVisaApiClient.Components.Base.VisaClientComponentBase
@using BlazorWebAssemblyVisaApiClient.Common.Exceptions
@using BlazorWebAssemblyVisaApiClient.Infrastructure.Services.UserDataProvider
@using VisaApiClient
@using BlazorWebAssemblyVisaApiClient.Components
@using BlazorWebAssemblyVisaApiClient.Infrastructure.Helpers
@using BlazorWebAssemblyVisaApiClient.Infrastructure.Services.UserDataProvider
@using FluentValidation
@using VisaApiClient
@inherits BlazorWebAssemblyVisaApiClient.Components.Base.VisaClientComponentBase
<EditForm Model="model" class="with-centered-content">
<div >

View File

@@ -1,17 +1,17 @@
@page "/applications/new"
@using System.Net
@using AutoMapper
@using BlazorWebAssemblyVisaApiClient.Validation.VisaApplications.Models
@using VisaApiClient
@using BlazorWebAssemblyVisaApiClient.Components
@using BlazorWebAssemblyVisaApiClient.Components.FormComponents
@using BlazorWebAssemblyVisaApiClient.Components.FormComponents.VisaApplications
@using BlazorWebAssemblyVisaApiClient.Infrastructure.Helpers
@using BlazorWebAssemblyVisaApiClient.Infrastructure.Services.DateTimeProvider
@using BlazorWebAssemblyVisaApiClient.Infrastructure.Services.UserDataProvider
@using BlazorWebAssemblyVisaApiClient.Validation
@using BlazorWebAssemblyVisaApiClient.Validation.VisaApplications.Models
@using FluentValidation
@using Newtonsoft.Json.Linq
@using BlazorWebAssemblyVisaApiClient.Components.FormComponents
@using VisaApiClient
@inherits BlazorWebAssemblyVisaApiClient.Components.Base.VisaClientComponentBase
<PageTitle>New Application</PageTitle>

Some files were not shown because too many files have changed in this diff Show More