wallabag.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. // Copyright 2017 Frédéric Guillot. All rights reserved.
  2. // Use of this source code is governed by the Apache 2.0
  3. // license that can be found in the LICENSE file.
  4. package wallabag
  5. import (
  6. "encoding/json"
  7. "fmt"
  8. "io"
  9. "net/url"
  10. "github.com/miniflux/miniflux/http/client"
  11. )
  12. // Client represents a Wallabag client.
  13. type Client struct {
  14. baseURL string
  15. clientID string
  16. clientSecret string
  17. username string
  18. password string
  19. }
  20. // AddEntry sends a link to Wallabag.
  21. func (c *Client) AddEntry(link, title string) error {
  22. accessToken, err := c.getAccessToken()
  23. if err != nil {
  24. return err
  25. }
  26. return c.createEntry(accessToken, link, title)
  27. }
  28. func (c *Client) createEntry(accessToken, link, title string) error {
  29. endpoint, err := getAPIEndpoint(c.baseURL, "/api/entries.json")
  30. if err != nil {
  31. return fmt.Errorf("wallbag: unable to get entries endpoint: %v", err)
  32. }
  33. clt := client.New(endpoint)
  34. clt.WithAuthorization("Bearer " + accessToken)
  35. response, err := clt.PostJSON(map[string]string{"url": link, "title": title})
  36. if err != nil {
  37. return fmt.Errorf("wallabag: unable to post entry: %v", err)
  38. }
  39. if response.HasServerFailure() {
  40. return fmt.Errorf("wallabag: request failed, status=%d", response.StatusCode)
  41. }
  42. return nil
  43. }
  44. func (c *Client) getAccessToken() (string, error) {
  45. values := url.Values{}
  46. values.Add("grant_type", "password")
  47. values.Add("client_id", c.clientID)
  48. values.Add("client_secret", c.clientSecret)
  49. values.Add("username", c.username)
  50. values.Add("password", c.password)
  51. endpoint, err := getAPIEndpoint(c.baseURL, "/oauth/v2/token")
  52. if err != nil {
  53. return "", fmt.Errorf("wallbag: unable to get token endpoint: %v", err)
  54. }
  55. clt := client.New(endpoint)
  56. response, err := clt.PostForm(values)
  57. if err != nil {
  58. return "", fmt.Errorf("wallabag: unable to get access token: %v", err)
  59. }
  60. if response.HasServerFailure() {
  61. return "", fmt.Errorf("wallabag: request failed, status=%d", response.StatusCode)
  62. }
  63. token, err := decodeTokenResponse(response.Body)
  64. if err != nil {
  65. return "", err
  66. }
  67. return token.AccessToken, nil
  68. }
  69. // NewClient returns a new Wallabag client.
  70. func NewClient(baseURL, clientID, clientSecret, username, password string) *Client {
  71. return &Client{baseURL, clientID, clientSecret, username, password}
  72. }
  73. func getAPIEndpoint(baseURL, path string) (string, error) {
  74. u, err := url.Parse(baseURL)
  75. if err != nil {
  76. return "", fmt.Errorf("wallabag: invalid API endpoint: %v", err)
  77. }
  78. u.Path = path
  79. return u.String(), nil
  80. }
  81. type tokenResponse struct {
  82. AccessToken string `json:"access_token"`
  83. Expires int `json:"expires_in"`
  84. RefreshToken string `json:"refresh_token"`
  85. Scope string `json:"scope"`
  86. TokenType string `json:"token_type"`
  87. }
  88. func decodeTokenResponse(body io.Reader) (*tokenResponse, error) {
  89. var token tokenResponse
  90. decoder := json.NewDecoder(body)
  91. if err := decoder.Decode(&token); err != nil {
  92. return nil, fmt.Errorf("wallabag: unable to decode token response: %v", err)
  93. }
  94. return &token, nil
  95. }