kopia lustrzana https://github.com/reiver/greatape
refactor(workspace): 🎨 improve structure and format of the code
rodzic
0af876979b
commit
1c2528ec80
|
@ -24,3 +24,15 @@ type Link struct {
|
|||
Type *string `json:"type,omitempty"`
|
||||
Template *string `json:"template,omitempty"`
|
||||
}
|
||||
|
||||
func (webfinger *Webfinger) Self() string {
|
||||
self := ""
|
||||
for _, link := range webfinger.Links {
|
||||
if link.Rel == "self" && link.Type != nil && *link.Type == "application/activity+json" {
|
||||
self = *link.Href
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return self
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
package domain
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Username string
|
||||
|
||||
func (u Username) IsFederated() bool {
|
||||
return strings.Contains(string(u), "@")
|
||||
}
|
||||
|
||||
func (u Username) IsEmpty() bool {
|
||||
return strings.TrimSpace(string(u)) == ""
|
||||
}
|
||||
|
||||
func (u Username) Webfinger() string {
|
||||
username := string(u)
|
||||
parts := strings.Split(username, "@")
|
||||
return fmt.Sprintf("https://%s/.well-known/webfinger?resource=acct:%s", parts[1], username)
|
||||
}
|
||||
|
||||
func (u Username) String() string {
|
||||
return string(u)
|
||||
}
|
|
@ -23,17 +23,17 @@ var Follow = route.New(HttpGet, "/u/:name/follow", func(x IContext) error {
|
|||
webfingerUrl := x.StringUtil().Format("https://%s/.well-known/webfinger?resource=acct:%s", parts[1], follower)
|
||||
resp, err := http.Get(webfingerUrl)
|
||||
if err != nil {
|
||||
x.InternalServerError(err.Error())
|
||||
x.InternalServerError(err)
|
||||
}
|
||||
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
x.InternalServerError(err.Error())
|
||||
x.InternalServerError(err)
|
||||
}
|
||||
|
||||
webfinger, err := activitypub.UnmarshalWebfinger(data)
|
||||
if err != nil {
|
||||
x.InternalServerError(err.Error())
|
||||
x.InternalServerError(err)
|
||||
}
|
||||
|
||||
template := ""
|
||||
|
|
|
@ -22,7 +22,7 @@ var Followers = route.New(HttpGet, "/u/:username/followers", func(x IContext) er
|
|||
followers := &[]types.FollowerResponse{}
|
||||
err := repos.FindFollowers(followers, actor).Error
|
||||
if err != nil {
|
||||
x.InternalServerError(err.Error())
|
||||
x.InternalServerError(err)
|
||||
}
|
||||
|
||||
items := []string{}
|
||||
|
@ -51,7 +51,7 @@ var AcceptFollowRequest = route.New(HttpPut, "/u/:username/followers/:id/accept"
|
|||
|
||||
follower := &repos.Follower{}
|
||||
if err := repos.FindFollowerById(follower, followerId).Error; err != nil {
|
||||
return x.InternalServerError(err.Error())
|
||||
return x.InternalServerError(err)
|
||||
}
|
||||
|
||||
data, _ := json.Marshal(&activitypub.Activity{
|
||||
|
@ -70,12 +70,12 @@ var AcceptFollowRequest = route.New(HttpPut, "/u/:username/followers/:id/accept"
|
|||
|
||||
keyId := x.StringUtil().Format("%s://%s/u/%s#main-key", config.PROTOCOL, config.DOMAIN, username)
|
||||
|
||||
if err := x.PostActivityStream(follower.HandleInbox, keyId, user.PrivateKey, data, nil); err != nil {
|
||||
return x.InternalServerError(err.Error())
|
||||
if err := x.PostActivityStreamSigned(follower.HandleInbox, keyId, user.PrivateKey, data, nil); err != nil {
|
||||
return x.InternalServerError(err)
|
||||
}
|
||||
|
||||
if err := repos.AcceptFollower(follower.ID).Error; err != nil {
|
||||
return x.InternalServerError(err.Error())
|
||||
return x.InternalServerError(err)
|
||||
}
|
||||
|
||||
return x.Nothing()
|
||||
|
|
|
@ -17,7 +17,7 @@ var Following = route.New(HttpGet, "/u/:username/following", func(x IContext) er
|
|||
followings := &[]types.FollowerResponse{}
|
||||
err := repos.FindFollowing(followings, actor).Error
|
||||
if err != nil {
|
||||
x.InternalServerError(err.Error())
|
||||
x.InternalServerError(err)
|
||||
}
|
||||
|
||||
items := []string{}
|
||||
|
|
|
@ -44,8 +44,8 @@ var InboxPost = route.New(HttpPost, "/u/:username/inbox", func(x IContext) error
|
|||
|
||||
{
|
||||
actor := &activitypub.Actor{}
|
||||
if err := x.GetActivityStream(url, keyId, user.PrivateKey, nil, actor); err != nil {
|
||||
return x.InternalServerError(err.Error())
|
||||
if err := x.GetActivityStreamSigned(url, keyId, user.PrivateKey, nil, actor); err != nil {
|
||||
return x.InternalServerError(err)
|
||||
}
|
||||
|
||||
inbox = actor.Inbox
|
||||
|
@ -53,7 +53,7 @@ var InboxPost = route.New(HttpPost, "/u/:username/inbox", func(x IContext) error
|
|||
|
||||
data, err := json.Marshal(activity)
|
||||
if err != nil {
|
||||
return x.InternalServerError(err.Error())
|
||||
return x.InternalServerError(err)
|
||||
}
|
||||
|
||||
follower := &repos.Follower{
|
||||
|
@ -65,7 +65,7 @@ var InboxPost = route.New(HttpPost, "/u/:username/inbox", func(x IContext) error
|
|||
}
|
||||
|
||||
if err := repos.CreateFollower(follower); err.Error != nil {
|
||||
return x.Conflict(err.Error.Error())
|
||||
return x.Conflict(err.Error)
|
||||
}
|
||||
|
||||
if user.Access == repos.ACCESS_PUBLIC {
|
||||
|
@ -77,13 +77,13 @@ var InboxPost = route.New(HttpPost, "/u/:username/inbox", func(x IContext) error
|
|||
Object: activity,
|
||||
})
|
||||
|
||||
if err := x.PostActivityStream(inbox, keyId, user.PrivateKey, data, nil); err != nil {
|
||||
return x.InternalServerError(err.Error())
|
||||
if err := x.PostActivityStreamSigned(inbox, keyId, user.PrivateKey, data, nil); err != nil {
|
||||
return x.InternalServerError(err)
|
||||
}
|
||||
|
||||
err := repos.AcceptFollower(follower.ID).Error
|
||||
if err != nil {
|
||||
return x.InternalServerError(err.Error())
|
||||
return x.InternalServerError(err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -112,7 +112,7 @@ var InboxPost = route.New(HttpPost, "/u/:username/inbox", func(x IContext) error
|
|||
}
|
||||
|
||||
if err := repos.CreateIncomingActivity(message); err.Error != nil {
|
||||
return x.Conflict(err.Error.Error())
|
||||
return x.Conflict(err.Error)
|
||||
}
|
||||
|
||||
return x.Nothing()
|
||||
|
|
|
@ -19,7 +19,7 @@ var OutboxPost = route.New(HttpPost, "/u/:username/outbox", func(x IContext) err
|
|||
|
||||
object := &activitypub.Object{}
|
||||
if err := x.ParseBodyAndValidate(object); err != nil {
|
||||
return x.BadRequest(err.Error())
|
||||
return x.BadRequest(err)
|
||||
}
|
||||
|
||||
key := &types.KeyResponse{}
|
||||
|
@ -35,7 +35,7 @@ var OutboxPost = route.New(HttpPost, "/u/:username/outbox", func(x IContext) err
|
|||
{
|
||||
note := &activitypub.Note{}
|
||||
if err := x.ParseBodyAndValidate(note); err != nil {
|
||||
return x.BadRequest(err.Error())
|
||||
return x.BadRequest(err)
|
||||
}
|
||||
|
||||
activity := note.Wrap(username)
|
||||
|
@ -44,16 +44,16 @@ var OutboxPost = route.New(HttpPost, "/u/:username/outbox", func(x IContext) err
|
|||
|
||||
if to != activitypub.Public {
|
||||
recipient := &activitypub.Actor{}
|
||||
if err := x.GetActivityStream(to, keyId, key.PrivateKey, nil, recipient); err != nil {
|
||||
return x.InternalServerError(err.Error())
|
||||
if err := x.GetActivityStreamSigned(to, keyId, key.PrivateKey, nil, recipient); err != nil {
|
||||
return x.InternalServerError(err)
|
||||
}
|
||||
|
||||
to = recipient.ID
|
||||
|
||||
data, _ := json.Marshal(activity)
|
||||
output := &struct{}{}
|
||||
if err := x.PostActivityStream(recipient.Inbox, keyId, key.PrivateKey, data, output); err != nil {
|
||||
return x.InternalServerError(err.Error())
|
||||
if err := x.PostActivityStreamSigned(recipient.Inbox, keyId, key.PrivateKey, data, output); err != nil {
|
||||
return x.InternalServerError(err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,7 +66,7 @@ var OutboxPost = route.New(HttpPost, "/u/:username/outbox", func(x IContext) err
|
|||
}
|
||||
|
||||
if err := repos.CreateOutgoingActivity(message); err.Error != nil {
|
||||
return x.Conflict(err.Error.Error())
|
||||
return x.Conflict(err.Error)
|
||||
}
|
||||
|
||||
return x.Nothing()
|
||||
|
|
|
@ -28,7 +28,7 @@ var GetProfile = route.New(HttpGet, "/api/v1/profile", func(x IContext) error {
|
|||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return x.Unauthorized("not_found")
|
||||
} else {
|
||||
return x.InternalServerError(err.Error())
|
||||
return x.InternalServerError(err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ var Signup = route.New(HttpPost, "/api/v1/signup", func(x IContext) error {
|
|||
}
|
||||
|
||||
if err := repos.CreateUser(user); err.Error != nil {
|
||||
return x.Conflict(err.Error.Error())
|
||||
return x.Conflict(err.Error)
|
||||
}
|
||||
|
||||
token := jwt.Generate(&jwt.TokenPayload{
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
var Upload = route.New(contracts.HttpPost, "/upload", func(x contracts.IContext) error {
|
||||
file, err := x.Request().FormFile("file")
|
||||
if err != nil {
|
||||
return x.InternalServerError(err.Error())
|
||||
return x.InternalServerError(err)
|
||||
}
|
||||
|
||||
uuid := uuid.New().String()
|
||||
|
@ -21,7 +21,7 @@ var Upload = route.New(contracts.HttpPost, "/upload", func(x contracts.IContext)
|
|||
|
||||
filePath := path.Join(config.UPLOAD_PATH, fileName)
|
||||
if err = x.SaveFile(file, filePath); err != nil {
|
||||
return x.InternalServerError(err.Error())
|
||||
return x.InternalServerError(err)
|
||||
}
|
||||
|
||||
return x.Json(struct {
|
||||
|
|
|
@ -2,58 +2,53 @@ package routes
|
|||
|
||||
import (
|
||||
"activitypub"
|
||||
"app/models/domain"
|
||||
"app/models/repos"
|
||||
"config"
|
||||
. "contracts"
|
||||
"contracts"
|
||||
"errors"
|
||||
"fmt"
|
||||
"server/mime"
|
||||
"server/route"
|
||||
"strings"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var User = route.New(HttpGet, "/u/:username", func(x IContext) error {
|
||||
username := x.Request().Params("username")
|
||||
if username == "" {
|
||||
return x.BadRequest("Bad request")
|
||||
var User = route.New(contracts.HttpGet, "/u/:username", func(x contracts.IContext) error {
|
||||
username := domain.Username(x.Request().Params("username"))
|
||||
if username.IsEmpty() {
|
||||
return x.BadRequest("username required.")
|
||||
}
|
||||
|
||||
if x.StringUtil().Contains(username, "@") {
|
||||
parts := x.StringUtil().Split(username, "@")
|
||||
url := x.StringUtil().Format("https://%s/.well-known/webfinger?resource=acct:%s", parts[1], username)
|
||||
|
||||
if username.IsFederated() {
|
||||
webfinger := activitypub.Webfinger{}
|
||||
if err := x.GetActivityStream(url, "", "", nil, &webfinger); err != nil {
|
||||
return x.InternalServerError(err.Error())
|
||||
if err := x.GetActivityStream(username.Webfinger(), nil, &webfinger); err != nil {
|
||||
return x.InternalServerError(err)
|
||||
}
|
||||
|
||||
actor := activitypub.Actor{}
|
||||
if err := x.GetActivityStream(webfinger.Aliases[0], "activitystream", "", nil, &actor); err != nil {
|
||||
return x.InternalServerError(err.Error())
|
||||
if err := x.GetActivityStream(webfinger.Self(), nil, &actor); err != nil {
|
||||
return x.InternalServerError(err)
|
||||
}
|
||||
|
||||
return x.Activity(actor)
|
||||
}
|
||||
|
||||
user := &repos.User{}
|
||||
err := repos.FindUserByUsername(user, username).Error
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return x.NotFound("No record found for %s.", username)
|
||||
}
|
||||
|
||||
actor := createActor(user)
|
||||
if strings.Contains(x.Request().Header("Accept"), mime.ActivityJson) {
|
||||
return x.Activity(actor)
|
||||
} else if config.DOMAIN == config.CLIENT_DOMAIN {
|
||||
return x.Render("user", ViewData{
|
||||
"Title": fmt.Sprintf("%s's Public Profile", user.DisplayName),
|
||||
"Username": user.Username,
|
||||
"Actor": actor,
|
||||
})
|
||||
} else {
|
||||
client := x.StringUtil().Format("%s://%s/u/%s", config.PROTOCOL, config.CLIENT_DOMAIN, user.Username)
|
||||
return x.Redirect(client)
|
||||
user := &repos.User{}
|
||||
err := repos.FindUserByUsername(user, username.String()).Error
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return x.NotFound("No record found for %s.", username)
|
||||
}
|
||||
|
||||
str := x.StringUtil()
|
||||
actor := createActor(user)
|
||||
if x.Request().AcceptsActivityStream() {
|
||||
return x.Activity(actor)
|
||||
} else if config.ExternalClient() {
|
||||
return x.Redirect(str.Format("%s://%s/u/%s", config.PROTOCOL, config.CLIENT_DOMAIN, user.Username))
|
||||
} else {
|
||||
return x.Render("user", contracts.ViewData{
|
||||
"Title": str.Format("%s's Public Profile", user.DisplayName),
|
||||
"Username": user.Username,
|
||||
"Actor": actor,
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -25,7 +25,7 @@ var WebFinger = route.New(HttpGet, "/.well-known/webfinger", func(x IContext) er
|
|||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return x.NotFound("No record found for %s.", name)
|
||||
} else {
|
||||
return x.InternalServerError(err.Error())
|
||||
return x.InternalServerError(err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -50,3 +50,7 @@ func BodyLimit() int {
|
|||
|
||||
return int(maxFileSize) * 1024 * 1024
|
||||
}
|
||||
|
||||
func ExternalClient() bool {
|
||||
return DOMAIN != CLIENT_DOMAIN
|
||||
}
|
||||
|
|
|
@ -23,14 +23,15 @@ type (
|
|||
Activity(interface{}) error
|
||||
File(string) error
|
||||
|
||||
GetActivityStream(url, keyId, privateKey string, data []byte, output interface{}) error
|
||||
PostActivityStream(url, keyId, privateKey string, data []byte, output interface{}) error
|
||||
GetActivityStream(url string, data []byte, output interface{}) error
|
||||
PostActivityStream(url string, data []byte, output interface{}) error
|
||||
GetActivityStreamSigned(url, keyId, privateKey string, data []byte, output interface{}) error
|
||||
PostActivityStreamSigned(url, keyId, privateKey string, data []byte, output interface{}) error
|
||||
|
||||
// Error(int, string, ...any) IServerError
|
||||
BadRequest(string, ...any) IServerError
|
||||
NotFound(string, ...any) IServerError
|
||||
InternalServerError(string, ...any) IServerError
|
||||
Unauthorized(string, ...any) IServerError
|
||||
Conflict(string, ...any) IServerError
|
||||
BadRequest(interface{}, ...any) IServerError
|
||||
NotFound(interface{}, ...any) IServerError
|
||||
InternalServerError(interface{}, ...any) IServerError
|
||||
Unauthorized(interface{}, ...any) IServerError
|
||||
Conflict(interface{}, ...any) IServerError
|
||||
}
|
||||
)
|
||||
|
|
|
@ -115,6 +115,7 @@ type (
|
|||
FormValue(string) string
|
||||
FormFile(key string) (*multipart.FileHeader, error)
|
||||
Header(string) string
|
||||
AcceptsActivityStream() bool
|
||||
}
|
||||
|
||||
IResponse interface {
|
||||
|
|
|
@ -123,27 +123,34 @@ func (context *httpServerContext) GetUser() uint {
|
|||
}
|
||||
|
||||
// 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) Error(code int, format interface{}, args ...any) IServerError {
|
||||
switch arg := format.(type) {
|
||||
case string:
|
||||
return newError(code, fmt.Sprintf(arg, args...))
|
||||
case error:
|
||||
return newError(code, arg.Error())
|
||||
default:
|
||||
return newError(code, fmt.Sprintf("%v", arg))
|
||||
}
|
||||
}
|
||||
|
||||
func (context *httpServerContext) BadRequest(format string, args ...any) IServerError {
|
||||
func (context *httpServerContext) BadRequest(format interface{}, args ...any) IServerError {
|
||||
return context.Error(StatusBadRequest, format, args...)
|
||||
}
|
||||
|
||||
func (context *httpServerContext) NotFound(format string, args ...any) IServerError {
|
||||
func (context *httpServerContext) NotFound(format interface{}, args ...any) IServerError {
|
||||
return context.Error(StatusNotFound, format, args...)
|
||||
}
|
||||
|
||||
func (context *httpServerContext) InternalServerError(format string, args ...any) IServerError {
|
||||
func (context *httpServerContext) InternalServerError(format interface{}, args ...any) IServerError {
|
||||
return context.Error(StatusInternalServerError, format, args...)
|
||||
}
|
||||
|
||||
func (context *httpServerContext) Unauthorized(format string, args ...any) IServerError {
|
||||
func (context *httpServerContext) Unauthorized(format interface{}, args ...any) IServerError {
|
||||
return context.Error(StatusUnauthorized, format, args...)
|
||||
}
|
||||
|
||||
func (context *httpServerContext) Conflict(format string, args ...any) IServerError {
|
||||
func (context *httpServerContext) Conflict(format interface{}, args ...any) IServerError {
|
||||
return context.Error(StatusConflict, format, args...)
|
||||
}
|
||||
|
||||
|
@ -164,8 +171,6 @@ func (context *httpServerContext) signRequest(keyId, privateKey string, data []b
|
|||
req.Header.Set("Digest", digest)
|
||||
}
|
||||
|
||||
req.Header.Set("Accept", mime.ActivityJson)
|
||||
|
||||
if err := signer.Sign(req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -184,16 +189,14 @@ func (context *httpServerContext) requestActivityStream(method, url, keyId, priv
|
|||
return err
|
||||
}
|
||||
|
||||
req.Header.Set("Accept", mime.ActivityJson)
|
||||
|
||||
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
|
||||
|
@ -208,27 +211,27 @@ func (context *httpServerContext) requestActivityStream(method, url, keyId, priv
|
|||
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 {
|
||||
if err := json.NewDecoder(res.Body).Decode(output); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (context *httpServerContext) GetActivityStream(url, keyId, privateKey string, data []byte, output interface{}) error {
|
||||
func (context *httpServerContext) GetActivityStream(url string, data []byte, output interface{}) error {
|
||||
return context.requestActivityStream(http.MethodGet, url, "", "", data, output)
|
||||
}
|
||||
|
||||
func (context *httpServerContext) PostActivityStream(url string, data []byte, output interface{}) error {
|
||||
return context.requestActivityStream(http.MethodPost, url, "", "", data, output)
|
||||
}
|
||||
|
||||
func (context *httpServerContext) GetActivityStreamSigned(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 {
|
||||
func (context *httpServerContext) PostActivityStreamSigned(url, keyId, privateKey string, data []byte, output interface{}) error {
|
||||
return context.requestActivityStream(http.MethodPost, url, keyId, privateKey, data, output)
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ package server
|
|||
import (
|
||||
"contracts"
|
||||
"mime/multipart"
|
||||
"server/mime"
|
||||
"strings"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
@ -41,3 +43,7 @@ func (request *httpRequest) FormValue(key string) string {
|
|||
func (request *httpRequest) FormFile(key string) (*multipart.FileHeader, error) {
|
||||
return request.context.FormFile(key)
|
||||
}
|
||||
|
||||
func (request *httpRequest) AcceptsActivityStream() bool {
|
||||
return strings.Contains(request.Header("Accept"), mime.ActivityJson)
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue