package models import ( "context" "database/sql" "errors" "fmt" "git.eeqj.de/sneak/upaas/internal/database" ) // Volume represents a volume mount for an app container. type Volume struct { db *database.Database ID int64 AppID string HostPath string ContainerPath string ReadOnly bool } // NewVolume creates a new Volume with a database reference. func NewVolume(db *database.Database) *Volume { return &Volume{db: db} } // Save inserts or updates the volume in the database. func (v *Volume) Save(ctx context.Context) error { if v.ID == 0 { return v.insert(ctx) } return v.update(ctx) } // Delete removes the volume from the database. func (v *Volume) Delete(ctx context.Context) error { _, err := v.db.Exec(ctx, "DELETE FROM app_volumes WHERE id = ?", v.ID) return err } func (v *Volume) insert(ctx context.Context) error { query := ` INSERT INTO app_volumes (app_id, host_path, container_path, readonly) VALUES (?, ?, ?, ?)` result, err := v.db.Exec(ctx, query, v.AppID, v.HostPath, v.ContainerPath, v.ReadOnly, ) if err != nil { return err } id, err := result.LastInsertId() if err != nil { return err } v.ID = id return nil } func (v *Volume) update(ctx context.Context) error { query := ` UPDATE app_volumes SET host_path = ?, container_path = ?, readonly = ? WHERE id = ?` _, err := v.db.Exec(ctx, query, v.HostPath, v.ContainerPath, v.ReadOnly, v.ID) return err } // FindVolume finds a volume by ID. // //nolint:nilnil // returning nil,nil is idiomatic for "not found" in Active Record func FindVolume( ctx context.Context, db *database.Database, id int64, ) (*Volume, error) { vol := NewVolume(db) query := ` SELECT id, app_id, host_path, container_path, readonly FROM app_volumes WHERE id = ?` row := db.QueryRow(ctx, query, id) err := row.Scan( &vol.ID, &vol.AppID, &vol.HostPath, &vol.ContainerPath, &vol.ReadOnly, ) if err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, nil } return nil, fmt.Errorf("scanning volume: %w", err) } return vol, nil } // FindVolumesByAppID finds all volumes for an app. func FindVolumesByAppID( ctx context.Context, db *database.Database, appID string, ) ([]*Volume, error) { query := ` SELECT id, app_id, host_path, container_path, readonly FROM app_volumes WHERE app_id = ? ORDER BY container_path` rows, err := db.Query(ctx, query, appID) if err != nil { return nil, fmt.Errorf("querying volumes by app: %w", err) } defer func() { _ = rows.Close() }() var volumes []*Volume for rows.Next() { vol := NewVolume(db) scanErr := rows.Scan( &vol.ID, &vol.AppID, &vol.HostPath, &vol.ContainerPath, &vol.ReadOnly, ) if scanErr != nil { return nil, scanErr } volumes = append(volumes, vol) } return volumes, rows.Err() } // DeleteVolumesByAppID deletes all volumes for an app. func DeleteVolumesByAppID( ctx context.Context, db *database.Database, appID string, ) error { _, err := db.Exec(ctx, "DELETE FROM app_volumes WHERE app_id = ?", appID) return err }