@@ -4,11 +4,11 @@ import (
44 "bytes"
55 "encoding/json"
66 "fmt"
7+ "io"
78 "net/http"
89 "os"
910 "time"
1011
11- "github.com/mhsanaei/3x-ui/v2/logger"
1212 "github.com/mhsanaei/3x-ui/v2/util/common"
1313)
1414
@@ -18,156 +18,192 @@ type WarpService struct {
1818 SettingService
1919}
2020
21+ const (
22+ warpAPIBase = "https://api.cloudflareclient.com/v0a4005"
23+ warpClientVer = "a-6.30-3596"
24+ )
25+
26+ var warpHTTPClient = & http.Client {Timeout : 15 * time .Second }
27+
2128func (s * WarpService ) GetWarpData () (string , error ) {
22- warp , err := s .SettingService .GetWarp ()
23- if err != nil {
24- return "" , err
25- }
26- return warp , nil
29+ return s .SettingService .GetWarp ()
2730}
2831
2932func (s * WarpService ) DelWarpData () error {
30- err := s .SettingService .SetWarp ("" )
31- if err != nil {
32- return err
33- }
34- return nil
33+ return s .SettingService .SetWarp ("" )
3534}
3635
3736func (s * WarpService ) GetWarpConfig () (string , error ) {
38- var warpData map [string ]string
39- warp , err := s .SettingService .GetWarp ()
37+ warpData , err := s .loadWarpCreds ()
4038 if err != nil {
4139 return "" , err
4240 }
43- err = json .Unmarshal ([]byte (warp ), & warpData )
44- if err != nil {
45- return "" , err
46- }
47-
48- url := fmt .Sprintf ("https://api.cloudflareclient.com/v0a2158/reg/%s" , warpData ["device_id" ])
4941
50- req , err := http .NewRequest ("GET" , url , nil )
42+ url := fmt .Sprintf ("%s/reg/%s" , warpAPIBase , warpData ["device_id" ])
43+ req , err := http .NewRequest (http .MethodGet , url , nil )
5144 if err != nil {
5245 return "" , err
5346 }
5447 req .Header .Set ("Authorization" , "Bearer " + warpData ["access_token" ])
5548
56- client := & http.Client {}
57- resp , err := client .Do (req )
58- if err != nil {
59- return "" , err
60- }
61- defer resp .Body .Close ()
62- buffer := & bytes.Buffer {}
63- _ , err = buffer .ReadFrom (resp .Body )
49+ body , err := doWarpRequest (req )
6450 if err != nil {
6551 return "" , err
6652 }
67-
68- return buffer .String (), nil
53+ return string (body ), nil
6954}
7055
7156func (s * WarpService ) RegWarp (secretKey string , publicKey string ) (string , error ) {
72- tos := time .Now ().UTC ().Format ("2006-01-02T15:04:05.000Z" )
7357 hostName , _ := os .Hostname ()
74- data := fmt .Sprintf (`{"key":"%s","tos":"%s","type": "PC","model": "x-ui", "name": "%s"}` , publicKey , tos , hostName )
75-
76- url := "https://api.cloudflareclient.com/v0a2158/reg"
77-
78- req , err := http .NewRequest ("POST" , url , bytes .NewBuffer ([]byte (data )))
58+ reqBody , err := json .Marshal (map [string ]any {
59+ "key" : publicKey ,
60+ "tos" : time .Now ().UTC ().Format ("2006-01-02T15:04:05.000Z" ),
61+ "type" : "PC" ,
62+ "model" : "x-ui" ,
63+ "name" : hostName ,
64+ })
7965 if err != nil {
8066 return "" , err
8167 }
8268
83- req .Header .Add ("CF-Client-Version" , "a-7.21-0721" )
84- req .Header .Add ("Content-Type" , "application/json" )
85-
86- client := & http.Client {}
87- resp , err := client .Do (req )
69+ req , err := http .NewRequest (http .MethodPost , warpAPIBase + "/reg" , bytes .NewReader (reqBody ))
8870 if err != nil {
8971 return "" , err
9072 }
91- defer resp .Body .Close ()
92- buffer := & bytes.Buffer {}
93- _ , err = buffer .ReadFrom (resp .Body )
73+ req .Header .Set ("CF-Client-Version" , warpClientVer )
74+ req .Header .Set ("Content-Type" , "application/json" )
75+
76+ body , err := doWarpRequest (req )
9477 if err != nil {
9578 return "" , err
9679 }
9780
98- var rspData map [string ]any
99- err = json .Unmarshal (buffer .Bytes (), & rspData )
100- if err != nil {
81+ var rsp map [string ]any
82+ if err := json .Unmarshal (body , & rsp ); err != nil {
10183 return "" , err
10284 }
10385
104- deviceId := rspData ["id" ].(string )
105- token := rspData ["token" ].(string )
106- license , ok := rspData ["account" ].(map [string ]any )["license" ].(string )
86+ deviceID , ok := rsp ["id" ].(string )
10787 if ! ok {
108- logger .Debug ("Error accessing license value." )
109- return "" , err
88+ return "" , common .NewError ("warp register: missing 'id' in response" )
89+ }
90+ token , ok := rsp ["token" ].(string )
91+ if ! ok {
92+ return "" , common .NewError ("warp register: missing 'token' in response" )
93+ }
94+ account , ok := rsp ["account" ].(map [string ]any )
95+ if ! ok {
96+ return "" , common .NewError ("warp register: missing 'account' in response" )
97+ }
98+ license , ok := account ["license" ].(string )
99+ if ! ok {
100+ return "" , common .NewError ("warp register: missing 'account.license' in response" )
110101 }
111102
112- warpData := fmt .Sprintf ("{\n \" access_token\" : \" %s\" ,\n \" device_id\" : \" %s\" ," , token , deviceId )
113- warpData += fmt .Sprintf ("\n \" license_key\" : \" %s\" ,\n \" private_key\" : \" %s\" \n }" , license , secretKey )
114-
115- s .SettingService .SetWarp (warpData )
116-
117- result := fmt .Sprintf ("{\n \" data\" : %s,\n \" config\" : %s\n }" , warpData , buffer .String ())
103+ warpData := map [string ]string {
104+ "access_token" : token ,
105+ "device_id" : deviceID ,
106+ "license_key" : license ,
107+ "private_key" : secretKey ,
108+ }
109+ warpJSON , err := json .MarshalIndent (warpData , "" , " " )
110+ if err != nil {
111+ return "" , err
112+ }
113+ if err := s .SettingService .SetWarp (string (warpJSON )); err != nil {
114+ return "" , err
115+ }
118116
119- return result , nil
117+ result , err := json .MarshalIndent (map [string ]any {
118+ "data" : warpData ,
119+ "config" : json .RawMessage (body ),
120+ }, "" , " " )
121+ if err != nil {
122+ return "" , err
123+ }
124+ return string (result ), nil
120125}
121126
122127func (s * WarpService ) SetWarpLicense (license string ) (string , error ) {
123- var warpData map [string ]string
124- warp , err := s .SettingService .GetWarp ()
128+ warpData , err := s .loadWarpCreds ()
125129 if err != nil {
126130 return "" , err
127131 }
128- err = json .Unmarshal ([]byte (warp ), & warpData )
132+
133+ url := fmt .Sprintf ("%s/reg/%s/account" , warpAPIBase , warpData ["device_id" ])
134+ reqBody , err := json .Marshal (map [string ]string {"license" : license })
129135 if err != nil {
130136 return "" , err
131137 }
132138
133- url := fmt .Sprintf ("https://api.cloudflareclient.com/v0a2158/reg/%s/account" , warpData ["device_id" ])
134- data := fmt .Sprintf (`{"license": "%s"}` , license )
135-
136- req , err := http .NewRequest ("PUT" , url , bytes .NewBuffer ([]byte (data )))
139+ req , err := http .NewRequest (http .MethodPut , url , bytes .NewReader (reqBody ))
137140 if err != nil {
138141 return "" , err
139142 }
140143 req .Header .Set ("Authorization" , "Bearer " + warpData ["access_token" ])
144+ req .Header .Set ("Content-Type" , "application/json" )
141145
142- client := & http.Client {}
143- resp , err := client .Do (req )
144- if err != nil {
145- return "" , err
146- }
147- defer resp .Body .Close ()
148- buffer := & bytes.Buffer {}
149- _ , err = buffer .ReadFrom (resp .Body )
146+ body , err := doWarpRequest (req )
150147 if err != nil {
151148 return "" , err
152149 }
153150
154151 var response map [string ]any
155- err = json .Unmarshal (buffer .Bytes (), & response )
156- if err != nil {
152+ if err := json .Unmarshal (body , & response ); err != nil {
157153 return "" , err
158154 }
159- if response ["success" ] == false {
160- errorArr , _ := response ["errors" ].([]any )
161- errorObj := errorArr [0 ].(map [string ]any )
162- return "" , common .NewError (errorObj ["code" ], errorObj ["message" ])
155+ if success , _ := response ["success" ].(bool ); ! success {
156+ if errorArr , ok := response ["errors" ].([]any ); ok && len (errorArr ) > 0 {
157+ if errorObj , ok := errorArr [0 ].(map [string ]any ); ok {
158+ return "" , common .NewError (errorObj ["code" ], errorObj ["message" ])
159+ }
160+ }
161+ return "" , common .NewError ("warp set license failed: unknown error" )
163162 }
164163
165164 warpData ["license_key" ] = license
166165 newWarpData , err := json .MarshalIndent (warpData , "" , " " )
167166 if err != nil {
168167 return "" , err
169168 }
170- s .SettingService .SetWarp (string (newWarpData ))
171-
169+ if err := s .SettingService .SetWarp (string (newWarpData )); err != nil {
170+ return "" , err
171+ }
172172 return string (newWarpData ), nil
173173}
174+
175+ // loadWarpCreds reads the stored warp JSON and ensures access_token + device_id are set.
176+ func (s * WarpService ) loadWarpCreds () (map [string ]string , error ) {
177+ warp , err := s .SettingService .GetWarp ()
178+ if err != nil {
179+ return nil , err
180+ }
181+ var data map [string ]string
182+ if err := json .Unmarshal ([]byte (warp ), & data ); err != nil {
183+ return nil , err
184+ }
185+ if data ["access_token" ] == "" || data ["device_id" ] == "" {
186+ return nil , common .NewError ("warp not registered: missing access_token or device_id" )
187+ }
188+ return data , nil
189+ }
190+
191+ // doWarpRequest sends the request and returns the response body on 2xx.
192+ // Non-2xx responses are returned as errors including the status code and body.
193+ func doWarpRequest (req * http.Request ) ([]byte , error ) {
194+ resp , err := warpHTTPClient .Do (req )
195+ if err != nil {
196+ return nil , err
197+ }
198+ defer resp .Body .Close ()
199+
200+ body , err := io .ReadAll (resp .Body )
201+ if err != nil {
202+ return nil , err
203+ }
204+ if resp .StatusCode < 200 || resp .StatusCode >= 300 {
205+ return nil , common .NewErrorf ("warp api %s %s returned status %d: %s" ,
206+ req .Method , req .URL .Path , resp .StatusCode , string (body ))
207+ }
208+ return body , nil
209+ }
0 commit comments