Initial commit

This commit is contained in:
Francesco Lorenzo D'Amico
2026-07-17 14:06:58 +02:00
parent be57ac7b65
commit 0adcd6c08a
6 changed files with 135 additions and 5 deletions
+14
View File
@@ -0,0 +1,14 @@
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY ["Francesco.Recipes.World/Francesco.Recipes.World.csproj", "Francesco.Recipes.World/"]
RUN dotnet restore "Francesco.Recipes.World/Francesco.Recipes.World.csproj"
COPY . .
WORKDIR "/src/Francesco.Recipes.World"
RUN dotnet publish "Francesco.Recipes.World.csproj" -c Release -o /app/publish
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "Francesco.Recipes.World.dll"]
@@ -32,14 +32,52 @@
return BadRequest("No ingredients provided.");
}
var shoppingList = await _shoppingListRepository.AddIngredientsToShoppingListAsync(request.RecipeId, request.IngredientIds);
// Compute duplicates for this recipe in the current shopping list
var existingShoppingList = await _context.ShoppingLists
.Include(sl => sl.RecipeShoppingList)
.ThenInclude(rsl => rsl.SelectedIngredients)
.ThenInclude(si => si.RecipeIngredient)
.Include(sl => sl.RecipeShoppingList)
.ThenInclude(rsl => rsl.Recipe)
.FirstOrDefaultAsync(sl => sl.RecipeShoppingList.Any(rsl => rsl.Recipe.Id == request.RecipeId));
var existingForRecipe = existingShoppingList?
.RecipeShoppingList.FirstOrDefault(rsl => rsl.Recipe.Id == request.RecipeId);
var existingIds = existingForRecipe?.SelectedIngredients
.Where(si => si.RecipeIngredient != null)
.Select(si => si.RecipeIngredient.Id)
.ToHashSet() ?? new HashSet<Guid>();
var skippedExisting = request.IngredientIds.Where(id => existingIds.Contains(id)).Distinct().ToList();
var ingredientIdsToAdd = request.IngredientIds.Except(skippedExisting).Distinct().ToList();
if (ingredientIdsToAdd.Count == 0)
{
return Json(new
{
shoppingListId = existingShoppingList?.Id,
skippedExisting,
addedCount = 0,
skippedCount = skippedExisting.Count,
noAdditions = true,
});
}
var shoppingList = await _shoppingListRepository.AddIngredientsToShoppingListAsync(request.RecipeId, ingredientIdsToAdd);
if (shoppingList == null)
{
return BadRequest("Error creating shopping list.");
}
return Json(new { shoppingListId = shoppingList.Id });
return Json(new
{
shoppingListId = shoppingList.Id,
skippedExisting,
addedCount = ingredientIdsToAdd.Count,
skippedCount = skippedExisting.Count,
});
}
[HttpGet("RecipeCount")]
@@ -196,10 +196,28 @@
})
.then(result => {
localStorage.setItem('shoppingListId', result.shoppingListId);
alert('Einkaufsliste aktualisiert.');
const showInfo = (msg) => {
if (window.Swal) {
Swal.fire({ icon: 'info', title: 'Hinweis', text: msg, timer: 1800, showConfirmButton: false });
} else { alert(msg); }
};
if (result.skippedCount && result.skippedCount > 0) {
if (result.addedCount && result.addedCount > 0) {
showInfo(`Einkaufsliste aktualisiert. ${result.skippedCount} Zutaten waren bereits vorhanden und wurden übersprungen.`);
} else {
showInfo('Diese Zutaten sind bereits in der Einkaufsliste vorhanden.');
}
} else {
showInfo('Einkaufsliste aktualisiert.');
}
})
.catch(error => {
if (window.Swal) {
Swal.fire({ icon: 'error', title: 'Fehler', text: 'Fehler beim Aktualisieren der Einkaufsliste.' });
} else {
alert('Fehler beim Aktualisieren der Einkaufsliste.');
}
});
};
});
@@ -13,7 +13,7 @@
id = ri.Id,
name = ri.Ingredient.Name,
amount = ri.Quantity,
unit = ri.Unit.Name,
unit = ri.Unit.Symbol,
shoppingListId = Model.RecipeIngredientToShoppingListMap.ContainsKey(ri.Id)
? Model.RecipeIngredientToShoppingListMap[ri.Id]
: Guid.Empty
@@ -104,3 +104,4 @@
</script>
<script src="~/js/site.js"></script>
}
@@ -0,0 +1,11 @@
{
"ConnectionStrings": {
"FrancescosRecipesWorldDbContextConnection": "Server=sqlserver-recipes,1433;Database=Francesco.Recipes.World;User Id=sa;Password=YourRecipesPassword123!;TrustServerCertificate=True;MultipleActiveResultSets=true"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
+48
View File
@@ -0,0 +1,48 @@
networks:
recipesnet-dev:
driver: bridge
services:
francescos-recipes-world:
container_name: francescos-recipes-world-dev
depends_on:
sqlserver-recipes:
condition: service_healthy
environment:
- ASPNETCORE_ENVIRONMENT=Production
- TZ=Europe/Zurich
- ConnectionStrings__FrancescosRecipesWorldDbContextConnection=Server=sqlserver-recipes,1433;Database=Francesco.Recipes.World;User Id=sa;Password=YourRecipesPassword123!;TrustServerCertificate=True;MultipleActiveResultSets=true
image: francesco448/francescos.recipes.world:latest
networks:
- recipesnet-dev
ports:
- '9003:8080'
restart: unless-stopped
volumes:
- /mnt/PoolHDD/AppData/Francescos--Recipes-World/app:/app
working_dir: /app
sqlserver-recipes:
container_name: sqlserver-recipes-dev
environment:
- ACCEPT_EULA=Y
- SA_PASSWORD=YourRecipesPassword123!
healthcheck:
interval: 10s
retries: 10
start_period: 30s
test:
- CMD-SHELL
- >-
/opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P YourRecipesPassword123!
-Q 'SELECT 1' -No
timeout: 5s
image: mcr.microsoft.com/mssql/server:2022-latest
networks:
- recipesnet-dev
ports:
- '1702:1433'
restart: unless-stopped
volumes:
- /mnt/PoolHDD/AppData/Francescos--Recipes-World/sqlserver:/var/opt/mssql