mirror of
https://github.com/truewhile/MeBox.git
synced 2026-10-06 05:16:38 +08:00
fix: Emby 远程挂载账号编辑保留凭据 + 代理开关回显
- UpdateStrmAccount 对 emby_remote 做合并式更新:只覆盖传入键,未提及的 地址/用户名/密码/token 密文保留,避免编辑代理开关时清空凭据 - 账号列表/编辑表单回显 proxy_play(服务端解密后下发) - 前端 StrmAccount 类型与编辑对话框支持 proxy_play 回显
This commit is contained in:
@@ -28,17 +28,25 @@ type strmAccountView struct {
|
|||||||
model.StrmAccount
|
model.StrmAccount
|
||||||
HasCredential bool `json:"has_credential"`
|
HasCredential bool `json:"has_credential"`
|
||||||
ProviderLabel string `json:"provider_label"`
|
ProviderLabel string `json:"provider_label"`
|
||||||
|
// ProxyPlay 仅远程 Emby 挂载账号返回:播放流量是否经过 MMTL 代理(编辑回显用)。
|
||||||
|
ProxyPlay *bool `json:"proxy_play,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func strmAccountViews(accounts []model.StrmAccount) []strmAccountView {
|
func strmAccountViews(svc *service.Container, accounts []model.StrmAccount) []strmAccountView {
|
||||||
out := make([]strmAccountView, 0, len(accounts))
|
out := make([]strmAccountView, 0, len(accounts))
|
||||||
for i := range accounts {
|
for i := range accounts {
|
||||||
a := accounts[i]
|
a := accounts[i]
|
||||||
out = append(out, strmAccountView{
|
view := strmAccountView{
|
||||||
StrmAccount: a,
|
StrmAccount: a,
|
||||||
HasCredential: service.HasStrmAccountCredential(&a),
|
HasCredential: service.HasStrmAccountCredential(&a),
|
||||||
ProviderLabel: providerLabelOf(a.Provider),
|
ProviderLabel: providerLabelOf(a.Provider),
|
||||||
})
|
}
|
||||||
|
if a.Provider == model.StrmProviderEmbyRemote && svc != nil && svc.EmbyRemote != nil {
|
||||||
|
if proxyPlay, err := svc.EmbyRemote.ProxyPlayOf(&a); err == nil {
|
||||||
|
view.ProxyPlay = &proxyPlay
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out = append(out, view)
|
||||||
}
|
}
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
@@ -58,7 +66,7 @@ func listStrmAccountsHandler(svc *service.Container) gin.HandlerFunc {
|
|||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.JSON(http.StatusOK, strmAccountViews(accounts))
|
c.JSON(http.StatusOK, strmAccountViews(svc, accounts))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,7 +82,7 @@ func createStrmAccountHandler(svc *service.Container) gin.HandlerFunc {
|
|||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
views := strmAccountViews([]model.StrmAccount{*acct})
|
views := strmAccountViews(svc, []model.StrmAccount{*acct})
|
||||||
c.JSON(http.StatusOK, views[0])
|
c.JSON(http.StatusOK, views[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -92,7 +100,7 @@ func updateStrmAccountHandler(svc *service.Container) gin.HandlerFunc {
|
|||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
views := strmAccountViews([]model.StrmAccount{*acct})
|
views := strmAccountViews(svc, []model.StrmAccount{*acct})
|
||||||
c.JSON(http.StatusOK, views[0])
|
c.JSON(http.StatusOK, views[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -114,7 +122,7 @@ func testStrmAccountHandler(svc *service.Container) gin.HandlerFunc {
|
|||||||
c.JSON(http.StatusNotFound, gin.H{"error": "网盘账号不存在"})
|
c.JSON(http.StatusNotFound, gin.H{"error": "网盘账号不存在"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
views := strmAccountViews([]model.StrmAccount{*acct})
|
views := strmAccountViews(svc, []model.StrmAccount{*acct})
|
||||||
c.JSON(http.StatusOK, views[0])
|
c.JSON(http.StatusOK, views[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -272,6 +272,15 @@ func (r *EmbyRemoteService) TestConnection(ctx context.Context, acct *model.Strm
|
|||||||
return r.doGet(ctx, acct, cfg, "/System/Info", nil, &out)
|
return r.doGet(ctx, acct, cfg, "/System/Info", nil, &out)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ProxyPlayOf 返回账号是否配置了播放代理(供账号列表/编辑回显)。
|
||||||
|
func (r *EmbyRemoteService) ProxyPlayOf(acct *model.StrmAccount) (bool, error) {
|
||||||
|
cfg, err := r.configOf(acct)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return cfg.ProxyPlay, nil
|
||||||
|
}
|
||||||
|
|
||||||
// ─── 元数据 / 目录聚合 ─────────────────────────────────────────────────────────
|
// ─── 元数据 / 目录聚合 ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
// RemoteViews 拉取远程媒体库(View)列表,返回远程原始 view map(未重写)。
|
// RemoteViews 拉取远程媒体库(View)列表,返回远程原始 view map(未重写)。
|
||||||
|
|||||||
@@ -237,6 +237,33 @@ func (s *StrmService) strmAccountConfig(acct *model.StrmAccount) (map[string]str
|
|||||||
return cfg, nil
|
return cfg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// mergeEmbyRemoteConfig 对远程 Emby 账号配置做合并式更新:config 中出现的键
|
||||||
|
// 覆盖写入(敏感键按明文加密),未出现的键保留原密文;显式空字符串=清除。
|
||||||
|
func (s *StrmService) mergeEmbyRemoteConfig(existing string, config map[string]string) (string, error) {
|
||||||
|
out := map[string]string{}
|
||||||
|
if strings.TrimSpace(existing) != "" {
|
||||||
|
if err := json.Unmarshal([]byte(existing), &out); err != nil {
|
||||||
|
return "", fmt.Errorf("decode account config: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for k, v := range config {
|
||||||
|
if v == "" {
|
||||||
|
delete(out, k)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strmContains(StrmAccountSecretKeys, k) {
|
||||||
|
out[k] = s.crypto.Encrypt(v)
|
||||||
|
} else {
|
||||||
|
out[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data, err := json.Marshal(out)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(data), nil
|
||||||
|
}
|
||||||
|
|
||||||
// HasStrmAccountCredential 报告账号是否已配置核心凭据(用于前端展示)。
|
// HasStrmAccountCredential 报告账号是否已配置核心凭据(用于前端展示)。
|
||||||
func HasStrmAccountCredential(acct *model.StrmAccount) bool {
|
func HasStrmAccountCredential(acct *model.StrmAccount) bool {
|
||||||
switch acct.Provider {
|
switch acct.Provider {
|
||||||
@@ -292,7 +319,16 @@ func (s *StrmService) UpdateStrmAccount(ctx context.Context, id, name string, en
|
|||||||
acct.Enabled = *enabled
|
acct.Enabled = *enabled
|
||||||
}
|
}
|
||||||
if len(config) > 0 {
|
if len(config) > 0 {
|
||||||
enc, err := s.strmAccountConfigJSON(config, true)
|
var enc string
|
||||||
|
var err error
|
||||||
|
if acct.Provider == model.StrmProviderEmbyRemote {
|
||||||
|
// 远程 Emby 账号:合并式更新。config 中出现的键覆盖(敏感键按明文
|
||||||
|
// 加密写入),未出现的键保留原密文——避免编辑「代理开关」时把已
|
||||||
|
// 保存的地址与凭据清空。
|
||||||
|
enc, err = s.mergeEmbyRemoteConfig(acct.Config, config)
|
||||||
|
} else {
|
||||||
|
enc, err = s.strmAccountConfigJSON(config, true)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ export function StrmAccountDialog({
|
|||||||
const [username, setUsername] = useState('')
|
const [username, setUsername] = useState('')
|
||||||
const [password, setPassword] = useState('')
|
const [password, setPassword] = useState('')
|
||||||
const [token, setToken] = useState('')
|
const [token, setToken] = useState('')
|
||||||
const [proxyPlay, setProxyPlay] = useState(false)
|
const [proxyPlay, setProxyPlay] = useState(existing?.proxy_play ?? false)
|
||||||
const [saving, setSaving] = useState(false)
|
const [saving, setSaving] = useState(false)
|
||||||
|
|
||||||
const buildConfig = (): Record<string, string> => {
|
const buildConfig = (): Record<string, string> => {
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ export interface StrmAccount {
|
|||||||
last_test_ok: boolean
|
last_test_ok: boolean
|
||||||
has_credential: boolean
|
has_credential: boolean
|
||||||
provider_label: string
|
provider_label: string
|
||||||
|
/** 仅远程 Emby 挂载账号返回:播放流量是否经过 MMTL 代理 */
|
||||||
|
proxy_play?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface StrmAccountInput {
|
export interface StrmAccountInput {
|
||||||
|
|||||||
Reference in New Issue
Block a user