package server import ( "bytes" "config" . "contracts" "crypto/sha256" "encoding/base64" "encoding/json" "fmt" "io" "mime/multipart" "net/http" "path" "server/mime" "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() } func (context *httpServerContext) String(payload any) error { return context.underlyingContext.SendString(payload.(string)) } 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) } func (context *httpServerContext) File(key string) error { // TODO: Compress the response filePath := path.Join(config.UPLOAD_PATH, key) return context.underlyingContext.SendFile(filePath) } 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) } func (context *httpServerContext) SaveFile(f *multipart.FileHeader, path string) error { return context.underlyingContext.SaveFile(f, path) } 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...) } func (context *httpServerContext) signRequest(keyId, privateKey string, data []byte, req *http.Request) error { privKey, err := httpsig.ParseRsaPrivateKeyFromPemStr(privateKey) if err != nil { return err } signer := httpsig.NewRSASHA256Signer(keyId, privKey, []string{"Date", "Digest"}) 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) req.Header.Set("Content-Type", mime.ActivityJsonUtf8) req.Header.Set("Digest", digest) } req.Header.Set("Accept", mime.ActivityJson) if err := signer.Sign(req); err != nil { return err } return nil } 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) } req, err := http.NewRequest(method, url, reader) if err != nil { return err } if privateKey != "" { if err := context.signRequest(keyId, privateKey, data, req); err != nil { return err } } if keyId == "activitystream" { req.Header.Add("Accept", "application/activity+json") } res, err := context.httpClient.Do(req) if err != nil { return err } if res.Body != nil { defer res.Body.Close() } if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusAccepted { return fmt.Errorf("%s", res.Status) } j, err := io.ReadAll(res.Body) if err != nil { return err } fmt.Println(string(j)) if output != nil { if err := json.Unmarshal(j, output); err != nil { return err } } return nil } 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) }