From b3355831d868962377eba9b740b5906196c0f6d5 Mon Sep 17 00:00:00 2001 From: chloe Date: Wed, 11 Mar 2026 18:58:55 +0000 Subject: [PATCH] Switch from token to cookie auth --- ...0260311160009_AddRecipeFields.Designer.cs} | 7 ++-- ...e.cs => 20260311160009_AddRecipeFields.cs} | 4 +- .../ApplicationDbContextModelSnapshot.cs | 3 +- Seasoned.Backend/Program.cs | 5 ++- Seasoned.Backend/Seasoned.Backend.csproj | 2 + Seasoned.Backend/Services/RecipeService.cs | 4 +- Seasoned.Frontend/app/pages/gallery.vue | 39 +++++++++---------- Seasoned.Frontend/app/pages/index.vue | 16 ++++---- Seasoned.Frontend/app/pages/login.vue | 25 ++---------- 9 files changed, 46 insertions(+), 59 deletions(-) rename Seasoned.Backend/Migrations/{20260306062007_InitialCreate.Designer.cs => 20260311160009_AddRecipeFields.Designer.cs} (98%) rename Seasoned.Backend/Migrations/{20260306062007_InitialCreate.cs => 20260311160009_AddRecipeFields.cs} (98%) 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