128 lines
4.2 KiB
C#
128 lines
4.2 KiB
C#
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
|
using NoticeBoard.Data;
|
|
using NoticeBoard.Infrastructure;
|
|
|
|
var builder = WebApplication.CreateBuilder(args);
|
|
|
|
// Configure the shared "now" used for Meow expiry checks. Blank ("" or unset) keeps the
|
|
// original behaviour (host machine's local time zone, e.g. IIS on a Windows Server set to
|
|
// AEST). Docker's compose file sets this to "UTC" by default — see docker-compose.yml.
|
|
Clock.Configure(builder.Configuration["TimeZone"]);
|
|
|
|
// SQLite database. Defaults to the app's own directory (unchanged behaviour for IIS).
|
|
// Set the "DataDirectory" setting (env var DataDirectory=... in Docker) to store the
|
|
// database elsewhere — used by the Docker image so the DB lives on a mounted volume.
|
|
var dataDir = builder.Configuration["DataDirectory"] ?? builder.Environment.ContentRootPath;
|
|
Directory.CreateDirectory(dataDir);
|
|
var dbPath = Path.Combine(dataDir, "noticeboard.db");
|
|
builder.Services.AddDbContext<AppDbContext>(options =>
|
|
options.UseSqlite($"Data Source={dbPath}"));
|
|
|
|
builder.Services.AddControllersWithViews();
|
|
builder.Services.AddHttpClient();
|
|
|
|
// Cookie authentication for admin panel
|
|
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
|
|
.AddCookie(options =>
|
|
{
|
|
options.LoginPath = "/account/login";
|
|
options.LogoutPath = "/account/logout";
|
|
options.ExpireTimeSpan = TimeSpan.FromHours(12);
|
|
options.SlidingExpiration = true;
|
|
});
|
|
|
|
var app = builder.Build();
|
|
|
|
// Auto-create database on startup
|
|
using (var scope = app.Services.CreateScope())
|
|
{
|
|
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
|
db.Database.EnsureCreated();
|
|
|
|
// This project doesn't use formal EF Core migrations — EnsureCreated only builds a
|
|
// brand-new database from the current model. For an existing Slides table (from before
|
|
// the ExpiresAt column existed), add it here so upgrades don't require a manual step.
|
|
var connection = db.Database.GetDbConnection();
|
|
await connection.OpenAsync();
|
|
var hasExpiresAt = false;
|
|
await using (var checkCmd = connection.CreateCommand())
|
|
{
|
|
checkCmd.CommandText = "PRAGMA table_info(Slides);";
|
|
await using var reader = await checkCmd.ExecuteReaderAsync();
|
|
while (await reader.ReadAsync())
|
|
{
|
|
if (string.Equals(reader["name"]?.ToString(), "ExpiresAt", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
hasExpiresAt = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!hasExpiresAt)
|
|
{
|
|
await using var alterCmd = connection.CreateCommand();
|
|
alterCmd.CommandText = "ALTER TABLE Slides ADD COLUMN ExpiresAt TEXT NULL;";
|
|
await alterCmd.ExecuteNonQueryAsync();
|
|
}
|
|
}
|
|
|
|
if (!app.Environment.IsDevelopment())
|
|
{
|
|
app.UseExceptionHandler("/error");
|
|
app.UseHsts();
|
|
}
|
|
|
|
app.UseStaticFiles();
|
|
app.UseRouting();
|
|
app.UseAuthentication();
|
|
app.UseAuthorization();
|
|
|
|
// Ensure uploads directory exists
|
|
var uploadsPath = Path.Combine(app.Environment.WebRootPath, "uploads");
|
|
if (!Directory.Exists(uploadsPath))
|
|
Directory.CreateDirectory(uploadsPath);
|
|
|
|
app.MapControllerRoute(
|
|
name: "account",
|
|
pattern: "account/{action=Login}",
|
|
defaults: new { controller = "Account" });
|
|
|
|
app.MapControllerRoute(
|
|
name: "admin",
|
|
pattern: "admin/{action=Index}/{id?}",
|
|
defaults: new { controller = "Admin" });
|
|
|
|
app.MapControllerRoute(
|
|
name: "slides",
|
|
pattern: "admin/slides/{action=Index}/{id?}",
|
|
defaults: new { controller = "Slides" });
|
|
|
|
app.MapControllerRoute(
|
|
name: "devices",
|
|
pattern: "admin/devices/{action=Index}/{id?}",
|
|
defaults: new { controller = "Devices" });
|
|
|
|
app.MapControllerRoute(
|
|
name: "api",
|
|
pattern: "api/{action}/{id?}",
|
|
defaults: new { controller = "Api" });
|
|
|
|
app.MapControllerRoute(
|
|
name: "display",
|
|
pattern: "d/{slug}",
|
|
defaults: new { controller = "Display", action = "Show" });
|
|
|
|
app.MapControllerRoute(
|
|
name: "display-root",
|
|
pattern: "{slug}",
|
|
defaults: new { controller = "Display", action = "Show" },
|
|
constraints: new { slug = new NoticeBoard.Routing.DeviceSlugConstraint() });
|
|
|
|
app.MapControllerRoute(
|
|
name: "default",
|
|
pattern: "",
|
|
defaults: new { controller = "Admin", action = "Index" });
|
|
|
|
app.Run();
|