diff --git a/Seasoned.Backend/Migrations/20260306062007_InitialCreate.Designer.cs b/Seasoned.Backend/Migrations/20260311160009_AddRecipeFields.Designer.cs
similarity index 98%
rename from Seasoned.Backend/Migrations/20260306062007_InitialCreate.Designer.cs
rename to Seasoned.Backend/Migrations/20260311160009_AddRecipeFields.Designer.cs
index 75b8f50..f75098d 100644
--- a/Seasoned.Backend/Migrations/20260306062007_InitialCreate.Designer.cs
+++ b/Seasoned.Backend/Migrations/20260311160009_AddRecipeFields.Designer.cs
@@ -13,8 +13,8 @@ using Seasoned.Backend.Data;
namespace Seasoned.Backend.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
- [Migration("20260306062007_InitialCreate")]
- partial class InitialCreate
+ [Migration("20260311160009_AddRecipeFields")]
+ partial class AddRecipeFields
{
///
protected override void BuildTargetModel(ModelBuilder modelBuilder)
@@ -234,7 +234,8 @@ namespace Seasoned.Backend.Migrations
b.Property("CreatedAt")
.HasColumnType("timestamp with time zone");
- b.Property("Description")
+ b.Property("Icon")
+ .IsRequired()
.HasColumnType("text");
b.PrimitiveCollection>("Ingredients")
diff --git a/Seasoned.Backend/Migrations/20260306062007_InitialCreate.cs b/Seasoned.Backend/Migrations/20260311160009_AddRecipeFields.cs
similarity index 98%
rename from Seasoned.Backend/Migrations/20260306062007_InitialCreate.cs
rename to Seasoned.Backend/Migrations/20260311160009_AddRecipeFields.cs
index 82439f9..234d7ac 100644
--- a/Seasoned.Backend/Migrations/20260306062007_InitialCreate.cs
+++ b/Seasoned.Backend/Migrations/20260311160009_AddRecipeFields.cs
@@ -8,7 +8,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
namespace Seasoned.Backend.Migrations
{
///
- public partial class InitialCreate : Migration
+ public partial class AddRecipeFields : Migration
{
///
protected override void Up(MigrationBuilder migrationBuilder)
@@ -168,7 +168,7 @@ namespace Seasoned.Backend.Migrations
Id = table.Column(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
Title = table.Column(type: "text", nullable: false),
- Description = table.Column(type: "text", nullable: true),
+ Icon = table.Column(type: "text", nullable: false),
Ingredients = table.Column>(type: "text[]", nullable: false),
Instructions = table.Column>(type: "text[]", nullable: false),
CreatedAt = table.Column(type: "timestamp with time zone", nullable: false),
diff --git a/Seasoned.Backend/Migrations/ApplicationDbContextModelSnapshot.cs b/Seasoned.Backend/Migrations/ApplicationDbContextModelSnapshot.cs
index eb0c8d9..8d2f33b 100644
--- a/Seasoned.Backend/Migrations/ApplicationDbContextModelSnapshot.cs
+++ b/Seasoned.Backend/Migrations/ApplicationDbContextModelSnapshot.cs
@@ -231,7 +231,8 @@ namespace Seasoned.Backend.Migrations
b.Property("CreatedAt")
.HasColumnType("timestamp with time zone");
- b.Property("Description")
+ b.Property("Icon")
+ .IsRequired()
.HasColumnType("text");
b.PrimitiveCollection>("Ingredients")
diff --git a/Seasoned.Backend/Program.cs b/Seasoned.Backend/Program.cs
index cca8fe9..df525e4 100644
--- a/Seasoned.Backend/Program.cs
+++ b/Seasoned.Backend/Program.cs
@@ -5,6 +5,9 @@ using Microsoft.EntityFrameworkCore;
using Seasoned.Backend.Data;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
+using DotNetEnv;
+
+Env.Load("../.env");
var builder = WebApplication.CreateBuilder(args);
@@ -49,7 +52,7 @@ var app = builder.Build();
using (var scope = app.Services.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService();
- db.Database.EnsureCreated();
+ db.Database.Migrate();
}
app.UseDefaultFiles();
diff --git a/Seasoned.Backend/Seasoned.Backend.csproj b/Seasoned.Backend/Seasoned.Backend.csproj
index a2bc6df..d2af8a4 100644
--- a/Seasoned.Backend/Seasoned.Backend.csproj
+++ b/Seasoned.Backend/Seasoned.Backend.csproj
@@ -8,6 +8,8 @@
+
+
diff --git a/Seasoned.Backend/Services/RecipeService.cs b/Seasoned.Backend/Services/RecipeService.cs
index 25396a4..64eecd5 100644
--- a/Seasoned.Backend/Services/RecipeService.cs
+++ b/Seasoned.Backend/Services/RecipeService.cs
@@ -42,12 +42,12 @@ public class RecipeService : IRecipeService
""instructions"": [""string"", ""string""]
}";
- var config = new GenerationConfig {
+ var generationConfig = new GenerationConfig {
ResponseMimeType = "application/json",
Temperature = 0.1f
};
- var request = new GenerateContentRequest(prompt, config);
+ var request = new GenerateContentRequest(prompt, generationConfig);
await Task.Run(() => request.AddMedia(base64Image, image.ContentType ?? "image/png"));
diff --git a/Seasoned.Frontend/app/pages/gallery.vue b/Seasoned.Frontend/app/pages/gallery.vue
index 530948f..2df87f9 100644
--- a/Seasoned.Frontend/app/pages/gallery.vue
+++ b/Seasoned.Frontend/app/pages/gallery.vue
@@ -197,25 +197,17 @@ const showDetails = ref(false)
const selectedRecipe = ref(null)
const isEditing = ref(false)
const originalRecipe = ref(null)
+const config = useRuntimeConfig()
onMounted(async () => {
await fetchRecipes()
})
const fetchRecipes = async () => {
- const token = useCookie('seasoned_token').value
- || (import.meta.client ? localStorage.getItem('token') : null)
-
- if (!token) {
- return navigateTo('/login')
- }
-
try {
loading.value = true
const data = await $fetch(`${config.public.apiBase}api/recipe/my-collection`, {
- headers: {
- 'Authorization': `Bearer ${token}`
- }
+ credentials: 'include'
})
recipes.value = data
} catch (err) {
@@ -234,34 +226,38 @@ const openRecipe = (recipe) => {
}
const editRecipe = (recipe) => {
+ originalRecipe.value = JSON.parse(JSON.stringify(recipe))
selectedRecipe.value = { ...recipe }
- originalRecipe.value = { ...recipe }
isEditing.value = true
showDetails.value = true
}
const closeDetails = () => {
+ if (isEditing.value && originalRecipe.value) {
+ const index = recipes.value.findIndex(r => r.id === originalRecipe.value.id)
+ if (index !== -1) {
+ recipes.value[index] = originalRecipe.value
+ }
+ }
+
showDetails.value = false
isEditing.value = false
+ originalRecipe.value = null
}
const saveChanges = async () => {
- const token = useCookie('seasoned_token').value
-
- try {
+ try {
await $fetch(`${config.public.apiBase}api/recipe/update/${selectedRecipe.value.id}`, {
method: 'PUT',
- headers: { 'Authorization': `Bearer ${token}` },
+ credentials: 'include',
body: selectedRecipe.value
})
await fetchRecipes()
-
- isEditing.value = false
- showDetails.value = false
+ closeDetails()
} catch (e) {
console.error("Failed to update recipe:", e)
- alert("Could not save changes. Please try again.")
+ alert("Could not save changes. Your session might have expired.")
}
}
@@ -297,12 +293,13 @@ const saveChanges = async () => {
//showDetails.value = false
//}
-const getRecipeIcon = (title) => {
- const t = title.toLowerCase()
+const getRecipeIcon = (recipe) => {
if (recipe.icon) return recipe.icon
+ const t = (recipe.title || '').toLowerCase()
if (t.includes('cake') || t.includes('cookie') || t.includes('dessert')) return 'mdi-cookie'
if (t.includes('soup') || t.includes('stew')) return 'mdi-bowl-mix'
if (t.includes('drink') || t.includes('cocktail')) return 'mdi-glass-cocktail'
+
return 'mdi-silverware-fork-knife'
}
\ No newline at end of file
diff --git a/Seasoned.Frontend/app/pages/index.vue b/Seasoned.Frontend/app/pages/index.vue
index cf18cfc..9f158b7 100644
--- a/Seasoned.Frontend/app/pages/index.vue
+++ b/Seasoned.Frontend/app/pages/index.vue
@@ -144,11 +144,15 @@ const isDragging = ref(false)
const saving = ref(false)
const hasSaved = ref(false)
-const isAuthenticated = () => {
- if (import.meta.client) {
- return !!localStorage.getItem('token');
+const isAuthenticated = async () => {
+ try {
+ await $fetch('/api/auth/manage/info', {
+ credentials: 'include'
+ })
+ return true
+ } catch {
+ return false
}
- return false;
}
const handleViewCollection = () => {
@@ -196,7 +200,6 @@ const uploadImage = async () => {
const saveToCollection = async () => {
if (!recipe.value || hasSaved.value) return;
- // 1. Get the token (same logic as gallery)
const token = useCookie('seasoned_token').value
|| (import.meta.client ? localStorage.getItem('token') : null)
@@ -210,9 +213,6 @@ const saveToCollection = async () => {
try {
await $fetch(`${config.public.apiBase}api/recipe/save`, {
method: 'POST',
- headers: {
- 'Authorization': `Bearer ${token}`
- },
body: recipe.value
});
hasSaved.value = true;
diff --git a/Seasoned.Frontend/app/pages/login.vue b/Seasoned.Frontend/app/pages/login.vue
index 3b9b837..0d1b3bc 100644
--- a/Seasoned.Frontend/app/pages/login.vue
+++ b/Seasoned.Frontend/app/pages/login.vue
@@ -72,7 +72,7 @@ const handleAuth = async () => {
const endpoint = isLogin.value ? 'api/auth/login' : 'api/auth/register'
const url = isLogin.value
- ? `${config.public.apiBase}${endpoint}?useCookies=false`
+ ? `${config.public.apiBase}${endpoint}?useCookies=true`
: `${config.public.apiBase}${endpoint}`
try {
@@ -80,33 +80,16 @@ const handleAuth = async () => {
method: 'POST',
body: {
email: email.value,
- userName: email.value,
password: password.value
}
})
if (isLogin.value) {
- if (response.accessToken) {
-
- const tokenCookie = useCookie('seasoned_token', { maxAge: response.expiresIn })
- tokenCookie.value = response.accessToken
-
- if (import.meta.client) {
- localStorage.setItem('token', response.accessToken)
- }
-
- navigateTo('/gallery')
- }
- } else {
- alert("Account created successfully! Please sign in to open your ledger.")
- isLogin.value = true
+ navigateTo('/gallery')
+
}
} catch (err) {
- const errorDetail = err.data?.errors
- ? Object.values(err.data.errors).flat().join('\n')
- : "Check your credentials and try again."
-
- alert(`Authentication failed:\n${errorDetail}`)
+ alert("Authentication failed. Check your credentials.")
}
}
\ No newline at end of file