wallabag.go 3.2 KB

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