223 lines
6.1 KiB
Go
223 lines
6.1 KiB
Go
|
//
|
||
|
// lurkcoin HTTPS API (version 3)
|
||
|
// Copyright © 2020 by luk3yx
|
||
|
//
|
||
|
// This program is free software: you can redistribute it and/or modify
|
||
|
// it under the terms of the GNU Affero General Public License as
|
||
|
// published by the Free Software Foundation, either version 3 of the
|
||
|
// License, or (at your option) any later version.
|
||
|
//
|
||
|
// This program is distributed in the hope that it will be useful,
|
||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
// GNU Affero General Public License for more details.
|
||
|
//
|
||
|
// You should have received a copy of the GNU Affero General Public License
|
||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||
|
//
|
||
|
|
||
|
// API documentation:
|
||
|
// https://gist.github.com/luk3yx/7a07f8b307c9afbcf94cf47d7f41d9cb
|
||
|
|
||
|
package api
|
||
|
|
||
|
import (
|
||
|
"encoding/json"
|
||
|
"errors"
|
||
|
"github.com/julienschmidt/httprouter"
|
||
|
"lurkcoin"
|
||
|
"net/http"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
func v3WrapHTTPHandler(db lurkcoin.Database, autoLogin bool,
|
||
|
handlerFunc HTTPHandler) httprouter.Handle {
|
||
|
return func(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
|
||
|
req := MakeHTTPRequest(db, r, params)
|
||
|
defer req.AbortTransaction()
|
||
|
|
||
|
var result interface{}
|
||
|
var err error
|
||
|
if !autoLogin || req.Authenticate() == nil {
|
||
|
result, err = handlerFunc(req)
|
||
|
} else {
|
||
|
err = errors.New("ERR_INVALIDLOGIN")
|
||
|
}
|
||
|
|
||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||
|
res := make(map[string]interface{})
|
||
|
if err == nil {
|
||
|
req.FinishTransaction()
|
||
|
res["success"] = true
|
||
|
res["result"] = result
|
||
|
w.WriteHeader(http.StatusOK)
|
||
|
} else {
|
||
|
req.AbortTransaction()
|
||
|
var c int
|
||
|
res["success"] = false
|
||
|
res["error"], res["message"], c = lurkcoin.LookupError(err.Error())
|
||
|
w.WriteHeader(c)
|
||
|
}
|
||
|
|
||
|
// TODO: Possibly write JSON directly to the ResponseWriter.
|
||
|
raw, enc_err := json.Marshal(res)
|
||
|
if enc_err != nil {
|
||
|
raw = []byte(`{"success":false,"error":"ERR_INTERNALERROR","message":"Internal error!"}`)
|
||
|
}
|
||
|
w.Write(raw)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func v3Get(router *httprouter.Router, db lurkcoin.Database, url string,
|
||
|
requireLogin bool, f HTTPHandler) {
|
||
|
f2 := v3WrapHTTPHandler(db, requireLogin, f)
|
||
|
url = "/v3/" + url
|
||
|
router.GET(url, f2)
|
||
|
router.POST(url, f2)
|
||
|
}
|
||
|
|
||
|
func v3Post(router *httprouter.Router, db lurkcoin.Database, url string,
|
||
|
requireLogin bool, f HTTPHandler) {
|
||
|
router.POST("/v3/"+url, v3WrapHTTPHandler(db, requireLogin, f))
|
||
|
}
|
||
|
|
||
|
func v3Put(router *httprouter.Router, db lurkcoin.Database, url string,
|
||
|
requireLogin bool, f HTTPHandler) {
|
||
|
f2 := v3WrapHTTPHandler(db, requireLogin, f)
|
||
|
router.PUT("/v3/"+url, f2)
|
||
|
router.POST("/v3/set_"+url, f2)
|
||
|
}
|
||
|
|
||
|
func addV3API(router *httprouter.Router, db lurkcoin.Database) {
|
||
|
v3Get(router, db, "summary", true,
|
||
|
func(r *HTTPRequest) (interface{}, error) {
|
||
|
return r.Server.GetSummary(), nil
|
||
|
})
|
||
|
|
||
|
v3Post(router, db, "pay", false,
|
||
|
func(r *HTTPRequest) (transaction interface{}, err error) {
|
||
|
var p struct {
|
||
|
Source string `json:"source"`
|
||
|
Target string `json:"target"`
|
||
|
TargetServer string `json:"target_server"`
|
||
|
Amount lurkcoin.Currency `json:"amount"`
|
||
|
LocalCurrency bool `json:"local_currency"`
|
||
|
}
|
||
|
err = r.Unmarshal(&p)
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
err = r.Authenticate(p.TargetServer)
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
if p.Amount.IsNil() {
|
||
|
err = errors.New("ERR_INVALIDAMOUNT")
|
||
|
return
|
||
|
}
|
||
|
targetServer, ok := r.DbTransaction.GetCachedServer(p.TargetServer)
|
||
|
if !ok {
|
||
|
err = errors.New("ERR_SERVERNOTFOUND")
|
||
|
return
|
||
|
}
|
||
|
transaction, err = r.Server.Pay(p.Source, p.Target, targetServer,
|
||
|
p.Amount, p.LocalCurrency, true)
|
||
|
return
|
||
|
})
|
||
|
|
||
|
v3Get(router, db, "balance", true,
|
||
|
func(r *HTTPRequest) (interface{}, error) {
|
||
|
return r.Server.GetBalance(), nil
|
||
|
})
|
||
|
|
||
|
v3Get(router, db, "history", true,
|
||
|
func(r *HTTPRequest) (interface{}, error) {
|
||
|
return r.Server.GetHistory(), nil
|
||
|
})
|
||
|
|
||
|
v3Post(router, db, "exchange_rates", false,
|
||
|
func(r *HTTPRequest) (interface{}, error) {
|
||
|
var p struct {
|
||
|
Source string `json:"source"`
|
||
|
Target string `json:"target"`
|
||
|
Amount lurkcoin.Currency
|
||
|
}
|
||
|
r.Unmarshal(&p)
|
||
|
if p.Amount.IsNil() {
|
||
|
return nil, errors.New("ERR_INVALIDAMOUNT")
|
||
|
}
|
||
|
return lurkcoin.GetExchangeRate(r.Database, p.Source, p.Target,
|
||
|
p.Amount)
|
||
|
})
|
||
|
|
||
|
v3Get(router, db, "pending_transactions", true,
|
||
|
func(r *HTTPRequest) (interface{}, error) {
|
||
|
return r.Server.GetPendingTransactions(), nil
|
||
|
})
|
||
|
|
||
|
type transactionList struct {
|
||
|
TransactionIDs []string `json:"transactions"`
|
||
|
}
|
||
|
v3Post(router, db, "acknowledge_transactions", true,
|
||
|
func(r *HTTPRequest) (interface{}, error) {
|
||
|
var p transactionList
|
||
|
r.Unmarshal(&p)
|
||
|
for _, id := range p.TransactionIDs {
|
||
|
r.Server.RemovePendingTransaction(id)
|
||
|
}
|
||
|
return nil, nil
|
||
|
})
|
||
|
|
||
|
v3Post(router, db, "reject_transactions", true,
|
||
|
func(r *HTTPRequest) (interface{}, error) {
|
||
|
var p transactionList
|
||
|
r.Unmarshal(&p)
|
||
|
for _, id := range p.TransactionIDs {
|
||
|
r.Server.RejectPendingTransaction(id, r.DbTransaction)
|
||
|
}
|
||
|
return nil, nil
|
||
|
})
|
||
|
|
||
|
v3Get(router, db, "target_balance", true,
|
||
|
func(r *HTTPRequest) (interface{}, error) {
|
||
|
return r.Server.GetTargetBalance(), nil
|
||
|
})
|
||
|
|
||
|
v3Put(router, db, "target_balance", true,
|
||
|
func(r *HTTPRequest) (interface{}, error) {
|
||
|
var p struct {
|
||
|
TargetBalance lurkcoin.Currency `json:"target_balance"`
|
||
|
}
|
||
|
err := r.Unmarshal(&p)
|
||
|
if err != nil {
|
||
|
return nil, errors.New("ERR_INVALIDREQUEST")
|
||
|
}
|
||
|
if p.TargetBalance.IsNil() {
|
||
|
return nil, errors.New("ERR_INVALIDAMOUNT")
|
||
|
}
|
||
|
ok := r.Server.SetTargetBalance(p.TargetBalance)
|
||
|
if !ok {
|
||
|
return nil, errors.New("ERR_INVALIDAMOUNT")
|
||
|
}
|
||
|
return nil, nil
|
||
|
})
|
||
|
|
||
|
v3Get(router, db, "webhook_url", true,
|
||
|
func(r *HTTPRequest) (interface{}, error) {
|
||
|
if r.Server.WebhookURL == "" {
|
||
|
return nil, nil
|
||
|
}
|
||
|
return r.Server.WebhookURL, nil
|
||
|
})
|
||
|
|
||
|
v3Get(router, db, "version", false,
|
||
|
func(r *HTTPRequest) (interface{}, error) {
|
||
|
return map[string]interface{}{
|
||
|
"version": lurkcoin.VERSION,
|
||
|
"copyright": strings.Split(lurkcoin.COPYRIGHT, "\n"),
|
||
|
"license": "AGPLv3",
|
||
|
"source": lurkcoin.SOURCE_URL,
|
||
|
}, nil
|
||
|
})
|
||
|
}
|