Detailed changes
@@ -169,27 +169,27 @@ Listen = "%s"`, defaultDBConn, defaultFetchInterval, defaultFetchInterval, defau
if os.IsNotExist(err) {
file, err = os.Create(*flagConfig)
if err != nil {
- return err
+ return fmt.Errorf("failed to create file: %w", err)
}
defer file.Close()
_, err = file.WriteString(defaultConfig)
if err != nil {
- return err
+ return fmt.Errorf("failed to write to file: %w", err)
}
fmt.Println("Config file created at", *flagConfig)
fmt.Println("Please edit it and restart the server")
os.Exit(0)
} else {
- return err
+ return fmt.Errorf("failed to open config file: %w", err)
}
}
defer file.Close()
_, err = toml.DecodeFile(*flagConfig, &config)
if err != nil {
- return err
+ return fmt.Errorf("failed to decode TOML file: %w", err)
}
if config.FetchInterval < defaultFetchInterval {
@@ -8,6 +8,7 @@ import (
"database/sql"
_ "embed"
"errors"
+ "fmt"
"sync"
_ "modernc.org/sqlite"
@@ -20,7 +21,12 @@ var mutex = &sync.Mutex{}
// Open opens a connection to the SQLite database.
func Open(dbPath string) (*sql.DB, error) {
- return sql.Open("sqlite", "file:"+dbPath+"?_pragma=journal_mode%3DWAL")
+ db, err := sql.Open("sqlite", "file:"+dbPath+"?_pragma=journal_mode%3DWAL")
+ if err != nil {
+ return nil, fmt.Errorf("failed to open database: %w", err)
+ }
+
+ return db, nil
}
// VerifySchema checks whether the schema has been initialised and initialises it
@@ -34,11 +40,15 @@ func InitialiseDatabase(dbConn *sql.DB) error {
defer mutex.Unlock()
if _, err := dbConn.Exec(schema); err != nil {
- return err
+ return fmt.Errorf("failed to execute SQL: %w", err)
}
return nil
}
- return err
+ if err != nil {
+ return fmt.Errorf("failed to scan row: %w", err)
+ }
+
+ return nil
}
@@ -166,6 +166,9 @@ func updateSchemaVersion(tx *sql.Tx, version int) error {
}
_, err := tx.Exec(`UPDATE schema_migrations SET version = @version;`, sql.Named("version", version))
+ if err != nil {
+ return fmt.Errorf("failed to execute SQL: %w", err)
+ }
- return err
+ return nil
}
@@ -6,6 +6,7 @@ package db
import (
"database/sql"
+ "fmt"
"sync"
)
@@ -16,12 +17,15 @@ func DeleteProject(db *sql.DB, mu *sync.Mutex, id string) error {
_, err := db.Exec("DELETE FROM projects WHERE id = ?", id)
if err != nil {
- return err
+ return fmt.Errorf("failed to execute SQL: %w", err)
}
_, err = db.Exec("DELETE FROM releases WHERE project_id = ?", id)
+ if err != nil {
+ return fmt.Errorf("failed to execute SQL: %w", err)
+ }
- return err
+ return nil
}
// GetProject returns a project from the database.
@@ -30,7 +34,7 @@ func GetProject(db *sql.DB, id string) (map[string]string, error) {
err := db.QueryRow("SELECT name, forge, url, version FROM projects WHERE id = ?", id).Scan(&name, &forge, &url, &version)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("failed to scan row: %w", err)
}
project := map[string]string{
@@ -56,15 +60,18 @@ func UpsertProject(db *sql.DB, mu *sync.Mutex, id, url, name, forge, running str
name = excluded.name,
forge = excluded.forge,
version = excluded.version;`, id, url, name, forge, running)
+ if err != nil {
+ return fmt.Errorf("failed to execute SQL: %w", err)
+ }
- return err
+ return nil
}
// GetProjects returns a list of all projects in the database.
func GetProjects(db *sql.DB) ([]map[string]string, error) {
rows, err := db.Query("SELECT id, name, url, forge, version FROM projects")
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("failed to query database: %w", err)
}
defer rows.Close()
@@ -74,7 +81,7 @@ func GetProjects(db *sql.DB) ([]map[string]string, error) {
err = rows.Scan(&id, &name, &url, &forge, &version)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("failed to scan row: %w", err)
}
project := map[string]string{
@@ -6,6 +6,7 @@ package db
import (
"database/sql"
+ "fmt"
"sync"
)
@@ -24,15 +25,18 @@ func UpsertRelease(db *sql.DB, mu *sync.Mutex, id, projectID, url, tag, content,
tag = excluded.tag,
content = excluded.content,
date = excluded.date;`, id, projectID, url, tag, content, date)
+ if err != nil {
+ return fmt.Errorf("failed to execute SQL: %w", err)
+ }
- return err
+ return nil
}
// GetReleases returns all releases for a project with a given id from the database.
func GetReleases(db *sql.DB, projectID string) ([]map[string]string, error) {
rows, err := db.Query(`SELECT id, url, tag, content, date FROM releases WHERE project_id = ?`, projectID)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("failed to query database: %w", err)
}
defer rows.Close()
@@ -48,7 +52,7 @@ func GetReleases(db *sql.DB, projectID string) ([]map[string]string, error) {
err := rows.Scan(&id, &url, &tag, &content, &date)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("failed to scan row: %w", err)
}
releases = append(releases, map[string]string{
@@ -6,6 +6,7 @@ package db
import (
"database/sql"
+ "fmt"
"time"
)
@@ -16,8 +17,11 @@ func DeleteUser(db *sql.DB, user string) error {
defer mutex.Unlock()
_, err := db.Exec("DELETE FROM users WHERE username = ?", user)
+ if err != nil {
+ return fmt.Errorf("failed to execute SQL: %w", err)
+ }
- return err
+ return nil
}
// CreateUser creates a new user in the database and returns an error if it fails.
@@ -26,8 +30,11 @@ func CreateUser(db *sql.DB, username, hash, salt string) error {
defer mutex.Unlock()
_, err := db.Exec("INSERT INTO users (username, hash, salt) VALUES (?, ?, ?)", username, hash, salt)
+ if err != nil {
+ return fmt.Errorf("failed to execute SQL: %w", err)
+ }
- return err
+ return nil
}
// GetUser returns a user's hash and salt from the database as strings and
@@ -36,8 +43,11 @@ func GetUser(db *sql.DB, username string) (string, string, error) {
var hash, salt string
err := db.QueryRow("SELECT hash, salt FROM users WHERE username = ?", username).Scan(&hash, &salt)
+ if err != nil {
+ return "", "", fmt.Errorf("failed to scan row: %w", err)
+ }
- return hash, salt, err
+ return hash, salt, nil
}
// GetUsers returns a list of all users in the database as a slice of strings
@@ -45,7 +55,7 @@ func GetUser(db *sql.DB, username string) (string, string, error) {
func GetUsers(db *sql.DB) ([]string, error) {
rows, err := db.Query("SELECT username FROM users")
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("failed to query database: %w", err)
}
defer rows.Close()
@@ -55,7 +65,7 @@ func GetUsers(db *sql.DB) ([]string, error) {
err = rows.Scan(&user)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("failed to scan row: %w", err)
}
users = append(users, user)
@@ -74,12 +84,12 @@ func GetSession(db *sql.DB, session string) (string, time.Time, error) {
err := db.QueryRow("SELECT username, expires FROM sessions WHERE token = ?", session).Scan(&username, &expiresString)
if err != nil {
- return "", time.Time{}, err
+ return "", time.Time{}, fmt.Errorf("failed to scan row: %w", err)
}
expires, err := time.Parse(time.RFC3339, expiresString)
if err != nil {
- return "", time.Time{}, err
+ return "", time.Time{}, fmt.Errorf("failed to parse time: %w", err)
}
return username, expires, nil
@@ -92,8 +102,11 @@ func InvalidateSession(db *sql.DB, session string, expiry time.Time) error {
defer mutex.Unlock()
_, err := db.Exec("UPDATE sessions SET expires = ? WHERE token = ?", expiry.Format(time.RFC3339), session)
+ if err != nil {
+ return fmt.Errorf("failed to execute SQL: %w", err)
+ }
- return err
+ return nil
}
// CreateSession creates a new session in the database and returns an error if
@@ -103,6 +116,9 @@ func CreateSession(db *sql.DB, username, token string, expiry time.Time) error {
defer mutex.Unlock()
_, err := db.Exec("INSERT INTO sessions (token, username, expires) VALUES (?, ?, ?)", token, username, expiry.Format(time.RFC3339))
+ if err != nil {
+ return fmt.Errorf("failed to execute SQL: %w", err)
+ }
- return err
+ return nil
}
@@ -47,7 +47,7 @@ func GetReleases(gitURI, forge string) ([]Release, error) {
tagRefs, err := r.Tags()
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("failed to get repository tags: %w", err)
}
parsedURI, err := url.Parse(gitURI)
@@ -73,7 +73,7 @@ func GetReleases(gitURI, forge string) ([]Release, error) {
if errors.Is(err, plumbing.ErrObjectNotFound) {
commitTag, err := r.CommitObject(tagRef.Hash())
if err != nil {
- return err
+ return fmt.Errorf("failed to get commit object: %w", err)
}
message = commitTag.Message
@@ -106,7 +106,7 @@ func GetReleases(gitURI, forge string) ([]Release, error) {
return nil
})
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("failed to iterate references: %w", err)
}
return releases, nil
@@ -142,9 +142,9 @@ func minimalClone(url string) (r *git.Repository, err error) {
return r, nil
}
- return r, err
+ return r, fmt.Errorf("failed to fetch repository: %w", err)
} else if !errors.Is(err, git.ErrRepositoryNotExists) {
- return nil, err
+ return nil, fmt.Errorf("failed to fetch repository: %w", err)
}
r, err = git.PlainClone(path, false, &git.CloneOptions{
@@ -170,7 +170,7 @@ func minimalClone(url string) (r *git.Repository, err error) {
Shared: false,
})
- return r, err
+ return r, fmt.Errorf("failed to clone repository: %w", err)
}
// RemoveRepo removes a repository from the local filesystem.
@@ -182,7 +182,7 @@ func RemoveRepo(url string) (err error) {
err = os.RemoveAll(path)
if err != nil {
- return err
+ return fmt.Errorf("failed to remove directory: %w", err)
}
path = path[:strings.LastIndex(path, "/")]
@@ -214,7 +214,7 @@ func stringifyRepo(url string) (path string, err error) {
ep, err := transport.NewEndpoint(url)
if err != nil {
- return "", err
+ return "", fmt.Errorf("failed to create git endpoint: %w", err)
}
switch ep.Protocol {
@@ -49,7 +49,7 @@ func GetReleases(dbConn *sql.DB, mu *sync.Mutex, proj Project) (Project, error)
ret, err := db.GetReleases(dbConn, proj.ID)
if err != nil {
- return proj, err
+ return proj, fmt.Errorf("failed to get releases from database: %w", err)
}
if len(ret) == 0 {
@@ -81,7 +81,7 @@ func fetchReleases(dbConn *sql.DB, mu *sync.Mutex, p Project) (Project, error) {
rssReleases, err := rss.GetReleases(p.URL)
if err != nil {
fmt.Println("Error getting RSS releases:", err)
- return p, err
+ return p, fmt.Errorf("failed to get releases from RSS feed: %w", err)
}
for _, release := range rssReleases {
@@ -103,7 +103,7 @@ func fetchReleases(dbConn *sql.DB, mu *sync.Mutex, p Project) (Project, error) {
default:
gitReleases, err := git.GetReleases(p.URL, p.Forge)
if err != nil {
- return p, err
+ return p, fmt.Errorf("failed to get releases from git: %w", err)
}
for _, release := range gitReleases {
@@ -153,7 +153,7 @@ func upsertReleases(dbConn *sql.DB, mu *sync.Mutex, projID string, releases []Re
err := db.UpsertRelease(dbConn, mu, release.ID, projID, release.URL, release.Tag, release.Content, date)
if err != nil {
log.Printf("Error upserting release: %v", err)
- return err
+ return fmt.Errorf("failed to upsert release: %w", err)
}
}
@@ -261,7 +261,7 @@ func GetProject(dbConn *sql.DB, proj Project) (Project, error) {
if err != nil && errors.Is(err, sql.ErrNoRows) {
return proj, nil
} else if err != nil {
- return proj, err
+ return proj, fmt.Errorf("failed to get project from database: %w", err)
}
p := Project{
@@ -273,7 +273,7 @@ func GetProject(dbConn *sql.DB, proj Project) (Project, error) {
Releases: nil,
}
- return p, err
+ return p, nil
}
// GetProjectWithReleases returns a single project from the database along with its releases.
@@ -290,7 +290,7 @@ func GetProjectWithReleases(dbConn *sql.DB, mu *sync.Mutex, proj Project) (Proje
func GetProjects(dbConn *sql.DB) ([]Project, error) {
projectsDB, err := db.GetProjects(dbConn)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("failed to get projects from database: %w", err)
}
projects := make([]Project, len(projectsDB))
@@ -31,7 +31,7 @@ func GetReleases(feedURL string) ([]Release, error) {
feed, err := fp.ParseURL(strings.TrimSuffix(feedURL, "/") + "/releases.atom")
if err != nil {
fmt.Println(err)
- return nil, err
+ return nil, fmt.Errorf("failed to parse RSS feed: %w", err)
}
releases := make([]Release, 0)
@@ -8,6 +8,7 @@ import (
"crypto/rand"
"database/sql"
"encoding/base64"
+ "fmt"
"time"
"git.sr.ht/~amolith/willow/db"
@@ -28,7 +29,7 @@ const (
func argonHash(password, salt string) (string, error) {
decodedSalt, err := base64.StdEncoding.DecodeString(salt)
if err != nil {
- return "", err
+ return "", fmt.Errorf("failed to decode base64: %w", err)
}
return base64.StdEncoding.EncodeToString(argon2.IDKey([]byte(password), decodedSalt, argon2Time, argon2Memory, argon2Threads, argon2KeyLen)), nil
@@ -41,7 +42,7 @@ func generateSalt() (string, error) {
_, err := rand.Read(salt)
if err != nil {
- return "", err
+ return "", fmt.Errorf("failed to generate random bytes: %w", err)
}
return base64.StdEncoding.EncodeToString(salt), nil
@@ -60,18 +61,30 @@ func Register(dbConn *sql.DB, username, password string) error {
return err
}
- return db.CreateUser(dbConn, username, hash, salt)
+ err = db.CreateUser(dbConn, username, hash, salt)
+ if err != nil {
+ return fmt.Errorf("failed to create user: %w", err)
+ }
+
+ return nil
}
// Delete removes a user from the database.
-func Delete(dbConn *sql.DB, username string) error { return db.DeleteUser(dbConn, username) }
+func Delete(dbConn *sql.DB, username string) error {
+ err := db.DeleteUser(dbConn, username)
+ if err != nil {
+ return fmt.Errorf("failed to delete user: %w", err)
+ }
+
+ return nil
+}
// UserAuthorised accepts a username string, a token string, and returns true if the
// user is authorised, false if not, and an error if one is encountered.
func UserAuthorised(dbConn *sql.DB, username, token string) (bool, error) {
dbHash, dbSalt, err := db.GetUser(dbConn, username)
if err != nil {
- return false, err
+ return false, fmt.Errorf("failed to get user: %w", err)
}
providedHash, err := argonHash(token, dbSalt)
@@ -86,8 +99,12 @@ func UserAuthorised(dbConn *sql.DB, username, token string) (bool, error) {
// valid and false if not.
func SessionAuthorised(dbConn *sql.DB, session string) (bool, error) {
dbResult, expiry, err := db.GetSession(dbConn, session)
- if dbResult == "" || expiry.Before(time.Now()) || err != nil {
- return false, err
+ if err != nil {
+ return false, fmt.Errorf("failed to get session: %w", err)
+ }
+
+ if dbResult == "" || expiry.Before(time.Now()) {
+ return false, nil
}
return true, nil
@@ -95,7 +112,12 @@ func SessionAuthorised(dbConn *sql.DB, session string) (bool, error) {
// InvalidateSession invalidates a session by setting the expiration date to now.
func InvalidateSession(dbConn *sql.DB, session string) error {
- return db.InvalidateSession(dbConn, session, time.Now())
+ err := db.InvalidateSession(dbConn, session, time.Now())
+ if err != nil {
+ return fmt.Errorf("failed to invalidate session: %w", err)
+ }
+
+ return nil
}
// CreateSession accepts a username, generates a token, stores it in the
@@ -110,11 +132,18 @@ func CreateSession(dbConn *sql.DB, username string) (string, time.Time, error) {
err = db.CreateSession(dbConn, username, token, expiry)
if err != nil {
- return "", time.Time{}, err
+ return "", time.Time{}, fmt.Errorf("failed to create session: %w", err)
}
return token, expiry, nil
}
// GetUsers returns a list of all users in the database as a slice of strings.
-func GetUsers(dbConn *sql.DB) ([]string, error) { return db.GetUsers(dbConn) }
+func GetUsers(dbConn *sql.DB) ([]string, error) {
+ users, err := db.GetUsers(dbConn)
+ if err != nil {
+ return nil, fmt.Errorf("failed to get users: %w", err)
+ }
+
+ return users, nil
+}