Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
陈曦
sub2api
Commits
37047919
Commit
37047919
authored
Feb 05, 2026
by
LLLLLLiulei
Browse files
fix: harden import/export flow
parent
0b45d48e
Changes
5
Show whitespace changes
Inline
Side-by-side
backend/internal/handler/admin/account_data.go
View file @
37047919
...
...
@@ -21,8 +21,8 @@ const (
)
type
DataPayload
struct
{
Type
string
`json:"type"`
Version
int
`json:"version"`
Type
string
`json:"type
,omitempty
"`
Version
int
`json:"version
,omitempty
"`
ExportedAt
string
`json:"exported_at"`
Proxies
[]
DataProxy
`json:"proxies"`
Accounts
[]
DataAccount
`json:"accounts"`
...
...
@@ -160,8 +160,6 @@ func (h *AccountHandler) ExportData(c *gin.Context) {
}
payload
:=
DataPayload
{
Type
:
dataType
,
Version
:
dataVersion
,
ExportedAt
:
time
.
Now
()
.
UTC
()
.
Format
(
time
.
RFC3339
),
Proxies
:
dataProxies
,
Accounts
:
dataAccounts
,
...
...
@@ -218,9 +216,17 @@ func (h *AccountHandler) ImportData(c *gin.Context) {
})
continue
}
normalizedStatus
:=
normalizeProxyStatus
(
item
.
Status
)
if
existingID
,
ok
:=
proxyKeyToID
[
key
];
ok
{
proxyKeyToID
[
key
]
=
existingID
result
.
ProxyReused
++
if
normalizedStatus
!=
""
{
if
proxy
,
err
:=
h
.
adminService
.
GetProxy
(
c
.
Request
.
Context
(),
existingID
);
err
==
nil
&&
proxy
!=
nil
&&
proxy
.
Status
!=
normalizedStatus
{
_
,
_
=
h
.
adminService
.
UpdateProxy
(
c
.
Request
.
Context
(),
existingID
,
&
service
.
UpdateProxyInput
{
Status
:
normalizedStatus
,
})
}
}
continue
}
...
...
@@ -245,9 +251,9 @@ func (h *AccountHandler) ImportData(c *gin.Context) {
proxyKeyToID
[
key
]
=
created
.
ID
result
.
ProxyCreated
++
if
item
.
Status
!=
""
&&
item
.
Status
!=
created
.
Status
{
if
normalized
Status
!=
""
&&
normalized
Status
!=
created
.
Status
{
_
,
_
=
h
.
adminService
.
UpdateProxy
(
c
.
Request
.
Context
(),
created
.
ID
,
&
service
.
UpdateProxyInput
{
Status
:
item
.
Status
,
Status
:
normalized
Status
,
})
}
}
...
...
@@ -465,15 +471,18 @@ func parseIncludeProxies(c *gin.Context) (bool, error) {
}
func
validateDataHeader
(
payload
DataPayload
)
error
{
if
payload
.
Type
==
""
{
return
errors
.
New
(
"data type is required"
)
}
if
payload
.
Type
!=
dataType
&&
payload
.
Type
!=
legacyDataType
{
if
payload
.
Type
!=
""
&&
payload
.
Type
!=
dataType
&&
payload
.
Type
!=
legacyDataType
{
return
fmt
.
Errorf
(
"unsupported data type: %s"
,
payload
.
Type
)
}
if
payload
.
Version
!=
dataVersion
{
if
payload
.
Version
!=
0
&&
payload
.
Version
!=
dataVersion
{
return
fmt
.
Errorf
(
"unsupported data version: %d"
,
payload
.
Version
)
}
if
payload
.
Proxies
==
nil
{
return
errors
.
New
(
"proxies is required"
)
}
if
payload
.
Accounts
==
nil
{
return
errors
.
New
(
"accounts is required"
)
}
return
nil
}
...
...
@@ -493,9 +502,8 @@ func validateDataProxy(item DataProxy) error {
return
fmt
.
Errorf
(
"proxy protocol is invalid: %s"
,
item
.
Protocol
)
}
if
item
.
Status
!=
""
{
switch
item
.
Status
{
case
service
.
StatusActive
,
service
.
StatusDisabled
,
"inactive"
:
default
:
normalizedStatus
:=
normalizeProxyStatus
(
item
.
Status
)
if
normalizedStatus
!=
service
.
StatusActive
&&
normalizedStatus
!=
"inactive"
{
return
fmt
.
Errorf
(
"proxy status is invalid: %s"
,
item
.
Status
)
}
}
...
...
@@ -538,3 +546,17 @@ func defaultProxyName(name string) string {
}
return
name
}
func
normalizeProxyStatus
(
status
string
)
string
{
normalized
:=
strings
.
TrimSpace
(
strings
.
ToLower
(
status
))
switch
normalized
{
case
""
:
return
""
case
service
.
StatusActive
:
return
service
.
StatusActive
case
"inactive"
,
service
.
StatusDisabled
:
return
"inactive"
default
:
return
normalized
}
}
backend/internal/handler/admin/account_data_handler_test.go
View file @
37047919
...
...
@@ -120,7 +120,8 @@ func TestExportDataIncludesSecrets(t *testing.T) {
var
resp
dataResponse
require
.
NoError
(
t
,
json
.
Unmarshal
(
rec
.
Body
.
Bytes
(),
&
resp
))
require
.
Equal
(
t
,
0
,
resp
.
Code
)
require
.
Equal
(
t
,
dataType
,
resp
.
Data
.
Type
)
require
.
Empty
(
t
,
resp
.
Data
.
Type
)
require
.
Equal
(
t
,
0
,
resp
.
Data
.
Version
)
require
.
Len
(
t
,
resp
.
Data
.
Proxies
,
1
)
require
.
Equal
(
t
,
"pass"
,
resp
.
Data
.
Proxies
[
0
]
.
Password
)
require
.
Len
(
t
,
resp
.
Data
.
Accounts
,
1
)
...
...
backend/internal/handler/admin/proxy_data.go
View file @
37047919
...
...
@@ -61,8 +61,6 @@ func (h *ProxyHandler) ExportData(c *gin.Context) {
}
payload
:=
DataPayload
{
Type
:
dataType
,
Version
:
dataVersion
,
ExportedAt
:
time
.
Now
()
.
UTC
()
.
Format
(
time
.
RFC3339
),
Proxies
:
dataProxies
,
Accounts
:
[]
DataAccount
{},
...
...
@@ -123,10 +121,11 @@ func (h *ProxyHandler) ImportData(c *gin.Context) {
continue
}
normalizedStatus
:=
normalizeProxyStatus
(
item
.
Status
)
if
existing
,
ok
:=
proxyByKey
[
key
];
ok
{
result
.
ProxyReused
++
if
item
.
Status
!=
""
&&
item
.
Status
!=
existing
.
Status
{
if
_
,
err
:=
h
.
adminService
.
UpdateProxy
(
ctx
,
existing
.
ID
,
&
service
.
UpdateProxyInput
{
Status
:
item
.
Status
});
err
!=
nil
{
if
normalized
Status
!=
""
&&
normalized
Status
!=
existing
.
Status
{
if
_
,
err
:=
h
.
adminService
.
UpdateProxy
(
ctx
,
existing
.
ID
,
&
service
.
UpdateProxyInput
{
Status
:
normalized
Status
});
err
!=
nil
{
result
.
Errors
=
append
(
result
.
Errors
,
DataImportError
{
Kind
:
"proxy"
,
Name
:
item
.
Name
,
...
...
@@ -160,8 +159,8 @@ func (h *ProxyHandler) ImportData(c *gin.Context) {
result
.
ProxyCreated
++
proxyByKey
[
key
]
=
*
created
if
item
.
Status
!=
""
&&
item
.
Status
!=
created
.
Status
{
if
_
,
err
:=
h
.
adminService
.
UpdateProxy
(
ctx
,
created
.
ID
,
&
service
.
UpdateProxyInput
{
Status
:
item
.
Status
});
err
!=
nil
{
if
normalized
Status
!=
""
&&
normalized
Status
!=
created
.
Status
{
if
_
,
err
:=
h
.
adminService
.
UpdateProxy
(
ctx
,
created
.
ID
,
&
service
.
UpdateProxyInput
{
Status
:
normalized
Status
});
err
!=
nil
{
result
.
Errors
=
append
(
result
.
Errors
,
DataImportError
{
Kind
:
"proxy"
,
Name
:
item
.
Name
,
...
...
@@ -170,7 +169,7 @@ func (h *ProxyHandler) ImportData(c *gin.Context) {
})
}
}
latencyProbeIDs
=
append
(
latency
P
robe
IDs
,
created
.
ID
)
// CreateProxy already triggers a
latency
p
robe
, avoid double probing here.
}
if
len
(
latencyProbeIDs
)
>
0
{
...
...
@@ -186,18 +185,10 @@ func (h *ProxyHandler) ImportData(c *gin.Context) {
}
func
(
h
*
ProxyHandler
)
getProxiesByIDs
(
ctx
context
.
Context
,
ids
[]
int64
)
([]
service
.
Proxy
,
error
)
{
out
:=
make
([]
service
.
Proxy
,
0
,
len
(
ids
))
for
_
,
id
:=
range
ids
{
proxy
,
err
:=
h
.
adminService
.
GetProxy
(
ctx
,
id
)
if
err
!=
nil
{
return
nil
,
err
if
len
(
ids
)
==
0
{
return
[]
service
.
Proxy
{},
nil
}
if
proxy
==
nil
{
continue
}
out
=
append
(
out
,
*
proxy
)
}
return
out
,
nil
return
h
.
adminService
.
GetProxiesByIDs
(
ctx
,
ids
)
}
func
parseProxyIDs
(
c
*
gin
.
Context
)
([]
int64
,
error
)
{
...
...
backend/internal/handler/admin/proxy_data_handler_test.go
View file @
37047919
...
...
@@ -69,7 +69,8 @@ func TestProxyExportDataRespectsFilters(t *testing.T) {
var
resp
proxyDataResponse
require
.
NoError
(
t
,
json
.
Unmarshal
(
rec
.
Body
.
Bytes
(),
&
resp
))
require
.
Equal
(
t
,
0
,
resp
.
Code
)
require
.
Equal
(
t
,
dataType
,
resp
.
Data
.
Type
)
require
.
Empty
(
t
,
resp
.
Data
.
Type
)
require
.
Equal
(
t
,
0
,
resp
.
Data
.
Version
)
require
.
Len
(
t
,
resp
.
Data
.
Proxies
,
1
)
require
.
Len
(
t
,
resp
.
Data
.
Accounts
,
0
)
require
.
Equal
(
t
,
"https"
,
resp
.
Data
.
Proxies
[
0
]
.
Protocol
)
...
...
@@ -156,6 +157,7 @@ func TestProxyImportDataReusesAndTriggersLatencyProbe(t *testing.T) {
"status"
:
"active"
,
},
},
"accounts"
:
[]
map
[
string
]
any
{},
},
}
...
...
@@ -181,6 +183,6 @@ func TestProxyImportDataReusesAndTriggersLatencyProbe(t *testing.T) {
require
.
Eventually
(
t
,
func
()
bool
{
adminSvc
.
mu
.
Lock
()
defer
adminSvc
.
mu
.
Unlock
()
return
len
(
adminSvc
.
testedProxyIDs
)
==
2
return
len
(
adminSvc
.
testedProxyIDs
)
==
1
},
time
.
Second
,
10
*
time
.
Millisecond
)
}
frontend/src/types/index.ts
View file @
37047919
...
...
@@ -728,8 +728,8 @@ export interface UpdateProxyRequest {
}
export
interface
AdminDataPayload
{
type
:
string
version
:
number
type
?
:
string
version
?
:
number
exported_at
:
string
proxies
:
AdminDataProxy
[]
accounts
:
AdminDataAccount
[]
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment