From e82a39f2e4507f968bf1747723c374f2ddfba1f7 Mon Sep 17 00:00:00 2001 From: James Griffin Date: Wed, 8 Apr 2026 14:58:58 -0300 Subject: [PATCH] Fix build and run issues --- Dockerfile | 2 +- Taskfile.yml | 12 ++++++------ internal/db/db.go | 7 ++++++- internal/db/schema.go | 24 ++++++++++++------------ internal/notification/notification.go | 8 ++++---- web/src/api.ts | 2 +- web/src/pages/Dashboard.tsx | 8 ++++---- 7 files changed, 34 insertions(+), 29 deletions(-) diff --git a/Dockerfile b/Dockerfile index baa1c30..c7a3283 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ FROM node:22-alpine AS frontend WORKDIR /app/web COPY web/package*.json ./ -RUN npm ci +RUN npm install COPY web/ ./ RUN npm run build diff --git a/Taskfile.yml b/Taskfile.yml index 493c863..d0bee28 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -104,16 +104,16 @@ tasks: cmd: docker build -t walkies . docker:up: - desc: Build and start with docker-compose - cmd: docker-compose up --build + desc: Build and start with docker compose + cmd: docker compose up --build docker:down: - desc: Stop docker-compose services - cmd: docker-compose down + desc: Stop docker compose services + cmd: docker compose down docker:logs: - desc: Tail docker-compose logs - cmd: docker-compose logs -f + desc: Tail docker compose logs + cmd: docker compose logs -f # ── Utilities ──────────────────────────────────────────────────────────────── diff --git a/internal/db/db.go b/internal/db/db.go index 06c9a98..ad85461 100644 --- a/internal/db/db.go +++ b/internal/db/db.go @@ -3,9 +3,10 @@ package db import ( "context" "database/sql" + "errors" "fmt" - _ "github.com/go-sql-driver/mysql" + "github.com/go-sql-driver/mysql" ) func Open(dsn string) (*sql.DB, error) { @@ -22,6 +23,10 @@ func Open(dsn string) (*sql.DB, error) { func Migrate(ctx context.Context, db *sql.DB) error { for _, stmt := range statements { if _, err := db.ExecContext(ctx, stmt); err != nil { + var mysqlErr *mysql.MySQLError + if errors.As(err, &mysqlErr) && mysqlErr.Number == 1060 { + continue // duplicate column — already exists + } return fmt.Errorf("migrate: %w", err) } } diff --git a/internal/db/schema.go b/internal/db/schema.go index 6c228a1..0c33f41 100644 --- a/internal/db/schema.go +++ b/internal/db/schema.go @@ -10,7 +10,7 @@ var statements = []string{ active TINYINT NOT NULL DEFAULT 1, is_trainee TINYINT NOT NULL DEFAULT 0, phone VARCHAR(20) NULL, - operational_roles TEXT NOT NULL DEFAULT '', + operational_roles TEXT NOT NULL, notification_preference VARCHAR(50) NOT NULL DEFAULT 'email', admin_notes TEXT NULL, last_login DATETIME NULL, @@ -19,15 +19,15 @@ var statements = []string{ created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci`, - // Additive column migrations for existing deployments - `ALTER TABLE volunteers ADD COLUMN IF NOT EXISTS is_trainee TINYINT NOT NULL DEFAULT 0`, - `ALTER TABLE volunteers ADD COLUMN IF NOT EXISTS phone VARCHAR(20) NULL`, - `ALTER TABLE volunteers ADD COLUMN IF NOT EXISTS operational_roles TEXT NOT NULL DEFAULT ''`, - `ALTER TABLE volunteers ADD COLUMN IF NOT EXISTS notification_preference VARCHAR(50) NOT NULL DEFAULT 'email'`, - `ALTER TABLE volunteers ADD COLUMN IF NOT EXISTS admin_notes TEXT NULL`, - `ALTER TABLE volunteers ADD COLUMN IF NOT EXISTS last_login DATETIME NULL`, - `ALTER TABLE volunteers ADD COLUMN IF NOT EXISTS invite_token VARCHAR(255) NULL`, - `ALTER TABLE volunteers ADD COLUMN IF NOT EXISTS invite_expires_at DATETIME NULL`, + // Additive column migrations for existing deployments (duplicates ignored at runtime) + `ALTER TABLE volunteers ADD COLUMN is_trainee TINYINT NOT NULL DEFAULT 0`, + `ALTER TABLE volunteers ADD COLUMN phone VARCHAR(20) NULL`, + `ALTER TABLE volunteers ADD COLUMN operational_roles TEXT NOT NULL`, + `ALTER TABLE volunteers ADD COLUMN notification_preference VARCHAR(50) NOT NULL DEFAULT 'email'`, + `ALTER TABLE volunteers ADD COLUMN admin_notes TEXT NULL`, + `ALTER TABLE volunteers ADD COLUMN last_login DATETIME NULL`, + `ALTER TABLE volunteers ADD COLUMN invite_token VARCHAR(255) NULL`, + `ALTER TABLE volunteers ADD COLUMN invite_expires_at DATETIME NULL`, `CREATE TABLE IF NOT EXISTS schedules ( id INT AUTO_INCREMENT PRIMARY KEY, volunteer_id INT NOT NULL, @@ -72,11 +72,11 @@ var statements = []string{ id INT AUTO_INCREMENT PRIMARY KEY, volunteer_id INT NOT NULL, message TEXT NOT NULL, - read TINYINT NOT NULL DEFAULT 0, + is_read TINYINT NOT NULL DEFAULT 0, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (volunteer_id) REFERENCES volunteers(id) ON DELETE CASCADE, INDEX idx_volunteer_id (volunteer_id), - INDEX idx_read (read) + INDEX idx_is_read (is_read) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci`, `CREATE TABLE IF NOT EXISTS shift_templates ( id INT AUTO_INCREMENT PRIMARY KEY, diff --git a/internal/notification/notification.go b/internal/notification/notification.go index bb7e8db..6533392 100644 --- a/internal/notification/notification.go +++ b/internal/notification/notification.go @@ -14,7 +14,7 @@ type Notification struct { ID int64 `json:"id"` VolunteerID int64 `json:"volunteer_id"` Message string `json:"message"` - Read bool `json:"read"` + Read bool `json:"is_read"` CreatedAt time.Time `json:"created_at"` } @@ -42,7 +42,7 @@ func (s *Store) GetByID(ctx context.Context, id int64) (*Notification, error) { n := &Notification{} var createdAt string err := s.db.QueryRowContext(ctx, - `SELECT id, volunteer_id, message, read, created_at FROM notifications WHERE id = ?`, id, + `SELECT id, volunteer_id, message, is_read, created_at FROM notifications WHERE id = ?`, id, ).Scan(&n.ID, &n.VolunteerID, &n.Message, &n.Read, &createdAt) if errors.Is(err, sql.ErrNoRows) { return nil, ErrNotFound @@ -56,7 +56,7 @@ func (s *Store) GetByID(ctx context.Context, id int64) (*Notification, error) { func (s *Store) ListForVolunteer(ctx context.Context, volunteerID int64) ([]Notification, error) { rows, err := s.db.QueryContext(ctx, - `SELECT id, volunteer_id, message, read, created_at FROM notifications WHERE volunteer_id = ? ORDER BY created_at DESC`, + `SELECT id, volunteer_id, message, is_read, created_at FROM notifications WHERE volunteer_id = ? ORDER BY created_at DESC`, volunteerID, ) if err != nil { @@ -85,7 +85,7 @@ func (s *Store) CreateNotification(ctx context.Context, volunteerID int64, messa func (s *Store) MarkRead(ctx context.Context, id, volunteerID int64) (*Notification, error) { result, err := s.db.ExecContext(ctx, - `UPDATE notifications SET read = 1 WHERE id = ? AND volunteer_id = ?`, + `UPDATE notifications SET is_read = 1 WHERE id = ? AND volunteer_id = ?`, id, volunteerID, ) if err != nil { diff --git a/web/src/api.ts b/web/src/api.ts index 36dfe4b..a9bb6e8 100644 --- a/web/src/api.ts +++ b/web/src/api.ts @@ -213,7 +213,7 @@ export interface Notification { id: number; volunteer_id: number; message: string; - read: boolean; + is_read: boolean; created_at: string; } diff --git a/web/src/pages/Dashboard.tsx b/web/src/pages/Dashboard.tsx index 5e6e74f..f51bbd4 100644 --- a/web/src/pages/Dashboard.tsx +++ b/web/src/pages/Dashboard.tsx @@ -44,7 +44,7 @@ export default function Dashboard() { async function handleMarkRead(id: number) { try { await api.markRead(id); - setNotifications(prev => prev.map(n => n.id === id ? { ...n, read: true } : n)); + setNotifications(prev => prev.map(n => n.id === id ? { ...n, is_read: true } : n)); } catch {} } @@ -52,7 +52,7 @@ export default function Dashboard() { .filter(s => new Date(s.date) >= now) .slice(0, 5); - const unreadNotifications = notifications.filter(n => !n.read); + const unreadNotifications = notifications.filter(n => !n.is_read); return (
@@ -93,9 +93,9 @@ export default function Dashboard() { ) : (