Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,9 @@ RUN apt-get update \

COPY --from=build --chown=appuser:appgroup /app/publish .

# Writable directory for SQLite database and local storage
RUN mkdir -p /app/data /app/storage && chown appuser:appgroup /app/data /app/storage

USER appuser
EXPOSE 8080

Expand Down
16 changes: 10 additions & 6 deletions framework/SimpleModule.Hosting/SimpleModuleHostExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using SimpleModule.Blazor;
using SimpleModule.Core.Constants;
using SimpleModule.Core.Events;
Expand Down Expand Up @@ -43,7 +42,7 @@ public static WebApplicationBuilder AddSimpleModuleInfrastructure(
builder.Services.AddSingleton(options);

BridgeAspireConnectionString(builder.Configuration);
ValidateDatabaseConfiguration(builder.Configuration);
options.DatabaseProvider = ValidateDatabaseConfiguration(builder.Configuration);

builder.Services.AddProblemDetails();
builder.Services.AddExceptionHandler<GlobalExceptionHandler>();
Expand Down Expand Up @@ -108,8 +107,11 @@ public static WebApplicationBuilder AddSimpleModuleInfrastructure(
/// </summary>
public static async Task UseSimpleModuleInfrastructure(this WebApplication app)
{
// Database initialization (non-production only)
if (!app.Environment.IsProduction())
// Database initialization
// SQLite (file-based) always needs auto-initialization since the DB file may not exist.
// Managed databases (PostgreSQL, SQL Server) skip this in production — apply migrations externally.
var smOptions = app.Services.GetRequiredService<SimpleModuleOptions>();
if (!app.Environment.IsProduction() || smOptions.DatabaseProvider == DatabaseProvider.Sqlite)
{
using var scope = app.Services.CreateScope();
var infos = scope.ServiceProvider.GetServices<ModuleDbContextInfo>();
Expand Down Expand Up @@ -187,7 +189,9 @@ private static void BridgeAspireConnectionString(ConfigurationManager configurat
}
}

private static void ValidateDatabaseConfiguration(ConfigurationManager configuration)
private static DatabaseProvider ValidateDatabaseConfiguration(
ConfigurationManager configuration
)
{
var dbOptions =
configuration.GetSection(DatabaseConstants.SectionName).Get<DatabaseOptions>()
Expand All @@ -202,7 +206,7 @@ private static void ValidateDatabaseConfiguration(ConfigurationManager configura
);
}

_ = DatabaseProviderDetector.Detect(connString, dbOptions.Provider);
return DatabaseProviderDetector.Detect(connString, dbOptions.Provider);
}

private static void UseStaticFileCaching(WebApplication app)
Expand Down
6 changes: 6 additions & 0 deletions framework/SimpleModule.Hosting/SimpleModuleOptions.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Microsoft.Extensions.DependencyInjection;
using SimpleModule.Core;
using SimpleModule.Database;

namespace SimpleModule.Hosting;

Expand All @@ -15,6 +16,11 @@ public class SimpleModuleOptions

public bool EnableDevTools { get; set; } = true;

/// <summary>
/// The detected database provider, set during startup validation.
/// </summary>
internal DatabaseProvider DatabaseProvider { get; set; }

/// <summary>
/// Configures options for a module. Called by generated Configure{Module}() extension methods.
/// </summary>
Expand Down
11 changes: 11 additions & 0 deletions template/SimpleModule.Host/appsettings.Production.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"Database": {
"DefaultConnection": "Data Source=/app/data/app.db",
"Provider": "Sqlite"
},
"Storage": {
"Local": {
"BasePath": "/app/storage"
}
}
}
28 changes: 21 additions & 7 deletions tools/build-orchestrator.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,34 @@ function runBuild(workspacePath, workspaceName) {
});
}

async function runWithConcurrency(items, concurrency, fn) {
const results = [];
let index = 0;

async function worker() {
while (index < items.length) {
const i = index++;
results[i] = await fn(items[i]);
}
}

await Promise.all(Array.from({ length: Math.min(concurrency, items.length) }, () => worker()));
return results;
}

async function main() {
try {
log('startup', `Starting production build (VITE_MODE=${mode})...`);

const workspaces = discoverBuildableWorkspaces(rootDir);
log('startup', `Found ${workspaces.length} workspaces to build`);

// Build all workspaces in parallel for better performance
await Promise.all(
workspaces.map((workspace) => {
const workspaceName = path.basename(workspace);
return runBuild(workspace, workspaceName);
})
);
// Limit concurrency to avoid OOM in memory-constrained environments (e.g. Docker)
const concurrency = parseInt(process.env.BUILD_CONCURRENCY, 10) || 4;
await runWithConcurrency(workspaces, concurrency, (workspace) => {
const workspaceName = path.basename(workspace);
return runBuild(workspace, workspaceName);
});

log('complete', 'All workspaces built successfully!');
process.exit(0);
Expand Down
Loading