feat: db migrations, encrypted passwords
All checks were successful
ci/woodpecker/tag/release Pipeline was successful
All checks were successful
ci/woodpecker/tag/release Pipeline was successful
This commit is contained in:
parent
84e5feeb81
commit
ece8280358
8 changed files with 490 additions and 65 deletions
|
@ -1,14 +1,15 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"database/sql"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
_ "modernc.org/sqlite"
|
||||
|
||||
"git.nakama.town/fmartingr/butterrobot/internal/migration"
|
||||
"git.nakama.town/fmartingr/butterrobot/internal/model"
|
||||
)
|
||||
|
||||
|
@ -505,7 +506,10 @@ func (d *Database) GetUserByID(id int64) (*model.User, error) {
|
|||
// CreateUser creates a new user
|
||||
func (d *Database) CreateUser(username, password string) (*model.User, error) {
|
||||
// Hash password
|
||||
hashedPassword := hashPassword(password)
|
||||
hashedPassword, err := hashPassword(password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Insert user
|
||||
query := `
|
||||
|
@ -555,9 +559,9 @@ func (d *Database) CheckCredentials(username, password string) (*model.User, err
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// Check password
|
||||
hashedPassword := hashPassword(password)
|
||||
if dbPassword != hashedPassword {
|
||||
// Check password with bcrypt
|
||||
err = bcrypt.CompareHashAndPassword([]byte(dbPassword), []byte(password))
|
||||
if err != nil {
|
||||
return nil, errors.New("invalid credentials")
|
||||
}
|
||||
|
||||
|
@ -569,73 +573,60 @@ func (d *Database) CheckCredentials(username, password string) (*model.User, err
|
|||
}
|
||||
|
||||
// Helper function to hash password
|
||||
func hashPassword(password string) string {
|
||||
// In a real implementation, use a proper password hashing library like bcrypt
|
||||
// This is a simplified version for demonstration
|
||||
hasher := sha256.New()
|
||||
hasher.Write([]byte(password))
|
||||
return hex.EncodeToString(hasher.Sum(nil))
|
||||
func hashPassword(password string) (string, error) {
|
||||
// Use bcrypt for secure password hashing
|
||||
// The cost parameter is the computational cost, higher is more secure but slower
|
||||
// Recommended minimum is 12
|
||||
hashedBytes, err := bcrypt.GenerateFromPassword([]byte(password), 12)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(hashedBytes), nil
|
||||
}
|
||||
|
||||
// Initialize database tables
|
||||
func initDatabase(db *sql.DB) error {
|
||||
// Create channels table
|
||||
_, err := db.Exec(`
|
||||
CREATE TABLE IF NOT EXISTS channels (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
platform TEXT NOT NULL,
|
||||
platform_channel_id TEXT NOT NULL,
|
||||
enabled BOOLEAN NOT NULL DEFAULT 0,
|
||||
channel_raw TEXT NOT NULL,
|
||||
UNIQUE(platform, platform_channel_id)
|
||||
)
|
||||
`)
|
||||
if err != nil {
|
||||
return err
|
||||
// Ensure migration table exists
|
||||
if err := migration.EnsureMigrationTable(db); err != nil {
|
||||
return fmt.Errorf("failed to create migration table: %w", err)
|
||||
}
|
||||
|
||||
// Create channel_plugin table
|
||||
_, err = db.Exec(`
|
||||
CREATE TABLE IF NOT EXISTS channel_plugin (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
channel_id INTEGER NOT NULL,
|
||||
plugin_id TEXT NOT NULL,
|
||||
enabled BOOLEAN NOT NULL DEFAULT 0,
|
||||
config TEXT NOT NULL DEFAULT '{}',
|
||||
UNIQUE(channel_id, plugin_id),
|
||||
FOREIGN KEY (channel_id) REFERENCES channels (id) ON DELETE CASCADE
|
||||
)
|
||||
`)
|
||||
|
||||
// Get applied migrations
|
||||
applied, err := migration.GetAppliedMigrations(db)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("failed to get applied migrations: %w", err)
|
||||
}
|
||||
|
||||
// Create users table
|
||||
_, err = db.Exec(`
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
username TEXT NOT NULL UNIQUE,
|
||||
password TEXT NOT NULL
|
||||
)
|
||||
`)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
// Get all migration versions
|
||||
allMigrations := make([]int, 0, len(migration.Migrations))
|
||||
for version := range migration.Migrations {
|
||||
allMigrations = append(allMigrations, version)
|
||||
}
|
||||
|
||||
// Create default admin user if it doesn't exist
|
||||
var count int
|
||||
err = db.QueryRow("SELECT COUNT(*) FROM users").Scan(&count)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
// Create a map of applied migrations for quick lookup
|
||||
appliedMap := make(map[int]bool)
|
||||
for _, version := range applied {
|
||||
appliedMap[version] = true
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
hashedPassword := hashPassword("admin")
|
||||
_, err = db.Exec("INSERT INTO users (username, password) VALUES (?, ?)", "admin", hashedPassword)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
// Count pending migrations
|
||||
pendingCount := 0
|
||||
for _, version := range allMigrations {
|
||||
if !appliedMap[version] {
|
||||
pendingCount++
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Run migrations if needed
|
||||
if pendingCount > 0 {
|
||||
fmt.Printf("Running %d pending database migrations...\n", pendingCount)
|
||||
if err := migration.Migrate(db); err != nil {
|
||||
return fmt.Errorf("migration failed: %w", err)
|
||||
}
|
||||
fmt.Println("Database migrations completed successfully.")
|
||||
} else {
|
||||
fmt.Println("Database schema is up to date.")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue