Added docker-compose. Updated startups for API and Web layer.
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -361,3 +361,6 @@ MigrationBackup/
|
|||||||
|
|
||||||
# Fody - auto-generated XML schema
|
# Fody - auto-generated XML schema
|
||||||
FodyWeavers.xsd
|
FodyWeavers.xsd
|
||||||
|
|
||||||
|
# Environment
|
||||||
|
.env
|
||||||
@@ -2,13 +2,16 @@ using JSMR.Api.Startup;
|
|||||||
|
|
||||||
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
|
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
|
ConfigurationManager configuration = builder.Configuration;
|
||||||
|
IWebHostEnvironment environment = builder.Environment;
|
||||||
|
|
||||||
builder.Services
|
builder.Services
|
||||||
.AddAppServices(builder)
|
.AddAppServices(configuration)
|
||||||
.AddAppJson()
|
.AddAppJson()
|
||||||
.AddAppOpenApi()
|
.AddAppOpenApi()
|
||||||
.AddAppAuthentication()
|
.AddAppAuthentication(environment)
|
||||||
.AddAppCors(builder)
|
.AddAppCors(configuration);
|
||||||
.AddAppLogging(builder);
|
//.AddAppLogging(builder);
|
||||||
|
|
||||||
builder.Host.UseAppSerilog();
|
builder.Host.UseAppSerilog();
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,31 @@
|
|||||||
using Serilog;
|
using Serilog;
|
||||||
|
using Serilog.Events;
|
||||||
|
|
||||||
namespace JSMR.Api.Startup;
|
namespace JSMR.Api.Startup;
|
||||||
|
|
||||||
public static class HostBuilderExtensions
|
public static class HostBuilderExtensions
|
||||||
{
|
{
|
||||||
public static IHostBuilder UseAppSerilog(this IHostBuilder host)
|
public static IHostBuilder UseAppSerilog(this IHostBuilder host)
|
||||||
=> host.UseSerilog();
|
{
|
||||||
|
return host.UseSerilog((context, services, loggerConfiguration) =>
|
||||||
|
{
|
||||||
|
IConfiguration configuration = context.Configuration;
|
||||||
|
IHostEnvironment environment = context.HostingEnvironment;
|
||||||
|
|
||||||
|
loggerConfiguration
|
||||||
|
.ReadFrom.Configuration(configuration)
|
||||||
|
.ReadFrom.Services(services)
|
||||||
|
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
|
||||||
|
.Enrich.WithProperty("Service", "JSMR.Api")
|
||||||
|
.Enrich.WithProperty("Environment", environment.EnvironmentName);
|
||||||
|
|
||||||
|
// Conditionally add Seq if configured correctly
|
||||||
|
string? seqUrl = configuration["Seq:ServerUrl"];
|
||||||
|
|
||||||
|
if (Uri.TryCreate(seqUrl, UriKind.Absolute, out _))
|
||||||
|
{
|
||||||
|
loggerConfiguration.WriteTo.Seq(seqUrl);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -4,8 +4,6 @@ using JSMR.Infrastructure.DI;
|
|||||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||||
using Microsoft.AspNetCore.Http.Json;
|
using Microsoft.AspNetCore.Http.Json;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Serilog;
|
|
||||||
using Serilog.Events;
|
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
@@ -13,14 +11,14 @@ namespace JSMR.Api.Startup;
|
|||||||
|
|
||||||
public static class ServiceCollectionExtensions
|
public static class ServiceCollectionExtensions
|
||||||
{
|
{
|
||||||
public static IServiceCollection AddAppServices(this IServiceCollection services, IHostApplicationBuilder builder)
|
public static IServiceCollection AddAppServices(this IServiceCollection services, IConfigurationManager configuration)
|
||||||
{
|
{
|
||||||
services
|
services
|
||||||
.AddMemoryCache()
|
.AddMemoryCache()
|
||||||
.AddApplication()
|
.AddApplication()
|
||||||
.AddInfrastructure();
|
.AddInfrastructure();
|
||||||
|
|
||||||
string connectionString = builder.Configuration.GetConnectionString("AppDb")
|
string connectionString = configuration.GetConnectionString("AppDb")
|
||||||
?? throw new InvalidOperationException("Missing ConnectionStrings:AppDb");
|
?? throw new InvalidOperationException("Missing ConnectionStrings:AppDb");
|
||||||
|
|
||||||
services.AddDbContextFactory<AppDbContext>(opt =>
|
services.AddDbContextFactory<AppDbContext>(opt =>
|
||||||
@@ -51,7 +49,7 @@ public static class ServiceCollectionExtensions
|
|||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IServiceCollection AddAppAuthentication(this IServiceCollection services)
|
public static IServiceCollection AddAppAuthentication(this IServiceCollection services, IHostEnvironment environment)
|
||||||
{
|
{
|
||||||
services.AddAuthorization();
|
services.AddAuthorization();
|
||||||
|
|
||||||
@@ -61,8 +59,15 @@ public static class ServiceCollectionExtensions
|
|||||||
{
|
{
|
||||||
options.Cookie.Name = "vw_auth";
|
options.Cookie.Name = "vw_auth";
|
||||||
options.Cookie.HttpOnly = true;
|
options.Cookie.HttpOnly = true;
|
||||||
options.Cookie.SameSite = SameSiteMode.None;
|
//options.Cookie.SameSite = SameSiteMode.None;
|
||||||
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
|
//options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
|
||||||
|
|
||||||
|
options.Cookie.SameSite = SameSiteMode.Lax;
|
||||||
|
|
||||||
|
options.Cookie.SecurePolicy =
|
||||||
|
environment.IsDevelopment()
|
||||||
|
? CookieSecurePolicy.SameAsRequest
|
||||||
|
: CookieSecurePolicy.Always;
|
||||||
|
|
||||||
options.Events = new CookieAuthenticationEvents
|
options.Events = new CookieAuthenticationEvents
|
||||||
{
|
{
|
||||||
@@ -82,37 +87,44 @@ public static class ServiceCollectionExtensions
|
|||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IServiceCollection AddAppCors(this IServiceCollection services, IHostApplicationBuilder builder)
|
public static IServiceCollection AddAppCors(this IServiceCollection services, IConfigurationManager configuration)
|
||||||
{
|
{
|
||||||
// Prefer config-based origins so you stop editing code for ports.
|
string[] origins = configuration.GetSection("Cors:AllowedOrigins").Get<string[]>() ?? [];
|
||||||
// appsettings.Development.json:
|
|
||||||
// "Cors": { "AllowedOrigins": [ "https://localhost:7112", ... ] }
|
|
||||||
string[] origins = builder.Configuration.GetSection("Cors:AllowedOrigins").Get<string[]>() ?? [];
|
|
||||||
|
|
||||||
services.AddCors(options =>
|
services.AddCors(options =>
|
||||||
{
|
{
|
||||||
options.AddPolicy("ui", policyBuilder =>
|
options.AddPolicy("ui", policyBuilder =>
|
||||||
|
{
|
||||||
|
if (origins.Length == 0)
|
||||||
|
{
|
||||||
|
// In container/prod you often don't need CORS at all (same-origin),
|
||||||
|
// but if it *is* needed and not configured, fail closed rather than crash.
|
||||||
|
// Do not call WithOrigins().
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
policyBuilder.WithOrigins(origins)
|
policyBuilder.WithOrigins(origins)
|
||||||
.AllowAnyHeader()
|
.AllowAnyHeader()
|
||||||
.AllowAnyMethod()
|
.AllowAnyMethod()
|
||||||
.AllowCredentials());
|
.AllowCredentials();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IServiceCollection AddAppLogging(this IServiceCollection services, IHostApplicationBuilder builder)
|
//public static IServiceCollection AddAppLogging(this IServiceCollection services, IHostApplicationBuilder builder)
|
||||||
{
|
//{
|
||||||
var config = builder.Configuration;
|
// var config = builder.Configuration;
|
||||||
var env = builder.Environment;
|
// var env = builder.Environment;
|
||||||
|
|
||||||
Log.Logger = new LoggerConfiguration()
|
// Log.Logger = new LoggerConfiguration()
|
||||||
.ReadFrom.Configuration(config)
|
// .ReadFrom.Configuration(config)
|
||||||
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
|
// .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
|
||||||
.Enrich.WithProperty("Service", "JSMR.Api")
|
// .Enrich.WithProperty("Service", "JSMR.Api")
|
||||||
.Enrich.WithProperty("Environment", env.EnvironmentName)
|
// .Enrich.WithProperty("Environment", env.EnvironmentName)
|
||||||
.CreateLogger();
|
// .CreateLogger();
|
||||||
|
|
||||||
return services;
|
// return services;
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
@@ -15,12 +15,18 @@ public static class WebApplicationExtensions
|
|||||||
{
|
{
|
||||||
public static WebApplication UseAppPipeline(this WebApplication app, IHostEnvironment env)
|
public static WebApplication UseAppPipeline(this WebApplication app, IHostEnvironment env)
|
||||||
{
|
{
|
||||||
|
string[] origins = app.Configuration.GetSection("Cors:AllowedOrigins").Get<string[]>() ?? [];
|
||||||
|
|
||||||
|
if (origins.Length > 0)
|
||||||
app.UseCors("ui");
|
app.UseCors("ui");
|
||||||
|
|
||||||
if (env.IsDevelopment())
|
if (env.IsDevelopment())
|
||||||
app.MapOpenApi();
|
app.MapOpenApi();
|
||||||
|
|
||||||
|
if (!env.IsDevelopment())
|
||||||
|
{
|
||||||
app.UseHttpsRedirection();
|
app.UseHttpsRedirection();
|
||||||
|
}
|
||||||
|
|
||||||
app.UseAuthentication();
|
app.UseAuthentication();
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
|
|||||||
@@ -25,11 +25,10 @@
|
|||||||
},
|
},
|
||||||
"Enrich": [ "FromLogContext", "WithMachineName", "WithProcessId", "WithThreadId" ],
|
"Enrich": [ "FromLogContext", "WithMachineName", "WithProcessId", "WithThreadId" ],
|
||||||
"WriteTo": [
|
"WriteTo": [
|
||||||
{ "Name": "Console" },
|
{ "Name": "Console" }
|
||||||
{
|
|
||||||
"Name": "Seq",
|
|
||||||
"Args": { "serverUrl": "%SEQ_URL%" }
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"Seq": {
|
||||||
|
"ServerUrl": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
8
JSMR.UI.Blazor/Dockerfile
Normal file
8
JSMR.UI.Blazor/Dockerfile
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
|
||||||
|
WORKDIR /app
|
||||||
|
COPY . .
|
||||||
|
RUN dotnet publish -c Release -o out
|
||||||
|
|
||||||
|
FROM nginx:alpine
|
||||||
|
COPY --from=build /app/out/wwwroot /usr/share/nginx/html
|
||||||
|
COPY JSMR.UI.Blazor/nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
@@ -10,9 +10,14 @@ var builder = WebAssemblyHostBuilder.CreateDefault(args);
|
|||||||
builder.RootComponents.Add<App>("#app");
|
builder.RootComponents.Add<App>("#app");
|
||||||
builder.RootComponents.Add<HeadOutlet>("head::after");
|
builder.RootComponents.Add<HeadOutlet>("head::after");
|
||||||
|
|
||||||
string apiBase = builder.Configuration["ApiBaseUrl"] ?? builder.HostEnvironment.BaseAddress;
|
//string apiBase = builder.Configuration["ApiBaseUrl"] ?? builder.HostEnvironment.BaseAddress;
|
||||||
|
//Console.WriteLine(apiBase);
|
||||||
|
|
||||||
Console.WriteLine(apiBase);
|
// If ApiBaseUrl is set (VS dev), use it. Otherwise (docker/prod), use same-origin.
|
||||||
|
var apiBase = builder.Configuration["ApiBaseUrl"];
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(apiBase))
|
||||||
|
apiBase = builder.HostEnvironment.BaseAddress;
|
||||||
|
|
||||||
// Old way
|
// Old way
|
||||||
//builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(apiBase) });
|
//builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(apiBase) });
|
||||||
|
|||||||
32
JSMR.UI.Blazor/nginx.conf
Normal file
32
JSMR.UI.Blazor/nginx.conf
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
# Blazor WASM: serve static files, and fallback to index.html for client-side routes
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Proxy API calls to the api service (docker-compose DNS name: api)
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://api:8080;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
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 auth endpoints too (yours are /auth/login, /auth/logout)
|
||||||
|
location /auth/ {
|
||||||
|
proxy_pass http://api:8080;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
3
JSMR.UI.Blazor/wwwroot/appsettings.Development.json
Normal file
3
JSMR.UI.Blazor/wwwroot/appsettings.Development.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"ApiBaseUrl": "https://localhost:7277"
|
||||||
|
}
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"ApiBaseUrl": "https://localhost:7277"
|
"ApiBaseUrl": ""
|
||||||
}
|
}
|
||||||
5
JSMR.sln
5
JSMR.sln
@@ -17,6 +17,11 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JSMR.UI.Blazor", "JSMR.UI.B
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JSMR.Worker", "JSMR.Worker\JSMR.Worker.csproj", "{964BD375-FAE3-4044-A09B-5C43919C9B52}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JSMR.Worker", "JSMR.Worker\JSMR.Worker.csproj", "{964BD375-FAE3-4044-A09B-5C43919C9B52}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F3C045AD-3861-4079-85F0-EDEEE83765B7}"
|
||||||
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
docker-compose.yml = docker-compose.yml
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
|||||||
27
docker-compose.yml
Normal file
27
docker-compose.yml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
services:
|
||||||
|
api:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: JSMR.Api/Dockerfile
|
||||||
|
ports:
|
||||||
|
- "5000:8080"
|
||||||
|
environment:
|
||||||
|
ASPNETCORE_ENVIRONMENT: ${ASPNETCORE_ENVIRONMENT:-Production}
|
||||||
|
ConnectionStrings__AppDb: ${APPDB_CONN}
|
||||||
|
Seq__ServerUrl: ${SEQ_URL:-}
|
||||||
|
networks:
|
||||||
|
- app-net
|
||||||
|
|
||||||
|
web:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: JSMR.UI.Blazor/Dockerfile
|
||||||
|
ports:
|
||||||
|
- "5001:80"
|
||||||
|
depends_on:
|
||||||
|
- api
|
||||||
|
networks:
|
||||||
|
- app-net
|
||||||
|
|
||||||
|
networks:
|
||||||
|
app-net: {}
|
||||||
Reference in New Issue
Block a user