mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-05 20:54:14 +00:00
refactor(clients): finish migrating to ClientService + tidy IP routes
Two related cleanups in the new /clients surface: 1. Move ResetAllClientTraffics (bulk-reset of xray_client_traffic + last_traffic_reset_time, with node-runtime propagation) from InboundService to ClientService. PeriodicTrafficResetJob now holds a clientService and calls j.clientService.ResetAllClientTraffics(&j.inboundService, id). The last client-mutation method on InboundService is gone. 2. Shorten redundantly-named routes/handlers under /panel/api/clients: - /clientIps/:email -> /ips/:email (handler getIps) - /clearClientIps/:email -> /clearIps/:email (handler clearIps) The "client" prefix was redundant inside the clients namespace. Frontend (InboundInfoModal) and api-docs updated to match. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
d4ddf702de
commit
a79cb9fe6d
6 changed files with 75 additions and 76 deletions
|
|
@ -473,7 +473,7 @@ export const sections = [
|
|||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/panel/api/clients/clientIps/:email',
|
||||
path: '/panel/api/clients/ips/:email',
|
||||
summary: 'List source IPs that have connected with the given client’s credentials. Returns an array of "ip (timestamp)" strings.',
|
||||
params: [
|
||||
{ name: 'email', in: 'path', type: 'string', desc: 'Client email.' },
|
||||
|
|
@ -481,7 +481,7 @@ export const sections = [
|
|||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/panel/api/clients/clearClientIps/:email',
|
||||
path: '/panel/api/clients/clearIps/:email',
|
||||
summary: 'Reset the recorded IP list for a client.',
|
||||
params: [
|
||||
{ name: 'email', in: 'path', type: 'string', desc: 'Client email.' },
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@ async function loadClientIps() {
|
|||
if (!clientStats.value?.email) return;
|
||||
refreshing.value = true;
|
||||
try {
|
||||
const msg = await HttpUtil.post(`/panel/api/clients/clientIps/${clientStats.value.email}`);
|
||||
const msg = await HttpUtil.post(`/panel/api/clients/ips/${clientStats.value.email}`);
|
||||
if (!msg?.success) {
|
||||
clientIpsText.value = msg?.obj || 'No IP record';
|
||||
clientIpsArray.value = [];
|
||||
|
|
@ -164,7 +164,7 @@ async function loadClientIps() {
|
|||
|
||||
async function clearClientIps() {
|
||||
if (!clientStats.value?.email) return;
|
||||
const msg = await HttpUtil.post(`/panel/api/clients/clearClientIps/${clientStats.value.email}`);
|
||||
const msg = await HttpUtil.post(`/panel/api/clients/clearIps/${clientStats.value.email}`);
|
||||
if (msg?.success) {
|
||||
clientIpsArray.value = [];
|
||||
clientIpsText.value = t('tgbot.noIpRecord');
|
||||
|
|
|
|||
|
|
@ -36,8 +36,8 @@ func (a *ClientController) initRouter(g *gin.RouterGroup) {
|
|||
g.POST("/delDepleted", a.delDepleted)
|
||||
g.POST("/resetTraffic/:email", a.resetTrafficByEmail)
|
||||
g.POST("/updateTraffic/:email", a.updateTrafficByEmail)
|
||||
g.POST("/clientIps/:email", a.getClientIps)
|
||||
g.POST("/clearClientIps/:email", a.clearClientIps)
|
||||
g.POST("/ips/:email", a.getIps)
|
||||
g.POST("/clearIps/:email", a.clearIps)
|
||||
g.POST("/onlines", a.onlines)
|
||||
g.POST("/lastOnline", a.lastOnline)
|
||||
g.GET("/traffic/:email", a.getTrafficByEmail)
|
||||
|
|
@ -213,7 +213,7 @@ func (a *ClientController) updateTrafficByEmail(c *gin.Context) {
|
|||
jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.inboundClientUpdateSuccess"), nil)
|
||||
}
|
||||
|
||||
func (a *ClientController) getClientIps(c *gin.Context) {
|
||||
func (a *ClientController) getIps(c *gin.Context) {
|
||||
email := c.Param("email")
|
||||
ips, err := a.inboundService.GetInboundClientIps(email)
|
||||
if err != nil || ips == "" {
|
||||
|
|
@ -249,7 +249,7 @@ func (a *ClientController) getClientIps(c *gin.Context) {
|
|||
jsonObj(c, ips, nil)
|
||||
}
|
||||
|
||||
func (a *ClientController) clearClientIps(c *gin.Context) {
|
||||
func (a *ClientController) clearIps(c *gin.Context) {
|
||||
email := c.Param("email")
|
||||
if err := a.inboundService.ClearClientIps(email); err != nil {
|
||||
jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.updateSuccess"), err)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ type Period string
|
|||
// PeriodicTrafficResetJob resets traffic statistics for inbounds based on their configured reset period.
|
||||
type PeriodicTrafficResetJob struct {
|
||||
inboundService service.InboundService
|
||||
clientService service.ClientService
|
||||
period Period
|
||||
}
|
||||
|
||||
|
|
@ -42,7 +43,7 @@ func (j *PeriodicTrafficResetJob) Run() {
|
|||
logger.Warning("Failed to reset traffic for inbound", inbound.Id, ":", resetInboundErr)
|
||||
}
|
||||
|
||||
resetClientErr := j.inboundService.ResetAllClientTraffics(inbound.Id)
|
||||
resetClientErr := j.clientService.ResetAllClientTraffics(&j.inboundService, inbound.Id)
|
||||
if resetClientErr != nil {
|
||||
logger.Warning("Failed to reset traffic for all users of inbound", inbound.Id, ":", resetClientErr)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -569,6 +569,71 @@ func (s *ClientService) DelDepleted(inboundSvc *InboundService) (int, bool, erro
|
|||
return deleted, needRestart, nil
|
||||
}
|
||||
|
||||
func (s *ClientService) ResetAllClientTraffics(inboundSvc *InboundService, id int) error {
|
||||
return submitTrafficWrite(func() error {
|
||||
return s.resetAllClientTrafficsLocked(inboundSvc, id)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *ClientService) resetAllClientTrafficsLocked(inboundSvc *InboundService, id int) error {
|
||||
db := database.GetDB()
|
||||
now := time.Now().Unix() * 1000
|
||||
|
||||
if err := db.Transaction(func(tx *gorm.DB) error {
|
||||
whereText := "inbound_id "
|
||||
if id == -1 {
|
||||
whereText += " > ?"
|
||||
} else {
|
||||
whereText += " = ?"
|
||||
}
|
||||
|
||||
result := tx.Model(xray.ClientTraffic{}).
|
||||
Where(whereText, id).
|
||||
Updates(map[string]any{"enable": true, "up": 0, "down": 0})
|
||||
|
||||
if result.Error != nil {
|
||||
return result.Error
|
||||
}
|
||||
|
||||
inboundWhereText := "id "
|
||||
if id == -1 {
|
||||
inboundWhereText += " > ?"
|
||||
} else {
|
||||
inboundWhereText += " = ?"
|
||||
}
|
||||
|
||||
result = tx.Model(model.Inbound{}).
|
||||
Where(inboundWhereText, id).
|
||||
Update("last_traffic_reset_time", now)
|
||||
|
||||
return result.Error
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var inbounds []model.Inbound
|
||||
q := db.Model(model.Inbound{}).Where("node_id IS NOT NULL")
|
||||
if id != -1 {
|
||||
q = q.Where("id = ?", id)
|
||||
}
|
||||
if err := q.Find(&inbounds).Error; err != nil {
|
||||
logger.Warning("ResetAllClientTraffics: discover node inbounds failed:", err)
|
||||
return nil
|
||||
}
|
||||
for i := range inbounds {
|
||||
ib := &inbounds[i]
|
||||
rt, rterr := inboundSvc.runtimeFor(ib)
|
||||
if rterr != nil {
|
||||
logger.Warning("ResetAllClientTraffics: runtime lookup for inbound", ib.Id, "failed:", rterr)
|
||||
continue
|
||||
}
|
||||
if e := rt.ResetInboundClientTraffics(context.Background(), ib); e != nil {
|
||||
logger.Warning("ResetAllClientTraffics: remote propagation to", rt.Name(), "failed:", e)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ClientService) ResetAllTraffics() (bool, error) {
|
||||
res := database.GetDB().Model(&xray.ClientTraffic{}).
|
||||
Where("1 = 1").
|
||||
|
|
|
|||
|
|
@ -2101,73 +2101,6 @@ func (s *InboundService) resetClientTrafficLocked(id int, clientEmail string) (b
|
|||
return needRestart, nil
|
||||
}
|
||||
|
||||
func (s *InboundService) ResetAllClientTraffics(id int) error {
|
||||
return submitTrafficWrite(func() error {
|
||||
return s.resetAllClientTrafficsLocked(id)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *InboundService) resetAllClientTrafficsLocked(id int) error {
|
||||
db := database.GetDB()
|
||||
now := time.Now().Unix() * 1000
|
||||
|
||||
if err := db.Transaction(func(tx *gorm.DB) error {
|
||||
whereText := "inbound_id "
|
||||
if id == -1 {
|
||||
whereText += " > ?"
|
||||
} else {
|
||||
whereText += " = ?"
|
||||
}
|
||||
|
||||
// Reset client traffics
|
||||
result := tx.Model(xray.ClientTraffic{}).
|
||||
Where(whereText, id).
|
||||
Updates(map[string]any{"enable": true, "up": 0, "down": 0})
|
||||
|
||||
if result.Error != nil {
|
||||
return result.Error
|
||||
}
|
||||
|
||||
// Update lastTrafficResetTime for the inbound(s)
|
||||
inboundWhereText := "id "
|
||||
if id == -1 {
|
||||
inboundWhereText += " > ?"
|
||||
} else {
|
||||
inboundWhereText += " = ?"
|
||||
}
|
||||
|
||||
result = tx.Model(model.Inbound{}).
|
||||
Where(inboundWhereText, id).
|
||||
Update("last_traffic_reset_time", now)
|
||||
|
||||
return result.Error
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var inbounds []model.Inbound
|
||||
q := db.Model(model.Inbound{}).Where("node_id IS NOT NULL")
|
||||
if id != -1 {
|
||||
q = q.Where("id = ?", id)
|
||||
}
|
||||
if err := q.Find(&inbounds).Error; err != nil {
|
||||
logger.Warning("ResetAllClientTraffics: discover node inbounds failed:", err)
|
||||
return nil
|
||||
}
|
||||
for i := range inbounds {
|
||||
ib := &inbounds[i]
|
||||
rt, rterr := s.runtimeFor(ib)
|
||||
if rterr != nil {
|
||||
logger.Warning("ResetAllClientTraffics: runtime lookup for inbound", ib.Id, "failed:", rterr)
|
||||
continue
|
||||
}
|
||||
if e := rt.ResetInboundClientTraffics(context.Background(), ib); e != nil {
|
||||
logger.Warning("ResetAllClientTraffics: remote propagation to", rt.Name(), "failed:", e)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *InboundService) ResetAllTraffics() error {
|
||||
return submitTrafficWrite(func() error {
|
||||
return s.resetAllTrafficsLocked()
|
||||
|
|
|
|||
Loading…
Reference in a new issue