diff --git a/cmd/server/main.go b/cmd/server/main.go index d517747..b7d053e 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -1,27 +1,20 @@ package main import ( - "bufio" "context" - "database/sql" - "fmt" "log" "net/http" "os" "os/signal" - "path/filepath" - "strings" "syscall" "time" - "github.com/google/uuid" "github.com/joho/godotenv" _ "github.com/mattn/go-sqlite3" - "golang.org/x/crypto/bcrypt" "mal/api/auth" "mal/integrations/jikan" - dbpkg "mal/internal/db" + "mal/internal/db/sqlite" "mal/internal/server" "mal/internal/worker" "mal/pkg/middleware" @@ -30,78 +23,17 @@ import ( func main() { _ = godotenv.Load() - if len(os.Args) > 1 && os.Args[1] == "create-user" { - if len(os.Args) != 4 { - log.Fatalf("Usage: %s create-user ", os.Args[0]) - } - - username := os.Args[2] - password := os.Args[3] - - db, err := sql.Open("sqlite3", fmt.Sprintf("file:%s?_foreign_keys=on", dbFile())) - if err != nil { - log.Fatalf("failed to open db: %v", err) - } - defer db.Close() - - var existingID string - err = db.QueryRow("SELECT id FROM user WHERE username = ?", username).Scan(&existingID) - if err != nil && err != sql.ErrNoRows { - log.Fatalf("database error: %v", err) - } - - if err == nil { - fmt.Printf("User '%s' already exists. Do you want to overwrite their password? [y/N]: ", username) - reader := bufio.NewReader(os.Stdin) - response, _ := reader.ReadString('\n') - response = strings.TrimSpace(strings.ToLower(response)) - - if response != "y" && response != "yes" { - fmt.Println("Operation cancelled.") - return - } - - hash, err := bcrypt.GenerateFromPassword([]byte(password), 12) - if err != nil { - log.Fatalf("failed to hash password: %v", err) - } - - _, err = db.Exec("UPDATE user SET password_hash = ? WHERE id = ?", string(hash), existingID) - if err != nil { - log.Fatalf("failed to update user: %v", err) - } - - fmt.Printf("✅ Password for '%s' updated successfully!\n", username) - return - } - - hash, err := bcrypt.GenerateFromPassword([]byte(password), 12) - if err != nil { - log.Fatalf("failed to hash password: %v", err) - } - - id := uuid.New().String() - _, err = db.Exec("INSERT INTO user (id, username, password_hash) VALUES (?, ?, ?)", id, username, string(hash)) - if err != nil { - log.Fatalf("failed to create user: %v", err) - } - - fmt.Printf("✅ Brugeren '%s' blev oprettet med succes!\n", username) - return - } - - db, err := sql.Open("sqlite3", fmt.Sprintf("file:%s?_foreign_keys=on", dbFile())) + db, err := sqlite.Open(sqlite.GetDBFile()) if err != nil { log.Fatalf("failed to open db: %v", err) } defer db.Close() - migrationsDir := migrationsDir() - if err := dbpkg.RunMigrations(db, migrationsDir); err != nil { - log.Fatalf("failed to run migrations: %v", err) + queries, err := sqlite.Init(db) + if err != nil { + log.Fatalf("failed to initialize database: %v", err) } - queries := dbpkg.New(db) jikanClient := jikan.NewClient(queries) ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) @@ -139,24 +71,6 @@ func main() { } } -func dbFile() string { - if f := os.Getenv("DATABASE_FILE"); f != "" { - return f - } - return "mal.db" -} - -func migrationsDir() string { - if dir := os.Getenv("MIGRATIONS_DIR"); dir != "" { - return dir - } - wd, err := os.Getwd() - if err != nil { - log.Fatalf("failed to get working directory: %v", err) - } - return filepath.Join(wd, "migrations") -} - func playbackSecret() string { secret := os.Getenv("PLAYBACK_PROXY_SECRET") if len(secret) < 32 { diff --git a/cmd/user/main.go b/cmd/user/main.go new file mode 100644 index 0000000..56b72cb --- /dev/null +++ b/cmd/user/main.go @@ -0,0 +1,73 @@ +package main + +import ( + "bufio" + "database/sql" + "fmt" + "log" + "os" + "strings" + + "github.com/google/uuid" + "golang.org/x/crypto/bcrypt" + "mal/internal/db/sqlite" +) + +func main() { + if len(os.Args) != 3 { + log.Fatalf("Usage: go run cmd/user/main.go ") + } + + username := os.Args[1] + password := os.Args[2] + + db, err := sqlite.Open(sqlite.GetDBFile()) + if err != nil { + log.Fatalf("failed to open db: %v", err) + } + defer db.Close() + + var existingID string + err = db.QueryRow("SELECT id FROM user WHERE username = ?", username).Scan(&existingID) + if err != nil && err != sql.ErrNoRows { + log.Fatalf("database error: %v", err) + } + + if err == nil { + fmt.Printf("User '%s' already exists. Do you want to overwrite their password? [y/N]: ", username) + reader := bufio.NewReader(os.Stdin) + response, _ := reader.ReadString('\n') + response = strings.TrimSpace(strings.ToLower(response)) + + if response != "y" && response != "yes" { + fmt.Println("Operation cancelled.") + return + } + + hash, err := bcrypt.GenerateFromPassword([]byte(password), 12) + if err != nil { + log.Fatalf("failed to hash password: %v", err) + } + + _, err = db.Exec("UPDATE user SET password_hash = ? WHERE id = ?", string(hash), existingID) + if err != nil { + log.Fatalf("failed to update user: %v", err) + } + + fmt.Printf("Password for '%s' updated successfully!\n", username) + return + } + + hash, err := bcrypt.GenerateFromPassword([]byte(password), 12) + if err != nil { + log.Fatalf("failed to hash password: %v", err) + } + + id := uuid.New().String() + _, err = db.Exec("INSERT INTO user (id, username, password_hash) VALUES (?, ?, ?)", id, username, string(hash)) + if err != nil { + log.Fatalf("failed to create user: %v", err) + } + + fmt.Printf("User '%s' was created successfully!\n", username) +} diff --git a/go.mod b/go.mod index c1342a5..5a10afa 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/andybalholm/brotli v1.1.0 // indirect github.com/andybalholm/cascadia v1.3.3 // indirect github.com/klauspost/compress v1.17.4 // indirect - golang.org/x/sync v0.20.0 // indirect + golang.org/x/sync v0.20.0 // direct golang.org/x/sys v0.43.0 // indirect golang.org/x/text v0.36.0 // indirect ) diff --git a/internal/db/sqlite/sqlite.go b/internal/db/sqlite/sqlite.go new file mode 100644 index 0000000..ba6df68 --- /dev/null +++ b/internal/db/sqlite/sqlite.go @@ -0,0 +1,48 @@ +package sqlite + +import ( + "database/sql" + "fmt" + "os" + "path/filepath" + + _ "github.com/mattn/go-sqlite3" + "mal/internal/db" +) + +func Open(dbFile string) (*sql.DB, error) { + db, err := sql.Open("sqlite3", fmt.Sprintf("file:%s?_foreign_keys=on", dbFile)) + if err != nil { + return nil, fmt.Errorf("failed to open db: %w", err) + } + return db, nil +} + +func GetDBFile() string { + if f := os.Getenv("DATABASE_FILE"); f != "" { + return f + } + return "mal.db" +} + +func GetMigrationsDir() (string, error) { + if dir := os.Getenv("MIGRATIONS_DIR"); dir != "" { + return dir, nil + } + wd, err := os.Getwd() + if err != nil { + return "", fmt.Errorf("failed to get working directory: %w", err) + } + return filepath.Join(wd, "migrations"), nil +} + +func Init(db *sql.DB) (*database.Queries, error) { + migrationsDir, err := GetMigrationsDir() + if err != nil { + return nil, err + } + if err := database.RunMigrations(db, migrationsDir); err != nil { + return nil, fmt.Errorf("failed to run migrations: %w", err) + } + return database.New(db), nil +}