This commit is contained in:
@@ -9,6 +9,13 @@
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Views\NewFolder\**" />
|
||||
<Content Remove="Views\NewFolder\**" />
|
||||
<EmbeddedResource Remove="Views\NewFolder\**" />
|
||||
<None Remove="Views\NewFolder\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AngularJS.Core" Version="1.8.2" />
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.67" />
|
||||
@@ -20,6 +27,7 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="6.0.36" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.36" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.36">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
@@ -42,7 +50,6 @@
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Migrations\" />
|
||||
<Folder Include="Views\NewFolder\" />
|
||||
<Folder Include="wwwroot\images\meals\" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
50
Aberwyn/Controllers/SetupApiController.cs
Normal file
50
Aberwyn/Controllers/SetupApiController.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using MySql.Data.MySqlClient;
|
||||
|
||||
namespace Aberwyn.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("api/setup")]
|
||||
public class SetupApiController : ControllerBase
|
||||
{
|
||||
[HttpPost("testdb")]
|
||||
public IActionResult TestDbConnection([FromBody] DbTestRequest request)
|
||||
{
|
||||
try
|
||||
{
|
||||
var baseConnStr = $"server={request.Host};port={request.Port};user={request.User};password={request.Pass};";
|
||||
|
||||
var testDbName = $"testcheck_{Guid.NewGuid():N}".Substring(0, 12); // säker tillfällig databas
|
||||
|
||||
using (var conn = new MySqlConnection(baseConnStr + "database=information_schema;"))
|
||||
{
|
||||
conn.Open();
|
||||
|
||||
// Försök skapa en temporär databas
|
||||
var createCmd = new MySqlCommand($"CREATE DATABASE `{testDbName}`", conn);
|
||||
createCmd.ExecuteNonQuery();
|
||||
|
||||
// Och radera den direkt
|
||||
var dropCmd = new MySqlCommand($"DROP DATABASE `{testDbName}`", conn);
|
||||
dropCmd.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
return Ok(new { success = true, message = "Anslutning OK och CREATE DATABASE tillåten." });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Ok(new { success = false, message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class DbTestRequest
|
||||
{
|
||||
public string Host { get; set; }
|
||||
public string Port { get; set; }
|
||||
public string Db { get; set; }
|
||||
public string User { get; set; }
|
||||
public string Pass { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
151
Aberwyn/Controllers/SetupController.cs
Normal file
151
Aberwyn/Controllers/SetupController.cs
Normal file
@@ -0,0 +1,151 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace Aberwyn.Controllers
|
||||
{
|
||||
using System.Text.Json;
|
||||
using System.IO;
|
||||
using Aberwyn.Models;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using MySql.Data.MySqlClient;
|
||||
using Aberwyn.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
[Route("setup")]
|
||||
public class SetupController : Controller
|
||||
{
|
||||
private readonly IWebHostEnvironment _env;
|
||||
|
||||
public override void OnActionExecuting(ActionExecutingContext context)
|
||||
{
|
||||
ViewBag.IsSetupMode = true;
|
||||
base.OnActionExecuting(context);
|
||||
}
|
||||
public SetupController(IWebHostEnvironment env)
|
||||
{
|
||||
_env = env;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public IActionResult Index()
|
||||
{
|
||||
return View(new SetupSettings());
|
||||
}
|
||||
|
||||
[Authorize(Roles = "Admin")]
|
||||
[HttpPost("reset")]
|
||||
public IActionResult Reset()
|
||||
{
|
||||
var path = Path.Combine(_env.ContentRootPath, "infrastructure", "setup.json");
|
||||
|
||||
var resetSettings = new SetupSettings
|
||||
{
|
||||
IsConfigured = false,
|
||||
DbHost = "",
|
||||
DbPort = 3306,
|
||||
DbName = "",
|
||||
DbUser = "",
|
||||
DbPassword = "",
|
||||
AdminUsername = "admin",
|
||||
AdminEmail = "admin@localhost",
|
||||
AdminPassword = "Admin123!"
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(resetSettings, new JsonSerializerOptions { WriteIndented = true });
|
||||
System.IO.File.WriteAllText(path, json);
|
||||
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
|
||||
[HttpPost("")]
|
||||
public async Task<IActionResult> Setup([FromBody] SetupSettings model)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
var allErrors = ModelState
|
||||
.Where(e => e.Value.Errors.Count > 0)
|
||||
.Select(e => new { Field = e.Key, Errors = e.Value.Errors.Select(x => x.ErrorMessage) });
|
||||
|
||||
return BadRequest(new { error = "Modellen är ogiltig", details = allErrors });
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Skapa databasen om den inte finns
|
||||
var baseConnStr = $"server={model.DbHost};port={model.DbPort};user={model.DbUser};password={model.DbPassword};";
|
||||
using (var conn = new MySqlConnection(baseConnStr + "database=information_schema;"))
|
||||
{
|
||||
conn.Open();
|
||||
var cmd = new MySqlCommand("SELECT SCHEMA_NAME FROM SCHEMATA WHERE SCHEMA_NAME = @dbName", conn);
|
||||
cmd.Parameters.AddWithValue("@dbName", model.DbName);
|
||||
var exists = cmd.ExecuteScalar();
|
||||
|
||||
if (exists == null)
|
||||
{
|
||||
var createCmd = new MySqlCommand($"CREATE DATABASE `{model.DbName}`", conn);
|
||||
createCmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
|
||||
// Bygg services temporärt för att skapa admin
|
||||
var connectionString = $"server={model.DbHost};port={model.DbPort};database={model.DbName};user={model.DbUser};password={model.DbPassword}";
|
||||
var tempProvider = SetupService.BuildTemporaryServices(connectionString);
|
||||
|
||||
using var scope = tempProvider.CreateScope();
|
||||
|
||||
// Skapa databastabeller via EF
|
||||
var db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
||||
await db.Database.MigrateAsync();
|
||||
|
||||
var userManager = scope.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>();
|
||||
var roleManager = scope.ServiceProvider.GetRequiredService<RoleManager<IdentityRole>>();
|
||||
|
||||
// Skapa roller
|
||||
string[] roles = { "Admin", "Chef", "Budget" };
|
||||
foreach (var role in roles)
|
||||
{
|
||||
if (!await roleManager.RoleExistsAsync(role))
|
||||
await roleManager.CreateAsync(new IdentityRole(role));
|
||||
}
|
||||
|
||||
// Skapa adminanvändare
|
||||
var adminUser = new ApplicationUser
|
||||
{
|
||||
UserName = model.AdminUsername,
|
||||
Email = model.AdminEmail
|
||||
};
|
||||
|
||||
var existingUser = await userManager.FindByNameAsync(model.AdminUsername);
|
||||
if (existingUser == null)
|
||||
{
|
||||
var result = await userManager.CreateAsync(adminUser, model.AdminPassword);
|
||||
if (!result.Succeeded)
|
||||
{
|
||||
return BadRequest(new { error = "Kunde inte skapa administratör", details = result.Errors });
|
||||
}
|
||||
|
||||
await userManager.AddToRoleAsync(adminUser, "Admin");
|
||||
}
|
||||
|
||||
model.IsConfigured = true;
|
||||
|
||||
// Spara inställningarna
|
||||
var json = JsonSerializer.Serialize(model, new JsonSerializerOptions { WriteIndented = true });
|
||||
var filePath = Path.Combine(_env.ContentRootPath, "infrastructure", "setup.json");
|
||||
System.IO.File.WriteAllText(filePath, json);
|
||||
|
||||
return Ok(new { message = "Installation slutförd!" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(new { error = "Fel vid installation", details = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
public IActionResult SetupComplete() => View();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Aberwyn.Models;
|
||||
|
||||
@@ -6,10 +7,16 @@ namespace Aberwyn.Data
|
||||
{
|
||||
public static class IdentityDataInitializer
|
||||
{
|
||||
public static async Task SeedData(IServiceProvider serviceProvider)
|
||||
public static async Task<IdentityResult> SeedData(IServiceProvider serviceProvider, SetupSettings? setup = null)
|
||||
{
|
||||
var userManager = serviceProvider.GetRequiredService<UserManager<ApplicationUser>>();
|
||||
var roleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole>>();
|
||||
var config = serviceProvider.GetService<IConfiguration>();
|
||||
|
||||
if (setup == null && config != null)
|
||||
{
|
||||
setup = config.GetSection("SetupSettings").Get<SetupSettings>() ?? new SetupSettings();
|
||||
}
|
||||
|
||||
string[] roles = { "Admin", "Chef", "Budget" };
|
||||
|
||||
@@ -19,26 +26,38 @@ namespace Aberwyn.Data
|
||||
await roleManager.CreateAsync(new IdentityRole(role));
|
||||
}
|
||||
|
||||
string adminUsername = "admin";
|
||||
string adminEmail = "admin@localhost";
|
||||
string password = "Admin123!";
|
||||
|
||||
if (await userManager.FindByEmailAsync(adminEmail) == null)
|
||||
var existingUser = await userManager.FindByEmailAsync(setup.AdminEmail);
|
||||
if (existingUser == null)
|
||||
{
|
||||
var user = new ApplicationUser
|
||||
{
|
||||
UserName = adminUsername,
|
||||
Email = adminEmail,
|
||||
UserName = setup.AdminUsername,
|
||||
Email = setup.AdminEmail,
|
||||
EmailConfirmed = true
|
||||
};
|
||||
|
||||
var result = await userManager.CreateAsync(user, password);
|
||||
var result = await userManager.CreateAsync(user, setup.AdminPassword);
|
||||
|
||||
if (result.Succeeded)
|
||||
await userManager.AddToRoleAsync(user, "Admin");
|
||||
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
var token = await userManager.GeneratePasswordResetTokenAsync(existingUser);
|
||||
var result = await userManager.ResetPasswordAsync(existingUser, token, setup.AdminPassword);
|
||||
|
||||
if (result.Succeeded)
|
||||
{
|
||||
await userManager.AddToRoleAsync(user, "Admin");
|
||||
var rolesForUser = await userManager.GetRolesAsync(existingUser);
|
||||
if (!rolesForUser.Contains("Admin"))
|
||||
await userManager.AddToRoleAsync(existingUser, "Admin");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
55
Aberwyn/Data/SetupService.cs
Normal file
55
Aberwyn/Data/SetupService.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using Aberwyn.Models;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace Aberwyn.Data
|
||||
{
|
||||
// SetupService.cs
|
||||
public class SetupService
|
||||
{
|
||||
private readonly IWebHostEnvironment _env;
|
||||
private readonly string _filePath;
|
||||
|
||||
public SetupService(IWebHostEnvironment env)
|
||||
{
|
||||
_env = env;
|
||||
_filePath = Path.Combine(_env.ContentRootPath, "infrastructure", "setup.json");
|
||||
}
|
||||
|
||||
|
||||
public SetupSettings GetSetup()
|
||||
{
|
||||
if (!File.Exists(_filePath))
|
||||
return new SetupSettings { IsConfigured = false };
|
||||
|
||||
var json = File.ReadAllText(_filePath);
|
||||
return JsonSerializer.Deserialize<SetupSettings>(json) ?? new SetupSettings { IsConfigured = false };
|
||||
}
|
||||
|
||||
|
||||
internal static IServiceProvider BuildTemporaryServices(string connectionString)
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
|
||||
// Konfigurera EF + Identity
|
||||
services.AddDbContext<ApplicationDbContext>(options =>
|
||||
options.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString)));
|
||||
|
||||
services.AddIdentity<ApplicationUser, IdentityRole>()
|
||||
.AddEntityFrameworkStores<ApplicationDbContext>()
|
||||
.AddDefaultTokenProviders();
|
||||
|
||||
// Lägg till en tom konfiguration för att undvika null
|
||||
services.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build());
|
||||
|
||||
// Valfritt: Lägg till loggning om något kräver det
|
||||
services.AddLogging();
|
||||
|
||||
return services.BuildServiceProvider();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
11
Aberwyn/Infrastructure/setup.json
Normal file
11
Aberwyn/Infrastructure/setup.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"AdminUsername": "tai",
|
||||
"AdminEmail": "tai@zcz.se",
|
||||
"AdminPassword": "Admin123!",
|
||||
"IsConfigured": true,
|
||||
"DbHost": "localhost",
|
||||
"DbPort": 3306,
|
||||
"DbName": "aberwyn_test",
|
||||
"DbUser": "root",
|
||||
"DbPassword": "rootpass"
|
||||
}
|
||||
17
Aberwyn/Models/SetupSettings.cs
Normal file
17
Aberwyn/Models/SetupSettings.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
namespace Aberwyn.Models
|
||||
{
|
||||
public class SetupSettings
|
||||
{
|
||||
public string AdminUsername { get; set; } = "admin";
|
||||
public string AdminEmail { get; set; } = "admin@localhost";
|
||||
public string AdminPassword { get; set; } = "Admin123!";
|
||||
public bool IsConfigured { get; set; }
|
||||
public string DbHost { get; set; }
|
||||
public int DbPort { get; set; }
|
||||
public string DbName { get; set; }
|
||||
public string DbUser { get; set; }
|
||||
public string DbPassword { get; set; }
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,14 +1,12 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
using Aberwyn.Data;
|
||||
using System.Text;
|
||||
using System.Globalization;
|
||||
using Microsoft.AspNetCore.Localization;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Aberwyn.Models;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||
using System.Text.Json;
|
||||
|
||||
var config = new ConfigurationBuilder()
|
||||
.SetBasePath(Directory.GetCurrentDirectory())
|
||||
@@ -16,7 +14,6 @@ var config = new ConfigurationBuilder()
|
||||
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", optional: true)
|
||||
.AddEnvironmentVariables()
|
||||
.Build();
|
||||
Console.WriteLine("📦 DEBUG: laddad raw-connectionstring: " + config.GetConnectionString("DefaultConnection"));
|
||||
|
||||
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
|
||||
{
|
||||
@@ -24,22 +21,47 @@ var builder = WebApplication.CreateBuilder(new WebApplicationOptions
|
||||
EnvironmentName = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"
|
||||
});
|
||||
builder.Configuration.AddConfiguration(config);
|
||||
Console.WriteLine("🌍 Miljö: " + builder.Environment.EnvironmentName);
|
||||
Console.WriteLine("🔗 Connection string: " + builder.Configuration.GetConnectionString("DefaultConnection"));
|
||||
|
||||
// Läser setup.json
|
||||
var setupFilePath = Path.Combine("infrastructure", "setup.json");
|
||||
SetupSettings setup;
|
||||
try
|
||||
{
|
||||
if (!File.Exists(setupFilePath))
|
||||
throw new Exception("setup.json saknas.");
|
||||
|
||||
using var jsonStream = File.OpenRead(setupFilePath);
|
||||
setup = JsonSerializer.Deserialize<SetupSettings>(jsonStream)!;
|
||||
|
||||
if (setup.IsConfigured)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(setup.DbHost) ||
|
||||
string.IsNullOrWhiteSpace(setup.DbName) ||
|
||||
string.IsNullOrWhiteSpace(setup.DbUser) ||
|
||||
string.IsNullOrWhiteSpace(setup.DbPassword))
|
||||
{
|
||||
throw new Exception("Databasinställningarna är ofullständiga.");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"❌ Fel vid läsning av setup.json: {ex.Message}");
|
||||
setup = new SetupSettings { IsConfigured = false };
|
||||
}
|
||||
|
||||
|
||||
// Add services to the container.
|
||||
// Add services to the container
|
||||
builder.Services.AddControllersWithViews()
|
||||
.AddJsonOptions(opts =>
|
||||
{
|
||||
// Beh<65>ll propertynamn som i C#-klassen (PascalCase)
|
||||
opts.JsonSerializerOptions.PropertyNamingPolicy = null;
|
||||
// Ignorera null-v<>rden vid serialisering
|
||||
opts.JsonSerializerOptions.IgnoreNullValues = true;
|
||||
});
|
||||
|
||||
builder.Services.AddSession(options =>
|
||||
{
|
||||
options.IdleTimeout = TimeSpan.FromHours(1);
|
||||
options.IdleTimeout = TimeSpan.FromDays(30);
|
||||
options.Cookie.HttpOnly = true;
|
||||
options.Cookie.IsEssential = true;
|
||||
});
|
||||
@@ -47,24 +69,32 @@ builder.Services.AddSession(options =>
|
||||
builder.Services.AddRazorPages();
|
||||
builder.Services.AddHttpClient();
|
||||
|
||||
// Configure your DbContext with MySQLs
|
||||
Console.WriteLine("🔗 Connection string: " + builder.Configuration.GetConnectionString("DefaultConnection"));
|
||||
|
||||
builder.Services.AddDbContext<ApplicationDbContext>(options =>
|
||||
options.UseMySql(
|
||||
builder.Configuration.GetConnectionString("DefaultConnection"),
|
||||
new MySqlServerVersion(new Version(8, 0, 36)),
|
||||
mySqlOptions => mySqlOptions.EnableRetryOnFailure()
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
builder.Services.AddIdentity<ApplicationUser, IdentityRole>(options =>
|
||||
// Registrera rätt databas och identity beroende på om setup är klar
|
||||
if (setup.IsConfigured)
|
||||
{
|
||||
options.SignIn.RequireConfirmedAccount = false;
|
||||
})
|
||||
.AddEntityFrameworkStores<ApplicationDbContext>()
|
||||
.AddDefaultTokenProviders();
|
||||
string connectionString = $"Server={setup.DbHost};Port={setup.DbPort};Database={setup.DbName};User={setup.DbUser};Password={setup.DbPassword};";
|
||||
|
||||
builder.Services.AddDbContext<ApplicationDbContext>(options =>
|
||||
options.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString)));
|
||||
|
||||
builder.Services.AddIdentity<ApplicationUser, IdentityRole>(options =>
|
||||
{
|
||||
options.SignIn.RequireConfirmedAccount = false;
|
||||
})
|
||||
.AddEntityFrameworkStores<ApplicationDbContext>()
|
||||
.AddDefaultTokenProviders();
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.Services.AddDbContext<ApplicationDbContext>(options =>
|
||||
options.UseInMemoryDatabase("TempSetup"));
|
||||
|
||||
builder.Services.AddIdentity<ApplicationUser, IdentityRole>()
|
||||
.AddEntityFrameworkStores<ApplicationDbContext>()
|
||||
.AddDefaultTokenProviders();
|
||||
}
|
||||
|
||||
// Identity inställningar
|
||||
builder.Services.Configure<IdentityOptions>(options =>
|
||||
{
|
||||
options.Password.RequireDigit = false;
|
||||
@@ -74,8 +104,7 @@ builder.Services.Configure<IdentityOptions>(options =>
|
||||
options.Password.RequireUppercase = false;
|
||||
});
|
||||
|
||||
builder.Services.AddControllersWithViews();
|
||||
// Register your services
|
||||
// Appens övriga tjänster
|
||||
builder.Services.AddScoped<MenuService>();
|
||||
|
||||
builder.Services.AddSingleton<PushNotificationService>(sp =>
|
||||
@@ -89,11 +118,14 @@ builder.Services.AddSingleton<PushNotificationService>(sp =>
|
||||
});
|
||||
|
||||
builder.Services.Configure<VapidOptions>(builder.Configuration.GetSection("Vapid"));
|
||||
|
||||
builder.Services.ConfigureApplicationCookie(options =>
|
||||
{
|
||||
options.LoginPath = "/Identity/Account/Login"; // korrekt för ditt nuvarande upplägg
|
||||
options.LoginPath = "/Identity/Account/Login";
|
||||
});
|
||||
|
||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||
|
||||
builder.Services.Configure<RequestLocalizationOptions>(options =>
|
||||
{
|
||||
var supportedCultures = new[] { new CultureInfo("sv-SE") };
|
||||
@@ -101,60 +133,70 @@ builder.Services.Configure<RequestLocalizationOptions>(options =>
|
||||
options.SupportedCultures = supportedCultures;
|
||||
options.SupportedUICultures = supportedCultures;
|
||||
});
|
||||
|
||||
//builder.Configuration
|
||||
// .SetBasePath(Directory.GetCurrentDirectory())
|
||||
// .AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true, reloadOnChange: true)
|
||||
// .AddEnvironmentVariables();
|
||||
builder.Services.AddSingleton<PushNotificationService>(sp =>
|
||||
{
|
||||
var config = sp.GetRequiredService<IConfiguration>();
|
||||
return new PushNotificationService(
|
||||
config["VapidKeys:Subject"],
|
||||
config["VapidKeys:PublicKey"],
|
||||
config["VapidKeys:PrivateKey"]
|
||||
);
|
||||
});
|
||||
builder.Services.AddSingleton<SetupService>();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
app.UseStaticFiles();
|
||||
// Middleware: om ej konfigurerad → redirect till /setup
|
||||
app.Use(async (context, next) =>
|
||||
{
|
||||
var setupService = context.RequestServices.GetRequiredService<SetupService>();
|
||||
var currentSetup = setupService.GetSetup();
|
||||
|
||||
var path = context.Request.Path;
|
||||
var method = context.Request.Method;
|
||||
|
||||
if (!currentSetup.IsConfigured &&
|
||||
!path.StartsWithSegments("/setup") &&
|
||||
!(path == "/setup" && method == "POST") && // 👈 tillåt POST till /setup
|
||||
!path.StartsWithSegments("/api/setup"))
|
||||
{
|
||||
context.Response.Redirect("/setup");
|
||||
return;
|
||||
}
|
||||
|
||||
await next();
|
||||
});
|
||||
|
||||
|
||||
|
||||
// Configure the HTTP request pipeline
|
||||
if (!app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseExceptionHandler("/Home/Error");
|
||||
app.UseHsts();
|
||||
}
|
||||
|
||||
app.UseStaticFiles();
|
||||
|
||||
app.UseRouting();
|
||||
app.UseSession();
|
||||
app.UseAuthentication();;
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
|
||||
// Map controller endpoints (including AJAX JSON actions)
|
||||
// Routing
|
||||
app.MapControllers();
|
||||
|
||||
// Conventional MVC route
|
||||
app.MapControllerRoute(
|
||||
name: "default",
|
||||
pattern: "{controller=Home}/{action=Index}/{id?}");
|
||||
|
||||
// Map Razor Pages
|
||||
app.MapRazorPages();
|
||||
using (var scope = app.Services.CreateScope())
|
||||
|
||||
// Init: migrera databas och skapa admin
|
||||
if (setup.IsConfigured)
|
||||
{
|
||||
using var scope = app.Services.CreateScope();
|
||||
var services = scope.ServiceProvider;
|
||||
|
||||
var context = services.GetRequiredService<ApplicationDbContext>();
|
||||
// Vänta tills databasen är redo
|
||||
|
||||
int retries = 10;
|
||||
while (retries > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
context.Database.OpenConnection(); // bara testa öppna anslutningen
|
||||
context.Database.OpenConnection();
|
||||
context.Database.CloseConnection();
|
||||
break; // lyckades!
|
||||
break;
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -164,17 +206,17 @@ using (var scope = app.Services.CreateScope())
|
||||
}
|
||||
}
|
||||
|
||||
// Kör alla EF-migrationer automatiskt
|
||||
context.Database.Migrate();
|
||||
|
||||
// Skapa standardroller och admin
|
||||
await IdentityDataInitializer.SeedData(services);
|
||||
var userManager = services.GetRequiredService<UserManager<ApplicationUser>>();
|
||||
var anyUsers = await userManager.Users.AnyAsync();
|
||||
|
||||
// (valfritt) Lägg in exempelbudgetdata
|
||||
// await TestDataSeeder.SeedBudget(context);
|
||||
if (!anyUsers)
|
||||
{
|
||||
Console.WriteLine("🧩 Ingen användare hittades – skapar admin...");
|
||||
await IdentityDataInitializer.SeedData(services, setup);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
app.Run();
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"Aberwyn (Dev)": {
|
||||
"Aberwyn": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
@@ -16,14 +16,6 @@
|
||||
},
|
||||
"applicationUrl": "https://localhost:7290;http://localhost:5290"
|
||||
},
|
||||
"Aberwyn (Prod)": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Production"
|
||||
},
|
||||
"applicationUrl": "https://localhost:7290;http://localhost:5290"
|
||||
},
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
|
||||
395
Aberwyn/Views/Setup/Index.cshtml
Normal file
395
Aberwyn/Views/Setup/Index.cshtml
Normal file
@@ -0,0 +1,395 @@
|
||||
@model Aberwyn.Models.SetupSettings
|
||||
@{
|
||||
ViewData["Title"] = "Installera Aberwyn";
|
||||
}
|
||||
|
||||
<h1>Installera Aberwyn</h1>
|
||||
|
||||
@if (ViewBag.Error != null)
|
||||
{
|
||||
<div class="alert alert-danger">@ViewBag.Error</div>
|
||||
}
|
||||
|
||||
<style>
|
||||
.setup-step {
|
||||
margin-bottom: 2rem;
|
||||
padding: 1.5rem;
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 6px rgba(0,0,0,0.05);
|
||||
}
|
||||
.form-group {
|
||||
margin-bottom: 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
label {
|
||||
font-weight: bold;
|
||||
margin-bottom: 0.2rem;
|
||||
}
|
||||
input.form-control {
|
||||
padding: 0.5rem;
|
||||
font-size: 1rem;
|
||||
border-radius: 6px;
|
||||
}
|
||||
.is-invalid {
|
||||
border-color: #dc3545;
|
||||
}
|
||||
.invalid-feedback {
|
||||
color: #dc3545;
|
||||
font-size: 0.875rem;
|
||||
display: none;
|
||||
}
|
||||
.is-invalid + .invalid-feedback {
|
||||
display: block;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 10px 20px;
|
||||
font-size: 1rem;
|
||||
border-radius: 6px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s ease;
|
||||
margin-top: 0.5rem;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
.btn-primary { background-color: #6a0dad; color: white; }
|
||||
.btn-primary:hover { background-color: #5a0cab; }
|
||||
|
||||
.btn-secondary { background-color: #adb5bd; color: black; }
|
||||
.btn-secondary:hover { background-color: #9ca3af; }
|
||||
|
||||
.btn-success { background-color: #198754; color: white; }
|
||||
.btn-success:hover { background-color: #157347; }
|
||||
|
||||
.btn-danger { background-color: #dc3545; color: white; }
|
||||
.btn-danger:hover { background-color: #bb2d3b; }
|
||||
|
||||
.btn-outline-info {
|
||||
border: 1px solid #0dcaf0;
|
||||
color: #0dcaf0;
|
||||
background: none;
|
||||
}
|
||||
.btn-outline-info:hover {
|
||||
background-color: #0dcaf0;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#setup-summary ul {
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
#setup-summary li {
|
||||
margin-bottom: 0.4rem;
|
||||
}
|
||||
.spinner {
|
||||
display: inline-block;
|
||||
width: 1.2rem;
|
||||
height: 1.2rem;
|
||||
border: 3px solid rgba(0,0,0,0.2);
|
||||
border-top-color: #6a0dad;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
@@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
</style>
|
||||
|
||||
<form method="post" id="setup-form">
|
||||
<div id="step-1" class="setup-step">
|
||||
<h2>Steg 1: Databasinställningar</h2>
|
||||
<div class="form-group">
|
||||
<label for="DbHost">Server</label>
|
||||
<input asp-for="DbHost" name="DbHost" class="form-control" id="DbHost" required />
|
||||
<div class="invalid-feedback">Fältet är obligatoriskt</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="DbPort">Port</label>
|
||||
<input asp-for="DbPort" name="DbPort" class="form-control" id="DbPort" value="3306" required />
|
||||
<div class="invalid-feedback">Fältet är obligatoriskt</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="DbName">Databasnamn</label>
|
||||
<input asp-for="DbName" name="DbName" class="form-control" id="DbName" required />
|
||||
<div class="invalid-feedback">Fältet är obligatoriskt</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="DbUser">Användarnamn</label>
|
||||
<input asp-for="DbUser" name="DbUser" class="form-control" id="DbUser" required />
|
||||
<div class="invalid-feedback">Fältet är obligatoriskt</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="DbPassword">Lösenord</label>
|
||||
<input asp-for="DbPassword" name="DbPassword" type="password" class="form-control" id="DbPassword" required />
|
||||
<div class="invalid-feedback">Fältet är obligatoriskt</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-outline-info" onclick="testDbConnection()">Testa anslutning</button>
|
||||
<div id="db-test-result" class="mt-2"></div>
|
||||
<button type="button" class="btn btn-primary" onclick="validateStep(1) && goToStep(2)">Nästa</button>
|
||||
</div>
|
||||
|
||||
<div id="step-2" class="setup-step" style="display: none;">
|
||||
<h2>Steg 2: Administratör</h2>
|
||||
<div class="form-group">
|
||||
<label for="AdminUsername">Admin Användarnamn</label>
|
||||
<input asp-for="AdminUsername" name="AdminUsername" class="form-control" id="AdminUsername" required />
|
||||
<div class="invalid-feedback">Fältet är obligatoriskt</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="AdminEmail">E-postadress</label>
|
||||
<input asp-for="AdminEmail" name="AdminEmail" type="email" class="form-control" id="AdminEmail" required />
|
||||
<div class="invalid-feedback">Fältet är obligatoriskt</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="AdminPassword">Lösenord</label>
|
||||
<input asp-for="AdminPassword" name="AdminPassword" type="password" class="form-control"
|
||||
id="AdminPassword"
|
||||
pattern="(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^a-zA-Z\d]).{6,}"
|
||||
title="Minst 6 tecken, inklusive versal, gemen, siffra och specialtecken"
|
||||
required />
|
||||
<div class="invalid-feedback">Ange ett giltigt lösenord enligt kraven</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="ConfirmPassword">Bekräfta lösenord</label>
|
||||
<input type="password" id="ConfirmPassword" class="form-control" required />
|
||||
<span id="password-match-status" style="margin-top: 4px; font-size: 1.2rem;"></span>
|
||||
<div class="invalid-feedback" id="confirmPasswordError">Lösenorden matchar inte</div>
|
||||
</div>
|
||||
|
||||
|
||||
<ul id="password-criteria" style="list-style: none; padding-left: 0; font-size: 0.9rem; margin-top: 0.5rem;">
|
||||
<li id="length-criteria">❌ Minst 6 tecken</li>
|
||||
<li id="uppercase-criteria">❌ Minst en stor bokstav (A–Z)</li>
|
||||
<li id="lowercase-criteria">❌ Minst en liten bokstav (a–z)</li>
|
||||
<li id="digit-criteria">❌ Minst en siffra (0–9)</li>
|
||||
<li id="special-criteria">❌ Minst ett specialtecken (!#&...)</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<button type="button" class="btn btn-secondary" onclick="goToStep(1)">Tillbaka</button>
|
||||
<button type="button" class="btn btn-primary" onclick="validateAdminStep() && goToStep(3)">Nästa</button>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="step-3" class="setup-step" style="display: none;">
|
||||
<h2>Steg 3: Konfiguration pågår...</h2>
|
||||
<div id="setup-summary">
|
||||
<ul>
|
||||
<li><strong>Server:</strong> <span id="summary-DbHost"></span></li>
|
||||
<li><strong>Port:</strong> <span id="summary-DbPort"></span></li>
|
||||
<li><strong>Databas:</strong> <span id="summary-DbName"></span></li>
|
||||
<li><strong>Användare:</strong> <span id="summary-DbUser"></span></li>
|
||||
<li><strong>Adminnamn:</strong> <span id="summary-AdminUsername"></span></li>
|
||||
<li><strong>Admin-e-post:</strong> <span id="summary-AdminEmail"></span></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="setup-progress" class="mt-3">
|
||||
<p>
|
||||
Skapar databastabeller och konfigurerar administratörskonto...
|
||||
<span class="spinner"></span>
|
||||
</p>
|
||||
</div>
|
||||
<button type="button" class="btn btn-secondary" onclick="goToStep(2)">Tillbaka</button>
|
||||
<button type="button" class="btn btn-success" onclick="submitSetup()">Slutför installation</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
@if (User.IsInRole("Admin"))
|
||||
{
|
||||
<form method="post" asp-action="Reset" class="mt-3" onsubmit="return confirm('Är du säker på att du vill återställa konfigurationen?')">
|
||||
<button type="submit" class="btn btn-danger">Återställ inställningar</button>
|
||||
</form>
|
||||
}
|
||||
|
||||
@section Scripts {
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.5/jquery.validate.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate-unobtrusive/3.2.12/jquery.validate.unobtrusive.min.js"></script>
|
||||
|
||||
<script>
|
||||
function goToStep(step) {
|
||||
document.querySelectorAll('.setup-step').forEach(s => s.style.display = 'none');
|
||||
document.getElementById('step-' + step).style.display = 'block';
|
||||
|
||||
if (step === 3) {
|
||||
document.getElementById('summary-DbHost').textContent = document.getElementById('DbHost').value;
|
||||
document.getElementById('summary-DbPort').textContent = document.getElementById('DbPort').value;
|
||||
document.getElementById('summary-DbName').textContent = document.getElementById('DbName').value;
|
||||
document.getElementById('summary-DbUser').textContent = document.getElementById('DbUser').value;
|
||||
document.getElementById('summary-AdminUsername').textContent = document.getElementById('AdminUsername').value;
|
||||
document.getElementById('summary-AdminEmail').textContent = document.getElementById('AdminEmail').value;
|
||||
|
||||
document.getElementById('setup-progress').innerHTML = `<p>Redo att påbörja installationen.</p>`;
|
||||
}
|
||||
}
|
||||
document.getElementById('AdminPassword').addEventListener('input', function () {
|
||||
const val = this.value;
|
||||
|
||||
const hasLength = val.length >= 6;
|
||||
const hasUpper = /[A-Z]/.test(val);
|
||||
const hasLower = /[a-z]/.test(val);
|
||||
const hasDigit = /\d/.test(val);
|
||||
const hasSpecial = /[!@@#$%^&*(),.?":{}|<>_\-\\[\]\/+=~`]/.test(val); // lägg till specialtecken
|
||||
|
||||
updateCriteria('length-criteria', hasLength);
|
||||
updateCriteria('uppercase-criteria', hasUpper);
|
||||
updateCriteria('lowercase-criteria', hasLower);
|
||||
updateCriteria('digit-criteria', hasDigit);
|
||||
updateCriteria('special-criteria', hasSpecial);
|
||||
});
|
||||
|
||||
function updateCriteria(id, isValid) {
|
||||
const el = document.getElementById(id);
|
||||
if (isValid) {
|
||||
el.textContent = '✔ ' + el.textContent.slice(2);
|
||||
el.style.color = 'green';
|
||||
} else {
|
||||
el.textContent = '❌ ' + el.textContent.slice(2);
|
||||
el.style.color = 'red';
|
||||
}
|
||||
}
|
||||
|
||||
function validateStep(step) {
|
||||
let isValid = true;
|
||||
document.querySelectorAll('#step-' + step + ' [required]').forEach(field => {
|
||||
if (!field.value.trim()) {
|
||||
field.classList.add('is-invalid');
|
||||
isValid = false;
|
||||
} else {
|
||||
field.classList.remove('is-invalid');
|
||||
}
|
||||
});
|
||||
return isValid;
|
||||
}
|
||||
|
||||
function testDbConnection() {
|
||||
const host = document.getElementById('DbHost').value;
|
||||
const port = document.getElementById('DbPort').value;
|
||||
const db = document.getElementById('DbName').value;
|
||||
const user = document.getElementById('DbUser').value;
|
||||
const pass = document.getElementById('DbPassword').value;
|
||||
|
||||
const output = document.getElementById('db-test-result');
|
||||
output.innerHTML = '<span class="spinner"></span> Testar...';
|
||||
|
||||
fetch('/api/setup/testdb', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ host, port, db, user, pass })
|
||||
})
|
||||
.then(async res => {
|
||||
const text = await res.text();
|
||||
console.log("Raw:", text);
|
||||
try {
|
||||
const result = JSON.parse(text);
|
||||
if (result.success) {
|
||||
output.innerHTML = '✅ <span class="text-success">Anslutning lyckades!</span>';
|
||||
} else {
|
||||
output.innerHTML = '❌ <span class="text-danger">' + result.message + '</span>';
|
||||
}
|
||||
} catch (e) {
|
||||
output.innerHTML = '❌ <span class="text-danger">Felaktigt svar från servern: ' + text + '</span>';
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
output.innerHTML = '❌ <span class="text-danger">Nätverksfel: ' + err.message + '</span>';
|
||||
});
|
||||
}
|
||||
function validateAdminStep() {
|
||||
let valid = validateStep(2);
|
||||
const pass = document.getElementById('AdminPassword');
|
||||
const confirm = document.getElementById('ConfirmPassword');
|
||||
const confirmError = document.getElementById('confirmPasswordError');
|
||||
|
||||
if (pass.value !== confirm.value) {
|
||||
confirm.classList.add('is-invalid');
|
||||
confirmError.style.display = 'block';
|
||||
valid = false;
|
||||
} else {
|
||||
confirm.classList.remove('is-invalid');
|
||||
confirmError.style.display = 'none';
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
document.getElementById('ConfirmPassword').addEventListener('input', function () {
|
||||
const pass = document.getElementById('AdminPassword').value;
|
||||
const confirm = this.value;
|
||||
const confirmField = document.getElementById('ConfirmPassword');
|
||||
const matchStatus = document.getElementById('password-match-status');
|
||||
|
||||
if (confirm.length === 0) {
|
||||
matchStatus.innerHTML = '';
|
||||
confirmField.classList.remove('is-invalid');
|
||||
return;
|
||||
}
|
||||
|
||||
if (pass === confirm) {
|
||||
matchStatus.innerHTML = '✔';
|
||||
matchStatus.style.color = 'green';
|
||||
confirmField.classList.remove('is-invalid');
|
||||
} else {
|
||||
matchStatus.innerHTML = '❌';
|
||||
matchStatus.style.color = 'red';
|
||||
confirmField.classList.add('is-invalid');
|
||||
}
|
||||
});
|
||||
|
||||
async function submitSetup() {
|
||||
goToStep(3);
|
||||
const form = document.getElementById('setup-form');
|
||||
const formData = new FormData(form);
|
||||
const data = {};
|
||||
formData.forEach((value, key) => { data[key] = value; });
|
||||
|
||||
const resultEl = document.getElementById('setup-progress');
|
||||
resultEl.innerHTML = `<p>
|
||||
Skapar databastabeller och konfigurerar administratörskonto...
|
||||
<span class="spinner"></span>
|
||||
</p>`;
|
||||
|
||||
try {
|
||||
const response = await fetch('/setup', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
|
||||
const text = await response.text();
|
||||
let result;
|
||||
try {
|
||||
result = JSON.parse(text);
|
||||
} catch (err) {
|
||||
throw new Error("Kunde inte tolka JSON-svar: " + text);
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
resultEl.innerHTML = `
|
||||
<div class="alert alert-danger mt-3">
|
||||
❌ Fel vid installation: ${result.error || 'Okänt fel'}
|
||||
</div>`;
|
||||
return;
|
||||
}
|
||||
|
||||
resultEl.innerHTML = `
|
||||
<div class="alert alert-success mt-3">
|
||||
✅ ${result.message || 'Installation slutförd!'}
|
||||
<br />
|
||||
<button class="btn btn-success mt-3" onclick="window.location.href='/'">Gå vidare</button>
|
||||
</div>`;
|
||||
} catch (err) {
|
||||
console.error("Något gick fel:", err);
|
||||
resultEl.innerHTML = `<div class="alert alert-danger mt-3">
|
||||
❌ Fel vid installation: ${err.message}
|
||||
</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
}
|
||||
17
Aberwyn/Views/Setup/SetupComplete.cshtml
Normal file
17
Aberwyn/Views/Setup/SetupComplete.cshtml
Normal file
@@ -0,0 +1,17 @@
|
||||
@{
|
||||
ViewData["Title"] = "Klart!";
|
||||
}
|
||||
|
||||
<h1>✅ Aberwyn är nu installerad</h1>
|
||||
<p>Du kan nu starta om applikationen för att börja använda den.</p>
|
||||
|
||||
<a href="/" class="btn btn-primary">Till startsidan</a>
|
||||
|
||||
<hr />
|
||||
|
||||
<h3>🔄 Återställ installation</h3>
|
||||
<p>Om du vill nollställa installationen och börja om från början, klicka här:</p>
|
||||
|
||||
<form method="post" asp-action="Reset">
|
||||
<button type="submit" class="btn btn-danger">Återställ konfiguration</button>
|
||||
</form>
|
||||
@@ -21,35 +21,46 @@
|
||||
<span class="initial L2">L</span><span class="name">udwig</span>
|
||||
</h1>
|
||||
</div>
|
||||
<partial name="_LoginPartial" />
|
||||
@if (ViewBag.IsSetupMode as bool? != true)
|
||||
{
|
||||
<partial name="_LoginPartial" />
|
||||
}
|
||||
</header>
|
||||
|
||||
<div class="page-content">
|
||||
<aside class="sidebar">
|
||||
<ul class="nav-list">
|
||||
<li><a asp-controller="Home" asp-action="Index"><i class="fas fa-home"></i> Home</a></li>
|
||||
@if (User.IsInRole("Admin") || User.IsInRole("Budget"))
|
||||
{
|
||||
<li><a asp-controller="Budget" asp-action="Index"><i class="fas fa-wallet"></i> Budget</a></li>
|
||||
}
|
||||
<li><a asp-controller="Home" asp-action="Menu"><i class="fas fa-utensils"></i> Veckomeny</a></li>
|
||||
@if (ViewBag.RestaurantIsOpen as bool? == true)
|
||||
{
|
||||
<li><a asp-controller="FoodMenu" asp-action="PizzaOrder"><i class="fas fa-pizza-slice"></i> Beställ pizza</a></li>
|
||||
}
|
||||
@if (User.IsInRole("Admin") || User.IsInRole("Chef"))
|
||||
{
|
||||
<li><a asp-controller="FoodMenu" asp-action="Veckomeny"><i class="fas fa-calendar-week"></i> Administrera Veckomeny</a></li>
|
||||
<li><a asp-controller="FoodMenu" asp-action="MealAdmin"><i class="fas fa-list"></i> Måltider</a></li>
|
||||
<li><a asp-controller="FoodMenu" asp-action="PizzaAdmin"><i class="fas fa-list"></i> Pizza Admin</a></li>
|
||||
@if (ViewBag.IsSetupMode as bool? != true)
|
||||
{
|
||||
<ul class="nav-list">
|
||||
<li><a asp-controller="Home" asp-action="Index"><i class="fas fa-home"></i> Home</a></li>
|
||||
@if (User.IsInRole("Admin") || User.IsInRole("Budget"))
|
||||
{
|
||||
<li><a asp-controller="Budget" asp-action="Index"><i class="fas fa-wallet"></i> Budget</a></li>
|
||||
}
|
||||
<li><a asp-controller="Home" asp-action="Menu"><i class="fas fa-utensils"></i> Veckomeny</a></li>
|
||||
@if (ViewBag.RestaurantIsOpen as bool? == true)
|
||||
{
|
||||
<li><a asp-controller="FoodMenu" asp-action="PizzaOrder"><i class="fas fa-pizza-slice"></i> Beställ pizza</a></li>
|
||||
}
|
||||
@if (User.IsInRole("Admin") || User.IsInRole("Chef"))
|
||||
{
|
||||
<li><a asp-controller="FoodMenu" asp-action="Veckomeny"><i class="fas fa-calendar-week"></i> Administrera Veckomeny</a></li>
|
||||
<li><a asp-controller="FoodMenu" asp-action="MealAdmin"><i class="fas fa-list"></i> Måltider</a></li>
|
||||
<li><a asp-controller="FoodMenu" asp-action="PizzaAdmin"><i class="fas fa-list"></i> Pizza Admin</a></li>
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@if (User.IsInRole("Admin"))
|
||||
{
|
||||
<li><a asp-controller="Admin" asp-action="Index"><i class="fas fa-cog"></i> Adminpanel</a></li>
|
||||
}
|
||||
</ul>
|
||||
@if (User.IsInRole("Admin"))
|
||||
{
|
||||
<li><a asp-controller="Admin" asp-action="Index"><i class="fas fa-cog"></i> Adminpanel</a></li>
|
||||
}
|
||||
</ul>
|
||||
} else
|
||||
{
|
||||
<ul class="nav-list">
|
||||
<li><a asp-controller="Setup" asp-action="Index"> Setup</a></li>
|
||||
</ul>
|
||||
}
|
||||
</aside>
|
||||
|
||||
<main class="main-panel">
|
||||
|
||||
@@ -31,7 +31,7 @@ else
|
||||
<div class="auth-dropdown">
|
||||
<form method="post" asp-area="Identity" asp-page="/Account/Login" style="padding: 12px; display: flex; flex-direction: column; gap: 8px;">
|
||||
<input type="hidden" name="ReturnUrl" value="/" />
|
||||
<input name="Input.Email" type="email" placeholder="E-post" required style="padding: 6px; font-size: 14px;" />
|
||||
<input name="Input.Username" type="text" placeholder="Användarnamn" required style="padding: 6px; font-size: 14px;" />
|
||||
<input name="Input.Password" type="password" placeholder="Lösenord" required style="padding: 6px; font-size: 14px;" />
|
||||
<button type="submit" style="background-color: #3A4E62; color: white; border: none; padding: 6px 10px; border-radius: 4px; font-size: 14px;">
|
||||
Logga in
|
||||
|
||||
@@ -13,5 +13,6 @@
|
||||
"Subject": "mailto:e@zcz.se",
|
||||
"PublicKey": "BBLmMdU3X3e79SqzAy4vIAJI0jmzRME17F9UKbO8XT1dfnO-mWIPKIrFDbIZD4_3ic7uoijK61vaGdfFUk3HUfU",
|
||||
"PrivateKey": "oranoCmCo8HXdc03juNgbeSlKE39N3DYus_eMunLsnc"
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user