wallabag.go 3.3 KB

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