diff --git a/src/x3-client/client.go b/src/x3-client/client.go new file mode 100644 index 0000000..c13bf66 --- /dev/null +++ b/src/x3-client/client.go @@ -0,0 +1,27 @@ +package x3_client + +import ( + "net/http" + "net/http/cookiejar" +) + +type X3Client struct { + Host string + + httpClient *http.Client +} + +func NewX3Client(host, port string) (*X3Client, error) { + jar, err := cookiejar.New(nil) + if err != nil { + return nil, err + } + + return &X3Client{ + Host: host + ":" + port, + + httpClient: &http.Client{ + Jar: jar, + }, + }, nil +} diff --git a/src/x3-client/http.go b/src/x3-client/http.go new file mode 100644 index 0000000..d440ef0 --- /dev/null +++ b/src/x3-client/http.go @@ -0,0 +1,163 @@ +package x3_client + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" +) + +func makeGetRequest[R any](httpClient *http.Client, url string) (R, error) { + var res R + + resp, err := httpClient.Get(url) + if err != nil { + return res, err + } + respBody, err := io.ReadAll(resp.Body) + if err != nil { + return res, err + } + err = json.Unmarshal(respBody, &res) + if err != nil { + return res, err + } + return res, nil +} + +func makePostRequest[Q, R any](httpClient *http.Client, url string, req Q) (R, error) { + var res R + body, err := json.Marshal(req) + if err != nil { + return res, err + } + resp, err := httpClient.Post(url, "application/json", bytes.NewReader(body)) + if err != nil { + return res, err + } + respBody, err := io.ReadAll(resp.Body) + if err != nil { + return res, err + } + err = json.Unmarshal(respBody, &res) + if err != nil { + return res, err + } + return res, nil +} + +const loginURL = "/login" + +func (c *X3Client) Login(request LoginRequest) (GeneralResponse[LoginResponse], error) { + return makePostRequest[interface{}, GeneralResponse[LoginResponse]](c.httpClient, c.Host+loginURL+fmt.Sprintf("?username=%s&password=%s", request.Username, request.Password), nil) +} + +const inboundsURL = "/panel/api/inbounds/list" + +func (c *X3Client) GetInbounds() (GeneralResponse[[]Inbound], error) { + return makeGetRequest[GeneralResponse[[]Inbound]](c.httpClient, c.Host+inboundsURL) +} + +const inboundURL = "/panel/api/inbounds/get/%d" + +func (c *X3Client) GetInbound(inboundId int) (GeneralResponse[Inbound], error) { + return makeGetRequest[GeneralResponse[Inbound]](c.httpClient, c.Host+fmt.Sprintf(inboundURL, inboundId)) +} + +const inboundClientTrafficURL = "/panel/api/inbounds/getClientTraffics/%s" + +func (c *X3Client) GetInboundClientTraffic(email string) (GeneralResponse[ClientStats], error) { + return makeGetRequest[GeneralResponse[ClientStats]](c.httpClient, c.Host+fmt.Sprintf(inboundClientTrafficURL, email)) +} + +const inboundClientTrafficByIdURL = "/panel/api/inbounds/getClientTrafficsById/%s" + +func (c *X3Client) GetInboundClientTrafficById(uuid string) (GeneralResponse[ClientStats], error) { + return makeGetRequest[GeneralResponse[ClientStats]](c.httpClient, c.Host+fmt.Sprintf(inboundClientTrafficByIdURL, uuid)) +} + +const inboundCreateBackupURL = "/panel/api/inbounds/createbackup" + +func (c *X3Client) CreateInboundBackup() (GeneralResponse[interface{}], error) { + return makeGetRequest[GeneralResponse[interface{}]](c.httpClient, c.Host+inboundCreateBackupURL) +} + +const clientIpsURL = "/panel/api/inbounds/clientIps/%s" + +func (c *X3Client) GetClientIps(email string) (GeneralResponse[string], error) { + return makePostRequest[interface{}, GeneralResponse[string]](c.httpClient, c.Host+fmt.Sprintf(clientIpsURL, email), nil) +} + +const addInboundURL = "/panel/api/inbounds/add" + +func (c *X3Client) AddInbound(inbound Inbound) (GeneralResponse[Inbound], error) { + return makePostRequest[Inbound, GeneralResponse[Inbound]](c.httpClient, c.Host+addInboundURL, inbound) +} + +const addClientToInboundURL = "/panel/api/inbounds/addClient" + +func (c *X3Client) AddClientToInbound(client CreateClient) (GeneralResponse[interface{}], error) { + return makePostRequest[CreateClient, GeneralResponse[interface{}]](c.httpClient, c.Host+addClientToInboundURL, client) +} + +const updateInboundURL = "/panel/api/inbounds/update/%d" + +func (c *X3Client) UpdateInbound(inbound Inbound) (GeneralResponse[Inbound], error) { + return makePostRequest[Inbound, GeneralResponse[Inbound]](c.httpClient, c.Host+fmt.Sprintf(updateInboundURL, inbound.Id), inbound) +} + +// TODO: Check if client id is int not an uuid +const updateClientInboundURL = "/panel/api/inbounds/updateClient/%d" + +func (c *X3Client) UpdateClientInbound(client CreateClient) (GeneralResponse[interface{}], error) { + return makePostRequest[CreateClient, GeneralResponse[interface{}]](c.httpClient, c.Host+fmt.Sprintf(updateClientInboundURL, client.Id), client) +} + +const clearClientIpsURL = "/panel/api/inbounds/clearClientIps/%s" + +func (c *X3Client) ClearClientIps(email string) (GeneralResponse[interface{}], error) { + return makePostRequest[interface{}, GeneralResponse[interface{}]](c.httpClient, c.Host+fmt.Sprintf(clearClientIpsURL, email), nil) +} + +const resetAllTrafficURL = "/panel/api/inbounds/resetAllTraffics" + +func (c *X3Client) ResetAllTraffic() (GeneralResponse[interface{}], error) { + return makePostRequest[interface{}, GeneralResponse[interface{}]](c.httpClient, c.Host+resetAllTrafficURL, nil) +} + +const resetAllClientTrafficByInboundURL = "/panel/api/inbounds/resetAllClientTraffics/%d" + +func (c *X3Client) ResetAllClientTrafficByInbound(inboundId int) (GeneralResponse[interface{}], error) { + return makePostRequest[interface{}, GeneralResponse[interface{}]](c.httpClient, c.Host+fmt.Sprintf(resetAllClientTrafficByInboundURL, inboundId), nil) +} + +const resetClientTrafficURL = "/panel/api/inbounds/%d/resetClientTraffic/%s" + +func (c *X3Client) ResetClientTraffic(inboundId int, email string) (GeneralResponse[interface{}], error) { + return makePostRequest[interface{}, GeneralResponse[interface{}]](c.httpClient, c.Host+fmt.Sprintf(resetClientTrafficURL, inboundId, email), nil) +} + +const deleteClientURL = "/panel/api/inbounds/%d/delClient/%s" + +func (c *X3Client) DeleteClient(inboundId int, uuid string) (GeneralResponse[interface{}], error) { + return makePostRequest[interface{}, GeneralResponse[interface{}]](c.httpClient, c.Host+fmt.Sprintf(deleteClientURL, inboundId, uuid), nil) +} + +const deleteInboundURL = "/panel/api/inbounds/del/%d" + +func (c *X3Client) DeleteInbound(inboundId int) (GeneralResponse[interface{}], error) { + return makePostRequest[interface{}, GeneralResponse[interface{}]](c.httpClient, c.Host+fmt.Sprintf(deleteInboundURL, inboundId), nil) +} + +const deleteDepletedClientsURL = "/panel/api/inbounds/delDepletedClients/%d" + +func (c *X3Client) DeleteDepletedClients(inboundId int) (GeneralResponse[interface{}], error) { + return makePostRequest[interface{}, GeneralResponse[interface{}]](c.httpClient, c.Host+fmt.Sprintf(deleteDepletedClientsURL, inboundId), nil) +} + +const onlineClientsURL = "/panel/api/inbounds/onlines" + +func (c *X3Client) GetOnlineClients() (GeneralResponse[interface{}], error) { + return makePostRequest[interface{}, GeneralResponse[interface{}]](c.httpClient, c.Host+onlineClientsURL, nil) +} diff --git a/src/x3-client/models.go b/src/x3-client/models.go new file mode 100644 index 0000000..989a3c0 --- /dev/null +++ b/src/x3-client/models.go @@ -0,0 +1,51 @@ +package x3_client + +type GeneralResponse[T any] struct { + Success bool `json:"success"` + Message string `json:"msg"` + Object T `json:"obj"` +} + +type LoginRequest struct { + Username string `json:"username"` + Password string `json:"password"` +} + +type LoginResponse struct { +} + +type Inbound struct { + Id int `json:"id"` + Up int `json:"up"` + Down int `json:"down"` + Total int `json:"total"` + Remark string `json:"remark"` + Enable bool `json:"enable"` + ExpiryTime int `json:"expiryTime"` + ClientStats []ClientStats `json:"clientStats"` + Listen string `json:"listen"` + Port int `json:"port"` + Protocol string `json:"protocol"` + Settings string `json:"settings"` + StreamSettings string `json:"streamSettings"` + Tag string `json:"tag"` + Sniffing string `json:"sniffing"` + Allocate string `json:"allocate"` +} + +type ClientStats struct { + Id int `json:"id"` + InboundId int `json:"inboundId"` + Enable bool `json:"enable"` + Email string `json:"email"` + Up int `json:"up"` + Down int `json:"down"` + ExpiryTime int `json:"expiryTime"` + Total int `json:"total"` + Reset int `json:"reset"` +} + +type CreateClient struct { + Id int `json:"id"` + Settings string `json:"settings"` +}