package main import ( "fmt" "log" "path/filepath" "sync" "time" "github.com/go-git/go-billy/v5/memfs" "github.com/go-git/go-git/v5/storage/memory" ) var ( tickMu sync.Mutex mgr *ACMEManager mgrMu sync.Mutex ) func getACMEManager() (*ACMEManager, error) { mgrMu.Lock() defer mgrMu.Unlock() if mgr == nil { var err error mgr, err = NewACMEManager() if err != nil { return nil, err } } return mgr, nil } func initServer() { err := LoadDomainConfigs() if err != nil { log.Fatalf("Error loading domain configs: %v", err) } serverTick() } func serverTick() { tickMu.Lock() defer tickMu.Unlock() fmt.Println("Tick!") mgr, err := getACMEManager() if err != nil { fmt.Printf("Error getting acme manager: %v\n", err) return } now := time.Now().UTC() localDomainConfigs := domainStore.Snapshot() for domainStr, domainConfig := range localDomainConfigs { if !domainConfig.GetBool("Domain.enabled") { continue } renewPeriod := domainConfig.GetInt("Certificates.renew_period") lastIssued := time.Unix(domainConfig.GetInt64("Internal.last_issued"), 0).UTC() renewalDue := lastIssued.AddDate(0, 0, renewPeriod) if now.After(renewalDue) { //TODO extra check if certificate expiry (create cache?) _, err = mgr.RenewForDomain(domainStr) if err != nil { // if no existing cert, obtain instead _, err = mgr.ObtainForDomain(domainStr) if err != nil { fmt.Printf("Error obtaining domain certificates for domain %s: %v\n", domainStr, err) continue } } domainConfig.Set("Internal.last_issued", time.Now().UTC().Unix()) err = WriteDomainConfig(domainConfig) if err != nil { fmt.Printf("Error saving domain config %s: %v\n", domainStr, err) continue } err = EncryptFileXChaCha(domainConfig.GetString("Certificates.crypto_key"), filepath.Join(mgr.certsRoot, domainStr, domainStr+".crt"), filepath.Join(mgr.certsRoot, domainStr, domainStr+".crt.crpt"), nil) if err != nil { fmt.Printf("Error encrypting domain cert for domain %s: %v\n", domainStr, err) continue } err = EncryptFileXChaCha(domainConfig.GetString("Certificates.crypto_key"), filepath.Join(mgr.certsRoot, domainStr, domainStr+".key"), filepath.Join(mgr.certsRoot, domainStr, domainStr+".key.crpt"), nil) if err != nil { fmt.Printf("Error encrypting domain key for domain %s: %v\n", domainStr, err) continue } giteaClient := createGiteaClient() if giteaClient == nil { fmt.Printf("Error creating gitea client for domain %s: %v\n", domainStr, err) continue } gitWorkspace := &GitWorkspace{ Storage: memory.NewStorage(), FS: memfs.New(), } var repoUrl string if !domainConfig.GetBool("Internal.repo_exists") { repoUrl = createGiteaRepo(domainStr, giteaClient) if repoUrl == "" { fmt.Printf("Error creating Gitea repo for domain %s\n", domainStr) continue } domainConfig.Set("Internal.repo_exists", true) err = WriteDomainConfig(domainConfig) if err != nil { fmt.Printf("Error saving domain config %s: %v\n", domainStr, err) continue } err = initRepo(repoUrl, gitWorkspace) if err != nil { fmt.Printf("Error initializing repo for domain %s: %v\n", domainStr, err) continue } } else { repoUrl = config.GetString("Git.server") + "/" + config.GetString("Git.org_name") + "/" + domainStr + domainConfig.GetString("Repo.repo_suffix") + ".git" err = cloneRepo(repoUrl, gitWorkspace) if err != nil { fmt.Printf("Error cloning repo for domain %s: %v\n", domainStr, err) continue } } err = addAndPushCerts(domainStr, gitWorkspace) if err != nil { fmt.Printf("Error pushing certificates for domain %s: %v\n", domainStr, err) continue } fmt.Printf("Successfully pushed certificates for domain %s\n", domainStr) } } err = SaveDomainConfigs() if err != nil { fmt.Printf("Error saving domain configs: %v\n", err) } } func reloadServer() { fmt.Println("Reloading configs...") err := LoadDomainConfigs() if err != nil { fmt.Printf("Error loading domain configs: %v\n", err) return } mgrMu.Lock() mgr = nil mgrMu.Unlock() fmt.Println("Successfully reloaded configs") } func stopServer() { fmt.Println("Shutting down server") }