Woohoo working version gooo
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Elias Jansson
2025-06-04 18:01:05 +02:00
parent 84c6c45a0b
commit c44fbfdca9
7 changed files with 214 additions and 114 deletions

View File

@@ -102,54 +102,20 @@ namespace Aberwyn.Controllers
}
[HttpPost]
[Authorize(Roles = "Admin")]
public IActionResult ImportMealsFromProd()
public IActionResult ImportMenusFromCustom(string dbHost, int dbPort, string dbName, string dbUser, string dbPassword)
{
var prodService = MenuService.CreateWithSetup(_env);
var devService = new MenuService(_context); // injicerad context används
var connStr = $"Server={dbHost};Port={dbPort};Database={dbName};Uid={dbUser};Pwd={dbPassword};";
var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
optionsBuilder.UseMySql(connStr, ServerVersion.AutoDetect(connStr));
var prodMeals = prodService.GetMealsDetailed();
foreach (var meal in prodMeals)
{
// Kopiera utan ID (för att undvika konflikt) och spara
var newMeal = new Meal
{
Name = meal.Name,
Description = meal.Description,
ProteinType = meal.ProteinType,
Category = meal.Category,
CarbType = meal.CarbType,
RecipeUrl = meal.RecipeUrl,
ImageUrl = meal.ImageUrl,
IsAvailable = meal.IsAvailable,
CreatedAt = meal.CreatedAt,
Instructions = meal.Instructions,
ImageData = meal.ImageData,
ImageMimeType = meal.ImageMimeType,
Ingredients = meal.Ingredients.Select(i => new Ingredient
{
Quantity = i.Quantity,
Item = i.Item
}).ToList()
};
devService.SaveOrUpdateMealWithIngredients(newMeal);
}
return Content("Import klar!");
}
[HttpPost]
[Authorize(Roles = "Admin")]
public IActionResult ImportMenusFromProd()
{
var prodService = MenuService.CreateWithConfig(_configuration, _env, useProdDb: true);
using var customContext = new ApplicationDbContext(optionsBuilder.Options);
var sourceService = new MenuService(customContext);
var devService = new MenuService(_context);
var allProdMenus = prodService.GetAllWeeklyMenus();
var allMeals = devService.GetMeals();
var sourceMenus = sourceService.GetAllWeeklyMenus();
var devMeals = devService.GetMeals();
foreach (var menu in allProdMenus)
foreach (var menu in sourceMenus)
{
var newMenu = new WeeklyMenu
{
@@ -163,34 +129,92 @@ namespace Aberwyn.Controllers
};
if (!string.IsNullOrEmpty(menu.BreakfastMealName))
newMenu.BreakfastMealId = allMeals.FirstOrDefault(m => m.Name == menu.BreakfastMealName)?.Id;
newMenu.BreakfastMealId = devMeals.FirstOrDefault(m => m.Name == menu.BreakfastMealName)?.Id;
if (!string.IsNullOrEmpty(menu.LunchMealName))
newMenu.LunchMealId = allMeals.FirstOrDefault(m => m.Name == menu.LunchMealName)?.Id;
newMenu.LunchMealId = devMeals.FirstOrDefault(m => m.Name == menu.LunchMealName)?.Id;
if (!string.IsNullOrEmpty(menu.DinnerMealName))
newMenu.DinnerMealId = allMeals.FirstOrDefault(m => m.Name == menu.DinnerMealName)?.Id;
newMenu.DinnerMealId = devMeals.FirstOrDefault(m => m.Name == menu.DinnerMealName)?.Id;
_context.WeeklyMenus.Add(newMenu);
}
_context.SaveChanges();
TempData["Message"] = "Import av veckomenyer klar.";
TempData["Message"] = $"✅ Import av veckomenyer från extern databas klar ({sourceMenus.Count}).";
return RedirectToAction("Index");
}
[HttpPost]
[Authorize(Roles = "Admin")]
public IActionResult ImportBudgetFromProd()
public IActionResult ImportMealsFromCustom(string dbHost, int dbPort, string dbName, string dbUser, string dbPassword)
{
// Hämta connection till produktion
using var prodContext = ApplicationDbContextFactory.CreateWithConfig(_env, true);
var connStr = $"Server={dbHost};Port={dbPort};Database={dbName};Uid={dbUser};Pwd={dbPassword};";
var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
optionsBuilder.UseMySql(connStr, ServerVersion.AutoDetect(connStr), mySqlOptions => mySqlOptions.CommandTimeout(180));
using var customContext = new ApplicationDbContext(optionsBuilder.Options);
// Importera definitioner först
var prodCategoryDefs = prodContext.BudgetCategoryDefinitions.ToList();
var prodItemDefs = prodContext.BudgetItemDefinitions.ToList();
var customService = new MenuService(customContext);
var devService = new MenuService(_context);
foreach (var def in prodCategoryDefs)
try
{
var importedMeals = customService.GetMealsDetailed(); // Ska inkludera Ingredients
foreach (var meal in importedMeals)
{
var newMeal = new Meal
{
Id = meal.Id, // 👈 Viktigt!
Name = meal.Name,
Description = meal.Description,
ProteinType = meal.ProteinType,
Category = meal.Category,
CarbType = meal.CarbType,
RecipeUrl = meal.RecipeUrl,
ImageUrl = meal.ImageUrl,
IsAvailable = meal.IsAvailable,
CreatedAt = meal.CreatedAt,
Instructions = meal.Instructions,
ImageData = meal.ImageData,
ImageMimeType = meal.ImageMimeType,
Ingredients = meal.Ingredients.Select(i => new Ingredient
{
MealId = meal.Id, // 👈 Koppla till rätt måltid
Quantity = i.Quantity,
Item = i.Item
}).ToList()
};
devService.SaveOrUpdateMealWithIngredients(newMeal);
}
TempData["Message"] = $"✅ {importedMeals.Count} måltider importerade från extern databas.";
}
catch (Exception ex)
{
TempData["Message"] = $"❌ Fel vid import: {ex.Message}";
}
return RedirectToAction("Index");
}
[HttpPost]
[Authorize(Roles = "Admin")]
public IActionResult ImportBudgetFromCustom(string dbHost, int dbPort, string dbName, string dbUser, string dbPassword)
{
var connStr = $"Server={dbHost};Port={dbPort};Database={dbName};Uid={dbUser};Pwd={dbPassword};";
var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
optionsBuilder.UseMySql(connStr, ServerVersion.AutoDetect(connStr));
using var sourceContext = new ApplicationDbContext(optionsBuilder.Options);
var categoryDefs = sourceContext.BudgetCategoryDefinitions.ToList();
var itemDefs = sourceContext.BudgetItemDefinitions.ToList();
foreach (var def in categoryDefs)
{
if (!_context.BudgetCategoryDefinitions.Any(d => d.Name == def.Name))
{
@@ -199,11 +223,10 @@ namespace Aberwyn.Controllers
Name = def.Name,
Color = def.Color ?? "#cccccc"
});
}
}
foreach (var def in prodItemDefs)
foreach (var def in itemDefs)
{
if (!_context.BudgetItemDefinitions.Any(d => d.Name == def.Name))
{
@@ -211,19 +234,17 @@ namespace Aberwyn.Controllers
}
}
_context.SaveChanges(); // Se till att ID:n finns för FK:n nedan
_context.SaveChanges();
// Ladda definitioner i minnet för snabb lookup
var devCategoryDefs = _context.BudgetCategoryDefinitions.ToList();
var devItemDefs = _context.BudgetItemDefinitions.ToList();
// Importera budgetperioder med kategorier och items
var prodPeriods = prodContext.BudgetPeriods
var periods = sourceContext.BudgetPeriods
.Include(p => p.Categories)
.ThenInclude(c => c.Items)
.ToList();
foreach (var period in prodPeriods)
foreach (var period in periods)
{
var exists = _context.BudgetPeriods
.Any(p => p.Year == period.Year && p.Month == period.Month);
@@ -259,11 +280,13 @@ namespace Aberwyn.Controllers
}
_context.SaveChanges();
TempData["Message"] = "✅ Import av budgetdata från produktion är klar.";
TempData["Message"] = $"✅ Import av budgetdata från extern databas klar ({periods.Count} månader).";
return RedirectToAction("Index");
}
//Todo
[HttpGet]
@@ -314,9 +337,31 @@ namespace Aberwyn.Controllers
return Ok();
}
[HttpPost]
[Authorize(Roles = "Admin")]
public IActionResult TestDbConnection(string dbHost, int dbPort, string dbName, string dbUser, string dbPassword)
{
var connStr = $"Server={dbHost};Port={dbPort};Database={dbName};Uid={dbUser};Pwd={dbPassword};";
try
{
var builder = new DbContextOptionsBuilder<ApplicationDbContext>();
builder.UseMySql(connStr, ServerVersion.AutoDetect(connStr));
using var context = new ApplicationDbContext(builder.Options);
context.Database.OpenConnection();
context.Database.CloseConnection();
return Json(new { success = true, message = "✅ Anslutning lyckades!" });
}
catch (Exception ex)
{
return Json(new { success = false, message = $"❌ Anslutning misslyckades: {ex.Message}" });
}
}
}
public class AdminUserViewModel
{
public string UserId { get; set; }

View File

@@ -10,12 +10,13 @@ namespace Aberwyn.Controllers
private readonly IConfiguration _configuration;
private readonly IHostEnvironment _env;
private readonly MenuService _menuService;
public MealController(IConfiguration configuration, IHostEnvironment env)
public MealController(MenuService menuService, IConfiguration configuration, IHostEnvironment env)
{
_menuService = menuService;
_configuration = configuration;
_env = env;
}
[HttpGet]
public IActionResult View(int id, bool edit = false)
{

View File

@@ -16,6 +16,9 @@ namespace Aberwyn.Data
base.OnModelCreating(builder);
builder.Entity<WeeklyMenu>().ToTable("WeeklyMenu");
builder.Entity<Meal>()
.Property(m => m.Id)
.ValueGeneratedNever();
}

View File

@@ -16,37 +16,6 @@ public class MenuService
_context = context;
}
// Detta är en alternativ konstruktör används manuellt vid t.ex. import
public static MenuService CreateWithConfig(IConfiguration config, IHostEnvironment env, bool useProdDb = false)
{
var basePath = env.ContentRootPath ?? Directory.GetCurrentDirectory();
var setupPath = Path.Combine(basePath, "infrastructure", "setup.json");
if (!File.Exists(setupPath))
throw new FileNotFoundException("setup.json saknas i infrastructure/");
var setupJson = File.ReadAllText(setupPath);
var setup = System.Text.Json.JsonSerializer.Deserialize<SetupSettings>(setupJson)!;
if (!setup.IsConfigured || string.IsNullOrWhiteSpace(setup.DbPassword))
throw new InvalidOperationException("setup.json är ofullständig.");
var csBuilder = new MySqlConnector.MySqlConnectionStringBuilder
{
Server = setup.DbHost,
Port = (uint)setup.DbPort,
Database = setup.DbName,
UserID = setup.DbUser,
Password = setup.DbPassword,
AllowUserVariables = true
};
var builder = new DbContextOptionsBuilder<ApplicationDbContext>();
builder.UseMySql(csBuilder.ConnectionString, ServerVersion.AutoDetect(csBuilder.ConnectionString));
var context = new ApplicationDbContext(builder.Options);
return new MenuService(context);
}
public static MenuService CreateWithSetup(IHostEnvironment env)
{
var setup = SetupLoader.Load(env);
@@ -83,15 +52,26 @@ public class MenuService
meal.Name = meal.Name.Trim();
meal.CreatedAt = meal.CreatedAt == default ? DateTime.Now : meal.CreatedAt;
if (meal.Id == 0)
_context.Meals.Add(meal);
var existing = _context.Meals
.AsNoTracking()
.FirstOrDefault(m => m.Id == meal.Id);
if (existing == null)
{
// Nytt objekt försök behålla ID:t från prod
_context.Entry(meal).State = EntityState.Added;
}
else
{
// Befintlig uppdatera
_context.Meals.Update(meal);
}
_context.SaveChanges();
}
public List<WeeklyMenu> GetAllWeeklyMenus()
public List<WeeklyMenu> GetAllWeeklyMenus()
{
var menus = _context.WeeklyMenus.ToList();
@@ -139,10 +119,12 @@ public List<WeeklyMenu> GetAllWeeklyMenus()
public List<Meal> GetMealsDetailed()
{
return _context.Meals
.Include(m => m.Ingredients) // 🧠 detta behövs!
.OrderByDescending(m => m.CreatedAt)
.ToList();
}
public Meal GetMealById(int id)
{
var meal = _context.Meals

View File

@@ -2,10 +2,10 @@
"AdminUsername": "admin",
"AdminEmail": "admin@localhost",
"AdminPassword": "Admin123!",
"IsConfigured": false,
"DbHost": null,
"IsConfigured": true,
"DbHost": "192.168.1.108",
"DbPort": 3306,
"DbName": null,
"DbUser": null,
"DbPassword": null
"DbName": "lewel_prod",
"DbUser": "lewel",
"DbPassword": "W542.Hl;)%ta"
}

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Aberwyn.Data;
using Aberwyn.Models;

View File

@@ -84,20 +84,88 @@
<button type="submit" class="btn btn-warning mt-2">Skicka testnotis</button>
</form>
<hr />
<h3>Importera måltider från produktion</h3>
<form method="post" asp-action="ImportMealsFromProd">
<button type="submit" class="btn btn-danger">Importera alla måltider</button>
</form>
<form method="post" asp-action="ImportMenusFromProd">
<button type="submit" class="btn btn-danger mt-2">Importera veckomenyer</button>
</form>
<form method="post" asp-action="ImportBudgetFromProd">
<button type="submit" class="btn btn-danger mt-2">Importera budgetdata</button>
</form>
<div class="card mb-4">
<div class="card-header bg-secondary text-white">
Importera från annan databas
</div>
<div class="card-body">
<form id="customDbForm">
<div class="form-group">
<label>Server:</label>
<input type="text" name="dbHost" class="form-control" required />
</div>
<div class="form-group">
<label>Port:</label>
<input type="number" name="dbPort" class="form-control" value="3306" required />
</div>
<div class="form-group">
<label>Databas:</label>
<input type="text" name="dbName" class="form-control" required />
</div>
<div class="form-group">
<label>Användarnamn:</label>
<input type="text" name="dbUser" class="form-control" required />
</div>
<div class="form-group">
<label>Lösenord:</label>
<input type="password" name="dbPassword" class="form-control" required />
</div>
</form>
<button class="btn btn-info mt-3" onclick="testDbConnection()">🔌 Testa anslutning</button>
<div id="testResult" class="mt-2"></div>
<button class="btn btn-primary mt-3" onclick="submitTo('ImportMealsFromCustom')">🍽 Importera måltider</button>
<button class="btn btn-primary mt-3" onclick="submitTo('ImportMenusFromCustom')">🗓 Importera menyer</button>
<button class="btn btn-primary mt-3" onclick="submitTo('ImportBudgetFromCustom')">💰 Importera budget</button>
</div>
</div>
<script>
function collectFormData() {
const form = document.getElementById("customDbForm");
return new FormData(form); // 🔁 Byt ut till ren FormData istället för URLSearchParams
}
function submitTo(action) {
const data = collectFormData();
fetch(`/admin/${action}`, {
method: "POST",
body: data
})
.then(async res => {
const text = await res.text();
if (res.ok) {
alert(`✅ ${action} lyckades`);
} else {
alert(`❌ Fel vid ${action}:\n${text}`);
}
});
}
function testDbConnection() {
const data = collectFormData();
fetch(`/admin/TestDbConnection`, {
method: "POST",
body: data
})
.then(res => res.json())
.then(data => {
const div = document.getElementById("testResult");
div.innerText = data.message;
div.className = data.success ? "text-success mt-2" : "text-danger mt-2";
})
.catch(err => {
document.getElementById("testResult").innerText = "❌ Ett tekniskt fel uppstod.";
console.error(err);
});
}
async function sendPush(event) {
event.preventDefault(); // 🚫 stoppa formuläret från att göra vanlig POST