diff --git a/main.go b/main.go index ead390d..d47deeb 100644 --- a/main.go +++ b/main.go @@ -39,6 +39,9 @@ var nojsFS embed.FS //go:embed duck.webp var defaultDuck string +//go:embed duck.webp +var duckFS embed.FS + func main() { other.SetupFlags() flag.Parse() @@ -167,6 +170,7 @@ func newServer() { public := webpublic.New( fmt.Sprintf(":%v", config.GlobalConfig.General.PrivatePort), &defaultDuck, + duckFS, ) if err = public.Start(); err != nil { log.Fatal().Err(err).Msg("Failed to start public server") diff --git a/temp.toml b/temp.toml index bffcb29..55dee49 100644 --- a/temp.toml +++ b/temp.toml @@ -1,8 +1,7 @@ -# . [general] protocol = "https" domain = "serveo.net" - subdomain = "5dc9c90be02fecb3b132ad4d4877555a" + subdomain = "e8f7573481d6b2311fb076f4190ac852" private_port = 8080 public_port = 443 diff --git a/web/debug/server.go b/web/debug/server.go index 542e198..490e9fa 100644 --- a/web/debug/server.go +++ b/web/debug/server.go @@ -30,6 +30,8 @@ func New(addr string) *Server { handler.HandleFunc("GET /import-user", issueUserImport) handler.HandleFunc("GET /keys-for", returnKeypair) handler.HandleFunc("GET /import-server", importServerInfo) + handler.HandleFunc("GET /request-follow", kickoffFollow) + handler.HandleFunc("POST /send-as", proxyMessageToTarget) web := http.Server{ Addr: addr, Handler: webutils.ChainMiddlewares( diff --git a/web/debug/users.go b/web/debug/users.go index 040e74b..872d7ab 100644 --- a/web/debug/users.go +++ b/web/debug/users.go @@ -6,6 +6,7 @@ import ( "encoding/json" "encoding/pem" "fmt" + "io" "net/http" "strconv" "time" @@ -206,13 +207,78 @@ func returnKeypair(w http.ResponseWriter, r *http.Request) { func issueUserImport(w http.ResponseWriter, r *http.Request) { target := r.FormValue("target") _, err := activitypub.ImportRemoteAccountByHandle(target) - hlog.FromRequest(r).Info().Err(err).Msg("Err from import request") + if err != nil { + hlog.FromRequest(r).Info().Err(err).Msg("Err from import request") + } +} + +func proxyMessageToTarget(w http.ResponseWriter, r *http.Request) { + type Inbound struct { + From string `json:"from"` + Target string `json:"target"` + Message any `json:"message"` + } + log := hlog.FromRequest(r) + data := Inbound{} + dec := json.NewDecoder(r.Body) + err := dec.Decode(&data) + if err != nil { + log.Warn().Err(err).Msg("Failed to decode json body") + webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError) + return + } + log.Debug().Any("data", data).Msg("Received message") + user, err := dbgen.User.GetByUsername(data.From) + if err != nil { + log.Error().Err(err).Msg("Failed to get user from storage") + webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError) + return + } + targetId, err := activitypub.ImportRemoteAccountByHandle(data.Target) + if err != nil { + log.Error().Err(err).Msg("Failed to import target user") + webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError) + return + } + target, err := dbgen.User.Where(dbgen.User.ID.Eq(targetId)). + Preload(dbgen.User.RemoteInfo). + First() + if err != nil { + log.Error().Err(err).Msg("Failed to get target user from db") + webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError) + return + } + + outBody, err := json.Marshal(data.Message) + if err != nil { + log.Error().Err(err).Msg("Failed to marshal out data") + webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError) + return + } + log.Debug().Bytes("request-body", outBody).Msg("Body of proxied request") + response, err := webshared.RequestSignedCavage( + "POST", + target.RemoteInfo.InboxLink, + outBody, + user, + ) + if err != nil { + log.Error().Err(err).Msg("Request failed") + webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError) + return + } + defer response.Body.Close() + respBody, _ := io.ReadAll(response.Body) + log.Debug(). + Int("status-code", response.StatusCode). + Bytes("body", respBody). + Msg("Response from message") } func kickoffFollow(w http.ResponseWriter, r *http.Request) { type Inbound struct { - Id string - Target string + From string `json:"from"` + To string `json:"to"` } var data Inbound dec := json.NewDecoder(r.Body) diff --git a/web/public/middleware/authFetchCheck.go b/web/public/middleware/authFetchCheck.go index d87e920..1e39f5c 100644 --- a/web/public/middleware/authFetchCheck.go +++ b/web/public/middleware/authFetchCheck.go @@ -44,8 +44,6 @@ func BuildAuthorizedFetchCheck(forNonGet bool, forGet bool) webutils.HandlerBuil return func(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { log := hlog.FromRequest(r) - log.Info().Msg("AuthFetch middleware") - defer log.Info().Msg("AuhFetch completed") path := r.URL.Path // Check always open path first for _, re := range publicPaths { diff --git a/web/public/server.go b/web/public/server.go index aa56ee7..065472e 100644 --- a/web/public/server.go +++ b/web/public/server.go @@ -29,6 +29,7 @@ package webpublic import ( "context" "fmt" + "io/fs" "net/http" webutils "git.mstar.dev/mstar/goutils/http" @@ -41,7 +42,7 @@ type Server struct { server *http.Server } -func New(addr string, duckImg *string) *Server { +func New(addr string, duckImg *string, duckFs fs.FS) *Server { handler := http.NewServeMux() handler.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(500) @@ -53,11 +54,8 @@ func New(addr string, duckImg *string) *Server { handler.HandleFunc("GET /nodeinfo/2.1", api.NodeInfo21) handler.HandleFunc("GET /nodeinfo/2.0", api.NodeInfo20) handler.HandleFunc("GET /errors/{name}", errorTypeHandler) - handler.HandleFunc("GET /default-image", func(w http.ResponseWriter, r *http.Request) { - w.Header().Add("Content-Type", "image/web") - w.Header().Add("Content-Disposition", "attachment; filename=\"duck.webp\"") - fmt.Fprint(w, *duckImg) - }) + handler.HandleFunc("GET /default-image", buildServeDefaultImage(duckImg, duckFs)) + handler.HandleFunc("GET /default-image.webp", buildServeDefaultImage(duckImg, duckFs)) server := http.Server{ Handler: webutils.ChainMiddlewares( handler, @@ -76,3 +74,15 @@ func (s *Server) Start() error { func (s *Server) Stop() error { return s.server.Shutdown(context.Background()) } + +func buildServeDefaultImage( + duckImg *string, + duckFs fs.FS, +) func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + http.ServeFileFS(w, r, duckFs, "duck.webp") + // w.Header().Add("Content-Type", "image/webp") + // w.Header().Add("Content-Disposition", "attachment; filename=\"duck.webp\"") + // fmt.Fprint(w, *duckImg) + } +} diff --git a/web/shared/clientRfc9421.go b/web/shared/clientRfc9421.go index 6fe9d5d..22510cb 100644 --- a/web/shared/clientRfc9421.go +++ b/web/shared/clientRfc9421.go @@ -5,10 +5,12 @@ import ( "crypto/sha256" "crypto/x509" "encoding/base64" + "io" "net/http" "slices" "time" + "github.com/rs/zerolog/log" "github.com/yaronf/httpsign" "git.mstar.dev/mstar/linstrom/config" @@ -77,11 +79,16 @@ func RequestSignedCavage( body []byte, actor *models.User, ) (*http.Response, error) { - req, err := NewRequest(method, target, nil) + var bodyReader io.Reader + if body != nil { + bodyReader = bytes.NewReader(body) + } + req, err := NewRequest(method, target, bodyReader) if err != nil { return nil, err } req.Header.Add("Accept", "application/activity+json") + req.Header.Add("Content-Type", "application/activity+json") var keyBytes []byte if config.GlobalConfig.Experimental.UseEd25519Keys { @@ -101,6 +108,7 @@ func RequestSignedCavage( if err != nil { return nil, err } + log.Debug().Bytes("body", body).Any("headers", req.Header).Msg("Sending signed request") return RequestClient.Do(req) } @@ -119,7 +127,7 @@ func applyBodyHash(headers http.Header, body []byte) error { return nil } hash := sha256.Sum256(body) - based := base64.StdEncoding.EncodeToString(hash[:]) - headers.Set("Digest", "SHA-256="+based) + header := "SHA-256=" + base64.StdEncoding.EncodeToString(hash[:]) + headers.Set("Digest", header) return nil } diff --git a/web/shared/linstromUrlType.go b/web/shared/linstromUrlType.go index 2639163..9377b54 100644 --- a/web/shared/linstromUrlType.go +++ b/web/shared/linstromUrlType.go @@ -5,7 +5,7 @@ import "strings" // TODO: Define linstrom uri type var hardcodedUrls = map[string]string{ - "default-media": "/default-image", + "default-media": "/default-image.webp", } func EnsurePublicUrl(rawUrl string) string {