feat: add AlwaysIgnoreUrls for URL filtering; update README and release version to 1.3-01
Build and Release / release (push) Successful in 1m43s

This commit is contained in:
2026-06-12 10:31:40 +07:00
parent 5aac0d6297
commit 48f801e05b
6 changed files with 176 additions and 87 deletions
+4
View File
@@ -23,6 +23,10 @@ var AlwaysIgnoreDomains = []string{
"autopatchos.starrails.com",
}
var AlwaysIgnoreUrls = []string{
"/query_security_file",
}
var BlockUrls = []string{
"/data_abtest_api/config/experiment/list",
"/common/hkrpg_global/announcement/api/getAlertPic",
+5
View File
@@ -136,6 +136,11 @@ func main() {
return req, nil
}
if matchURL(path, AlwaysIgnoreUrls) {
zlog.Warn().Str("url", req.URL.String()).Msg("PASS URL")
return req, nil
}
if matchDomain(host, RedirectDomains) {
if matchURL(path, BlockUrls) {
full := req.URL.String()
+1 -1
View File
@@ -1,4 +1,4 @@
# Changelog
### UPDATE
- Fix bug in macos
- Support linux, macos, window
Binary file not shown.
+164 -84
View File
@@ -9,12 +9,19 @@ import (
"fmt"
"io"
"net/http"
"net/url"
"os"
"path/filepath"
"strconv"
"strings"
"time"
)
const (
apiRequestTimeout = 45 * time.Second
maxHTTPAttempts = 5
)
// API responses structures
type CommonResponse struct {
Status bool `json:"status"`
@@ -83,7 +90,7 @@ func main() {
gameIdsFlag := flag.String("game-ids", "", "Comma-separated Game IDs (defaults to ENV_GAME_IDS)")
filesFlag := flag.String("files", "", "Comma-separated list of files to upload (defaults to scanning prebuild/)")
cTypeFlag := flag.String("type", "", "Component type: LAUNCHER, PROXY, SERVER (defaults to ENV_COMPONENT_TYPE or PROXY)")
flag.Parse()
// 1. Resolve settings from Env or Flags
@@ -324,24 +331,93 @@ func getFileInfoAndHash(path string) (int64, string, error) {
return stat.Size(), hex.EncodeToString(hasher.Sum(nil)), nil
}
func apiEndpoint(apiURL, path string) string {
return strings.TrimRight(apiURL, "/") + path
}
func retryDelay(attempt int) time.Duration {
delay := time.Duration(1<<uint(attempt-1)) * time.Second
if delay > 20*time.Second {
return 20 * time.Second
}
return delay
}
func isRetryableStatus(statusCode int) bool {
return statusCode == http.StatusRequestTimeout ||
statusCode == http.StatusTooEarly ||
statusCode == http.StatusTooManyRequests ||
statusCode >= http.StatusInternalServerError
}
func isAccepted(statusCode int, accepted ...int) bool {
for _, code := range accepted {
if statusCode == code {
return true
}
}
return false
}
func doHTTPWithRetry(label string, client *http.Client, buildRequest func() (*http.Request, error), accepted ...int) ([]byte, int, error) {
var lastErr error
for attempt := 1; attempt <= maxHTTPAttempts; attempt++ {
req, err := buildRequest()
if err != nil {
return nil, 0, err
}
resp, err := client.Do(req)
if err != nil {
lastErr = err
if attempt < maxHTTPAttempts {
delay := retryDelay(attempt)
fmt.Fprintf(os.Stderr, "%s attempt %d/%d failed: %v. Retrying in %s...\n", label, attempt, maxHTTPAttempts, err, delay)
time.Sleep(delay)
continue
}
return nil, 0, err
}
bodyBytes, readErr := io.ReadAll(resp.Body)
_ = resp.Body.Close()
if readErr != nil {
return nil, resp.StatusCode, readErr
}
if isAccepted(resp.StatusCode, accepted...) {
return bodyBytes, resp.StatusCode, nil
}
if !isRetryableStatus(resp.StatusCode) || attempt == maxHTTPAttempts {
return bodyBytes, resp.StatusCode, nil
}
lastErr = fmt.Errorf("status %d: %s", resp.StatusCode, strings.TrimSpace(string(bodyBytes)))
delay := retryDelay(attempt)
fmt.Fprintf(os.Stderr, "%s attempt %d/%d returned %v. Retrying in %s...\n", label, attempt, maxHTTPAttempts, lastErr, delay)
time.Sleep(delay)
}
return nil, 0, lastErr
}
func refreshRobotToken(apiURL, robotToken string) (string, error) {
url := fmt.Sprintf("%s/robot-tokens/refresh", apiURL)
req, err := http.NewRequest("POST", url, nil)
endpoint := apiEndpoint(apiURL, "/robot-tokens/refresh")
client := &http.Client{Timeout: apiRequestTimeout}
bodyBytes, statusCode, err := doHTTPWithRetry("refresh robot token", client, func() (*http.Request, error) {
req, err := http.NewRequest("POST", endpoint, nil)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+robotToken)
return req, nil
}, http.StatusOK)
if err != nil {
return "", err
}
req.Header.Set("Authorization", "Bearer "+robotToken)
client := &http.Client{Timeout: 15 * time.Second}
resp, err := client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
bodyBytes, _ := io.ReadAll(resp.Body)
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("refresh token failed with status %d: %s", resp.StatusCode, string(bodyBytes))
if statusCode != http.StatusOK {
return "", fmt.Errorf("refresh token failed with status %d: %s", statusCode, string(bodyBytes))
}
var cr CommonResponse
@@ -362,25 +438,30 @@ func refreshRobotToken(apiURL, robotToken string) (string, error) {
}
func getPresignedURL(apiURL, accessToken, fileName, contentType string, size int64) (*PreSignedResponse, error) {
u := fmt.Sprintf("%s/media/presigned?fileName=%s&content_type=%s&size=%d",
apiURL, fileName, contentType, size)
req, err := http.NewRequest("GET", u, nil)
endpoint, err := url.Parse(apiEndpoint(apiURL, "/media/presigned"))
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+accessToken)
query := endpoint.Query()
query.Set("fileName", fileName)
query.Set("content_type", contentType)
query.Set("size", strconv.FormatInt(size, 10))
endpoint.RawQuery = query.Encode()
client := &http.Client{Timeout: 15 * time.Second}
resp, err := client.Do(req)
client := &http.Client{Timeout: apiRequestTimeout}
bodyBytes, statusCode, err := doHTTPWithRetry("get presigned URL", client, func() (*http.Request, error) {
req, err := http.NewRequest("GET", endpoint.String(), nil)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+accessToken)
return req, nil
}, http.StatusOK)
if err != nil {
return nil, err
}
defer resp.Body.Close()
bodyBytes, _ := io.ReadAll(resp.Body)
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("failed to get presigned URL (status %d): %s", resp.StatusCode, string(bodyBytes))
if statusCode != http.StatusOK {
return nil, fmt.Errorf("failed to get presigned URL (status %d): %s", statusCode, string(bodyBytes))
}
var cr CommonResponse
@@ -443,7 +524,7 @@ func uploadFileToS3(path string, presigned *PreSignedResponse) error {
}
func completePreSignedUpload(apiURL, accessToken, tokenID, hash string) (string, error) {
url := fmt.Sprintf("%s/media/presigned/complete", apiURL)
endpoint := apiEndpoint(apiURL, "/media/presigned/complete")
metaJSON, _ := json.Marshal(map[string]string{"sha256": hash})
dto := PreSignedCompleteDto{
@@ -453,23 +534,21 @@ func completePreSignedUpload(apiURL, accessToken, tokenID, hash string) (string,
payload, _ := json.Marshal(dto)
req, err := http.NewRequest("POST", url, bytes.NewReader(payload))
client := &http.Client{Timeout: apiRequestTimeout}
bodyBytes, statusCode, err := doHTTPWithRetry("complete presigned upload", client, func() (*http.Request, error) {
req, err := http.NewRequest("POST", endpoint, bytes.NewReader(payload))
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("Content-Type", "application/json")
return req, nil
}, http.StatusOK)
if err != nil {
return "", err
}
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{Timeout: 30 * time.Second}
resp, err := client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
bodyBytes, _ := io.ReadAll(resp.Body)
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("complete upload failed (status %d): %s", resp.StatusCode, string(bodyBytes))
if statusCode != http.StatusOK {
return "", fmt.Errorf("complete upload failed (status %d): %s", statusCode, string(bodyBytes))
}
var cr CommonResponse
@@ -490,27 +569,25 @@ func completePreSignedUpload(apiURL, accessToken, tokenID, hash string) (string,
}
func createComponent(apiURL, accessToken string, dto CreateComponentRequest) (string, error) {
url := fmt.Sprintf("%s/components", apiURL)
endpoint := apiEndpoint(apiURL, "/components")
payload, _ := json.Marshal(dto)
req, err := http.NewRequest("POST", url, bytes.NewReader(payload))
client := &http.Client{Timeout: apiRequestTimeout}
bodyBytes, statusCode, err := doHTTPWithRetry("create component", client, func() (*http.Request, error) {
req, err := http.NewRequest("POST", endpoint, bytes.NewReader(payload))
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("Content-Type", "application/json")
return req, nil
}, http.StatusCreated, http.StatusOK)
if err != nil {
return "", err
}
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{Timeout: 30 * time.Second}
resp, err := client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
bodyBytes, _ := io.ReadAll(resp.Body)
if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("create component failed (status %d): %s", resp.StatusCode, string(bodyBytes))
if statusCode != http.StatusCreated && statusCode != http.StatusOK {
return "", fmt.Errorf("create component failed (status %d): %s", statusCode, string(bodyBytes))
}
var cr CommonResponse
@@ -533,25 +610,30 @@ func createComponent(apiURL, accessToken string, dto CreateComponentRequest) (st
}
func findExistingComponent(apiURL, accessToken, cType, platform, version string) (string, error) {
u := fmt.Sprintf("%s/components?type=%s&platform=%s&search=%s",
apiURL, cType, platform, version)
req, err := http.NewRequest("GET", u, nil)
endpoint, err := url.Parse(apiEndpoint(apiURL, "/components"))
if err != nil {
return "", err
}
req.Header.Set("Authorization", "Bearer "+accessToken)
query := endpoint.Query()
query.Set("type", cType)
query.Set("platform", platform)
query.Set("search", version)
endpoint.RawQuery = query.Encode()
client := &http.Client{Timeout: 15 * time.Second}
resp, err := client.Do(req)
client := &http.Client{Timeout: apiRequestTimeout}
bodyBytes, statusCode, err := doHTTPWithRetry("search existing component", client, func() (*http.Request, error) {
req, err := http.NewRequest("GET", endpoint.String(), nil)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+accessToken)
return req, nil
}, http.StatusOK)
if err != nil {
return "", err
}
defer resp.Body.Close()
bodyBytes, _ := io.ReadAll(resp.Body)
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("failed to search components (status %d): %s", resp.StatusCode, string(bodyBytes))
if statusCode != http.StatusOK {
return "", fmt.Errorf("failed to search components (status %d): %s", statusCode, string(bodyBytes))
}
var pr struct {
@@ -582,27 +664,25 @@ func findExistingComponent(apiURL, accessToken, cType, platform, version string)
}
func updateComponent(apiURL, accessToken, id string, dto UpdateComponentRequest) (string, error) {
url := fmt.Sprintf("%s/components/%s", apiURL, id)
endpoint := apiEndpoint(apiURL, "/components/"+url.PathEscape(id))
payload, _ := json.Marshal(dto)
req, err := http.NewRequest("PUT", url, bytes.NewReader(payload))
client := &http.Client{Timeout: apiRequestTimeout}
bodyBytes, statusCode, err := doHTTPWithRetry("update component", client, func() (*http.Request, error) {
req, err := http.NewRequest("PUT", endpoint, bytes.NewReader(payload))
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("Content-Type", "application/json")
return req, nil
}, http.StatusOK)
if err != nil {
return "", err
}
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{Timeout: 30 * time.Second}
resp, err := client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
bodyBytes, _ := io.ReadAll(resp.Body)
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("update component failed (status %d): %s", resp.StatusCode, string(bodyBytes))
if statusCode != http.StatusOK {
return "", fmt.Errorf("update component failed (status %d): %s", statusCode, string(bodyBytes))
}
var cr CommonResponse
+2 -2
View File
@@ -1,5 +1,5 @@
{
"tag": "1.2-03",
"title": "PreBuild Version 1.2 - 03"
"tag": "1.3-01",
"title": "PreBuild Version 1.3 - 01"
}