feat(admin): authorize batch purchase starts
This commit is contained in:
@@ -5,7 +5,9 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
@@ -16,6 +18,9 @@ const (
|
||||
sessionSecretEnv = "CMBUYER_SESSION_SECRET"
|
||||
cookieSecureEnv = "CMBUYER_COOKIE_SECURE"
|
||||
databaseSourceEnv = "CMBUYER_DATABASE_SOURCE"
|
||||
authorizationTTLEnv = "CMBUYER_AUTHORIZATION_TTL"
|
||||
maxTaskQuantityEnv = "CMBUYER_MAX_TASK_QUANTITY"
|
||||
maxTotalPriceEnv = "CMBUYER_MAX_TOTAL_PRICE"
|
||||
minimumSecretLength = 32
|
||||
)
|
||||
|
||||
@@ -26,6 +31,9 @@ type Config struct {
|
||||
SessionSecret []byte
|
||||
CookieSecure bool
|
||||
DatabaseSource string
|
||||
AuthorizationTTL time.Duration
|
||||
MaxTaskQuantity int
|
||||
MaxTotalPrice string
|
||||
}
|
||||
|
||||
// LoadFromEnv 从进程环境读取配置。错误只指出缺失或非法的变量名,绝不回显秘密。
|
||||
@@ -71,6 +79,29 @@ func Load(lookup func(string) (string, bool)) (Config, error) {
|
||||
if err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
ttlText, err := required(lookup, authorizationTTLEnv)
|
||||
if err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
ttl, err := time.ParseDuration(ttlText)
|
||||
if err != nil || ttl <= 0 {
|
||||
return Config{}, fmt.Errorf("%s must be a positive duration", authorizationTTLEnv)
|
||||
}
|
||||
quantityText, err := required(lookup, maxTaskQuantityEnv)
|
||||
if err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
maxQuantity, err := strconv.Atoi(quantityText)
|
||||
if err != nil || maxQuantity < 1 {
|
||||
return Config{}, fmt.Errorf("%s must be a positive integer", maxTaskQuantityEnv)
|
||||
}
|
||||
maxPrice, err := required(lookup, maxTotalPriceEnv)
|
||||
if err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
if !canonicalMoney(maxPrice) {
|
||||
return Config{}, fmt.Errorf("%s must be a canonical positive decimal", maxTotalPriceEnv)
|
||||
}
|
||||
|
||||
return Config{
|
||||
AdminUsername: username,
|
||||
@@ -78,9 +109,25 @@ func Load(lookup func(string) (string, bool)) (Config, error) {
|
||||
SessionSecret: []byte(secret),
|
||||
CookieSecure: cookieSecure,
|
||||
DatabaseSource: databaseSource,
|
||||
AuthorizationTTL: ttl, MaxTaskQuantity: maxQuantity, MaxTotalPrice: maxPrice,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func canonicalMoney(value string) bool {
|
||||
parts := strings.Split(value, ".")
|
||||
if len(parts) != 2 || len(parts[0]) == 0 || len(parts[1]) != 2 || (len(parts[0]) > 1 && parts[0][0] == '0') {
|
||||
return false
|
||||
}
|
||||
for _, part := range parts {
|
||||
for _, ch := range part {
|
||||
if ch < '0' || ch > '9' {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return strings.Trim(parts[0]+parts[1], "0") != ""
|
||||
}
|
||||
|
||||
func required(lookup func(string) (string, bool), name string) (string, error) {
|
||||
value, present := lookup(name)
|
||||
if !present || strings.TrimSpace(value) == "" {
|
||||
|
||||
Reference in New Issue
Block a user