diff --git a/api/.vscode/launch.json b/api/.vscode/launch.json index a4b3b9f1..55263cca 100644 --- a/api/.vscode/launch.json +++ b/api/.vscode/launch.json @@ -11,7 +11,9 @@ "mode": "auto", "program": "${workspaceRoot}/server.go", "cwd": "${workspaceRoot}", - "env": {}, + "env": { + "PATH": "${env:PATH}:/Applications/darktable.app/Contents/MacOS/" + }, "args": [] } ] diff --git a/api/database/mysql.go b/api/database/mysql.go index c5d19129..27b305b4 100644 --- a/api/database/mysql.go +++ b/api/database/mysql.go @@ -1,27 +1,19 @@ package database import ( - "context" - "database/sql" "log" "net/url" "os" - "time" "github.com/pkg/errors" + "github.com/viktorstrate/photoview/api/graphql/models" - // Load mysql driver - _ "github.com/go-sql-driver/mysql" - - "github.com/golang-migrate/migrate" - "github.com/golang-migrate/migrate/database/mysql" - - // Migrate from file - _ "github.com/golang-migrate/migrate/source/file" + "gorm.io/driver/mysql" + "gorm.io/gorm" ) // SetupDatabase connects to the database using environment variables -func SetupDatabase() (*sql.DB, error) { +func SetupDatabase() (*gorm.DB, error) { address, err := url.Parse(os.Getenv("MYSQL_URL")) if err != nil { @@ -40,62 +32,46 @@ func SetupDatabase() (*sql.DB, error) { log.Printf("Connecting to database: %s", address) - var db *sql.DB - - db, err = sql.Open("mysql", address.String()) + db, err := gorm.Open(mysql.Open(address.String()), &gorm.Config{}) if err != nil { - return nil, errors.New("Could not connect to database, exiting") + return nil, errors.Wrap(err, "could not connect to database") } - tryCount := 0 + // var db *sql.DB - for { - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() + // db, err = sql.Open("mysql", address.String()) + // if err != nil { + // return nil, errors.New("Could not connect to database, exiting") + // } - if err := db.PingContext(ctx); err != nil { - if tryCount < 4 { - tryCount++ - log.Printf("WARN: Could not ping database: %s, Will retry after 1 second", err) - time.Sleep(time.Second) - continue - } else { - return nil, errors.Wrap(err, "Could not ping database, exiting") - } - } + // tryCount := 0 - break - } + // for { + // ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + // defer cancel() - db.SetMaxOpenConns(80) + // if err := db.PingContext(ctx); err != nil { + // if tryCount < 4 { + // tryCount++ + // log.Printf("WARN: Could not ping database: %s, Will retry after 1 second", err) + // time.Sleep(time.Second) + // continue + // } else { + // return nil, errors.Wrap(err, "Could not ping database, exiting") + // } + // } + + // break + // } + + // db.SetMaxOpenConns(80) return db, nil } -func MigrateDatabase(db *sql.DB) error { - driver, err := mysql.WithInstance(db, &mysql.Config{}) - if err != nil { - return err - } +func MigrateDatabase(db *gorm.DB) error { - m, err := migrate.NewWithDatabaseInstance( - "file://database/migrations", - "mysql", - driver, - ) - if err != nil { - return err - } - - if err := m.Up(); err != nil { - if err.Error() == "no change" { - log.Println("Database is up to date") - } else { - return err - } - } else { - log.Println("Database migrated") - } + db.AutoMigrate(&models.User{}, &models.AccessToken{}) return nil } diff --git a/api/go.mod b/api/go.mod index 055143dc..478dfc75 100644 --- a/api/go.mod +++ b/api/go.mod @@ -31,4 +31,6 @@ require ( golang.org/x/sys v0.0.0-20201112073958-5cba982894dd // indirect gopkg.in/vansante/go-ffprobe.v2 v2.0.2 gopkg.in/yaml.v2 v2.3.0 // indirect + gorm.io/driver/mysql v1.0.3 + gorm.io/gorm v1.20.7 ) diff --git a/api/go.sum b/api/go.sum index 70d5d435..1d6aca3c 100644 --- a/api/go.sum +++ b/api/go.sum @@ -58,6 +58,10 @@ github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCO github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.1 h1:g39TucaRWyV3dwDO++eEc6qf8TVIQ/Da48WmqjZ3i7E= +github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -176,5 +180,10 @@ gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gorm.io/driver/mysql v1.0.3 h1:+JKBYPfn1tygR1/of/Fh2T8iwuVwzt+PEJmKaXzMQXg= +gorm.io/driver/mysql v1.0.3/go.mod h1:twGxftLBlFgNVNakL7F+P/x9oYqoymG3YYT8cAfI9oI= +gorm.io/gorm v1.20.4/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= +gorm.io/gorm v1.20.7 h1:rMS4CL3pNmYq1V5/X+nHHjh1Dx6dnf27+Cai5zabo+M= +gorm.io/gorm v1.20.7/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= sourcegraph.com/sourcegraph/appdash v0.0.0-20180110180208-2cc67fd64755/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= sourcegraph.com/sourcegraph/appdash-data v0.0.0-20151005221446-73f23eafcf67/go.mod h1:L5q+DGLGOQFpo1snNEkLOJT2d1YTW66rWNzatr3He1k= diff --git a/api/graphql/auth/auth.go b/api/graphql/auth/auth.go index 6af833e4..5f1d4cb0 100644 --- a/api/graphql/auth/auth.go +++ b/api/graphql/auth/auth.go @@ -2,7 +2,6 @@ package auth import ( "context" - "database/sql" "errors" "log" "net/http" @@ -10,6 +9,7 @@ import ( "github.com/99designs/gqlgen/handler" "github.com/viktorstrate/photoview/api/graphql/models" + "gorm.io/gorm" ) var ErrUnauthorized = errors.New("unauthorized") @@ -23,7 +23,7 @@ type contextKey struct { } // Middleware decodes the share session cookie and packs the session into context -func Middleware(db *sql.DB) func(http.Handler) http.Handler { +func Middleware(db *gorm.DB) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -66,7 +66,7 @@ func UserFromContext(ctx context.Context) *models.User { return raw } -func AuthWebsocketInit(db *sql.DB) func(context.Context, handler.InitPayload) (context.Context, error) { +func AuthWebsocketInit(db *gorm.DB) func(context.Context, handler.InitPayload) (context.Context, error) { return func(ctx context.Context, initPayload handler.InitPayload) (context.Context, error) { bearer, exists := initPayload["Authorization"].(string) diff --git a/api/graphql/directive.go b/api/graphql/directive.go index b617f6d2..2e6d8318 100644 --- a/api/graphql/directive.go +++ b/api/graphql/directive.go @@ -2,14 +2,14 @@ package api import ( "context" - "database/sql" "errors" "github.com/99designs/gqlgen/graphql" "github.com/viktorstrate/photoview/api/graphql/auth" + "gorm.io/gorm" ) -func IsAdmin(database *sql.DB) func(ctx context.Context, obj interface{}, next graphql.Resolver) (res interface{}, err error) { +func IsAdmin(database *gorm.DB) func(ctx context.Context, obj interface{}, next graphql.Resolver) (res interface{}, err error) { return func(ctx context.Context, obj interface{}, next graphql.Resolver) (res interface{}, err error) { user := auth.UserFromContext(ctx) diff --git a/api/graphql/models/site_info.go b/api/graphql/models/site_info.go index dc36bfb0..53041cd7 100644 --- a/api/graphql/models/site_info.go +++ b/api/graphql/models/site_info.go @@ -1,48 +1,59 @@ package models import ( - "database/sql" - "github.com/pkg/errors" + "gorm.io/gorm" ) -func initializeSiteInfoRow(db *sql.DB) (*SiteInfo, error) { - _, err := db.Exec("INSERT INTO site_info (initial_setup, periodic_scan_interval, concurrent_workers) VALUES (true, 0, 3)") - if err != nil { - return nil, errors.Wrap(err, "initialize site_info row") - } +// func initializeSiteInfoRow(db *gorm.DB) (*SiteInfo, error) { +// _, err := db.Exec("INSERT INTO site_info (initial_setup, periodic_scan_interval, concurrent_workers) VALUES (true, 0, 3)") +// if err != nil { +// return nil, errors.Wrap(err, "initialize site_info row") +// } - siteInfo := &SiteInfo{} +// siteInfo := &SiteInfo{} - row := db.QueryRow("SELECT * FROM site_info") - if err := row.Scan(&siteInfo.InitialSetup, &siteInfo.PeriodicScanInterval, &siteInfo.ConcurrentWorkers); err != nil { - return nil, errors.Wrap(err, "get site_info row after initialization") - } +// row := db.QueryRow("SELECT * FROM site_info") +// if err := row.Scan(&siteInfo.InitialSetup, &siteInfo.PeriodicScanInterval, &siteInfo.ConcurrentWorkers); err != nil { +// return nil, errors.Wrap(err, "get site_info row after initialization") +// } - return siteInfo, nil -} +// return siteInfo, nil +// } // GetSiteInfo gets the site info row from the database, and creates it if it does not exist -func GetSiteInfo(db *sql.DB) (*SiteInfo, error) { - rows, err := db.Query("SELECT * FROM site_info") - defer rows.Close() - if err != nil { - return nil, err +func GetSiteInfo(db *gorm.DB) (*SiteInfo, error) { + + var siteInfo SiteInfo + + result := db.FirstOrCreate(&siteInfo, SiteInfo{ + InitialSetup: true, + PeriodicScanInterval: 0, + ConcurrentWorkers: 3, + }) + if result.Error != nil { + return nil, errors.Wrap(result.Error, "get site info from database") } - siteInfo := &SiteInfo{} + // rows, err := db.Query("SELECT * FROM site_info") + // defer rows.Close() + // if err != nil { + // return nil, err + // } - if !rows.Next() { - // Entry does not exist - siteInfo, err = initializeSiteInfoRow(db) - if err != nil { - return nil, err - } - } else { - if err := rows.Scan(&siteInfo.InitialSetup, &siteInfo.PeriodicScanInterval, &siteInfo.ConcurrentWorkers); err != nil { - return nil, err - } - } + // siteInfo := &SiteInfo{} - return siteInfo, nil + // if !rows.Next() { + // // Entry does not exist + // siteInfo, err = initializeSiteInfoRow(db) + // if err != nil { + // return nil, err + // } + // } else { + // if err := rows.Scan(&siteInfo.InitialSetup, &siteInfo.PeriodicScanInterval, &siteInfo.ConcurrentWorkers); err != nil { + // return nil, err + // } + // } + + return &siteInfo, nil } diff --git a/api/graphql/models/user.go b/api/graphql/models/user.go index 9bd9a000..5b8d6a82 100644 --- a/api/graphql/models/user.go +++ b/api/graphql/models/user.go @@ -2,7 +2,6 @@ package models import ( "crypto/rand" - "database/sql" "fmt" "log" "os" @@ -10,63 +9,68 @@ import ( "github.com/pkg/errors" "golang.org/x/crypto/bcrypt" + "gorm.io/gorm" ) type User struct { - UserID int - Username string - Password *string - RootPath string - Admin bool + gorm.Model + Username string `gorm:"unique,size:128"` + Password *string `gorm:"size:256` + RootPath string `gorm:"size:512` + Admin bool `gorm:"default:false"` } -func (u *User) ID() int { - return u.UserID -} +// func (u *User) ID() int { +// return u.UserID +// } type AccessToken struct { - Value string + gorm.Model + UserID uint + User User `gorm:"constraint:OnDelete:CASCADE;"` + Value string `gorm:"size:24` Expire time.Time } var ErrorInvalidUserCredentials = errors.New("invalid credentials") -func NewUserFromRow(row *sql.Row) (*User, error) { - user := User{} +// func NewUserFromRow(row *sql.Row) (*User, error) { +// user := User{} - if err := row.Scan(&user.UserID, &user.Username, &user.Password, &user.RootPath, &user.Admin); err != nil { - return nil, errors.Wrap(err, "failed to scan user from database") - } +// if err := row.Scan(&user.UserID, &user.Username, &user.Password, &user.RootPath, &user.Admin); err != nil { +// return nil, errors.Wrap(err, "failed to scan user from database") +// } - return &user, nil -} +// return &user, nil +// } -func NewUsersFromRows(rows *sql.Rows) ([]*User, error) { - users := make([]*User, 0) +// func NewUsersFromRows(rows *sql.Rows) ([]*User, error) { +// users := make([]*User, 0) - for rows.Next() { - var user User - if err := rows.Scan(&user.UserID, &user.Username, &user.Password, &user.RootPath, &user.Admin); err != nil { - return nil, errors.Wrap(err, "failed to scan users from database") - } - users = append(users, &user) - } +// for rows.Next() { +// var user User +// if err := rows.Scan(&user.UserID, &user.Username, &user.Password, &user.RootPath, &user.Admin); err != nil { +// return nil, errors.Wrap(err, "failed to scan users from database") +// } +// users = append(users, &user) +// } - rows.Close() +// rows.Close() - return users, nil -} +// return users, nil +// } -func AuthorizeUser(database *sql.DB, username string, password string) (*User, error) { - row := database.QueryRow("SELECT * FROM user WHERE username = ?", username) +func AuthorizeUser(db *gorm.DB, username string, password string) (*User, error) { + // row := database.QueryRow("SELECT * FROM user WHERE username = ?", username) - user, err := NewUserFromRow(row) - if err != nil { - if err == sql.ErrNoRows { + var user User + + result := db.Where("username = ?", username).First(&user) + if result.Error != nil { + if errors.Is(result.Error, gorm.ErrRecordNotFound) { return nil, ErrorInvalidUserCredentials - } else { - return nil, err } + return nil, errors.Wrap(result.Error, "failed to get user by username when authorizing") } if user.Password == nil { @@ -81,7 +85,7 @@ func AuthorizeUser(database *sql.DB, username string, password string) (*User, e } } - return user, nil + return &user, nil } var ErrorInvalidRootPath = errors.New("invalid root path") @@ -96,11 +100,17 @@ func ValidRootPath(rootPath string) bool { return true } -func RegisterUser(database *sql.Tx, username string, password *string, rootPath string, admin bool) (*User, error) { +func RegisterUser(db *gorm.DB, username string, password *string, rootPath string, admin bool) (*User, error) { if !ValidRootPath(rootPath) { return nil, ErrorInvalidRootPath } + user := User{ + Username: username, + RootPath: rootPath, + Admin: admin, + } + if password != nil { hashedPassBytes, err := bcrypt.GenerateFromPassword([]byte(*password), 12) if err != nil { @@ -108,29 +118,18 @@ func RegisterUser(database *sql.Tx, username string, password *string, rootPath } hashedPass := string(hashedPassBytes) - if _, err := database.Exec("INSERT INTO user (username, password, root_path, admin) VALUES (?, ?, ?, ?)", username, hashedPass, rootPath, admin); err != nil { - return nil, errors.Wrap(err, "insert new user with password into database") - } - } else { - if _, err := database.Exec("INSERT INTO user (username, root_path, admin) VALUES (?, ?, ?)", username, rootPath, admin); err != nil { - return nil, errors.Wrap(err, "insert user without password into database") - } + user.Password = &hashedPass } - row := database.QueryRow("SELECT * FROM user WHERE username = ?", username) - if row == nil { - return nil, ErrorInvalidUserCredentials + result := db.Create(&user) + if result.Error != nil { + return nil, errors.Wrap(result.Error, "insert new user with password into database") } - user, err := NewUserFromRow(row) - if err != nil { - return nil, err - } - - return user, nil + return &user, nil } -func (user *User) GenerateAccessToken(database *sql.Tx) (*AccessToken, error) { +func (user *User) GenerateAccessToken(db *gorm.DB) (*AccessToken, error) { bytes := make([]byte, 24) if _, err := rand.Read(bytes); err != nil { return nil, errors.New(fmt.Sprintf("Could not generate token: %s\n", err.Error())) @@ -142,38 +141,56 @@ func (user *User) GenerateAccessToken(database *sql.Tx) (*AccessToken, error) { token_value := string(bytes) expire := time.Now().Add(14 * 24 * time.Hour) - expireString := expire.UTC().Format("2006-01-02 15:04:05") + // expireString := expire.UTC().Format("2006-01-02 15:04:05") - if _, err := database.Exec("INSERT INTO access_token (value, expire, user_id) VALUES (?, ?, ?)", token_value, expireString, user.UserID); err != nil { - return nil, err - } + // if _, err := database.Exec("INSERT INTO access_token (value, expire, user_id) VALUES (?, ?, ?)", token_value, expireString, user.UserID); err != nil { + // return nil, err + // } token := AccessToken{ + UserID: user.ID, Value: token_value, Expire: expire, } + result := db.Create(&token) + if result.Error != nil { + return nil, errors.Wrap(result.Error, "saving access token to database") + } + return &token, nil } -func VerifyTokenAndGetUser(database *sql.DB, token string) (*User, error) { +func VerifyTokenAndGetUser(db *gorm.DB, token string) (*User, error) { now := time.Now().UTC().Format("2006-01-02 15:04:05") - row := database.QueryRow("SELECT (user_id) FROM access_token WHERE expire > ? AND value = ?", now, token) + // row := database.QueryRow("SELECT (user_id) FROM access_token WHERE expire > ? AND value = ?", now, token) - var userId string - - if err := row.Scan(&userId); err != nil { - log.Println(err.Error()) - return nil, err + var accessToken AccessToken + result := db.Where("expire > ? AND value = ?", now, token).First(&accessToken) + if result.Error != nil { + return nil, result.Error } - row = database.QueryRow("SELECT * FROM user WHERE user_id = ?", userId) - user, err := NewUserFromRow(row) - if err != nil { - return nil, err + // var userId string + + // if err := row.Scan(&userId); err != nil { + // log.Println(err.Error()) + // return nil, err + // } + + // row = db.QueryRow("SELECT * FROM user WHERE user_id = ?", userId) + // user, err := NewUserFromRow(row) + // if err != nil { + // return nil, err + // } + + var user User + result = db.First(&user, accessToken.ID) + if result.Error != nil { + return nil, result.Error } - return user, nil + return &user, nil } diff --git a/api/graphql/resolvers/root.go b/api/graphql/resolvers/root.go index 46b8f06c..e5063839 100644 --- a/api/graphql/resolvers/root.go +++ b/api/graphql/resolvers/root.go @@ -2,16 +2,16 @@ package resolvers import ( "context" - "database/sql" api "github.com/viktorstrate/photoview/api/graphql" "github.com/viktorstrate/photoview/api/graphql/models" + "gorm.io/gorm" ) //go:generate go run github.com/99designs/gqlgen type Resolver struct { - Database *sql.DB + Database *gorm.DB } func (r *Resolver) Mutation() api.MutationResolver { diff --git a/api/graphql/resolvers/share_token.go b/api/graphql/resolvers/share_token.go index bf08f701..616059a2 100644 --- a/api/graphql/resolvers/share_token.go +++ b/api/graphql/resolvers/share_token.go @@ -6,6 +6,7 @@ import ( "time" "github.com/pkg/errors" + "gorm.io/gorm" api "github.com/viktorstrate/photoview/api/graphql" "github.com/viktorstrate/photoview/api/graphql/auth" @@ -261,7 +262,7 @@ func hashSharePassword(password *string) (*string, error) { return hashed_password, nil } -func getUserToken(db *sql.DB, user *models.User, tokenValue string) (*models.ShareToken, error) { +func getUserToken(db *gorm.DB, user *models.User, tokenValue string) (*models.ShareToken, error) { row := db.QueryRow(` SELECT share_token.* FROM share_token, user WHERE share_token.value = ? AND diff --git a/api/routes/authenticate_media.go b/api/routes/authenticate_media.go index 0659ca07..5f540ce4 100644 --- a/api/routes/authenticate_media.go +++ b/api/routes/authenticate_media.go @@ -8,9 +8,10 @@ import ( "github.com/viktorstrate/photoview/api/graphql/auth" "github.com/viktorstrate/photoview/api/graphql/models" "golang.org/x/crypto/bcrypt" + "gorm.io/gorm" ) -func authenticateMedia(media *models.Media, db *sql.DB, r *http.Request) (success bool, responseMessage string, responseStatus int, errorMessage error) { +func authenticateMedia(media *models.Media, db *gorm.DB, r *http.Request) (success bool, responseMessage string, responseStatus int, errorMessage error) { user := auth.UserFromContext(r.Context()) if user != nil { diff --git a/api/routes/photos.go b/api/routes/photos.go index 6ed40ae2..0377bf4d 100644 --- a/api/routes/photos.go +++ b/api/routes/photos.go @@ -1,7 +1,6 @@ package routes import ( - "database/sql" "log" "net/http" "os" @@ -9,12 +8,13 @@ import ( "strconv" "github.com/gorilla/mux" + "gorm.io/gorm" "github.com/viktorstrate/photoview/api/graphql/models" "github.com/viktorstrate/photoview/api/scanner" ) -func RegisterPhotoRoutes(db *sql.DB, router *mux.Router) { +func RegisterPhotoRoutes(db *gorm.DB, router *mux.Router) { router.HandleFunc("/{name}", func(w http.ResponseWriter, r *http.Request) { media_name := mux.Vars(r)["name"] diff --git a/api/routes/videos.go b/api/routes/videos.go index a33cb5c9..c737fd60 100644 --- a/api/routes/videos.go +++ b/api/routes/videos.go @@ -1,7 +1,6 @@ package routes import ( - "database/sql" "log" "net/http" "os" @@ -11,9 +10,10 @@ import ( "github.com/gorilla/mux" "github.com/viktorstrate/photoview/api/graphql/models" "github.com/viktorstrate/photoview/api/scanner" + "gorm.io/gorm" ) -func RegisterVideoRoutes(db *sql.DB, router *mux.Router) { +func RegisterVideoRoutes(db *gorm.DB, router *mux.Router) { router.HandleFunc("/{name}", func(w http.ResponseWriter, r *http.Request) { media_name := mux.Vars(r)["name"] diff --git a/api/scanner/cleanup_media.go b/api/scanner/cleanup_media.go index 1365c3f5..fa6c3eee 100644 --- a/api/scanner/cleanup_media.go +++ b/api/scanner/cleanup_media.go @@ -9,9 +9,10 @@ import ( "github.com/pkg/errors" "github.com/viktorstrate/photoview/api/graphql/models" + "gorm.io/gorm" ) -func CleanupMedia(db *sql.DB, albumId int, albumMedia []*models.Media) []error { +func CleanupMedia(db *gorm.DB, albumId int, albumMedia []*models.Media) []error { albumMediaIds := make([]interface{}, len(albumMedia)) for i, photo := range albumMedia { albumMediaIds[i] = photo.MediaID @@ -72,7 +73,7 @@ func CleanupMedia(db *sql.DB, albumId int, albumMedia []*models.Media) []error { return deleteErrors } -func deleteOldUserAlbums(db *sql.DB, scannedAlbums []*models.Album, user *models.User) []error { +func deleteOldUserAlbums(db *gorm.DB, scannedAlbums []*models.Album, user *models.User) []error { if len(scannedAlbums) == 0 { return nil } diff --git a/api/scanner/periodic_scanner.go b/api/scanner/periodic_scanner.go index 671739c8..c8a4152b 100644 --- a/api/scanner/periodic_scanner.go +++ b/api/scanner/periodic_scanner.go @@ -1,22 +1,23 @@ package scanner import ( - "database/sql" "log" "sync" "time" + + "gorm.io/gorm" ) type periodicScanner struct { ticker *time.Ticker ticker_changed chan bool mutex *sync.Mutex - db *sql.DB + db *gorm.DB } var mainPeriodicScanner *periodicScanner = nil -func getPeriodicScanInterval(db *sql.DB) (time.Duration, error) { +func getPeriodicScanInterval(db *gorm.DB) (time.Duration, error) { row := db.QueryRow("SELECT periodic_scan_interval FROM site_info") var intervalSeconds int @@ -27,7 +28,7 @@ func getPeriodicScanInterval(db *sql.DB) (time.Duration, error) { return time.Duration(intervalSeconds) * time.Second, nil } -func InitializePeriodicScanner(db *sql.DB) error { +func InitializePeriodicScanner(db *gorm.DB) error { if mainPeriodicScanner != nil { panic("periodic scanner has already been initialized") } diff --git a/api/scanner/queue.go b/api/scanner/queue.go index d3c49337..619661ba 100644 --- a/api/scanner/queue.go +++ b/api/scanner/queue.go @@ -1,7 +1,6 @@ package scanner import ( - "database/sql" "fmt" "log" "sync" @@ -11,6 +10,7 @@ import ( "github.com/viktorstrate/photoview/api/graphql/models" "github.com/viktorstrate/photoview/api/graphql/notification" "github.com/viktorstrate/photoview/api/utils" + "gorm.io/gorm" ) type ScannerJob struct { @@ -18,7 +18,7 @@ type ScannerJob struct { cache *AlbumScannerCache } -func (job *ScannerJob) Run(db *sql.DB) { +func (job *ScannerJob) Run(db *gorm.DB) { scanAlbum(job.album, job.cache, db) } @@ -31,13 +31,13 @@ type ScannerQueue struct { idle_chan chan bool in_progress []ScannerJob up_next []ScannerJob - db *sql.DB + db *gorm.DB settings ScannerQueueSettings } var global_scanner_queue ScannerQueue -func InitializeScannerQueue(db *sql.DB) error { +func InitializeScannerQueue(db *gorm.DB) error { var concurrentWorkers int { @@ -146,14 +146,11 @@ func (queue *ScannerQueue) notify() bool { } func AddAllToQueue() error { - rows, err := global_scanner_queue.db.Query("SELECT * FROM user") - if err != nil { - return errors.Wrap(err, "get all users from database") - } - users, err := models.NewUsersFromRows(rows) - if err != nil { - return errors.Wrap(err, "parse all users from db") + var users []*models.User + result := global_scanner_queue.db.Find(&users) + if result.Error != nil { + return errors.Wrap(result.Error, "get all users from database") } for _, user := range users { @@ -167,7 +164,7 @@ func AddUserToQueue(user *models.User) error { album_cache := MakeAlbumCache() albums, album_errors := findAlbumsForUser(global_scanner_queue.db, user, album_cache) for _, err := range album_errors { - return errors.Wrapf(err, "find albums for user (user_id: %d)", user.UserID) + return errors.Wrapf(err, "find albums for user (user_id: %d)", user.ID) } global_scanner_queue.mutex.Lock() diff --git a/api/scanner/scanner_album.go b/api/scanner/scanner_album.go index 552d36e2..4c0aa6fe 100644 --- a/api/scanner/scanner_album.go +++ b/api/scanner/scanner_album.go @@ -1,7 +1,6 @@ package scanner import ( - "database/sql" "fmt" "io/ioutil" "path" @@ -10,9 +9,10 @@ import ( "github.com/viktorstrate/photoview/api/graphql/models" "github.com/viktorstrate/photoview/api/graphql/notification" "github.com/viktorstrate/photoview/api/utils" + "gorm.io/gorm" ) -func scanAlbum(album *models.Album, cache *AlbumScannerCache, db *sql.DB) { +func scanAlbum(album *models.Album, cache *AlbumScannerCache, db *gorm.DB) { album_notify_key := utils.GenerateToken() notifyThrottle := utils.NewThrottle(500 * time.Millisecond) @@ -85,7 +85,7 @@ func scanAlbum(album *models.Album, cache *AlbumScannerCache, db *sql.DB) { } } -func findMediaForAlbum(album *models.Album, cache *AlbumScannerCache, db *sql.DB, onScanPhoto func(photo *models.Media, newPhoto bool)) ([]*models.Media, error) { +func findMediaForAlbum(album *models.Album, cache *AlbumScannerCache, db *gorm.DB, onScanPhoto func(photo *models.Media, newPhoto bool)) ([]*models.Media, error) { albumPhotos := make([]*models.Media, 0) diff --git a/api/scanner/scanner_user.go b/api/scanner/scanner_user.go index 7c297a60..07448bc6 100644 --- a/api/scanner/scanner_user.go +++ b/api/scanner/scanner_user.go @@ -2,7 +2,6 @@ package scanner import ( "container/list" - "database/sql" "fmt" "io/ioutil" "log" @@ -13,9 +12,10 @@ import ( "github.com/viktorstrate/photoview/api/graphql/models" "github.com/viktorstrate/photoview/api/graphql/notification" "github.com/viktorstrate/photoview/api/utils" + "gorm.io/gorm" ) -func findAlbumsForUser(db *sql.DB, user *models.User, album_cache *AlbumScannerCache) ([]*models.Album, []error) { +func findAlbumsForUser(db *gorm.DB, user *models.User, album_cache *AlbumScannerCache) ([]*models.Album, []error) { // Check if user directory exists on the file system if _, err := os.Stat(user.RootPath); err != nil { diff --git a/api/server.go b/api/server.go index 84ea8749..36af483a 100644 --- a/api/server.go +++ b/api/server.go @@ -35,7 +35,6 @@ func main() { if err != nil { log.Panicf("Could not connect to database: %s\n", err) } - defer db.Close() // Migrate database if err := database.MigrateDatabase(db); err != nil {