tracks/internal/database/database.go
Claude 4e9e0b4efa
Add default admin user and admin-only user creation
Features added:
- Automatic creation of default admin user on first startup (login: admin, password: admin)
- Admin-only endpoint POST /api/admin/users for creating new users
- Admin users can set is_admin flag when creating users
- Non-admin users are blocked from accessing admin endpoints

Implementation:
- Added CreateDefaultAdmin() function in internal/database/database.go
  - Checks if any users exist, creates admin only if database is empty
  - Admin user: login "admin", password "admin", is_admin true
- Added CreateUser() method to auth service for admin user creation
- Added CreateUser() handler to auth handler
- Added /api/admin/users endpoint with AuthMiddleware + AdminMiddleware
- Updated README_GOLANG.md with:
  - Default admin credentials
  - Instructions for creating additional users
  - Admin API documentation

Security:
- Default admin password should be changed after first login
- AdminMiddleware ensures only users with is_admin=true can access admin routes
- Non-admin users receive 403 Forbidden when accessing admin endpoints

Tested:
- Default admin creation on startup ✓
- Admin login with default credentials ✓
- Admin can create new users ✓
- New users can login ✓
- Non-admin users blocked from admin endpoints ✓
2025-11-05 11:35:36 +00:00

145 lines
3.1 KiB
Go

package database
import (
"fmt"
"log"
"time"
"github.com/TracksApp/tracks/internal/config"
"github.com/TracksApp/tracks/internal/models"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
// DB is the global database instance
var DB *gorm.DB
// Initialize sets up the database connection
func Initialize(cfg *config.DatabaseConfig) error {
db, err := gorm.Open(sqlite.Open(cfg.Name), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info),
NowFunc: func() time.Time {
return time.Now().UTC()
},
})
if err != nil {
return fmt.Errorf("failed to connect to database: %w", err)
}
// Set connection pool settings
sqlDB, err := db.DB()
if err != nil {
return fmt.Errorf("failed to get database instance: %w", err)
}
sqlDB.SetMaxIdleConns(10)
sqlDB.SetMaxOpenConns(100)
sqlDB.SetConnMaxLifetime(time.Hour)
DB = db
log.Println("Database connection established")
return nil
}
// AutoMigrate runs database migrations
func AutoMigrate() error {
if DB == nil {
return fmt.Errorf("database not initialized")
}
log.Println("Running database migrations...")
err := DB.AutoMigrate(
&models.User{},
&models.Preference{},
&models.Context{},
&models.Project{},
&models.Todo{},
&models.RecurringTodo{},
&models.Tag{},
&models.Tagging{},
&models.Dependency{},
&models.Note{},
&models.Attachment{},
)
if err != nil {
return fmt.Errorf("failed to run migrations: %w", err)
}
log.Println("Database migrations completed")
return nil
}
// Close closes the database connection
func Close() error {
if DB == nil {
return nil
}
sqlDB, err := DB.DB()
if err != nil {
return err
}
return sqlDB.Close()
}
// GetDB returns the database instance
func GetDB() *gorm.DB {
return DB
}
// CreateDefaultAdmin creates a default admin user if no users exist
func CreateDefaultAdmin() error {
if DB == nil {
return fmt.Errorf("database not initialized")
}
// Check if any users exist
var count int64
if err := DB.Model(&models.User{}).Count(&count).Error; err != nil {
return fmt.Errorf("failed to count users: %w", err)
}
// If users exist, don't create default admin
if count > 0 {
log.Println("Users already exist, skipping default admin creation")
return nil
}
log.Println("Creating default admin user (login: admin, password: admin)")
// Create default admin user
admin := models.User{
Login: "admin",
FirstName: "Admin",
LastName: "User",
IsAdmin: true,
AuthType: models.AuthTypeDatabase,
Token: "default-admin-token",
}
// Set password
if err := admin.SetPassword("admin"); err != nil {
return fmt.Errorf("failed to set admin password: %w", err)
}
// Save admin user
if err := DB.Create(&admin).Error; err != nil {
return fmt.Errorf("failed to create admin user: %w", err)
}
// Create default preference for admin
preference := models.Preference{
UserID: admin.ID,
}
if err := DB.Create(&preference).Error; err != nil {
return fmt.Errorf("failed to create admin preference: %w", err)
}
log.Printf("Default admin user created successfully (ID: %d)", admin.ID)
return nil
}