wallabag.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  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 // import "miniflux.app/integration/wallabag"
  5. import (
  6. "encoding/json"
  7. "fmt"
  8. "io"
  9. "net/url"
  10. "miniflux.app/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. onlyURL bool
  20. }
  21. // NewClient returns a new Wallabag client.
  22. func NewClient(baseURL, clientID, clientSecret, username, password string, onlyURL bool) *Client {
  23. return &Client{baseURL, clientID, clientSecret, username, password, onlyURL}
  24. }
  25. // AddEntry sends a link to Wallabag.
  26. // Pass an empty string in `content` to let Wallabag fetch the article content.
  27. func (c *Client) AddEntry(link, title, content string) error {
  28. if c.baseURL == "" || c.clientID == "" || c.clientSecret == "" || c.username == "" || c.password == "" {
  29. return fmt.Errorf("wallabag: missing credentials")
  30. }
  31. accessToken, err := c.getAccessToken()
  32. if err != nil {
  33. return err
  34. }
  35. return c.createEntry(accessToken, link, title, content)
  36. }
  37. func (c *Client) createEntry(accessToken, link, title, content string) error {
  38. endpoint, err := getAPIEndpoint(c.baseURL, "/api/entries.json")
  39. if err != nil {
  40. return fmt.Errorf("wallbag: unable to get entries endpoint: %v", err)
  41. }
  42. data := map[string]string{"url": link, "title": title}
  43. if !c.onlyURL {
  44. data["content"] = content
  45. }
  46. clt := client.New(endpoint)
  47. clt.WithAuthorization("Bearer " + accessToken)
  48. response, err := clt.PostJSON(data)
  49. if err != nil {
  50. return fmt.Errorf("wallabag: unable to post entry: %v", err)
  51. }
  52. if response.HasServerFailure() {
  53. return fmt.Errorf("wallabag: request failed, status=%d", response.StatusCode)
  54. }
  55. return nil
  56. }
  57. func (c *Client) getAccessToken() (string, error) {
  58. values := url.Values{}
  59. values.Add("grant_type", "password")
  60. values.Add("client_id", c.clientID)
  61. values.Add("client_secret", c.clientSecret)
  62. values.Add("username", c.username)
  63. values.Add("password", c.password)
  64. endpoint, err := getAPIEndpoint(c.baseURL, "/oauth/v2/token")
  65. if err != nil {
  66. return "", fmt.Errorf("wallbag: unable to get token endpoint: %v", err)
  67. }
  68. clt := client.New(endpoint)
  69. response, err := clt.PostForm(values)
  70. if err != nil {
  71. return "", fmt.Errorf("wallabag: unable to get access token: %v", err)
  72. }
  73. if response.HasServerFailure() {
  74. return "", fmt.Errorf("wallabag: request failed, status=%d", response.StatusCode)
  75. }
  76. token, err := decodeTokenResponse(response.Body)
  77. if err != nil {
  78. return "", err
  79. }
  80. return token.AccessToken, nil
  81. }
  82. func getAPIEndpoint(baseURL, path string) (string, error) {
  83. u, err := url.Parse(baseURL)
  84. if err != nil {
  85. return "", fmt.Errorf("wallabag: invalid API endpoint: %v", err)
  86. }
  87. u.Path = path
  88. return u.String(), nil
  89. }
  90. type tokenResponse struct {
  91. AccessToken string `json:"access_token"`
  92. Expires int `json:"expires_in"`
  93. RefreshToken string `json:"refresh_token"`
  94. Scope string `json:"scope"`
  95. TokenType string `json:"token_type"`
  96. }
  97. func decodeTokenResponse(body io.Reader) (*tokenResponse, error) {
  98. var token tokenResponse
  99. decoder := json.NewDecoder(body)
  100. if err := decoder.Decode(&token); err != nil {
  101. return nil, fmt.Errorf("wallabag: unable to decode token response: %v", err)
  102. }
  103. return &token, nil
  104. }