Skip to content

Commit 8fb7d47

Browse files
author
QTom
committed
feat(admin): 代理密码可见性 + 复制代理 URL 功能
- 新增 AdminProxy / AdminProxyWithAccountCount DTO,遵循项目 Admin DTO 分层模式 - Proxy.Password 恢复 json:"-" 隐藏,ProxyFromService 不再赋值密码(纵深防御) - 管理员接口使用 ProxyFromServiceAdmin / ProxyWithAccountCountFromServiceAdmin - 前端代理列表新增 Auth 列:显示用户名 + 掩码密码 + 眼睛图标切换可见性 - Address 列新增复制按钮:左键复制完整 URL,右键选择格式 - 编辑模态框密码预填充 + 脏标记,避免误更新
1 parent dd8df48 commit 8fb7d47

6 files changed

Lines changed: 230 additions & 28 deletions

File tree

backend/internal/handler/admin/proxy_handler.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,9 @@ func (h *ProxyHandler) List(c *gin.Context) {
6464
return
6565
}
6666

67-
out := make([]dto.ProxyWithAccountCount, 0, len(proxies))
67+
out := make([]dto.AdminProxyWithAccountCount, 0, len(proxies))
6868
for i := range proxies {
69-
out = append(out, *dto.ProxyWithAccountCountFromService(&proxies[i]))
69+
out = append(out, *dto.ProxyWithAccountCountFromServiceAdmin(&proxies[i]))
7070
}
7171
response.Paginated(c, out, total, page, pageSize)
7272
}
@@ -83,9 +83,9 @@ func (h *ProxyHandler) GetAll(c *gin.Context) {
8383
response.ErrorFrom(c, err)
8484
return
8585
}
86-
out := make([]dto.ProxyWithAccountCount, 0, len(proxies))
86+
out := make([]dto.AdminProxyWithAccountCount, 0, len(proxies))
8787
for i := range proxies {
88-
out = append(out, *dto.ProxyWithAccountCountFromService(&proxies[i]))
88+
out = append(out, *dto.ProxyWithAccountCountFromServiceAdmin(&proxies[i]))
8989
}
9090
response.Success(c, out)
9191
return
@@ -97,9 +97,9 @@ func (h *ProxyHandler) GetAll(c *gin.Context) {
9797
return
9898
}
9999

100-
out := make([]dto.Proxy, 0, len(proxies))
100+
out := make([]dto.AdminProxy, 0, len(proxies))
101101
for i := range proxies {
102-
out = append(out, *dto.ProxyFromService(&proxies[i]))
102+
out = append(out, *dto.ProxyFromServiceAdmin(&proxies[i]))
103103
}
104104
response.Success(c, out)
105105
}
@@ -119,7 +119,7 @@ func (h *ProxyHandler) GetByID(c *gin.Context) {
119119
return
120120
}
121121

122-
response.Success(c, dto.ProxyFromService(proxy))
122+
response.Success(c, dto.ProxyFromServiceAdmin(proxy))
123123
}
124124

125125
// Create handles creating a new proxy
@@ -143,7 +143,7 @@ func (h *ProxyHandler) Create(c *gin.Context) {
143143
if err != nil {
144144
return nil, err
145145
}
146-
return dto.ProxyFromService(proxy), nil
146+
return dto.ProxyFromServiceAdmin(proxy), nil
147147
})
148148
}
149149

@@ -176,7 +176,7 @@ func (h *ProxyHandler) Update(c *gin.Context) {
176176
return
177177
}
178178

179-
response.Success(c, dto.ProxyFromService(proxy))
179+
response.Success(c, dto.ProxyFromServiceAdmin(proxy))
180180
}
181181

182182
// Delete handles deleting a proxy

backend/internal/handler/dto/mappers.go

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,6 @@ func ProxyFromService(p *service.Proxy) *Proxy {
293293
Host: p.Host,
294294
Port: p.Port,
295295
Username: p.Username,
296-
Password: p.Password,
297296
Status: p.Status,
298297
CreatedAt: p.CreatedAt,
299298
UpdatedAt: p.UpdatedAt,
@@ -323,6 +322,51 @@ func ProxyWithAccountCountFromService(p *service.ProxyWithAccountCount) *ProxyWi
323322
}
324323
}
325324

325+
// ProxyFromServiceAdmin converts a service Proxy to AdminProxy DTO for admin users.
326+
// It includes the password field - user-facing endpoints must not use this.
327+
func ProxyFromServiceAdmin(p *service.Proxy) *AdminProxy {
328+
if p == nil {
329+
return nil
330+
}
331+
base := ProxyFromService(p)
332+
if base == nil {
333+
return nil
334+
}
335+
return &AdminProxy{
336+
Proxy: *base,
337+
Password: p.Password,
338+
}
339+
}
340+
341+
// ProxyWithAccountCountFromServiceAdmin converts a service ProxyWithAccountCount to AdminProxyWithAccountCount DTO.
342+
// It includes the password field - user-facing endpoints must not use this.
343+
func ProxyWithAccountCountFromServiceAdmin(p *service.ProxyWithAccountCount) *AdminProxyWithAccountCount {
344+
if p == nil {
345+
return nil
346+
}
347+
admin := ProxyFromServiceAdmin(&p.Proxy)
348+
if admin == nil {
349+
return nil
350+
}
351+
return &AdminProxyWithAccountCount{
352+
AdminProxy: *admin,
353+
AccountCount: p.AccountCount,
354+
LatencyMs: p.LatencyMs,
355+
LatencyStatus: p.LatencyStatus,
356+
LatencyMessage: p.LatencyMessage,
357+
IPAddress: p.IPAddress,
358+
Country: p.Country,
359+
CountryCode: p.CountryCode,
360+
Region: p.Region,
361+
City: p.City,
362+
QualityStatus: p.QualityStatus,
363+
QualityScore: p.QualityScore,
364+
QualityGrade: p.QualityGrade,
365+
QualitySummary: p.QualitySummary,
366+
QualityChecked: p.QualityChecked,
367+
}
368+
}
369+
326370
func ProxyAccountSummaryFromService(a *service.ProxyAccountSummary) *ProxyAccountSummary {
327371
if a == nil {
328372
return nil

backend/internal/handler/dto/types.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,32 @@ type ProxyWithAccountCount struct {
221221
QualityChecked *int64 `json:"quality_checked,omitempty"`
222222
}
223223

224+
// AdminProxy 是管理员接口使用的 proxy DTO(包含密码等敏感字段)。
225+
// 注意:普通接口不得使用此 DTO。
226+
type AdminProxy struct {
227+
Proxy
228+
Password string `json:"password,omitempty"`
229+
}
230+
231+
// AdminProxyWithAccountCount 是管理员接口使用的带账号统计的 proxy DTO。
232+
type AdminProxyWithAccountCount struct {
233+
AdminProxy
234+
AccountCount int64 `json:"account_count"`
235+
LatencyMs *int64 `json:"latency_ms,omitempty"`
236+
LatencyStatus string `json:"latency_status,omitempty"`
237+
LatencyMessage string `json:"latency_message,omitempty"`
238+
IPAddress string `json:"ip_address,omitempty"`
239+
Country string `json:"country,omitempty"`
240+
CountryCode string `json:"country_code,omitempty"`
241+
Region string `json:"region,omitempty"`
242+
City string `json:"city,omitempty"`
243+
QualityStatus string `json:"quality_status,omitempty"`
244+
QualityScore *int `json:"quality_score,omitempty"`
245+
QualityGrade string `json:"quality_grade,omitempty"`
246+
QualitySummary string `json:"quality_summary,omitempty"`
247+
QualityChecked *int64 `json:"quality_checked,omitempty"`
248+
}
249+
224250
type ProxyAccountSummary struct {
225251
ID int64 `json:"id"`
226252
Name string `json:"name"`

frontend/src/i18n/locales/en.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2345,6 +2345,8 @@ export default {
23452345
dataExportConfirm: 'Confirm Export',
23462346
dataExported: 'Data exported successfully',
23472347
dataExportFailed: 'Failed to export data',
2348+
copyProxyUrl: 'Copy Proxy URL',
2349+
urlCopied: 'Proxy URL copied',
23482350
searchProxies: 'Search proxies...',
23492351
allProtocols: 'All Protocols',
23502352
allStatus: 'All Status',
@@ -2358,6 +2360,7 @@ export default {
23582360
name: 'Name',
23592361
protocol: 'Protocol',
23602362
address: 'Address',
2363+
auth: 'Auth',
23612364
location: 'Location',
23622365
status: 'Status',
23632366
accounts: 'Accounts',

frontend/src/i18n/locales/zh.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2459,6 +2459,7 @@ export default {
24592459
name: '名称',
24602460
protocol: '协议',
24612461
address: '地址',
2462+
auth: '认证',
24622463
location: '地理位置',
24632464
status: '状态',
24642465
accounts: '账号数',
@@ -2486,6 +2487,8 @@ export default {
24862487
allStatuses: '全部状态'
24872488
},
24882489
// Additional keys used in ProxiesView
2490+
copyProxyUrl: '复制代理 URL',
2491+
urlCopied: '代理 URL 已复制',
24892492
allProtocols: '全部协议',
24902493
allStatus: '全部状态',
24912494
searchProxies: '搜索代理...',

0 commit comments

Comments
 (0)