package laosiji_app import ( "bytes" "context" "crypto/aes" "encoding/base64" "encoding/json" "errors" "fmt" "io" "net/http" "strings" "time" "91porn-server/common/log" ) type Config struct { AppID string `json:"appId"` APIKey string `json:"apiKey"` APIURL string `json:"apiUrl"` } var cfg Config func Init(c Config) { c.APIURL = strings.TrimRight(strings.TrimSpace(c.APIURL), "/") cfg = c } func Configured() bool { return cfg.AppID != "" && cfg.APIKey != "" && cfg.APIURL != "" } type Response struct { Status string `json:"status"` Data string `json:"data"` Time string `json:"time"` Error string `json:"error"` ErrorCode int `json:"errorCode"` } func post(ctx context.Context, endpoint string, req map[string]interface{}, response interface{}) error { if !Configured() { return errors.New("laosiji app config is incomplete") } jsonData, err := json.Marshal(req) if err != nil { return err } encryptedData, err := encryptBase64(string(jsonData), cfg.APIKey) if err != nil { return err } httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, bytes.NewBufferString(encryptedData)) if err != nil { return err } httpReq.Header.Set("Content-Type", "application/json") httpReq.Header.Set("appid", cfg.AppID) client := &http.Client{Timeout: 10 * time.Second} resp, err := client.Do(httpReq) if err != nil { return err } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { return err } if resp.StatusCode != http.StatusOK { return fmt.Errorf("laosiji app http status %d", resp.StatusCode) } var result Response if err = json.Unmarshal(body, &result); err != nil { return err } if result.Status != "y" { return fmt.Errorf("laosiji app errorCode:%d error:%s", result.ErrorCode, result.Error) } data, err := decryptBase64(result.Data, cfg.APIKey) if err != nil { return err } if err = json.Unmarshal([]byte(data), response); err != nil { log.Warn("laosiji_app unmarshal response failed", log.E(err)) return err } return nil } func encryptBase64(input, key string) (string, error) { keyBytes, err := aesKey(key) if err != nil { return "", err } block, err := aes.NewCipher(keyBytes) if err != nil { return "", err } plainText := pkcs7Padding([]byte(input), block.BlockSize()) encrypted := make([]byte, len(plainText)) for start, end := 0, block.BlockSize(); start < len(plainText); start, end = start+block.BlockSize(), end+block.BlockSize() { block.Encrypt(encrypted[start:end], plainText[start:end]) } return base64.StdEncoding.EncodeToString(encrypted), nil } func decryptBase64(cipherText, key string) (string, error) { keyBytes, err := aesKey(key) if err != nil { return "", err } data, err := base64.StdEncoding.DecodeString(cipherText) if err != nil { return "", err } block, err := aes.NewCipher(keyBytes) if err != nil { return "", err } if len(data) == 0 || len(data)%block.BlockSize() != 0 { return "", errors.New("invalid encrypted payload length") } decrypted := make([]byte, len(data)) for start, end := 0, block.BlockSize(); start < len(data); start, end = start+block.BlockSize(), end+block.BlockSize() { block.Decrypt(decrypted[start:end], data[start:end]) } decrypted, err = pkcs7UnPadding(decrypted, block.BlockSize()) if err != nil { return "", err } return string(decrypted), nil } func aesKey(key string) ([]byte, error) { if len(key) > aes.BlockSize { key = key[:aes.BlockSize] } if len(key) != aes.BlockSize { return nil, fmt.Errorf("invalid AES key length %d", len(key)) } return []byte(key), nil } func pkcs7Padding(src []byte, blockSize int) []byte { padding := blockSize - len(src)%blockSize return append(src, bytes.Repeat([]byte{byte(padding)}, padding)...) } func pkcs7UnPadding(src []byte, blockSize int) ([]byte, error) { if len(src) == 0 { return nil, errors.New("empty padded payload") } padding := int(src[len(src)-1]) if padding == 0 || padding > blockSize || padding > len(src) { return nil, errors.New("invalid PKCS7 padding") } for _, value := range src[len(src)-padding:] { if int(value) != padding { return nil, errors.New("invalid PKCS7 padding") } } return src[:len(src)-padding], nil }