1
0

client.go 121 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659
  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. existing := make(map[string]struct{}, len(emails))
  116. const orphanChunk = 400
  117. for start := 0; start < len(emails); start += orphanChunk {
  118. end := min(start+orphanChunk, len(emails))
  119. var found []string
  120. if err := db.Model(&model.ClientRecord{}).Where("email IN ?", emails[start:end]).Pluck("email", &found).Error; err != nil {
  121. logger.Warning("compactOrphans pluck:", err)
  122. return clients
  123. }
  124. for _, e := range found {
  125. existing[e] = struct{}{}
  126. }
  127. }
  128. if len(existing) == len(emails) {
  129. return clients
  130. }
  131. out := make([]any, 0, len(existing))
  132. for _, c := range clients {
  133. cm, ok := c.(map[string]any)
  134. if !ok {
  135. out = append(out, c)
  136. continue
  137. }
  138. e, _ := cm["email"].(string)
  139. if e == "" {
  140. out = append(out, c)
  141. continue
  142. }
  143. if _, ok := existing[e]; ok {
  144. out = append(out, c)
  145. }
  146. }
  147. return out
  148. }
  149. func tombstoneClientEmail(email string) {
  150. if email == "" {
  151. return
  152. }
  153. recentlyDeletedMu.Lock()
  154. defer recentlyDeletedMu.Unlock()
  155. recentlyDeleted[email] = time.Now()
  156. cutoff := time.Now().Add(-deleteTombstoneTTL)
  157. for e, ts := range recentlyDeleted {
  158. if ts.Before(cutoff) {
  159. delete(recentlyDeleted, e)
  160. }
  161. }
  162. }
  163. func tombstoneClientEmails(emails []string) {
  164. if len(emails) == 0 {
  165. return
  166. }
  167. now := time.Now()
  168. cutoff := now.Add(-deleteTombstoneTTL)
  169. recentlyDeletedMu.Lock()
  170. defer recentlyDeletedMu.Unlock()
  171. for _, email := range emails {
  172. if email != "" {
  173. recentlyDeleted[email] = now
  174. }
  175. }
  176. for e, ts := range recentlyDeleted {
  177. if ts.Before(cutoff) {
  178. delete(recentlyDeleted, e)
  179. }
  180. }
  181. }
  182. func isClientEmailTombstoned(email string) bool {
  183. if email == "" {
  184. return false
  185. }
  186. recentlyDeletedMu.Lock()
  187. defer recentlyDeletedMu.Unlock()
  188. ts, ok := recentlyDeleted[email]
  189. if !ok {
  190. return false
  191. }
  192. if time.Since(ts) > deleteTombstoneTTL {
  193. delete(recentlyDeleted, email)
  194. return false
  195. }
  196. return true
  197. }
  198. func (s *ClientService) SyncInbound(tx *gorm.DB, inboundId int, clients []model.Client) error {
  199. if tx == nil {
  200. tx = database.GetDB()
  201. }
  202. if err := tx.Where("inbound_id = ?", inboundId).Delete(&model.ClientInbound{}).Error; err != nil {
  203. return err
  204. }
  205. emails := make([]string, 0, len(clients))
  206. seen := make(map[string]struct{}, len(clients))
  207. for i := range clients {
  208. email := strings.TrimSpace(clients[i].Email)
  209. if email == "" {
  210. continue
  211. }
  212. if _, ok := seen[email]; ok {
  213. continue
  214. }
  215. seen[email] = struct{}{}
  216. emails = append(emails, email)
  217. }
  218. existing := make(map[string]*model.ClientRecord, len(emails))
  219. const selectChunk = 400
  220. for start := 0; start < len(emails); start += selectChunk {
  221. end := min(start+selectChunk, len(emails))
  222. var rows []model.ClientRecord
  223. if err := tx.Where("email IN ?", emails[start:end]).Find(&rows).Error; err != nil {
  224. return err
  225. }
  226. for i := range rows {
  227. r := rows[i]
  228. existing[r.Email] = &r
  229. }
  230. }
  231. idByEmail := make(map[string]int, len(emails))
  232. pending := make(map[string]*model.ClientRecord, len(emails))
  233. toCreate := make([]*model.ClientRecord, 0, len(emails))
  234. for i := range clients {
  235. email := strings.TrimSpace(clients[i].Email)
  236. if email == "" {
  237. continue
  238. }
  239. incoming := clients[i].ToRecord()
  240. row, ok := existing[email]
  241. if !ok {
  242. if _, dup := pending[email]; !dup {
  243. pending[email] = incoming
  244. toCreate = append(toCreate, incoming)
  245. }
  246. continue
  247. }
  248. before := *row
  249. if incoming.UUID != "" {
  250. row.UUID = incoming.UUID
  251. }
  252. if incoming.Password != "" {
  253. row.Password = incoming.Password
  254. }
  255. if incoming.Auth != "" {
  256. row.Auth = incoming.Auth
  257. }
  258. row.Flow = incoming.Flow
  259. if incoming.Security != "" {
  260. row.Security = incoming.Security
  261. }
  262. if incoming.Reverse != "" {
  263. row.Reverse = incoming.Reverse
  264. }
  265. row.SubID = incoming.SubID
  266. row.LimitIP = incoming.LimitIP
  267. row.TotalGB = incoming.TotalGB
  268. row.ExpiryTime = incoming.ExpiryTime
  269. row.Enable = incoming.Enable
  270. row.TgID = incoming.TgID
  271. if incoming.Group != "" {
  272. row.Group = incoming.Group
  273. }
  274. row.Comment = incoming.Comment
  275. row.Reset = incoming.Reset
  276. if incoming.CreatedAt > 0 && (row.CreatedAt == 0 || incoming.CreatedAt < row.CreatedAt) {
  277. row.CreatedAt = incoming.CreatedAt
  278. }
  279. preservedUpdatedAt := max(incoming.UpdatedAt, row.UpdatedAt)
  280. row.UpdatedAt = preservedUpdatedAt
  281. idByEmail[email] = row.Id
  282. if *row == before {
  283. continue
  284. }
  285. if err := tx.Save(row).Error; err != nil {
  286. return err
  287. }
  288. if err := tx.Model(&model.ClientRecord{}).
  289. Where("id = ?", row.Id).
  290. UpdateColumn("updated_at", preservedUpdatedAt).Error; err != nil {
  291. return err
  292. }
  293. }
  294. if len(toCreate) > 0 {
  295. if err := tx.CreateInBatches(toCreate, 200).Error; err != nil {
  296. return err
  297. }
  298. for _, rec := range toCreate {
  299. idByEmail[rec.Email] = rec.Id
  300. }
  301. }
  302. links := make([]model.ClientInbound, 0, len(clients))
  303. linked := make(map[int]struct{}, len(clients))
  304. for i := range clients {
  305. email := strings.TrimSpace(clients[i].Email)
  306. if email == "" {
  307. continue
  308. }
  309. id, ok := idByEmail[email]
  310. if !ok {
  311. continue
  312. }
  313. if _, dup := linked[id]; dup {
  314. continue
  315. }
  316. linked[id] = struct{}{}
  317. links = append(links, model.ClientInbound{
  318. ClientId: id,
  319. InboundId: inboundId,
  320. FlowOverride: clients[i].Flow,
  321. })
  322. }
  323. if len(links) > 0 {
  324. if err := tx.CreateInBatches(links, 200).Error; err != nil {
  325. return err
  326. }
  327. }
  328. return nil
  329. }
  330. func (s *ClientService) DetachInbound(tx *gorm.DB, inboundId int) error {
  331. if tx == nil {
  332. tx = database.GetDB()
  333. }
  334. return tx.Where("inbound_id = ?", inboundId).Delete(&model.ClientInbound{}).Error
  335. }
  336. func (s *ClientService) ListForInbound(tx *gorm.DB, inboundId int) ([]model.Client, error) {
  337. if tx == nil {
  338. tx = database.GetDB()
  339. }
  340. type joinedRow struct {
  341. model.ClientRecord
  342. FlowOverride string
  343. }
  344. var rows []joinedRow
  345. err := tx.Table("clients").
  346. Select("clients.*, client_inbounds.flow_override AS flow_override").
  347. Joins("JOIN client_inbounds ON client_inbounds.client_id = clients.id").
  348. Where("client_inbounds.inbound_id = ?", inboundId).
  349. Order("clients.id ASC").
  350. Find(&rows).Error
  351. if err != nil {
  352. return nil, err
  353. }
  354. out := make([]model.Client, 0, len(rows))
  355. for i := range rows {
  356. c := rows[i].ToClient()
  357. c.Flow = rows[i].FlowOverride
  358. out = append(out, *c)
  359. }
  360. return out, nil
  361. }
  362. func (s *ClientService) GetRecordByEmail(tx *gorm.DB, email string) (*model.ClientRecord, error) {
  363. if tx == nil {
  364. tx = database.GetDB()
  365. }
  366. row := &model.ClientRecord{}
  367. err := tx.Where("email = ?", email).First(row).Error
  368. if err != nil {
  369. return nil, err
  370. }
  371. return row, nil
  372. }
  373. // EffectiveFlow returns the client's flow from the first flow-capable inbound
  374. // it is attached to (lowest inbound_id with a non-empty flow_override). The
  375. // canonical clients.Flow column is unreliable for multi-inbound clients: a
  376. // non-flow inbound (Hysteria, WS, gRPC, …) carries an empty flow and, when its
  377. // SyncInbound runs last, overwrites the column to "" even though a VLESS Reality
  378. // inbound stored a real flow. The per-inbound flow_override is always correct,
  379. // so derive the display flow from it (order-independent). See issue #4792.
  380. func (s *ClientService) EffectiveFlow(tx *gorm.DB, recordId int) (string, error) {
  381. if tx == nil {
  382. tx = database.GetDB()
  383. }
  384. var flows []string
  385. err := tx.Model(&model.ClientInbound{}).
  386. Where("client_id = ? AND flow_override <> ?", recordId, "").
  387. Order("inbound_id ASC").
  388. Limit(1).
  389. Pluck("flow_override", &flows).Error
  390. if err != nil {
  391. return "", err
  392. }
  393. if len(flows) == 0 {
  394. return "", nil
  395. }
  396. return flows[0], nil
  397. }
  398. func (s *ClientService) GetInboundIdsForEmail(tx *gorm.DB, email string) ([]int, error) {
  399. if tx == nil {
  400. tx = database.GetDB()
  401. }
  402. var ids []int
  403. err := tx.Table("client_inbounds").
  404. Select("client_inbounds.inbound_id").
  405. Joins("JOIN clients ON clients.id = client_inbounds.client_id").
  406. Where("clients.email = ?", email).
  407. Scan(&ids).Error
  408. if err != nil {
  409. return nil, err
  410. }
  411. return ids, nil
  412. }
  413. func (s *ClientService) GetByID(id int) (*model.ClientRecord, error) {
  414. row := &model.ClientRecord{}
  415. if err := database.GetDB().Where("id = ?", id).First(row).Error; err != nil {
  416. return nil, err
  417. }
  418. return row, nil
  419. }
  420. func (s *ClientService) GetInboundIdsForRecord(id int) ([]int, error) {
  421. var ids []int
  422. err := database.GetDB().Table("client_inbounds").
  423. Where("client_id = ?", id).
  424. Order("inbound_id ASC").
  425. Pluck("inbound_id", &ids).Error
  426. if err != nil {
  427. return nil, err
  428. }
  429. return ids, nil
  430. }
  431. func (s *ClientService) List() ([]ClientWithAttachments, error) {
  432. db := database.GetDB()
  433. var rows []model.ClientRecord
  434. if err := db.Order("id ASC").Find(&rows).Error; err != nil {
  435. return nil, err
  436. }
  437. if len(rows) == 0 {
  438. return []ClientWithAttachments{}, nil
  439. }
  440. clientIds := make([]int, 0, len(rows))
  441. emails := make([]string, 0, len(rows))
  442. for i := range rows {
  443. clientIds = append(clientIds, rows[i].Id)
  444. if rows[i].Email != "" {
  445. emails = append(emails, rows[i].Email)
  446. }
  447. }
  448. attachments := make(map[int][]int, len(rows))
  449. for _, batch := range chunkInts(clientIds, sqlInChunk) {
  450. var links []model.ClientInbound
  451. if err := db.Where("client_id IN ?", batch).Find(&links).Error; err != nil {
  452. return nil, err
  453. }
  454. for _, l := range links {
  455. attachments[l.ClientId] = append(attachments[l.ClientId], l.InboundId)
  456. }
  457. }
  458. trafficByEmail := make(map[string]*xray.ClientTraffic, len(emails))
  459. if len(emails) > 0 {
  460. var stats []xray.ClientTraffic
  461. for _, batch := range chunkStrings(emails, sqlInChunk) {
  462. var batchStats []xray.ClientTraffic
  463. if err := db.Where("email IN ?", batch).Find(&batchStats).Error; err != nil {
  464. return nil, err
  465. }
  466. stats = append(stats, batchStats...)
  467. }
  468. for i := range stats {
  469. trafficByEmail[stats[i].Email] = &stats[i]
  470. }
  471. }
  472. out := make([]ClientWithAttachments, 0, len(rows))
  473. for i := range rows {
  474. out = append(out, ClientWithAttachments{
  475. ClientRecord: rows[i],
  476. InboundIds: attachments[rows[i].Id],
  477. Traffic: trafficByEmail[rows[i].Email],
  478. })
  479. }
  480. return out, nil
  481. }
  482. type ClientCreatePayload struct {
  483. Client model.Client `json:"client"`
  484. InboundIds []int `json:"inboundIds"`
  485. }
  486. func hasForbiddenClientChar(s string) bool {
  487. for _, r := range s {
  488. if r == '/' || r == '\\' || r == ' ' || r < 0x20 || r == 0x7f {
  489. return true
  490. }
  491. }
  492. return false
  493. }
  494. func validateClientEmail(email string) error {
  495. if hasForbiddenClientChar(email) {
  496. return common.NewError("client email contains an invalid character:", email)
  497. }
  498. return nil
  499. }
  500. func validateClientSubID(subID string) error {
  501. if hasForbiddenClientChar(subID) {
  502. return common.NewError("client subId contains an invalid character:", subID)
  503. }
  504. return nil
  505. }
  506. func (s *ClientService) HasPendingNode(inboundSvc *InboundService, email string) bool {
  507. if strings.TrimSpace(email) == "" {
  508. return false
  509. }
  510. ids, err := s.GetInboundIdsForEmail(nil, email)
  511. if err != nil {
  512. return false
  513. }
  514. return inboundSvc.AnyNodePending(ids)
  515. }
  516. func (s *ClientService) Create(inboundSvc *InboundService, payload *ClientCreatePayload) (bool, error) {
  517. if payload == nil {
  518. return false, common.NewError("empty payload")
  519. }
  520. client := payload.Client
  521. if strings.TrimSpace(client.Email) == "" {
  522. return false, common.NewError("client email is required")
  523. }
  524. if err := validateClientEmail(client.Email); err != nil {
  525. return false, err
  526. }
  527. if err := validateClientSubID(client.SubID); err != nil {
  528. return false, err
  529. }
  530. if len(payload.InboundIds) == 0 {
  531. return false, common.NewError("at least one inbound is required")
  532. }
  533. if client.SubID == "" {
  534. client.SubID = uuid.NewString()
  535. }
  536. if !client.Enable {
  537. client.Enable = true
  538. }
  539. now := time.Now().UnixMilli()
  540. if client.CreatedAt == 0 {
  541. client.CreatedAt = now
  542. }
  543. client.UpdatedAt = now
  544. existing := &model.ClientRecord{}
  545. err := database.GetDB().Where("email = ?", client.Email).First(existing).Error
  546. if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
  547. return false, err
  548. }
  549. emailTaken := !errors.Is(err, gorm.ErrRecordNotFound)
  550. if emailTaken {
  551. if existing.SubID == "" || existing.SubID != client.SubID {
  552. return false, common.NewError("email already in use:", client.Email)
  553. }
  554. }
  555. if client.SubID != "" {
  556. var subTaken int64
  557. if err := database.GetDB().Model(&model.ClientRecord{}).
  558. Where("sub_id = ? AND email <> ?", client.SubID, client.Email).
  559. Count(&subTaken).Error; err != nil {
  560. return false, err
  561. }
  562. if subTaken > 0 {
  563. return false, common.NewError("subId already in use:", client.SubID)
  564. }
  565. }
  566. needRestart := false
  567. for _, ibId := range payload.InboundIds {
  568. inbound, getErr := inboundSvc.GetInbound(ibId)
  569. if getErr != nil {
  570. return needRestart, getErr
  571. }
  572. if err := s.fillProtocolDefaults(&client, inbound); err != nil {
  573. return needRestart, err
  574. }
  575. settingsPayload, mErr := json.Marshal(map[string][]model.Client{"clients": {clientWithInboundFlow(client, inbound)}})
  576. if mErr != nil {
  577. return needRestart, mErr
  578. }
  579. nr, addErr := s.AddInboundClient(inboundSvc, &model.Inbound{
  580. Id: ibId,
  581. Settings: string(settingsPayload),
  582. })
  583. if addErr != nil {
  584. return needRestart, addErr
  585. }
  586. if nr {
  587. needRestart = true
  588. }
  589. }
  590. return needRestart, nil
  591. }
  592. func (s *ClientService) fillProtocolDefaults(c *model.Client, ib *model.Inbound) error {
  593. switch ib.Protocol {
  594. case model.VMESS, model.VLESS:
  595. if c.ID == "" {
  596. c.ID = uuid.NewString()
  597. }
  598. case model.Trojan:
  599. if c.Password == "" {
  600. c.Password = strings.ReplaceAll(uuid.NewString(), "-", "")
  601. }
  602. case model.Shadowsocks:
  603. method := shadowsocksMethodFromSettings(ib.Settings)
  604. if c.Password == "" || !validShadowsocksClientKey(method, c.Password) {
  605. c.Password = randomShadowsocksClientKey(method)
  606. }
  607. case model.Hysteria:
  608. if c.Auth == "" {
  609. c.Auth = strings.ReplaceAll(uuid.NewString(), "-", "")
  610. }
  611. }
  612. return nil
  613. }
  614. func clientWithInboundFlow(c model.Client, ib *model.Inbound) model.Client {
  615. if !inboundCanEnableTlsFlow(string(ib.Protocol), ib.StreamSettings) {
  616. c.Flow = ""
  617. }
  618. return c
  619. }
  620. func shadowsocksMethodFromSettings(settings string) string {
  621. if settings == "" {
  622. return ""
  623. }
  624. var m map[string]any
  625. if err := json.Unmarshal([]byte(settings), &m); err != nil {
  626. return ""
  627. }
  628. method, _ := m["method"].(string)
  629. return method
  630. }
  631. func randomShadowsocksClientKey(method string) string {
  632. if n := shadowsocksKeyBytes(method); n > 0 {
  633. return random.Base64Bytes(n)
  634. }
  635. return strings.ReplaceAll(uuid.NewString(), "-", "")
  636. }
  637. func validShadowsocksClientKey(method, key string) bool {
  638. n := shadowsocksKeyBytes(method)
  639. if n == 0 {
  640. return key != ""
  641. }
  642. decoded, err := base64.StdEncoding.DecodeString(key)
  643. if err != nil {
  644. return false
  645. }
  646. return len(decoded) == n
  647. }
  648. func shadowsocksKeyBytes(method string) int {
  649. switch method {
  650. case "2022-blake3-aes-128-gcm":
  651. return 16
  652. case "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305":
  653. return 32
  654. }
  655. return 0
  656. }
  657. func applyShadowsocksClientMethod(clients []any, settings map[string]any) {
  658. method, _ := settings["method"].(string)
  659. is2022 := strings.HasPrefix(method, "2022-blake3-")
  660. for i := range clients {
  661. cm, ok := clients[i].(map[string]any)
  662. if !ok {
  663. continue
  664. }
  665. if is2022 {
  666. if _, hasKey := cm["method"]; hasKey {
  667. delete(cm, "method")
  668. clients[i] = cm
  669. }
  670. continue
  671. }
  672. if method == "" {
  673. continue
  674. }
  675. if existing, _ := cm["method"].(string); existing != "" {
  676. continue
  677. }
  678. cm["method"] = method
  679. clients[i] = cm
  680. }
  681. }
  682. func (s *ClientService) Update(inboundSvc *InboundService, id int, updated model.Client, inboundFilter ...int) (bool, error) {
  683. existing, err := s.GetByID(id)
  684. if err != nil {
  685. return false, err
  686. }
  687. inboundIds, err := s.GetInboundIdsForRecord(id)
  688. if err != nil {
  689. return false, err
  690. }
  691. if len(inboundFilter) > 0 {
  692. allow := make(map[int]struct{}, len(inboundFilter))
  693. for _, fid := range inboundFilter {
  694. allow[fid] = struct{}{}
  695. }
  696. filtered := inboundIds[:0:0]
  697. for _, ibId := range inboundIds {
  698. if _, ok := allow[ibId]; ok {
  699. filtered = append(filtered, ibId)
  700. }
  701. }
  702. inboundIds = filtered
  703. }
  704. if strings.TrimSpace(updated.Email) == "" {
  705. return false, common.NewError("client email is required")
  706. }
  707. if err := validateClientEmail(updated.Email); err != nil {
  708. return false, err
  709. }
  710. if err := validateClientSubID(updated.SubID); err != nil {
  711. return false, err
  712. }
  713. if updated.SubID == "" {
  714. updated.SubID = existing.SubID
  715. }
  716. if updated.SubID == "" {
  717. updated.SubID = uuid.NewString()
  718. }
  719. updated.UpdatedAt = time.Now().UnixMilli()
  720. if updated.CreatedAt == 0 {
  721. updated.CreatedAt = existing.CreatedAt
  722. }
  723. if updated.Email != existing.Email {
  724. var collisionCount int64
  725. if err := database.GetDB().Model(&model.ClientRecord{}).
  726. Where("email = ? AND id <> ?", updated.Email, id).
  727. Count(&collisionCount).Error; err != nil {
  728. return false, err
  729. }
  730. if collisionCount > 0 {
  731. return false, common.NewError("Duplicate email:", updated.Email)
  732. }
  733. if err := database.GetDB().Model(&model.ClientRecord{}).
  734. Where("id = ?", id).
  735. Update("email", updated.Email).Error; err != nil {
  736. return false, err
  737. }
  738. }
  739. if updated.SubID != "" {
  740. var subCollision int64
  741. if err := database.GetDB().Model(&model.ClientRecord{}).
  742. Where("sub_id = ? AND id <> ?", updated.SubID, id).
  743. Count(&subCollision).Error; err != nil {
  744. return false, err
  745. }
  746. if subCollision > 0 {
  747. return false, common.NewError("Duplicate subId:", updated.SubID)
  748. }
  749. }
  750. needRestart := false
  751. for _, ibId := range inboundIds {
  752. inbound, getErr := inboundSvc.GetInbound(ibId)
  753. if getErr != nil {
  754. if errors.Is(getErr, gorm.ErrRecordNotFound) {
  755. if err := database.GetDB().
  756. Where("client_id = ? AND inbound_id = ?", id, ibId).
  757. Delete(&model.ClientInbound{}).Error; err != nil {
  758. return needRestart, err
  759. }
  760. continue
  761. }
  762. return needRestart, getErr
  763. }
  764. oldKey := clientKeyForProtocol(inbound.Protocol, existing)
  765. if oldKey == "" {
  766. continue
  767. }
  768. if err := s.fillProtocolDefaults(&updated, inbound); err != nil {
  769. return needRestart, err
  770. }
  771. settingsPayload, mErr := json.Marshal(map[string][]model.Client{"clients": {clientWithInboundFlow(updated, inbound)}})
  772. if mErr != nil {
  773. return needRestart, mErr
  774. }
  775. nr, upErr := s.UpdateInboundClient(inboundSvc, &model.Inbound{
  776. Id: ibId,
  777. Settings: string(settingsPayload),
  778. }, oldKey)
  779. if upErr != nil {
  780. return needRestart, upErr
  781. }
  782. if nr {
  783. needRestart = true
  784. }
  785. }
  786. reverseStr := ""
  787. if updated.Reverse != nil && strings.TrimSpace(updated.Reverse.Tag) != "" {
  788. if b, mErr := json.Marshal(updated.Reverse); mErr == nil {
  789. reverseStr = string(b)
  790. }
  791. }
  792. if err := database.GetDB().Model(&model.ClientRecord{}).
  793. Where("id = ?", id).
  794. Update("reverse", reverseStr).Error; err != nil {
  795. return needRestart, err
  796. }
  797. if err := database.GetDB().Model(&model.ClientRecord{}).
  798. Where("id = ?", id).
  799. UpdateColumn("updated_at", time.Now().UnixMilli()).Error; err != nil {
  800. return needRestart, err
  801. }
  802. return needRestart, nil
  803. }
  804. func (s *ClientService) Delete(inboundSvc *InboundService, id int, keepTraffic bool) (bool, error) {
  805. existing, err := s.GetByID(id)
  806. if err != nil {
  807. return false, err
  808. }
  809. tombstoneClientEmail(existing.Email)
  810. inboundIds, err := s.GetInboundIdsForRecord(id)
  811. if err != nil {
  812. return false, err
  813. }
  814. needRestart := false
  815. for _, ibId := range inboundIds {
  816. inbound, getErr := inboundSvc.GetInbound(ibId)
  817. if getErr != nil {
  818. if errors.Is(getErr, gorm.ErrRecordNotFound) {
  819. continue
  820. }
  821. return needRestart, getErr
  822. }
  823. key := clientKeyForProtocol(inbound.Protocol, existing)
  824. if key == "" {
  825. continue
  826. }
  827. nr, delErr := s.DelInboundClient(inboundSvc, ibId, key, false)
  828. if delErr != nil {
  829. return needRestart, delErr
  830. }
  831. if nr {
  832. needRestart = true
  833. }
  834. }
  835. db := database.GetDB()
  836. if err := db.Where("client_id = ?", id).Delete(&model.ClientInbound{}).Error; err != nil {
  837. return needRestart, err
  838. }
  839. if !keepTraffic && existing.Email != "" {
  840. if err := db.Where("email = ?", existing.Email).Delete(&xray.ClientTraffic{}).Error; err != nil {
  841. return needRestart, err
  842. }
  843. if err := db.Where("client_email = ?", existing.Email).Delete(&model.InboundClientIps{}).Error; err != nil {
  844. return needRestart, err
  845. }
  846. }
  847. if err := db.Delete(&model.ClientRecord{}, id).Error; err != nil {
  848. return needRestart, err
  849. }
  850. return needRestart, nil
  851. }
  852. func (s *ClientService) Attach(inboundSvc *InboundService, id int, inboundIds []int) (bool, error) {
  853. existing, err := s.GetByID(id)
  854. if err != nil {
  855. return false, err
  856. }
  857. currentIds, err := s.GetInboundIdsForRecord(id)
  858. if err != nil {
  859. return false, err
  860. }
  861. have := make(map[int]struct{}, len(currentIds))
  862. for _, x := range currentIds {
  863. have[x] = struct{}{}
  864. }
  865. clientWire := existing.ToClient()
  866. flow, ffErr := s.EffectiveFlow(nil, id)
  867. if ffErr != nil {
  868. return false, ffErr
  869. }
  870. clientWire.Flow = flow
  871. clientWire.UpdatedAt = time.Now().UnixMilli()
  872. needRestart := false
  873. for _, ibId := range inboundIds {
  874. if _, attached := have[ibId]; attached {
  875. continue
  876. }
  877. inbound, getErr := inboundSvc.GetInbound(ibId)
  878. if getErr != nil {
  879. return needRestart, getErr
  880. }
  881. copyClient := *clientWire
  882. if err := s.fillProtocolDefaults(&copyClient, inbound); err != nil {
  883. return needRestart, err
  884. }
  885. settingsPayload, mErr := json.Marshal(map[string][]model.Client{"clients": {clientWithInboundFlow(copyClient, inbound)}})
  886. if mErr != nil {
  887. return needRestart, mErr
  888. }
  889. nr, addErr := s.AddInboundClient(inboundSvc, &model.Inbound{
  890. Id: ibId,
  891. Settings: string(settingsPayload),
  892. })
  893. if addErr != nil {
  894. return needRestart, addErr
  895. }
  896. if nr {
  897. needRestart = true
  898. }
  899. }
  900. return needRestart, nil
  901. }
  902. func (s *ClientService) CreateOne(inboundSvc *InboundService, inboundId int, client model.Client) (bool, error) {
  903. return s.Create(inboundSvc, &ClientCreatePayload{
  904. Client: client,
  905. InboundIds: []int{inboundId},
  906. })
  907. }
  908. func (s *ClientService) DetachByEmail(inboundSvc *InboundService, inboundId int, email string) (bool, error) {
  909. if email == "" {
  910. return false, common.NewError("client email is required")
  911. }
  912. rec, err := s.GetRecordByEmail(nil, email)
  913. if err != nil {
  914. return false, err
  915. }
  916. return s.Detach(inboundSvc, rec.Id, []int{inboundId})
  917. }
  918. func (s *ClientService) AttachByEmail(inboundSvc *InboundService, email string, inboundIds []int) (bool, error) {
  919. if email == "" {
  920. return false, common.NewError("client email is required")
  921. }
  922. rec, err := s.GetRecordByEmail(nil, email)
  923. if err != nil {
  924. return false, err
  925. }
  926. return s.Attach(inboundSvc, rec.Id, inboundIds)
  927. }
  928. // BulkAttachResult reports the outcome of a bulk attach across target inbounds.
  929. type BulkAttachResult struct {
  930. Attached []string `json:"attached"`
  931. Skipped []string `json:"skipped"`
  932. Errors []string `json:"errors"`
  933. }
  934. // BulkAttach attaches the given existing clients (by email) to each target inbound,
  935. // reusing their identity (email/UUID/password/subId) and a shared traffic row. It adds
  936. // all clients to a target in a single AddInboundClient call, and reports clients already
  937. // present on a target as skipped.
  938. func (s *ClientService) BulkAttach(inboundSvc *InboundService, emails []string, inboundIds []int) (*BulkAttachResult, bool, error) {
  939. result := &BulkAttachResult{}
  940. if len(emails) == 0 || len(inboundIds) == 0 {
  941. return result, false, nil
  942. }
  943. recordErr := func(format string, args ...any) {
  944. msg := fmt.Sprintf(format, args...)
  945. result.Errors = append(result.Errors, msg)
  946. logger.Warningf("[BulkAttach] %s", msg)
  947. }
  948. records := make([]*model.ClientRecord, 0, len(emails))
  949. seenEmail := make(map[string]struct{}, len(emails))
  950. for _, email := range emails {
  951. if email == "" {
  952. continue
  953. }
  954. key := strings.ToLower(email)
  955. if _, ok := seenEmail[key]; ok {
  956. continue
  957. }
  958. seenEmail[key] = struct{}{}
  959. rec, err := s.GetRecordByEmail(nil, email)
  960. if err != nil {
  961. recordErr("%s: %v", email, err)
  962. continue
  963. }
  964. records = append(records, rec)
  965. }
  966. emailSubIDs, sidErr := inboundSvc.getAllEmailSubIDs()
  967. if sidErr != nil {
  968. emailSubIDs = nil
  969. logger.Warningf("[BulkAttach] getAllEmailSubIDs: %v", sidErr)
  970. }
  971. needRestart := false
  972. for _, ibId := range inboundIds {
  973. inbound, err := inboundSvc.GetInbound(ibId)
  974. if err != nil {
  975. recordErr("inbound %d: %v", ibId, err)
  976. continue
  977. }
  978. existingClients, err := inboundSvc.GetClients(inbound)
  979. if err != nil {
  980. recordErr("inbound %d: %v", ibId, err)
  981. continue
  982. }
  983. have := make(map[string]struct{}, len(existingClients))
  984. for _, c := range existingClients {
  985. have[strings.ToLower(c.Email)] = struct{}{}
  986. }
  987. clientsToAdd := make([]model.Client, 0, len(records))
  988. for _, rec := range records {
  989. if _, attached := have[strings.ToLower(rec.Email)]; attached {
  990. result.Skipped = append(result.Skipped, rec.Email)
  991. continue
  992. }
  993. client := *rec.ToClient()
  994. client.UpdatedAt = time.Now().UnixMilli()
  995. if err := s.fillProtocolDefaults(&client, inbound); err != nil {
  996. recordErr("%s -> inbound %d: %v", rec.Email, ibId, err)
  997. continue
  998. }
  999. clientsToAdd = append(clientsToAdd, clientWithInboundFlow(client, inbound))
  1000. }
  1001. if len(clientsToAdd) == 0 {
  1002. continue
  1003. }
  1004. payload, err := json.Marshal(map[string][]model.Client{"clients": clientsToAdd})
  1005. if err != nil {
  1006. recordErr("inbound %d: %v", ibId, err)
  1007. continue
  1008. }
  1009. nr, err := s.addInboundClient(inboundSvc, &model.Inbound{Id: ibId, Settings: string(payload)}, emailSubIDs)
  1010. if err != nil {
  1011. recordErr("inbound %d: %v", ibId, err)
  1012. continue
  1013. }
  1014. if nr {
  1015. needRestart = true
  1016. }
  1017. for _, c := range clientsToAdd {
  1018. result.Attached = append(result.Attached, c.Email)
  1019. }
  1020. }
  1021. return result, needRestart, nil
  1022. }
  1023. // BulkDetachResult reports the outcome of a bulk detach across target inbounds.
  1024. type BulkDetachResult struct {
  1025. Detached []string `json:"detached"`
  1026. Skipped []string `json:"skipped"`
  1027. Errors []string `json:"errors"`
  1028. }
  1029. // BulkDetach detaches the given existing clients (by email) from each target inbound.
  1030. // (email, inbound) pairs where the client is not currently attached are silently skipped
  1031. // at the inbound level; emails that aren't attached to any of the requested inbounds
  1032. // are reported under skipped. ClientRecord rows are kept even when they become orphaned
  1033. // (matches single-client detach semantics); callers should use bulkDelete for full removal.
  1034. func (s *ClientService) BulkDetach(inboundSvc *InboundService, emails []string, inboundIds []int) (*BulkDetachResult, bool, error) {
  1035. result := &BulkDetachResult{}
  1036. if len(emails) == 0 || len(inboundIds) == 0 {
  1037. return result, false, nil
  1038. }
  1039. recordErr := func(format string, args ...any) {
  1040. msg := fmt.Sprintf(format, args...)
  1041. result.Errors = append(result.Errors, msg)
  1042. logger.Warningf("[BulkDetach] %s", msg)
  1043. }
  1044. requested := make(map[int]struct{}, len(inboundIds))
  1045. for _, id := range inboundIds {
  1046. requested[id] = struct{}{}
  1047. }
  1048. recsByInbound := make(map[int][]*model.ClientRecord)
  1049. emailOrder := make([]string, 0, len(emails))
  1050. emailRepr := make(map[string]string, len(emails))
  1051. emailFailed := make(map[string]bool, len(emails))
  1052. seenEmail := make(map[string]struct{}, len(emails))
  1053. for _, email := range emails {
  1054. if email == "" {
  1055. continue
  1056. }
  1057. key := strings.ToLower(email)
  1058. if _, ok := seenEmail[key]; ok {
  1059. continue
  1060. }
  1061. seenEmail[key] = struct{}{}
  1062. rec, err := s.GetRecordByEmail(nil, email)
  1063. if err != nil {
  1064. recordErr("%s: %v", email, err)
  1065. continue
  1066. }
  1067. currentIds, err := s.GetInboundIdsForRecord(rec.Id)
  1068. if err != nil {
  1069. recordErr("%s: %v", email, err)
  1070. continue
  1071. }
  1072. matched := false
  1073. for _, id := range currentIds {
  1074. if _, ok := requested[id]; ok {
  1075. recsByInbound[id] = append(recsByInbound[id], rec)
  1076. matched = true
  1077. }
  1078. }
  1079. if !matched {
  1080. result.Skipped = append(result.Skipped, rec.Email)
  1081. continue
  1082. }
  1083. emailOrder = append(emailOrder, key)
  1084. emailRepr[key] = rec.Email
  1085. }
  1086. needRestart := false
  1087. for _, ibId := range inboundIds {
  1088. recs, ok := recsByInbound[ibId]
  1089. if !ok {
  1090. continue
  1091. }
  1092. delete(recsByInbound, ibId)
  1093. nr, err := s.delInboundClients(inboundSvc, ibId, recs, true)
  1094. if err != nil {
  1095. recordErr("inbound %d: %v", ibId, err)
  1096. for _, rec := range recs {
  1097. emailFailed[strings.ToLower(rec.Email)] = true
  1098. }
  1099. continue
  1100. }
  1101. if nr {
  1102. needRestart = true
  1103. }
  1104. }
  1105. for _, key := range emailOrder {
  1106. if emailFailed[key] {
  1107. continue
  1108. }
  1109. result.Detached = append(result.Detached, emailRepr[key])
  1110. }
  1111. return result, needRestart, nil
  1112. }
  1113. // delInboundClients removes several clients from a single inbound in one pass:
  1114. // one settings rewrite, one runtime sweep, one Save and one SyncInbound for the
  1115. // whole batch, instead of repeating the full per-client cycle. It mirrors the
  1116. // semantics of DelInboundClient for each removed client. needRestart is the OR
  1117. // across all removals.
  1118. func (s *ClientService) delInboundClients(inboundSvc *InboundService, inboundId int, recs []*model.ClientRecord, keepTraffic bool) (bool, error) {
  1119. if len(recs) == 0 {
  1120. return false, nil
  1121. }
  1122. defer lockInbound(inboundId).Unlock()
  1123. oldInbound, err := inboundSvc.GetInbound(inboundId)
  1124. if err != nil {
  1125. logger.Error("Load Old Data Error")
  1126. return false, err
  1127. }
  1128. var settings map[string]any
  1129. if err := json.Unmarshal([]byte(oldInbound.Settings), &settings); err != nil {
  1130. return false, err
  1131. }
  1132. clientKey := "id"
  1133. switch oldInbound.Protocol {
  1134. case "trojan":
  1135. clientKey = "password"
  1136. case "shadowsocks":
  1137. clientKey = "email"
  1138. case "hysteria":
  1139. clientKey = "auth"
  1140. }
  1141. wanted := make(map[string]struct{}, len(recs))
  1142. for _, rec := range recs {
  1143. if k := clientKeyForProtocol(oldInbound.Protocol, rec); k != "" {
  1144. wanted[k] = struct{}{}
  1145. }
  1146. }
  1147. interfaceClients, ok := settings["clients"].([]any)
  1148. if !ok {
  1149. return false, common.NewError("invalid clients format in inbound settings")
  1150. }
  1151. type removedClient struct {
  1152. email string
  1153. needApiDel bool
  1154. }
  1155. removed := make([]removedClient, 0, len(wanted))
  1156. newClients := make([]any, 0, len(interfaceClients))
  1157. for _, client := range interfaceClients {
  1158. c, ok := client.(map[string]any)
  1159. if !ok {
  1160. newClients = append(newClients, client)
  1161. continue
  1162. }
  1163. cid, _ := c[clientKey].(string)
  1164. if _, hit := wanted[cid]; hit && cid != "" {
  1165. email, _ := c["email"].(string)
  1166. enable, _ := c["enable"].(bool)
  1167. removed = append(removed, removedClient{email: email, needApiDel: enable})
  1168. continue
  1169. }
  1170. newClients = append(newClients, client)
  1171. }
  1172. if len(removed) == 0 {
  1173. return false, nil
  1174. }
  1175. db := database.GetDB()
  1176. newClients = compactOrphans(db, newClients)
  1177. if newClients == nil {
  1178. newClients = []any{}
  1179. }
  1180. settings["clients"] = newClients
  1181. newSettings, err := json.MarshalIndent(settings, "", " ")
  1182. if err != nil {
  1183. return false, err
  1184. }
  1185. oldInbound.Settings = string(newSettings)
  1186. var sharedSet map[string]bool
  1187. if !keepTraffic {
  1188. removedEmails := make([]string, 0, len(removed))
  1189. for _, r := range removed {
  1190. if r.email != "" {
  1191. removedEmails = append(removedEmails, r.email)
  1192. }
  1193. }
  1194. var sharedErr error
  1195. sharedSet, sharedErr = inboundSvc.emailsUsedByOtherInbounds(removedEmails, inboundId)
  1196. if sharedErr != nil {
  1197. return false, sharedErr
  1198. }
  1199. }
  1200. needRestart := false
  1201. markDirty := false
  1202. for _, r := range removed {
  1203. email := r.email
  1204. emailShared := sharedSet[strings.ToLower(strings.TrimSpace(email))]
  1205. if !emailShared && !keepTraffic {
  1206. if err := inboundSvc.DelClientIPs(db, email); err != nil {
  1207. logger.Error("Error in delete client IPs")
  1208. return needRestart, err
  1209. }
  1210. }
  1211. if len(email) > 0 {
  1212. var enables []bool
  1213. if err := db.Model(xray.ClientTraffic{}).Where("email = ?", email).Limit(1).Pluck("enable", &enables).Error; err != nil {
  1214. logger.Error("Get stats error")
  1215. return needRestart, err
  1216. }
  1217. notDepleted := len(enables) > 0 && enables[0]
  1218. if !emailShared && !keepTraffic {
  1219. if err := inboundSvc.DelClientStat(db, email); err != nil {
  1220. logger.Error("Delete stats Data Error")
  1221. return needRestart, err
  1222. }
  1223. }
  1224. if r.needApiDel && notDepleted && oldInbound.NodeID == nil {
  1225. rt, rterr := inboundSvc.runtimeFor(oldInbound)
  1226. if rterr != nil {
  1227. needRestart = true
  1228. } else if err1 := rt.RemoveUser(context.Background(), oldInbound, email); err1 != nil {
  1229. if !strings.Contains(err1.Error(), fmt.Sprintf("User %s not found.", email)) {
  1230. needRestart = true
  1231. }
  1232. }
  1233. }
  1234. }
  1235. if oldInbound.NodeID != nil && len(email) > 0 {
  1236. rt, push, dirty, perr := inboundSvc.nodePushPlan(oldInbound)
  1237. if perr != nil {
  1238. return needRestart, perr
  1239. }
  1240. if dirty {
  1241. markDirty = true
  1242. }
  1243. if push {
  1244. if err1 := rt.DeleteUser(context.Background(), oldInbound, email); err1 != nil {
  1245. logger.Warning("Error in deleting client on", rt.Name(), ":", err1)
  1246. markDirty = true
  1247. }
  1248. }
  1249. }
  1250. }
  1251. if err := db.Save(oldInbound).Error; err != nil {
  1252. return needRestart, err
  1253. }
  1254. finalClients, gcErr := inboundSvc.GetClients(oldInbound)
  1255. if gcErr != nil {
  1256. return needRestart, gcErr
  1257. }
  1258. if err := s.SyncInbound(db, inboundId, finalClients); err != nil {
  1259. return needRestart, err
  1260. }
  1261. if markDirty && oldInbound.NodeID != nil {
  1262. if dErr := (&NodeService{}).MarkNodeDirty(*oldInbound.NodeID); dErr != nil {
  1263. logger.Warning("mark node dirty failed:", dErr)
  1264. }
  1265. }
  1266. return needRestart, nil
  1267. }
  1268. func (s *ClientService) DetachByEmailMany(inboundSvc *InboundService, email string, inboundIds []int) (bool, error) {
  1269. if email == "" {
  1270. return false, common.NewError("client email is required")
  1271. }
  1272. rec, err := s.GetRecordByEmail(nil, email)
  1273. if err != nil {
  1274. return false, err
  1275. }
  1276. return s.Detach(inboundSvc, rec.Id, inboundIds)
  1277. }
  1278. func (s *ClientService) DeleteByEmail(inboundSvc *InboundService, email string, keepTraffic bool) (bool, error) {
  1279. if email == "" {
  1280. return false, common.NewError("client email is required")
  1281. }
  1282. rec, err := s.GetRecordByEmail(nil, email)
  1283. if err == nil {
  1284. return s.Delete(inboundSvc, rec.Id, keepTraffic)
  1285. }
  1286. if !errors.Is(err, gorm.ErrRecordNotFound) {
  1287. return false, err
  1288. }
  1289. inboundIds, idsErr := s.findInboundIdsByClientEmail(email)
  1290. if idsErr != nil {
  1291. return false, idsErr
  1292. }
  1293. if len(inboundIds) == 0 {
  1294. return false, common.NewError(fmt.Sprintf("client %q not found in any inbound or client record", email))
  1295. }
  1296. needRestart := false
  1297. for _, ibId := range inboundIds {
  1298. nr, delErr := s.DelInboundClientByEmail(inboundSvc, ibId, email, false)
  1299. if delErr != nil {
  1300. return needRestart, delErr
  1301. }
  1302. if nr {
  1303. needRestart = true
  1304. }
  1305. }
  1306. if !keepTraffic {
  1307. db := database.GetDB()
  1308. if err := db.Where("email = ?", email).Delete(&xray.ClientTraffic{}).Error; err != nil {
  1309. return needRestart, err
  1310. }
  1311. if err := db.Where("client_email = ?", email).Delete(&model.InboundClientIps{}).Error; err != nil {
  1312. return needRestart, err
  1313. }
  1314. }
  1315. return needRestart, nil
  1316. }
  1317. // findInboundIdsByClientEmail returns every inbound whose settings.clients[]
  1318. // JSON contains an entry with the given email. Driver-portable (no JSON
  1319. // operators) by parsing in Go — fine for the rare fallback path.
  1320. func (s *ClientService) findInboundIdsByClientEmail(email string) ([]int, error) {
  1321. var inbounds []model.Inbound
  1322. if err := database.GetDB().
  1323. Select("id, settings").
  1324. Where("settings LIKE ?", "%"+email+"%").
  1325. Find(&inbounds).Error; err != nil {
  1326. return nil, err
  1327. }
  1328. out := make([]int, 0, len(inbounds))
  1329. for _, ib := range inbounds {
  1330. var settings map[string]any
  1331. if err := json.Unmarshal([]byte(ib.Settings), &settings); err != nil {
  1332. continue
  1333. }
  1334. clients, ok := settings["clients"].([]any)
  1335. if !ok {
  1336. continue
  1337. }
  1338. for _, c := range clients {
  1339. cm, ok := c.(map[string]any)
  1340. if !ok {
  1341. continue
  1342. }
  1343. if cEmail, _ := cm["email"].(string); cEmail == email {
  1344. out = append(out, ib.Id)
  1345. break
  1346. }
  1347. }
  1348. }
  1349. return out, nil
  1350. }
  1351. func (s *ClientService) UpdateByEmail(inboundSvc *InboundService, email string, updated model.Client, inboundFilter ...int) (bool, error) {
  1352. if email == "" {
  1353. return false, common.NewError("client email is required")
  1354. }
  1355. rec, err := s.GetRecordByEmail(nil, email)
  1356. if err != nil {
  1357. return false, err
  1358. }
  1359. return s.Update(inboundSvc, rec.Id, updated, inboundFilter...)
  1360. }
  1361. func (s *ClientService) ResetTrafficByEmail(inboundSvc *InboundService, email string) (bool, error) {
  1362. if email == "" {
  1363. return false, common.NewError("client email is required")
  1364. }
  1365. rec, err := s.GetRecordByEmail(nil, email)
  1366. if err != nil {
  1367. return false, err
  1368. }
  1369. inboundIds, err := s.GetInboundIdsForRecord(rec.Id)
  1370. if err != nil {
  1371. return false, err
  1372. }
  1373. if len(inboundIds) == 0 {
  1374. if rErr := inboundSvc.ResetClientTrafficByEmail(email); rErr != nil {
  1375. return false, rErr
  1376. }
  1377. return false, nil
  1378. }
  1379. needRestart := false
  1380. for _, ibId := range inboundIds {
  1381. nr, rErr := inboundSvc.ResetClientTraffic(ibId, email)
  1382. if rErr != nil {
  1383. return needRestart, rErr
  1384. }
  1385. if nr {
  1386. needRestart = true
  1387. }
  1388. }
  1389. return needRestart, nil
  1390. }
  1391. // ClientSlim is the row-shape used by the clients page. It drops fields the
  1392. // table never reads (UUID, password, auth, flow, security, reverse, tgId)
  1393. // so the list payload stays compact even when the panel manages thousands
  1394. // of clients. Modals that need the full record still call /get/:email.
  1395. type ClientSlim struct {
  1396. Email string `json:"email"`
  1397. SubID string `json:"subId"`
  1398. Enable bool `json:"enable"`
  1399. TotalGB int64 `json:"totalGB"`
  1400. ExpiryTime int64 `json:"expiryTime"`
  1401. LimitIP int `json:"limitIp"`
  1402. Reset int `json:"reset"`
  1403. Group string `json:"group,omitempty"`
  1404. Comment string `json:"comment,omitempty"`
  1405. InboundIds []int `json:"inboundIds"`
  1406. Traffic *xray.ClientTraffic `json:"traffic,omitempty"`
  1407. CreatedAt int64 `json:"createdAt"`
  1408. UpdatedAt int64 `json:"updatedAt"`
  1409. }
  1410. // ClientPageParams are the query params accepted by /panel/api/clients/list/paged.
  1411. // All fields are optional — the empty value means "no filter" / defaults.
  1412. //
  1413. // Filter / Protocol / Inbound accept either a single value or a comma-separated
  1414. // list; matching is OR within a field and AND across fields. The numeric range
  1415. // fields treat 0 as "unset" on the lower bound and 0 (or negative) as
  1416. // "unbounded" on the upper bound.
  1417. type ClientPageParams struct {
  1418. Page int `form:"page"`
  1419. PageSize int `form:"pageSize"`
  1420. Search string `form:"search"`
  1421. Filter string `form:"filter"`
  1422. Protocol string `form:"protocol"`
  1423. Inbound string `form:"inbound"`
  1424. Sort string `form:"sort"`
  1425. Order string `form:"order"`
  1426. ExpiryFrom int64 `form:"expiryFrom"`
  1427. ExpiryTo int64 `form:"expiryTo"`
  1428. UsageFrom int64 `form:"usageFrom"`
  1429. UsageTo int64 `form:"usageTo"`
  1430. AutoRenew string `form:"autoRenew"`
  1431. HasTgID string `form:"hasTgId"`
  1432. HasComment string `form:"hasComment"`
  1433. Group string `form:"group"`
  1434. }
  1435. // ClientPageResponse is the shape returned by ListPaged. `Total` is the
  1436. // row count in the DB; `Filtered` is the count after Search/Filter/Protocol
  1437. // were applied, before pagination. The page contains at most PageSize items.
  1438. // Summary is computed across the full DB row set so dashboard counters
  1439. // on the clients page stay stable as the user paginates/filters.
  1440. type ClientPageResponse struct {
  1441. Items []ClientSlim `json:"items"`
  1442. Total int `json:"total"`
  1443. Filtered int `json:"filtered"`
  1444. Page int `json:"page"`
  1445. PageSize int `json:"pageSize"`
  1446. Summary ClientsSummary `json:"summary"`
  1447. Groups []string `json:"groups"`
  1448. }
  1449. // ClientsSummary collects per-bucket counts plus the matching email lists so
  1450. // the clients page can render the dashboard stat cards and their hover
  1451. // popovers without shipping the full client array.
  1452. type ClientsSummary struct {
  1453. Total int `json:"total"`
  1454. Active int `json:"active"`
  1455. Online []string `json:"online"`
  1456. Depleted []string `json:"depleted"`
  1457. Expiring []string `json:"expiring"`
  1458. Deactive []string `json:"deactive"`
  1459. }
  1460. const (
  1461. clientPageDefaultSize = 25
  1462. clientPageMaxSize = 200
  1463. )
  1464. // ListPaged loads every client (with traffic + attachments) into memory,
  1465. // applies the requested filter / search / protocol predicates, sorts, and
  1466. // returns the requested page along with total and filtered counts. The DB
  1467. // query itself is unchanged from List(); the win is that the response
  1468. // only carries 25-ish slim rows over the wire instead of all 2000 full
  1469. // records, which on real panels was the dominant cost.
  1470. func (s *ClientService) ListPaged(inboundSvc *InboundService, settingSvc *SettingService, params ClientPageParams) (*ClientPageResponse, error) {
  1471. all, err := s.List()
  1472. if err != nil {
  1473. return nil, err
  1474. }
  1475. total := len(all)
  1476. pageSize := params.PageSize
  1477. if pageSize <= 0 {
  1478. pageSize = clientPageDefaultSize
  1479. }
  1480. if pageSize > clientPageMaxSize {
  1481. pageSize = clientPageMaxSize
  1482. }
  1483. page := params.Page
  1484. if page <= 0 {
  1485. page = 1
  1486. }
  1487. protocols := parseCSVStrings(params.Protocol)
  1488. inboundIDs := parseCSVInts(params.Inbound)
  1489. buckets := parseCSVStrings(params.Filter)
  1490. var protocolByInbound map[int]string
  1491. if len(protocols) > 0 {
  1492. inbounds, err := inboundSvc.GetAllInbounds()
  1493. if err == nil {
  1494. protocolByInbound = make(map[int]string, len(inbounds))
  1495. for _, ib := range inbounds {
  1496. protocolByInbound[ib.Id] = string(ib.Protocol)
  1497. }
  1498. }
  1499. }
  1500. onlines := inboundSvc.GetOnlineClients()
  1501. onlineSet := make(map[string]struct{}, len(onlines))
  1502. for _, e := range onlines {
  1503. onlineSet[e] = struct{}{}
  1504. }
  1505. var expireDiffMs, trafficDiffBytes int64
  1506. if settingSvc != nil {
  1507. if v, err := settingSvc.GetExpireDiff(); err == nil {
  1508. expireDiffMs = int64(v) * 86400000
  1509. }
  1510. if v, err := settingSvc.GetTrafficDiff(); err == nil {
  1511. trafficDiffBytes = int64(v) * 1073741824
  1512. }
  1513. }
  1514. nowMs := time.Now().UnixMilli()
  1515. summary := buildClientsSummary(all, onlineSet, nowMs, expireDiffMs, trafficDiffBytes)
  1516. needle := strings.ToLower(strings.TrimSpace(params.Search))
  1517. filtered := make([]ClientWithAttachments, 0, len(all))
  1518. for _, c := range all {
  1519. if needle != "" && !clientMatchesSearch(c, needle) {
  1520. continue
  1521. }
  1522. if len(protocols) > 0 && !clientMatchesAnyProtocol(c, protocols, protocolByInbound) {
  1523. continue
  1524. }
  1525. if len(inboundIDs) > 0 && !clientMatchesAnyInbound(c, inboundIDs) {
  1526. continue
  1527. }
  1528. if len(buckets) > 0 && !clientMatchesAnyBucket(c, buckets, onlineSet, nowMs, expireDiffMs, trafficDiffBytes) {
  1529. continue
  1530. }
  1531. if !clientMatchesExpiryRange(c, params.ExpiryFrom, params.ExpiryTo) {
  1532. continue
  1533. }
  1534. if !clientMatchesUsageRange(c, params.UsageFrom, params.UsageTo) {
  1535. continue
  1536. }
  1537. if !clientMatchesAutoRenew(c, params.AutoRenew) {
  1538. continue
  1539. }
  1540. if !clientMatchesHasTgID(c, params.HasTgID) {
  1541. continue
  1542. }
  1543. if !clientMatchesHasComment(c, params.HasComment) {
  1544. continue
  1545. }
  1546. if !clientMatchesAnyGroup(c, params.Group) {
  1547. continue
  1548. }
  1549. filtered = append(filtered, c)
  1550. }
  1551. sortClients(filtered, params.Sort, params.Order)
  1552. filteredCount := len(filtered)
  1553. start := (page - 1) * pageSize
  1554. end := start + pageSize
  1555. if start > filteredCount {
  1556. start = filteredCount
  1557. }
  1558. if end > filteredCount {
  1559. end = filteredCount
  1560. }
  1561. pageRows := filtered[start:end]
  1562. items := make([]ClientSlim, 0, len(pageRows))
  1563. for _, c := range pageRows {
  1564. items = append(items, toClientSlim(c))
  1565. }
  1566. groupRows, gErr := s.ListGroups()
  1567. if gErr != nil {
  1568. return nil, gErr
  1569. }
  1570. groups := make([]string, 0, len(groupRows))
  1571. for _, g := range groupRows {
  1572. groups = append(groups, g.Name)
  1573. }
  1574. return &ClientPageResponse{
  1575. Items: items,
  1576. Total: total,
  1577. Filtered: filteredCount,
  1578. Page: page,
  1579. PageSize: pageSize,
  1580. Summary: summary,
  1581. Groups: groups,
  1582. }, nil
  1583. }
  1584. type GroupSummary struct {
  1585. Name string `json:"name"`
  1586. ClientCount int `json:"clientCount"`
  1587. TrafficUsed int64 `json:"trafficUsed"`
  1588. }
  1589. func (s *ClientService) ListGroups() ([]GroupSummary, error) {
  1590. db := database.GetDB()
  1591. // email is unique in both clients and client_traffics, so the LEFT JOIN
  1592. // never double-counts a client's traffic.
  1593. var derived []GroupSummary
  1594. if err := db.Table("clients AS c").
  1595. Select("c.group_name AS name, COUNT(*) AS client_count, COALESCE(SUM(ct.up + ct.down), 0) AS traffic_used").
  1596. Joins("LEFT JOIN client_traffics ct ON ct.email = c.email").
  1597. Where("c.group_name <> ''").
  1598. Group("c.group_name").
  1599. Scan(&derived).Error; err != nil {
  1600. return nil, err
  1601. }
  1602. var stored []model.ClientGroup
  1603. if err := db.Find(&stored).Error; err != nil {
  1604. return nil, err
  1605. }
  1606. type groupAgg struct {
  1607. count int
  1608. traffic int64
  1609. }
  1610. merged := make(map[string]groupAgg, len(derived)+len(stored))
  1611. for _, g := range stored {
  1612. merged[g.Name] = groupAgg{}
  1613. }
  1614. for _, g := range derived {
  1615. merged[g.Name] = groupAgg{count: g.ClientCount, traffic: g.TrafficUsed}
  1616. }
  1617. out := make([]GroupSummary, 0, len(merged))
  1618. for name, agg := range merged {
  1619. out = append(out, GroupSummary{Name: name, ClientCount: agg.count, TrafficUsed: agg.traffic})
  1620. }
  1621. sort.Slice(out, func(i, j int) bool {
  1622. return strings.ToLower(out[i].Name) < strings.ToLower(out[j].Name)
  1623. })
  1624. return out, nil
  1625. }
  1626. func (s *ClientService) EmailsByGroup(name string) ([]string, error) {
  1627. name = strings.TrimSpace(name)
  1628. if name == "" {
  1629. return []string{}, nil
  1630. }
  1631. db := database.GetDB()
  1632. var emails []string
  1633. if err := db.Model(&model.ClientRecord{}).
  1634. Where("group_name = ?", name).
  1635. Order("email ASC").
  1636. Pluck("email", &emails).Error; err != nil {
  1637. return nil, err
  1638. }
  1639. if emails == nil {
  1640. emails = []string{}
  1641. }
  1642. return emails, nil
  1643. }
  1644. func (s *ClientService) BulkResetTraffic(inboundSvc *InboundService, emails []string) (int, error) {
  1645. if len(emails) == 0 {
  1646. return 0, nil
  1647. }
  1648. seen := map[string]struct{}{}
  1649. cleanEmails := make([]string, 0, len(emails))
  1650. for _, e := range emails {
  1651. e = strings.TrimSpace(e)
  1652. if e == "" {
  1653. continue
  1654. }
  1655. if _, ok := seen[e]; ok {
  1656. continue
  1657. }
  1658. seen[e] = struct{}{}
  1659. cleanEmails = append(cleanEmails, e)
  1660. }
  1661. if len(cleanEmails) == 0 {
  1662. return 0, nil
  1663. }
  1664. affected := 0
  1665. err := submitTrafficWrite(func() error {
  1666. db := database.GetDB()
  1667. return db.Transaction(func(tx *gorm.DB) error {
  1668. for _, batch := range chunkStrings(cleanEmails, sqlInChunk) {
  1669. res := tx.Model(xray.ClientTraffic{}).
  1670. Where("email IN ?", batch).
  1671. Updates(map[string]any{"enable": true, "up": 0, "down": 0})
  1672. if res.Error != nil {
  1673. return res.Error
  1674. }
  1675. affected += int(res.RowsAffected)
  1676. }
  1677. return nil
  1678. })
  1679. })
  1680. if err != nil {
  1681. return 0, err
  1682. }
  1683. return affected, nil
  1684. }
  1685. func (s *ClientService) CreateGroup(name string) error {
  1686. name = strings.TrimSpace(name)
  1687. if name == "" {
  1688. return common.NewError("group name is required")
  1689. }
  1690. db := database.GetDB()
  1691. var count int64
  1692. if err := db.Model(&model.ClientGroup{}).Where("name = ?", name).Count(&count).Error; err != nil {
  1693. return err
  1694. }
  1695. if count > 0 {
  1696. return common.NewError("group already exists")
  1697. }
  1698. return db.Create(&model.ClientGroup{Name: name}).Error
  1699. }
  1700. func (s *ClientService) RenameGroup(oldName, newName string) (int, error) {
  1701. oldName = strings.TrimSpace(oldName)
  1702. newName = strings.TrimSpace(newName)
  1703. if oldName == "" {
  1704. return 0, common.NewError("old group name is required")
  1705. }
  1706. if newName == "" {
  1707. return 0, common.NewError("new group name is required")
  1708. }
  1709. if oldName == newName {
  1710. return 0, nil
  1711. }
  1712. return s.replaceGroupValue(oldName, newName)
  1713. }
  1714. func (s *ClientService) DeleteGroup(name string) (int, error) {
  1715. name = strings.TrimSpace(name)
  1716. if name == "" {
  1717. return 0, common.NewError("group name is required")
  1718. }
  1719. return s.replaceGroupValue(name, "")
  1720. }
  1721. func (s *ClientService) RemoveFromGroup(emails []string) (int, error) {
  1722. return s.AddToGroup(emails, "")
  1723. }
  1724. func (s *ClientService) AddToGroup(emails []string, group string) (int, error) {
  1725. group = strings.TrimSpace(group)
  1726. if len(emails) == 0 {
  1727. return 0, nil
  1728. }
  1729. db := database.GetDB()
  1730. if group != "" {
  1731. var exists int64
  1732. if err := db.Model(&model.ClientGroup{}).Where("name = ?", group).Count(&exists).Error; err != nil {
  1733. return 0, err
  1734. }
  1735. if exists == 0 {
  1736. var derived int64
  1737. if err := db.Model(&model.ClientRecord{}).Where("group_name = ?", group).Count(&derived).Error; err != nil {
  1738. return 0, err
  1739. }
  1740. if derived == 0 {
  1741. if err := db.Create(&model.ClientGroup{Name: group}).Error; err != nil {
  1742. return 0, err
  1743. }
  1744. }
  1745. }
  1746. }
  1747. var records []model.ClientRecord
  1748. for _, batch := range chunkStrings(emails, sqlInChunk) {
  1749. var rows []model.ClientRecord
  1750. if err := db.Where("email IN ?", batch).Find(&rows).Error; err != nil {
  1751. return 0, err
  1752. }
  1753. records = append(records, rows...)
  1754. }
  1755. if len(records) == 0 {
  1756. return 0, nil
  1757. }
  1758. affectedEmails := make([]string, 0, len(records))
  1759. for _, r := range records {
  1760. affectedEmails = append(affectedEmails, r.Email)
  1761. }
  1762. tx := db.Begin()
  1763. for _, batch := range chunkStrings(affectedEmails, sqlInChunk) {
  1764. if err := tx.Model(&model.ClientRecord{}).
  1765. Where("email IN ?", batch).
  1766. UpdateColumn("group_name", group).Error; err != nil {
  1767. tx.Rollback()
  1768. return 0, err
  1769. }
  1770. }
  1771. var inboundIDs []int
  1772. inboundIDSeen := make(map[int]struct{})
  1773. for _, batch := range chunkStrings(affectedEmails, sqlInChunk) {
  1774. var ids []int
  1775. if err := tx.Table("client_inbounds").
  1776. Joins("JOIN clients ON clients.id = client_inbounds.client_id").
  1777. Where("clients.email IN ?", batch).
  1778. Distinct("client_inbounds.inbound_id").
  1779. Pluck("inbound_id", &ids).Error; err != nil {
  1780. tx.Rollback()
  1781. return 0, err
  1782. }
  1783. for _, id := range ids {
  1784. if _, ok := inboundIDSeen[id]; !ok {
  1785. inboundIDSeen[id] = struct{}{}
  1786. inboundIDs = append(inboundIDs, id)
  1787. }
  1788. }
  1789. }
  1790. emailSet := make(map[string]struct{}, len(affectedEmails))
  1791. for _, e := range affectedEmails {
  1792. emailSet[e] = struct{}{}
  1793. }
  1794. for _, ibID := range inboundIDs {
  1795. var ib model.Inbound
  1796. if err := tx.First(&ib, ibID).Error; err != nil {
  1797. tx.Rollback()
  1798. return 0, err
  1799. }
  1800. var settings map[string]any
  1801. if err := json.Unmarshal([]byte(ib.Settings), &settings); err != nil {
  1802. continue
  1803. }
  1804. clients, ok := settings["clients"].([]any)
  1805. if !ok {
  1806. continue
  1807. }
  1808. modified := false
  1809. for i := range clients {
  1810. cm, ok := clients[i].(map[string]any)
  1811. if !ok {
  1812. continue
  1813. }
  1814. email, _ := cm["email"].(string)
  1815. if _, hit := emailSet[email]; !hit {
  1816. continue
  1817. }
  1818. if group == "" {
  1819. delete(cm, "group")
  1820. } else {
  1821. cm["group"] = group
  1822. }
  1823. clients[i] = cm
  1824. modified = true
  1825. }
  1826. if modified {
  1827. settings["clients"] = clients
  1828. newSettings, err := json.Marshal(settings)
  1829. if err != nil {
  1830. continue
  1831. }
  1832. ib.Settings = string(newSettings)
  1833. if err := tx.Save(&ib).Error; err != nil {
  1834. tx.Rollback()
  1835. return 0, err
  1836. }
  1837. }
  1838. }
  1839. if err := tx.Commit().Error; err != nil {
  1840. return 0, err
  1841. }
  1842. return len(records), nil
  1843. }
  1844. func (s *ClientService) replaceGroupValue(oldName, newName string) (int, error) {
  1845. db := database.GetDB()
  1846. if newName == "" {
  1847. if err := db.Where("name = ?", oldName).Delete(&model.ClientGroup{}).Error; err != nil {
  1848. return 0, err
  1849. }
  1850. } else {
  1851. if err := db.Model(&model.ClientGroup{}).Where("name = ?", oldName).Update("name", newName).Error; err != nil {
  1852. return 0, err
  1853. }
  1854. }
  1855. var records []model.ClientRecord
  1856. if err := db.Where("group_name = ?", oldName).Find(&records).Error; err != nil {
  1857. return 0, err
  1858. }
  1859. if len(records) == 0 {
  1860. return 0, nil
  1861. }
  1862. affectedEmails := make([]string, 0, len(records))
  1863. for _, r := range records {
  1864. affectedEmails = append(affectedEmails, r.Email)
  1865. }
  1866. tx := db.Begin()
  1867. if err := tx.Model(&model.ClientRecord{}).
  1868. Where("group_name = ?", oldName).
  1869. UpdateColumn("group_name", newName).Error; err != nil {
  1870. tx.Rollback()
  1871. return 0, err
  1872. }
  1873. var inboundIDs []int
  1874. inboundIDSeen := make(map[int]struct{})
  1875. for _, batch := range chunkStrings(affectedEmails, sqlInChunk) {
  1876. var ids []int
  1877. if err := tx.Table("client_inbounds").
  1878. Joins("JOIN clients ON clients.id = client_inbounds.client_id").
  1879. Where("clients.email IN ?", batch).
  1880. Distinct("client_inbounds.inbound_id").
  1881. Pluck("inbound_id", &ids).Error; err != nil {
  1882. tx.Rollback()
  1883. return 0, err
  1884. }
  1885. for _, id := range ids {
  1886. if _, ok := inboundIDSeen[id]; !ok {
  1887. inboundIDSeen[id] = struct{}{}
  1888. inboundIDs = append(inboundIDs, id)
  1889. }
  1890. }
  1891. }
  1892. for _, ibID := range inboundIDs {
  1893. var ib model.Inbound
  1894. if err := tx.First(&ib, ibID).Error; err != nil {
  1895. tx.Rollback()
  1896. return 0, err
  1897. }
  1898. var settings map[string]any
  1899. if err := json.Unmarshal([]byte(ib.Settings), &settings); err != nil {
  1900. continue
  1901. }
  1902. clients, ok := settings["clients"].([]any)
  1903. if !ok {
  1904. continue
  1905. }
  1906. modified := false
  1907. for i := range clients {
  1908. cm, ok := clients[i].(map[string]any)
  1909. if !ok {
  1910. continue
  1911. }
  1912. if g, ok := cm["group"].(string); ok && g == oldName {
  1913. if newName == "" {
  1914. delete(cm, "group")
  1915. } else {
  1916. cm["group"] = newName
  1917. }
  1918. clients[i] = cm
  1919. modified = true
  1920. }
  1921. }
  1922. if modified {
  1923. settings["clients"] = clients
  1924. newSettings, err := json.Marshal(settings)
  1925. if err != nil {
  1926. continue
  1927. }
  1928. ib.Settings = string(newSettings)
  1929. if err := tx.Save(&ib).Error; err != nil {
  1930. tx.Rollback()
  1931. return 0, err
  1932. }
  1933. }
  1934. }
  1935. if err := tx.Commit().Error; err != nil {
  1936. return 0, err
  1937. }
  1938. return len(records), nil
  1939. }
  1940. func buildClientsSummary(all []ClientWithAttachments, onlineSet map[string]struct{}, nowMs, expireDiffMs, trafficDiffBytes int64) ClientsSummary {
  1941. s := ClientsSummary{
  1942. Total: len(all),
  1943. Online: []string{},
  1944. Depleted: []string{},
  1945. Expiring: []string{},
  1946. Deactive: []string{},
  1947. }
  1948. for _, c := range all {
  1949. used := int64(0)
  1950. if c.Traffic != nil {
  1951. used = c.Traffic.Up + c.Traffic.Down
  1952. }
  1953. exhausted := c.TotalGB > 0 && used >= c.TotalGB
  1954. expired := c.ExpiryTime > 0 && c.ExpiryTime <= nowMs
  1955. if c.Enable {
  1956. if _, ok := onlineSet[c.Email]; ok {
  1957. s.Online = append(s.Online, c.Email)
  1958. }
  1959. }
  1960. if exhausted || expired {
  1961. s.Depleted = append(s.Depleted, c.Email)
  1962. continue
  1963. }
  1964. if !c.Enable {
  1965. s.Deactive = append(s.Deactive, c.Email)
  1966. continue
  1967. }
  1968. nearExpiry := c.ExpiryTime > 0 && c.ExpiryTime-nowMs < expireDiffMs
  1969. nearLimit := c.TotalGB > 0 && c.TotalGB-used < trafficDiffBytes
  1970. if nearExpiry || nearLimit {
  1971. s.Expiring = append(s.Expiring, c.Email)
  1972. } else {
  1973. s.Active++
  1974. }
  1975. }
  1976. return s
  1977. }
  1978. func toClientSlim(c ClientWithAttachments) ClientSlim {
  1979. return ClientSlim{
  1980. Email: c.Email,
  1981. SubID: c.SubID,
  1982. Enable: c.Enable,
  1983. TotalGB: c.TotalGB,
  1984. ExpiryTime: c.ExpiryTime,
  1985. LimitIP: c.LimitIP,
  1986. Reset: c.Reset,
  1987. Group: c.Group,
  1988. Comment: c.Comment,
  1989. InboundIds: c.InboundIds,
  1990. Traffic: c.Traffic,
  1991. CreatedAt: c.CreatedAt,
  1992. UpdatedAt: c.UpdatedAt,
  1993. }
  1994. }
  1995. func clientMatchesSearch(c ClientWithAttachments, needle string) bool {
  1996. if needle == "" {
  1997. return true
  1998. }
  1999. candidates := [...]string{c.Email, c.SubID, c.Comment, c.UUID, c.Password, c.Auth}
  2000. for _, v := range candidates {
  2001. if v != "" && strings.Contains(strings.ToLower(v), needle) {
  2002. return true
  2003. }
  2004. }
  2005. return false
  2006. }
  2007. // parseCSVStrings splits a comma-separated list, trims/lower-cases each item,
  2008. // and drops blanks. Returns nil when the input has no usable entries — the
  2009. // caller can then skip the predicate entirely.
  2010. func parseCSVStrings(raw string) []string {
  2011. if raw == "" {
  2012. return nil
  2013. }
  2014. parts := strings.Split(raw, ",")
  2015. out := make([]string, 0, len(parts))
  2016. for _, p := range parts {
  2017. s := strings.ToLower(strings.TrimSpace(p))
  2018. if s != "" {
  2019. out = append(out, s)
  2020. }
  2021. }
  2022. if len(out) == 0 {
  2023. return nil
  2024. }
  2025. return out
  2026. }
  2027. // parseCSVInts is parseCSVStrings for positive integer IDs; non-numeric or
  2028. // non-positive entries are silently dropped.
  2029. func parseCSVInts(raw string) []int {
  2030. if raw == "" {
  2031. return nil
  2032. }
  2033. parts := strings.Split(raw, ",")
  2034. out := make([]int, 0, len(parts))
  2035. for _, p := range parts {
  2036. s := strings.TrimSpace(p)
  2037. if s == "" {
  2038. continue
  2039. }
  2040. if n, err := strconv.Atoi(s); err == nil && n > 0 {
  2041. out = append(out, n)
  2042. }
  2043. }
  2044. if len(out) == 0 {
  2045. return nil
  2046. }
  2047. return out
  2048. }
  2049. func clientMatchesAnyProtocol(c ClientWithAttachments, protocols []string, byInbound map[int]string) bool {
  2050. for _, id := range c.InboundIds {
  2051. p := byInbound[id]
  2052. if p == "" {
  2053. continue
  2054. }
  2055. if slices.Contains(protocols, strings.ToLower(p)) {
  2056. return true
  2057. }
  2058. }
  2059. return false
  2060. }
  2061. func clientMatchesAnyInbound(c ClientWithAttachments, inboundIds []int) bool {
  2062. for _, id := range c.InboundIds {
  2063. if slices.Contains(inboundIds, id) {
  2064. return true
  2065. }
  2066. }
  2067. return false
  2068. }
  2069. func clientMatchesAnyBucket(c ClientWithAttachments, buckets []string, onlineSet map[string]struct{}, nowMs, expireDiffMs, trafficDiffBytes int64) bool {
  2070. for _, b := range buckets {
  2071. if clientMatchesBucket(c, b, onlineSet, nowMs, expireDiffMs, trafficDiffBytes) {
  2072. return true
  2073. }
  2074. }
  2075. return false
  2076. }
  2077. func clientMatchesExpiryRange(c ClientWithAttachments, fromMs, toMs int64) bool {
  2078. if fromMs <= 0 && toMs <= 0 {
  2079. return true
  2080. }
  2081. // expiryTime of 0 means "never expires"; treat it as outside any bounded
  2082. // range so users filtering by date see only clients with concrete expiries.
  2083. if c.ExpiryTime == 0 {
  2084. return false
  2085. }
  2086. // Negative expiry is the "delayed start" sentinel; same treatment as never.
  2087. if c.ExpiryTime < 0 {
  2088. return false
  2089. }
  2090. if fromMs > 0 && c.ExpiryTime < fromMs {
  2091. return false
  2092. }
  2093. if toMs > 0 && c.ExpiryTime > toMs {
  2094. return false
  2095. }
  2096. return true
  2097. }
  2098. func clientMatchesUsageRange(c ClientWithAttachments, fromBytes, toBytes int64) bool {
  2099. if fromBytes <= 0 && toBytes <= 0 {
  2100. return true
  2101. }
  2102. used := int64(0)
  2103. if c.Traffic != nil {
  2104. used = c.Traffic.Up + c.Traffic.Down
  2105. }
  2106. if fromBytes > 0 && used < fromBytes {
  2107. return false
  2108. }
  2109. if toBytes > 0 && used > toBytes {
  2110. return false
  2111. }
  2112. return true
  2113. }
  2114. func clientMatchesAutoRenew(c ClientWithAttachments, mode string) bool {
  2115. switch strings.ToLower(strings.TrimSpace(mode)) {
  2116. case "on":
  2117. return c.Reset > 0
  2118. case "off":
  2119. return c.Reset <= 0
  2120. }
  2121. return true
  2122. }
  2123. func clientMatchesHasTgID(c ClientWithAttachments, mode string) bool {
  2124. switch strings.ToLower(strings.TrimSpace(mode)) {
  2125. case "yes":
  2126. return c.TgID != 0
  2127. case "no":
  2128. return c.TgID == 0
  2129. }
  2130. return true
  2131. }
  2132. func clientMatchesHasComment(c ClientWithAttachments, mode string) bool {
  2133. switch strings.ToLower(strings.TrimSpace(mode)) {
  2134. case "yes":
  2135. return strings.TrimSpace(c.Comment) != ""
  2136. case "no":
  2137. return strings.TrimSpace(c.Comment) == ""
  2138. }
  2139. return true
  2140. }
  2141. func clientMatchesAnyGroup(c ClientWithAttachments, csv string) bool {
  2142. groups := parseCSVStrings(csv)
  2143. if len(groups) == 0 {
  2144. return true
  2145. }
  2146. current := strings.TrimSpace(c.Group)
  2147. for _, g := range groups {
  2148. if g == "" {
  2149. if current == "" {
  2150. return true
  2151. }
  2152. continue
  2153. }
  2154. if strings.EqualFold(g, current) {
  2155. return true
  2156. }
  2157. }
  2158. return false
  2159. }
  2160. func clientMatchesBucket(c ClientWithAttachments, bucket string, onlineSet map[string]struct{}, nowMs, expireDiffMs, trafficDiffBytes int64) bool {
  2161. if bucket == "" {
  2162. return true
  2163. }
  2164. used := int64(0)
  2165. if c.Traffic != nil {
  2166. used = c.Traffic.Up + c.Traffic.Down
  2167. }
  2168. exhausted := c.TotalGB > 0 && used >= c.TotalGB
  2169. expired := c.ExpiryTime > 0 && c.ExpiryTime <= nowMs
  2170. switch bucket {
  2171. case "online":
  2172. if onlineSet == nil {
  2173. return false
  2174. }
  2175. _, ok := onlineSet[c.Email]
  2176. return ok && c.Enable
  2177. case "depleted":
  2178. return exhausted || expired
  2179. case "deactive":
  2180. return !c.Enable
  2181. case "active":
  2182. return c.Enable && !exhausted && !expired
  2183. case "expiring":
  2184. if !c.Enable || exhausted || expired {
  2185. return false
  2186. }
  2187. nearExpiry := c.ExpiryTime > 0 && c.ExpiryTime-nowMs < expireDiffMs
  2188. nearLimit := c.TotalGB > 0 && c.TotalGB-used < trafficDiffBytes
  2189. return nearExpiry || nearLimit
  2190. }
  2191. return true
  2192. }
  2193. func sortClients(rows []ClientWithAttachments, sortKey, order string) {
  2194. if sortKey == "" {
  2195. return
  2196. }
  2197. desc := order == "descend"
  2198. less := func(i, j int) bool {
  2199. a, b := rows[i], rows[j]
  2200. switch sortKey {
  2201. case "enable":
  2202. if a.Enable == b.Enable {
  2203. return false
  2204. }
  2205. return !a.Enable && b.Enable
  2206. case "email":
  2207. return strings.ToLower(a.Email) < strings.ToLower(b.Email)
  2208. case "inboundIds":
  2209. return len(a.InboundIds) < len(b.InboundIds)
  2210. case "traffic":
  2211. ua := int64(0)
  2212. if a.Traffic != nil {
  2213. ua = a.Traffic.Up + a.Traffic.Down
  2214. }
  2215. ub := int64(0)
  2216. if b.Traffic != nil {
  2217. ub = b.Traffic.Up + b.Traffic.Down
  2218. }
  2219. return ua < ub
  2220. case "remaining":
  2221. ra := int64(1<<62 - 1)
  2222. if a.TotalGB > 0 {
  2223. used := int64(0)
  2224. if a.Traffic != nil {
  2225. used = a.Traffic.Up + a.Traffic.Down
  2226. }
  2227. ra = a.TotalGB - used
  2228. }
  2229. rb := int64(1<<62 - 1)
  2230. if b.TotalGB > 0 {
  2231. used := int64(0)
  2232. if b.Traffic != nil {
  2233. used = b.Traffic.Up + b.Traffic.Down
  2234. }
  2235. rb = b.TotalGB - used
  2236. }
  2237. return ra < rb
  2238. case "expiryTime":
  2239. ea := int64(1<<62 - 1)
  2240. if a.ExpiryTime > 0 {
  2241. ea = a.ExpiryTime
  2242. }
  2243. eb := int64(1<<62 - 1)
  2244. if b.ExpiryTime > 0 {
  2245. eb = b.ExpiryTime
  2246. }
  2247. return ea < eb
  2248. case "createdAt":
  2249. if a.CreatedAt == b.CreatedAt {
  2250. return a.Id < b.Id
  2251. }
  2252. return a.CreatedAt < b.CreatedAt
  2253. case "updatedAt":
  2254. if a.UpdatedAt == b.UpdatedAt {
  2255. return a.Id < b.Id
  2256. }
  2257. return a.UpdatedAt < b.UpdatedAt
  2258. case "lastOnline":
  2259. la := int64(0)
  2260. if a.Traffic != nil {
  2261. la = a.Traffic.LastOnline
  2262. }
  2263. lb := int64(0)
  2264. if b.Traffic != nil {
  2265. lb = b.Traffic.LastOnline
  2266. }
  2267. if la == lb {
  2268. return a.Id < b.Id
  2269. }
  2270. return la < lb
  2271. }
  2272. return false
  2273. }
  2274. sort.SliceStable(rows, func(i, j int) bool {
  2275. if desc {
  2276. return less(j, i)
  2277. }
  2278. return less(i, j)
  2279. })
  2280. }
  2281. // BulkAdjustResult is returned by BulkAdjust to report how many clients were
  2282. // successfully updated and which were skipped (typically because the field
  2283. // being adjusted was unlimited for that client) or failed.
  2284. type BulkAdjustResult struct {
  2285. Adjusted int `json:"adjusted"`
  2286. Skipped []BulkAdjustReport `json:"skipped,omitempty"`
  2287. }
  2288. type BulkAdjustReport struct {
  2289. Email string `json:"email"`
  2290. Reason string `json:"reason"`
  2291. }
  2292. type bulkAdjustEntry struct {
  2293. record *model.ClientRecord
  2294. applyExpiry bool
  2295. newExpiry int64
  2296. applyTotal bool
  2297. newTotal int64
  2298. }
  2299. // BulkAdjust shifts ExpiryTime by addDays (days) and TotalGB by addBytes
  2300. // for every email in the list. Clients whose corresponding field is
  2301. // unlimited (0) are skipped — bulk extend should not accidentally
  2302. // limit an unlimited client. addDays and addBytes may be negative.
  2303. //
  2304. // Like BulkDelete, the work is grouped by inbound so each inbound's
  2305. // settings JSON is parsed and written exactly once regardless of how
  2306. // many target emails it contains.
  2307. func (s *ClientService) BulkAdjust(inboundSvc *InboundService, emails []string, addDays int, addBytes int64) (BulkAdjustResult, bool, error) {
  2308. result := BulkAdjustResult{}
  2309. if len(emails) == 0 {
  2310. return result, false, nil
  2311. }
  2312. if addDays == 0 && addBytes == 0 {
  2313. return result, false, common.NewError("no adjustment specified")
  2314. }
  2315. addExpiryMs := int64(addDays) * 24 * 60 * 60 * 1000
  2316. seen := map[string]struct{}{}
  2317. cleanEmails := make([]string, 0, len(emails))
  2318. for _, e := range emails {
  2319. e = strings.TrimSpace(e)
  2320. if e == "" {
  2321. continue
  2322. }
  2323. if _, ok := seen[e]; ok {
  2324. continue
  2325. }
  2326. seen[e] = struct{}{}
  2327. cleanEmails = append(cleanEmails, e)
  2328. }
  2329. if len(cleanEmails) == 0 {
  2330. return result, false, nil
  2331. }
  2332. db := database.GetDB()
  2333. var records []model.ClientRecord
  2334. for _, batch := range chunkStrings(cleanEmails, sqlInChunk) {
  2335. var rows []model.ClientRecord
  2336. if err := db.Where("email IN ?", batch).Find(&rows).Error; err != nil {
  2337. return result, false, err
  2338. }
  2339. records = append(records, rows...)
  2340. }
  2341. recordsByEmail := make(map[string]*model.ClientRecord, len(records))
  2342. for i := range records {
  2343. recordsByEmail[records[i].Email] = &records[i]
  2344. }
  2345. skippedReasons := map[string]string{}
  2346. for _, email := range cleanEmails {
  2347. if _, ok := recordsByEmail[email]; !ok {
  2348. skippedReasons[email] = "client not found"
  2349. }
  2350. }
  2351. plan := map[string]*bulkAdjustEntry{}
  2352. for email, rec := range recordsByEmail {
  2353. entry := &bulkAdjustEntry{record: rec}
  2354. if addDays != 0 {
  2355. switch {
  2356. case rec.ExpiryTime == 0:
  2357. if _, exists := skippedReasons[email]; !exists {
  2358. skippedReasons[email] = "unlimited expiry"
  2359. }
  2360. case rec.ExpiryTime > 0:
  2361. next := rec.ExpiryTime + addExpiryMs
  2362. if next <= 0 {
  2363. if _, exists := skippedReasons[email]; !exists {
  2364. skippedReasons[email] = "reduction exceeds remaining time"
  2365. }
  2366. } else {
  2367. entry.applyExpiry = true
  2368. entry.newExpiry = next
  2369. }
  2370. default:
  2371. next := rec.ExpiryTime - addExpiryMs
  2372. if next >= 0 {
  2373. if _, exists := skippedReasons[email]; !exists {
  2374. skippedReasons[email] = "reduction exceeds delay window"
  2375. }
  2376. } else {
  2377. entry.applyExpiry = true
  2378. entry.newExpiry = next
  2379. }
  2380. }
  2381. }
  2382. if addBytes != 0 {
  2383. if rec.TotalGB == 0 {
  2384. if _, exists := skippedReasons[email]; !exists {
  2385. skippedReasons[email] = "unlimited traffic"
  2386. }
  2387. } else {
  2388. next := max(rec.TotalGB+addBytes, 0)
  2389. entry.applyTotal = true
  2390. entry.newTotal = next
  2391. }
  2392. }
  2393. if entry.applyExpiry || entry.applyTotal {
  2394. plan[email] = entry
  2395. }
  2396. }
  2397. if len(plan) == 0 {
  2398. for email, reason := range skippedReasons {
  2399. result.Skipped = append(result.Skipped, BulkAdjustReport{Email: email, Reason: reason})
  2400. }
  2401. return result, false, nil
  2402. }
  2403. plannedIds := make([]int, 0, len(plan))
  2404. recordIdToEmail := make(map[int]string, len(plan))
  2405. for email, entry := range plan {
  2406. plannedIds = append(plannedIds, entry.record.Id)
  2407. recordIdToEmail[entry.record.Id] = email
  2408. }
  2409. var mappings []model.ClientInbound
  2410. for _, batch := range chunkInts(plannedIds, sqlInChunk) {
  2411. var rows []model.ClientInbound
  2412. if err := db.Where("client_id IN ?", batch).Find(&rows).Error; err != nil {
  2413. return result, false, err
  2414. }
  2415. mappings = append(mappings, rows...)
  2416. }
  2417. emailsByInbound := map[int][]string{}
  2418. for _, m := range mappings {
  2419. email, ok := recordIdToEmail[m.ClientId]
  2420. if !ok {
  2421. continue
  2422. }
  2423. emailsByInbound[m.InboundId] = append(emailsByInbound[m.InboundId], email)
  2424. }
  2425. needRestart := false
  2426. for inboundId, ibEmails := range emailsByInbound {
  2427. ibRes := s.bulkAdjustInboundClients(inboundSvc, inboundId, ibEmails, plan)
  2428. if ibRes.needRestart {
  2429. needRestart = true
  2430. }
  2431. for email, reason := range ibRes.perEmailSkipped {
  2432. if _, already := skippedReasons[email]; !already {
  2433. skippedReasons[email] = reason
  2434. }
  2435. }
  2436. }
  2437. for email, entry := range plan {
  2438. if _, skipped := skippedReasons[email]; skipped {
  2439. continue
  2440. }
  2441. updates := map[string]any{}
  2442. if entry.applyExpiry {
  2443. updates["expiry_time"] = entry.newExpiry
  2444. }
  2445. if entry.applyTotal {
  2446. updates["total"] = entry.newTotal
  2447. }
  2448. if len(updates) == 0 {
  2449. continue
  2450. }
  2451. if err := db.Model(xray.ClientTraffic{}).Where("email = ?", email).Updates(updates).Error; err != nil {
  2452. if _, already := skippedReasons[email]; !already {
  2453. skippedReasons[email] = err.Error()
  2454. }
  2455. continue
  2456. }
  2457. result.Adjusted++
  2458. }
  2459. for email, reason := range skippedReasons {
  2460. result.Skipped = append(result.Skipped, BulkAdjustReport{Email: email, Reason: reason})
  2461. }
  2462. return result, needRestart, nil
  2463. }
  2464. type bulkInboundAdjustResult struct {
  2465. perEmailSkipped map[string]string
  2466. needRestart bool
  2467. }
  2468. // bulkAdjustInboundClients applies expiry/total deltas to multiple clients
  2469. // inside a single inbound's settings JSON. The xray runtime is updated
  2470. // only for remote-node inbounds; local nodes do not need a notification
  2471. // because the AddUser payload does not include totalGB/expiryTime —
  2472. // changing those fields is identity-preserving and the panel's traffic
  2473. // enforcement loop picks up the new limits from ClientTraffic directly.
  2474. func (s *ClientService) bulkAdjustInboundClients(
  2475. inboundSvc *InboundService,
  2476. inboundId int,
  2477. emails []string,
  2478. plan map[string]*bulkAdjustEntry,
  2479. ) bulkInboundAdjustResult {
  2480. res := bulkInboundAdjustResult{perEmailSkipped: map[string]string{}}
  2481. defer lockInbound(inboundId).Unlock()
  2482. oldInbound, err := inboundSvc.GetInbound(inboundId)
  2483. if err != nil {
  2484. logger.Error("Load Old Data Error")
  2485. for _, e := range emails {
  2486. res.perEmailSkipped[e] = err.Error()
  2487. }
  2488. return res
  2489. }
  2490. var settings map[string]any
  2491. if err := json.Unmarshal([]byte(oldInbound.Settings), &settings); err != nil {
  2492. for _, e := range emails {
  2493. res.perEmailSkipped[e] = err.Error()
  2494. }
  2495. return res
  2496. }
  2497. clientKey := "id"
  2498. switch oldInbound.Protocol {
  2499. case model.Trojan:
  2500. clientKey = "password"
  2501. case model.Shadowsocks:
  2502. clientKey = "email"
  2503. case model.Hysteria:
  2504. clientKey = "auth"
  2505. }
  2506. keyToEmail := make(map[string]string, len(emails))
  2507. for _, email := range emails {
  2508. entry := plan[email]
  2509. if entry == nil {
  2510. res.perEmailSkipped[email] = "client not found"
  2511. continue
  2512. }
  2513. key := clientKeyForProtocol(oldInbound.Protocol, entry.record)
  2514. if key == "" {
  2515. res.perEmailSkipped[email] = "missing client key for protocol"
  2516. continue
  2517. }
  2518. keyToEmail[key] = email
  2519. }
  2520. interfaceClients, _ := settings["clients"].([]any)
  2521. foundEmails := map[string]bool{}
  2522. nowMs := time.Now().Unix() * 1000
  2523. for i, client := range interfaceClients {
  2524. c, ok := client.(map[string]any)
  2525. if !ok {
  2526. continue
  2527. }
  2528. cKey, _ := c[clientKey].(string)
  2529. targetEmail, found := keyToEmail[cKey]
  2530. if !found {
  2531. continue
  2532. }
  2533. entry := plan[targetEmail]
  2534. if entry.applyExpiry {
  2535. c["expiryTime"] = entry.newExpiry
  2536. }
  2537. if entry.applyTotal {
  2538. c["totalGB"] = entry.newTotal
  2539. }
  2540. c["updated_at"] = nowMs
  2541. interfaceClients[i] = c
  2542. foundEmails[targetEmail] = true
  2543. }
  2544. for _, email := range keyToEmail {
  2545. if !foundEmails[email] {
  2546. res.perEmailSkipped[email] = "Client Not Found In Inbound"
  2547. }
  2548. }
  2549. if len(foundEmails) == 0 {
  2550. return res
  2551. }
  2552. settings["clients"] = interfaceClients
  2553. newSettings, err := json.MarshalIndent(settings, "", " ")
  2554. if err != nil {
  2555. for email := range foundEmails {
  2556. res.perEmailSkipped[email] = err.Error()
  2557. }
  2558. return res
  2559. }
  2560. oldInbound.Settings = string(newSettings)
  2561. markDirty := false
  2562. if oldInbound.NodeID != nil {
  2563. rt, push, dirty, perr := inboundSvc.nodePushPlan(oldInbound)
  2564. if perr != nil {
  2565. for email := range foundEmails {
  2566. res.perEmailSkipped[email] = perr.Error()
  2567. delete(foundEmails, email)
  2568. }
  2569. } else {
  2570. if dirty {
  2571. markDirty = true
  2572. }
  2573. if push {
  2574. for email := range foundEmails {
  2575. entry := plan[email]
  2576. updated := *entry.record.ToClient()
  2577. if entry.applyExpiry {
  2578. updated.ExpiryTime = entry.newExpiry
  2579. }
  2580. if entry.applyTotal {
  2581. updated.TotalGB = entry.newTotal
  2582. }
  2583. updated.UpdatedAt = nowMs
  2584. if err1 := rt.UpdateUser(context.Background(), oldInbound, email, updated); err1 != nil {
  2585. logger.Warning("Error in updating client on", rt.Name(), ":", err1)
  2586. markDirty = true
  2587. }
  2588. }
  2589. }
  2590. }
  2591. }
  2592. db := database.GetDB()
  2593. txErr := db.Transaction(func(tx *gorm.DB) error {
  2594. if err := tx.Save(oldInbound).Error; err != nil {
  2595. return err
  2596. }
  2597. finalClients, gcErr := inboundSvc.GetClients(oldInbound)
  2598. if gcErr != nil {
  2599. return gcErr
  2600. }
  2601. return s.SyncInbound(tx, inboundId, finalClients)
  2602. })
  2603. if txErr != nil {
  2604. for email := range foundEmails {
  2605. if _, skip := res.perEmailSkipped[email]; !skip {
  2606. res.perEmailSkipped[email] = txErr.Error()
  2607. }
  2608. }
  2609. } else if markDirty && oldInbound.NodeID != nil {
  2610. if dErr := (&NodeService{}).MarkNodeDirty(*oldInbound.NodeID); dErr != nil {
  2611. logger.Warning("mark node dirty failed:", dErr)
  2612. }
  2613. }
  2614. return res
  2615. }
  2616. // BulkDeleteResult mirrors BulkAdjustResult: total deleted plus per-email
  2617. // skip reasons when an email could not be processed.
  2618. type BulkDeleteResult struct {
  2619. Deleted int `json:"deleted"`
  2620. Skipped []BulkDeleteReport `json:"skipped,omitempty"`
  2621. }
  2622. type BulkDeleteReport struct {
  2623. Email string `json:"email"`
  2624. Reason string `json:"reason"`
  2625. }
  2626. const sqlInChunk = 400
  2627. // BulkDelete removes every client in the list in one optimized pass.
  2628. // Instead of running the full single-delete pipeline N times (which would
  2629. // re-read, re-parse, and re-write each inbound's settings JSON for every
  2630. // email), it groups emails by inbound and performs a single
  2631. // read-modify-write per inbound. Per-row DB cleanups are also batched with
  2632. // IN-clause queries at the end. Errors on a particular email are recorded
  2633. // in the Skipped list and processing continues for the rest.
  2634. func (s *ClientService) BulkDelete(inboundSvc *InboundService, emails []string, keepTraffic bool) (BulkDeleteResult, bool, error) {
  2635. result := BulkDeleteResult{}
  2636. seen := map[string]struct{}{}
  2637. cleanEmails := make([]string, 0, len(emails))
  2638. for _, e := range emails {
  2639. e = strings.TrimSpace(e)
  2640. if e == "" {
  2641. continue
  2642. }
  2643. if _, ok := seen[e]; ok {
  2644. continue
  2645. }
  2646. seen[e] = struct{}{}
  2647. cleanEmails = append(cleanEmails, e)
  2648. }
  2649. if len(cleanEmails) == 0 {
  2650. return result, false, nil
  2651. }
  2652. db := database.GetDB()
  2653. var records []model.ClientRecord
  2654. for _, batch := range chunkStrings(cleanEmails, sqlInChunk) {
  2655. var rows []model.ClientRecord
  2656. if err := db.Where("email IN ?", batch).Find(&rows).Error; err != nil {
  2657. return result, false, err
  2658. }
  2659. records = append(records, rows...)
  2660. }
  2661. recordsByEmail := make(map[string]*model.ClientRecord, len(records))
  2662. tombstoneEmails := make([]string, 0, len(records))
  2663. for i := range records {
  2664. recordsByEmail[records[i].Email] = &records[i]
  2665. tombstoneEmails = append(tombstoneEmails, records[i].Email)
  2666. }
  2667. tombstoneClientEmails(tombstoneEmails)
  2668. skippedReasons := map[string]string{}
  2669. for _, email := range cleanEmails {
  2670. if _, ok := recordsByEmail[email]; !ok {
  2671. skippedReasons[email] = "client not found"
  2672. }
  2673. }
  2674. clientIds := make([]int, 0, len(recordsByEmail))
  2675. recordIdToEmail := make(map[int]string, len(recordsByEmail))
  2676. for _, r := range recordsByEmail {
  2677. clientIds = append(clientIds, r.Id)
  2678. recordIdToEmail[r.Id] = r.Email
  2679. }
  2680. emailsByInbound := map[int][]string{}
  2681. if len(clientIds) > 0 {
  2682. var mappings []model.ClientInbound
  2683. for _, batch := range chunkInts(clientIds, sqlInChunk) {
  2684. var rows []model.ClientInbound
  2685. if err := db.Where("client_id IN ?", batch).Find(&rows).Error; err != nil {
  2686. return result, false, err
  2687. }
  2688. mappings = append(mappings, rows...)
  2689. }
  2690. for _, m := range mappings {
  2691. email, ok := recordIdToEmail[m.ClientId]
  2692. if !ok {
  2693. continue
  2694. }
  2695. emailsByInbound[m.InboundId] = append(emailsByInbound[m.InboundId], email)
  2696. }
  2697. }
  2698. needRestart := false
  2699. for inboundId, ibEmails := range emailsByInbound {
  2700. ibResult := s.bulkDelInboundClients(inboundSvc, inboundId, ibEmails, recordsByEmail, false)
  2701. if ibResult.needRestart {
  2702. needRestart = true
  2703. }
  2704. for email, reason := range ibResult.perEmailSkipped {
  2705. if _, already := skippedReasons[email]; !already {
  2706. skippedReasons[email] = reason
  2707. }
  2708. }
  2709. }
  2710. successEmails := make([]string, 0, len(recordsByEmail))
  2711. successIds := make([]int, 0, len(recordsByEmail))
  2712. for email, rec := range recordsByEmail {
  2713. if _, skipped := skippedReasons[email]; skipped {
  2714. continue
  2715. }
  2716. successEmails = append(successEmails, email)
  2717. successIds = append(successIds, rec.Id)
  2718. }
  2719. if len(successIds) > 0 {
  2720. for _, batch := range chunkInts(successIds, sqlInChunk) {
  2721. if err := db.Where("client_id IN ?", batch).Delete(&model.ClientInbound{}).Error; err != nil {
  2722. return result, needRestart, err
  2723. }
  2724. }
  2725. if !keepTraffic && len(successEmails) > 0 {
  2726. for _, batch := range chunkStrings(successEmails, sqlInChunk) {
  2727. if err := db.Where("email IN ?", batch).Delete(&xray.ClientTraffic{}).Error; err != nil {
  2728. return result, needRestart, err
  2729. }
  2730. if err := db.Where("client_email IN ?", batch).Delete(&model.InboundClientIps{}).Error; err != nil {
  2731. return result, needRestart, err
  2732. }
  2733. }
  2734. }
  2735. for _, batch := range chunkInts(successIds, sqlInChunk) {
  2736. if err := db.Where("id IN ?", batch).Delete(&model.ClientRecord{}).Error; err != nil {
  2737. return result, needRestart, err
  2738. }
  2739. }
  2740. }
  2741. result.Deleted = len(successEmails)
  2742. for email, reason := range skippedReasons {
  2743. result.Skipped = append(result.Skipped, BulkDeleteReport{Email: email, Reason: reason})
  2744. }
  2745. return result, needRestart, nil
  2746. }
  2747. type bulkInboundDeleteResult struct {
  2748. perEmailSkipped map[string]string
  2749. needRestart bool
  2750. }
  2751. // bulkDelInboundClients removes multiple clients from a single inbound's
  2752. // settings JSON in one read-modify-write cycle, runs the xray runtime
  2753. // RemoveUser/DeleteUser calls, and persists the inbound. The returned map
  2754. // holds per-email failure reasons; emails not present in the map are
  2755. // considered successful for this inbound.
  2756. func (s *ClientService) bulkDelInboundClients(
  2757. inboundSvc *InboundService,
  2758. inboundId int,
  2759. emails []string,
  2760. records map[string]*model.ClientRecord,
  2761. keepTraffic bool,
  2762. ) bulkInboundDeleteResult {
  2763. res := bulkInboundDeleteResult{perEmailSkipped: map[string]string{}}
  2764. defer lockInbound(inboundId).Unlock()
  2765. oldInbound, err := inboundSvc.GetInbound(inboundId)
  2766. if err != nil {
  2767. logger.Error("Load Old Data Error")
  2768. for _, e := range emails {
  2769. res.perEmailSkipped[e] = err.Error()
  2770. }
  2771. return res
  2772. }
  2773. var settings map[string]any
  2774. if err := json.Unmarshal([]byte(oldInbound.Settings), &settings); err != nil {
  2775. for _, e := range emails {
  2776. res.perEmailSkipped[e] = err.Error()
  2777. }
  2778. return res
  2779. }
  2780. clientKey := "id"
  2781. switch oldInbound.Protocol {
  2782. case model.Trojan:
  2783. clientKey = "password"
  2784. case model.Shadowsocks:
  2785. clientKey = "email"
  2786. case model.Hysteria:
  2787. clientKey = "auth"
  2788. }
  2789. keyToEmail := make(map[string]string, len(emails))
  2790. for _, email := range emails {
  2791. rec := records[email]
  2792. if rec == nil {
  2793. res.perEmailSkipped[email] = "client not found"
  2794. continue
  2795. }
  2796. key := clientKeyForProtocol(oldInbound.Protocol, rec)
  2797. if key == "" {
  2798. res.perEmailSkipped[email] = "missing client key for protocol"
  2799. continue
  2800. }
  2801. keyToEmail[key] = email
  2802. }
  2803. interfaceClients, _ := settings["clients"].([]any)
  2804. newClients := make([]any, 0, len(interfaceClients))
  2805. foundEmails := map[string]bool{}
  2806. enableByEmail := map[string]bool{}
  2807. for _, client := range interfaceClients {
  2808. c, ok := client.(map[string]any)
  2809. if !ok {
  2810. newClients = append(newClients, client)
  2811. continue
  2812. }
  2813. cKey, _ := c[clientKey].(string)
  2814. if targetEmail, found := keyToEmail[cKey]; found {
  2815. foundEmails[targetEmail] = true
  2816. if em, _ := c["email"].(string); em != "" {
  2817. en, _ := c["enable"].(bool)
  2818. enableByEmail[em] = en
  2819. }
  2820. continue
  2821. }
  2822. newClients = append(newClients, client)
  2823. }
  2824. for _, email := range keyToEmail {
  2825. if !foundEmails[email] {
  2826. res.perEmailSkipped[email] = "Client Not Found In Inbound"
  2827. }
  2828. }
  2829. db := database.GetDB()
  2830. newClients = compactOrphans(db, newClients)
  2831. if newClients == nil {
  2832. newClients = []any{}
  2833. }
  2834. settings["clients"] = newClients
  2835. newSettings, err := json.MarshalIndent(settings, "", " ")
  2836. if err != nil {
  2837. for email := range foundEmails {
  2838. if _, skip := res.perEmailSkipped[email]; !skip {
  2839. res.perEmailSkipped[email] = err.Error()
  2840. }
  2841. }
  2842. return res
  2843. }
  2844. oldInbound.Settings = string(newSettings)
  2845. foundList := make([]string, 0, len(foundEmails))
  2846. for email := range foundEmails {
  2847. foundList = append(foundList, email)
  2848. }
  2849. notDepletedByEmail := map[string]bool{}
  2850. if len(foundList) > 0 {
  2851. type trafficRow struct {
  2852. Email string
  2853. Enable bool
  2854. }
  2855. for _, batch := range chunkStrings(foundList, sqlInChunk) {
  2856. var rows []trafficRow
  2857. if err := db.Model(xray.ClientTraffic{}).
  2858. Where("email IN ?", batch).
  2859. Select("email, enable").
  2860. Scan(&rows).Error; err == nil {
  2861. for _, r := range rows {
  2862. notDepletedByEmail[r.Email] = r.Enable
  2863. }
  2864. }
  2865. }
  2866. }
  2867. var sharedSet map[string]bool
  2868. if !keepTraffic {
  2869. var sharedErr error
  2870. sharedSet, sharedErr = inboundSvc.emailsUsedByOtherInbounds(foundList, inboundId)
  2871. if sharedErr != nil {
  2872. for email := range foundEmails {
  2873. res.perEmailSkipped[email] = sharedErr.Error()
  2874. delete(foundEmails, email)
  2875. }
  2876. return res
  2877. }
  2878. }
  2879. if !keepTraffic {
  2880. purge := make([]string, 0, len(foundEmails))
  2881. for email := range foundEmails {
  2882. if !sharedSet[strings.ToLower(strings.TrimSpace(email))] {
  2883. purge = append(purge, email)
  2884. }
  2885. }
  2886. if len(purge) > 0 {
  2887. if delErr := inboundSvc.delClientIPsByEmails(db, purge); delErr != nil {
  2888. logger.Error("Error in delete client IPs")
  2889. for _, email := range purge {
  2890. res.perEmailSkipped[email] = delErr.Error()
  2891. delete(foundEmails, email)
  2892. }
  2893. } else if delErr := inboundSvc.delClientStatsByEmails(db, purge); delErr != nil {
  2894. logger.Error("Delete stats Data Error")
  2895. for _, email := range purge {
  2896. res.perEmailSkipped[email] = delErr.Error()
  2897. delete(foundEmails, email)
  2898. }
  2899. }
  2900. }
  2901. }
  2902. markDirty := false
  2903. if oldInbound.NodeID == nil {
  2904. rt, rterr := inboundSvc.runtimeFor(oldInbound)
  2905. if rterr != nil {
  2906. res.needRestart = true
  2907. } else {
  2908. for email := range foundEmails {
  2909. if !enableByEmail[email] || !notDepletedByEmail[email] {
  2910. continue
  2911. }
  2912. err1 := rt.RemoveUser(context.Background(), oldInbound, email)
  2913. if err1 == nil {
  2914. logger.Debug("Client deleted on", rt.Name(), ":", email)
  2915. } else if strings.Contains(err1.Error(), fmt.Sprintf("User %s not found.", email)) {
  2916. logger.Debug("User is already deleted. Nothing to do more...")
  2917. } else {
  2918. logger.Debug("Error in deleting client on", rt.Name(), ":", err1)
  2919. res.needRestart = true
  2920. }
  2921. }
  2922. }
  2923. } else {
  2924. rt, push, dirty, perr := inboundSvc.nodePushPlan(oldInbound)
  2925. if perr != nil {
  2926. for email := range foundEmails {
  2927. res.perEmailSkipped[email] = perr.Error()
  2928. delete(foundEmails, email)
  2929. }
  2930. } else {
  2931. if dirty {
  2932. markDirty = true
  2933. }
  2934. if push {
  2935. for email := range foundEmails {
  2936. if err1 := rt.DeleteUser(context.Background(), oldInbound, email); err1 != nil {
  2937. logger.Warning("Error in deleting client on", rt.Name(), ":", err1)
  2938. markDirty = true
  2939. }
  2940. }
  2941. }
  2942. }
  2943. }
  2944. txErr := db.Transaction(func(tx *gorm.DB) error {
  2945. if err := tx.Save(oldInbound).Error; err != nil {
  2946. return err
  2947. }
  2948. finalClients, err := inboundSvc.GetClients(oldInbound)
  2949. if err != nil {
  2950. return err
  2951. }
  2952. return s.SyncInbound(tx, inboundId, finalClients)
  2953. })
  2954. if txErr != nil {
  2955. for email := range foundEmails {
  2956. if _, skip := res.perEmailSkipped[email]; !skip {
  2957. res.perEmailSkipped[email] = txErr.Error()
  2958. }
  2959. }
  2960. } else if markDirty && oldInbound.NodeID != nil {
  2961. if dErr := (&NodeService{}).MarkNodeDirty(*oldInbound.NodeID); dErr != nil {
  2962. logger.Warning("mark node dirty failed:", dErr)
  2963. }
  2964. }
  2965. return res
  2966. }
  2967. // BulkCreateResult mirrors BulkAdjustResult for the create flow.
  2968. type BulkCreateResult struct {
  2969. Created int `json:"created"`
  2970. Skipped []BulkCreateReport `json:"skipped,omitempty"`
  2971. }
  2972. type BulkCreateReport struct {
  2973. Email string `json:"email"`
  2974. Reason string `json:"reason"`
  2975. }
  2976. func (s *ClientService) BulkCreate(inboundSvc *InboundService, payloads []ClientCreatePayload) (BulkCreateResult, bool, error) {
  2977. result := BulkCreateResult{}
  2978. if len(payloads) == 0 {
  2979. return result, false, nil
  2980. }
  2981. skip := func(email, reason string) {
  2982. if strings.TrimSpace(email) == "" {
  2983. email = "(missing email)"
  2984. }
  2985. result.Skipped = append(result.Skipped, BulkCreateReport{Email: email, Reason: reason})
  2986. }
  2987. emailSubIDs, err := inboundSvc.getAllEmailSubIDs()
  2988. if err != nil {
  2989. emailSubIDs = nil
  2990. }
  2991. type prepared struct {
  2992. client model.Client
  2993. inboundIds []int
  2994. }
  2995. prep := make([]prepared, 0, len(payloads))
  2996. emails := make([]string, 0, len(payloads))
  2997. subIDs := make([]string, 0, len(payloads))
  2998. seenEmail := make(map[string]struct{}, len(payloads))
  2999. seenSubID := make(map[string]string, len(payloads))
  3000. for i := range payloads {
  3001. client := payloads[i].Client
  3002. email := strings.TrimSpace(client.Email)
  3003. if email == "" {
  3004. skip("", "client email is required")
  3005. continue
  3006. }
  3007. if verr := validateClientEmail(email); verr != nil {
  3008. skip(email, verr.Error())
  3009. continue
  3010. }
  3011. if verr := validateClientSubID(client.SubID); verr != nil {
  3012. skip(email, verr.Error())
  3013. continue
  3014. }
  3015. if len(payloads[i].InboundIds) == 0 {
  3016. skip(email, "at least one inbound is required")
  3017. continue
  3018. }
  3019. client.Email = email
  3020. if client.SubID == "" {
  3021. client.SubID = uuid.NewString()
  3022. }
  3023. if !client.Enable {
  3024. client.Enable = true
  3025. }
  3026. now := time.Now().UnixMilli()
  3027. if client.CreatedAt == 0 {
  3028. client.CreatedAt = now
  3029. }
  3030. client.UpdatedAt = now
  3031. le := strings.ToLower(email)
  3032. if _, dup := seenEmail[le]; dup {
  3033. skip(email, "email already in use: "+email)
  3034. continue
  3035. }
  3036. if owner, ok := seenSubID[client.SubID]; ok && owner != le {
  3037. skip(email, "subId already in use: "+client.SubID)
  3038. continue
  3039. }
  3040. seenEmail[le] = struct{}{}
  3041. seenSubID[client.SubID] = le
  3042. prep = append(prep, prepared{client: client, inboundIds: payloads[i].InboundIds})
  3043. emails = append(emails, email)
  3044. subIDs = append(subIDs, client.SubID)
  3045. }
  3046. if len(prep) == 0 {
  3047. return result, false, nil
  3048. }
  3049. db := database.GetDB()
  3050. const lookupChunk = 400
  3051. existingEmailSub := make(map[string]string, len(emails))
  3052. for start := 0; start < len(emails); start += lookupChunk {
  3053. end := min(start+lookupChunk, len(emails))
  3054. var rows []model.ClientRecord
  3055. if e := db.Where("email IN ?", emails[start:end]).Find(&rows).Error; e != nil {
  3056. return result, false, e
  3057. }
  3058. for i := range rows {
  3059. existingEmailSub[strings.ToLower(rows[i].Email)] = rows[i].SubID
  3060. }
  3061. }
  3062. existingSubOwner := make(map[string]string, len(subIDs))
  3063. for start := 0; start < len(subIDs); start += lookupChunk {
  3064. end := min(start+lookupChunk, len(subIDs))
  3065. var rows []model.ClientRecord
  3066. if e := db.Where("sub_id IN ?", subIDs[start:end]).Find(&rows).Error; e != nil {
  3067. return result, false, e
  3068. }
  3069. for i := range rows {
  3070. existingSubOwner[rows[i].SubID] = strings.ToLower(rows[i].Email)
  3071. }
  3072. }
  3073. inboundCache := make(map[int]*model.Inbound)
  3074. getIb := func(id int) (*model.Inbound, error) {
  3075. if ib, ok := inboundCache[id]; ok {
  3076. return ib, nil
  3077. }
  3078. ib, e := inboundSvc.GetInbound(id)
  3079. if e != nil {
  3080. return nil, e
  3081. }
  3082. inboundCache[id] = ib
  3083. return ib, nil
  3084. }
  3085. byInbound := make(map[int][]model.Client)
  3086. idxByInbound := make(map[int][]int)
  3087. inboundOrder := make([]int, 0)
  3088. failed := make([]bool, len(prep))
  3089. reason := make([]string, len(prep))
  3090. for idx := range prep {
  3091. le := strings.ToLower(prep[idx].client.Email)
  3092. if existSub, ok := existingEmailSub[le]; ok && existSub != prep[idx].client.SubID {
  3093. failed[idx] = true
  3094. reason[idx] = "email already in use: " + prep[idx].client.Email
  3095. continue
  3096. }
  3097. if owner, ok := existingSubOwner[prep[idx].client.SubID]; ok && owner != le {
  3098. failed[idx] = true
  3099. reason[idx] = "subId already in use: " + prep[idx].client.SubID
  3100. continue
  3101. }
  3102. ok := true
  3103. for _, ibId := range prep[idx].inboundIds {
  3104. ib, e := getIb(ibId)
  3105. if e != nil {
  3106. failed[idx] = true
  3107. reason[idx] = e.Error()
  3108. ok = false
  3109. break
  3110. }
  3111. if e := s.fillProtocolDefaults(&prep[idx].client, ib); e != nil {
  3112. failed[idx] = true
  3113. reason[idx] = e.Error()
  3114. ok = false
  3115. break
  3116. }
  3117. }
  3118. if !ok {
  3119. continue
  3120. }
  3121. for _, ibId := range prep[idx].inboundIds {
  3122. ib, _ := getIb(ibId)
  3123. if _, seen := byInbound[ibId]; !seen {
  3124. inboundOrder = append(inboundOrder, ibId)
  3125. }
  3126. byInbound[ibId] = append(byInbound[ibId], clientWithInboundFlow(prep[idx].client, ib))
  3127. idxByInbound[ibId] = append(idxByInbound[ibId], idx)
  3128. }
  3129. }
  3130. needRestart := false
  3131. for _, ibId := range inboundOrder {
  3132. payload, e := json.Marshal(map[string][]model.Client{"clients": byInbound[ibId]})
  3133. if e == nil {
  3134. var nr bool
  3135. nr, e = s.addInboundClient(inboundSvc, &model.Inbound{Id: ibId, Settings: string(payload)}, emailSubIDs)
  3136. if e == nil && nr {
  3137. needRestart = true
  3138. }
  3139. }
  3140. if e != nil {
  3141. for _, idx := range idxByInbound[ibId] {
  3142. failed[idx] = true
  3143. if reason[idx] == "" {
  3144. reason[idx] = e.Error()
  3145. }
  3146. }
  3147. }
  3148. }
  3149. for idx := range prep {
  3150. if failed[idx] {
  3151. skip(prep[idx].client.Email, reason[idx])
  3152. } else {
  3153. result.Created++
  3154. }
  3155. }
  3156. return result, needRestart, nil
  3157. }
  3158. func (s *ClientService) DelDepleted(inboundSvc *InboundService) (int, bool, error) {
  3159. db := database.GetDB()
  3160. now := time.Now().UnixMilli()
  3161. depletedClause := "reset = 0 and ((total > 0 and up + down >= total) or (expiry_time > 0 and expiry_time <= ?))"
  3162. var rows []xray.ClientTraffic
  3163. if err := db.Where(depletedClause, now).Find(&rows).Error; err != nil {
  3164. return 0, false, err
  3165. }
  3166. if len(rows) == 0 {
  3167. return 0, false, nil
  3168. }
  3169. seen := make(map[string]struct{}, len(rows))
  3170. emails := make([]string, 0, len(rows))
  3171. for _, r := range rows {
  3172. if r.Email == "" {
  3173. continue
  3174. }
  3175. if _, ok := seen[r.Email]; ok {
  3176. continue
  3177. }
  3178. seen[r.Email] = struct{}{}
  3179. emails = append(emails, r.Email)
  3180. }
  3181. if len(emails) == 0 {
  3182. return 0, false, nil
  3183. }
  3184. res, needRestart, err := s.BulkDelete(inboundSvc, emails, false)
  3185. if err != nil {
  3186. return res.Deleted, needRestart, err
  3187. }
  3188. return res.Deleted, needRestart, nil
  3189. }
  3190. func (s *ClientService) ResetAllClientTraffics(inboundSvc *InboundService, id int) error {
  3191. return submitTrafficWrite(func() error {
  3192. return s.resetAllClientTrafficsLocked(id)
  3193. })
  3194. }
  3195. func (s *ClientService) resetAllClientTrafficsLocked(id int) error {
  3196. db := database.GetDB()
  3197. now := time.Now().Unix() * 1000
  3198. if err := db.Transaction(func(tx *gorm.DB) error {
  3199. whereText := "inbound_id "
  3200. if id == -1 {
  3201. whereText += " > ?"
  3202. } else {
  3203. whereText += " = ?"
  3204. }
  3205. result := tx.Model(xray.ClientTraffic{}).
  3206. Where(whereText, id).
  3207. Updates(map[string]any{"enable": true, "up": 0, "down": 0})
  3208. if result.Error != nil {
  3209. return result.Error
  3210. }
  3211. inboundWhereText := "id "
  3212. if id == -1 {
  3213. inboundWhereText += " > ?"
  3214. } else {
  3215. inboundWhereText += " = ?"
  3216. }
  3217. result = tx.Model(model.Inbound{}).
  3218. Where(inboundWhereText, id).
  3219. Update("last_traffic_reset_time", now)
  3220. return result.Error
  3221. }); err != nil {
  3222. return err
  3223. }
  3224. return nil
  3225. }
  3226. func (s *ClientService) ResetAllTraffics() (bool, error) {
  3227. res := database.GetDB().Model(&xray.ClientTraffic{}).
  3228. Where("1 = 1").
  3229. Updates(map[string]any{"up": 0, "down": 0})
  3230. if res.Error != nil {
  3231. return false, res.Error
  3232. }
  3233. return res.RowsAffected > 0, nil
  3234. }
  3235. func (s *ClientService) Detach(inboundSvc *InboundService, id int, inboundIds []int) (bool, error) {
  3236. existing, err := s.GetByID(id)
  3237. if err != nil {
  3238. return false, err
  3239. }
  3240. currentIds, err := s.GetInboundIdsForRecord(id)
  3241. if err != nil {
  3242. return false, err
  3243. }
  3244. have := make(map[int]struct{}, len(currentIds))
  3245. for _, x := range currentIds {
  3246. have[x] = struct{}{}
  3247. }
  3248. needRestart := false
  3249. for _, ibId := range inboundIds {
  3250. if _, attached := have[ibId]; !attached {
  3251. continue
  3252. }
  3253. inbound, getErr := inboundSvc.GetInbound(ibId)
  3254. if getErr != nil {
  3255. return needRestart, getErr
  3256. }
  3257. key := clientKeyForProtocol(inbound.Protocol, existing)
  3258. if key == "" {
  3259. continue
  3260. }
  3261. nr, delErr := s.DelInboundClient(inboundSvc, ibId, key, true)
  3262. if delErr != nil {
  3263. return needRestart, delErr
  3264. }
  3265. if nr {
  3266. needRestart = true
  3267. }
  3268. }
  3269. return needRestart, nil
  3270. }
  3271. func (s *ClientService) checkEmailsExistForClients(inboundSvc *InboundService, clients []model.Client, emailSubIDs map[string]string) (string, error) {
  3272. if emailSubIDs == nil {
  3273. var err error
  3274. emailSubIDs, err = inboundSvc.getAllEmailSubIDs()
  3275. if err != nil {
  3276. return "", err
  3277. }
  3278. }
  3279. seen := make(map[string]string, len(clients))
  3280. for _, client := range clients {
  3281. if client.Email == "" {
  3282. continue
  3283. }
  3284. key := strings.ToLower(client.Email)
  3285. if prev, ok := seen[key]; ok {
  3286. if prev != client.SubID || client.SubID == "" {
  3287. return client.Email, nil
  3288. }
  3289. continue
  3290. }
  3291. seen[key] = client.SubID
  3292. if existingSub, ok := emailSubIDs[key]; ok {
  3293. if client.SubID == "" || existingSub == "" || existingSub != client.SubID {
  3294. return client.Email, nil
  3295. }
  3296. }
  3297. }
  3298. return "", nil
  3299. }
  3300. func (s *ClientService) AddInboundClient(inboundSvc *InboundService, data *model.Inbound) (bool, error) {
  3301. return s.addInboundClient(inboundSvc, data, nil)
  3302. }
  3303. // addInboundClient is AddInboundClient with an optional precomputed email→subId
  3304. // map. Bulk callers pass a single snapshot so the global getAllEmailSubIDs scan
  3305. // runs once for the whole batch instead of once per target inbound; a nil map
  3306. // makes it compute its own (the single-add path).
  3307. func (s *ClientService) addInboundClient(inboundSvc *InboundService, data *model.Inbound, emailSubIDs map[string]string) (bool, error) {
  3308. defer lockInbound(data.Id).Unlock()
  3309. clients, err := inboundSvc.GetClients(data)
  3310. if err != nil {
  3311. return false, err
  3312. }
  3313. var settings map[string]any
  3314. err = json.Unmarshal([]byte(data.Settings), &settings)
  3315. if err != nil {
  3316. return false, err
  3317. }
  3318. interfaceClients := settings["clients"].([]any)
  3319. nowTs := time.Now().Unix() * 1000
  3320. for i := range interfaceClients {
  3321. if cm, ok := interfaceClients[i].(map[string]any); ok {
  3322. if _, ok2 := cm["created_at"]; !ok2 {
  3323. cm["created_at"] = nowTs
  3324. }
  3325. cm["updated_at"] = nowTs
  3326. existingSub, _ := cm["subId"].(string)
  3327. if strings.TrimSpace(existingSub) == "" {
  3328. cm["subId"] = random.NumLower(16)
  3329. }
  3330. interfaceClients[i] = cm
  3331. }
  3332. }
  3333. existEmail, err := s.checkEmailsExistForClients(inboundSvc, clients, emailSubIDs)
  3334. if err != nil {
  3335. return false, err
  3336. }
  3337. if existEmail != "" {
  3338. return false, common.NewError("Duplicate email:", existEmail)
  3339. }
  3340. oldInbound, err := inboundSvc.GetInbound(data.Id)
  3341. if err != nil {
  3342. return false, err
  3343. }
  3344. for _, client := range clients {
  3345. if strings.TrimSpace(client.Email) == "" {
  3346. return false, common.NewError("client email is required")
  3347. }
  3348. switch oldInbound.Protocol {
  3349. case "trojan":
  3350. if client.Password == "" {
  3351. return false, common.NewError("empty client ID")
  3352. }
  3353. case "shadowsocks":
  3354. if client.Email == "" {
  3355. return false, common.NewError("empty client ID")
  3356. }
  3357. case "hysteria":
  3358. if client.Auth == "" {
  3359. return false, common.NewError("empty client ID")
  3360. }
  3361. default:
  3362. if client.ID == "" {
  3363. return false, common.NewError("empty client ID")
  3364. }
  3365. }
  3366. }
  3367. var oldSettings map[string]any
  3368. err = json.Unmarshal([]byte(oldInbound.Settings), &oldSettings)
  3369. if err != nil {
  3370. return false, err
  3371. }
  3372. if oldInbound.Protocol == model.Shadowsocks {
  3373. applyShadowsocksClientMethod(interfaceClients, oldSettings)
  3374. }
  3375. oldClients := oldSettings["clients"].([]any)
  3376. oldClients = compactOrphans(database.GetDB(), oldClients)
  3377. oldClients = append(oldClients, interfaceClients...)
  3378. oldSettings["clients"] = oldClients
  3379. newSettings, err := json.MarshalIndent(oldSettings, "", " ")
  3380. if err != nil {
  3381. return false, err
  3382. }
  3383. oldInbound.Settings = string(newSettings)
  3384. db := database.GetDB()
  3385. tx := db.Begin()
  3386. markDirty := false
  3387. defer func() {
  3388. if err != nil {
  3389. tx.Rollback()
  3390. return
  3391. }
  3392. tx.Commit()
  3393. if markDirty && oldInbound.NodeID != nil {
  3394. if dErr := (&NodeService{}).MarkNodeDirty(*oldInbound.NodeID); dErr != nil {
  3395. logger.Warning("mark node dirty failed:", dErr)
  3396. }
  3397. }
  3398. }()
  3399. needRestart := false
  3400. rt, push, dirty, perr := inboundSvc.nodePushPlan(oldInbound)
  3401. if perr != nil {
  3402. err = perr
  3403. return false, err
  3404. }
  3405. if dirty {
  3406. markDirty = true
  3407. }
  3408. if oldInbound.NodeID == nil {
  3409. if !push {
  3410. needRestart = true
  3411. } else {
  3412. for _, client := range clients {
  3413. if len(client.Email) == 0 {
  3414. needRestart = true
  3415. continue
  3416. }
  3417. inboundSvc.AddClientStat(tx, data.Id, &client)
  3418. if !client.Enable {
  3419. continue
  3420. }
  3421. cipher := ""
  3422. if oldInbound.Protocol == "shadowsocks" {
  3423. cipher = oldSettings["method"].(string)
  3424. }
  3425. err1 := rt.AddUser(context.Background(), oldInbound, map[string]any{
  3426. "email": client.Email,
  3427. "id": client.ID,
  3428. "auth": client.Auth,
  3429. "security": client.Security,
  3430. "flow": client.Flow,
  3431. "password": client.Password,
  3432. "cipher": cipher,
  3433. })
  3434. if err1 == nil {
  3435. logger.Debug("Client added on", rt.Name(), ":", client.Email)
  3436. } else {
  3437. logger.Debug("Error in adding client on", rt.Name(), ":", err1)
  3438. needRestart = true
  3439. }
  3440. }
  3441. }
  3442. } else {
  3443. for _, client := range clients {
  3444. if len(client.Email) > 0 {
  3445. inboundSvc.AddClientStat(tx, data.Id, &client)
  3446. }
  3447. if push {
  3448. if err1 := rt.AddClient(context.Background(), oldInbound, client); err1 != nil {
  3449. logger.Warning("Error in adding client on", rt.Name(), ":", err1)
  3450. markDirty = true
  3451. push = false
  3452. }
  3453. }
  3454. }
  3455. }
  3456. if err = tx.Save(oldInbound).Error; err != nil {
  3457. return false, err
  3458. }
  3459. finalClients, gcErr := inboundSvc.GetClients(oldInbound)
  3460. if gcErr != nil {
  3461. err = gcErr
  3462. return false, err
  3463. }
  3464. if err = s.SyncInbound(tx, oldInbound.Id, finalClients); err != nil {
  3465. return false, err
  3466. }
  3467. return needRestart, nil
  3468. }
  3469. func (s *ClientService) UpdateInboundClient(inboundSvc *InboundService, data *model.Inbound, clientId string) (bool, error) {
  3470. defer lockInbound(data.Id).Unlock()
  3471. clients, err := inboundSvc.GetClients(data)
  3472. if err != nil {
  3473. return false, err
  3474. }
  3475. var settings map[string]any
  3476. err = json.Unmarshal([]byte(data.Settings), &settings)
  3477. if err != nil {
  3478. return false, err
  3479. }
  3480. interfaceClients := settings["clients"].([]any)
  3481. oldInbound, err := inboundSvc.GetInbound(data.Id)
  3482. if err != nil {
  3483. return false, err
  3484. }
  3485. oldClients, err := inboundSvc.GetClients(oldInbound)
  3486. if err != nil {
  3487. return false, err
  3488. }
  3489. oldEmail := ""
  3490. newClientId := ""
  3491. clientIndex := -1
  3492. for index, oldClient := range oldClients {
  3493. oldClientId := ""
  3494. switch oldInbound.Protocol {
  3495. case "trojan":
  3496. oldClientId = oldClient.Password
  3497. newClientId = clients[0].Password
  3498. case "shadowsocks":
  3499. oldClientId = oldClient.Email
  3500. newClientId = clients[0].Email
  3501. case "hysteria":
  3502. oldClientId = oldClient.Auth
  3503. newClientId = clients[0].Auth
  3504. default:
  3505. oldClientId = oldClient.ID
  3506. newClientId = clients[0].ID
  3507. }
  3508. if clientId == oldClientId {
  3509. oldEmail = oldClient.Email
  3510. clientIndex = index
  3511. break
  3512. }
  3513. }
  3514. if clientIndex == -1 {
  3515. var rec model.ClientRecord
  3516. var lookupErr error
  3517. switch oldInbound.Protocol {
  3518. case "trojan":
  3519. lookupErr = database.GetDB().Where("password = ?", clientId).First(&rec).Error
  3520. case "shadowsocks":
  3521. lookupErr = database.GetDB().Where("email = ?", clientId).First(&rec).Error
  3522. case "hysteria":
  3523. lookupErr = database.GetDB().Where("auth = ?", clientId).First(&rec).Error
  3524. default:
  3525. lookupErr = database.GetDB().Where("uuid = ?", clientId).First(&rec).Error
  3526. }
  3527. if lookupErr == nil && rec.Email != "" {
  3528. for index, oldClient := range oldClients {
  3529. if oldClient.Email == rec.Email {
  3530. oldEmail = oldClient.Email
  3531. clientIndex = index
  3532. break
  3533. }
  3534. }
  3535. }
  3536. }
  3537. if newClientId == "" || clientIndex == -1 {
  3538. return false, common.NewError("empty client ID")
  3539. }
  3540. if strings.TrimSpace(clients[0].Email) == "" {
  3541. return false, common.NewError("client email is required")
  3542. }
  3543. if clients[0].Email != oldEmail {
  3544. existEmail, err := s.checkEmailsExistForClients(inboundSvc, clients, nil)
  3545. if err != nil {
  3546. return false, err
  3547. }
  3548. if existEmail != "" {
  3549. return false, common.NewError("Duplicate email:", existEmail)
  3550. }
  3551. }
  3552. var oldSettings map[string]any
  3553. err = json.Unmarshal([]byte(oldInbound.Settings), &oldSettings)
  3554. if err != nil {
  3555. return false, err
  3556. }
  3557. settingsClients := oldSettings["clients"].([]any)
  3558. var preservedCreated any
  3559. var preservedSubID string
  3560. if clientIndex >= 0 && clientIndex < len(settingsClients) {
  3561. if oldMap, ok := settingsClients[clientIndex].(map[string]any); ok {
  3562. if v, ok2 := oldMap["created_at"]; ok2 {
  3563. preservedCreated = v
  3564. }
  3565. preservedSubID, _ = oldMap["subId"].(string)
  3566. }
  3567. }
  3568. if len(interfaceClients) > 0 {
  3569. if newMap, ok := interfaceClients[0].(map[string]any); ok {
  3570. if preservedCreated == nil {
  3571. preservedCreated = time.Now().Unix() * 1000
  3572. }
  3573. newMap["created_at"] = preservedCreated
  3574. newMap["updated_at"] = time.Now().Unix() * 1000
  3575. newSub, _ := newMap["subId"].(string)
  3576. if strings.TrimSpace(newSub) == "" {
  3577. if strings.TrimSpace(preservedSubID) != "" {
  3578. newMap["subId"] = preservedSubID
  3579. } else {
  3580. newMap["subId"] = random.NumLower(16)
  3581. }
  3582. }
  3583. interfaceClients[0] = newMap
  3584. }
  3585. }
  3586. if oldInbound.Protocol == model.Shadowsocks {
  3587. applyShadowsocksClientMethod(interfaceClients, oldSettings)
  3588. }
  3589. settingsClients[clientIndex] = interfaceClients[0]
  3590. oldSettings["clients"] = settingsClients
  3591. if oldInbound.Protocol == model.VLESS {
  3592. hasVisionFlow := false
  3593. for _, c := range settingsClients {
  3594. cm, ok := c.(map[string]any)
  3595. if !ok {
  3596. continue
  3597. }
  3598. if flow, _ := cm["flow"].(string); flow == "xtls-rprx-vision" {
  3599. hasVisionFlow = true
  3600. break
  3601. }
  3602. }
  3603. if !hasVisionFlow {
  3604. delete(oldSettings, "testseed")
  3605. }
  3606. }
  3607. newSettings, err := json.MarshalIndent(oldSettings, "", " ")
  3608. if err != nil {
  3609. return false, err
  3610. }
  3611. oldInbound.Settings = string(newSettings)
  3612. db := database.GetDB()
  3613. tx := db.Begin()
  3614. markDirty := false
  3615. defer func() {
  3616. if err != nil {
  3617. tx.Rollback()
  3618. return
  3619. }
  3620. tx.Commit()
  3621. if markDirty && oldInbound.NodeID != nil {
  3622. if dErr := (&NodeService{}).MarkNodeDirty(*oldInbound.NodeID); dErr != nil {
  3623. logger.Warning("mark node dirty failed:", dErr)
  3624. }
  3625. }
  3626. }()
  3627. if len(clients[0].Email) > 0 {
  3628. if len(oldEmail) > 0 {
  3629. emailUnchanged := strings.EqualFold(oldEmail, clients[0].Email)
  3630. targetExists := int64(0)
  3631. if !emailUnchanged {
  3632. if err = tx.Model(xray.ClientTraffic{}).Where("email = ?", clients[0].Email).Count(&targetExists).Error; err != nil {
  3633. return false, err
  3634. }
  3635. }
  3636. if emailUnchanged || targetExists == 0 {
  3637. err = inboundSvc.UpdateClientStat(tx, oldEmail, &clients[0])
  3638. if err != nil {
  3639. return false, err
  3640. }
  3641. err = inboundSvc.UpdateClientIPs(tx, oldEmail, clients[0].Email)
  3642. if err != nil {
  3643. return false, err
  3644. }
  3645. } else {
  3646. stillUsed, sErr := inboundSvc.emailUsedByOtherInbounds(oldEmail, data.Id)
  3647. if sErr != nil {
  3648. return false, sErr
  3649. }
  3650. if !stillUsed {
  3651. if err = inboundSvc.DelClientStat(tx, oldEmail); err != nil {
  3652. return false, err
  3653. }
  3654. if err = inboundSvc.DelClientIPs(tx, oldEmail); err != nil {
  3655. return false, err
  3656. }
  3657. }
  3658. if err = inboundSvc.UpdateClientStat(tx, clients[0].Email, &clients[0]); err != nil {
  3659. return false, err
  3660. }
  3661. }
  3662. } else {
  3663. inboundSvc.AddClientStat(tx, data.Id, &clients[0])
  3664. }
  3665. } else {
  3666. stillUsed, err := inboundSvc.emailUsedByOtherInbounds(oldEmail, data.Id)
  3667. if err != nil {
  3668. return false, err
  3669. }
  3670. if !stillUsed {
  3671. err = inboundSvc.DelClientStat(tx, oldEmail)
  3672. if err != nil {
  3673. return false, err
  3674. }
  3675. err = inboundSvc.DelClientIPs(tx, oldEmail)
  3676. if err != nil {
  3677. return false, err
  3678. }
  3679. }
  3680. }
  3681. needRestart := false
  3682. if len(oldEmail) > 0 {
  3683. rt, push, dirty, perr := inboundSvc.nodePushPlan(oldInbound)
  3684. if perr != nil {
  3685. err = perr
  3686. return false, err
  3687. }
  3688. if dirty {
  3689. markDirty = true
  3690. }
  3691. if oldInbound.NodeID == nil {
  3692. if !push {
  3693. needRestart = true
  3694. } else {
  3695. if oldClients[clientIndex].Enable {
  3696. err1 := rt.RemoveUser(context.Background(), oldInbound, oldEmail)
  3697. if err1 == nil {
  3698. logger.Debug("Old client deleted on", rt.Name(), ":", oldEmail)
  3699. } else if strings.Contains(err1.Error(), fmt.Sprintf("User %s not found.", oldEmail)) {
  3700. logger.Debug("User is already deleted. Nothing to do more...")
  3701. } else {
  3702. logger.Debug("Error in deleting client on", rt.Name(), ":", err1)
  3703. needRestart = true
  3704. }
  3705. }
  3706. if clients[0].Enable {
  3707. cipher := ""
  3708. if oldInbound.Protocol == "shadowsocks" {
  3709. cipher = oldSettings["method"].(string)
  3710. }
  3711. err1 := rt.AddUser(context.Background(), oldInbound, map[string]any{
  3712. "email": clients[0].Email,
  3713. "id": clients[0].ID,
  3714. "security": clients[0].Security,
  3715. "flow": clients[0].Flow,
  3716. "auth": clients[0].Auth,
  3717. "password": clients[0].Password,
  3718. "cipher": cipher,
  3719. })
  3720. if err1 == nil {
  3721. logger.Debug("Client edited on", rt.Name(), ":", clients[0].Email)
  3722. } else {
  3723. logger.Debug("Error in adding client on", rt.Name(), ":", err1)
  3724. needRestart = true
  3725. }
  3726. }
  3727. }
  3728. } else if push {
  3729. if err1 := rt.UpdateUser(context.Background(), oldInbound, oldEmail, clients[0]); err1 != nil {
  3730. logger.Warning("Error in updating client on", rt.Name(), ":", err1)
  3731. markDirty = true
  3732. }
  3733. }
  3734. } else {
  3735. logger.Debug("Client old email not found")
  3736. needRestart = true
  3737. }
  3738. if err = tx.Save(oldInbound).Error; err != nil {
  3739. return false, err
  3740. }
  3741. finalClients, gcErr := inboundSvc.GetClients(oldInbound)
  3742. if gcErr != nil {
  3743. err = gcErr
  3744. return false, err
  3745. }
  3746. if err = s.SyncInbound(tx, oldInbound.Id, finalClients); err != nil {
  3747. return false, err
  3748. }
  3749. return needRestart, nil
  3750. }
  3751. func (s *ClientService) DelInboundClient(inboundSvc *InboundService, inboundId int, clientId string, keepTraffic bool) (bool, error) {
  3752. defer lockInbound(inboundId).Unlock()
  3753. oldInbound, err := inboundSvc.GetInbound(inboundId)
  3754. if err != nil {
  3755. logger.Error("Load Old Data Error")
  3756. return false, err
  3757. }
  3758. var settings map[string]any
  3759. err = json.Unmarshal([]byte(oldInbound.Settings), &settings)
  3760. if err != nil {
  3761. return false, err
  3762. }
  3763. email := ""
  3764. client_key := "id"
  3765. switch oldInbound.Protocol {
  3766. case "trojan":
  3767. client_key = "password"
  3768. case "shadowsocks":
  3769. client_key = "email"
  3770. case "hysteria":
  3771. client_key = "auth"
  3772. }
  3773. interfaceClients := settings["clients"].([]any)
  3774. var newClients []any
  3775. needApiDel := false
  3776. clientFound := false
  3777. for _, client := range interfaceClients {
  3778. c := client.(map[string]any)
  3779. c_id := c[client_key].(string)
  3780. if c_id == clientId {
  3781. clientFound = true
  3782. email, _ = c["email"].(string)
  3783. needApiDel, _ = c["enable"].(bool)
  3784. } else {
  3785. newClients = append(newClients, client)
  3786. }
  3787. }
  3788. if !clientFound {
  3789. return false, common.NewError("Client Not Found In Inbound For ID:", clientId)
  3790. }
  3791. db := database.GetDB()
  3792. newClients = compactOrphans(db, newClients)
  3793. if newClients == nil {
  3794. newClients = []any{}
  3795. }
  3796. settings["clients"] = newClients
  3797. newSettings, err := json.MarshalIndent(settings, "", " ")
  3798. if err != nil {
  3799. return false, err
  3800. }
  3801. oldInbound.Settings = string(newSettings)
  3802. emailShared, err := inboundSvc.emailUsedByOtherInbounds(email, inboundId)
  3803. if err != nil {
  3804. return false, err
  3805. }
  3806. if !emailShared && !keepTraffic {
  3807. err = inboundSvc.DelClientIPs(db, email)
  3808. if err != nil {
  3809. logger.Error("Error in delete client IPs")
  3810. return false, err
  3811. }
  3812. }
  3813. needRestart := false
  3814. markDirty := false
  3815. if len(email) > 0 {
  3816. var enables []bool
  3817. err = db.Model(xray.ClientTraffic{}).Where("email = ?", email).Limit(1).Pluck("enable", &enables).Error
  3818. if err != nil {
  3819. logger.Error("Get stats error")
  3820. return false, err
  3821. }
  3822. notDepleted := len(enables) > 0 && enables[0]
  3823. if !emailShared && !keepTraffic {
  3824. err = inboundSvc.DelClientStat(db, email)
  3825. if err != nil {
  3826. logger.Error("Delete stats Data Error")
  3827. return false, err
  3828. }
  3829. }
  3830. if needApiDel && notDepleted && oldInbound.NodeID == nil {
  3831. rt, rterr := inboundSvc.runtimeFor(oldInbound)
  3832. if rterr != nil {
  3833. needRestart = true
  3834. } else {
  3835. err1 := rt.RemoveUser(context.Background(), oldInbound, email)
  3836. if err1 == nil {
  3837. logger.Debug("Client deleted on", rt.Name(), ":", email)
  3838. needRestart = false
  3839. } else if strings.Contains(err1.Error(), fmt.Sprintf("User %s not found.", email)) {
  3840. logger.Debug("User is already deleted. Nothing to do more...")
  3841. } else {
  3842. logger.Debug("Error in deleting client on", rt.Name(), ":", err1)
  3843. needRestart = true
  3844. }
  3845. }
  3846. }
  3847. }
  3848. if oldInbound.NodeID != nil && len(email) > 0 {
  3849. rt, push, dirty, perr := inboundSvc.nodePushPlan(oldInbound)
  3850. if perr != nil {
  3851. return false, perr
  3852. }
  3853. if dirty {
  3854. markDirty = true
  3855. }
  3856. if push {
  3857. if err1 := rt.DeleteUser(context.Background(), oldInbound, email); err1 != nil {
  3858. logger.Warning("Error in deleting client on", rt.Name(), ":", err1)
  3859. markDirty = true
  3860. }
  3861. }
  3862. }
  3863. if err := db.Save(oldInbound).Error; err != nil {
  3864. return false, err
  3865. }
  3866. finalClients, gcErr := inboundSvc.GetClients(oldInbound)
  3867. if gcErr != nil {
  3868. return false, gcErr
  3869. }
  3870. if err := s.SyncInbound(db, inboundId, finalClients); err != nil {
  3871. return false, err
  3872. }
  3873. if markDirty && oldInbound.NodeID != nil {
  3874. if dErr := (&NodeService{}).MarkNodeDirty(*oldInbound.NodeID); dErr != nil {
  3875. logger.Warning("mark node dirty failed:", dErr)
  3876. }
  3877. }
  3878. return needRestart, nil
  3879. }
  3880. func (s *ClientService) DelInboundClientByEmail(inboundSvc *InboundService, inboundId int, email string, keepTraffic bool) (bool, error) {
  3881. defer lockInbound(inboundId).Unlock()
  3882. oldInbound, err := inboundSvc.GetInbound(inboundId)
  3883. if err != nil {
  3884. logger.Error("Load Old Data Error")
  3885. return false, err
  3886. }
  3887. var settings map[string]any
  3888. if err := json.Unmarshal([]byte(oldInbound.Settings), &settings); err != nil {
  3889. return false, err
  3890. }
  3891. interfaceClients, ok := settings["clients"].([]any)
  3892. if !ok {
  3893. return false, common.NewError("invalid clients format in inbound settings")
  3894. }
  3895. var newClients []any
  3896. needApiDel := false
  3897. found := false
  3898. for _, client := range interfaceClients {
  3899. c, ok := client.(map[string]any)
  3900. if !ok {
  3901. continue
  3902. }
  3903. if cEmail, ok := c["email"].(string); ok && cEmail == email {
  3904. found = true
  3905. needApiDel, _ = c["enable"].(bool)
  3906. } else {
  3907. newClients = append(newClients, client)
  3908. }
  3909. }
  3910. if !found {
  3911. return false, common.NewError(fmt.Sprintf("client with email %s not found", email))
  3912. }
  3913. db := database.GetDB()
  3914. newClients = compactOrphans(db, newClients)
  3915. if newClients == nil {
  3916. newClients = []any{}
  3917. }
  3918. settings["clients"] = newClients
  3919. newSettings, err := json.MarshalIndent(settings, "", " ")
  3920. if err != nil {
  3921. return false, err
  3922. }
  3923. oldInbound.Settings = string(newSettings)
  3924. emailShared, err := inboundSvc.emailUsedByOtherInbounds(email, inboundId)
  3925. if err != nil {
  3926. return false, err
  3927. }
  3928. if !emailShared && !keepTraffic {
  3929. if err := inboundSvc.DelClientIPs(db, email); err != nil {
  3930. logger.Error("Error in delete client IPs")
  3931. return false, err
  3932. }
  3933. }
  3934. needRestart := false
  3935. markDirty := false
  3936. if len(email) > 0 && !emailShared {
  3937. if !keepTraffic {
  3938. traffic, err := inboundSvc.GetClientTrafficByEmail(email)
  3939. if err != nil {
  3940. return false, err
  3941. }
  3942. if traffic != nil {
  3943. if err := inboundSvc.DelClientStat(db, email); err != nil {
  3944. logger.Error("Delete stats Data Error")
  3945. return false, err
  3946. }
  3947. }
  3948. }
  3949. if needApiDel {
  3950. rt, push, dirty, perr := inboundSvc.nodePushPlan(oldInbound)
  3951. if perr != nil {
  3952. return false, perr
  3953. }
  3954. if dirty {
  3955. markDirty = true
  3956. }
  3957. if oldInbound.NodeID == nil {
  3958. if !push {
  3959. needRestart = true
  3960. } else if err1 := rt.RemoveUser(context.Background(), oldInbound, email); err1 == nil {
  3961. logger.Debug("Client deleted on", rt.Name(), ":", email)
  3962. needRestart = false
  3963. } else if strings.Contains(err1.Error(), fmt.Sprintf("User %s not found.", email)) {
  3964. logger.Debug("User is already deleted. Nothing to do more...")
  3965. } else {
  3966. logger.Debug("Error in deleting client on", rt.Name(), ":", email)
  3967. needRestart = true
  3968. }
  3969. } else if push {
  3970. if err1 := rt.DeleteUser(context.Background(), oldInbound, email); err1 != nil {
  3971. logger.Warning("Error in deleting client on", rt.Name(), ":", err1)
  3972. markDirty = true
  3973. }
  3974. }
  3975. }
  3976. }
  3977. if err := db.Save(oldInbound).Error; err != nil {
  3978. return false, err
  3979. }
  3980. finalClients, gcErr := inboundSvc.GetClients(oldInbound)
  3981. if gcErr != nil {
  3982. return false, gcErr
  3983. }
  3984. if err := s.SyncInbound(db, inboundId, finalClients); err != nil {
  3985. return false, err
  3986. }
  3987. if markDirty && oldInbound.NodeID != nil {
  3988. if dErr := (&NodeService{}).MarkNodeDirty(*oldInbound.NodeID); dErr != nil {
  3989. logger.Warning("mark node dirty failed:", dErr)
  3990. }
  3991. }
  3992. return needRestart, nil
  3993. }
  3994. func (s *ClientService) SetClientTelegramUserID(inboundSvc *InboundService, trafficId int, tgId int64) (bool, error) {
  3995. traffic, inbound, err := inboundSvc.GetClientInboundByTrafficID(trafficId)
  3996. if err != nil {
  3997. return false, err
  3998. }
  3999. if inbound == nil {
  4000. return false, common.NewError("Inbound Not Found For Traffic ID:", trafficId)
  4001. }
  4002. clientEmail := traffic.Email
  4003. oldClients, err := inboundSvc.GetClients(inbound)
  4004. if err != nil {
  4005. return false, err
  4006. }
  4007. clientId := ""
  4008. for _, oldClient := range oldClients {
  4009. if oldClient.Email == clientEmail {
  4010. switch inbound.Protocol {
  4011. case "trojan":
  4012. clientId = oldClient.Password
  4013. case "shadowsocks":
  4014. clientId = oldClient.Email
  4015. default:
  4016. clientId = oldClient.ID
  4017. }
  4018. break
  4019. }
  4020. }
  4021. if len(clientId) == 0 {
  4022. return false, common.NewError("Client Not Found For Email:", clientEmail)
  4023. }
  4024. var settings map[string]any
  4025. err = json.Unmarshal([]byte(inbound.Settings), &settings)
  4026. if err != nil {
  4027. return false, err
  4028. }
  4029. clients := settings["clients"].([]any)
  4030. var newClients []any
  4031. for client_index := range clients {
  4032. c := clients[client_index].(map[string]any)
  4033. if c["email"] == clientEmail {
  4034. c["tgId"] = tgId
  4035. c["updated_at"] = time.Now().Unix() * 1000
  4036. newClients = append(newClients, any(c))
  4037. }
  4038. }
  4039. settings["clients"] = newClients
  4040. modifiedSettings, err := json.MarshalIndent(settings, "", " ")
  4041. if err != nil {
  4042. return false, err
  4043. }
  4044. inbound.Settings = string(modifiedSettings)
  4045. needRestart, err := s.UpdateInboundClient(inboundSvc, inbound, clientId)
  4046. return needRestart, err
  4047. }
  4048. func (s *ClientService) checkIsEnabledByEmail(inboundSvc *InboundService, clientEmail string) (bool, error) {
  4049. _, inbound, err := inboundSvc.GetClientInboundByEmail(clientEmail)
  4050. if err != nil {
  4051. return false, err
  4052. }
  4053. if inbound == nil {
  4054. return false, common.NewError("Inbound Not Found For Email:", clientEmail)
  4055. }
  4056. clients, err := inboundSvc.GetClients(inbound)
  4057. if err != nil {
  4058. return false, err
  4059. }
  4060. isEnable := false
  4061. for _, client := range clients {
  4062. if client.Email == clientEmail {
  4063. isEnable = client.Enable
  4064. break
  4065. }
  4066. }
  4067. return isEnable, err
  4068. }
  4069. func (s *ClientService) ToggleClientEnableByEmail(inboundSvc *InboundService, clientEmail string) (bool, bool, error) {
  4070. _, inbound, err := inboundSvc.GetClientInboundByEmail(clientEmail)
  4071. if err != nil {
  4072. return false, false, err
  4073. }
  4074. if inbound == nil {
  4075. return false, false, common.NewError("Inbound Not Found For Email:", clientEmail)
  4076. }
  4077. oldClients, err := inboundSvc.GetClients(inbound)
  4078. if err != nil {
  4079. return false, false, err
  4080. }
  4081. clientId := ""
  4082. clientOldEnabled := false
  4083. for _, oldClient := range oldClients {
  4084. if oldClient.Email == clientEmail {
  4085. switch inbound.Protocol {
  4086. case "trojan":
  4087. clientId = oldClient.Password
  4088. case "shadowsocks":
  4089. clientId = oldClient.Email
  4090. default:
  4091. clientId = oldClient.ID
  4092. }
  4093. clientOldEnabled = oldClient.Enable
  4094. break
  4095. }
  4096. }
  4097. if len(clientId) == 0 {
  4098. return false, false, common.NewError("Client Not Found For Email:", clientEmail)
  4099. }
  4100. var settings map[string]any
  4101. err = json.Unmarshal([]byte(inbound.Settings), &settings)
  4102. if err != nil {
  4103. return false, false, err
  4104. }
  4105. clients := settings["clients"].([]any)
  4106. var newClients []any
  4107. for client_index := range clients {
  4108. c := clients[client_index].(map[string]any)
  4109. if c["email"] == clientEmail {
  4110. c["enable"] = !clientOldEnabled
  4111. c["updated_at"] = time.Now().Unix() * 1000
  4112. newClients = append(newClients, any(c))
  4113. }
  4114. }
  4115. settings["clients"] = newClients
  4116. modifiedSettings, err := json.MarshalIndent(settings, "", " ")
  4117. if err != nil {
  4118. return false, false, err
  4119. }
  4120. inbound.Settings = string(modifiedSettings)
  4121. needRestart, err := s.UpdateInboundClient(inboundSvc, inbound, clientId)
  4122. if err != nil {
  4123. return false, needRestart, err
  4124. }
  4125. return !clientOldEnabled, needRestart, nil
  4126. }
  4127. func (s *ClientService) SetClientEnableByEmail(inboundSvc *InboundService, clientEmail string, enable bool) (bool, bool, error) {
  4128. current, err := s.checkIsEnabledByEmail(inboundSvc, clientEmail)
  4129. if err != nil {
  4130. return false, false, err
  4131. }
  4132. if current == enable {
  4133. return false, false, nil
  4134. }
  4135. newEnabled, needRestart, err := s.ToggleClientEnableByEmail(inboundSvc, clientEmail)
  4136. if err != nil {
  4137. return false, needRestart, err
  4138. }
  4139. return newEnabled == enable, needRestart, nil
  4140. }
  4141. func (s *ClientService) ResetClientIpLimitByEmail(inboundSvc *InboundService, clientEmail string, count int) (bool, error) {
  4142. _, inbound, err := inboundSvc.GetClientInboundByEmail(clientEmail)
  4143. if err != nil {
  4144. return false, err
  4145. }
  4146. if inbound == nil {
  4147. return false, common.NewError("Inbound Not Found For Email:", clientEmail)
  4148. }
  4149. oldClients, err := inboundSvc.GetClients(inbound)
  4150. if err != nil {
  4151. return false, err
  4152. }
  4153. clientId := ""
  4154. for _, oldClient := range oldClients {
  4155. if oldClient.Email == clientEmail {
  4156. switch inbound.Protocol {
  4157. case "trojan":
  4158. clientId = oldClient.Password
  4159. case "shadowsocks":
  4160. clientId = oldClient.Email
  4161. default:
  4162. clientId = oldClient.ID
  4163. }
  4164. break
  4165. }
  4166. }
  4167. if len(clientId) == 0 {
  4168. return false, common.NewError("Client Not Found For Email:", clientEmail)
  4169. }
  4170. var settings map[string]any
  4171. err = json.Unmarshal([]byte(inbound.Settings), &settings)
  4172. if err != nil {
  4173. return false, err
  4174. }
  4175. clients := settings["clients"].([]any)
  4176. var newClients []any
  4177. for client_index := range clients {
  4178. c := clients[client_index].(map[string]any)
  4179. if c["email"] == clientEmail {
  4180. c["limitIp"] = count
  4181. c["updated_at"] = time.Now().Unix() * 1000
  4182. newClients = append(newClients, any(c))
  4183. }
  4184. }
  4185. settings["clients"] = newClients
  4186. modifiedSettings, err := json.MarshalIndent(settings, "", " ")
  4187. if err != nil {
  4188. return false, err
  4189. }
  4190. inbound.Settings = string(modifiedSettings)
  4191. needRestart, err := s.UpdateInboundClient(inboundSvc, inbound, clientId)
  4192. return needRestart, err
  4193. }
  4194. func (s *ClientService) ResetClientExpiryTimeByEmail(inboundSvc *InboundService, clientEmail string, expiry_time int64) (bool, error) {
  4195. _, inbound, err := inboundSvc.GetClientInboundByEmail(clientEmail)
  4196. if err != nil {
  4197. return false, err
  4198. }
  4199. if inbound == nil {
  4200. return false, common.NewError("Inbound Not Found For Email:", clientEmail)
  4201. }
  4202. oldClients, err := inboundSvc.GetClients(inbound)
  4203. if err != nil {
  4204. return false, err
  4205. }
  4206. clientId := ""
  4207. for _, oldClient := range oldClients {
  4208. if oldClient.Email == clientEmail {
  4209. switch inbound.Protocol {
  4210. case "trojan":
  4211. clientId = oldClient.Password
  4212. case "shadowsocks":
  4213. clientId = oldClient.Email
  4214. default:
  4215. clientId = oldClient.ID
  4216. }
  4217. break
  4218. }
  4219. }
  4220. if len(clientId) == 0 {
  4221. return false, common.NewError("Client Not Found For Email:", clientEmail)
  4222. }
  4223. var settings map[string]any
  4224. err = json.Unmarshal([]byte(inbound.Settings), &settings)
  4225. if err != nil {
  4226. return false, err
  4227. }
  4228. clients := settings["clients"].([]any)
  4229. var newClients []any
  4230. for client_index := range clients {
  4231. c := clients[client_index].(map[string]any)
  4232. if c["email"] == clientEmail {
  4233. c["expiryTime"] = expiry_time
  4234. c["updated_at"] = time.Now().Unix() * 1000
  4235. newClients = append(newClients, any(c))
  4236. }
  4237. }
  4238. settings["clients"] = newClients
  4239. modifiedSettings, err := json.MarshalIndent(settings, "", " ")
  4240. if err != nil {
  4241. return false, err
  4242. }
  4243. inbound.Settings = string(modifiedSettings)
  4244. needRestart, err := s.UpdateInboundClient(inboundSvc, inbound, clientId)
  4245. return needRestart, err
  4246. }
  4247. func (s *ClientService) ResetClientTrafficLimitByEmail(inboundSvc *InboundService, clientEmail string, totalGB int) (bool, error) {
  4248. if totalGB < 0 {
  4249. return false, common.NewError("totalGB must be >= 0")
  4250. }
  4251. _, inbound, err := inboundSvc.GetClientInboundByEmail(clientEmail)
  4252. if err != nil {
  4253. return false, err
  4254. }
  4255. if inbound == nil {
  4256. return false, common.NewError("Inbound Not Found For Email:", clientEmail)
  4257. }
  4258. oldClients, err := inboundSvc.GetClients(inbound)
  4259. if err != nil {
  4260. return false, err
  4261. }
  4262. clientId := ""
  4263. for _, oldClient := range oldClients {
  4264. if oldClient.Email == clientEmail {
  4265. switch inbound.Protocol {
  4266. case "trojan":
  4267. clientId = oldClient.Password
  4268. case "shadowsocks":
  4269. clientId = oldClient.Email
  4270. default:
  4271. clientId = oldClient.ID
  4272. }
  4273. break
  4274. }
  4275. }
  4276. if len(clientId) == 0 {
  4277. return false, common.NewError("Client Not Found For Email:", clientEmail)
  4278. }
  4279. var settings map[string]any
  4280. err = json.Unmarshal([]byte(inbound.Settings), &settings)
  4281. if err != nil {
  4282. return false, err
  4283. }
  4284. clients := settings["clients"].([]any)
  4285. var newClients []any
  4286. for client_index := range clients {
  4287. c := clients[client_index].(map[string]any)
  4288. if c["email"] == clientEmail {
  4289. c["totalGB"] = totalGB * 1024 * 1024 * 1024
  4290. c["updated_at"] = time.Now().Unix() * 1000
  4291. newClients = append(newClients, any(c))
  4292. }
  4293. }
  4294. settings["clients"] = newClients
  4295. modifiedSettings, err := json.MarshalIndent(settings, "", " ")
  4296. if err != nil {
  4297. return false, err
  4298. }
  4299. inbound.Settings = string(modifiedSettings)
  4300. needRestart, err := s.UpdateInboundClient(inboundSvc, inbound, clientId)
  4301. return needRestart, err
  4302. }