Files
Charon/backend/internal/models/manual_challenge.go
GitHub Actions d7939bed70 feat: add ManualDNSChallenge component and related hooks for manual DNS challenge management
- Implemented `useManualChallenge`, `useChallengePoll`, and `useManualChallengeMutations` hooks for managing manual DNS challenges.
- Created tests for the `useManualChallenge` hooks to ensure correct fetching and mutation behavior.
- Added `ManualDNSChallenge` component for displaying challenge details and actions.
- Developed end-to-end tests for the Manual DNS Provider feature, covering provider selection, challenge UI, and accessibility compliance.
- Included error handling tests for verification failures and network errors.
2026-01-12 04:01:40 +00:00

97 lines
3.5 KiB
Go

// Package models defines the database schema and domain types.
package models
import (
"time"
)
// ChallengeStatus represents the state of a manual DNS challenge.
type ChallengeStatus string
const (
// ChallengeStatusCreated indicates the challenge has been created but not yet processed.
ChallengeStatusCreated ChallengeStatus = "created"
// ChallengeStatusPending indicates the challenge is waiting for DNS propagation.
ChallengeStatusPending ChallengeStatus = "pending"
// ChallengeStatusVerifying indicates DNS record was found, verification in progress.
ChallengeStatusVerifying ChallengeStatus = "verifying"
// ChallengeStatusVerified indicates the challenge was successfully verified.
ChallengeStatusVerified ChallengeStatus = "verified"
// ChallengeStatusExpired indicates the challenge timed out.
ChallengeStatusExpired ChallengeStatus = "expired"
// ChallengeStatusFailed indicates the challenge failed.
ChallengeStatusFailed ChallengeStatus = "failed"
)
// ManualChallenge represents a manual DNS challenge for ACME DNS-01 validation.
// Users manually create the required TXT record at their DNS provider.
type ManualChallenge struct {
// ID is the primary key (UUIDv4, cryptographically random).
ID string `json:"id" gorm:"primaryKey;size:36"`
// ProviderID is the foreign key to the DNS provider.
ProviderID uint `json:"provider_id" gorm:"index;not null"`
// UserID is the foreign key for ownership validation.
UserID uint `json:"user_id" gorm:"index;not null"`
// FQDN is the fully qualified domain name for the TXT record.
// Example: "_acme-challenge.example.com"
FQDN string `json:"fqdn" gorm:"index;not null;size:255"`
// Token is the ACME challenge token (for identification).
Token string `json:"token" gorm:"size:255"`
// Value is the TXT record value that must be created.
Value string `json:"value" gorm:"not null;size:255"`
// Status is the current state of the challenge.
Status ChallengeStatus `json:"status" gorm:"index;not null;size:20;default:'created'"`
// ErrorMessage stores any error message if the challenge failed.
ErrorMessage string `json:"error_message,omitempty" gorm:"type:text"`
// DNSPropagated indicates if the DNS record has been detected.
DNSPropagated bool `json:"dns_propagated" gorm:"default:false"`
// CreatedAt is when the challenge was created.
CreatedAt time.Time `json:"created_at"`
// ExpiresAt is when the challenge will expire.
ExpiresAt time.Time `json:"expires_at" gorm:"index"`
// LastCheckAt is when DNS was last checked for propagation.
LastCheckAt *time.Time `json:"last_check_at,omitempty"`
// VerifiedAt is when the challenge was successfully verified.
VerifiedAt *time.Time `json:"verified_at,omitempty"`
}
// TableName specifies the database table name.
func (ManualChallenge) TableName() string {
return "manual_challenges"
}
// IsTerminal returns true if the challenge is in a terminal state.
func (c *ManualChallenge) IsTerminal() bool {
return c.Status == ChallengeStatusVerified ||
c.Status == ChallengeStatusExpired ||
c.Status == ChallengeStatusFailed
}
// IsActive returns true if the challenge is in an active state.
func (c *ManualChallenge) IsActive() bool {
return c.Status == ChallengeStatusCreated ||
c.Status == ChallengeStatusPending ||
c.Status == ChallengeStatusVerifying
}
// TimeRemaining returns the duration until the challenge expires.
func (c *ManualChallenge) TimeRemaining() time.Duration {
remaining := time.Until(c.ExpiresAt)
if remaining < 0 {
return 0
}
return remaining
}