333 lines
6.6 KiB
Go
333 lines
6.6 KiB
Go
package logweb
|
||
|
||
import (
|
||
"strings"
|
||
|
||
"time"
|
||
|
||
"xk-hy-transit-go/internal/config"
|
||
|
||
syncer "xk-hy-transit-go/internal/sync"
|
||
|
||
"xk-hy-transit-go/internal/xkapi"
|
||
)
|
||
|
||
// ConfigViewResponse Web 配置页 API 响应(敏感字段已脱敏)。
|
||
|
||
type ConfigViewResponse struct {
|
||
FetchedAt string `json:"fetchedAt"`
|
||
|
||
XkAPIBaseURL string `json:"xkApiBaseUrl"`
|
||
|
||
Cloud cloudConfigView `json:"cloud"`
|
||
|
||
LocalEnv localEnvView `json:"localEnv"`
|
||
|
||
Effective effectiveConfigView `json:"effective"`
|
||
|
||
Diagnostics []string `json:"diagnostics"`
|
||
|
||
CloudRaw map[string]any `json:"cloudRaw,omitempty"`
|
||
}
|
||
|
||
type cloudConfigView struct {
|
||
OrganID string `json:"organID"`
|
||
|
||
UnitID string `json:"unitID"`
|
||
|
||
OrganName string `json:"organName"`
|
||
|
||
HosCode string `json:"hosCode"`
|
||
|
||
HosName string `json:"hosName"`
|
||
|
||
FileBucket string `json:"fileBucket"`
|
||
|
||
FileUploadURL string `json:"fileUploadUrl"`
|
||
|
||
AppKeyPreview string `json:"appKeyPreview"`
|
||
|
||
AppSecretSet bool `json:"appSecretSet"`
|
||
|
||
AesKeySet bool `json:"aesKeySet"`
|
||
|
||
UploadTokenSet bool `json:"uploadTokenSet"`
|
||
|
||
UploadTokenPreview string `json:"uploadTokenPreview"`
|
||
|
||
RawError string `json:"rawError"`
|
||
}
|
||
|
||
type localEnvView struct {
|
||
HyAppKeyPreview string `json:"hyAppKeyPreview"`
|
||
|
||
HyAppSecretSet bool `json:"hyAppSecretSet"`
|
||
|
||
HyAesKeySet bool `json:"hyAesKeySet"`
|
||
|
||
HyFileBucket string `json:"hyFileBucket"`
|
||
|
||
FileUploadViaForward bool `json:"fileUploadViaForward"`
|
||
|
||
ForwardBaseURL string `json:"forwardBaseUrl"`
|
||
|
||
ForwardSharedSecretSet bool `json:"forwardSharedSecretSet"`
|
||
|
||
XkAPITokenSet bool `json:"xkApiTokenSet"`
|
||
}
|
||
|
||
type effectiveConfigView struct {
|
||
OrganID string `json:"organID"`
|
||
|
||
UnitID string `json:"unitID"`
|
||
|
||
OrganName string `json:"organName"`
|
||
|
||
AppKeyPreview string `json:"appKeyPreview"`
|
||
|
||
AppSecretSet bool `json:"appSecretSet"`
|
||
|
||
AesKeySet bool `json:"aesKeySet"`
|
||
|
||
FileBucket string `json:"fileBucket"`
|
||
|
||
UploadTokenSet bool `json:"uploadTokenSet"`
|
||
|
||
UploadTokenPreview string `json:"uploadTokenPreview"`
|
||
|
||
UploadTokenSource string `json:"uploadTokenSource"`
|
||
|
||
FileUploadURL string `json:"fileUploadURL"`
|
||
|
||
SourceNote string `json:"sourceNote"`
|
||
|
||
ConfigRefreshed string `json:"configRefreshedAt"`
|
||
}
|
||
|
||
func maskPreview(s string) string {
|
||
|
||
s = strings.TrimSpace(s)
|
||
|
||
if s == "" {
|
||
|
||
return ""
|
||
|
||
}
|
||
|
||
if len(s) <= 12 {
|
||
|
||
return s
|
||
|
||
}
|
||
|
||
return s[:8] + "****" + s[len(s)-4:]
|
||
|
||
}
|
||
|
||
func BuildConfigView(snap syncer.ConfigSnapshot, cfg config.Config) ConfigViewResponse {
|
||
|
||
cloud := snap.Cloud
|
||
|
||
localToken := strings.TrimSpace(snap.UploadToken)
|
||
|
||
cloudToken := strings.TrimSpace(snap.CloudUploadToken)
|
||
|
||
if cloudToken == "" {
|
||
|
||
cloudToken = strings.TrimSpace(cloud.UploadToken)
|
||
|
||
}
|
||
|
||
effectiveBucket := strings.TrimSpace(snap.EffectiveFileBucket)
|
||
|
||
view := ConfigViewResponse{
|
||
|
||
FetchedAt: time.Now().Format(time.RFC3339),
|
||
|
||
XkAPIBaseURL: cfg.XkAPIBaseURL,
|
||
|
||
Cloud: cloudConfigView{
|
||
|
||
OrganID: cloud.OrganID,
|
||
|
||
UnitID: cloud.UnitID,
|
||
|
||
OrganName: cloud.OrganName,
|
||
|
||
HosCode: cloud.HosCode,
|
||
|
||
HosName: cloud.HosName,
|
||
|
||
FileBucket: cloud.FileBucket,
|
||
|
||
FileUploadURL: cloud.FileUploadURL,
|
||
|
||
AppKeyPreview: maskPreview(cloud.AppKey),
|
||
|
||
AppSecretSet: strings.TrimSpace(cloud.AppSecret) != "",
|
||
|
||
AesKeySet: strings.TrimSpace(cloud.AesKey) != "",
|
||
|
||
UploadTokenSet: cloudToken != "",
|
||
|
||
UploadTokenPreview: maskPreview(cloudToken),
|
||
|
||
RawError: snap.CloudFetchError,
|
||
},
|
||
|
||
LocalEnv: localEnvView{
|
||
|
||
HyAppKeyPreview: maskPreview(cfg.HyAppKey),
|
||
|
||
HyAppSecretSet: strings.TrimSpace(cfg.HyAppSecret) != "",
|
||
|
||
HyAesKeySet: strings.TrimSpace(cfg.HyAesKey) != "",
|
||
|
||
HyFileBucket: cfg.HyFileBucket,
|
||
|
||
FileUploadViaForward: cfg.FileUploadViaForward,
|
||
|
||
ForwardBaseURL: cfg.ForwardBaseURL,
|
||
|
||
ForwardSharedSecretSet: strings.TrimSpace(cfg.ForwardSharedSecret) != "",
|
||
|
||
XkAPITokenSet: strings.TrimSpace(cfg.XkAPIToken) != "",
|
||
},
|
||
|
||
Effective: effectiveConfigView{
|
||
|
||
OrganID: snap.EffectiveOrgan.OrganID,
|
||
|
||
UnitID: snap.EffectiveOrgan.UnitID,
|
||
|
||
OrganName: snap.EffectiveOrgan.OrganName,
|
||
|
||
AppKeyPreview: maskPreview(snap.EffectiveAppKey),
|
||
|
||
AppSecretSet: strings.TrimSpace(snap.EffectiveSecret) != "",
|
||
|
||
AesKeySet: strings.TrimSpace(snap.EffectiveAesKey) != "",
|
||
|
||
FileBucket: effectiveBucket,
|
||
|
||
UploadTokenSet: localToken != "",
|
||
|
||
UploadTokenPreview: maskPreview(localToken),
|
||
|
||
UploadTokenSource: snap.UploadTokenSource,
|
||
|
||
FileUploadURL: snap.FileUploadURL,
|
||
|
||
SourceNote: "uploadToken:Runner 按 FileAuth.java 本地生成;appKey/secret/bucket 云端非空优先",
|
||
|
||
ConfigRefreshed: formatTime(snap.RefreshedAt),
|
||
},
|
||
}
|
||
|
||
if !snap.CloudOK {
|
||
|
||
view.Diagnostics = append(view.Diagnostics, "无法连接 xk-api /supervise/config:"+snap.CloudFetchError)
|
||
|
||
}
|
||
|
||
if effectiveBucket == "" {
|
||
|
||
view.Diagnostics = append(view.Diagnostics, "fileBucket 为空:请在 xk-api 配置 HY_FILE_BUCKET(或 transit .env HY_FILE_BUCKET)")
|
||
|
||
}
|
||
|
||
if !view.Effective.UploadTokenSet {
|
||
|
||
view.Diagnostics = append(view.Diagnostics, "本地 uploadToken 未生成:检查 HY_APP_KEY、HY_APP_SECRET、HY_FILE_BUCKET 后点「重新拉取」")
|
||
|
||
}
|
||
|
||
if cloudToken != "" && localToken != "" && cloudToken != localToken {
|
||
|
||
view.Diagnostics = append(view.Diagnostics, "云端预生成 uploadToken 与 Runner 本地 token 不一致(已使用本地 FileAuth.java,以 Runner 为准)")
|
||
|
||
}
|
||
|
||
if cloud.AppKey != "" && cfg.HyAppKey != "" && cloud.AppKey != cfg.HyAppKey {
|
||
|
||
view.Diagnostics = append(view.Diagnostics, "云端 appKey 与 transit 本地 HY_APP_KEY 不一致,生效值以云端为准,请点「重新拉取」")
|
||
|
||
}
|
||
|
||
if view.Effective.AppKeyPreview == "" {
|
||
|
||
view.Diagnostics = append(view.Diagnostics, "生效 appKey 为空:请在 xk-api 或 transit .env 配置 HY_APP_KEY")
|
||
|
||
}
|
||
|
||
if cfg.FileUploadViaForward {
|
||
|
||
if view.LocalEnv.ForwardSharedSecretSet {
|
||
|
||
view.Diagnostics = append(view.Diagnostics, "PDF 经 forward 上传:请确认 forward .env 的 FORWARD_SHARED_SECRET 与 transit 一致")
|
||
|
||
}
|
||
|
||
view.Diagnostics = append(view.Diagnostics, "PDF 经 forward 上传:请确认 forward ALLOW_IPS 包含中转机 IP(否则 HTTP 403 body=forbidden)")
|
||
|
||
}
|
||
|
||
view.CloudRaw = redactCloudRaw(cloud)
|
||
|
||
return view
|
||
|
||
}
|
||
|
||
func formatTime(t time.Time) string {
|
||
|
||
if t.IsZero() {
|
||
|
||
return "-"
|
||
|
||
}
|
||
|
||
return t.Format(time.RFC3339)
|
||
|
||
}
|
||
|
||
func redactCloudRaw(c xkapi.ConfigResponse) map[string]any {
|
||
|
||
return map[string]any{
|
||
|
||
"organID": c.OrganID,
|
||
|
||
"unitID": c.UnitID,
|
||
|
||
"organName": c.OrganName,
|
||
|
||
"hosCode": c.HosCode,
|
||
|
||
"hosName": c.HosName,
|
||
|
||
"fileUploadUrl": c.FileUploadURL,
|
||
|
||
"fileBucket": c.FileBucket,
|
||
|
||
"appKey": maskPreview(c.AppKey),
|
||
|
||
"appSecret": boolLabel(c.AppSecret != ""),
|
||
|
||
"aesKey": boolLabel(c.AesKey != ""),
|
||
|
||
"uploadToken": maskPreview(c.UploadToken),
|
||
}
|
||
|
||
}
|
||
|
||
func boolLabel(set bool) string {
|
||
|
||
if set {
|
||
|
||
return "(已配置)"
|
||
|
||
}
|
||
|
||
return "(未配置)"
|
||
|
||
}
|