feat: added database migration on startup and user+list db migrations
This commit is contained in:
@@ -69,6 +69,10 @@ func run(serviceName string, serviceVersion string) error {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
if err := database.RunMigrations(db); err != nil {
|
||||||
|
slog.Error("Failed to run migrations", slog.Any("error", err))
|
||||||
|
}
|
||||||
|
|
||||||
srv := makeServer(db, cfg.Port)
|
srv := makeServer(db, cfg.Port)
|
||||||
go func() {
|
go func() {
|
||||||
slog.Info("Starting http server", "addr", srv.Addr)
|
slog.Info("Starting http server", "addr", srv.Addr)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ module github.com/robindittmar/dttmr-api
|
|||||||
go 1.26
|
go 1.26
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/golang-migrate/migrate/v4 v4.19.1
|
||||||
github.com/jackc/pgx/v5 v5.10.0
|
github.com/jackc/pgx/v5 v5.10.0
|
||||||
github.com/joho/godotenv v1.5.1
|
github.com/joho/godotenv v1.5.1
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0
|
||||||
@@ -25,6 +26,7 @@ require (
|
|||||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||||
|
github.com/lib/pq v1.10.9 // indirect
|
||||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0 // indirect
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0 // indirect
|
||||||
go.opentelemetry.io/otel/metric v1.44.0 // indirect
|
go.opentelemetry.io/otel/metric v1.44.0 // indirect
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"embed"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
|
"github.com/golang-migrate/migrate/v4"
|
||||||
|
"github.com/golang-migrate/migrate/v4/database/postgres"
|
||||||
|
"github.com/golang-migrate/migrate/v4/source/iofs"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed migrations/*.sql
|
||||||
|
var migrationFS embed.FS
|
||||||
|
|
||||||
|
func RunMigrations(db *sql.DB) error {
|
||||||
|
sourceDriver, err := iofs.New(migrationFS, "migrations")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to load embedded migrations: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dbDriver, err := postgres.WithInstance(db, &postgres.Config{})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create migration db driver: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
m, err := migrate.NewWithInstance("iofs", sourceDriver, "postgres", dbDriver)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to initialize migrator: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
slog.Info("Running database migrations...")
|
||||||
|
err = m.Up()
|
||||||
|
|
||||||
|
if err != nil && !errors.Is(err, migrate.ErrNoChange) {
|
||||||
|
return fmt.Errorf("failed to run database migrations: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
slog.Info("Database migrations applied successfully")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
DROP TABLE IF EXISTS sessions;
|
||||||
|
DROP TABLE IF EXISTS users;
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS users (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
email VARCHAR(255) UNIQUE NOT NULL,
|
||||||
|
name VARCHAR(255) NOT NULL,
|
||||||
|
password_hash VARCHAR(255) NOT NULL,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS sessions (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||||
|
token_id UUID UNIQUE NOT NULL,
|
||||||
|
is_revoked BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
expires_at TIMESTAMPTZ NOT NULL,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||||
|
);
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
DROP TABLE IF EXISTS list_items;
|
||||||
|
DROP TABLE IF EXISTS list_users;
|
||||||
|
DROP TABLE IF EXISTS lists;
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS lists (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
name VARCHAR(255) NOT NULL,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
modified_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS list_users (
|
||||||
|
list_id UUID REFERENCES lists(id) ON DELETE CASCADE,
|
||||||
|
user_id UUID NOT NULL,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
PRIMARY KEY (list_id, user_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_list_users_user_id ON list_users(user_id);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS list_items (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
list_id UUID REFERENCES lists(id) ON DELETE CASCADE,
|
||||||
|
title VARCHAR(255) NOT NULL,
|
||||||
|
is_completed BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
modified_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_list_items_list_id ON list_items(list_id);
|
||||||
Reference in New Issue
Block a user