Add comprehensive human-readable comments to all Go files
- Comments explain what each function does and why - Fixed unused strconv import in queue.go - Better documentation for SMTP email service - Explained journalctl log parsing in logs.go - Clarified queue operations in queue.go
This commit is contained in:
parent
3e3620a24f
commit
68285d861a
9 changed files with 728 additions and 203 deletions
|
|
@ -2,53 +2,101 @@ package db
|
|||
|
||||
import "time"
|
||||
|
||||
// =============================================================================
|
||||
// Admin User (ImcUser) Operations
|
||||
// These functions manage admin users who can log into the web interface.
|
||||
// =============================================================================
|
||||
|
||||
// GetImcUserByUsername looks up an admin user by their username.
|
||||
// Returns the user with their associated domain permissions preloaded.
|
||||
// username: the username to search for.
|
||||
// Returns: the user and any error.
|
||||
func (d *DB) GetImcUserByUsername(username string) (*ImcUser, error) {
|
||||
var user ImcUser
|
||||
// Preload loads related data (Domains) to avoid N+1 queries.
|
||||
// Where creates a SQL WHERE clause, ? is a placeholder for the username.
|
||||
if err := d.Preload("Domains.Domain").Where("username = ?", username).First(&user).Error; err != nil {
|
||||
return nil, err
|
||||
return nil, err // Return nil user and the error
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
// GetImcUserByID looks up an admin user by their ID.
|
||||
// Returns the user with their associated domain permissions preloaded.
|
||||
// id: the user's primary key ID.
|
||||
// Returns: the user and any error.
|
||||
func (d *DB) GetImcUserByID(id uint) (*ImcUser, error) {
|
||||
var user ImcUser
|
||||
// First(&user, id) looks up by primary key.
|
||||
if err := d.Preload("Domains.Domain").First(&user, id).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
// CreateImcUser creates a new admin user account.
|
||||
// username: the login username (must be unique).
|
||||
// passwordHash: the bcrypt hash of the password (NOT the plain password).
|
||||
// role: "admin" or "user".
|
||||
// Returns: the created user and any error.
|
||||
func (d *DB) CreateImcUser(username, passwordHash, role string) (*ImcUser, error) {
|
||||
user := ImcUser{
|
||||
Username: username,
|
||||
PasswordHash: passwordHash,
|
||||
Role: role,
|
||||
}
|
||||
// Create inserts the record into the database.
|
||||
if err := d.Create(&user).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
// UpdateImcUserPassword updates the password hash for a user.
|
||||
// id: the user's ID.
|
||||
// passwordHash: the new bcrypt hash of the password.
|
||||
func (d *DB) UpdateImcUserPassword(id uint, passwordHash string) error {
|
||||
// Model specifies which table/struct to update.
|
||||
// Where filters which rows to update.
|
||||
// Update only changes the specified fields.
|
||||
return d.Model(&ImcUser{}).Where("id = ?", id).Update("password_hash", passwordHash).Error
|
||||
}
|
||||
|
||||
// DeleteImcUser removes an admin user from the database.
|
||||
// id: the user's ID to delete.
|
||||
func (d *DB) DeleteImcUser(id uint) error {
|
||||
return d.Delete(&ImcUser{}, id).Error
|
||||
return d.Delete(&ImcUser{}, id).Error // Delete by primary key
|
||||
}
|
||||
|
||||
// EnsureAdminUser creates the admin user if it doesn't exist, or updates the password if it does.
|
||||
// This is used during --reset-admin-password and startup.
|
||||
// username: always "admin" in our case.
|
||||
// passwordHash: the bcrypt hash of the password.
|
||||
func (d *DB) EnsureAdminUser(username, passwordHash string) error {
|
||||
var user ImcUser
|
||||
// Try to find an existing admin user with this username.
|
||||
err := d.Where("username = ? AND role = ?", username, "admin").First(&user).Error
|
||||
if err == nil {
|
||||
// User exists - update their password.
|
||||
return d.Model(&ImcUser{}).Where("id = ?", user.ID).Update("password_hash", passwordHash).Error
|
||||
}
|
||||
// User doesn't exist - create them.
|
||||
_, err = d.CreateImcUser(username, passwordHash, "admin")
|
||||
return err
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Domain Access Control
|
||||
// These functions manage which domains users can access.
|
||||
// =============================================================================
|
||||
|
||||
// GetUserAccessibleDomains returns all domains a user can access.
|
||||
// Admin users can access all domains; regular users only their assigned domains.
|
||||
// userID: the ID of the user.
|
||||
// isAdmin: whether the user has admin privileges.
|
||||
// Returns: a list of accessible domains.
|
||||
func (d *DB) GetUserAccessibleDomains(userID uint, isAdmin bool) ([]Domain, error) {
|
||||
// Admins get access to all domains.
|
||||
if isAdmin {
|
||||
var domains []Domain
|
||||
if err := d.Find(&domains).Error; err != nil {
|
||||
|
|
@ -57,38 +105,51 @@ func (d *DB) GetUserAccessibleDomains(userID uint, isAdmin bool) ([]Domain, erro
|
|||
return domains, nil
|
||||
}
|
||||
|
||||
// Regular users query the imc_users2domains table.
|
||||
// We use raw SQL with Joins because GORM's association methods
|
||||
// don't handle our cross-database queries well.
|
||||
var domains []Domain
|
||||
err := d.Table("imc_users2domains").
|
||||
Select("virtual_domains.*").
|
||||
Joins("JOIN virtual_domains ON virtual_domains.id = imc_users2domains.domain_id").
|
||||
Where("imc_users2domains.user_id = ?", userID).
|
||||
Find(&domains).Error
|
||||
err := d.Table("imc_users2domains"). // Query from this table
|
||||
Select("virtual_domains.*"). // Select all columns from domains
|
||||
Joins("JOIN virtual_domains ON virtual_domains.id = imc_users2domains.domain_id"). // Join to get domain details
|
||||
Where("imc_users2domains.user_id = ?", userID). // Only this user's domains
|
||||
Find(&domains).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if domains == nil {
|
||||
domains = []Domain{}
|
||||
domains = []Domain{} // Return empty slice, not nil
|
||||
}
|
||||
return domains, nil
|
||||
}
|
||||
|
||||
// CanAccessDomain checks if a user can access a specific domain.
|
||||
// userID: the ID of the user.
|
||||
// domainName: the name of the domain (e.g., "example.org").
|
||||
// isAdmin: whether the user has admin privileges.
|
||||
// Returns: true if the user can access the domain, false otherwise.
|
||||
func (d *DB) CanAccessDomain(userID uint, domainName string, isAdmin bool) (bool, error) {
|
||||
// Admins can access all domains.
|
||||
if isAdmin {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Count matching records - if count > 0, access is granted.
|
||||
var count int64
|
||||
err := d.Table("imc_users2domains").
|
||||
Select("COUNT(*)").
|
||||
Joins("JOIN virtual_domains ON virtual_domains.id = imc_users2domains.domain_id").
|
||||
Where("imc_users2domains.user_id = ? AND virtual_domains.name = ?", userID, domainName).
|
||||
Select("COUNT(*)"). // Count matching rows
|
||||
Joins("JOIN virtual_domains ON virtual_domains.id = imc_users2domains.domain_id"). // Join to domain table
|
||||
Where("imc_users2domains.user_id = ? AND virtual_domains.name = ?", userID, domainName). // Match user and domain name
|
||||
Count(&count).Error
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return count > 0, nil
|
||||
return count > 0, nil // True if at least one match found
|
||||
}
|
||||
|
||||
// AddUserToDomain grants a user access to a domain.
|
||||
// userID: the ID of the user.
|
||||
// domainID: the ID of the domain.
|
||||
func (d *DB) AddUserToDomain(userID, domainID uint) error {
|
||||
ud := ImcUserDomain{
|
||||
UserID: userID,
|
||||
|
|
@ -97,15 +158,22 @@ func (d *DB) AddUserToDomain(userID, domainID uint) error {
|
|||
return d.Create(&ud).Error
|
||||
}
|
||||
|
||||
// RemoveUserFromDomain revokes a user's access to a domain.
|
||||
// userID: the ID of the user.
|
||||
// domainID: the ID of the domain.
|
||||
func (d *DB) RemoveUserFromDomain(userID, domainID uint) error {
|
||||
// Delete records matching both user_id and domain_id.
|
||||
return d.Where("user_id = ? AND domain_id = ?", userID, domainID).Delete(&ImcUserDomain{}).Error
|
||||
}
|
||||
|
||||
// GetUsersForDomain returns all admin users who can access a specific domain.
|
||||
// domainID: the ID of the domain.
|
||||
// Returns: a list of users who can access this domain.
|
||||
func (d *DB) GetUsersForDomain(domainID uint) ([]ImcUser, error) {
|
||||
var users []ImcUser
|
||||
err := d.Table("imc_users2domains").
|
||||
Select("imc_users.*").
|
||||
Joins("JOIN imc_users ON imc_users.id = imc_users2domains.user_id").
|
||||
Select("imc_users.*"). // Select all columns from imc_users
|
||||
Joins("JOIN imc_users ON imc_users.id = imc_users2domains.user_id"). // Join to users table
|
||||
Where("imc_users2domains.domain_id = ?", domainID).
|
||||
Find(&users).Error
|
||||
if err != nil {
|
||||
|
|
@ -117,12 +185,25 @@ func (d *DB) GetUsersForDomain(domainID uint) ([]ImcUser, error) {
|
|||
return users, nil
|
||||
}
|
||||
|
||||
// IsUserInDomain checks if a user already has access to a domain.
|
||||
// userID: the ID of the user.
|
||||
// domainID: the ID of the domain.
|
||||
// Returns: true if the user already has access, false otherwise.
|
||||
func (d *DB) IsUserInDomain(userID, domainID uint) (bool, error) {
|
||||
var count int64
|
||||
err := d.Model(&ImcUserDomain{}).Where("user_id = ? AND domain_id = ?", userID, domainID).Count(&count).Error
|
||||
return count > 0, err
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Password Reset Tokens
|
||||
// These functions manage one-time tokens for password reset.
|
||||
// =============================================================================
|
||||
|
||||
// CreatePasswordResetToken creates a new password reset token.
|
||||
// userID: the ID of the user requesting password reset.
|
||||
// token: the random token string.
|
||||
// expiresAt: when this token becomes invalid.
|
||||
func (d *DB) CreatePasswordResetToken(userID uint, token string, expiresAt time.Time) error {
|
||||
resetToken := PasswordResetToken{
|
||||
UserID: userID,
|
||||
|
|
@ -132,8 +213,12 @@ func (d *DB) CreatePasswordResetToken(userID uint, token string, expiresAt time.
|
|||
return d.Create(&resetToken).Error
|
||||
}
|
||||
|
||||
// GetValidPasswordResetToken looks up a valid (unused, not expired) reset token.
|
||||
// token: the token string to look up.
|
||||
// Returns: the token if valid, or an error if not found/expired/used.
|
||||
func (d *DB) GetValidPasswordResetToken(token string) (*PasswordResetToken, error) {
|
||||
var resetToken PasswordResetToken
|
||||
// Check token exists, hasn't been used, and hasn't expired.
|
||||
err := d.Where("token = ? AND used = false AND expires_at > ?", token, time.Now()).First(&resetToken).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -141,6 +226,8 @@ func (d *DB) GetValidPasswordResetToken(token string) (*PasswordResetToken, erro
|
|||
return &resetToken, nil
|
||||
}
|
||||
|
||||
// MarkResetTokenUsed marks a token as used (prevents reuse).
|
||||
// token: the token string to mark as used.
|
||||
func (d *DB) MarkResetTokenUsed(token string) error {
|
||||
return d.Model(&PasswordResetToken{}).Where("token = ?", token).Update("used", true).Error
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue