Merge branch 'next' into feat/new-workflow

This commit is contained in:
Fu Diwei 2025-01-17 18:07:50 +08:00
commit ee2cca17fe
4 changed files with 76 additions and 3 deletions

View File

@ -9,6 +9,7 @@ import (
"github.com/go-acme/lego/v4/lego"
"github.com/go-acme/lego/v4/registration"
"golang.org/x/sync/singleflight"
"github.com/usual2970/certimate/internal/domain"
"github.com/usual2970/certimate/internal/pkg/utils/certs"
@ -79,9 +80,21 @@ type acmeAccountRepository interface {
Save(ca, email, key string, resource *registration.Resource) error
}
func registerAcmeUser(client *lego.Client, sslProviderConfig *acmeSSLProviderConfig, user *acmeUser) (*registration.Resource, error) {
// TODO: fix 潜在的并发问题
var registerGroup singleflight.Group
func registerAcmeUser(client *lego.Client, sslProviderConfig *acmeSSLProviderConfig, user *acmeUser) (*registration.Resource, error) {
resp, err, _ := registerGroup.Do(fmt.Sprintf("register_acme_user_%s_%s", sslProviderConfig.Provider, user.GetEmail()), func() (interface{}, error) {
return register(client, sslProviderConfig, user)
})
if err != nil {
return nil, err
}
return resp.(*registration.Resource), nil
}
func register(client *lego.Client, sslProviderConfig *acmeSSLProviderConfig, user *acmeUser) (*registration.Resource, error) {
var reg *registration.Resource
var err error
switch sslProviderConfig.Provider {

View File

@ -7,12 +7,14 @@ import (
"os"
"strconv"
"strings"
"sync"
"github.com/go-acme/lego/v4/certcrypto"
"github.com/go-acme/lego/v4/certificate"
"github.com/go-acme/lego/v4/challenge"
"github.com/go-acme/lego/v4/challenge/dns01"
"github.com/go-acme/lego/v4/lego"
"golang.org/x/time/rate"
"github.com/usual2970/certimate/internal/domain"
"github.com/usual2970/certimate/internal/pkg/utils/slices"
@ -184,6 +186,20 @@ type proxyApplicant struct {
options *applicantOptions
}
var limiters sync.Map
const (
limitBurst = 300
limitRate float64 = float64(1) / float64(36)
)
func getLimiter(key string) *rate.Limiter {
limiter, _ := limiters.LoadOrStore(key, rate.NewLimiter(rate.Limit(limitRate), 300))
return limiter.(*rate.Limiter)
}
func (d *proxyApplicant) Apply() (*ApplyCertResult, error) {
limiter := getLimiter(fmt.Sprintf("apply_%s", d.options.ContactEmail))
limiter.Wait(context.Background())
return apply(d.applicant, d.options)
}

View File

@ -0,0 +1,44 @@
package applicant
import (
"testing"
"time"
"golang.org/x/time/rate"
)
func TestRateLimit(t *testing.T) {
tests := []struct {
name string
burst int
rate rate.Limit
}{
{
name: "test1",
burst: 300,
rate: rate.Limit(float64(1) / float64(20)),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
rl := rate.NewLimiter(tt.rate, tt.burst)
if rl.Burst() != tt.burst {
t.Errorf("Burst() = %v, want %v", rl.Burst(), tt.burst)
}
if rl.Limit() != tt.rate {
t.Errorf("Limit() = %v, want %v", rl.Limit(), tt.rate)
}
t.Log("consume all tokens at once", rl.AllowN(time.Now(), tt.burst))
t.Log("consume more", rl.Allow())
time.Sleep(time.Second * 5)
t.Log("consume after 5 seconds", rl.Allow())
time.Sleep(time.Second * 20)
t.Log("consume after 20 seconds", rl.Allow())
})
}
}

View File

@ -1 +1 @@
export const version = "v0.3.0-alpha.8";
export const version = "v0.3.0-alpha.9";