client.go 85 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292
  1. package service
  2. import (
  3. "context"
  4. "encoding/base64"
  5. "encoding/json"
  6. "errors"
  7. "fmt"
  8. "slices"
  9. "sort"
  10. "strconv"
  11. "strings"
  12. "sync"
  13. "time"
  14. "github.com/google/uuid"
  15. "github.com/mhsanaei/3x-ui/v3/database"
  16. "github.com/mhsanaei/3x-ui/v3/database/model"
  17. "github.com/mhsanaei/3x-ui/v3/logger"
  18. "github.com/mhsanaei/3x-ui/v3/util/common"
  19. "github.com/mhsanaei/3x-ui/v3/util/random"
  20. "github.com/mhsanaei/3x-ui/v3/xray"
  21. "gorm.io/gorm"
  22. )
  23. type ClientWithAttachments struct {
  24. model.ClientRecord
  25. InboundIds []int `json:"inboundIds"`
  26. Traffic *xray.ClientTraffic `json:"traffic,omitempty"`
  27. }
  28. // MarshalJSON is required because model.ClientRecord defines its own
  29. // MarshalJSON. Go promotes the embedded method to the outer struct, so without
  30. // this the encoder would call ClientRecord.MarshalJSON for the whole value and
  31. // silently drop InboundIds and Traffic from the API response.
  32. func (c ClientWithAttachments) MarshalJSON() ([]byte, error) {
  33. rec, err := json.Marshal(c.ClientRecord)
  34. if err != nil {
  35. return nil, err
  36. }
  37. extras := struct {
  38. InboundIds []int `json:"inboundIds"`
  39. Traffic *xray.ClientTraffic `json:"traffic,omitempty"`
  40. }{InboundIds: c.InboundIds, Traffic: c.Traffic}
  41. extra, err := json.Marshal(extras)
  42. if err != nil {
  43. return nil, err
  44. }
  45. if len(rec) < 2 || rec[len(rec)-1] != '}' || len(extra) <= 2 {
  46. return rec, nil
  47. }
  48. const maxMarshalSize = 256 << 20
  49. if len(rec) > maxMarshalSize || len(extra) > maxMarshalSize {
  50. return rec, nil
  51. }
  52. out := make([]byte, 0, len(rec)+len(extra))
  53. out = append(out, rec[:len(rec)-1]...)
  54. if len(rec) > 2 {
  55. out = append(out, ',')
  56. }
  57. out = append(out, extra[1:]...)
  58. return out, nil
  59. }
  60. func clientKeyForProtocol(p model.Protocol, rec *model.ClientRecord) string {
  61. if rec == nil {
  62. return ""
  63. }
  64. switch p {
  65. case model.Trojan:
  66. return rec.Password
  67. case model.Shadowsocks:
  68. return rec.Email
  69. case model.Hysteria:
  70. return rec.Auth
  71. default:
  72. return rec.UUID
  73. }
  74. }
  75. type ClientService struct{}
  76. // Short-lived tombstone of just-deleted client emails so that a node snapshot
  77. // arriving between delete and node-side processing doesn't resurrect them.
  78. var (
  79. recentlyDeletedMu sync.Mutex
  80. recentlyDeleted = map[string]time.Time{}
  81. )
  82. const deleteTombstoneTTL = 90 * time.Second
  83. var (
  84. inboundMutationLocksMu sync.Mutex
  85. inboundMutationLocks = map[int]*sync.Mutex{}
  86. )
  87. func lockInbound(inboundId int) *sync.Mutex {
  88. inboundMutationLocksMu.Lock()
  89. defer inboundMutationLocksMu.Unlock()
  90. m, ok := inboundMutationLocks[inboundId]
  91. if !ok {
  92. m = &sync.Mutex{}
  93. inboundMutationLocks[inboundId] = m
  94. }
  95. m.Lock()
  96. return m
  97. }
  98. func compactOrphans(db *gorm.DB, clients []any) []any {
  99. if len(clients) == 0 {
  100. return clients
  101. }
  102. emails := make([]string, 0, len(clients))
  103. for _, c := range clients {
  104. cm, ok := c.(map[string]any)
  105. if !ok {
  106. continue
  107. }
  108. if e, _ := cm["email"].(string); e != "" {
  109. emails = append(emails, e)
  110. }
  111. }
  112. if len(emails) == 0 {
  113. return clients
  114. }
  115. var existingEmails []string
  116. if err := db.Model(&model.ClientRecord{}).Where("email IN ?", emails).Pluck("email", &existingEmails).Error; err != nil {
  117. logger.Warning("compactOrphans pluck:", err)
  118. return clients
  119. }
  120. if len(existingEmails) == len(emails) {
  121. return clients
  122. }
  123. existing := make(map[string]struct{}, len(existingEmails))
  124. for _, e := range existingEmails {
  125. existing[e] = struct{}{}
  126. }
  127. out := make([]any, 0, len(existingEmails))
  128. for _, c := range clients {
  129. cm, ok := c.(map[string]any)
  130. if !ok {
  131. out = append(out, c)
  132. continue
  133. }
  134. e, _ := cm["email"].(string)
  135. if e == "" {
  136. out = append(out, c)
  137. continue
  138. }
  139. if _, ok := existing[e]; ok {
  140. out = append(out, c)
  141. }
  142. }
  143. return out
  144. }
  145. func tombstoneClientEmail(email string) {
  146. if email == "" {
  147. return
  148. }
  149. recentlyDeletedMu.Lock()
  150. defer recentlyDeletedMu.Unlock()
  151. recentlyDeleted[email] = time.Now()
  152. cutoff := time.Now().Add(-deleteTombstoneTTL)
  153. for e, ts := range recentlyDeleted {
  154. if ts.Before(cutoff) {
  155. delete(recentlyDeleted, e)
  156. }
  157. }
  158. }
  159. func isClientEmailTombstoned(email string) bool {
  160. if email == "" {
  161. return false
  162. }
  163. recentlyDeletedMu.Lock()
  164. defer recentlyDeletedMu.Unlock()
  165. ts, ok := recentlyDeleted[email]
  166. if !ok {
  167. return false
  168. }
  169. if time.Since(ts) > deleteTombstoneTTL {
  170. delete(recentlyDeleted, email)
  171. return false
  172. }
  173. return true
  174. }
  175. func (s *ClientService) SyncInbound(tx *gorm.DB, inboundId int, clients []model.Client) error {
  176. if tx == nil {
  177. tx = database.GetDB()
  178. }
  179. if err := tx.Where("inbound_id = ?", inboundId).Delete(&model.ClientInbound{}).Error; err != nil {
  180. return err
  181. }
  182. for i := range clients {
  183. c := clients[i]
  184. email := strings.TrimSpace(c.Email)
  185. if email == "" {
  186. continue
  187. }
  188. incoming := c.ToRecord()
  189. row := &model.ClientRecord{}
  190. err := tx.Where("email = ?", email).First(row).Error
  191. if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
  192. return err
  193. }
  194. if errors.Is(err, gorm.ErrRecordNotFound) {
  195. if err := tx.Create(incoming).Error; err != nil {
  196. return err
  197. }
  198. row = incoming
  199. } else {
  200. if incoming.UUID != "" {
  201. row.UUID = incoming.UUID
  202. }
  203. if incoming.Password != "" {
  204. row.Password = incoming.Password
  205. }
  206. if incoming.Auth != "" {
  207. row.Auth = incoming.Auth
  208. }
  209. row.Flow = incoming.Flow
  210. if incoming.Security != "" {
  211. row.Security = incoming.Security
  212. }
  213. if incoming.Reverse != "" {
  214. row.Reverse = incoming.Reverse
  215. }
  216. row.SubID = incoming.SubID
  217. row.LimitIP = incoming.LimitIP
  218. row.TotalGB = incoming.TotalGB
  219. row.ExpiryTime = incoming.ExpiryTime
  220. row.Enable = incoming.Enable
  221. row.TgID = incoming.TgID
  222. row.Comment = incoming.Comment
  223. row.Reset = incoming.Reset
  224. if incoming.CreatedAt > 0 && (row.CreatedAt == 0 || incoming.CreatedAt < row.CreatedAt) {
  225. row.CreatedAt = incoming.CreatedAt
  226. }
  227. preservedUpdatedAt := row.UpdatedAt
  228. if incoming.UpdatedAt > preservedUpdatedAt {
  229. preservedUpdatedAt = incoming.UpdatedAt
  230. }
  231. row.UpdatedAt = preservedUpdatedAt
  232. if err := tx.Save(row).Error; err != nil {
  233. return err
  234. }
  235. if err := tx.Model(&model.ClientRecord{}).
  236. Where("id = ?", row.Id).
  237. UpdateColumn("updated_at", preservedUpdatedAt).Error; err != nil {
  238. return err
  239. }
  240. }
  241. link := model.ClientInbound{
  242. ClientId: row.Id,
  243. InboundId: inboundId,
  244. FlowOverride: c.Flow,
  245. }
  246. if err := tx.Create(&link).Error; err != nil {
  247. return err
  248. }
  249. }
  250. return nil
  251. }
  252. func (s *ClientService) DetachInbound(tx *gorm.DB, inboundId int) error {
  253. if tx == nil {
  254. tx = database.GetDB()
  255. }
  256. return tx.Where("inbound_id = ?", inboundId).Delete(&model.ClientInbound{}).Error
  257. }
  258. func (s *ClientService) ListForInbound(tx *gorm.DB, inboundId int) ([]model.Client, error) {
  259. if tx == nil {
  260. tx = database.GetDB()
  261. }
  262. type joinedRow struct {
  263. model.ClientRecord
  264. FlowOverride string
  265. }
  266. var rows []joinedRow
  267. err := tx.Table("clients").
  268. Select("clients.*, client_inbounds.flow_override AS flow_override").
  269. Joins("JOIN client_inbounds ON client_inbounds.client_id = clients.id").
  270. Where("client_inbounds.inbound_id = ?", inboundId).
  271. Order("clients.id ASC").
  272. Find(&rows).Error
  273. if err != nil {
  274. return nil, err
  275. }
  276. out := make([]model.Client, 0, len(rows))
  277. for i := range rows {
  278. c := rows[i].ToClient()
  279. if rows[i].FlowOverride != "" {
  280. c.Flow = rows[i].FlowOverride
  281. }
  282. out = append(out, *c)
  283. }
  284. return out, nil
  285. }
  286. func (s *ClientService) GetRecordByEmail(tx *gorm.DB, email string) (*model.ClientRecord, error) {
  287. if tx == nil {
  288. tx = database.GetDB()
  289. }
  290. row := &model.ClientRecord{}
  291. err := tx.Where("email = ?", email).First(row).Error
  292. if err != nil {
  293. return nil, err
  294. }
  295. return row, nil
  296. }
  297. func (s *ClientService) GetInboundIdsForEmail(tx *gorm.DB, email string) ([]int, error) {
  298. if tx == nil {
  299. tx = database.GetDB()
  300. }
  301. var ids []int
  302. err := tx.Table("client_inbounds").
  303. Select("client_inbounds.inbound_id").
  304. Joins("JOIN clients ON clients.id = client_inbounds.client_id").
  305. Where("clients.email = ?", email).
  306. Scan(&ids).Error
  307. if err != nil {
  308. return nil, err
  309. }
  310. return ids, nil
  311. }
  312. func (s *ClientService) GetByID(id int) (*model.ClientRecord, error) {
  313. row := &model.ClientRecord{}
  314. if err := database.GetDB().Where("id = ?", id).First(row).Error; err != nil {
  315. return nil, err
  316. }
  317. return row, nil
  318. }
  319. func (s *ClientService) GetInboundIdsForRecord(id int) ([]int, error) {
  320. var ids []int
  321. err := database.GetDB().Table("client_inbounds").
  322. Where("client_id = ?", id).
  323. Order("inbound_id ASC").
  324. Pluck("inbound_id", &ids).Error
  325. if err != nil {
  326. return nil, err
  327. }
  328. return ids, nil
  329. }
  330. func (s *ClientService) List() ([]ClientWithAttachments, error) {
  331. db := database.GetDB()
  332. var rows []model.ClientRecord
  333. if err := db.Order("id ASC").Find(&rows).Error; err != nil {
  334. return nil, err
  335. }
  336. if len(rows) == 0 {
  337. return []ClientWithAttachments{}, nil
  338. }
  339. clientIds := make([]int, 0, len(rows))
  340. emails := make([]string, 0, len(rows))
  341. for i := range rows {
  342. clientIds = append(clientIds, rows[i].Id)
  343. if rows[i].Email != "" {
  344. emails = append(emails, rows[i].Email)
  345. }
  346. }
  347. var links []model.ClientInbound
  348. if err := db.Where("client_id IN ?", clientIds).Find(&links).Error; err != nil {
  349. return nil, err
  350. }
  351. attachments := make(map[int][]int, len(rows))
  352. for _, l := range links {
  353. attachments[l.ClientId] = append(attachments[l.ClientId], l.InboundId)
  354. }
  355. trafficByEmail := make(map[string]*xray.ClientTraffic, len(emails))
  356. if len(emails) > 0 {
  357. var stats []xray.ClientTraffic
  358. if err := db.Where("email IN ?", emails).Find(&stats).Error; err != nil {
  359. return nil, err
  360. }
  361. for i := range stats {
  362. trafficByEmail[stats[i].Email] = &stats[i]
  363. }
  364. }
  365. out := make([]ClientWithAttachments, 0, len(rows))
  366. for i := range rows {
  367. out = append(out, ClientWithAttachments{
  368. ClientRecord: rows[i],
  369. InboundIds: attachments[rows[i].Id],
  370. Traffic: trafficByEmail[rows[i].Email],
  371. })
  372. }
  373. return out, nil
  374. }
  375. type ClientCreatePayload struct {
  376. Client model.Client `json:"client"`
  377. InboundIds []int `json:"inboundIds"`
  378. }
  379. func (s *ClientService) Create(inboundSvc *InboundService, payload *ClientCreatePayload) (bool, error) {
  380. if payload == nil {
  381. return false, common.NewError("empty payload")
  382. }
  383. client := payload.Client
  384. if strings.TrimSpace(client.Email) == "" {
  385. return false, common.NewError("client email is required")
  386. }
  387. if len(payload.InboundIds) == 0 {
  388. return false, common.NewError("at least one inbound is required")
  389. }
  390. if client.SubID == "" {
  391. client.SubID = uuid.NewString()
  392. }
  393. if !client.Enable {
  394. client.Enable = true
  395. }
  396. now := time.Now().UnixMilli()
  397. if client.CreatedAt == 0 {
  398. client.CreatedAt = now
  399. }
  400. client.UpdatedAt = now
  401. existing := &model.ClientRecord{}
  402. err := database.GetDB().Where("email = ?", client.Email).First(existing).Error
  403. if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
  404. return false, err
  405. }
  406. emailTaken := !errors.Is(err, gorm.ErrRecordNotFound)
  407. if emailTaken {
  408. if existing.SubID == "" || existing.SubID != client.SubID {
  409. return false, common.NewError("email already in use:", client.Email)
  410. }
  411. }
  412. needRestart := false
  413. for _, ibId := range payload.InboundIds {
  414. inbound, getErr := inboundSvc.GetInbound(ibId)
  415. if getErr != nil {
  416. return needRestart, getErr
  417. }
  418. if err := s.fillProtocolDefaults(&client, inbound); err != nil {
  419. return needRestart, err
  420. }
  421. settingsPayload, mErr := json.Marshal(map[string][]model.Client{"clients": {client}})
  422. if mErr != nil {
  423. return needRestart, mErr
  424. }
  425. nr, addErr := s.AddInboundClient(inboundSvc, &model.Inbound{
  426. Id: ibId,
  427. Settings: string(settingsPayload),
  428. })
  429. if addErr != nil {
  430. return needRestart, addErr
  431. }
  432. if nr {
  433. needRestart = true
  434. }
  435. }
  436. return needRestart, nil
  437. }
  438. func (s *ClientService) fillProtocolDefaults(c *model.Client, ib *model.Inbound) error {
  439. switch ib.Protocol {
  440. case model.VMESS, model.VLESS:
  441. if c.ID == "" {
  442. c.ID = uuid.NewString()
  443. }
  444. case model.Trojan:
  445. if c.Password == "" {
  446. c.Password = strings.ReplaceAll(uuid.NewString(), "-", "")
  447. }
  448. case model.Shadowsocks:
  449. method := shadowsocksMethodFromSettings(ib.Settings)
  450. if c.Password == "" || !validShadowsocksClientKey(method, c.Password) {
  451. c.Password = randomShadowsocksClientKey(method)
  452. }
  453. case model.Hysteria:
  454. if c.Auth == "" {
  455. c.Auth = strings.ReplaceAll(uuid.NewString(), "-", "")
  456. }
  457. }
  458. return nil
  459. }
  460. // shadowsocksMethodFromSettings pulls the "method" field out of the inbound's
  461. // settings JSON. Returns "" when the field is missing or settings is invalid.
  462. func shadowsocksMethodFromSettings(settings string) string {
  463. if settings == "" {
  464. return ""
  465. }
  466. var m map[string]any
  467. if err := json.Unmarshal([]byte(settings), &m); err != nil {
  468. return ""
  469. }
  470. method, _ := m["method"].(string)
  471. return method
  472. }
  473. // randomShadowsocksClientKey returns a per-client key sized to the cipher.
  474. // The 2022-blake3 ciphers require a base64-encoded key of an exact byte
  475. // length (16 bytes for aes-128-gcm, 32 bytes for aes-256-gcm and
  476. // chacha20-poly1305) — anything else fails with "bad key" on xray start.
  477. // Older ciphers accept arbitrary passwords, so we keep the uuid-style.
  478. func randomShadowsocksClientKey(method string) string {
  479. if n := shadowsocksKeyBytes(method); n > 0 {
  480. return random.Base64Bytes(n)
  481. }
  482. return strings.ReplaceAll(uuid.NewString(), "-", "")
  483. }
  484. // validShadowsocksClientKey reports whether key is acceptable for the cipher.
  485. // For 2022-blake3 it must decode to the exact byte length the cipher needs;
  486. // any other method accepts any non-empty string.
  487. func validShadowsocksClientKey(method, key string) bool {
  488. n := shadowsocksKeyBytes(method)
  489. if n == 0 {
  490. return key != ""
  491. }
  492. decoded, err := base64.StdEncoding.DecodeString(key)
  493. if err != nil {
  494. return false
  495. }
  496. return len(decoded) == n
  497. }
  498. func shadowsocksKeyBytes(method string) int {
  499. switch method {
  500. case "2022-blake3-aes-128-gcm":
  501. return 16
  502. case "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305":
  503. return 32
  504. }
  505. return 0
  506. }
  507. // applyShadowsocksClientMethod normalises the per-client "method" field
  508. // when an inbound is created or updated:
  509. // - Legacy ciphers: backfill `method` so xray's multi-user code is happy.
  510. // "unsupported cipher method:" otherwise.
  511. // - 2022-blake3-*: strip the per-client `method` because xray rejects
  512. // it with "users must have empty method". This matters after an admin
  513. // switches an existing inbound from a legacy cipher to a 2022 one.
  514. func applyShadowsocksClientMethod(clients []any, settings map[string]any) {
  515. method, _ := settings["method"].(string)
  516. is2022 := strings.HasPrefix(method, "2022-blake3-")
  517. for i := range clients {
  518. cm, ok := clients[i].(map[string]any)
  519. if !ok {
  520. continue
  521. }
  522. if is2022 {
  523. if _, hasKey := cm["method"]; hasKey {
  524. delete(cm, "method")
  525. clients[i] = cm
  526. }
  527. continue
  528. }
  529. if method == "" {
  530. continue
  531. }
  532. if existing, _ := cm["method"].(string); existing != "" {
  533. continue
  534. }
  535. cm["method"] = method
  536. clients[i] = cm
  537. }
  538. }
  539. func (s *ClientService) Update(inboundSvc *InboundService, id int, updated model.Client) (bool, error) {
  540. existing, err := s.GetByID(id)
  541. if err != nil {
  542. return false, err
  543. }
  544. inboundIds, err := s.GetInboundIdsForRecord(id)
  545. if err != nil {
  546. return false, err
  547. }
  548. if strings.TrimSpace(updated.Email) == "" {
  549. return false, common.NewError("client email is required")
  550. }
  551. if updated.SubID == "" {
  552. updated.SubID = existing.SubID
  553. }
  554. if updated.SubID == "" {
  555. updated.SubID = uuid.NewString()
  556. }
  557. updated.UpdatedAt = time.Now().UnixMilli()
  558. if updated.CreatedAt == 0 {
  559. updated.CreatedAt = existing.CreatedAt
  560. }
  561. // Rename the ClientRecord row up front when the email changes. SyncInbound
  562. // (invoked from UpdateInboundClient below) looks up by email — without
  563. // renaming first it would treat the new email as a brand-new client,
  564. // insert a duplicate ClientRecord, and leave the original orphaned.
  565. if updated.Email != existing.Email {
  566. var collisionCount int64
  567. if err := database.GetDB().Model(&model.ClientRecord{}).
  568. Where("email = ? AND id <> ?", updated.Email, id).
  569. Count(&collisionCount).Error; err != nil {
  570. return false, err
  571. }
  572. if collisionCount > 0 {
  573. return false, common.NewError("Duplicate email:", updated.Email)
  574. }
  575. if err := database.GetDB().Model(&model.ClientRecord{}).
  576. Where("id = ?", id).
  577. Update("email", updated.Email).Error; err != nil {
  578. return false, err
  579. }
  580. }
  581. needRestart := false
  582. for _, ibId := range inboundIds {
  583. inbound, getErr := inboundSvc.GetInbound(ibId)
  584. if getErr != nil {
  585. return needRestart, getErr
  586. }
  587. oldKey := clientKeyForProtocol(inbound.Protocol, existing)
  588. if oldKey == "" {
  589. continue
  590. }
  591. if err := s.fillProtocolDefaults(&updated, inbound); err != nil {
  592. return needRestart, err
  593. }
  594. settingsPayload, mErr := json.Marshal(map[string][]model.Client{"clients": {updated}})
  595. if mErr != nil {
  596. return needRestart, mErr
  597. }
  598. nr, upErr := s.UpdateInboundClient(inboundSvc, &model.Inbound{
  599. Id: ibId,
  600. Settings: string(settingsPayload),
  601. }, oldKey)
  602. if upErr != nil {
  603. return needRestart, upErr
  604. }
  605. if nr {
  606. needRestart = true
  607. }
  608. }
  609. if err := database.GetDB().Model(&model.ClientRecord{}).
  610. Where("id = ?", id).
  611. UpdateColumn("updated_at", time.Now().UnixMilli()).Error; err != nil {
  612. return needRestart, err
  613. }
  614. return needRestart, nil
  615. }
  616. func (s *ClientService) Delete(inboundSvc *InboundService, id int, keepTraffic bool) (bool, error) {
  617. existing, err := s.GetByID(id)
  618. if err != nil {
  619. return false, err
  620. }
  621. tombstoneClientEmail(existing.Email)
  622. inboundIds, err := s.GetInboundIdsForRecord(id)
  623. if err != nil {
  624. return false, err
  625. }
  626. needRestart := false
  627. for _, ibId := range inboundIds {
  628. inbound, getErr := inboundSvc.GetInbound(ibId)
  629. if getErr != nil {
  630. return needRestart, getErr
  631. }
  632. key := clientKeyForProtocol(inbound.Protocol, existing)
  633. if key == "" {
  634. continue
  635. }
  636. nr, delErr := s.DelInboundClient(inboundSvc, ibId, key)
  637. if delErr != nil {
  638. return needRestart, delErr
  639. }
  640. if nr {
  641. needRestart = true
  642. }
  643. }
  644. db := database.GetDB()
  645. if err := db.Where("client_id = ?", id).Delete(&model.ClientInbound{}).Error; err != nil {
  646. return needRestart, err
  647. }
  648. if !keepTraffic && existing.Email != "" {
  649. if err := db.Where("email = ?", existing.Email).Delete(&xray.ClientTraffic{}).Error; err != nil {
  650. return needRestart, err
  651. }
  652. if err := db.Where("client_email = ?", existing.Email).Delete(&model.InboundClientIps{}).Error; err != nil {
  653. return needRestart, err
  654. }
  655. }
  656. if err := db.Delete(&model.ClientRecord{}, id).Error; err != nil {
  657. return needRestart, err
  658. }
  659. return needRestart, nil
  660. }
  661. func (s *ClientService) Attach(inboundSvc *InboundService, id int, inboundIds []int) (bool, error) {
  662. existing, err := s.GetByID(id)
  663. if err != nil {
  664. return false, err
  665. }
  666. currentIds, err := s.GetInboundIdsForRecord(id)
  667. if err != nil {
  668. return false, err
  669. }
  670. have := make(map[int]struct{}, len(currentIds))
  671. for _, x := range currentIds {
  672. have[x] = struct{}{}
  673. }
  674. clientWire := existing.ToClient()
  675. clientWire.UpdatedAt = time.Now().UnixMilli()
  676. needRestart := false
  677. for _, ibId := range inboundIds {
  678. if _, attached := have[ibId]; attached {
  679. continue
  680. }
  681. inbound, getErr := inboundSvc.GetInbound(ibId)
  682. if getErr != nil {
  683. return needRestart, getErr
  684. }
  685. copyClient := *clientWire
  686. if err := s.fillProtocolDefaults(&copyClient, inbound); err != nil {
  687. return needRestart, err
  688. }
  689. settingsPayload, mErr := json.Marshal(map[string][]model.Client{"clients": {copyClient}})
  690. if mErr != nil {
  691. return needRestart, mErr
  692. }
  693. nr, addErr := s.AddInboundClient(inboundSvc, &model.Inbound{
  694. Id: ibId,
  695. Settings: string(settingsPayload),
  696. })
  697. if addErr != nil {
  698. return needRestart, addErr
  699. }
  700. if nr {
  701. needRestart = true
  702. }
  703. }
  704. return needRestart, nil
  705. }
  706. func (s *ClientService) CreateOne(inboundSvc *InboundService, inboundId int, client model.Client) (bool, error) {
  707. return s.Create(inboundSvc, &ClientCreatePayload{
  708. Client: client,
  709. InboundIds: []int{inboundId},
  710. })
  711. }
  712. func (s *ClientService) DetachByEmail(inboundSvc *InboundService, inboundId int, email string) (bool, error) {
  713. if email == "" {
  714. return false, common.NewError("client email is required")
  715. }
  716. rec, err := s.GetRecordByEmail(nil, email)
  717. if err != nil {
  718. return false, err
  719. }
  720. return s.Detach(inboundSvc, rec.Id, []int{inboundId})
  721. }
  722. func (s *ClientService) AttachByEmail(inboundSvc *InboundService, email string, inboundIds []int) (bool, error) {
  723. if email == "" {
  724. return false, common.NewError("client email is required")
  725. }
  726. rec, err := s.GetRecordByEmail(nil, email)
  727. if err != nil {
  728. return false, err
  729. }
  730. return s.Attach(inboundSvc, rec.Id, inboundIds)
  731. }
  732. func (s *ClientService) DetachByEmailMany(inboundSvc *InboundService, email string, inboundIds []int) (bool, error) {
  733. if email == "" {
  734. return false, common.NewError("client email is required")
  735. }
  736. rec, err := s.GetRecordByEmail(nil, email)
  737. if err != nil {
  738. return false, err
  739. }
  740. return s.Detach(inboundSvc, rec.Id, inboundIds)
  741. }
  742. func (s *ClientService) DeleteByEmail(inboundSvc *InboundService, email string, keepTraffic bool) (bool, error) {
  743. if email == "" {
  744. return false, common.NewError("client email is required")
  745. }
  746. rec, err := s.GetRecordByEmail(nil, email)
  747. if err != nil {
  748. return false, err
  749. }
  750. return s.Delete(inboundSvc, rec.Id, keepTraffic)
  751. }
  752. func (s *ClientService) UpdateByEmail(inboundSvc *InboundService, email string, updated model.Client) (bool, error) {
  753. if email == "" {
  754. return false, common.NewError("client email is required")
  755. }
  756. rec, err := s.GetRecordByEmail(nil, email)
  757. if err != nil {
  758. return false, err
  759. }
  760. return s.Update(inboundSvc, rec.Id, updated)
  761. }
  762. func (s *ClientService) ResetTrafficByEmail(inboundSvc *InboundService, email string) (bool, error) {
  763. if email == "" {
  764. return false, common.NewError("client email is required")
  765. }
  766. rec, err := s.GetRecordByEmail(nil, email)
  767. if err != nil {
  768. return false, err
  769. }
  770. inboundIds, err := s.GetInboundIdsForRecord(rec.Id)
  771. if err != nil {
  772. return false, err
  773. }
  774. if len(inboundIds) == 0 {
  775. if rErr := inboundSvc.ResetClientTrafficByEmail(email); rErr != nil {
  776. return false, rErr
  777. }
  778. return false, nil
  779. }
  780. needRestart := false
  781. for _, ibId := range inboundIds {
  782. nr, rErr := inboundSvc.ResetClientTraffic(ibId, email)
  783. if rErr != nil {
  784. return needRestart, rErr
  785. }
  786. if nr {
  787. needRestart = true
  788. }
  789. }
  790. return needRestart, nil
  791. }
  792. // ClientSlim is the row-shape used by the clients page. It drops fields the
  793. // table never reads (UUID, password, auth, flow, security, reverse, tgId)
  794. // so the list payload stays compact even when the panel manages thousands
  795. // of clients. Modals that need the full record still call /get/:email.
  796. type ClientSlim struct {
  797. Email string `json:"email"`
  798. SubID string `json:"subId"`
  799. Enable bool `json:"enable"`
  800. TotalGB int64 `json:"totalGB"`
  801. ExpiryTime int64 `json:"expiryTime"`
  802. LimitIP int `json:"limitIp"`
  803. Reset int `json:"reset"`
  804. Comment string `json:"comment,omitempty"`
  805. InboundIds []int `json:"inboundIds"`
  806. Traffic *xray.ClientTraffic `json:"traffic,omitempty"`
  807. CreatedAt int64 `json:"createdAt"`
  808. UpdatedAt int64 `json:"updatedAt"`
  809. }
  810. // ClientPageParams are the query params accepted by /panel/api/clients/list/paged.
  811. // All fields are optional — the empty value means "no filter" / defaults.
  812. //
  813. // Filter / Protocol / Inbound accept either a single value or a comma-separated
  814. // list; matching is OR within a field and AND across fields. The numeric range
  815. // fields treat 0 as "unset" on the lower bound and 0 (or negative) as
  816. // "unbounded" on the upper bound.
  817. type ClientPageParams struct {
  818. Page int `form:"page"`
  819. PageSize int `form:"pageSize"`
  820. Search string `form:"search"`
  821. Filter string `form:"filter"`
  822. Protocol string `form:"protocol"`
  823. Inbound string `form:"inbound"`
  824. Sort string `form:"sort"`
  825. Order string `form:"order"`
  826. ExpiryFrom int64 `form:"expiryFrom"`
  827. ExpiryTo int64 `form:"expiryTo"`
  828. UsageFrom int64 `form:"usageFrom"`
  829. UsageTo int64 `form:"usageTo"`
  830. AutoRenew string `form:"autoRenew"`
  831. HasTgID string `form:"hasTgId"`
  832. HasComment string `form:"hasComment"`
  833. }
  834. // ClientPageResponse is the shape returned by ListPaged. `Total` is the
  835. // row count in the DB; `Filtered` is the count after Search/Filter/Protocol
  836. // were applied, before pagination. The page contains at most PageSize items.
  837. // Summary is computed across the full DB row set so dashboard counters
  838. // on the clients page stay stable as the user paginates/filters.
  839. type ClientPageResponse struct {
  840. Items []ClientSlim `json:"items"`
  841. Total int `json:"total"`
  842. Filtered int `json:"filtered"`
  843. Page int `json:"page"`
  844. PageSize int `json:"pageSize"`
  845. Summary ClientsSummary `json:"summary"`
  846. }
  847. // ClientsSummary collects per-bucket counts plus the matching email lists so
  848. // the clients page can render the dashboard stat cards and their hover
  849. // popovers without shipping the full client array.
  850. type ClientsSummary struct {
  851. Total int `json:"total"`
  852. Active int `json:"active"`
  853. Online []string `json:"online"`
  854. Depleted []string `json:"depleted"`
  855. Expiring []string `json:"expiring"`
  856. Deactive []string `json:"deactive"`
  857. }
  858. const (
  859. clientPageDefaultSize = 25
  860. clientPageMaxSize = 200
  861. )
  862. // ListPaged loads every client (with traffic + attachments) into memory,
  863. // applies the requested filter / search / protocol predicates, sorts, and
  864. // returns the requested page along with total and filtered counts. The DB
  865. // query itself is unchanged from List(); the win is that the response
  866. // only carries 25-ish slim rows over the wire instead of all 2000 full
  867. // records, which on real panels was the dominant cost.
  868. func (s *ClientService) ListPaged(inboundSvc *InboundService, settingSvc *SettingService, params ClientPageParams) (*ClientPageResponse, error) {
  869. all, err := s.List()
  870. if err != nil {
  871. return nil, err
  872. }
  873. total := len(all)
  874. pageSize := params.PageSize
  875. if pageSize <= 0 {
  876. pageSize = clientPageDefaultSize
  877. }
  878. if pageSize > clientPageMaxSize {
  879. pageSize = clientPageMaxSize
  880. }
  881. page := params.Page
  882. if page <= 0 {
  883. page = 1
  884. }
  885. protocols := parseCSVStrings(params.Protocol)
  886. inboundIDs := parseCSVInts(params.Inbound)
  887. buckets := parseCSVStrings(params.Filter)
  888. var protocolByInbound map[int]string
  889. if len(protocols) > 0 {
  890. inbounds, err := inboundSvc.GetAllInbounds()
  891. if err == nil {
  892. protocolByInbound = make(map[int]string, len(inbounds))
  893. for _, ib := range inbounds {
  894. protocolByInbound[ib.Id] = string(ib.Protocol)
  895. }
  896. }
  897. }
  898. onlines := inboundSvc.GetOnlineClients()
  899. onlineSet := make(map[string]struct{}, len(onlines))
  900. for _, e := range onlines {
  901. onlineSet[e] = struct{}{}
  902. }
  903. var expireDiffMs, trafficDiffBytes int64
  904. if settingSvc != nil {
  905. if v, err := settingSvc.GetExpireDiff(); err == nil {
  906. expireDiffMs = int64(v) * 86400000
  907. }
  908. if v, err := settingSvc.GetTrafficDiff(); err == nil {
  909. trafficDiffBytes = int64(v) * 1073741824
  910. }
  911. }
  912. nowMs := time.Now().UnixMilli()
  913. summary := buildClientsSummary(all, onlineSet, nowMs, expireDiffMs, trafficDiffBytes)
  914. needle := strings.ToLower(strings.TrimSpace(params.Search))
  915. filtered := make([]ClientWithAttachments, 0, len(all))
  916. for _, c := range all {
  917. if needle != "" && !clientMatchesSearch(c, needle) {
  918. continue
  919. }
  920. if len(protocols) > 0 && !clientMatchesAnyProtocol(c, protocols, protocolByInbound) {
  921. continue
  922. }
  923. if len(inboundIDs) > 0 && !clientMatchesAnyInbound(c, inboundIDs) {
  924. continue
  925. }
  926. if len(buckets) > 0 && !clientMatchesAnyBucket(c, buckets, onlineSet, nowMs, expireDiffMs, trafficDiffBytes) {
  927. continue
  928. }
  929. if !clientMatchesExpiryRange(c, params.ExpiryFrom, params.ExpiryTo) {
  930. continue
  931. }
  932. if !clientMatchesUsageRange(c, params.UsageFrom, params.UsageTo) {
  933. continue
  934. }
  935. if !clientMatchesAutoRenew(c, params.AutoRenew) {
  936. continue
  937. }
  938. if !clientMatchesHasTgID(c, params.HasTgID) {
  939. continue
  940. }
  941. if !clientMatchesHasComment(c, params.HasComment) {
  942. continue
  943. }
  944. filtered = append(filtered, c)
  945. }
  946. sortClients(filtered, params.Sort, params.Order)
  947. filteredCount := len(filtered)
  948. start := (page - 1) * pageSize
  949. end := start + pageSize
  950. if start > filteredCount {
  951. start = filteredCount
  952. }
  953. if end > filteredCount {
  954. end = filteredCount
  955. }
  956. pageRows := filtered[start:end]
  957. items := make([]ClientSlim, 0, len(pageRows))
  958. for _, c := range pageRows {
  959. items = append(items, toClientSlim(c))
  960. }
  961. return &ClientPageResponse{
  962. Items: items,
  963. Total: total,
  964. Filtered: filteredCount,
  965. Page: page,
  966. PageSize: pageSize,
  967. Summary: summary,
  968. }, nil
  969. }
  970. func buildClientsSummary(all []ClientWithAttachments, onlineSet map[string]struct{}, nowMs, expireDiffMs, trafficDiffBytes int64) ClientsSummary {
  971. s := ClientsSummary{
  972. Total: len(all),
  973. Online: []string{},
  974. Depleted: []string{},
  975. Expiring: []string{},
  976. Deactive: []string{},
  977. }
  978. for _, c := range all {
  979. used := int64(0)
  980. if c.Traffic != nil {
  981. used = c.Traffic.Up + c.Traffic.Down
  982. }
  983. exhausted := c.TotalGB > 0 && used >= c.TotalGB
  984. expired := c.ExpiryTime > 0 && c.ExpiryTime <= nowMs
  985. if c.Enable {
  986. if _, ok := onlineSet[c.Email]; ok {
  987. s.Online = append(s.Online, c.Email)
  988. }
  989. }
  990. if exhausted || expired {
  991. s.Depleted = append(s.Depleted, c.Email)
  992. continue
  993. }
  994. if !c.Enable {
  995. s.Deactive = append(s.Deactive, c.Email)
  996. continue
  997. }
  998. nearExpiry := c.ExpiryTime > 0 && c.ExpiryTime-nowMs < expireDiffMs
  999. nearLimit := c.TotalGB > 0 && c.TotalGB-used < trafficDiffBytes
  1000. if nearExpiry || nearLimit {
  1001. s.Expiring = append(s.Expiring, c.Email)
  1002. } else {
  1003. s.Active++
  1004. }
  1005. }
  1006. return s
  1007. }
  1008. func toClientSlim(c ClientWithAttachments) ClientSlim {
  1009. return ClientSlim{
  1010. Email: c.Email,
  1011. SubID: c.SubID,
  1012. Enable: c.Enable,
  1013. TotalGB: c.TotalGB,
  1014. ExpiryTime: c.ExpiryTime,
  1015. LimitIP: c.LimitIP,
  1016. Reset: c.Reset,
  1017. Comment: c.Comment,
  1018. InboundIds: c.InboundIds,
  1019. Traffic: c.Traffic,
  1020. CreatedAt: c.CreatedAt,
  1021. UpdatedAt: c.UpdatedAt,
  1022. }
  1023. }
  1024. func clientMatchesSearch(c ClientWithAttachments, needle string) bool {
  1025. if needle == "" {
  1026. return true
  1027. }
  1028. candidates := [...]string{c.Email, c.SubID, c.Comment, c.UUID, c.Password, c.Auth}
  1029. for _, v := range candidates {
  1030. if v != "" && strings.Contains(strings.ToLower(v), needle) {
  1031. return true
  1032. }
  1033. }
  1034. return false
  1035. }
  1036. // parseCSVStrings splits a comma-separated list, trims/lower-cases each item,
  1037. // and drops blanks. Returns nil when the input has no usable entries — the
  1038. // caller can then skip the predicate entirely.
  1039. func parseCSVStrings(raw string) []string {
  1040. if raw == "" {
  1041. return nil
  1042. }
  1043. parts := strings.Split(raw, ",")
  1044. out := make([]string, 0, len(parts))
  1045. for _, p := range parts {
  1046. s := strings.ToLower(strings.TrimSpace(p))
  1047. if s != "" {
  1048. out = append(out, s)
  1049. }
  1050. }
  1051. if len(out) == 0 {
  1052. return nil
  1053. }
  1054. return out
  1055. }
  1056. // parseCSVInts is parseCSVStrings for positive integer IDs; non-numeric or
  1057. // non-positive entries are silently dropped.
  1058. func parseCSVInts(raw string) []int {
  1059. if raw == "" {
  1060. return nil
  1061. }
  1062. parts := strings.Split(raw, ",")
  1063. out := make([]int, 0, len(parts))
  1064. for _, p := range parts {
  1065. s := strings.TrimSpace(p)
  1066. if s == "" {
  1067. continue
  1068. }
  1069. if n, err := strconv.Atoi(s); err == nil && n > 0 {
  1070. out = append(out, n)
  1071. }
  1072. }
  1073. if len(out) == 0 {
  1074. return nil
  1075. }
  1076. return out
  1077. }
  1078. func clientMatchesAnyProtocol(c ClientWithAttachments, protocols []string, byInbound map[int]string) bool {
  1079. for _, id := range c.InboundIds {
  1080. p := byInbound[id]
  1081. if p == "" {
  1082. continue
  1083. }
  1084. if slices.Contains(protocols, strings.ToLower(p)) {
  1085. return true
  1086. }
  1087. }
  1088. return false
  1089. }
  1090. func clientMatchesAnyInbound(c ClientWithAttachments, inboundIds []int) bool {
  1091. for _, id := range c.InboundIds {
  1092. if slices.Contains(inboundIds, id) {
  1093. return true
  1094. }
  1095. }
  1096. return false
  1097. }
  1098. func clientMatchesAnyBucket(c ClientWithAttachments, buckets []string, onlineSet map[string]struct{}, nowMs, expireDiffMs, trafficDiffBytes int64) bool {
  1099. for _, b := range buckets {
  1100. if clientMatchesBucket(c, b, onlineSet, nowMs, expireDiffMs, trafficDiffBytes) {
  1101. return true
  1102. }
  1103. }
  1104. return false
  1105. }
  1106. func clientMatchesExpiryRange(c ClientWithAttachments, fromMs, toMs int64) bool {
  1107. if fromMs <= 0 && toMs <= 0 {
  1108. return true
  1109. }
  1110. // expiryTime of 0 means "never expires"; treat it as outside any bounded
  1111. // range so users filtering by date see only clients with concrete expiries.
  1112. if c.ExpiryTime == 0 {
  1113. return false
  1114. }
  1115. // Negative expiry is the "delayed start" sentinel; same treatment as never.
  1116. if c.ExpiryTime < 0 {
  1117. return false
  1118. }
  1119. if fromMs > 0 && c.ExpiryTime < fromMs {
  1120. return false
  1121. }
  1122. if toMs > 0 && c.ExpiryTime > toMs {
  1123. return false
  1124. }
  1125. return true
  1126. }
  1127. func clientMatchesUsageRange(c ClientWithAttachments, fromBytes, toBytes int64) bool {
  1128. if fromBytes <= 0 && toBytes <= 0 {
  1129. return true
  1130. }
  1131. used := int64(0)
  1132. if c.Traffic != nil {
  1133. used = c.Traffic.Up + c.Traffic.Down
  1134. }
  1135. if fromBytes > 0 && used < fromBytes {
  1136. return false
  1137. }
  1138. if toBytes > 0 && used > toBytes {
  1139. return false
  1140. }
  1141. return true
  1142. }
  1143. func clientMatchesAutoRenew(c ClientWithAttachments, mode string) bool {
  1144. switch strings.ToLower(strings.TrimSpace(mode)) {
  1145. case "on":
  1146. return c.Reset > 0
  1147. case "off":
  1148. return c.Reset <= 0
  1149. }
  1150. return true
  1151. }
  1152. func clientMatchesHasTgID(c ClientWithAttachments, mode string) bool {
  1153. switch strings.ToLower(strings.TrimSpace(mode)) {
  1154. case "yes":
  1155. return c.TgID != 0
  1156. case "no":
  1157. return c.TgID == 0
  1158. }
  1159. return true
  1160. }
  1161. func clientMatchesHasComment(c ClientWithAttachments, mode string) bool {
  1162. switch strings.ToLower(strings.TrimSpace(mode)) {
  1163. case "yes":
  1164. return strings.TrimSpace(c.Comment) != ""
  1165. case "no":
  1166. return strings.TrimSpace(c.Comment) == ""
  1167. }
  1168. return true
  1169. }
  1170. func clientMatchesBucket(c ClientWithAttachments, bucket string, onlineSet map[string]struct{}, nowMs, expireDiffMs, trafficDiffBytes int64) bool {
  1171. if bucket == "" {
  1172. return true
  1173. }
  1174. used := int64(0)
  1175. if c.Traffic != nil {
  1176. used = c.Traffic.Up + c.Traffic.Down
  1177. }
  1178. exhausted := c.TotalGB > 0 && used >= c.TotalGB
  1179. expired := c.ExpiryTime > 0 && c.ExpiryTime <= nowMs
  1180. switch bucket {
  1181. case "online":
  1182. if onlineSet == nil {
  1183. return false
  1184. }
  1185. _, ok := onlineSet[c.Email]
  1186. return ok && c.Enable
  1187. case "depleted":
  1188. return exhausted || expired
  1189. case "deactive":
  1190. return !c.Enable
  1191. case "active":
  1192. return c.Enable && !exhausted && !expired
  1193. case "expiring":
  1194. if !c.Enable || exhausted || expired {
  1195. return false
  1196. }
  1197. nearExpiry := c.ExpiryTime > 0 && c.ExpiryTime-nowMs < expireDiffMs
  1198. nearLimit := c.TotalGB > 0 && c.TotalGB-used < trafficDiffBytes
  1199. return nearExpiry || nearLimit
  1200. }
  1201. return true
  1202. }
  1203. func sortClients(rows []ClientWithAttachments, sortKey, order string) {
  1204. if sortKey == "" {
  1205. return
  1206. }
  1207. desc := order == "descend"
  1208. less := func(i, j int) bool {
  1209. a, b := rows[i], rows[j]
  1210. switch sortKey {
  1211. case "enable":
  1212. if a.Enable == b.Enable {
  1213. return false
  1214. }
  1215. return !a.Enable && b.Enable
  1216. case "email":
  1217. return strings.ToLower(a.Email) < strings.ToLower(b.Email)
  1218. case "inboundIds":
  1219. return len(a.InboundIds) < len(b.InboundIds)
  1220. case "traffic":
  1221. ua := int64(0)
  1222. if a.Traffic != nil {
  1223. ua = a.Traffic.Up + a.Traffic.Down
  1224. }
  1225. ub := int64(0)
  1226. if b.Traffic != nil {
  1227. ub = b.Traffic.Up + b.Traffic.Down
  1228. }
  1229. return ua < ub
  1230. case "remaining":
  1231. ra := int64(1<<62 - 1)
  1232. if a.TotalGB > 0 {
  1233. used := int64(0)
  1234. if a.Traffic != nil {
  1235. used = a.Traffic.Up + a.Traffic.Down
  1236. }
  1237. ra = a.TotalGB - used
  1238. }
  1239. rb := int64(1<<62 - 1)
  1240. if b.TotalGB > 0 {
  1241. used := int64(0)
  1242. if b.Traffic != nil {
  1243. used = b.Traffic.Up + b.Traffic.Down
  1244. }
  1245. rb = b.TotalGB - used
  1246. }
  1247. return ra < rb
  1248. case "expiryTime":
  1249. ea := int64(1<<62 - 1)
  1250. if a.ExpiryTime > 0 {
  1251. ea = a.ExpiryTime
  1252. }
  1253. eb := int64(1<<62 - 1)
  1254. if b.ExpiryTime > 0 {
  1255. eb = b.ExpiryTime
  1256. }
  1257. return ea < eb
  1258. case "createdAt":
  1259. if a.CreatedAt == b.CreatedAt {
  1260. return a.Id < b.Id
  1261. }
  1262. return a.CreatedAt < b.CreatedAt
  1263. case "updatedAt":
  1264. if a.UpdatedAt == b.UpdatedAt {
  1265. return a.Id < b.Id
  1266. }
  1267. return a.UpdatedAt < b.UpdatedAt
  1268. case "lastOnline":
  1269. la := int64(0)
  1270. if a.Traffic != nil {
  1271. la = a.Traffic.LastOnline
  1272. }
  1273. lb := int64(0)
  1274. if b.Traffic != nil {
  1275. lb = b.Traffic.LastOnline
  1276. }
  1277. if la == lb {
  1278. return a.Id < b.Id
  1279. }
  1280. return la < lb
  1281. }
  1282. return false
  1283. }
  1284. sort.SliceStable(rows, func(i, j int) bool {
  1285. if desc {
  1286. return less(j, i)
  1287. }
  1288. return less(i, j)
  1289. })
  1290. }
  1291. // BulkAdjustResult is returned by BulkAdjust to report how many clients were
  1292. // successfully updated and which were skipped (typically because the field
  1293. // being adjusted was unlimited for that client) or failed.
  1294. type BulkAdjustResult struct {
  1295. Adjusted int `json:"adjusted"`
  1296. Skipped []BulkAdjustReport `json:"skipped,omitempty"`
  1297. }
  1298. type BulkAdjustReport struct {
  1299. Email string `json:"email"`
  1300. Reason string `json:"reason"`
  1301. }
  1302. type bulkAdjustEntry struct {
  1303. record *model.ClientRecord
  1304. applyExpiry bool
  1305. newExpiry int64
  1306. applyTotal bool
  1307. newTotal int64
  1308. }
  1309. // BulkAdjust shifts ExpiryTime by addDays (days) and TotalGB by addBytes
  1310. // for every email in the list. Clients whose corresponding field is
  1311. // unlimited (0) are skipped — bulk extend should not accidentally
  1312. // limit an unlimited client. addDays and addBytes may be negative.
  1313. //
  1314. // Like BulkDelete, the work is grouped by inbound so each inbound's
  1315. // settings JSON is parsed and written exactly once regardless of how
  1316. // many target emails it contains.
  1317. func (s *ClientService) BulkAdjust(inboundSvc *InboundService, emails []string, addDays int, addBytes int64) (BulkAdjustResult, bool, error) {
  1318. result := BulkAdjustResult{}
  1319. if len(emails) == 0 {
  1320. return result, false, nil
  1321. }
  1322. if addDays == 0 && addBytes == 0 {
  1323. return result, false, common.NewError("no adjustment specified")
  1324. }
  1325. addExpiryMs := int64(addDays) * 24 * 60 * 60 * 1000
  1326. seen := map[string]struct{}{}
  1327. cleanEmails := make([]string, 0, len(emails))
  1328. for _, e := range emails {
  1329. e = strings.TrimSpace(e)
  1330. if e == "" {
  1331. continue
  1332. }
  1333. if _, ok := seen[e]; ok {
  1334. continue
  1335. }
  1336. seen[e] = struct{}{}
  1337. cleanEmails = append(cleanEmails, e)
  1338. }
  1339. if len(cleanEmails) == 0 {
  1340. return result, false, nil
  1341. }
  1342. db := database.GetDB()
  1343. var records []model.ClientRecord
  1344. if err := db.Where("email IN ?", cleanEmails).Find(&records).Error; err != nil {
  1345. return result, false, err
  1346. }
  1347. recordsByEmail := make(map[string]*model.ClientRecord, len(records))
  1348. for i := range records {
  1349. recordsByEmail[records[i].Email] = &records[i]
  1350. }
  1351. skippedReasons := map[string]string{}
  1352. for _, email := range cleanEmails {
  1353. if _, ok := recordsByEmail[email]; !ok {
  1354. skippedReasons[email] = "client not found"
  1355. }
  1356. }
  1357. plan := map[string]*bulkAdjustEntry{}
  1358. for email, rec := range recordsByEmail {
  1359. entry := &bulkAdjustEntry{record: rec}
  1360. if addDays != 0 {
  1361. switch {
  1362. case rec.ExpiryTime == 0:
  1363. if _, exists := skippedReasons[email]; !exists {
  1364. skippedReasons[email] = "unlimited expiry"
  1365. }
  1366. case rec.ExpiryTime > 0:
  1367. next := rec.ExpiryTime + addExpiryMs
  1368. if next <= 0 {
  1369. if _, exists := skippedReasons[email]; !exists {
  1370. skippedReasons[email] = "reduction exceeds remaining time"
  1371. }
  1372. } else {
  1373. entry.applyExpiry = true
  1374. entry.newExpiry = next
  1375. }
  1376. default:
  1377. next := rec.ExpiryTime - addExpiryMs
  1378. if next >= 0 {
  1379. if _, exists := skippedReasons[email]; !exists {
  1380. skippedReasons[email] = "reduction exceeds delay window"
  1381. }
  1382. } else {
  1383. entry.applyExpiry = true
  1384. entry.newExpiry = next
  1385. }
  1386. }
  1387. }
  1388. if addBytes != 0 {
  1389. if rec.TotalGB == 0 {
  1390. if _, exists := skippedReasons[email]; !exists {
  1391. skippedReasons[email] = "unlimited traffic"
  1392. }
  1393. } else {
  1394. next := max(rec.TotalGB+addBytes, 0)
  1395. entry.applyTotal = true
  1396. entry.newTotal = next
  1397. }
  1398. }
  1399. if entry.applyExpiry || entry.applyTotal {
  1400. plan[email] = entry
  1401. }
  1402. }
  1403. if len(plan) == 0 {
  1404. for email, reason := range skippedReasons {
  1405. result.Skipped = append(result.Skipped, BulkAdjustReport{Email: email, Reason: reason})
  1406. }
  1407. return result, false, nil
  1408. }
  1409. plannedIds := make([]int, 0, len(plan))
  1410. recordIdToEmail := make(map[int]string, len(plan))
  1411. for email, entry := range plan {
  1412. plannedIds = append(plannedIds, entry.record.Id)
  1413. recordIdToEmail[entry.record.Id] = email
  1414. }
  1415. var mappings []model.ClientInbound
  1416. if err := db.Where("client_id IN ?", plannedIds).Find(&mappings).Error; err != nil {
  1417. return result, false, err
  1418. }
  1419. emailsByInbound := map[int][]string{}
  1420. for _, m := range mappings {
  1421. email, ok := recordIdToEmail[m.ClientId]
  1422. if !ok {
  1423. continue
  1424. }
  1425. emailsByInbound[m.InboundId] = append(emailsByInbound[m.InboundId], email)
  1426. }
  1427. needRestart := false
  1428. for inboundId, ibEmails := range emailsByInbound {
  1429. ibRes := s.bulkAdjustInboundClients(inboundSvc, inboundId, ibEmails, plan)
  1430. if ibRes.needRestart {
  1431. needRestart = true
  1432. }
  1433. for email, reason := range ibRes.perEmailSkipped {
  1434. if _, already := skippedReasons[email]; !already {
  1435. skippedReasons[email] = reason
  1436. }
  1437. }
  1438. }
  1439. for email, entry := range plan {
  1440. if _, skipped := skippedReasons[email]; skipped {
  1441. continue
  1442. }
  1443. updates := map[string]any{}
  1444. if entry.applyExpiry {
  1445. updates["expiry_time"] = entry.newExpiry
  1446. }
  1447. if entry.applyTotal {
  1448. updates["total"] = entry.newTotal
  1449. }
  1450. if len(updates) == 0 {
  1451. continue
  1452. }
  1453. if err := db.Model(xray.ClientTraffic{}).Where("email = ?", email).Updates(updates).Error; err != nil {
  1454. if _, already := skippedReasons[email]; !already {
  1455. skippedReasons[email] = err.Error()
  1456. }
  1457. continue
  1458. }
  1459. result.Adjusted++
  1460. }
  1461. for email, reason := range skippedReasons {
  1462. result.Skipped = append(result.Skipped, BulkAdjustReport{Email: email, Reason: reason})
  1463. }
  1464. return result, needRestart, nil
  1465. }
  1466. type bulkInboundAdjustResult struct {
  1467. perEmailSkipped map[string]string
  1468. needRestart bool
  1469. }
  1470. // bulkAdjustInboundClients applies expiry/total deltas to multiple clients
  1471. // inside a single inbound's settings JSON. The xray runtime is updated
  1472. // only for remote-node inbounds; local nodes do not need a notification
  1473. // because the AddUser payload does not include totalGB/expiryTime —
  1474. // changing those fields is identity-preserving and the panel's traffic
  1475. // enforcement loop picks up the new limits from ClientTraffic directly.
  1476. func (s *ClientService) bulkAdjustInboundClients(
  1477. inboundSvc *InboundService,
  1478. inboundId int,
  1479. emails []string,
  1480. plan map[string]*bulkAdjustEntry,
  1481. ) bulkInboundAdjustResult {
  1482. res := bulkInboundAdjustResult{perEmailSkipped: map[string]string{}}
  1483. defer lockInbound(inboundId).Unlock()
  1484. oldInbound, err := inboundSvc.GetInbound(inboundId)
  1485. if err != nil {
  1486. logger.Error("Load Old Data Error")
  1487. for _, e := range emails {
  1488. res.perEmailSkipped[e] = err.Error()
  1489. }
  1490. return res
  1491. }
  1492. var settings map[string]any
  1493. if err := json.Unmarshal([]byte(oldInbound.Settings), &settings); err != nil {
  1494. for _, e := range emails {
  1495. res.perEmailSkipped[e] = err.Error()
  1496. }
  1497. return res
  1498. }
  1499. clientKey := "id"
  1500. switch oldInbound.Protocol {
  1501. case model.Trojan:
  1502. clientKey = "password"
  1503. case model.Shadowsocks:
  1504. clientKey = "email"
  1505. case model.Hysteria:
  1506. clientKey = "auth"
  1507. }
  1508. keyToEmail := make(map[string]string, len(emails))
  1509. for _, email := range emails {
  1510. entry := plan[email]
  1511. if entry == nil {
  1512. res.perEmailSkipped[email] = "client not found"
  1513. continue
  1514. }
  1515. key := clientKeyForProtocol(oldInbound.Protocol, entry.record)
  1516. if key == "" {
  1517. res.perEmailSkipped[email] = "missing client key for protocol"
  1518. continue
  1519. }
  1520. keyToEmail[key] = email
  1521. }
  1522. interfaceClients, _ := settings["clients"].([]any)
  1523. foundEmails := map[string]bool{}
  1524. nowMs := time.Now().Unix() * 1000
  1525. for i, client := range interfaceClients {
  1526. c, ok := client.(map[string]any)
  1527. if !ok {
  1528. continue
  1529. }
  1530. cKey, _ := c[clientKey].(string)
  1531. targetEmail, found := keyToEmail[cKey]
  1532. if !found {
  1533. continue
  1534. }
  1535. entry := plan[targetEmail]
  1536. if entry.applyExpiry {
  1537. c["expiryTime"] = entry.newExpiry
  1538. }
  1539. if entry.applyTotal {
  1540. c["totalGB"] = entry.newTotal
  1541. }
  1542. c["updated_at"] = nowMs
  1543. interfaceClients[i] = c
  1544. foundEmails[targetEmail] = true
  1545. }
  1546. for _, email := range keyToEmail {
  1547. if !foundEmails[email] {
  1548. res.perEmailSkipped[email] = "Client Not Found In Inbound"
  1549. }
  1550. }
  1551. if len(foundEmails) == 0 {
  1552. return res
  1553. }
  1554. settings["clients"] = interfaceClients
  1555. newSettings, err := json.MarshalIndent(settings, "", " ")
  1556. if err != nil {
  1557. for email := range foundEmails {
  1558. res.perEmailSkipped[email] = err.Error()
  1559. }
  1560. return res
  1561. }
  1562. oldInbound.Settings = string(newSettings)
  1563. if oldInbound.NodeID != nil {
  1564. rt, rterr := inboundSvc.runtimeFor(oldInbound)
  1565. if rterr != nil {
  1566. for email := range foundEmails {
  1567. res.perEmailSkipped[email] = rterr.Error()
  1568. delete(foundEmails, email)
  1569. }
  1570. } else {
  1571. for email := range foundEmails {
  1572. entry := plan[email]
  1573. updated := *entry.record.ToClient()
  1574. if entry.applyExpiry {
  1575. updated.ExpiryTime = entry.newExpiry
  1576. }
  1577. if entry.applyTotal {
  1578. updated.TotalGB = entry.newTotal
  1579. }
  1580. updated.UpdatedAt = nowMs
  1581. if err1 := rt.UpdateUser(context.Background(), oldInbound, email, updated); err1 != nil {
  1582. res.perEmailSkipped[email] = err1.Error()
  1583. delete(foundEmails, email)
  1584. }
  1585. }
  1586. }
  1587. }
  1588. db := database.GetDB()
  1589. if err := db.Save(oldInbound).Error; err != nil {
  1590. for email := range foundEmails {
  1591. if _, skip := res.perEmailSkipped[email]; !skip {
  1592. res.perEmailSkipped[email] = err.Error()
  1593. }
  1594. }
  1595. return res
  1596. }
  1597. finalClients, gcErr := inboundSvc.GetClients(oldInbound)
  1598. if gcErr == nil {
  1599. if syncErr := s.SyncInbound(db, inboundId, finalClients); syncErr != nil {
  1600. logger.Warning("bulkAdjust SyncInbound:", syncErr)
  1601. }
  1602. }
  1603. return res
  1604. }
  1605. // BulkDeleteResult mirrors BulkAdjustResult: total deleted plus per-email
  1606. // skip reasons when an email could not be processed.
  1607. type BulkDeleteResult struct {
  1608. Deleted int `json:"deleted"`
  1609. Skipped []BulkDeleteReport `json:"skipped,omitempty"`
  1610. }
  1611. type BulkDeleteReport struct {
  1612. Email string `json:"email"`
  1613. Reason string `json:"reason"`
  1614. }
  1615. // BulkDelete removes every client in the list in one optimized pass.
  1616. // Instead of running the full single-delete pipeline N times (which would
  1617. // re-read, re-parse, and re-write each inbound's settings JSON for every
  1618. // email), it groups emails by inbound and performs a single
  1619. // read-modify-write per inbound. Per-row DB cleanups are also batched with
  1620. // IN-clause queries at the end. Errors on a particular email are recorded
  1621. // in the Skipped list and processing continues for the rest.
  1622. func (s *ClientService) BulkDelete(inboundSvc *InboundService, emails []string, keepTraffic bool) (BulkDeleteResult, bool, error) {
  1623. result := BulkDeleteResult{}
  1624. seen := map[string]struct{}{}
  1625. cleanEmails := make([]string, 0, len(emails))
  1626. for _, e := range emails {
  1627. e = strings.TrimSpace(e)
  1628. if e == "" {
  1629. continue
  1630. }
  1631. if _, ok := seen[e]; ok {
  1632. continue
  1633. }
  1634. seen[e] = struct{}{}
  1635. cleanEmails = append(cleanEmails, e)
  1636. }
  1637. if len(cleanEmails) == 0 {
  1638. return result, false, nil
  1639. }
  1640. db := database.GetDB()
  1641. var records []model.ClientRecord
  1642. if err := db.Where("email IN ?", cleanEmails).Find(&records).Error; err != nil {
  1643. return result, false, err
  1644. }
  1645. recordsByEmail := make(map[string]*model.ClientRecord, len(records))
  1646. for i := range records {
  1647. recordsByEmail[records[i].Email] = &records[i]
  1648. tombstoneClientEmail(records[i].Email)
  1649. }
  1650. skippedReasons := map[string]string{}
  1651. for _, email := range cleanEmails {
  1652. if _, ok := recordsByEmail[email]; !ok {
  1653. skippedReasons[email] = "client not found"
  1654. }
  1655. }
  1656. clientIds := make([]int, 0, len(recordsByEmail))
  1657. recordIdToEmail := make(map[int]string, len(recordsByEmail))
  1658. for _, r := range recordsByEmail {
  1659. clientIds = append(clientIds, r.Id)
  1660. recordIdToEmail[r.Id] = r.Email
  1661. }
  1662. emailsByInbound := map[int][]string{}
  1663. if len(clientIds) > 0 {
  1664. var mappings []model.ClientInbound
  1665. if err := db.Where("client_id IN ?", clientIds).Find(&mappings).Error; err != nil {
  1666. return result, false, err
  1667. }
  1668. for _, m := range mappings {
  1669. email, ok := recordIdToEmail[m.ClientId]
  1670. if !ok {
  1671. continue
  1672. }
  1673. emailsByInbound[m.InboundId] = append(emailsByInbound[m.InboundId], email)
  1674. }
  1675. }
  1676. needRestart := false
  1677. for inboundId, ibEmails := range emailsByInbound {
  1678. ibResult := s.bulkDelInboundClients(inboundSvc, inboundId, ibEmails, recordsByEmail)
  1679. if ibResult.needRestart {
  1680. needRestart = true
  1681. }
  1682. for email, reason := range ibResult.perEmailSkipped {
  1683. if _, already := skippedReasons[email]; !already {
  1684. skippedReasons[email] = reason
  1685. }
  1686. }
  1687. }
  1688. successEmails := make([]string, 0, len(recordsByEmail))
  1689. successIds := make([]int, 0, len(recordsByEmail))
  1690. for email, rec := range recordsByEmail {
  1691. if _, skipped := skippedReasons[email]; skipped {
  1692. continue
  1693. }
  1694. successEmails = append(successEmails, email)
  1695. successIds = append(successIds, rec.Id)
  1696. }
  1697. if len(successIds) > 0 {
  1698. if err := db.Where("client_id IN ?", successIds).Delete(&model.ClientInbound{}).Error; err != nil {
  1699. return result, needRestart, err
  1700. }
  1701. if !keepTraffic && len(successEmails) > 0 {
  1702. if err := db.Where("email IN ?", successEmails).Delete(&xray.ClientTraffic{}).Error; err != nil {
  1703. return result, needRestart, err
  1704. }
  1705. if err := db.Where("client_email IN ?", successEmails).Delete(&model.InboundClientIps{}).Error; err != nil {
  1706. return result, needRestart, err
  1707. }
  1708. }
  1709. if err := db.Where("id IN ?", successIds).Delete(&model.ClientRecord{}).Error; err != nil {
  1710. return result, needRestart, err
  1711. }
  1712. }
  1713. result.Deleted = len(successEmails)
  1714. for email, reason := range skippedReasons {
  1715. result.Skipped = append(result.Skipped, BulkDeleteReport{Email: email, Reason: reason})
  1716. }
  1717. return result, needRestart, nil
  1718. }
  1719. type bulkInboundDeleteResult struct {
  1720. perEmailSkipped map[string]string
  1721. needRestart bool
  1722. }
  1723. // bulkDelInboundClients removes multiple clients from a single inbound's
  1724. // settings JSON in one read-modify-write cycle, runs the xray runtime
  1725. // RemoveUser/DeleteUser calls, and persists the inbound. The returned map
  1726. // holds per-email failure reasons; emails not present in the map are
  1727. // considered successful for this inbound.
  1728. func (s *ClientService) bulkDelInboundClients(
  1729. inboundSvc *InboundService,
  1730. inboundId int,
  1731. emails []string,
  1732. records map[string]*model.ClientRecord,
  1733. ) bulkInboundDeleteResult {
  1734. res := bulkInboundDeleteResult{perEmailSkipped: map[string]string{}}
  1735. defer lockInbound(inboundId).Unlock()
  1736. oldInbound, err := inboundSvc.GetInbound(inboundId)
  1737. if err != nil {
  1738. logger.Error("Load Old Data Error")
  1739. for _, e := range emails {
  1740. res.perEmailSkipped[e] = err.Error()
  1741. }
  1742. return res
  1743. }
  1744. var settings map[string]any
  1745. if err := json.Unmarshal([]byte(oldInbound.Settings), &settings); err != nil {
  1746. for _, e := range emails {
  1747. res.perEmailSkipped[e] = err.Error()
  1748. }
  1749. return res
  1750. }
  1751. clientKey := "id"
  1752. switch oldInbound.Protocol {
  1753. case model.Trojan:
  1754. clientKey = "password"
  1755. case model.Shadowsocks:
  1756. clientKey = "email"
  1757. case model.Hysteria:
  1758. clientKey = "auth"
  1759. }
  1760. keyToEmail := make(map[string]string, len(emails))
  1761. for _, email := range emails {
  1762. rec := records[email]
  1763. if rec == nil {
  1764. res.perEmailSkipped[email] = "client not found"
  1765. continue
  1766. }
  1767. key := clientKeyForProtocol(oldInbound.Protocol, rec)
  1768. if key == "" {
  1769. res.perEmailSkipped[email] = "missing client key for protocol"
  1770. continue
  1771. }
  1772. keyToEmail[key] = email
  1773. }
  1774. interfaceClients, _ := settings["clients"].([]any)
  1775. newClients := make([]any, 0, len(interfaceClients))
  1776. foundEmails := map[string]bool{}
  1777. enableByEmail := map[string]bool{}
  1778. for _, client := range interfaceClients {
  1779. c, ok := client.(map[string]any)
  1780. if !ok {
  1781. newClients = append(newClients, client)
  1782. continue
  1783. }
  1784. cKey, _ := c[clientKey].(string)
  1785. if targetEmail, found := keyToEmail[cKey]; found {
  1786. foundEmails[targetEmail] = true
  1787. if em, _ := c["email"].(string); em != "" {
  1788. en, _ := c["enable"].(bool)
  1789. enableByEmail[em] = en
  1790. }
  1791. continue
  1792. }
  1793. newClients = append(newClients, client)
  1794. }
  1795. for _, email := range keyToEmail {
  1796. if !foundEmails[email] {
  1797. res.perEmailSkipped[email] = "Client Not Found In Inbound"
  1798. }
  1799. }
  1800. db := database.GetDB()
  1801. newClients = compactOrphans(db, newClients)
  1802. if newClients == nil {
  1803. newClients = []any{}
  1804. }
  1805. settings["clients"] = newClients
  1806. newSettings, err := json.MarshalIndent(settings, "", " ")
  1807. if err != nil {
  1808. for email := range foundEmails {
  1809. if _, skip := res.perEmailSkipped[email]; !skip {
  1810. res.perEmailSkipped[email] = err.Error()
  1811. }
  1812. }
  1813. return res
  1814. }
  1815. oldInbound.Settings = string(newSettings)
  1816. foundList := make([]string, 0, len(foundEmails))
  1817. for email := range foundEmails {
  1818. foundList = append(foundList, email)
  1819. }
  1820. notDepletedByEmail := map[string]bool{}
  1821. if len(foundList) > 0 {
  1822. type trafficRow struct {
  1823. Email string
  1824. Enable bool
  1825. }
  1826. var rows []trafficRow
  1827. if err := db.Model(xray.ClientTraffic{}).
  1828. Where("email IN ?", foundList).
  1829. Select("email, enable").
  1830. Scan(&rows).Error; err == nil {
  1831. for _, r := range rows {
  1832. notDepletedByEmail[r.Email] = r.Enable
  1833. }
  1834. }
  1835. }
  1836. for email := range foundEmails {
  1837. shared, sharedErr := inboundSvc.emailUsedByOtherInbounds(email, inboundId)
  1838. if sharedErr != nil {
  1839. res.perEmailSkipped[email] = sharedErr.Error()
  1840. delete(foundEmails, email)
  1841. continue
  1842. }
  1843. if shared {
  1844. continue
  1845. }
  1846. if delErr := inboundSvc.DelClientIPs(db, email); delErr != nil {
  1847. logger.Error("Error in delete client IPs")
  1848. res.perEmailSkipped[email] = delErr.Error()
  1849. delete(foundEmails, email)
  1850. continue
  1851. }
  1852. if delErr := inboundSvc.DelClientStat(db, email); delErr != nil {
  1853. logger.Error("Delete stats Data Error")
  1854. res.perEmailSkipped[email] = delErr.Error()
  1855. delete(foundEmails, email)
  1856. continue
  1857. }
  1858. }
  1859. if oldInbound.NodeID == nil {
  1860. rt, rterr := inboundSvc.runtimeFor(oldInbound)
  1861. if rterr != nil {
  1862. res.needRestart = true
  1863. } else {
  1864. for email := range foundEmails {
  1865. if !enableByEmail[email] || !notDepletedByEmail[email] {
  1866. continue
  1867. }
  1868. err1 := rt.RemoveUser(context.Background(), oldInbound, email)
  1869. if err1 == nil {
  1870. logger.Debug("Client deleted on", rt.Name(), ":", email)
  1871. } else if strings.Contains(err1.Error(), fmt.Sprintf("User %s not found.", email)) {
  1872. logger.Debug("User is already deleted. Nothing to do more...")
  1873. } else {
  1874. logger.Debug("Error in deleting client on", rt.Name(), ":", err1)
  1875. res.needRestart = true
  1876. }
  1877. }
  1878. }
  1879. } else {
  1880. rt, rterr := inboundSvc.runtimeFor(oldInbound)
  1881. if rterr != nil {
  1882. for email := range foundEmails {
  1883. res.perEmailSkipped[email] = rterr.Error()
  1884. delete(foundEmails, email)
  1885. }
  1886. } else {
  1887. for email := range foundEmails {
  1888. if err1 := rt.DeleteUser(context.Background(), oldInbound, email); err1 != nil {
  1889. res.perEmailSkipped[email] = err1.Error()
  1890. delete(foundEmails, email)
  1891. }
  1892. }
  1893. }
  1894. }
  1895. if err := db.Save(oldInbound).Error; err != nil {
  1896. for email := range foundEmails {
  1897. if _, skip := res.perEmailSkipped[email]; !skip {
  1898. res.perEmailSkipped[email] = err.Error()
  1899. }
  1900. }
  1901. return res
  1902. }
  1903. finalClients, err := inboundSvc.GetClients(oldInbound)
  1904. if err != nil {
  1905. return res
  1906. }
  1907. if err := s.SyncInbound(db, inboundId, finalClients); err != nil {
  1908. return res
  1909. }
  1910. return res
  1911. }
  1912. // BulkCreateResult mirrors BulkAdjustResult for the create flow.
  1913. type BulkCreateResult struct {
  1914. Created int `json:"created"`
  1915. Skipped []BulkCreateReport `json:"skipped,omitempty"`
  1916. }
  1917. type BulkCreateReport struct {
  1918. Email string `json:"email"`
  1919. Reason string `json:"reason"`
  1920. }
  1921. // BulkCreate iterates payloads sequentially. Each item is the same shape
  1922. // the single-create endpoint accepts, so callers can submit a heterogeneous
  1923. // list (different inboundIds, plans, etc.) in one round-trip.
  1924. func (s *ClientService) BulkCreate(inboundSvc *InboundService, payloads []ClientCreatePayload) (BulkCreateResult, bool, error) {
  1925. result := BulkCreateResult{}
  1926. needRestart := false
  1927. for i := range payloads {
  1928. p := payloads[i]
  1929. email := strings.TrimSpace(p.Client.Email)
  1930. nr, err := s.Create(inboundSvc, &p)
  1931. if err != nil {
  1932. if email == "" {
  1933. email = "(missing email)"
  1934. }
  1935. result.Skipped = append(result.Skipped, BulkCreateReport{Email: email, Reason: err.Error()})
  1936. continue
  1937. }
  1938. if nr {
  1939. needRestart = true
  1940. }
  1941. result.Created++
  1942. }
  1943. return result, needRestart, nil
  1944. }
  1945. func (s *ClientService) DelDepleted(inboundSvc *InboundService) (int, bool, error) {
  1946. db := database.GetDB()
  1947. now := time.Now().UnixMilli()
  1948. depletedClause := "reset = 0 and ((total > 0 and up + down >= total) or (expiry_time > 0 and expiry_time <= ?))"
  1949. var rows []xray.ClientTraffic
  1950. if err := db.Where(depletedClause, now).Find(&rows).Error; err != nil {
  1951. return 0, false, err
  1952. }
  1953. if len(rows) == 0 {
  1954. return 0, false, nil
  1955. }
  1956. emails := make(map[string]struct{}, len(rows))
  1957. for _, r := range rows {
  1958. if r.Email != "" {
  1959. emails[r.Email] = struct{}{}
  1960. }
  1961. }
  1962. needRestart := false
  1963. deleted := 0
  1964. for email := range emails {
  1965. var rec model.ClientRecord
  1966. if err := db.Where("email = ?", email).First(&rec).Error; err != nil {
  1967. if errors.Is(err, gorm.ErrRecordNotFound) {
  1968. continue
  1969. }
  1970. return deleted, needRestart, err
  1971. }
  1972. nr, err := s.Delete(inboundSvc, rec.Id, false)
  1973. if err != nil {
  1974. return deleted, needRestart, err
  1975. }
  1976. if nr {
  1977. needRestart = true
  1978. }
  1979. deleted++
  1980. }
  1981. return deleted, needRestart, nil
  1982. }
  1983. func (s *ClientService) ResetAllClientTraffics(inboundSvc *InboundService, id int) error {
  1984. return submitTrafficWrite(func() error {
  1985. return s.resetAllClientTrafficsLocked(id)
  1986. })
  1987. }
  1988. func (s *ClientService) resetAllClientTrafficsLocked(id int) error {
  1989. db := database.GetDB()
  1990. now := time.Now().Unix() * 1000
  1991. if err := db.Transaction(func(tx *gorm.DB) error {
  1992. whereText := "inbound_id "
  1993. if id == -1 {
  1994. whereText += " > ?"
  1995. } else {
  1996. whereText += " = ?"
  1997. }
  1998. result := tx.Model(xray.ClientTraffic{}).
  1999. Where(whereText, id).
  2000. Updates(map[string]any{"enable": true, "up": 0, "down": 0})
  2001. if result.Error != nil {
  2002. return result.Error
  2003. }
  2004. inboundWhereText := "id "
  2005. if id == -1 {
  2006. inboundWhereText += " > ?"
  2007. } else {
  2008. inboundWhereText += " = ?"
  2009. }
  2010. result = tx.Model(model.Inbound{}).
  2011. Where(inboundWhereText, id).
  2012. Update("last_traffic_reset_time", now)
  2013. return result.Error
  2014. }); err != nil {
  2015. return err
  2016. }
  2017. return nil
  2018. }
  2019. func (s *ClientService) ResetAllTraffics() (bool, error) {
  2020. res := database.GetDB().Model(&xray.ClientTraffic{}).
  2021. Where("1 = 1").
  2022. Updates(map[string]any{"up": 0, "down": 0})
  2023. if res.Error != nil {
  2024. return false, res.Error
  2025. }
  2026. return res.RowsAffected > 0, nil
  2027. }
  2028. func (s *ClientService) Detach(inboundSvc *InboundService, id int, inboundIds []int) (bool, error) {
  2029. existing, err := s.GetByID(id)
  2030. if err != nil {
  2031. return false, err
  2032. }
  2033. currentIds, err := s.GetInboundIdsForRecord(id)
  2034. if err != nil {
  2035. return false, err
  2036. }
  2037. have := make(map[int]struct{}, len(currentIds))
  2038. for _, x := range currentIds {
  2039. have[x] = struct{}{}
  2040. }
  2041. needRestart := false
  2042. for _, ibId := range inboundIds {
  2043. if _, attached := have[ibId]; !attached {
  2044. continue
  2045. }
  2046. inbound, getErr := inboundSvc.GetInbound(ibId)
  2047. if getErr != nil {
  2048. return needRestart, getErr
  2049. }
  2050. key := clientKeyForProtocol(inbound.Protocol, existing)
  2051. if key == "" {
  2052. continue
  2053. }
  2054. nr, delErr := s.DelInboundClient(inboundSvc, ibId, key)
  2055. if delErr != nil {
  2056. return needRestart, delErr
  2057. }
  2058. if nr {
  2059. needRestart = true
  2060. }
  2061. }
  2062. return needRestart, nil
  2063. }
  2064. func (s *ClientService) checkEmailsExistForClients(inboundSvc *InboundService, clients []model.Client) (string, error) {
  2065. emailSubIDs, err := inboundSvc.getAllEmailSubIDs()
  2066. if err != nil {
  2067. return "", err
  2068. }
  2069. seen := make(map[string]string, len(clients))
  2070. for _, client := range clients {
  2071. if client.Email == "" {
  2072. continue
  2073. }
  2074. key := strings.ToLower(client.Email)
  2075. if prev, ok := seen[key]; ok {
  2076. if prev != client.SubID || client.SubID == "" {
  2077. return client.Email, nil
  2078. }
  2079. continue
  2080. }
  2081. seen[key] = client.SubID
  2082. if existingSub, ok := emailSubIDs[key]; ok {
  2083. if client.SubID == "" || existingSub == "" || existingSub != client.SubID {
  2084. return client.Email, nil
  2085. }
  2086. }
  2087. }
  2088. return "", nil
  2089. }
  2090. func (s *ClientService) AddInboundClient(inboundSvc *InboundService, data *model.Inbound) (bool, error) {
  2091. defer lockInbound(data.Id).Unlock()
  2092. clients, err := inboundSvc.GetClients(data)
  2093. if err != nil {
  2094. return false, err
  2095. }
  2096. var settings map[string]any
  2097. err = json.Unmarshal([]byte(data.Settings), &settings)
  2098. if err != nil {
  2099. return false, err
  2100. }
  2101. interfaceClients := settings["clients"].([]any)
  2102. nowTs := time.Now().Unix() * 1000
  2103. for i := range interfaceClients {
  2104. if cm, ok := interfaceClients[i].(map[string]any); ok {
  2105. if _, ok2 := cm["created_at"]; !ok2 {
  2106. cm["created_at"] = nowTs
  2107. }
  2108. cm["updated_at"] = nowTs
  2109. interfaceClients[i] = cm
  2110. }
  2111. }
  2112. existEmail, err := s.checkEmailsExistForClients(inboundSvc, clients)
  2113. if err != nil {
  2114. return false, err
  2115. }
  2116. if existEmail != "" {
  2117. return false, common.NewError("Duplicate email:", existEmail)
  2118. }
  2119. oldInbound, err := inboundSvc.GetInbound(data.Id)
  2120. if err != nil {
  2121. return false, err
  2122. }
  2123. for _, client := range clients {
  2124. if strings.TrimSpace(client.Email) == "" {
  2125. return false, common.NewError("client email is required")
  2126. }
  2127. switch oldInbound.Protocol {
  2128. case "trojan":
  2129. if client.Password == "" {
  2130. return false, common.NewError("empty client ID")
  2131. }
  2132. case "shadowsocks":
  2133. if client.Email == "" {
  2134. return false, common.NewError("empty client ID")
  2135. }
  2136. case "hysteria":
  2137. if client.Auth == "" {
  2138. return false, common.NewError("empty client ID")
  2139. }
  2140. default:
  2141. if client.ID == "" {
  2142. return false, common.NewError("empty client ID")
  2143. }
  2144. }
  2145. }
  2146. var oldSettings map[string]any
  2147. err = json.Unmarshal([]byte(oldInbound.Settings), &oldSettings)
  2148. if err != nil {
  2149. return false, err
  2150. }
  2151. if oldInbound.Protocol == model.Shadowsocks {
  2152. applyShadowsocksClientMethod(interfaceClients, oldSettings)
  2153. }
  2154. oldClients := oldSettings["clients"].([]any)
  2155. oldClients = compactOrphans(database.GetDB(), oldClients)
  2156. oldClients = append(oldClients, interfaceClients...)
  2157. oldSettings["clients"] = oldClients
  2158. newSettings, err := json.MarshalIndent(oldSettings, "", " ")
  2159. if err != nil {
  2160. return false, err
  2161. }
  2162. oldInbound.Settings = string(newSettings)
  2163. db := database.GetDB()
  2164. tx := db.Begin()
  2165. defer func() {
  2166. if err != nil {
  2167. tx.Rollback()
  2168. } else {
  2169. tx.Commit()
  2170. }
  2171. }()
  2172. needRestart := false
  2173. rt, rterr := inboundSvc.runtimeFor(oldInbound)
  2174. if rterr != nil {
  2175. if oldInbound.NodeID != nil {
  2176. err = rterr
  2177. return false, err
  2178. }
  2179. needRestart = true
  2180. } else if oldInbound.NodeID == nil {
  2181. for _, client := range clients {
  2182. if len(client.Email) == 0 {
  2183. needRestart = true
  2184. continue
  2185. }
  2186. inboundSvc.AddClientStat(tx, data.Id, &client)
  2187. if !client.Enable {
  2188. continue
  2189. }
  2190. cipher := ""
  2191. if oldInbound.Protocol == "shadowsocks" {
  2192. cipher = oldSettings["method"].(string)
  2193. }
  2194. err1 := rt.AddUser(context.Background(), oldInbound, map[string]any{
  2195. "email": client.Email,
  2196. "id": client.ID,
  2197. "auth": client.Auth,
  2198. "security": client.Security,
  2199. "flow": client.Flow,
  2200. "password": client.Password,
  2201. "cipher": cipher,
  2202. })
  2203. if err1 == nil {
  2204. logger.Debug("Client added on", rt.Name(), ":", client.Email)
  2205. } else {
  2206. logger.Debug("Error in adding client on", rt.Name(), ":", err1)
  2207. needRestart = true
  2208. }
  2209. }
  2210. } else {
  2211. for _, client := range clients {
  2212. if len(client.Email) > 0 {
  2213. inboundSvc.AddClientStat(tx, data.Id, &client)
  2214. }
  2215. if err1 := rt.AddClient(context.Background(), oldInbound, client); err1 != nil {
  2216. err = err1
  2217. return false, err
  2218. }
  2219. }
  2220. }
  2221. if err = tx.Save(oldInbound).Error; err != nil {
  2222. return false, err
  2223. }
  2224. finalClients, gcErr := inboundSvc.GetClients(oldInbound)
  2225. if gcErr != nil {
  2226. err = gcErr
  2227. return false, err
  2228. }
  2229. if err = s.SyncInbound(tx, oldInbound.Id, finalClients); err != nil {
  2230. return false, err
  2231. }
  2232. return needRestart, nil
  2233. }
  2234. func (s *ClientService) UpdateInboundClient(inboundSvc *InboundService, data *model.Inbound, clientId string) (bool, error) {
  2235. defer lockInbound(data.Id).Unlock()
  2236. clients, err := inboundSvc.GetClients(data)
  2237. if err != nil {
  2238. return false, err
  2239. }
  2240. var settings map[string]any
  2241. err = json.Unmarshal([]byte(data.Settings), &settings)
  2242. if err != nil {
  2243. return false, err
  2244. }
  2245. interfaceClients := settings["clients"].([]any)
  2246. oldInbound, err := inboundSvc.GetInbound(data.Id)
  2247. if err != nil {
  2248. return false, err
  2249. }
  2250. oldClients, err := inboundSvc.GetClients(oldInbound)
  2251. if err != nil {
  2252. return false, err
  2253. }
  2254. oldEmail := ""
  2255. newClientId := ""
  2256. clientIndex := -1
  2257. for index, oldClient := range oldClients {
  2258. oldClientId := ""
  2259. switch oldInbound.Protocol {
  2260. case "trojan":
  2261. oldClientId = oldClient.Password
  2262. newClientId = clients[0].Password
  2263. case "shadowsocks":
  2264. oldClientId = oldClient.Email
  2265. newClientId = clients[0].Email
  2266. case "hysteria":
  2267. oldClientId = oldClient.Auth
  2268. newClientId = clients[0].Auth
  2269. default:
  2270. oldClientId = oldClient.ID
  2271. newClientId = clients[0].ID
  2272. }
  2273. if clientId == oldClientId {
  2274. oldEmail = oldClient.Email
  2275. clientIndex = index
  2276. break
  2277. }
  2278. }
  2279. if clientIndex == -1 {
  2280. var rec model.ClientRecord
  2281. var lookupErr error
  2282. switch oldInbound.Protocol {
  2283. case "trojan":
  2284. lookupErr = database.GetDB().Where("password = ?", clientId).First(&rec).Error
  2285. case "shadowsocks":
  2286. lookupErr = database.GetDB().Where("email = ?", clientId).First(&rec).Error
  2287. case "hysteria":
  2288. lookupErr = database.GetDB().Where("auth = ?", clientId).First(&rec).Error
  2289. default:
  2290. lookupErr = database.GetDB().Where("uuid = ?", clientId).First(&rec).Error
  2291. }
  2292. if lookupErr == nil && rec.Email != "" {
  2293. for index, oldClient := range oldClients {
  2294. if oldClient.Email == rec.Email {
  2295. oldEmail = oldClient.Email
  2296. clientIndex = index
  2297. break
  2298. }
  2299. }
  2300. }
  2301. }
  2302. if newClientId == "" || clientIndex == -1 {
  2303. return false, common.NewError("empty client ID")
  2304. }
  2305. if strings.TrimSpace(clients[0].Email) == "" {
  2306. return false, common.NewError("client email is required")
  2307. }
  2308. if clients[0].Email != oldEmail {
  2309. existEmail, err := s.checkEmailsExistForClients(inboundSvc, clients)
  2310. if err != nil {
  2311. return false, err
  2312. }
  2313. if existEmail != "" {
  2314. return false, common.NewError("Duplicate email:", existEmail)
  2315. }
  2316. }
  2317. var oldSettings map[string]any
  2318. err = json.Unmarshal([]byte(oldInbound.Settings), &oldSettings)
  2319. if err != nil {
  2320. return false, err
  2321. }
  2322. settingsClients := oldSettings["clients"].([]any)
  2323. var preservedCreated any
  2324. if clientIndex >= 0 && clientIndex < len(settingsClients) {
  2325. if oldMap, ok := settingsClients[clientIndex].(map[string]any); ok {
  2326. if v, ok2 := oldMap["created_at"]; ok2 {
  2327. preservedCreated = v
  2328. }
  2329. }
  2330. }
  2331. if len(interfaceClients) > 0 {
  2332. if newMap, ok := interfaceClients[0].(map[string]any); ok {
  2333. if preservedCreated == nil {
  2334. preservedCreated = time.Now().Unix() * 1000
  2335. }
  2336. newMap["created_at"] = preservedCreated
  2337. newMap["updated_at"] = time.Now().Unix() * 1000
  2338. interfaceClients[0] = newMap
  2339. }
  2340. }
  2341. if oldInbound.Protocol == model.Shadowsocks {
  2342. applyShadowsocksClientMethod(interfaceClients, oldSettings)
  2343. }
  2344. settingsClients[clientIndex] = interfaceClients[0]
  2345. oldSettings["clients"] = settingsClients
  2346. if oldInbound.Protocol == model.VLESS {
  2347. hasVisionFlow := false
  2348. for _, c := range settingsClients {
  2349. cm, ok := c.(map[string]any)
  2350. if !ok {
  2351. continue
  2352. }
  2353. if flow, _ := cm["flow"].(string); flow == "xtls-rprx-vision" {
  2354. hasVisionFlow = true
  2355. break
  2356. }
  2357. }
  2358. if !hasVisionFlow {
  2359. delete(oldSettings, "testseed")
  2360. }
  2361. }
  2362. newSettings, err := json.MarshalIndent(oldSettings, "", " ")
  2363. if err != nil {
  2364. return false, err
  2365. }
  2366. oldInbound.Settings = string(newSettings)
  2367. db := database.GetDB()
  2368. tx := db.Begin()
  2369. defer func() {
  2370. if err != nil {
  2371. tx.Rollback()
  2372. } else {
  2373. tx.Commit()
  2374. }
  2375. }()
  2376. if len(clients[0].Email) > 0 {
  2377. if len(oldEmail) > 0 {
  2378. emailUnchanged := strings.EqualFold(oldEmail, clients[0].Email)
  2379. targetExists := int64(0)
  2380. if !emailUnchanged {
  2381. if err = tx.Model(xray.ClientTraffic{}).Where("email = ?", clients[0].Email).Count(&targetExists).Error; err != nil {
  2382. return false, err
  2383. }
  2384. }
  2385. if emailUnchanged || targetExists == 0 {
  2386. err = inboundSvc.UpdateClientStat(tx, oldEmail, &clients[0])
  2387. if err != nil {
  2388. return false, err
  2389. }
  2390. err = inboundSvc.UpdateClientIPs(tx, oldEmail, clients[0].Email)
  2391. if err != nil {
  2392. return false, err
  2393. }
  2394. } else {
  2395. stillUsed, sErr := inboundSvc.emailUsedByOtherInbounds(oldEmail, data.Id)
  2396. if sErr != nil {
  2397. return false, sErr
  2398. }
  2399. if !stillUsed {
  2400. if err = inboundSvc.DelClientStat(tx, oldEmail); err != nil {
  2401. return false, err
  2402. }
  2403. if err = inboundSvc.DelClientIPs(tx, oldEmail); err != nil {
  2404. return false, err
  2405. }
  2406. }
  2407. if err = inboundSvc.UpdateClientStat(tx, clients[0].Email, &clients[0]); err != nil {
  2408. return false, err
  2409. }
  2410. }
  2411. } else {
  2412. inboundSvc.AddClientStat(tx, data.Id, &clients[0])
  2413. }
  2414. } else {
  2415. stillUsed, err := inboundSvc.emailUsedByOtherInbounds(oldEmail, data.Id)
  2416. if err != nil {
  2417. return false, err
  2418. }
  2419. if !stillUsed {
  2420. err = inboundSvc.DelClientStat(tx, oldEmail)
  2421. if err != nil {
  2422. return false, err
  2423. }
  2424. err = inboundSvc.DelClientIPs(tx, oldEmail)
  2425. if err != nil {
  2426. return false, err
  2427. }
  2428. }
  2429. }
  2430. needRestart := false
  2431. if len(oldEmail) > 0 {
  2432. rt, rterr := inboundSvc.runtimeFor(oldInbound)
  2433. if rterr != nil {
  2434. if oldInbound.NodeID != nil {
  2435. err = rterr
  2436. return false, err
  2437. }
  2438. needRestart = true
  2439. } else if oldInbound.NodeID == nil {
  2440. if oldClients[clientIndex].Enable {
  2441. err1 := rt.RemoveUser(context.Background(), oldInbound, oldEmail)
  2442. if err1 == nil {
  2443. logger.Debug("Old client deleted on", rt.Name(), ":", oldEmail)
  2444. } else if strings.Contains(err1.Error(), fmt.Sprintf("User %s not found.", oldEmail)) {
  2445. logger.Debug("User is already deleted. Nothing to do more...")
  2446. } else {
  2447. logger.Debug("Error in deleting client on", rt.Name(), ":", err1)
  2448. needRestart = true
  2449. }
  2450. }
  2451. if clients[0].Enable {
  2452. cipher := ""
  2453. if oldInbound.Protocol == "shadowsocks" {
  2454. cipher = oldSettings["method"].(string)
  2455. }
  2456. err1 := rt.AddUser(context.Background(), oldInbound, map[string]any{
  2457. "email": clients[0].Email,
  2458. "id": clients[0].ID,
  2459. "security": clients[0].Security,
  2460. "flow": clients[0].Flow,
  2461. "auth": clients[0].Auth,
  2462. "password": clients[0].Password,
  2463. "cipher": cipher,
  2464. })
  2465. if err1 == nil {
  2466. logger.Debug("Client edited on", rt.Name(), ":", clients[0].Email)
  2467. } else {
  2468. logger.Debug("Error in adding client on", rt.Name(), ":", err1)
  2469. needRestart = true
  2470. }
  2471. }
  2472. } else {
  2473. if err1 := rt.UpdateUser(context.Background(), oldInbound, oldEmail, clients[0]); err1 != nil {
  2474. err = err1
  2475. return false, err
  2476. }
  2477. }
  2478. } else {
  2479. logger.Debug("Client old email not found")
  2480. needRestart = true
  2481. }
  2482. if err = tx.Save(oldInbound).Error; err != nil {
  2483. return false, err
  2484. }
  2485. finalClients, gcErr := inboundSvc.GetClients(oldInbound)
  2486. if gcErr != nil {
  2487. err = gcErr
  2488. return false, err
  2489. }
  2490. if err = s.SyncInbound(tx, oldInbound.Id, finalClients); err != nil {
  2491. return false, err
  2492. }
  2493. return needRestart, nil
  2494. }
  2495. func (s *ClientService) DelInboundClient(inboundSvc *InboundService, inboundId int, clientId string) (bool, error) {
  2496. defer lockInbound(inboundId).Unlock()
  2497. oldInbound, err := inboundSvc.GetInbound(inboundId)
  2498. if err != nil {
  2499. logger.Error("Load Old Data Error")
  2500. return false, err
  2501. }
  2502. var settings map[string]any
  2503. err = json.Unmarshal([]byte(oldInbound.Settings), &settings)
  2504. if err != nil {
  2505. return false, err
  2506. }
  2507. email := ""
  2508. client_key := "id"
  2509. switch oldInbound.Protocol {
  2510. case "trojan":
  2511. client_key = "password"
  2512. case "shadowsocks":
  2513. client_key = "email"
  2514. case "hysteria":
  2515. client_key = "auth"
  2516. }
  2517. interfaceClients := settings["clients"].([]any)
  2518. var newClients []any
  2519. needApiDel := false
  2520. clientFound := false
  2521. for _, client := range interfaceClients {
  2522. c := client.(map[string]any)
  2523. c_id := c[client_key].(string)
  2524. if c_id == clientId {
  2525. clientFound = true
  2526. email, _ = c["email"].(string)
  2527. needApiDel, _ = c["enable"].(bool)
  2528. } else {
  2529. newClients = append(newClients, client)
  2530. }
  2531. }
  2532. if !clientFound {
  2533. return false, common.NewError("Client Not Found In Inbound For ID:", clientId)
  2534. }
  2535. db := database.GetDB()
  2536. newClients = compactOrphans(db, newClients)
  2537. if newClients == nil {
  2538. newClients = []any{}
  2539. }
  2540. settings["clients"] = newClients
  2541. newSettings, err := json.MarshalIndent(settings, "", " ")
  2542. if err != nil {
  2543. return false, err
  2544. }
  2545. oldInbound.Settings = string(newSettings)
  2546. emailShared, err := inboundSvc.emailUsedByOtherInbounds(email, inboundId)
  2547. if err != nil {
  2548. return false, err
  2549. }
  2550. if !emailShared {
  2551. err = inboundSvc.DelClientIPs(db, email)
  2552. if err != nil {
  2553. logger.Error("Error in delete client IPs")
  2554. return false, err
  2555. }
  2556. }
  2557. needRestart := false
  2558. if len(email) > 0 {
  2559. var enables []bool
  2560. err = db.Model(xray.ClientTraffic{}).Where("email = ?", email).Limit(1).Pluck("enable", &enables).Error
  2561. if err != nil {
  2562. logger.Error("Get stats error")
  2563. return false, err
  2564. }
  2565. notDepleted := len(enables) > 0 && enables[0]
  2566. if !emailShared {
  2567. err = inboundSvc.DelClientStat(db, email)
  2568. if err != nil {
  2569. logger.Error("Delete stats Data Error")
  2570. return false, err
  2571. }
  2572. }
  2573. if needApiDel && notDepleted && oldInbound.NodeID == nil {
  2574. rt, rterr := inboundSvc.runtimeFor(oldInbound)
  2575. if rterr != nil {
  2576. needRestart = true
  2577. } else {
  2578. err1 := rt.RemoveUser(context.Background(), oldInbound, email)
  2579. if err1 == nil {
  2580. logger.Debug("Client deleted on", rt.Name(), ":", email)
  2581. needRestart = false
  2582. } else if strings.Contains(err1.Error(), fmt.Sprintf("User %s not found.", email)) {
  2583. logger.Debug("User is already deleted. Nothing to do more...")
  2584. } else {
  2585. logger.Debug("Error in deleting client on", rt.Name(), ":", err1)
  2586. needRestart = true
  2587. }
  2588. }
  2589. }
  2590. }
  2591. if oldInbound.NodeID != nil && len(email) > 0 {
  2592. rt, rterr := inboundSvc.runtimeFor(oldInbound)
  2593. if rterr != nil {
  2594. return false, rterr
  2595. }
  2596. if err1 := rt.DeleteUser(context.Background(), oldInbound, email); err1 != nil {
  2597. return false, err1
  2598. }
  2599. }
  2600. if err := db.Save(oldInbound).Error; err != nil {
  2601. return false, err
  2602. }
  2603. finalClients, gcErr := inboundSvc.GetClients(oldInbound)
  2604. if gcErr != nil {
  2605. return false, gcErr
  2606. }
  2607. if err := s.SyncInbound(db, inboundId, finalClients); err != nil {
  2608. return false, err
  2609. }
  2610. return needRestart, nil
  2611. }
  2612. func (s *ClientService) DelInboundClientByEmail(inboundSvc *InboundService, inboundId int, email string) (bool, error) {
  2613. defer lockInbound(inboundId).Unlock()
  2614. oldInbound, err := inboundSvc.GetInbound(inboundId)
  2615. if err != nil {
  2616. logger.Error("Load Old Data Error")
  2617. return false, err
  2618. }
  2619. var settings map[string]any
  2620. if err := json.Unmarshal([]byte(oldInbound.Settings), &settings); err != nil {
  2621. return false, err
  2622. }
  2623. interfaceClients, ok := settings["clients"].([]any)
  2624. if !ok {
  2625. return false, common.NewError("invalid clients format in inbound settings")
  2626. }
  2627. var newClients []any
  2628. needApiDel := false
  2629. found := false
  2630. for _, client := range interfaceClients {
  2631. c, ok := client.(map[string]any)
  2632. if !ok {
  2633. continue
  2634. }
  2635. if cEmail, ok := c["email"].(string); ok && cEmail == email {
  2636. found = true
  2637. needApiDel, _ = c["enable"].(bool)
  2638. } else {
  2639. newClients = append(newClients, client)
  2640. }
  2641. }
  2642. if !found {
  2643. return false, common.NewError(fmt.Sprintf("client with email %s not found", email))
  2644. }
  2645. db := database.GetDB()
  2646. newClients = compactOrphans(db, newClients)
  2647. if newClients == nil {
  2648. newClients = []any{}
  2649. }
  2650. settings["clients"] = newClients
  2651. newSettings, err := json.MarshalIndent(settings, "", " ")
  2652. if err != nil {
  2653. return false, err
  2654. }
  2655. oldInbound.Settings = string(newSettings)
  2656. emailShared, err := inboundSvc.emailUsedByOtherInbounds(email, inboundId)
  2657. if err != nil {
  2658. return false, err
  2659. }
  2660. if !emailShared {
  2661. if err := inboundSvc.DelClientIPs(db, email); err != nil {
  2662. logger.Error("Error in delete client IPs")
  2663. return false, err
  2664. }
  2665. }
  2666. needRestart := false
  2667. if len(email) > 0 && !emailShared {
  2668. traffic, err := inboundSvc.GetClientTrafficByEmail(email)
  2669. if err != nil {
  2670. return false, err
  2671. }
  2672. if traffic != nil {
  2673. if err := inboundSvc.DelClientStat(db, email); err != nil {
  2674. logger.Error("Delete stats Data Error")
  2675. return false, err
  2676. }
  2677. }
  2678. if needApiDel {
  2679. rt, rterr := inboundSvc.runtimeFor(oldInbound)
  2680. if rterr != nil {
  2681. if oldInbound.NodeID != nil {
  2682. return false, rterr
  2683. }
  2684. needRestart = true
  2685. } else if oldInbound.NodeID == nil {
  2686. if err1 := rt.RemoveUser(context.Background(), oldInbound, email); err1 == nil {
  2687. logger.Debug("Client deleted on", rt.Name(), ":", email)
  2688. needRestart = false
  2689. } else if strings.Contains(err1.Error(), fmt.Sprintf("User %s not found.", email)) {
  2690. logger.Debug("User is already deleted. Nothing to do more...")
  2691. } else {
  2692. logger.Debug("Error in deleting client on", rt.Name(), ":", err1)
  2693. needRestart = true
  2694. }
  2695. } else {
  2696. if err1 := rt.DeleteUser(context.Background(), oldInbound, email); err1 != nil {
  2697. return false, err1
  2698. }
  2699. }
  2700. }
  2701. }
  2702. if err := db.Save(oldInbound).Error; err != nil {
  2703. return false, err
  2704. }
  2705. finalClients, gcErr := inboundSvc.GetClients(oldInbound)
  2706. if gcErr != nil {
  2707. return false, gcErr
  2708. }
  2709. if err := s.SyncInbound(db, inboundId, finalClients); err != nil {
  2710. return false, err
  2711. }
  2712. return needRestart, nil
  2713. }
  2714. func (s *ClientService) SetClientTelegramUserID(inboundSvc *InboundService, trafficId int, tgId int64) (bool, error) {
  2715. traffic, inbound, err := inboundSvc.GetClientInboundByTrafficID(trafficId)
  2716. if err != nil {
  2717. return false, err
  2718. }
  2719. if inbound == nil {
  2720. return false, common.NewError("Inbound Not Found For Traffic ID:", trafficId)
  2721. }
  2722. clientEmail := traffic.Email
  2723. oldClients, err := inboundSvc.GetClients(inbound)
  2724. if err != nil {
  2725. return false, err
  2726. }
  2727. clientId := ""
  2728. for _, oldClient := range oldClients {
  2729. if oldClient.Email == clientEmail {
  2730. switch inbound.Protocol {
  2731. case "trojan":
  2732. clientId = oldClient.Password
  2733. case "shadowsocks":
  2734. clientId = oldClient.Email
  2735. default:
  2736. clientId = oldClient.ID
  2737. }
  2738. break
  2739. }
  2740. }
  2741. if len(clientId) == 0 {
  2742. return false, common.NewError("Client Not Found For Email:", clientEmail)
  2743. }
  2744. var settings map[string]any
  2745. err = json.Unmarshal([]byte(inbound.Settings), &settings)
  2746. if err != nil {
  2747. return false, err
  2748. }
  2749. clients := settings["clients"].([]any)
  2750. var newClients []any
  2751. for client_index := range clients {
  2752. c := clients[client_index].(map[string]any)
  2753. if c["email"] == clientEmail {
  2754. c["tgId"] = tgId
  2755. c["updated_at"] = time.Now().Unix() * 1000
  2756. newClients = append(newClients, any(c))
  2757. }
  2758. }
  2759. settings["clients"] = newClients
  2760. modifiedSettings, err := json.MarshalIndent(settings, "", " ")
  2761. if err != nil {
  2762. return false, err
  2763. }
  2764. inbound.Settings = string(modifiedSettings)
  2765. needRestart, err := s.UpdateInboundClient(inboundSvc, inbound, clientId)
  2766. return needRestart, err
  2767. }
  2768. func (s *ClientService) checkIsEnabledByEmail(inboundSvc *InboundService, clientEmail string) (bool, error) {
  2769. _, inbound, err := inboundSvc.GetClientInboundByEmail(clientEmail)
  2770. if err != nil {
  2771. return false, err
  2772. }
  2773. if inbound == nil {
  2774. return false, common.NewError("Inbound Not Found For Email:", clientEmail)
  2775. }
  2776. clients, err := inboundSvc.GetClients(inbound)
  2777. if err != nil {
  2778. return false, err
  2779. }
  2780. isEnable := false
  2781. for _, client := range clients {
  2782. if client.Email == clientEmail {
  2783. isEnable = client.Enable
  2784. break
  2785. }
  2786. }
  2787. return isEnable, err
  2788. }
  2789. func (s *ClientService) ToggleClientEnableByEmail(inboundSvc *InboundService, clientEmail string) (bool, bool, error) {
  2790. _, inbound, err := inboundSvc.GetClientInboundByEmail(clientEmail)
  2791. if err != nil {
  2792. return false, false, err
  2793. }
  2794. if inbound == nil {
  2795. return false, false, common.NewError("Inbound Not Found For Email:", clientEmail)
  2796. }
  2797. oldClients, err := inboundSvc.GetClients(inbound)
  2798. if err != nil {
  2799. return false, false, err
  2800. }
  2801. clientId := ""
  2802. clientOldEnabled := false
  2803. for _, oldClient := range oldClients {
  2804. if oldClient.Email == clientEmail {
  2805. switch inbound.Protocol {
  2806. case "trojan":
  2807. clientId = oldClient.Password
  2808. case "shadowsocks":
  2809. clientId = oldClient.Email
  2810. default:
  2811. clientId = oldClient.ID
  2812. }
  2813. clientOldEnabled = oldClient.Enable
  2814. break
  2815. }
  2816. }
  2817. if len(clientId) == 0 {
  2818. return false, false, common.NewError("Client Not Found For Email:", clientEmail)
  2819. }
  2820. var settings map[string]any
  2821. err = json.Unmarshal([]byte(inbound.Settings), &settings)
  2822. if err != nil {
  2823. return false, false, err
  2824. }
  2825. clients := settings["clients"].([]any)
  2826. var newClients []any
  2827. for client_index := range clients {
  2828. c := clients[client_index].(map[string]any)
  2829. if c["email"] == clientEmail {
  2830. c["enable"] = !clientOldEnabled
  2831. c["updated_at"] = time.Now().Unix() * 1000
  2832. newClients = append(newClients, any(c))
  2833. }
  2834. }
  2835. settings["clients"] = newClients
  2836. modifiedSettings, err := json.MarshalIndent(settings, "", " ")
  2837. if err != nil {
  2838. return false, false, err
  2839. }
  2840. inbound.Settings = string(modifiedSettings)
  2841. needRestart, err := s.UpdateInboundClient(inboundSvc, inbound, clientId)
  2842. if err != nil {
  2843. return false, needRestart, err
  2844. }
  2845. return !clientOldEnabled, needRestart, nil
  2846. }
  2847. func (s *ClientService) SetClientEnableByEmail(inboundSvc *InboundService, clientEmail string, enable bool) (bool, bool, error) {
  2848. current, err := s.checkIsEnabledByEmail(inboundSvc, clientEmail)
  2849. if err != nil {
  2850. return false, false, err
  2851. }
  2852. if current == enable {
  2853. return false, false, nil
  2854. }
  2855. newEnabled, needRestart, err := s.ToggleClientEnableByEmail(inboundSvc, clientEmail)
  2856. if err != nil {
  2857. return false, needRestart, err
  2858. }
  2859. return newEnabled == enable, needRestart, nil
  2860. }
  2861. func (s *ClientService) ResetClientIpLimitByEmail(inboundSvc *InboundService, clientEmail string, count int) (bool, error) {
  2862. _, inbound, err := inboundSvc.GetClientInboundByEmail(clientEmail)
  2863. if err != nil {
  2864. return false, err
  2865. }
  2866. if inbound == nil {
  2867. return false, common.NewError("Inbound Not Found For Email:", clientEmail)
  2868. }
  2869. oldClients, err := inboundSvc.GetClients(inbound)
  2870. if err != nil {
  2871. return false, err
  2872. }
  2873. clientId := ""
  2874. for _, oldClient := range oldClients {
  2875. if oldClient.Email == clientEmail {
  2876. switch inbound.Protocol {
  2877. case "trojan":
  2878. clientId = oldClient.Password
  2879. case "shadowsocks":
  2880. clientId = oldClient.Email
  2881. default:
  2882. clientId = oldClient.ID
  2883. }
  2884. break
  2885. }
  2886. }
  2887. if len(clientId) == 0 {
  2888. return false, common.NewError("Client Not Found For Email:", clientEmail)
  2889. }
  2890. var settings map[string]any
  2891. err = json.Unmarshal([]byte(inbound.Settings), &settings)
  2892. if err != nil {
  2893. return false, err
  2894. }
  2895. clients := settings["clients"].([]any)
  2896. var newClients []any
  2897. for client_index := range clients {
  2898. c := clients[client_index].(map[string]any)
  2899. if c["email"] == clientEmail {
  2900. c["limitIp"] = count
  2901. c["updated_at"] = time.Now().Unix() * 1000
  2902. newClients = append(newClients, any(c))
  2903. }
  2904. }
  2905. settings["clients"] = newClients
  2906. modifiedSettings, err := json.MarshalIndent(settings, "", " ")
  2907. if err != nil {
  2908. return false, err
  2909. }
  2910. inbound.Settings = string(modifiedSettings)
  2911. needRestart, err := s.UpdateInboundClient(inboundSvc, inbound, clientId)
  2912. return needRestart, err
  2913. }
  2914. func (s *ClientService) ResetClientExpiryTimeByEmail(inboundSvc *InboundService, clientEmail string, expiry_time int64) (bool, error) {
  2915. _, inbound, err := inboundSvc.GetClientInboundByEmail(clientEmail)
  2916. if err != nil {
  2917. return false, err
  2918. }
  2919. if inbound == nil {
  2920. return false, common.NewError("Inbound Not Found For Email:", clientEmail)
  2921. }
  2922. oldClients, err := inboundSvc.GetClients(inbound)
  2923. if err != nil {
  2924. return false, err
  2925. }
  2926. clientId := ""
  2927. for _, oldClient := range oldClients {
  2928. if oldClient.Email == clientEmail {
  2929. switch inbound.Protocol {
  2930. case "trojan":
  2931. clientId = oldClient.Password
  2932. case "shadowsocks":
  2933. clientId = oldClient.Email
  2934. default:
  2935. clientId = oldClient.ID
  2936. }
  2937. break
  2938. }
  2939. }
  2940. if len(clientId) == 0 {
  2941. return false, common.NewError("Client Not Found For Email:", clientEmail)
  2942. }
  2943. var settings map[string]any
  2944. err = json.Unmarshal([]byte(inbound.Settings), &settings)
  2945. if err != nil {
  2946. return false, err
  2947. }
  2948. clients := settings["clients"].([]any)
  2949. var newClients []any
  2950. for client_index := range clients {
  2951. c := clients[client_index].(map[string]any)
  2952. if c["email"] == clientEmail {
  2953. c["expiryTime"] = expiry_time
  2954. c["updated_at"] = time.Now().Unix() * 1000
  2955. newClients = append(newClients, any(c))
  2956. }
  2957. }
  2958. settings["clients"] = newClients
  2959. modifiedSettings, err := json.MarshalIndent(settings, "", " ")
  2960. if err != nil {
  2961. return false, err
  2962. }
  2963. inbound.Settings = string(modifiedSettings)
  2964. needRestart, err := s.UpdateInboundClient(inboundSvc, inbound, clientId)
  2965. return needRestart, err
  2966. }
  2967. func (s *ClientService) ResetClientTrafficLimitByEmail(inboundSvc *InboundService, clientEmail string, totalGB int) (bool, error) {
  2968. if totalGB < 0 {
  2969. return false, common.NewError("totalGB must be >= 0")
  2970. }
  2971. _, inbound, err := inboundSvc.GetClientInboundByEmail(clientEmail)
  2972. if err != nil {
  2973. return false, err
  2974. }
  2975. if inbound == nil {
  2976. return false, common.NewError("Inbound Not Found For Email:", clientEmail)
  2977. }
  2978. oldClients, err := inboundSvc.GetClients(inbound)
  2979. if err != nil {
  2980. return false, err
  2981. }
  2982. clientId := ""
  2983. for _, oldClient := range oldClients {
  2984. if oldClient.Email == clientEmail {
  2985. switch inbound.Protocol {
  2986. case "trojan":
  2987. clientId = oldClient.Password
  2988. case "shadowsocks":
  2989. clientId = oldClient.Email
  2990. default:
  2991. clientId = oldClient.ID
  2992. }
  2993. break
  2994. }
  2995. }
  2996. if len(clientId) == 0 {
  2997. return false, common.NewError("Client Not Found For Email:", clientEmail)
  2998. }
  2999. var settings map[string]any
  3000. err = json.Unmarshal([]byte(inbound.Settings), &settings)
  3001. if err != nil {
  3002. return false, err
  3003. }
  3004. clients := settings["clients"].([]any)
  3005. var newClients []any
  3006. for client_index := range clients {
  3007. c := clients[client_index].(map[string]any)
  3008. if c["email"] == clientEmail {
  3009. c["totalGB"] = totalGB * 1024 * 1024 * 1024
  3010. c["updated_at"] = time.Now().Unix() * 1000
  3011. newClients = append(newClients, any(c))
  3012. }
  3013. }
  3014. settings["clients"] = newClients
  3015. modifiedSettings, err := json.MarshalIndent(settings, "", " ")
  3016. if err != nil {
  3017. return false, err
  3018. }
  3019. inbound.Settings = string(modifiedSettings)
  3020. needRestart, err := s.UpdateInboundClient(inboundSvc, inbound, clientId)
  3021. return needRestart, err
  3022. }