29
.dockerignore
Normal file
29
.dockerignore
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.idea
|
||||||
|
.vs
|
||||||
|
.vscode
|
||||||
|
**/.idea
|
||||||
|
|
||||||
|
# Build artifacts
|
||||||
|
**/bin
|
||||||
|
**/obj
|
||||||
|
front/node_modules
|
||||||
|
front/dist
|
||||||
|
|
||||||
|
# Documentation
|
||||||
|
Docs/
|
||||||
|
|
||||||
|
# Dev files
|
||||||
|
docker/
|
||||||
|
*.sh
|
||||||
|
|
||||||
|
# Env files (secrets must not be in the image)
|
||||||
|
.env
|
||||||
|
deploy/.env
|
||||||
|
|
||||||
|
# Misc
|
||||||
|
**/*.user
|
||||||
|
**/*.DotSettings.user
|
||||||
17
deploy/.env.example
Normal file
17
deploy/.env.example
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# PostgreSQL (shared VPS instance — create DB/user manually)
|
||||||
|
POSTGRES_DB=aips_db
|
||||||
|
POSTGRES_USER=aips_user
|
||||||
|
POSTGRES_PASSWORD=CHANGE_ME_strong_password_here
|
||||||
|
|
||||||
|
# RabbitMQ
|
||||||
|
RABBITMQ_DEFAULT_USER=aips_rabbit
|
||||||
|
RABBITMQ_DEFAULT_PASS=CHANGE_ME_rabbit_password
|
||||||
|
RABBITMQ_DEFAULT_VHOST=/
|
||||||
|
RABBITMQ_EXCHANGE=aips
|
||||||
|
|
||||||
|
# JWT
|
||||||
|
JWT_ISSUER=AIPS
|
||||||
|
JWT_AUDIENCE=AIPSWebApi
|
||||||
|
JWT_KEY=CHANGE_ME_generate_a_64_char_random_string_here
|
||||||
|
JWT_EXPIRATION_MINUTES=60
|
||||||
|
JWT_REFRESH_TOKEN_EXPIRATION_DAYS=7
|
||||||
14
deploy/Dockerfile.front
Normal file
14
deploy/Dockerfile.front
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
FROM oven/bun:1 AS build
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY front/package.json front/bun.lock ./
|
||||||
|
RUN bun install --frozen-lockfile
|
||||||
|
|
||||||
|
COPY front/ .
|
||||||
|
RUN bun run build
|
||||||
|
|
||||||
|
FROM nginx:alpine
|
||||||
|
RUN rm /etc/nginx/conf.d/default.conf
|
||||||
|
COPY deploy/nginx/nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
COPY --from=build /app/dist /usr/share/nginx/html
|
||||||
|
EXPOSE 80
|
||||||
23
deploy/Dockerfile.rt
Normal file
23
deploy/Dockerfile.rt
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
|
||||||
|
WORKDIR /src
|
||||||
|
|
||||||
|
COPY dotnet/dotnet.sln dotnet/dotnet.sln
|
||||||
|
COPY dotnet/AipsCore/AipsCore.csproj dotnet/AipsCore/
|
||||||
|
COPY dotnet/AipsWebApi/AipsWebApi.csproj dotnet/AipsWebApi/
|
||||||
|
COPY dotnet/AipsRT/AipsRT.csproj dotnet/AipsRT/
|
||||||
|
COPY dotnet/AipsWorker/AipsWorker.csproj dotnet/AipsWorker/
|
||||||
|
|
||||||
|
WORKDIR /src/dotnet
|
||||||
|
RUN dotnet restore dotnet.sln
|
||||||
|
|
||||||
|
WORKDIR /src
|
||||||
|
COPY dotnet/ dotnet/
|
||||||
|
|
||||||
|
WORKDIR /src/dotnet
|
||||||
|
RUN dotnet publish AipsRT/AipsRT.csproj -c Release -o /app/publish --no-restore
|
||||||
|
|
||||||
|
FROM mcr.microsoft.com/dotnet/aspnet:10.0
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=build /app/publish .
|
||||||
|
EXPOSE 8080
|
||||||
|
ENTRYPOINT ["dotnet", "AipsRT.dll"]
|
||||||
23
deploy/Dockerfile.webapi
Normal file
23
deploy/Dockerfile.webapi
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
|
||||||
|
WORKDIR /src
|
||||||
|
|
||||||
|
COPY dotnet/dotnet.sln dotnet/dotnet.sln
|
||||||
|
COPY dotnet/AipsCore/AipsCore.csproj dotnet/AipsCore/
|
||||||
|
COPY dotnet/AipsWebApi/AipsWebApi.csproj dotnet/AipsWebApi/
|
||||||
|
COPY dotnet/AipsRT/AipsRT.csproj dotnet/AipsRT/
|
||||||
|
COPY dotnet/AipsWorker/AipsWorker.csproj dotnet/AipsWorker/
|
||||||
|
|
||||||
|
WORKDIR /src/dotnet
|
||||||
|
RUN dotnet restore dotnet.sln
|
||||||
|
|
||||||
|
WORKDIR /src
|
||||||
|
COPY dotnet/ dotnet/
|
||||||
|
|
||||||
|
WORKDIR /src/dotnet
|
||||||
|
RUN dotnet publish AipsWebApi/AipsWebApi.csproj -c Release -o /app/publish --no-restore
|
||||||
|
|
||||||
|
FROM mcr.microsoft.com/dotnet/aspnet:10.0
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=build /app/publish .
|
||||||
|
EXPOSE 8080
|
||||||
|
ENTRYPOINT ["dotnet", "AipsWebApi.dll"]
|
||||||
22
deploy/Dockerfile.worker
Normal file
22
deploy/Dockerfile.worker
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
|
||||||
|
WORKDIR /src
|
||||||
|
|
||||||
|
COPY dotnet/dotnet.sln dotnet/dotnet.sln
|
||||||
|
COPY dotnet/AipsCore/AipsCore.csproj dotnet/AipsCore/
|
||||||
|
COPY dotnet/AipsWebApi/AipsWebApi.csproj dotnet/AipsWebApi/
|
||||||
|
COPY dotnet/AipsRT/AipsRT.csproj dotnet/AipsRT/
|
||||||
|
COPY dotnet/AipsWorker/AipsWorker.csproj dotnet/AipsWorker/
|
||||||
|
|
||||||
|
WORKDIR /src/dotnet
|
||||||
|
RUN dotnet restore dotnet.sln
|
||||||
|
|
||||||
|
WORKDIR /src
|
||||||
|
COPY dotnet/ dotnet/
|
||||||
|
|
||||||
|
WORKDIR /src/dotnet
|
||||||
|
RUN dotnet publish AipsWorker/AipsWorker.csproj -c Release -o /app/publish --no-restore
|
||||||
|
|
||||||
|
FROM mcr.microsoft.com/dotnet/runtime:10.0
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=build /app/publish .
|
||||||
|
ENTRYPOINT ["dotnet", "AipsWorker.dll"]
|
||||||
103
deploy/docker-compose.yml
Normal file
103
deploy/docker-compose.yml
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
services:
|
||||||
|
rabbitmq:
|
||||||
|
image: rabbitmq:3-management
|
||||||
|
container_name: aips-rabbitmq
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
RABBITMQ_DEFAULT_USER: ${RABBITMQ_DEFAULT_USER}
|
||||||
|
RABBITMQ_DEFAULT_PASS: ${RABBITMQ_DEFAULT_PASS}
|
||||||
|
RABBITMQ_DEFAULT_VHOST: ${RABBITMQ_DEFAULT_VHOST}
|
||||||
|
volumes:
|
||||||
|
- rabbitmqdata:/var/lib/rabbitmq
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "rabbitmq-diagnostics", "-q", "ping"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
|
webapi:
|
||||||
|
build:
|
||||||
|
context: ..
|
||||||
|
dockerfile: deploy/Dockerfile.webapi
|
||||||
|
container_name: aips-webapi
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
ASPNETCORE_URLS: "http://+:8080"
|
||||||
|
ASPNETCORE_ENVIRONMENT: "Production"
|
||||||
|
DB_CONN_STRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB};Username=${POSTGRES_USER};Password=${POSTGRES_PASSWORD}"
|
||||||
|
RABBITMQ_AMQP_URI: "amqp://${RABBITMQ_DEFAULT_USER}:${RABBITMQ_DEFAULT_PASS}@rabbitmq:5672/${RABBITMQ_DEFAULT_VHOST}"
|
||||||
|
RABBITMQ_EXCHANGE: "${RABBITMQ_EXCHANGE}"
|
||||||
|
JWT_ISSUER: "${JWT_ISSUER}"
|
||||||
|
JWT_AUDIENCE: "${JWT_AUDIENCE}"
|
||||||
|
JWT_KEY: "${JWT_KEY}"
|
||||||
|
JWT_EXPIRATION_MINUTES: "${JWT_EXPIRATION_MINUTES}"
|
||||||
|
JWT_REFRESH_TOKEN_EXPIRATION_DAYS: "${JWT_REFRESH_TOKEN_EXPIRATION_DAYS}"
|
||||||
|
networks:
|
||||||
|
- default
|
||||||
|
- back_network
|
||||||
|
depends_on:
|
||||||
|
rabbitmq:
|
||||||
|
condition: service_healthy
|
||||||
|
|
||||||
|
rt:
|
||||||
|
build:
|
||||||
|
context: ..
|
||||||
|
dockerfile: deploy/Dockerfile.rt
|
||||||
|
container_name: aips-rt
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
ASPNETCORE_URLS: "http://+:8080"
|
||||||
|
ASPNETCORE_ENVIRONMENT: "Production"
|
||||||
|
DB_CONN_STRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB};Username=${POSTGRES_USER};Password=${POSTGRES_PASSWORD}"
|
||||||
|
RABBITMQ_AMQP_URI: "amqp://${RABBITMQ_DEFAULT_USER}:${RABBITMQ_DEFAULT_PASS}@rabbitmq:5672/${RABBITMQ_DEFAULT_VHOST}"
|
||||||
|
RABBITMQ_EXCHANGE: "${RABBITMQ_EXCHANGE}"
|
||||||
|
JWT_ISSUER: "${JWT_ISSUER}"
|
||||||
|
JWT_AUDIENCE: "${JWT_AUDIENCE}"
|
||||||
|
JWT_KEY: "${JWT_KEY}"
|
||||||
|
JWT_EXPIRATION_MINUTES: "${JWT_EXPIRATION_MINUTES}"
|
||||||
|
JWT_REFRESH_TOKEN_EXPIRATION_DAYS: "${JWT_REFRESH_TOKEN_EXPIRATION_DAYS}"
|
||||||
|
networks:
|
||||||
|
- default
|
||||||
|
- back_network
|
||||||
|
depends_on:
|
||||||
|
rabbitmq:
|
||||||
|
condition: service_healthy
|
||||||
|
|
||||||
|
worker:
|
||||||
|
build:
|
||||||
|
context: ..
|
||||||
|
dockerfile: deploy/Dockerfile.worker
|
||||||
|
container_name: aips-worker
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
DB_CONN_STRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB};Username=${POSTGRES_USER};Password=${POSTGRES_PASSWORD}"
|
||||||
|
RABBITMQ_AMQP_URI: "amqp://${RABBITMQ_DEFAULT_USER}:${RABBITMQ_DEFAULT_PASS}@rabbitmq:5672/${RABBITMQ_DEFAULT_VHOST}"
|
||||||
|
RABBITMQ_EXCHANGE: "${RABBITMQ_EXCHANGE}"
|
||||||
|
JWT_ISSUER: "${JWT_ISSUER}"
|
||||||
|
JWT_AUDIENCE: "${JWT_AUDIENCE}"
|
||||||
|
JWT_KEY: "${JWT_KEY}"
|
||||||
|
networks:
|
||||||
|
- default
|
||||||
|
- back_network
|
||||||
|
depends_on:
|
||||||
|
rabbitmq:
|
||||||
|
condition: service_healthy
|
||||||
|
|
||||||
|
nginx:
|
||||||
|
build:
|
||||||
|
context: ..
|
||||||
|
dockerfile: deploy/Dockerfile.front
|
||||||
|
container_name: aips-nginx
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "8090:80"
|
||||||
|
depends_on:
|
||||||
|
- webapi
|
||||||
|
- rt
|
||||||
|
|
||||||
|
networks:
|
||||||
|
back_network:
|
||||||
|
external: true
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
rabbitmqdata:
|
||||||
45
deploy/nginx/aips-global.conf
Normal file
45
deploy/nginx/aips-global.conf
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name aips.stewki.com;
|
||||||
|
|
||||||
|
location /.well-known/acme-challenge/ {
|
||||||
|
root /var/www/certbot;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
return 301 https://$host$request_uri;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
server_name aips.stewki.com;
|
||||||
|
|
||||||
|
ssl_certificate /etc/letsencrypt/live/aips.stewki.com/fullchain.pem;
|
||||||
|
ssl_certificate_key /etc/letsencrypt/live/aips.stewki.com/privkey.pem;
|
||||||
|
|
||||||
|
client_max_body_size 10M;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://host.docker.internal:8090;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /hubs/ {
|
||||||
|
proxy_pass http://host.docker.internal:8090;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
|
||||||
|
proxy_read_timeout 86400s;
|
||||||
|
proxy_send_timeout 86400s;
|
||||||
|
}
|
||||||
|
}
|
||||||
46
deploy/nginx/nginx.conf
Normal file
46
deploy/nginx/nginx.conf
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
upstream webapi {
|
||||||
|
server webapi:8080;
|
||||||
|
}
|
||||||
|
|
||||||
|
upstream rt {
|
||||||
|
server rt:8080;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
client_max_body_size 10M;
|
||||||
|
|
||||||
|
# REST API
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://webapi;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
# SignalR hubs (WebSocket support)
|
||||||
|
location /hubs/ {
|
||||||
|
proxy_pass http://rt;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
|
||||||
|
proxy_read_timeout 86400s;
|
||||||
|
proxy_send_timeout 86400s;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Vue SPA
|
||||||
|
location / {
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html;
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
}
|
||||||
27
dos-start-back.bat
Normal file
27
dos-start-back.bat
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<# : batch portion
|
||||||
|
@echo off
|
||||||
|
set "SCRIPT_DIR=%~dp0"
|
||||||
|
powershell -ExecutionPolicy Bypass "iex((Get-Content '%~f0' -Raw))"
|
||||||
|
exit /b
|
||||||
|
#>
|
||||||
|
|
||||||
|
Set-Location (Join-Path $env:SCRIPT_DIR "dotnet")
|
||||||
|
|
||||||
|
$jobs = @()
|
||||||
|
$jobs += Start-Job -ScriptBlock { Set-Location $using:PWD; dotnet run --project AipsWebApi 2>&1 | ForEach-Object { "[WebApi] $_" } }
|
||||||
|
$jobs += Start-Job -ScriptBlock { Set-Location $using:PWD; dotnet run --project AipsRT 2>&1 | ForEach-Object { "[RT] $_" } }
|
||||||
|
$jobs += Start-Job -ScriptBlock { Set-Location $using:PWD; dotnet run --project AipsWorker 2>&1 | ForEach-Object { "[Worker] $_" } }
|
||||||
|
|
||||||
|
try {
|
||||||
|
while ($jobs | Where-Object { $_.State -eq 'Running' }) {
|
||||||
|
foreach ($job in $jobs) {
|
||||||
|
Receive-Job -Job $job
|
||||||
|
}
|
||||||
|
Start-Sleep -Milliseconds 200
|
||||||
|
}
|
||||||
|
foreach ($job in $jobs) {
|
||||||
|
Receive-Job -Job $job
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
$jobs | Stop-Job -PassThru | Remove-Job
|
||||||
|
}
|
||||||
4
dos-start-front.bat
Normal file
4
dos-start-front.bat
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
@echo off
|
||||||
|
cd /d "%~dp0front"
|
||||||
|
|
||||||
|
bun dev
|
||||||
4
dos-start-infra.bat
Normal file
4
dos-start-infra.bat
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
@echo off
|
||||||
|
cd /d "%~dp0docker"
|
||||||
|
|
||||||
|
docker compose -p aips --env-file ..\.env up
|
||||||
@@ -8,7 +8,10 @@ using AipsRT.Services.Interfaces;
|
|||||||
using DotNetEnv;
|
using DotNetEnv;
|
||||||
using Microsoft.AspNetCore.SignalR;
|
using Microsoft.AspNetCore.SignalR;
|
||||||
|
|
||||||
|
if (File.Exists("../../.env"))
|
||||||
|
{
|
||||||
Env.Load("../../.env");
|
Env.Load("../../.env");
|
||||||
|
}
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,10 @@ using AipsCore.Infrastructure.Persistence.Db;
|
|||||||
using AipsWebApi.Middleware;
|
using AipsWebApi.Middleware;
|
||||||
using DotNetEnv;
|
using DotNetEnv;
|
||||||
|
|
||||||
|
if (File.Exists("../../.env"))
|
||||||
|
{
|
||||||
Env.Load("../../.env");
|
Env.Load("../../.env");
|
||||||
|
}
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user