Browse Source

Transfer code from API repo to own repo

master
Wim Brand 4 years ago
parent
commit
5c74f774ff
  1. 1
      .gitignore
  2. 3
      README.md
  3. 3
      go.mod
  4. 26
      pixelapi/admin.go
  5. 24
      pixelapi/file.go
  6. 18
      pixelapi/filesystem.go
  7. 8
      pixelapi/list.go
  8. 27
      pixelapi/misc.go
  9. 16
      pixelapi/patreon.go
  10. 194
      pixelapi/pixelapi.go
  11. 19
      pixelapi/subscription.go
  12. 135
      pixelapi/user.go

1
.gitignore

@ -0,0 +1 @@
go.sum

3
README.md

@ -1,3 +1,4 @@
# pixeldrain_api_client # pixeldrain_api_client
Client for the pixeldrain API. Used by pixeldrain itself for tranferring data between the web UI and API server. And for rendering JSON responses
Client for the pixeldrain API. Used by pixeldrain itself for tranferring data
between the web UI and API server. And for rendering JSON responses

3
go.mod

@ -0,0 +1,3 @@
module fornaxian.tech/pixeldrain_api_client
go 1.16

26
pixelapi/admin.go

@ -0,0 +1,26 @@
package pixelapi
import (
"net/url"
"fornaxian.tech/pixeldrain_server/api/restapi/apitype"
)
// AdminGetGlobals returns the global API settings
func (p *PixelAPI) AdminGetGlobals() (resp []apitype.AdminGlobal, err error) {
return resp, p.jsonRequest("GET", "admin/globals", &resp)
}
// AdminSetGlobals sets a global API setting
func (p *PixelAPI) AdminSetGlobals(key, value string) (err error) {
return p.form("POST", "admin/globals", url.Values{"key": {key}, "value": {value}}, nil)
}
// AdminBlockFiles blocks files from being downloaded
func (p *PixelAPI) AdminBlockFiles(text, abuseType, reporter string) (bl apitype.AdminBlockFiles, err error) {
return bl, p.form(
"POST", "admin/block_files",
url.Values{"text": {text}, "type": {abuseType}, "reporter": {reporter}},
&bl,
)
}

24
pixelapi/file.go

@ -0,0 +1,24 @@
package pixelapi
import (
"io"
"net/url"
"fornaxian.tech/pixeldrain_server/api/restapi/apitype"
)
// GetFile makes a file download request and returns a readcloser. Don't forget
// to close it!
func (p *PixelAPI) GetFile(id string) (io.ReadCloser, error) {
return p.getRaw("file/" + id)
}
// GetFileInfo gets the FileInfo from the pixeldrain API
func (p *PixelAPI) GetFileInfo(id string) (resp apitype.FileInfo, err error) {
return resp, p.jsonRequest("GET", "file/"+id+"/info", &resp)
}
// PostFileView adds a view to a file
func (p *PixelAPI) PostFileView(id, viewtoken string) (err error) {
return p.form("POST", "file/"+id+"/view", url.Values{"token": {viewtoken}}, nil)
}

18
pixelapi/filesystem.go

@ -0,0 +1,18 @@
package pixelapi
import (
"net/url"
"fornaxian.tech/pixeldrain_server/api/restapi/apitype"
)
// GetFilesystemBuckets returns a list of buckets for the user. You need to be
// authenticated
func (p *PixelAPI) GetFilesystemBuckets() (resp []apitype.Bucket, err error) {
return resp, p.jsonRequest("GET", "filesystem", &resp)
}
// GetFilesystemPath opens a filesystem path
func (p *PixelAPI) GetFilesystemPath(path string) (resp apitype.FilesystemPath, err error) {
return resp, p.jsonRequest("GET", "filesystem/"+url.PathEscape(path)+"?stat", &resp)
}

8
pixelapi/list.go

@ -0,0 +1,8 @@
package pixelapi
import "fornaxian.tech/pixeldrain_server/api/restapi/apitype"
// GetListID get a List from the pixeldrain API
func (p *PixelAPI) GetListID(id string) (resp apitype.ListInfo, err error) {
return resp, p.jsonRequest("GET", "list/"+id, &resp)
}

27
pixelapi/misc.go

@ -0,0 +1,27 @@
package pixelapi
import "fornaxian.tech/pixeldrain_server/api/restapi/apitype"
// Recaptcha stores the reCaptcha site key
type Recaptcha struct {
SiteKey string `json:"site_key"`
}
// GetMiscRecaptcha gets the reCaptcha site key from the pixelapi server. If
// reCaptcha is disabled the key will be empty
func (p *PixelAPI) GetMiscRecaptcha() (resp Recaptcha, err error) {
return resp, p.jsonRequest("GET", "misc/recaptcha", &resp)
}
// GetMiscViewToken requests a viewtoken from the server. The viewtoken is valid
// for a limited amount of time and can be used to add views to a file.
// Viewtokens can only be requested from localhost
func (p *PixelAPI) GetMiscViewToken() (resp string, err error) {
return resp, p.jsonRequest("GET", "misc/viewtoken", &resp)
}
// GetSiaPrice gets the price of one siacoin
func (p *PixelAPI) GetSiaPrice() (resp float64, err error) {
var sp apitype.SiaPrice
return sp.Price, p.jsonRequest("GET", "misc/sia_price", &sp)
}

16
pixelapi/patreon.go

@ -0,0 +1,16 @@
package pixelapi
import (
"fornaxian.tech/pixeldrain_server/api/restapi/apitype"
)
// GetPatreonByID returns information about a patron by the ID
func (p *PixelAPI) GetPatreonByID(id string) (resp apitype.Patron, err error) {
return resp, p.jsonRequest("GET", "patreon/"+id, &resp)
}
// PostPatreonLink links a patreon subscription to the pixeldrain account which
// is logged into this API client
func (p *PixelAPI) PostPatreonLink(id string) (err error) {
return p.jsonRequest("POST", "patreon/"+id+"/link_subscription", nil)
}

194
pixelapi/pixelapi.go

@ -0,0 +1,194 @@
package pixelapi
import (
"context"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"net/url"
"strings"
"time"
)
// PixelAPI is the Pixeldrain API client
type PixelAPI struct {
client *http.Client
apiEndpoint string
key string
realIP string
}
// New creates a new Pixeldrain API client to query the Pixeldrain API with
func New(apiEndpoint string) (api PixelAPI) {
api.client = &http.Client{Timeout: time.Minute * 5}
api.apiEndpoint = apiEndpoint
// Pixeldrain uses unix domain sockets on its servers to minimize latency
// between the web interface daemon and API daemon. Golang does not
// understand that it needs to dial a unix socket on this case so we create
// a custom HTTP transport which uses the unix socket instead of TCP
if strings.HasPrefix(apiEndpoint, "http://unix:") {
// Get the socket path from the API endpoint
var sockPath = strings.TrimPrefix(apiEndpoint, "http://unix:")
// Fake the dialer to use a unix socket instead of TCP
api.client.Transport = &http.Transport{
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
return net.Dial("unix", sockPath)
},
}
// Fake a domain name to stop Go's HTTP client from complaining about
// the domain name. This string will be completely ignored during
// requests
api.apiEndpoint = "http://api.sock"
} else {
api.client.Transport = http.DefaultTransport
}
return api
}
// Login logs a user into the pixeldrain API. The original PixelAPI does not get
// logged in, only the returned PixelAPI
func (p PixelAPI) Login(apiKey string) PixelAPI {
p.key = apiKey
return p
}
// RealIP sets the real IP address to use when making API requests
func (p PixelAPI) RealIP(ip string) PixelAPI {
p.realIP = ip
return p
}
// Standard response types
// Error is an error returned by the pixeldrain API. If the request failed
// before it could reach the API the error will be on a different type
type Error struct {
Status int `json:"-"` // One of the http.Status types
Success bool `json:"success"`
StatusCode string `json:"value"`
Message string `json:"message"`
// In case of the multiple_errors code this array will be populated with
// more errors
Errors []Error `json:"errors,omitempty"`
// Metadata regarding the error
Extra map[string]interface{} `json:"extra,omitempty"`
}
func (e Error) Error() string { return e.StatusCode }
// ErrIsServerError returns true if the error is a server-side error
func ErrIsServerError(err error) bool {
if apierr, ok := err.(Error); ok && apierr.Status >= 500 {
return true
}
return false
}
// ErrIsClientError returns true if the error is a client-side error
func ErrIsClientError(err error) bool {
if apierr, ok := err.(Error); ok && apierr.Status >= 400 && apierr.Status < 500 {
return true
}
return false
}
func (p *PixelAPI) do(r *http.Request) (*http.Response, error) {
if p.key != "" {
r.SetBasicAuth("", p.key)
}
if p.realIP != "" {
r.Header.Set("X-Real-IP", p.realIP)
}
return p.client.Do(r)
}
func (p *PixelAPI) getString(path string) (string, error) {
req, err := http.NewRequest("GET", p.apiEndpoint+"/"+path, nil)
if err != nil {
return "", err
}
resp, err := p.do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
bodyBytes, err := ioutil.ReadAll(resp.Body)
return string(bodyBytes), err
}
func (p *PixelAPI) getRaw(path string) (io.ReadCloser, error) {
req, err := http.NewRequest("GET", p.apiEndpoint+"/"+path, nil)
if err != nil {
return nil, err
}
resp, err := p.do(req)
if err != nil {
return nil, err
}
return resp.Body, err
}
func (p *PixelAPI) jsonRequest(method, path string, target interface{}) error {
req, err := http.NewRequest(method, p.apiEndpoint+"/"+path, nil)
if err != nil {
return err
}
resp, err := p.do(req)
if err != nil {
return err
}
defer resp.Body.Close()
return parseJSONResponse(resp, target)
}
func (p *PixelAPI) form(method, url string, vals url.Values, target interface{}) error {
req, err := http.NewRequest(method, url, strings.NewReader(vals.Encode()))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
resp, err := p.do(req)
if err != nil {
return err
}
defer resp.Body.Close()
return parseJSONResponse(resp, target)
}
func parseJSONResponse(resp *http.Response, target interface{}) (err error) {
// Test for client side and server side errors
if resp.StatusCode >= 400 {
errResp := Error{Status: resp.StatusCode}
if err = json.NewDecoder(resp.Body).Decode(&errResp); err != nil {
return err
}
return errResp
}
if target == nil {
return nil
}
if err = json.NewDecoder(resp.Body).Decode(target); err != nil {
return fmt.Errorf("failed to decode json response: %w", err)
}
return nil
}

19
pixelapi/subscription.go

@ -0,0 +1,19 @@
package pixelapi
import (
"net/url"
"fornaxian.tech/pixeldrain_server/api/restapi/apitype"
)
// GetSubscriptionID returns the subscription object identified by the given ID
func (p *PixelAPI) GetSubscriptionID(id string) (resp apitype.Subscription, err error) {
return resp, p.jsonRequest("GET", p.apiEndpoint+"/subscription/"+url.PathEscape(id), &resp)
}
// PostSubscriptionLink links a subscription to the logged in user account. Use
// Login() before calling this function to select the account to use. This
// action cannot be undone.
func (p *PixelAPI) PostSubscriptionLink(id string) (err error) {
return p.jsonRequest("POST", p.apiEndpoint+"/subscription/"+url.PathEscape(id)+"/link", nil)
}

135
pixelapi/user.go

@ -0,0 +1,135 @@
package pixelapi
import (
"net/url"
"strconv"
"fornaxian.tech/pixeldrain_server/api/restapi/apitype"
)
// UserRegister registers a new user on the Pixeldrain server. username and
// password are always required. email is optional, but without it you will not
// be able to reset your password in case you forget it. captcha depends on
// whether reCaptcha is enabled on the Pixeldrain server, this can be checked
// through the GetRecaptcha function.
//
// The register API can return multiple errors, which will be stored in the
// Errors array. Check for len(Errors) == 0 to see if an error occurred. If err
// != nil it means a connection error occurred
func (p *PixelAPI) UserRegister(username, email, password, captcha string) (err error) {
return p.form(
"POST", "user/register",
url.Values{
"username": {username},
"email": {email},
"password": {password},
"recaptcha_response": {captcha},
},
nil,
)
}
// PostUserLogin logs a user in with the provided credentials. The response will
// contain the returned API key. If saveKey is true the API key will also be
// saved in the client and following requests with this client will be
// autenticated
func (p *PixelAPI) PostUserLogin(username, password string) (resp apitype.UserSession, err error) {
return resp, p.form(
"POST", "user/login",
url.Values{"username": {username}, "password": {password}},
&resp,
)
}
// GetUser returns information about the logged in user. Requires an API key
func (p *PixelAPI) GetUser() (resp apitype.UserInfo, err error) {
return resp, p.jsonRequest("GET", "user", &resp)
}
// PostUserSession creates a new user sessions
func (p *PixelAPI) PostUserSession() (resp apitype.UserSession, err error) {
return resp, p.jsonRequest("POST", "user/session", &resp)
}
// GetUserSession lists all active user sessions
func (p *PixelAPI) GetUserSession() (resp []apitype.UserSession, err error) {
return resp, p.jsonRequest("GET", "user/session", &resp)
}
// DeleteUserSession destroys an API key so it can no longer be used to perform
// actions
func (p *PixelAPI) DeleteUserSession(key string) (err error) {
return p.jsonRequest("DELETE", "user/session", nil)
}
// GetUserFiles gets files uploaded by a user
func (p *PixelAPI) GetUserFiles() (resp apitype.FileInfoSlice, err error) {
return resp, p.jsonRequest("GET", "user/files", &resp)
}
// GetUserLists gets lists created by a user
func (p *PixelAPI) GetUserLists() (resp apitype.ListInfoSlice, err error) {
return resp, p.jsonRequest("GET", "user/lists", &resp)
}
// PutUserPassword changes the user's password
func (p *PixelAPI) PutUserPassword(oldPW, newPW string) (err error) {
return p.form(
"PUT", "user/password",
url.Values{"old_password": {oldPW}, "new_password": {newPW}},
nil,
)
}
// PutUserEmailReset starts the e-mail change process. An email will be sent to
// the new address to verify that it's real. Once the link in the e-mail is
// clicked the key it contains can be sent to the API with UserEmailResetConfirm
// and the change will be applied
func (p *PixelAPI) PutUserEmailReset(email string, delete bool) (err error) {
return p.form(
"PUT", "user/email_reset",
url.Values{"new_email": {email}, "delete": {strconv.FormatBool(delete)}},
nil,
)
}
// PutUserEmailResetConfirm finishes process of changing a user's e-mail address
func (p *PixelAPI) PutUserEmailResetConfirm(key string) (err error) {
return p.form(
"PUT", "user/email_reset_confirm",
url.Values{"key": {key}},
nil,
)
}
// PutUserPasswordReset starts the password reset process. An email will be sent
// the user to verify that it really wanted to reset the password. Once the link
// in the e-mail is clicked the key it contains can be sent to the API with
// UserPasswordResetConfirm and a new password can be set
func (p *PixelAPI) PutUserPasswordReset(email string, recaptchaResponse string) (err error) {
return p.form(
"PUT", "user/password_reset",
url.Values{"email": {email}, "recaptcha_response": {recaptchaResponse}},
nil,
)
}
// PutUserPasswordResetConfirm finishes process of resetting a user's password.
// If the key is valid the new_password parameter will be saved as the new
// password
func (p *PixelAPI) PutUserPasswordResetConfirm(key string, newPassword string) (err error) {
return p.form(
"PUT", "user/password_reset_confirm",
url.Values{"key": {key}, "new_password": {newPassword}},
nil,
)
}
// PutUserUsername changes the user's username.
func (p *PixelAPI) PutUserUsername(username string) (err error) {
return p.form(
"PUT", "user/username",
url.Values{"new_username": {username}},
nil,
)
}
Loading…
Cancel
Save