Woohoo working version gooo
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
@@ -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; }
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -16,6 +16,9 @@ namespace Aberwyn.Data
|
||||
base.OnModelCreating(builder);
|
||||
|
||||
builder.Entity<WeeklyMenu>().ToTable("WeeklyMenu");
|
||||
builder.Entity<Meal>()
|
||||
.Property(m => m.Id)
|
||||
.ValueGeneratedNever();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Aberwyn.Data;
|
||||
using Aberwyn.Models;
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user