ASP.NET Core 10 Web API с MS SQL Server, JWT аутентификацией и layered архитектурой.
- .NET 10 - ASP.NET Core Web API
- MS SQL Server 2022 - база данных в Docker
- EF Core 10 - ORM с миграциями
- JWT - аутентификация с access + refresh tokens
- Serilog - структурированное логирование
- Swagger/OpenAPI - документация API
- xUnit + TestContainers - integration tests
- Docker Desktop
- .NET 10 SDK (опционально, для локальной разработки)
docker compose up --buildПеред запуском задайте секреты Altegio в окружении (или в .env, который не коммитится):
ALTEGIO_BEARER_TOKEN=your-bearer-token
ALTEGIO_USER_TOKEN=your-user-token
ALTEGIO_COMPANY_ID=your-company-idAPI будет доступно по адресу: http://localhost:5000 Swagger UI: http://localhost:5000/swagger
-
Admin
- Email:
admin@local - Password:
Admin123! - Role: Admin
- Email:
-
Owner
- Email:
owner@local - Password:
Owner123! - Role: Owner
- Email:
curl http://localhost:5000/healthResponse:
{
"status": "healthy",
"database": "connected"
}curl -X POST http://localhost:5000/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "admin@local",
"password": "Admin123!"
}'Response:
{
"accessToken": "eyJhbGc...",
"refreshToken": "CfDJ8..."
}curl http://localhost:5000/api/v1/auth/me \
-H "Authorization: Bearer <your-access-token>"Response:
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"email": "admin@local",
"role": "Admin"
}curl -X POST http://localhost:5000/api/v1/auth/logout \
-H "Authorization: Bearer <your-access-token>"Response:
{
"message": "Logged out successfully"
}kudzierki-app/
├── src/
│ ├── Api/ # REST API, controllers, middleware
│ ├── Infrastructure/ # EF Core, repositories, services
│ └── Shared/ # Result pattern, abstractions
├── tests/
│ └── Api.IntegrationTests/ # Integration tests
├── docker-compose.yml
└── README.md
Shared Layer:
Result<T>- паттерн для обработки успеха/ошибокError- типизированные ошибкиITimeProvider- абстракция времени для тестирования
Infrastructure Layer:
ApplicationDbContext- EF Core контекстUser,RefreshToken- entity modelsUserRepository,RefreshTokenRepository- data accessAuthService- логика аутентификацииTokenService- генерация/валидация JWT
Api Layer:
AuthController- login, logout, me endpointsHealthController- health checksCorrelationIdMiddleware- трекинг запросов- Configuration extensions (Serilog, JWT, Swagger)
- Login: пользователь отправляет email + password
- Validation: система проверяет credentials
- Token Generation:
- Access token (15 минут) - содержит claims (id, email, role)
- Refresh token (7 дней) - хранится в БД для ревокации
- Authorization: access token в заголовке
Authorization: Bearer <token> - Logout: ревокация refresh tokens в БД
Таблицы:
Users- id, email (unique), passwordHash, role (Admin/Owner), timestampsRefreshTokens- id, userId (FK), token (unique), expiresAt, createdAt, revokedAt
Миграции:
cd src/Infrastructure
dotnet ef migrations add MigrationName --startup-project ../ApiПрименение миграций: Миграции применяются автоматически при запуске приложения через Polly retry policy.
docker run -d -p 1433:1433 \
-e ACCEPT_EULA=Y \
-e MSSQL_SA_PASSWORD='YourStrong@Password' \
mcr.microsoft.com/mssql/server:2022-latestcd src/Api
dotnet ef database update --project ../Infrastructuredotnet run --project src/Apidotnet testИнфраструктура тестов:
CustomWebApplicationFactory- TestContainers с MS SQLRespawn- сброс БД между тестамиFluentAssertions- readable assertions
- ✅ Login с валидными/невалидными credentials
- ✅ GetMe с токеном/без токена
- ✅ Logout flow
- ✅ Валидация запросов (400 Bad Request)
- ✅ Health endpoint с correlation ID
{
"ConnectionStrings": {
"DefaultConnection": "Server=mssql,1433;Database=kudzierki;..."
},
"Jwt": {
"Secret": "change-in-production",
"Issuer": "Kudzierki.Api",
"Audience": "Kudzierki.Client"
},
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft.EntityFrameworkCore": "Warning"
}
}
}
}Connection string можно переопределить:
ConnectionStrings__DefaultConnection="Server=...;Database=...;"Секреты Altegio можно передавать так:
ALTEGIO_BEARER_TOKEN="..."
ALTEGIO_USER_TOKEN="..."
Altegio__CompanyId="1"
# Optional: background sync config
Altegio__TransactionsSync__Enabled="true"
Altegio__TransactionsSync__PollingIntervalMinutes="5"
Altegio__TransactionsSync__ShortFromDaysOffset="-1"
Altegio__TransactionsSync__ShortToDaysOffset="30"
Altegio__TransactionsSync__FullFromDaysOffset="-30"
Altegio__TransactionsSync__FullToDaysOffset="365"
Altegio__TransactionsSync__FullSyncIntervalHours="24"Приложение валидирует Altegio настройки на старте и завершится с ошибкой, если токены не заданы.
- Структурированные логи в JSON формате
- Correlation ID для трекинга запросов
- Enrichers: Environment, MachineName
- Console sink для Development
[2026-01-13 12:00:00.123 +00:00] [INF] [a1b2c3d4] Login attempt for email: admin@local
- ✅ Пароли хешируются через
PasswordHasher<User>(bcrypt-based) - ✅ JWT подписываются HMAC SHA256
- ✅ Access tokens - short-lived (15 мин)
- ✅ Refresh tokens - long-lived (7 дней), хранятся в БД
- ✅ Logout ревоцирует refresh tokens
⚠️ ВАЖНО: измените JWT Secret в production!
docker exec -it kudzierki-mssql /opt/mssql-tools18/bin/sqlcmd \
-S localhost -U sa -P 'YourStrong@Password' -C \
-Q 'SELECT * FROM Users'- Server:
localhost,1433 - Username:
sa - Password:
YourStrong@Password - Trust Server Certificate: Yes
- Проверьте логи:
docker compose logs api - Убедитесь, что MSSQL healthy:
docker compose ps - Проверьте connection string в логах
# Пересоздать БД
docker compose down -v
docker compose up --build# Очистить контейнеры TestContainers
docker ps -a | grep testcontainers | awk '{print $1}' | xargs docker rm -fПосле успешного запуска базового каркаса:
- ✅ Добавить доменные модули (Scheduling, Cash)
- ✅ Реализовать business logic endpoints
- ✅ Добавить unit tests для services
- ✅ Настроить CI/CD pipeline
- ✅ Добавить rate limiting и CORS
MIT
POST /api/v1/altegio/finance/transactions/syncloads transactions from Altegio and stores them in local tables.GET /api/v1/altegio/finance/transactionsreads from local snapshots, not directly from Altegio API.- Response field
CreatedAtis mapped fromFirstSeenAtUtc(first observation time in this service). - Raw payload history is stored in
AltegioTransactionRawswith deduplication key(ExternalId, PayloadHash). - Current snapshot state is stored in
AltegioTransactionSnapshotswith unique keyExternalId.
Configure under Altegio:TransactionsSync in appsettings.json (or env vars).
Default behavior: disabled (Enabled: false).
Example config:
"Altegio": {
"TransactionsSync": {
"Enabled": true,
"PollingIntervalMinutes": 5,
"ShortFromDaysOffset": -1,
"ShortToDaysOffset": 30,
"FullFromDaysOffset": -30,
"FullToDaysOffset": 365,
"FullSyncIntervalHours": 24
}
}