208 righe
4.1 KiB
Go
208 righe
4.1 KiB
Go
package config
|
|
|
|
import (
|
|
"encoding/json"
|
|
"log"
|
|
"os"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
type Config struct {
|
|
Port string
|
|
CertDir string
|
|
AccountsDir string
|
|
DataDir string
|
|
}
|
|
|
|
func Load() *Config {
|
|
port := os.Getenv("PORT")
|
|
if port == "" {
|
|
port = "8080"
|
|
}
|
|
certDir := os.Getenv("CERT_DIR")
|
|
if certDir == "" {
|
|
certDir = "./data/certs"
|
|
}
|
|
accountsDir := os.Getenv("ACCOUNTS_DIR")
|
|
if accountsDir == "" {
|
|
accountsDir = "./data/accounts"
|
|
}
|
|
dataDir := os.Getenv("DATA_DIR")
|
|
if dataDir == "" {
|
|
dataDir = "./data"
|
|
}
|
|
|
|
return &Config{
|
|
Port: port,
|
|
CertDir: certDir,
|
|
AccountsDir: accountsDir,
|
|
DataDir: dataDir,
|
|
}
|
|
}
|
|
|
|
// CertStore is the in-memory store for certificates with file persistence
|
|
type CertStore struct {
|
|
mu sync.RWMutex
|
|
data map[string]*Certificate // key: domain
|
|
path string
|
|
nextID uint
|
|
}
|
|
|
|
var Store *CertStore
|
|
|
|
func InitStore(cfg *Config) {
|
|
// Ensure data directories exist
|
|
dirs := []string{cfg.DataDir, cfg.CertDir, cfg.AccountsDir}
|
|
for _, d := range dirs {
|
|
if err := os.MkdirAll(d, 0700); err != nil {
|
|
log.Fatalf("Failed to create directory %s: %v", d, err)
|
|
}
|
|
}
|
|
|
|
Store = &CertStore{
|
|
data: make(map[string]*Certificate),
|
|
path: cfg.DataDir + "/certs.json",
|
|
}
|
|
|
|
if err := Store.Load(); err != nil {
|
|
log.Printf("No existing cert store or failed to load: %v, starting fresh", err)
|
|
}
|
|
|
|
log.Println("Cert store initialized successfully")
|
|
}
|
|
|
|
// Load reads certificates from JSON file into memory
|
|
func (s *CertStore) Load() error {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
data, err := os.ReadFile(s.path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var certs []*Certificate
|
|
if err := json.Unmarshal(data, &certs); err != nil {
|
|
return err
|
|
}
|
|
|
|
s.data = make(map[string]*Certificate)
|
|
s.nextID = 0
|
|
for _, c := range certs {
|
|
s.data[c.Domain] = c
|
|
if c.ID >= s.nextID {
|
|
s.nextID = c.ID + 1
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// save writes the current in-memory certificates to JSON file
|
|
// IMPORTANT: caller must hold the lock (read or write) before calling this
|
|
func (s *CertStore) save() error {
|
|
certs := make([]*Certificate, 0, len(s.data))
|
|
for _, c := range s.data {
|
|
certs = append(certs, c)
|
|
}
|
|
|
|
data, err := json.MarshalIndent(certs, "", " ")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return os.WriteFile(s.path, data, 0600)
|
|
}
|
|
|
|
// Save writes the current in-memory certificates to JSON file (public, acquires own lock)
|
|
func (s *CertStore) Save() error {
|
|
s.mu.RLock()
|
|
defer s.mu.RUnlock()
|
|
return s.save()
|
|
}
|
|
|
|
// GetAll returns all certificates sorted by ID descending
|
|
func (s *CertStore) GetAll() []*Certificate {
|
|
s.mu.RLock()
|
|
defer s.mu.RUnlock()
|
|
|
|
certs := make([]*Certificate, 0, len(s.data))
|
|
for _, c := range s.data {
|
|
certs = append(certs, c)
|
|
}
|
|
|
|
// Sort by ID descending (newest first)
|
|
for i := 0; i < len(certs)-1; i++ {
|
|
for j := i + 1; j < len(certs); j++ {
|
|
if certs[i].ID < certs[j].ID {
|
|
certs[i], certs[j] = certs[j], certs[i]
|
|
}
|
|
}
|
|
}
|
|
return certs
|
|
}
|
|
|
|
// GetByDomain returns a certificate by domain
|
|
func (s *CertStore) GetByDomain(domain string) *Certificate {
|
|
s.mu.RLock()
|
|
defer s.mu.RUnlock()
|
|
return s.data[domain]
|
|
}
|
|
|
|
// GetByID returns a certificate by ID
|
|
func (s *CertStore) GetByID(id uint) *Certificate {
|
|
s.mu.RLock()
|
|
defer s.mu.RUnlock()
|
|
for _, c := range s.data {
|
|
if c.ID == id {
|
|
return c
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Upsert inserts or updates a certificate
|
|
func (s *CertStore) Upsert(cert *Certificate) error {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
if cert.ID == 0 {
|
|
cert.ID = s.nextID
|
|
s.nextID++
|
|
cert.CreatedAt = time.Now()
|
|
}
|
|
cert.UpdatedAt = time.Now()
|
|
|
|
s.data[cert.Domain] = cert
|
|
return s.save()
|
|
}
|
|
|
|
// Delete removes a certificate by ID
|
|
func (s *CertStore) Delete(id uint) error {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
for domain, c := range s.data {
|
|
if c.ID == id {
|
|
delete(s.data, domain)
|
|
return s.save()
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetActiveWithAutoRenew returns all active certs with auto-renewal enabled
|
|
func (s *CertStore) GetActiveWithAutoRenew() []*Certificate {
|
|
s.mu.RLock()
|
|
defer s.mu.RUnlock()
|
|
|
|
var result []*Certificate
|
|
for _, c := range s.data {
|
|
if c.AutoRenew && c.Status == "active" {
|
|
result = append(result, c)
|
|
}
|
|
}
|
|
return result
|
|
}
|