You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

136 lines
3.1 KiB

  1. package client
  2. import (
  3. "bytes"
  4. "context"
  5. "encoding/json"
  6. "fmt"
  7. "git.aiterp.net/lucifer/new-server/models"
  8. "io"
  9. "net"
  10. "net/http"
  11. "strings"
  12. "time"
  13. )
  14. type Client struct {
  15. APIRoot string
  16. }
  17. func (client *Client) GetDevices(ctx context.Context, fetchStr string) ([]models.Device, error) {
  18. devices := make([]models.Device, 0, 16)
  19. err := client.Fetch(ctx, "GET", "/api/devices/"+fetchStr, &devices, nil)
  20. if err != nil {
  21. return nil, err
  22. }
  23. return devices, nil
  24. }
  25. func (client *Client) PutDevice(ctx context.Context, fetchStr string, update models.DeviceUpdate) ([]models.Device, error) {
  26. devices := make([]models.Device, 0, 16)
  27. err := client.Fetch(ctx, "PUT", "/api/devices/"+fetchStr, &devices, update)
  28. if err != nil {
  29. return nil, err
  30. }
  31. return devices, nil
  32. }
  33. func (client *Client) PutDeviceState(ctx context.Context, fetchStr string, update models.NewDeviceState) ([]models.Device, error) {
  34. devices := make([]models.Device, 0, 16)
  35. err := client.Fetch(ctx, "PUT", "/api/devices/"+fetchStr+"/state", &devices, update)
  36. if err != nil {
  37. return nil, err
  38. }
  39. return devices, nil
  40. }
  41. func (client *Client) PutDeviceTags(ctx context.Context, fetchStr string, addTags []string, removeTags []string) ([]models.Device, error) {
  42. devices := make([]models.Device, 0, 16)
  43. err := client.Fetch(ctx, "PUT", "/api/devices/"+fetchStr+"/tags", &devices, map[string][]string{
  44. "add": addTags,
  45. "remove": removeTags,
  46. })
  47. if err != nil {
  48. return nil, err
  49. }
  50. return devices, nil
  51. }
  52. func (client *Client) FireEvent(ctx context.Context, event models.Event) error {
  53. err := client.Fetch(ctx, "POST", "/api/events", nil, event)
  54. if err != nil {
  55. return err
  56. }
  57. return nil
  58. }
  59. func (client *Client) Fetch(ctx context.Context, method string, path string, dst interface{}, body interface{}) error {
  60. var reqBody io.ReadWriter
  61. if body != nil && method != "GET" {
  62. reqBody = bytes.NewBuffer(make([]byte, 0, 512))
  63. err := json.NewEncoder(reqBody).Encode(body)
  64. if err != nil {
  65. return err
  66. }
  67. }
  68. req, err := http.NewRequest(method, client.APIRoot+path, reqBody)
  69. if err != nil {
  70. return err
  71. }
  72. res, err := httpClient.Do(req.WithContext(ctx))
  73. if err != nil {
  74. return err
  75. }
  76. defer res.Body.Close()
  77. if !strings.HasPrefix(res.Header.Get("Content-Type"), "application/json") {
  78. return fmt.Errorf("%s: %s", path, res.Status)
  79. }
  80. var resJson struct {
  81. Code int `json:"code"`
  82. Message *string `json:"message"`
  83. Data json.RawMessage `json:"data"`
  84. }
  85. err = json.NewDecoder(res.Body).Decode(&resJson)
  86. if err != nil {
  87. return err
  88. }
  89. if resJson.Code != 200 {
  90. msg := ""
  91. if resJson.Message != nil {
  92. msg = *resJson.Message
  93. }
  94. return fmt.Errorf("%d: %s", resJson.Code, msg)
  95. }
  96. if dst == nil {
  97. return nil
  98. }
  99. return json.Unmarshal(resJson.Data, dst)
  100. }
  101. var httpClient = &http.Client{
  102. Transport: &http.Transport{
  103. Proxy: http.ProxyFromEnvironment,
  104. DialContext: (&net.Dialer{
  105. Timeout: 30 * time.Second,
  106. KeepAlive: 30 * time.Second,
  107. }).DialContext,
  108. MaxIdleConns: 16,
  109. MaxIdleConnsPerHost: 16,
  110. IdleConnTimeout: time.Minute,
  111. },
  112. Timeout: time.Minute,
  113. }