chore: 并入 iotlcm-upstream-proxy 历史到 upstream-proxy/(原提交 44209ec)

This commit is contained in:
MiFaZhan
2026-11-02 13:28:02 +08:00
117 changed files with 8690 additions and 0 deletions
@@ -0,0 +1,5 @@
// =================================================================================
// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish.
// =================================================================================
package cardquery
@@ -0,0 +1,79 @@
package cardquery
import (
"crypto/subtle"
"encoding/json"
"net/http"
"strings"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/model"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/service"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
)
var appDiagnosisQueryPaths = map[string]bool{
"/sim/query/sim-basic-info": true,
"/sim/query/sim-status": true,
"/sim/query/sim-session": true,
"/sim/query/sim-apn-info": true,
"/sim/query/sim-imei": true,
"/sim/query/sim-device-status": true,
"/sim/query/sim-card-bind-status": true,
"/sim/query/sim-data-diagnosis": true,
"/sim/query/sim-stop-reason": true,
"/sim/query/sim-frozen-state": true,
"/sim/query/sim-datausage": true,
"/sim/query/sim-region-limit-status": true,
}
// AppDiagnosisQuery bypasses the proxy's local card lookup only for verified
// internal, read-only diagnosis requests. The existing /sim/* API is untouched.
func AppDiagnosisQuery(r *ghttp.Request) {
secret := strings.TrimSpace(g.Cfg().MustGet(r.Context(), "appDiagnosis.internalToken").String())
provided := r.Header.Get("X-App-Diagnosis-Token")
if secret == "" || subtle.ConstantTimeCompare([]byte(secret), []byte(provided)) != 1 {
appDiagnosisReject(r, http.StatusForbidden, "内部查询未授权")
return
}
path := strings.TrimPrefix(r.URL.Path, "/internal/app-diagnosis")
if !appDiagnosisQueryPaths[path] {
appDiagnosisReject(r, http.StatusNotFound, "诊断接口不存在")
return
}
if len(r.GetBody()) > 64*1024 {
appDiagnosisReject(r, http.StatusRequestEntityTooLarge, "请求内容过大")
return
}
var input model.SimBaseReqJson
if err := json.Unmarshal(r.GetBody(), &input); err != nil || len(input.SimKeys) != 1 || input.SimKeys[0] == nil {
appDiagnosisReject(r, http.StatusBadRequest, "诊断卡参数无效")
return
}
key := input.SimKeys[0]
key.ChanKey = strings.TrimSpace(key.ChanKey)
if key.ChanKey == "" || strings.TrimSpace(key.ICCID) == "" {
appDiagnosisReject(r, http.StatusBadRequest, "诊断卡通道或ICCID缺失")
return
}
// Channel credentials and gateway routing are loaded by UpstreamAPI from
// this proxy's own configured channel, never from the legacy database.
channel, ok := service.UpstreamAPI().GetChan(key.ChanKey)
if !ok || channel == nil || channel.Instance == nil {
appDiagnosisReject(r, http.StatusNotFound, "诊断通道不可用")
return
}
r.URL.Path = path
data, err := service.UpstreamAPI().SimBase(r.Context(), &input)
if err != nil {
r.SetError(err)
return
}
r.Response.WriteJson(g.Map{"code": 0, "data": data})
}
func appDiagnosisReject(r *ghttp.Request, status int, message string) {
r.Response.WriteStatus(status)
r.Response.WriteJson(g.Map{"code": status, "message": message})
}
@@ -0,0 +1,55 @@
package cardquery
import (
"net/http"
"strings"
"testing"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/os/gcfg"
)
func TestDiagnosisProxyRejectsUntrustedAndInvalidRequests(t *testing.T) {
adapter, err := gcfg.NewAdapterContent(`{"appDiagnosis":{"internalToken":"test-token"}}`)
if err != nil {
t.Fatal(err)
}
previous := g.Cfg().GetAdapter()
g.Cfg().SetAdapter(adapter)
t.Cleanup(func() { g.Cfg().SetAdapter(previous) })
server := ghttp.GetServer("diagnosis-proxy-auth-test")
server.SetAddr("127.0.0.1:0")
server.Group("/", func(group *ghttp.RouterGroup) {
group.POST("/internal/app-diagnosis/sim/*any", AppDiagnosisQuery)
})
if err := server.Start(); err != nil {
t.Fatal(err)
}
t.Cleanup(func() { _ = server.Shutdown() })
url := "http://" + server.GetListenedAddress() + "/internal/app-diagnosis"
for _, tc := range []struct {
path string
token string
body string
status int
}{
{"/sim/query/sim-basic-info", "", `{"simKeys":[{"iccid":"123","chanKey":"test"}]}`, http.StatusForbidden},
{"/sim/restart", "test-token", `{"simKeys":[{"iccid":"123","chanKey":"test"}]}`, http.StatusNotFound},
{"/sim/query/sim-basic-info", "test-token", `{"simKeys":[{"iccid":"123"}]}`, http.StatusBadRequest},
} {
request, err := http.NewRequest(http.MethodPost, url+tc.path, strings.NewReader(tc.body))
if err != nil {
t.Fatal(err)
}
request.Header.Set("X-App-Diagnosis-Token", tc.token)
response, err := http.DefaultClient.Do(request)
if err != nil {
t.Fatal(err)
}
_ = response.Body.Close()
if response.StatusCode != tc.status {
t.Errorf("%s: got %d want %d", tc.path, response.StatusCode, tc.status)
}
}
}
@@ -0,0 +1,15 @@
// =================================================================================
// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish.
// =================================================================================
package cardquery
import (
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/api/cardquery"
)
type ControllerV1 struct{}
func NewV1() cardquery.ICardqueryV1 {
return &ControllerV1{}
}
@@ -0,0 +1,65 @@
package cardquery
import (
"context"
"encoding/json"
"strings"
"github.com/gogf/gf/v2/frame/g"
v1 "git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/api/cardquery/v1"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/service"
)
// func (c *ControllerV1) SimBase(ctx context.Context, req *v1.SimBaseReq) (res *v1.SimBaseRes, err error) {
// g.Log().Debugf(ctx, "SimBase req %+v", req)
// r := g.RequestFromCtx(ctx)
// if r.Method != "POST" {
// return nil, gerror.New("method not allowed")
// }
// path := r.URL.Path
// if path == "" {
// return nil, gerror.New("path is required")
// }
// // 获取渠道KEY
// for _, simKey := range req.SimKeys {
// if simKey.ChanKey == "" {
// simKey.ChanKey = "channel_003"
// }
// }
// // 获取三码信息
// url := "http://iotlcmupstreamproxy-http-svc" + path
// client := g.Client()
// client.SetDiscovery(gsvc.GetRegistry())
// client.SetTimeout(time.Second * 10)
// postData, err := json.Marshal(req)
// if err != nil {
// return nil, err
// }
// resp, err := client.Post(ctx, url, postData)
// if err != nil {
// return nil, err
// }
// body := resp.ReadAllString()
// var simBaseRes model.SimBaseResJson
// err = json.Unmarshal([]byte(body), &simBaseRes)
// if err != nil {
// return nil, err
// }
// return &simBaseRes, nil
// }
func (c *ControllerV1) SimBase(ctx context.Context, req *v1.SimBaseReq) (res *v1.SimBaseRes, err error) {
request := g.RequestFromCtx(ctx)
if request != nil && (request.URL.Path == "/sim/change/sim-apn" || request.URL.Path == "/sim/query/sim-apn-capability") && strings.HasPrefix(strings.ToLower(request.Header.Get("Content-Type")), "application/json") {
// GoFrame通用字段转换不能保留json.RawMessage,APN协议直接按原始JSON绑定。
if err := json.Unmarshal(request.GetBody(), &req.SimBaseReqJson); err != nil {
return nil, err
}
}
out, err := service.SimKeyFill().SimKeyFill(ctx, &req.SimBaseReqJson)
if err != nil {
return nil, err
}
return &out, nil
}