Add CLI flags for setup and server configuration
- Add --setup flag to create admin user - Add --admin-user and --admin-password for setup - Generate cryptographically random passwords for --setup - Add --bind and --port flags for server binding - Add BIND env var support - Add SMTP email service for password reset - Add password reset token storage - Add auth header to queue/logs frontend pages - Fix journalctl timestamp format - Fix logs to return empty array instead of error when no entries - Fix queue handler to return proper error message - Hide Users/Aliases nav links when no domain selected
This commit is contained in:
parent
2c3a4c3daa
commit
53ce3b06ae
14 changed files with 435 additions and 69 deletions
|
|
@ -78,6 +78,17 @@ type ImcLoginAttempt struct {
|
|||
|
||||
func (ImcLoginAttempt) TableName() string { return "imc_login_attempts" }
|
||||
|
||||
type PasswordResetToken struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
UserID uint `gorm:"not null" json:"userId"`
|
||||
Token string `gorm:"size:64;not null;uniqueIndex" json:"token"`
|
||||
ExpiresAt time.Time `gorm:"not null" json:"expiresAt"`
|
||||
Used bool `gorm:"default:false" json:"used"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
}
|
||||
|
||||
func (PasswordResetToken) TableName() string { return "imc_password_reset_tokens" }
|
||||
|
||||
type DomainStats struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
|
|
@ -160,5 +171,21 @@ func (d *DB) InitSchema() error {
|
|||
INDEX idx_domain_id (domain_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
||||
`
|
||||
return d.Exec(users2DomainsSQL).Error
|
||||
if err := d.Exec(users2DomainsSQL).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resetTokensSQL := `
|
||||
CREATE TABLE IF NOT EXISTS imc_password_reset_tokens (
|
||||
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id INT UNSIGNED NOT NULL,
|
||||
token VARCHAR(64) NOT NULL UNIQUE,
|
||||
expires_at DATETIME NOT NULL,
|
||||
used BOOLEAN DEFAULT FALSE,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
INDEX idx_token (token),
|
||||
INDEX idx_user_id (user_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
||||
`
|
||||
return d.Exec(resetTokensSQL).Error
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
package db
|
||||
|
||||
import "time"
|
||||
|
||||
func (d *DB) GetImcUserByUsername(username string) (*ImcUser, error) {
|
||||
var user ImcUser
|
||||
if err := d.Preload("Domains.Domain").Where("username = ?", username).First(&user).Error; err != nil {
|
||||
|
|
@ -120,3 +122,25 @@ func (d *DB) IsUserInDomain(userID, domainID uint) (bool, error) {
|
|||
err := d.Model(&ImcUserDomain{}).Where("user_id = ? AND domain_id = ?", userID, domainID).Count(&count).Error
|
||||
return count > 0, err
|
||||
}
|
||||
|
||||
func (d *DB) CreatePasswordResetToken(userID uint, token string, expiresAt time.Time) error {
|
||||
resetToken := PasswordResetToken{
|
||||
UserID: userID,
|
||||
Token: token,
|
||||
ExpiresAt: expiresAt,
|
||||
}
|
||||
return d.Create(&resetToken).Error
|
||||
}
|
||||
|
||||
func (d *DB) GetValidPasswordResetToken(token string) (*PasswordResetToken, error) {
|
||||
var resetToken PasswordResetToken
|
||||
err := d.Where("token = ? AND used = false AND expires_at > ?", token, time.Now()).First(&resetToken).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resetToken, nil
|
||||
}
|
||||
|
||||
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