Initial sanitized code sync

This commit is contained in:
zqq61
2026-03-15 20:48:19 +08:00
commit 17ee51ba04
126 changed files with 22546 additions and 0 deletions

323
config/config.go Normal file
View File

@@ -0,0 +1,323 @@
package config
import (
"fmt"
"os"
"strconv"
"gopkg.in/yaml.v3"
"gorm.io/gorm"
)
type Config struct {
Proxy ProxyConfig `yaml:"proxy"`
Email EmailConfig `yaml:"email"`
Card CardConfig `yaml:"card"`
Stripe StripeConfig `yaml:"stripe"`
Captcha CaptchaConfig `yaml:"captcha"`
Account AccountConfig `yaml:"account"`
Team TeamConfig `yaml:"team"`
Output OutputConfig `yaml:"output"`
Batch BatchConfig `yaml:"batch"`
}
type BatchConfig struct {
Count int `yaml:"count"` // number of accounts to create (0 = auto-detect from card codes)
}
type CaptchaConfig struct {
Provider string `yaml:"provider"` // "hcaptchasolver"
APIKey string `yaml:"api_key"`
Proxy string `yaml:"proxy"` // fixed HTTP proxy for captcha solver
}
type ProxyConfig struct {
URL string `yaml:"url"`
B2Proxy B2ProxyConfig `yaml:"b2proxy"`
}
type B2ProxyConfig struct {
Enabled bool `yaml:"enabled"`
APIBase string `yaml:"api_base"` // e.g. http://global.rrp.b2proxy.com:8089
Zone string `yaml:"zone"` // e.g. "custom"
PType int `yaml:"ptype"` // e.g. 1
Proto string `yaml:"proto"` // "socks5" or "http"
SessTime int `yaml:"sess_time"` // sticky session minutes, default 5
}
type EmailConfig struct {
Provider string `yaml:"provider"`
MailGateway MailGatewayConfig `yaml:"mailgateway"`
Outlook OutlookEmailConfig `yaml:"outlook"`
}
type OutlookEmailConfig struct {
AccountsFile string `yaml:"accounts_file"` // path to file with email----password----client_id----refresh_token
POP3Server string `yaml:"pop3_server"` // default: outlook.office365.com
POP3Port int `yaml:"pop3_port"` // default: 995
}
type MailGatewayConfig struct {
BaseURL string `yaml:"base_url"`
APIKey string `yaml:"api_key"`
Provider string `yaml:"provider"` // e.g. "gptmail"
}
type CardConfig struct {
Provider string `yaml:"provider"` // "static", "file", "api"
Static StaticCardConfig `yaml:"static"`
File FileCardConfig `yaml:"file"`
API APICardConfig `yaml:"api"`
Pool CardPoolConfig `yaml:"pool"`
}
type StaticCardConfig struct {
Cards []CardEntry `yaml:"cards"`
}
type FileCardConfig struct {
Path string `yaml:"path"` // path to card TXT file
DefaultCountry string `yaml:"default_country"` // default country code e.g. JP
DefaultCurrency string `yaml:"default_currency"` // default currency e.g. JPY
}
type APICardConfig struct {
BaseURL string `yaml:"base_url"` // e.g. https://yyl.ncet.top
Codes []string `yaml:"codes"` // redeem codes list
CodesFile string `yaml:"codes_file"` // or path to file with one code per line
DefaultName string `yaml:"default_name"` // default cardholder name
DefaultCountry string `yaml:"default_country"` // default country code e.g. US
DefaultCurrency string `yaml:"default_currency"` // default currency e.g. USD
DefaultAddress string `yaml:"default_address"` // default billing address
DefaultCity string `yaml:"default_city"` // default billing city
DefaultState string `yaml:"default_state"` // default billing state
DefaultPostalCode string `yaml:"default_postal_code"` // default billing ZIP code
}
type CardEntry struct {
Number string `yaml:"number"`
ExpMonth string `yaml:"exp_month"`
ExpYear string `yaml:"exp_year"`
CVC string `yaml:"cvc"`
Name string `yaml:"name"`
Country string `yaml:"country"`
Currency string `yaml:"currency"`
Address string `yaml:"address"`
City string `yaml:"city"`
State string `yaml:"state"`
PostalCode string `yaml:"postal_code"`
}
type CardPoolConfig struct {
TTLMinutes int `yaml:"ttl_minutes"` // card validity in minutes (default: 60)
MultiBind bool `yaml:"multi_bind"` // allow reusing cards multiple times
MaxBinds int `yaml:"max_binds"` // max uses per card (0 = unlimited when multi_bind=true)
}
type StripeConfig struct {
BuildHash string `yaml:"build_hash"`
TagVersion string `yaml:"tag_version"`
StripeVersion string `yaml:"stripe_version"`
FingerprintDir string `yaml:"fingerprint_dir"` // directory with browser fingerprint JSON files
Aimizy AimizyConfig `yaml:"aimizy"`
}
type AimizyConfig struct {
Enabled bool `yaml:"enabled"`
BaseURL string `yaml:"base_url"` // default: https://team.aimizy.com
}
type AccountConfig struct {
PasswordLength int `yaml:"password_length"`
Locale string `yaml:"locale"`
}
type TeamConfig struct {
Enabled bool `yaml:"enabled"`
WorkspacePrefix string `yaml:"workspace_prefix"`
SeatQuantity int `yaml:"seat_quantity"`
Coupon string `yaml:"coupon"`
InviteCount int `yaml:"invite_count"` // number of members to invite (0 = skip)
}
type OutputConfig struct {
Dir string `yaml:"dir"`
}
// ExpectedCountry returns the country code associated with the current card configuration.
// Falls back to "US" if no country is configured.
func (c *CardConfig) ExpectedCountry() string {
switch c.Provider {
case "static", "":
if len(c.Static.Cards) > 0 && c.Static.Cards[0].Country != "" {
return c.Static.Cards[0].Country
}
case "api":
if c.API.DefaultCountry != "" {
return c.API.DefaultCountry
}
case "file":
if c.File.DefaultCountry != "" {
return c.File.DefaultCountry
}
}
return "US"
}
func Load(path string) (*Config, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("read config file: %w", err)
}
cfg := &Config{}
if err := yaml.Unmarshal(data, cfg); err != nil {
return nil, fmt.Errorf("parse config: %w", err)
}
// Outlook email defaults (YAML-only provider)
if cfg.Email.Outlook.POP3Server == "" {
cfg.Email.Outlook.POP3Server = "outlook.office365.com"
}
if cfg.Email.Outlook.POP3Port == 0 {
cfg.Email.Outlook.POP3Port = 995
}
applyDefaults(cfg)
return cfg, nil
}
// configRow mirrors internal/db.SystemConfig without importing internal/.
type configRow struct {
Key string
Value string
}
// LoadFromDB builds a Config by reading the system_configs table.
// It applies the same defaults as Load() for any missing values.
func LoadFromDB(d *gorm.DB) (*Config, error) {
var rows []configRow
if err := d.Table("system_configs").Select("key, value").Find(&rows).Error; err != nil {
return nil, fmt.Errorf("query configs: %w", err)
}
m := make(map[string]string, len(rows))
for _, r := range rows {
m[r.Key] = r.Value
}
cfg := &Config{}
// Proxy
cfg.Proxy.URL = m["proxy.url"]
cfg.Proxy.B2Proxy.Enabled = m["proxy.b2proxy.enabled"] == "true"
cfg.Proxy.B2Proxy.APIBase = m["proxy.b2proxy.api_base"]
cfg.Proxy.B2Proxy.Zone = m["proxy.b2proxy.zone"]
cfg.Proxy.B2Proxy.Proto = m["proxy.b2proxy.proto"]
cfg.Proxy.B2Proxy.SessTime, _ = strconv.Atoi(m["proxy.b2proxy.sess_time"])
// Email
cfg.Email.Provider = "mailgateway"
cfg.Email.MailGateway.BaseURL = m["email.gateway.base_url"]
cfg.Email.MailGateway.APIKey = m["email.gateway.api_key"]
cfg.Email.MailGateway.Provider = m["email.gateway.provider"]
// Card — DB mode uses DBCardProvider, not static/file/api
cfg.Card.Provider = "db"
cfg.Card.API.BaseURL = m["card.api_base_url"]
cfg.Card.API.DefaultName = m["card.default_name"]
cfg.Card.API.DefaultCountry = m["card.default_country"]
cfg.Card.API.DefaultCurrency = m["card.default_currency"]
cfg.Card.API.DefaultAddress = m["card.default_address"]
cfg.Card.API.DefaultCity = m["card.default_city"]
cfg.Card.API.DefaultState = m["card.default_state"]
cfg.Card.API.DefaultPostalCode = m["card.default_postal_code"]
cfg.Card.Pool.MaxBinds, _ = strconv.Atoi(m["card.max_binds"])
// Stripe
cfg.Stripe.BuildHash = m["stripe.build_hash"]
cfg.Stripe.TagVersion = m["stripe.tag_version"]
cfg.Stripe.FingerprintDir = m["stripe.fingerprint_dir"]
// Captcha
cfg.Captcha.Provider = m["captcha.provider"]
cfg.Captcha.APIKey = m["captcha.api_key"]
cfg.Captcha.Proxy = m["captcha.proxy"]
// Account
cfg.Account.PasswordLength, _ = strconv.Atoi(m["account.password_length"])
cfg.Account.Locale = m["account.locale"]
// Team
cfg.Team.Enabled = m["team.enabled"] == "true"
cfg.Team.WorkspacePrefix = m["team.workspace_prefix"]
cfg.Team.SeatQuantity, _ = strconv.Atoi(m["team.seat_quantity"])
cfg.Team.Coupon = m["team.coupon"]
cfg.Team.InviteCount, _ = strconv.Atoi(m["team.invite_count"])
// Output
cfg.Output.Dir = "./output"
// Apply same defaults as Load()
applyDefaults(cfg)
return cfg, nil
}
func applyDefaults(cfg *Config) {
if cfg.Stripe.BuildHash == "" {
cfg.Stripe.BuildHash = "ede17ac9fd"
}
if cfg.Stripe.TagVersion == "" {
cfg.Stripe.TagVersion = "4.5.43"
}
if cfg.Stripe.StripeVersion == "" {
cfg.Stripe.StripeVersion = "2025-03-31.basil"
}
if cfg.Account.PasswordLength == 0 {
cfg.Account.PasswordLength = 16
}
if cfg.Account.Locale == "" {
cfg.Account.Locale = "en-GB"
}
if cfg.Team.WorkspacePrefix == "" {
cfg.Team.WorkspacePrefix = "Team"
}
if cfg.Team.SeatQuantity == 0 {
cfg.Team.SeatQuantity = 5
}
if cfg.Team.Coupon == "" {
cfg.Team.Coupon = "team-1-month-free"
}
if cfg.Output.Dir == "" {
cfg.Output.Dir = "./output"
}
if cfg.Email.MailGateway.Provider == "" {
cfg.Email.MailGateway.Provider = "gptmail"
}
if cfg.Captcha.Provider == "" {
cfg.Captcha.Provider = "hcaptchasolver"
}
if cfg.Stripe.FingerprintDir == "" {
cfg.Stripe.FingerprintDir = "./fingerprints"
}
if cfg.Proxy.B2Proxy.APIBase == "" {
cfg.Proxy.B2Proxy.APIBase = "http://global.rrp.b2proxy.com:8089"
}
if cfg.Proxy.B2Proxy.Zone == "" {
cfg.Proxy.B2Proxy.Zone = "custom"
}
if cfg.Proxy.B2Proxy.PType == 0 {
cfg.Proxy.B2Proxy.PType = 1
}
if cfg.Proxy.B2Proxy.Proto == "" {
cfg.Proxy.B2Proxy.Proto = "socks5"
}
if cfg.Proxy.B2Proxy.SessTime == 0 {
cfg.Proxy.B2Proxy.SessTime = 5
}
if cfg.Stripe.Aimizy.BaseURL == "" {
cfg.Stripe.Aimizy.BaseURL = "https://team.aimizy.com"
}
if cfg.Card.Pool.MaxBinds == 0 {
cfg.Card.Pool.MaxBinds = 1
}
}