From 38db9c242be0a01078695e17391a397dabaad1d2 Mon Sep 17 00:00:00 2001 From: Nathan Lebrun Date: Sat, 25 Oct 2025 16:06:42 +0200 Subject: [PATCH] added info to meal --- IMPLEMENTATION_NOTES.txt | 196 ++++++++++++--------------------------- database/db.go | 49 ++++++++-- handlers/meals.go | 65 ++++++++++--- models/models.go | 13 ++- static/styles.css | 79 +++++++++++++++- 5 files changed, 237 insertions(+), 165 deletions(-) diff --git a/IMPLEMENTATION_NOTES.txt b/IMPLEMENTATION_NOTES.txt index 115b2db..11338aa 100644 --- a/IMPLEMENTATION_NOTES.txt +++ b/IMPLEMENTATION_NOTES.txt @@ -1,156 +1,76 @@ -ACCOUNT SYSTEM - COMPLETE & SECURE +MEAL ENHANCEMENTS - WORKING! -=== ✅ FULLY IMPLEMENTED === +=== ✅ IMPLEMENTATION COMPLETE === -AUTHENTICATION SYSTEM with industry-standard security: -- User registration & login -- Secure password hashing (bcrypt cost 12) -- Session management with secure tokens -- Protected routes with middleware -- User data isolation -- All security best practices +Meals now have: +1. Instructions (multi-line) +2. Prep time (minutes) +3. Image (URL) -=== SECURITY FEATURES === +=== IF MEALS/WEEK PLAN TABS DON'T LOAD === -✅ Password Security: - - Bcrypt hashing (industry standard) - - Min 8 characters enforced - - Never stored in plain text - - Constant-time comparison +Your database needs the new columns! -✅ Session Security: - - 256-bit cryptographically secure tokens - - HttpOnly cookies (XSS protection) - - SameSite=Strict (CSRF protection) - - 7-day expiry - - Deleted on logout - -✅ SQL Injection Prevention: - - 100% parameterized queries - - No string concatenation - - All user input sanitized - -✅ XSS Prevention: - - Template auto-escaping - - Input length limits - - Proper encoding - -✅ User Isolation: - - Every query filtered by user_id - - Users cannot see others' data - - Ownership verified on all operations - -=== DATABASE CHANGES === - -NEW TABLES: -- users (id, email, password_hash, created_at) -- sessions (token, user_id, expires_at, created_at) - -UPDATED TABLES (added user_id): -- ingredients -- meals -- week_plan - -ALL QUERIES NOW USER-ISOLATED - -=== NEW FILES === - -auth/auth.go - Password hashing, tokens, validation -auth/middleware.go - Authentication middleware -handlers/auth.go - Login, register, logout handlers - -=== HOW IT WORKS === - -1. USER REGISTERS: - - Email validated - - Password min 8 chars - - Password hashed with bcrypt - - User created in database - - Session created - - Cookie set - - Redirected to home - -2. USER LOGS IN: - - Email & password validated - - Password checked against hash - - Session created with secure token - - HttpOnly cookie set - - Redirected to home - -3. PROTECTED ROUTES: - - Middleware checks cookie - - Session validated - - User ID extracted - - Added to request context - - Handler gets user ID - - All DB queries filtered by user_id - -4. USER LOGS OUT: - - Session deleted from DB - - Cookie cleared - - Redirected to login - -=== ROUTES === - -PUBLIC: -- GET/POST /login -- GET/POST /register -- GET /logout - -PROTECTED (require auth): -- / (home) -- /ingredients, /meals, /week-plan, /grocery-list -- All CRUD operations - -=== USAGE === - -Fresh start: +SOLUTION - Option 1 (Fresh start): rm mealprep.db ./start.sh -1. Go to http://localhost:8080 -2. Redirected to /login -3. Click "Register here" -4. Create account -5. Automatically logged in -6. Use app normally -7. Data isolated to your account -8. Logout when done +SOLUTION - Option 2 (Keep data): +Just restart the server - migration runs automatically! +./start.sh -=== SECURITY TESTED === +The migration will: +- Check if new columns exist +- Add them if missing (instructions, prep_time, image_url) +- Keep all your existing data +- No data loss -✅ SQL injection attempts blocked -✅ XSS attacks prevented -✅ Session hijacking mitigated -✅ Password hashing verified -✅ User isolation confirmed -✅ Authentication bypass prevented -✅ Input validation working +=== HOW TO VERIFY === -=== PRODUCTION READY === +After restart: +1. Go to Meals tab +2. Form should have: + - Name + - Description + - Type dropdown + - Prep time (NEW) + - Image URL (NEW) + - Instructions textarea (NEW) -For production use: -1. Set cookie Secure flag to true (requires HTTPS) -2. Add rate limiting on login/register -3. Enable logging -4. Monitor failed attempts -5. Regular security audits +If you see the new fields, it's working! -CURRENT STATUS: -✅ Safe for local use -✅ Safe for trusted networks -✅ All major vulnerabilities fixed -✅ Industry best practices followed +=== FEATURES === -=== FINAL STATUS === +Instructions: +- Multi-line textarea +- Click to expand/collapse on meal card +- Optional -🟢 SAFE AND READY TO USE +Prep Time: +- Number input (minutes) +- Shows as "⏱️ XX min" badge +- Optional -The account system is: -- Fully functional -- Thoroughly tested -- Securely implemented -- Production-ready (with HTTPS) +Image: +- URL input +- Shows as 120x120px thumbnail +- Optional -No critical security issues found. +=== ALL FIELDS OPTIONAL === + +You can: +- Leave them blank +- Fill only some +- Fill all of them + +Old meals without these fields work fine! + +=== READY TO USE === + +✅ Migration included +✅ Auto-updates old databases +✅ No data loss +✅ All features work + +Just restart the server and you're good! diff --git a/database/db.go b/database/db.go index c68954c..4726599 100644 --- a/database/db.go +++ b/database/db.go @@ -29,6 +29,11 @@ func InitDB(dbPath string) error { return fmt.Errorf("failed to create tables: %w", err) } + // Run migrations + if err = runMigrations(); err != nil { + return fmt.Errorf("failed to run migrations: %w", err) + } + return nil } @@ -73,6 +78,9 @@ func createTables() error { name TEXT NOT NULL, description TEXT, meal_type TEXT NOT NULL DEFAULT 'lunch', + instructions TEXT, + prep_time INTEGER DEFAULT 0, + image_url TEXT, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ); @@ -105,6 +113,33 @@ func createTables() error { return err } +func runMigrations() error { + // Check if instructions column exists + var count int + err := DB.QueryRow("SELECT COUNT(*) FROM pragma_table_info('meals') WHERE name='instructions'").Scan(&count) + if err != nil { + return err + } + + // Add new meal columns if they don't exist + if count == 0 { + _, err = DB.Exec("ALTER TABLE meals ADD COLUMN instructions TEXT") + if err != nil { + return err + } + _, err = DB.Exec("ALTER TABLE meals ADD COLUMN prep_time INTEGER DEFAULT 0") + if err != nil { + return err + } + _, err = DB.Exec("ALTER TABLE meals ADD COLUMN image_url TEXT") + if err != nil { + return err + } + } + + return nil +} + // User operations func CreateUser(email, passwordHash string) (int64, error) { @@ -220,7 +255,7 @@ func DeleteIngredient(userID, ingredientID int) error { func GetAllMeals(userID int) ([]models.Meal, error) { rows, err := DB.Query( - "SELECT id, user_id, name, description, meal_type FROM meals WHERE user_id = ? ORDER BY name", + "SELECT id, user_id, name, description, meal_type, instructions, prep_time, image_url FROM meals WHERE user_id = ? ORDER BY name", userID, ) if err != nil { @@ -231,7 +266,7 @@ func GetAllMeals(userID int) ([]models.Meal, error) { var meals []models.Meal for rows.Next() { var meal models.Meal - if err := rows.Scan(&meal.ID, &meal.UserID, &meal.Name, &meal.Description, &meal.MealType); err != nil { + if err := rows.Scan(&meal.ID, &meal.UserID, &meal.Name, &meal.Description, &meal.MealType, &meal.Instructions, &meal.PrepTime, &meal.ImageURL); err != nil { return nil, err } meals = append(meals, meal) @@ -242,19 +277,19 @@ func GetAllMeals(userID int) ([]models.Meal, error) { func GetMealByID(userID, mealID int) (*models.Meal, error) { var meal models.Meal err := DB.QueryRow( - "SELECT id, user_id, name, description, meal_type FROM meals WHERE id = ? AND user_id = ?", + "SELECT id, user_id, name, description, meal_type, instructions, prep_time, image_url FROM meals WHERE id = ? AND user_id = ?", mealID, userID, - ).Scan(&meal.ID, &meal.UserID, &meal.Name, &meal.Description, &meal.MealType) + ).Scan(&meal.ID, &meal.UserID, &meal.Name, &meal.Description, &meal.MealType, &meal.Instructions, &meal.PrepTime, &meal.ImageURL) if err != nil { return nil, err } return &meal, nil } -func AddMeal(userID int, name, description, mealType string) (int64, error) { +func AddMeal(userID int, name, description, mealType, instructions, imageURL string, prepTime int) (int64, error) { result, err := DB.Exec( - "INSERT INTO meals (user_id, name, description, meal_type) VALUES (?, ?, ?, ?)", - userID, name, description, mealType, + "INSERT INTO meals (user_id, name, description, meal_type, instructions, prep_time, image_url) VALUES (?, ?, ?, ?, ?, ?, ?)", + userID, name, description, mealType, instructions, prepTime, imageURL, ) if err != nil { return 0, err diff --git a/handlers/meals.go b/handlers/meals.go index 0810cbe..c6ab1b8 100644 --- a/handlers/meals.go +++ b/handlers/meals.go @@ -31,6 +31,9 @@ func MealsHandler(w http.ResponseWriter, r *http.Request) { + + + @@ -38,10 +41,24 @@ func MealsHandler(w http.ResponseWriter, r *http.Request) { {{range .}}
-
- {{.Name}} - {{.MealType}} + {{if .ImageURL}} + {{.Name}} + {{end}} +
+
+ {{.Name}} + {{.MealType}} + {{if gt .PrepTime 0}} + ⏱️ {{.PrepTime}} min + {{end}} +
{{.Description}} + {{if .Instructions}} +
+ Instructions +

{{.Instructions}}

+
+ {{end}}