mirror of
https://github.com/go-shiori/shiori.git
synced 2025-09-29 16:25:58 +08:00
* refactor: base http server stdlib * refactor: swagger and frontend routes * fix: use global middlewares * refactor: removed gin from testutils * fix: object references in legacy webserver * refactor: legacy, swagger and system handlers * fix: added verbs to handlers * fix: server handlers ordering * refactor: bookmarks handlers * refactor: system api routes * tests: bookmark handlers * refactor: migrated api auth routes * chore: remove unused middlewares * docs: add swagger docs to refactored system api * chore: remove old auth routes * refactor: account apis * chore: removed old handlers * fix: api v1 handlers missing middlewares * refactor: migrated tag list route * refactor: bookmark routes * refactor: remove gin * chore: make styles * test: fixed tests * test: generate binary file without text * fix: global middleware missing from system api handler * fix: incorrect api handler * chore: avoid logging screenshot contents * tests: bookmarks domain * tests: shortcuts * test: missing tests * tests: server tests * test: remove test using syscall to avoid windows errors * chore: added middlewares
100 lines
2.8 KiB
Go
100 lines
2.8 KiB
Go
// Package database implements database operations and migrations
|
|
package database
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"embed"
|
|
"fmt"
|
|
"path"
|
|
|
|
"github.com/blang/semver"
|
|
"github.com/go-shiori/shiori/internal/model"
|
|
)
|
|
|
|
//go:embed migrations/*
|
|
var migrationFiles embed.FS
|
|
|
|
// migration represents a database schema migration
|
|
type migration struct {
|
|
fromVersion semver.Version
|
|
toVersion semver.Version
|
|
migrationFunc func(db *sql.DB) error
|
|
}
|
|
|
|
// txFn is a function that runs in a transaction.
|
|
type txFn func(tx *sql.Tx) error
|
|
|
|
// runInTransaction runs the given function in a transaction.
|
|
func runInTransaction(db *sql.DB, fn txFn) error {
|
|
tx, err := db.Begin()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to start transaction: %w", err)
|
|
}
|
|
defer tx.Rollback()
|
|
|
|
if err := fn(tx); err != nil {
|
|
return fmt.Errorf("failed to run transaction: %w", err)
|
|
}
|
|
|
|
if err := tx.Commit(); err != nil {
|
|
return fmt.Errorf("failed to commit transaction: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// newFuncMigration creates a new migration from a function.
|
|
func newFuncMigration(fromVersion, toVersion string, migrationFunc func(db *sql.DB) error) migration {
|
|
return migration{
|
|
fromVersion: semver.MustParse(fromVersion),
|
|
toVersion: semver.MustParse(toVersion),
|
|
migrationFunc: migrationFunc,
|
|
}
|
|
}
|
|
|
|
// newFileMigration creates a new migration from a file.
|
|
func newFileMigration(fromVersion, toVersion, filename string) migration {
|
|
return newFuncMigration(fromVersion, toVersion, func(db *sql.DB) error {
|
|
return runInTransaction(db, func(tx *sql.Tx) error {
|
|
migrationSQL, err := migrationFiles.ReadFile(path.Join("migrations", filename+".up.sql"))
|
|
if err != nil {
|
|
return fmt.Errorf("failed to read migration file: %w", err)
|
|
}
|
|
|
|
if _, err := tx.Exec(string(migrationSQL)); err != nil {
|
|
return fmt.Errorf("failed to execute migration %s to %s: %w", fromVersion, toVersion, err)
|
|
}
|
|
return nil
|
|
})
|
|
})
|
|
}
|
|
|
|
// runMigrations runs the given migrations.
|
|
func runMigrations(ctx context.Context, db model.DB, migrations []migration) error {
|
|
currentVersion := semver.Version{}
|
|
|
|
// Get current database version
|
|
dbVersion, err := db.GetDatabaseSchemaVersion(ctx)
|
|
if err == nil && dbVersion != "" {
|
|
currentVersion = semver.MustParse(dbVersion)
|
|
}
|
|
|
|
for _, migration := range migrations {
|
|
if !currentVersion.EQ(migration.fromVersion) {
|
|
continue
|
|
}
|
|
|
|
if err := migration.migrationFunc(db.WriterDB().DB); err != nil {
|
|
return fmt.Errorf("failed to run migration from %s to %s: %w", migration.fromVersion, migration.toVersion, err)
|
|
}
|
|
|
|
currentVersion = migration.toVersion
|
|
|
|
if err := db.SetDatabaseSchemaVersion(ctx, currentVersion.String()); err != nil {
|
|
return fmt.Errorf("failed to store database version %s from %s to %s: %w", currentVersion.String(), migration.fromVersion, migration.toVersion, err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|