2022-08-04 15:05:37 +00:00
|
|
|
package server
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2022-09-02 14:01:55 +00:00
|
|
|
"config"
|
2022-08-04 15:05:37 +00:00
|
|
|
. "contracts"
|
2022-08-11 12:10:34 +00:00
|
|
|
"crypto/sha256"
|
|
|
|
"encoding/base64"
|
2022-08-04 15:05:37 +00:00
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
2022-08-11 12:10:34 +00:00
|
|
|
"io"
|
2022-09-02 14:01:55 +00:00
|
|
|
"mime/multipart"
|
2022-08-04 15:05:37 +00:00
|
|
|
"net/http"
|
2022-09-02 14:01:55 +00:00
|
|
|
"path"
|
2022-08-29 11:24:06 +00:00
|
|
|
"server/mime"
|
2022-08-04 15:05:37 +00:00
|
|
|
"time"
|
|
|
|
"utility"
|
|
|
|
"utility/httpsig"
|
|
|
|
|
|
|
|
"github.com/gofiber/fiber/v2"
|
|
|
|
"github.com/google/uuid"
|
|
|
|
)
|
|
|
|
|
|
|
|
type httpServerContext struct {
|
|
|
|
underlyingContext *fiber.Ctx
|
|
|
|
request IRequest
|
|
|
|
response IResponse
|
|
|
|
stringUtil IStringUtil
|
|
|
|
httpClient *http.Client
|
|
|
|
}
|
|
|
|
|
|
|
|
func newContext(underlyingContext *fiber.Ctx) IContext {
|
|
|
|
return &httpServerContext{
|
|
|
|
underlyingContext: underlyingContext,
|
|
|
|
request: newRequest(underlyingContext),
|
|
|
|
response: newResponse(underlyingContext),
|
|
|
|
stringUtil: utility.NewStringUtil(),
|
|
|
|
httpClient: &http.Client{
|
|
|
|
Timeout: time.Second * 5,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (context *httpServerContext) GUID() string {
|
|
|
|
return uuid.New().String()
|
|
|
|
}
|
|
|
|
|
2022-08-29 17:58:56 +00:00
|
|
|
func (context *httpServerContext) String(payload any) error {
|
|
|
|
return context.underlyingContext.SendString(payload.(string))
|
2022-08-04 15:05:37 +00:00
|
|
|
}
|
|
|
|
|
2022-08-29 17:58:56 +00:00
|
|
|
func (context *httpServerContext) Json(payload interface{}) error {
|
|
|
|
return context.underlyingContext.JSON(payload)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (context *httpServerContext) Activity(payload interface{}) error {
|
|
|
|
data, err := json.Marshal(payload)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
context.underlyingContext.Response().Header.Add("Content-Type", mime.ActivityJsonUtf8)
|
|
|
|
return context.underlyingContext.Send(data)
|
2022-08-04 15:05:37 +00:00
|
|
|
}
|
|
|
|
|
2022-09-02 14:01:55 +00:00
|
|
|
func (context *httpServerContext) File(key string) error {
|
|
|
|
// TODO: Compress the response
|
|
|
|
filePath := path.Join(config.UPLOAD_PATH, key)
|
|
|
|
return context.underlyingContext.SendFile(filePath)
|
|
|
|
}
|
|
|
|
|
2022-08-04 15:05:37 +00:00
|
|
|
func (context *httpServerContext) Nothing() error {
|
|
|
|
return context.underlyingContext.JSON(&struct{}{})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (context *httpServerContext) Redirect(location string, status ...int) error {
|
|
|
|
return context.underlyingContext.Redirect(location, status...)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (context *httpServerContext) Render(name string, bind interface{}, layouts ...string) error {
|
|
|
|
return context.underlyingContext.Render(name, bind, layouts...)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (context *httpServerContext) Config() IConfig {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (context *httpServerContext) Storage() IStorage {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (context *httpServerContext) Request() IRequest {
|
|
|
|
return context.request
|
|
|
|
}
|
|
|
|
|
|
|
|
func (context *httpServerContext) Response() IResponse {
|
|
|
|
return context.response
|
|
|
|
}
|
|
|
|
|
|
|
|
func (context *httpServerContext) StringUtil() IStringUtil {
|
|
|
|
return context.stringUtil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (context *httpServerContext) ParseJson(interface{}) IResult {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (context *httpServerContext) ParseBodyAndValidate(body interface{}) error {
|
|
|
|
if err := context.underlyingContext.BodyParser(body); err != nil {
|
|
|
|
return fiber.ErrBadRequest
|
|
|
|
}
|
|
|
|
|
|
|
|
return utility.Validate(body)
|
|
|
|
}
|
|
|
|
|
2022-09-02 14:01:55 +00:00
|
|
|
func (context *httpServerContext) SaveFile(f *multipart.FileHeader, path string) error {
|
|
|
|
return context.underlyingContext.SaveFile(f, path)
|
|
|
|
}
|
|
|
|
|
2022-08-04 15:05:37 +00:00
|
|
|
func (context *httpServerContext) GetUser() uint {
|
|
|
|
id, _ := context.underlyingContext.Locals("USER").(uint)
|
|
|
|
return id
|
|
|
|
}
|
|
|
|
|
|
|
|
// Error create a server error with status code and message
|
|
|
|
func (context *httpServerContext) Error(code int, format string, args ...any) IServerError {
|
|
|
|
return newError(code, fmt.Sprintf(format, args...))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (context *httpServerContext) BadRequest(format string, args ...any) IServerError {
|
|
|
|
return context.Error(StatusBadRequest, format, args...)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (context *httpServerContext) NotFound(format string, args ...any) IServerError {
|
|
|
|
return context.Error(StatusNotFound, format, args...)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (context *httpServerContext) InternalServerError(format string, args ...any) IServerError {
|
|
|
|
return context.Error(StatusInternalServerError, format, args...)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (context *httpServerContext) Unauthorized(format string, args ...any) IServerError {
|
|
|
|
return context.Error(StatusUnauthorized, format, args...)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (context *httpServerContext) Conflict(format string, args ...any) IServerError {
|
|
|
|
return context.Error(StatusConflict, format, args...)
|
|
|
|
}
|
|
|
|
|
2022-08-11 12:10:34 +00:00
|
|
|
func (context *httpServerContext) signRequest(keyId, privateKey string, data []byte, req *http.Request) error {
|
2022-08-04 15:05:37 +00:00
|
|
|
privKey, err := httpsig.ParseRsaPrivateKeyFromPemStr(privateKey)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
signer := httpsig.NewRSASHA256Signer(keyId, privKey, []string{"Date", "Digest"})
|
2022-08-11 12:10:34 +00:00
|
|
|
if data != nil {
|
|
|
|
hasher := sha256.New()
|
|
|
|
hasher.Write(data)
|
|
|
|
sum := hasher.Sum(nil)
|
|
|
|
encodedHash := base64.StdEncoding.EncodeToString(sum)
|
|
|
|
digest := fmt.Sprintf("sha-256=%s", encodedHash)
|
2022-08-29 11:24:06 +00:00
|
|
|
req.Header.Set("Content-Type", mime.ActivityJsonUtf8)
|
2022-08-11 12:10:34 +00:00
|
|
|
req.Header.Set("Digest", digest)
|
2022-08-04 15:05:37 +00:00
|
|
|
}
|
|
|
|
|
2022-08-29 11:24:06 +00:00
|
|
|
req.Header.Set("Accept", mime.ActivityJson)
|
2022-08-04 15:05:37 +00:00
|
|
|
|
|
|
|
if err := signer.Sign(req); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-08-11 12:10:34 +00:00
|
|
|
func (context *httpServerContext) requestActivityStream(method, url, keyId, privateKey string, data []byte, output interface{}) error {
|
|
|
|
var reader io.Reader
|
|
|
|
if data != nil {
|
|
|
|
reader = bytes.NewBuffer(data)
|
2022-08-04 15:05:37 +00:00
|
|
|
}
|
|
|
|
|
2022-08-11 12:10:34 +00:00
|
|
|
req, err := http.NewRequest(method, url, reader)
|
2022-08-04 15:05:37 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-09-15 14:03:28 +00:00
|
|
|
if privateKey != "" {
|
|
|
|
if err := context.signRequest(keyId, privateKey, data, req); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if keyId == "activitystream" {
|
|
|
|
req.Header.Add("Accept", "application/activity+json")
|
2022-08-04 15:05:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
res, err := context.httpClient.Do(req)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if res.Body != nil {
|
|
|
|
defer res.Body.Close()
|
|
|
|
}
|
|
|
|
|
2022-08-15 17:34:03 +00:00
|
|
|
if res.StatusCode != http.StatusOK &&
|
|
|
|
res.StatusCode != http.StatusAccepted {
|
2022-08-04 15:05:37 +00:00
|
|
|
return fmt.Errorf("%s", res.Status)
|
|
|
|
}
|
|
|
|
|
2022-09-15 14:03:28 +00:00
|
|
|
j, err := io.ReadAll(res.Body)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Println(string(j))
|
|
|
|
|
2022-08-11 12:10:34 +00:00
|
|
|
if output != nil {
|
2022-09-15 14:03:28 +00:00
|
|
|
if err := json.Unmarshal(j, output); err != nil {
|
2022-08-11 12:10:34 +00:00
|
|
|
return err
|
|
|
|
}
|
2022-09-15 14:03:28 +00:00
|
|
|
|
2022-08-11 12:10:34 +00:00
|
|
|
}
|
|
|
|
|
2022-08-04 15:05:37 +00:00
|
|
|
return nil
|
|
|
|
}
|
2022-08-11 12:10:34 +00:00
|
|
|
|
|
|
|
func (context *httpServerContext) GetActivityStream(url, keyId, privateKey string, data []byte, output interface{}) error {
|
|
|
|
return context.requestActivityStream(http.MethodGet, url, keyId, privateKey, data, output)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (context *httpServerContext) PostActivityStream(url, keyId, privateKey string, data []byte, output interface{}) error {
|
|
|
|
return context.requestActivityStream(http.MethodPost, url, keyId, privateKey, data, output)
|
|
|
|
}
|