695 lines
22 KiB
Go
695 lines
22 KiB
Go
// Package sdkserver is the Go port of the Rust sdkserver: HTTP dispatch +
|
|
// auth + SRTools save upload, using the shared runtime configuration.
|
|
package sdkserver
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
"sync"
|
|
|
|
"gac-hkrpg-go/internal/common"
|
|
|
|
google "google.golang.org/protobuf/proto"
|
|
|
|
goproto "gac-hkrpg-go/protocol/proto"
|
|
)
|
|
|
|
// VersionConfig is one entry of versions.json.
|
|
type VersionConfig struct {
|
|
AssetBundleURL string `json:"asset_bundle_url"`
|
|
AssetBundleURLB string `json:"asset_bundle_url_b"`
|
|
ExResourceURL string `json:"ex_resource_url"`
|
|
LuaURL string `json:"lua_url"`
|
|
IfixURL string `json:"ifix_url"`
|
|
LuaVersion string `json:"lua_version"`
|
|
IfixVersion string `json:"ifix_version"`
|
|
}
|
|
|
|
type appState struct {
|
|
runtime common.RuntimeConfig
|
|
mu sync.Mutex
|
|
hotfixMap map[string]VersionConfig
|
|
}
|
|
|
|
func loadHotfix() map[string]VersionConfig {
|
|
out := map[string]VersionConfig{}
|
|
if raw, err := os.ReadFile("hotfix.json"); err == nil {
|
|
_ = json.Unmarshal(raw, &out)
|
|
}
|
|
if raw, err := os.ReadFile("versions.json"); err == nil {
|
|
_ = json.Unmarshal(raw, &out)
|
|
}
|
|
return out
|
|
}
|
|
|
|
// getOrInsertHotfix mirrors AppState::get_or_insert_hotfix — unknown versions
|
|
// are fetched from the official gateway via a proxy, then persisted.
|
|
func (a *appState) getOrInsertHotfix(version, dispatchSeed string) VersionConfig {
|
|
a.mu.Lock()
|
|
defer a.mu.Unlock()
|
|
|
|
exact, hasExact := a.hotfixMap[version]
|
|
if hasExact && exact.AssetBundleURL != "" {
|
|
return exact
|
|
}
|
|
// Mobile clients append build identifiers after the release version. Prefer
|
|
// an exact entry, then look up the release in the configured metadata.
|
|
if release, _, ok := strings.Cut(version, "_"); ok {
|
|
if cfg, found := a.hotfixMap[release]; found {
|
|
return cfg
|
|
}
|
|
}
|
|
if hasExact {
|
|
return exact
|
|
}
|
|
if cfg, ok := a.hotfixMap["CNBETAWin"+version]; ok {
|
|
return cfg
|
|
}
|
|
if cfg, ok := a.hotfixMap["OSBETAWin"+version]; ok {
|
|
return cfg
|
|
}
|
|
|
|
Log(fmt.Sprintf("trying to fetch hotfix for version %s with dispatch seed %s", version, dispatchSeed))
|
|
|
|
hotfix, err := fetchHotfix(version, dispatchSeed)
|
|
if err != nil {
|
|
Log("failed to fetch hotfix. reason: " + err.Error())
|
|
hotfix = VersionConfig{}
|
|
}
|
|
|
|
a.hotfixMap[version] = hotfix
|
|
if out, err := json.MarshalIndent(a.hotfixMap, "", " "); err == nil {
|
|
_ = os.WriteFile("versions.json", out, 0o644)
|
|
}
|
|
return a.hotfixMap[version]
|
|
}
|
|
|
|
// fetchHotfix queries the official query_gateway endpoint through a proxy.
|
|
func fetchHotfix(version, dispatchSeed string) (VersionConfig, error) {
|
|
const (
|
|
proxyHost = "proxy1.neonteam.dev"
|
|
osBeta = "beta-release01-asia.starrails.com"
|
|
osProd = "prod-official-asia-dp01.starrails.com"
|
|
cnBeta = "beta-release01-cn.bhsr.com"
|
|
cnProd = "prod-gf-cn-dp01.bhsr.com"
|
|
)
|
|
|
|
region, branch := parseVersionString(version)
|
|
host := osBeta
|
|
switch {
|
|
case region == "OS" && branch == "PROD":
|
|
host = osProd
|
|
case region == "CN" && branch == "BETA":
|
|
host = cnBeta
|
|
case region == "CN" && branch == "PROD":
|
|
host = cnProd
|
|
}
|
|
|
|
url := fmt.Sprintf("https://%s/%s/query_gateway?version=%s&platform_type=1&language_type=3&dispatch_seed=%s&channel_id=1&sub_channel_id=1&is_need_url=1",
|
|
proxyHost, host, version, dispatchSeed)
|
|
|
|
Log("fetching hotfix: " + url)
|
|
resp, err := http.Get(url)
|
|
if err != nil {
|
|
return VersionConfig{}, err
|
|
}
|
|
defer resp.Body.Close()
|
|
body, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return VersionConfig{}, err
|
|
}
|
|
Log("raw gateway response: " + string(body))
|
|
|
|
bin, err := base64.StdEncoding.DecodeString(string(body))
|
|
if err != nil {
|
|
return VersionConfig{}, err
|
|
}
|
|
decoded := &goproto.Gateserver{}
|
|
if err := google.Unmarshal(bin, decoded); err != nil {
|
|
return VersionConfig{}, err
|
|
}
|
|
if decoded.AssetBundleUrl == "" && decoded.ExResourceUrl == "" {
|
|
return VersionConfig{}, fmt.Errorf("asset_bundle_url and ex_resource_url are empty! gateway response: %+v", decoded)
|
|
}
|
|
|
|
return VersionConfig{
|
|
AssetBundleURL: decoded.AssetBundleUrl,
|
|
ExResourceURL: decoded.ExResourceUrl,
|
|
LuaURL: decoded.LuaUrl,
|
|
IfixURL: decoded.IfixUrl,
|
|
}, nil
|
|
}
|
|
|
|
// parseVersionString extracts region/branch, e.g. a version with the OSBETAWin prefix -> OS,BETA.
|
|
func parseVersionString(s string) (string, string) {
|
|
branches := []string{"PREbeta", "BETA", "PROD", "DEV", "PRE", "GM", "CECREATION"}
|
|
if len(s) < 2 {
|
|
return "OS", "BETA"
|
|
}
|
|
region := s[:2]
|
|
rest := s[2:]
|
|
for _, b := range branches {
|
|
if len(rest) >= len(b) && rest[:len(b)] == b {
|
|
return region, b
|
|
}
|
|
}
|
|
return region, "BETA"
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// handlers
|
|
// ---------------------------------------------------------------------------
|
|
|
|
func writeJSON(w http.ResponseWriter, v any) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_ = json.NewEncoder(w).Encode(v)
|
|
}
|
|
|
|
func queryDispatch(w http.ResponseWriter, _ *http.Request, runtime common.RuntimeConfig) {
|
|
rsp := &goproto.Dispatch{
|
|
Retcode: 0,
|
|
RegionList: []*goproto.RegionInfo{{
|
|
Name: "GaC hkrpg GO",
|
|
Title: "GaC hkrpg GO",
|
|
EnvType: "9",
|
|
DispatchUrl: runtime.PublicSDKURL + "/query_gateway",
|
|
}},
|
|
}
|
|
bin, err := google.Marshal(rsp)
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
_, _ = w.Write([]byte(base64.StdEncoding.EncodeToString(bin)))
|
|
}
|
|
|
|
func queryGateway(state *appState) http.HandlerFunc {
|
|
type params struct {
|
|
Version string `json:"-"`
|
|
DispatchSeed string `json:"-"`
|
|
}
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
q := r.URL.Query()
|
|
version := q.Get("version")
|
|
seed := q.Get("dispatch_seed")
|
|
_ = params{}
|
|
|
|
cfg := state.getOrInsertHotfix(version, seed)
|
|
if state.runtime.LuaOverlay.Enabled {
|
|
cfg.LuaURL = state.runtime.PublicSDKURL + luaOverlayRoute
|
|
}
|
|
assetURLB := cfg.AssetBundleURLB
|
|
if assetURLB == "" {
|
|
assetURLB = cfg.AssetBundleURL
|
|
}
|
|
ifixVersion := cfg.IfixVersion
|
|
if ifixVersion == "" {
|
|
ifixVersion = "0"
|
|
}
|
|
|
|
rsp := &goproto.Gateserver{
|
|
Ip: state.runtime.PublicGameHost,
|
|
Port: uint32(state.runtime.GamePort),
|
|
AssetBundleUrl: cfg.AssetBundleURL,
|
|
AssetBundleUrlB: assetURLB,
|
|
ExResourceUrl: cfg.ExResourceURL,
|
|
LuaUrl: cfg.LuaURL,
|
|
IfixUrl: cfg.IfixURL,
|
|
IfixVersion: ifixVersion,
|
|
LuaVersion: cfg.LuaVersion,
|
|
Unk1: true,
|
|
Unk2: true,
|
|
Unk3: true,
|
|
Unk4: true,
|
|
Unk5: true,
|
|
Unk6: true,
|
|
Unk7: true,
|
|
}
|
|
bin, err := google.Marshal(rsp)
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
_, _ = w.Write([]byte(base64.StdEncoding.EncodeToString(bin)))
|
|
}
|
|
}
|
|
|
|
type authRequestPayload struct {
|
|
Account string `json:"account"`
|
|
Password string `json:"password"`
|
|
UID string `json:"uid"`
|
|
Token string `json:"token"`
|
|
}
|
|
|
|
func parseAuthPayload(r *http.Request) authRequestPayload {
|
|
var payload authRequestPayload
|
|
if r.Body != nil {
|
|
body, err := io.ReadAll(r.Body)
|
|
if err == nil && len(body) > 0 {
|
|
_ = json.Unmarshal(body, &payload)
|
|
r.Body = io.NopCloser(bytes.NewBuffer(body))
|
|
}
|
|
}
|
|
return payload
|
|
}
|
|
|
|
func (a *appState) standardAccount(accountName string) map[string]any {
|
|
if accountName == "" {
|
|
accountName = a.runtime.AccountName
|
|
}
|
|
return map[string]any{
|
|
"name": accountName,
|
|
"account_name": accountName,
|
|
"area_code": "**",
|
|
"email": accountName,
|
|
"country": "US",
|
|
"is_email_verify": "1",
|
|
"token": "mostsecuretokenever",
|
|
"uid": fmt.Sprint(a.runtime.PlayerUID),
|
|
}
|
|
}
|
|
|
|
func (a *appState) loginReply(w http.ResponseWriter, r *http.Request) {
|
|
req := parseAuthPayload(r)
|
|
writeJSON(w, map[string]any{
|
|
"data": map[string]any{
|
|
"account": a.standardAccount(req.Account),
|
|
"device_grant_required": false,
|
|
"reactivate_required": false,
|
|
"realperson_required": false,
|
|
"safe_mobile_required": false,
|
|
},
|
|
"message": "OK",
|
|
"retcode": 0,
|
|
})
|
|
}
|
|
|
|
func (a *appState) granterLoginReply(w http.ResponseWriter) {
|
|
writeJSON(w, map[string]any{
|
|
"data": map[string]any{
|
|
"account_type": 1,
|
|
"combo_id": fmt.Sprint(a.runtime.PlayerUID),
|
|
"combo_token": "9065ad8507d5a1991cb6fddacac5999b780bbd92",
|
|
"data": `{"guest":false}`,
|
|
"heartbeat": false,
|
|
"open_id": fmt.Sprint(a.runtime.PlayerUID),
|
|
},
|
|
"message": "OK",
|
|
"retcode": 0,
|
|
})
|
|
}
|
|
|
|
func riskyCheckReply(w http.ResponseWriter) {
|
|
writeJSON(w, map[string]any{
|
|
"data": map[string]any{
|
|
"id": "06611ed14c3131a676b19c0d34c0644b",
|
|
"action": "ACTION_NONE",
|
|
"geetest": nil,
|
|
},
|
|
"message": "OK",
|
|
"retcode": 0,
|
|
})
|
|
}
|
|
|
|
func (a *appState) apnLoginReply(w http.ResponseWriter, r *http.Request) {
|
|
req := parseAuthPayload(r)
|
|
accName := req.Account
|
|
if accName == "" {
|
|
accName = a.runtime.AccountName
|
|
}
|
|
uidStr := fmt.Sprint(a.runtime.PlayerUID)
|
|
|
|
writeJSON(w, map[string]any{
|
|
"data": map[string]any{
|
|
"bind_email_action_ticket": "",
|
|
"reactivate_action_token": "",
|
|
"token": map[string]any{
|
|
"token": "mostsecuretokenever",
|
|
"token_type": 1,
|
|
},
|
|
"tokens": []any{
|
|
map[string]any{
|
|
"token": "mostsecuretokenever",
|
|
"token_type": 1,
|
|
},
|
|
},
|
|
"user_info": map[string]any{
|
|
"name": accName,
|
|
"account_name": accName,
|
|
"aid": uidStr,
|
|
"mid": uidStr,
|
|
"is_email_verify": 1,
|
|
"area_code": "**",
|
|
"country": "US",
|
|
"is_adult": 1,
|
|
"email": accName,
|
|
"mobile": "",
|
|
"safe_area_code": "",
|
|
"safe_mobile": "",
|
|
"realname": "",
|
|
"identity_code": "",
|
|
"rebind_area_code": "",
|
|
"rebind_mobile": "",
|
|
"rebind_mobile_time": "1",
|
|
"links": []any{},
|
|
"password_time": "1",
|
|
"unmasked_email": accName,
|
|
"unmasked_email_type": 1,
|
|
},
|
|
"ext_user_info": map[string]any{
|
|
"guardian_email": "",
|
|
"birth": "0",
|
|
},
|
|
},
|
|
"message": "OK",
|
|
"retcode": 0,
|
|
})
|
|
}
|
|
|
|
func (a *appState) apnVerifyReply(w http.ResponseWriter, r *http.Request) {
|
|
req := parseAuthPayload(r)
|
|
accName := req.Account
|
|
if accName == "" {
|
|
accName = a.runtime.AccountName
|
|
}
|
|
uidStr := fmt.Sprint(a.runtime.PlayerUID)
|
|
if req.UID != "" {
|
|
uidStr = req.UID
|
|
}
|
|
|
|
writeJSON(w, map[string]any{
|
|
"data": map[string]any{
|
|
"bind_email_action_ticket": "",
|
|
"reactivate_action_token": "",
|
|
"token": map[string]any{
|
|
"token": "mostsecuretokenever",
|
|
"token_type": 1,
|
|
},
|
|
"tokens": []any{
|
|
map[string]any{
|
|
"token": "mostsecuretokenever",
|
|
"token_type": 1,
|
|
},
|
|
},
|
|
"user_info": map[string]any{
|
|
"name": accName,
|
|
"account_name": accName,
|
|
"aid": uidStr,
|
|
"mid": uidStr,
|
|
"is_email_verify": 1,
|
|
"area_code": "**",
|
|
"country": "US",
|
|
"is_adult": 1,
|
|
"email": accName,
|
|
"mobile": "",
|
|
"safe_area_code": "",
|
|
"safe_mobile": "",
|
|
"realname": "",
|
|
"identity_code": "",
|
|
"rebind_area_code": "",
|
|
"rebind_mobile": "",
|
|
"rebind_mobile_time": "1",
|
|
"links": []any{},
|
|
"password_time": "1",
|
|
"unmasked_email": accName,
|
|
"unmasked_email_type": 1,
|
|
},
|
|
"ext_user_info": map[string]any{
|
|
"guardian_email": "",
|
|
"birth": "0",
|
|
},
|
|
},
|
|
"message": "OK",
|
|
"retcode": 0,
|
|
})
|
|
}
|
|
|
|
func shieldLoadConfigReply(w http.ResponseWriter) {
|
|
writeJSON(w, map[string]any{
|
|
"retcode": 0,
|
|
"message": "OK",
|
|
"data": map[string]any{
|
|
"id": 24,
|
|
"game_key": "hkrpg_global",
|
|
"client": "PC",
|
|
"identity": "I_IDENTITY",
|
|
"guest": false,
|
|
"ignore_versions": "",
|
|
"scene": "S_NORMAL",
|
|
"name": "Honkai: Star Rail",
|
|
"disable_regist": true,
|
|
"enable_email_captcha": false,
|
|
"thirdparty": []string{"fb", "tw", "gl", "ap"},
|
|
"disable_mmt": false,
|
|
"server_guest": false,
|
|
"thirdparty_ignore": map[string]any{},
|
|
"enable_ps_bind_account": false,
|
|
"thirdparty_login_configs": map[string]any{
|
|
"tw": map[string]any{"token_type": "TK_GAME_TOKEN", "game_token_expires_in": 2592000},
|
|
"ap": map[string]any{"token_type": "TK_GAME_TOKEN", "game_token_expires_in": 2592000},
|
|
"fb": map[string]any{"token_type": "TK_GAME_TOKEN", "game_token_expires_in": 2592000},
|
|
"gl": map[string]any{"token_type": "TK_GAME_TOKEN", "game_token_expires_in": 2592000},
|
|
},
|
|
"initialize_firebase": false,
|
|
"bbs_auth_login": false,
|
|
"bbs_auth_login_ignore": []any{},
|
|
"fetch_instance_id": false,
|
|
"enable_flash_login": false,
|
|
},
|
|
})
|
|
}
|
|
|
|
func comboGetConfigReply(w http.ResponseWriter) {
|
|
writeJSON(w, map[string]any{
|
|
"retcode": 0,
|
|
"message": "OK",
|
|
"data": map[string]any{
|
|
"protocol": true,
|
|
"qr_enabled": false,
|
|
"log_level": "INFO",
|
|
"announce_url": "",
|
|
"push_alias_type": 0,
|
|
"disable_ysdk_guard": true,
|
|
"enable_announce_pic_popup": false,
|
|
"app_name": "Honkai: Star Rail",
|
|
"qr_enabled_apps": map[string]bool{"bbs": false, "cloud": false},
|
|
"qr_app_icons": map[string]string{"app": "", "bbs": "", "cloud": ""},
|
|
"qr_cloud_display_name": "",
|
|
"enable_user_center": true,
|
|
"functional_switch_configs": map[string]any{},
|
|
},
|
|
})
|
|
}
|
|
|
|
func compareProtocolVersionReply(w http.ResponseWriter) {
|
|
writeJSON(w, map[string]any{
|
|
"retcode": 0,
|
|
"message": "OK",
|
|
"data": map[string]any{
|
|
"modified": false,
|
|
"protocol": nil,
|
|
},
|
|
})
|
|
}
|
|
|
|
func agreementInfosReply(w http.ResponseWriter) {
|
|
writeJSON(w, map[string]any{
|
|
"retcode": 0,
|
|
"message": "OK",
|
|
"data": map[string]any{
|
|
"marketing_agreements": []any{},
|
|
},
|
|
})
|
|
}
|
|
|
|
func comboConfigReply(w http.ResponseWriter) {
|
|
writeJSON(w, map[string]any{
|
|
"retcode": 0,
|
|
"message": "OK",
|
|
"data": map[string]any{
|
|
"vals": map[string]any{
|
|
"kibana_pc_config": `{"enable":0,"level":"Info","modules":["download"]}`,
|
|
"network_report_config": `{"enable":0,"status_codes":[206],"url_paths":["dataUpload","red_dot"]}`,
|
|
"list_price_tierv2_enable": "false",
|
|
"enable_web_dpi": "true",
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func deviceFpReply(w http.ResponseWriter) {
|
|
writeJSON(w, map[string]any{
|
|
"code": 200,
|
|
"device_fp": "20261030233000",
|
|
"msg": "ok",
|
|
})
|
|
}
|
|
|
|
func announcementReply(w http.ResponseWriter) {
|
|
writeJSON(w, map[string]any{
|
|
"retcode": 0,
|
|
"message": "OK",
|
|
"data": map[string]any{
|
|
"alert": false,
|
|
"alert_id": 0,
|
|
"remind": false,
|
|
"extra_remind": false,
|
|
"total": 0,
|
|
"list": []any{},
|
|
"infos": []any{},
|
|
},
|
|
})
|
|
}
|
|
|
|
func notFound(w http.ResponseWriter, _ *http.Request) {
|
|
writeJSON(w, map[string]any{"message": "OK", "retcode": 0})
|
|
}
|
|
|
|
// srToolsSave is the /srtools upload endpoint (SRTools writes freesr-data.json).
|
|
func srToolsSave(w http.ResponseWriter, r *http.Request) {
|
|
var payload struct {
|
|
Data json.RawMessage `json:"data"`
|
|
}
|
|
body, err := io.ReadAll(r.Body)
|
|
if err != nil || len(body) == 0 {
|
|
writeJSON(w, map[string]any{"message": "empty body", "status": 400})
|
|
return
|
|
}
|
|
|
|
toWrite := body
|
|
if err := json.Unmarshal(body, &payload); err == nil && len(payload.Data) > 0 {
|
|
toWrite = payload.Data
|
|
}
|
|
|
|
// pretty-print and store; freesr-data.json uses the same layout the
|
|
// gameserver parses, so pass through unchanged
|
|
var pretty map[string]any
|
|
if err := json.Unmarshal(toWrite, &pretty); err != nil {
|
|
writeJSON(w, map[string]any{"message": "malformed json: " + err.Error(), "status": 500})
|
|
return
|
|
}
|
|
out, _ := json.MarshalIndent(pretty, "", " ")
|
|
if err := os.WriteFile("freesr-data.json", out, 0o644); err != nil {
|
|
writeJSON(w, map[string]any{"message": "failed to write freesr-data.json: " + err.Error(), "status": 500})
|
|
return
|
|
}
|
|
|
|
// Reload and sync persistent
|
|
if d, err := common.LoadFreesrData(); err == nil {
|
|
d.SavePersistent()
|
|
}
|
|
|
|
writeJSON(w, map[string]any{"message": "OK", "status": 200})
|
|
}
|
|
|
|
func loggingMiddleware(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
query := ""
|
|
if r.URL.RawQuery != "" {
|
|
query = "?" + r.URL.RawQuery
|
|
}
|
|
Log(fmt.Sprintf("[%s] %s%s", r.Method, r.URL.Path, query))
|
|
next.ServeHTTP(w, r)
|
|
})
|
|
}
|
|
|
|
// Listen starts the HTTP sdkserver.
|
|
func Listen() error {
|
|
runtime, err := common.LoadRuntimeConfig()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
state := &appState{hotfixMap: loadHotfix(), runtime: runtime}
|
|
Log(fmt.Sprintf("loaded %d hotfix versions", len(state.hotfixMap)))
|
|
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/query_dispatch", func(w http.ResponseWriter, r *http.Request) { queryDispatch(w, r, runtime) })
|
|
mux.HandleFunc("/query_gateway", queryGateway(state))
|
|
|
|
// HoYoverse / Star Rail Global & CN Authentication Routes
|
|
mux.HandleFunc("/{product_name}/account/ma-passport/api/appLoginByPassword", func(w http.ResponseWriter, r *http.Request) { state.apnLoginReply(w, r) })
|
|
mux.HandleFunc("/account/ma-passport/api/appLoginByPassword", func(w http.ResponseWriter, r *http.Request) { state.apnLoginReply(w, r) })
|
|
mux.HandleFunc("/account/ma-cn-passport/app/loginByPassword", func(w http.ResponseWriter, r *http.Request) { state.apnLoginReply(w, r) })
|
|
|
|
mux.HandleFunc("/{product_name}/account/ma-passport/token/verify", func(w http.ResponseWriter, r *http.Request) { state.apnVerifyReply(w, r) })
|
|
mux.HandleFunc("/account/ma-passport/token/verify", func(w http.ResponseWriter, r *http.Request) { state.apnVerifyReply(w, r) })
|
|
mux.HandleFunc("/account/ma-cn-session/app/verify", func(w http.ResponseWriter, r *http.Request) { state.apnVerifyReply(w, r) })
|
|
|
|
mux.HandleFunc("/{product_name}/mdk/shield/api/login", func(w http.ResponseWriter, r *http.Request) { state.loginReply(w, r) })
|
|
mux.HandleFunc("/{product_name}/mdk/shield/api/verify", func(w http.ResponseWriter, r *http.Request) { state.loginReply(w, r) })
|
|
mux.HandleFunc("/{product_name}/mdk/shield/api/loadConfig", func(w http.ResponseWriter, _ *http.Request) { shieldLoadConfigReply(w) })
|
|
|
|
mux.HandleFunc("/{product_name}/combo/granter/api/getConfig", func(w http.ResponseWriter, _ *http.Request) { comboGetConfigReply(w) })
|
|
mux.HandleFunc("/{product_name}/combo/granter/api/compareProtocolVersion", func(w http.ResponseWriter, _ *http.Request) { compareProtocolVersionReply(w) })
|
|
mux.HandleFunc("/{product_name}/combo/granter/login/v2/login", func(w http.ResponseWriter, _ *http.Request) { state.granterLoginReply(w) })
|
|
mux.HandleFunc("/{product_name}/mdk/agreement/api/getAgreementInfos", func(w http.ResponseWriter, _ *http.Request) { agreementInfosReply(w) })
|
|
|
|
mux.HandleFunc("/combo/box/api/config/sdk/combo", func(w http.ResponseWriter, _ *http.Request) { comboConfigReply(w) })
|
|
mux.HandleFunc("/combo/box/api/config/sw/precache", func(w http.ResponseWriter, _ *http.Request) { comboConfigReply(w) })
|
|
mux.HandleFunc("/device-fp/api/getFp", func(w http.ResponseWriter, _ *http.Request) { deviceFpReply(w) })
|
|
mux.HandleFunc("/device-fp/api/getExtList", func(w http.ResponseWriter, _ *http.Request) { deviceFpReply(w) })
|
|
|
|
mux.HandleFunc("/account/risky/api/check", func(w http.ResponseWriter, _ *http.Request) { riskyCheckReply(w) })
|
|
mux.HandleFunc("/{product_name}/combo/red_dot/list", func(w http.ResponseWriter, _ *http.Request) { announcementReply(w) })
|
|
mux.HandleFunc("/common/{product_name}/announcement/api/getAlertAnn", func(w http.ResponseWriter, _ *http.Request) { announcementReply(w) })
|
|
mux.HandleFunc("/common/{product_name}/announcement/api/getAlertPic", func(w http.ResponseWriter, _ *http.Request) { announcementReply(w) })
|
|
|
|
mux.HandleFunc("/srtools", corsWrapper(srToolsSave))
|
|
registerAPIRoutes(mux)
|
|
|
|
// Fallback Router: ensures any unmapped path variations from various client versions are caught safely
|
|
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
|
path := r.URL.Path
|
|
if path == "/" || path == "/dashboard" {
|
|
apiRoot(w, r)
|
|
return
|
|
}
|
|
|
|
Log(fmt.Sprintf("Fallback matching for path: %s", path))
|
|
|
|
switch {
|
|
case strings.Contains(path, "loginByPassword") || strings.Contains(path, "appLoginByPassword"):
|
|
state.apnLoginReply(w, r)
|
|
case strings.Contains(path, "ma-passport/token/verify") || strings.Contains(path, "ma-cn-session/app/verify") || strings.HasSuffix(path, "/token/verify"):
|
|
state.apnVerifyReply(w, r)
|
|
case strings.Contains(path, "mdk/shield/api/login"):
|
|
state.loginReply(w, r)
|
|
case strings.Contains(path, "mdk/shield/api/verify"):
|
|
state.loginReply(w, r)
|
|
case strings.Contains(path, "combo/granter/login/v2/login"):
|
|
state.granterLoginReply(w)
|
|
case strings.Contains(path, "mdk/shield/api/loadConfig"):
|
|
shieldLoadConfigReply(w)
|
|
case strings.Contains(path, "combo/granter/api/getConfig"):
|
|
comboGetConfigReply(w)
|
|
case strings.Contains(path, "combo/granter/api/compareProtocolVersion"):
|
|
compareProtocolVersionReply(w)
|
|
case strings.Contains(path, "mdk/agreement/api/getAgreementInfos"):
|
|
agreementInfosReply(w)
|
|
case strings.Contains(path, "combo/box/api/config"):
|
|
comboConfigReply(w)
|
|
case strings.Contains(path, "risky/api/check"):
|
|
riskyCheckReply(w)
|
|
case strings.Contains(path, "device-fp"):
|
|
deviceFpReply(w)
|
|
case strings.Contains(path, "announcement") || strings.Contains(path, "red_dot"):
|
|
announcementReply(w)
|
|
default:
|
|
notFound(w, r)
|
|
}
|
|
})
|
|
|
|
addr := runtime.SDKAddress()
|
|
Log("sdkserver is listening at " + addr)
|
|
Log(fmt.Sprintf("REST API Gateway is live at %s/", runtime.PublicSDKURL))
|
|
return http.ListenAndServe(addr, loggingMiddleware(withLuaOverlay(mux, runtime.LuaOverlay)))
|
|
}
|
|
|
|
func Log(msg string) { fmt.Println("[sdkserver]", msg) }
|