init go project

This commit is contained in:
juancwu 2025-10-10 08:14:33 -04:00
commit 5dde43e409
85 changed files with 16720 additions and 0 deletions

48
internal/db/db.go Normal file
View file

@ -0,0 +1,48 @@
package db
import (
"fmt"
"log/slog"
"os"
"path/filepath"
"time"
_ "github.com/jackc/pgx/v5/stdlib"
"github.com/jmoiron/sqlx"
_ "modernc.org/sqlite"
)
func Init(driver, connection string) (*sqlx.DB, error) {
if driver == "sqlite" {
dir := filepath.Dir(connection)
err := os.MkdirAll(dir, 0755)
if err != nil {
return nil, fmt.Errorf("failed to create data directory: %w", err)
}
}
db, err := sqlx.Connect(driver, connection)
if err != nil {
return nil, fmt.Errorf("failed to connect: %w", err)
}
db.SetMaxOpenConns(25)
db.SetMaxIdleConns(5)
db.SetConnMaxLifetime(5 * time.Minute)
slog.Info("database connected", "driver", driver)
err = db.Ping()
if err != nil {
return nil, fmt.Errorf("failed to ping database: %w", err)
}
return db, nil
}
func Close(db *sqlx.DB) error {
if db != nil {
return db.Close()
}
return nil
}

6
internal/db/embed.go Normal file
View file

@ -0,0 +1,6 @@
package db
import "embed"
//go:embed migrations/*.sql
var migrationsFS embed.FS

55
internal/db/migrate.go Normal file
View file

@ -0,0 +1,55 @@
package db
import (
"database/sql"
"fmt"
"io/fs"
"log/slog"
"github.com/pressly/goose/v3"
)
var dialectMap = map[string]string{
"sqlite": "sqlite3",
"pgx": "postgres",
}
func getDialect(driver string) string {
dialect, ok := dialectMap[driver]
if ok {
return dialect
}
return driver
}
func setupGoose(driver string) error {
err := goose.SetDialect(getDialect(driver))
if err != nil {
return fmt.Errorf("failed to set dialect: %w", err)
}
migrationsDir, err := fs.Sub(migrationsFS, "migrations")
if err != nil {
return fmt.Errorf("failed to create sub-filesystem migrations directory: %w", err)
}
goose.SetBaseFS(migrationsDir)
return nil
}
func RunMigrations(db *sql.DB, driver string) error {
err := setupGoose(driver)
if err != nil {
return err
}
err = goose.Up(db, ".")
if err != nil {
return fmt.Errorf("failed to run migrations: %w", err)
}
slog.Info("migrations completed successfully")
return nil
}

View file

@ -0,0 +1,21 @@
-- +goose Up
-- +goose StatementBegin
CREATE TABLE IF NOT EXISTS users (
id TEXT PRIMARY KEY,
email TEXT UNIQUE NOT NULL,
password_hash TEXT NULL, -- Allow null for passwordless login
pending_email TEXT NULL, -- Store new email when changing email
email_verified_at TIMESTAMP NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
CREATE INDEX IF NOT EXISTS idx_users_passwordless ON users(id) WHERE password_hash IS NULL;
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
DROP INDEX IF EXISTS idx_users_passwordless;
DROP INDEX IF EXISTS idx_users_email;
DROP TABLE IF EXISTS users;
-- +goose StatementEnd