package wellknown import ( "fmt" "net/http" "net/url" "strings" "git.sleepycat.moe/sam/mercury/internal/database/sql" "git.sleepycat.moe/sam/mercury/web/api" "github.com/rs/zerolog/log" ) func (app *App) WebFinger(w http.ResponseWriter, r *http.Request) (any, error) { ctx := r.Context() validDomains := app.AppConfig.Web.WebFingerDomains() resource, err := url.QueryUnescape(r.FormValue("resource")) if err != nil { return nil, api.Error{Code: api.ErrBadRequest} } if !strings.HasPrefix(resource, "acct:") { return nil, api.Error{Code: api.ErrBadRequest, Details: "WebFinger only supports `acct:` queries"} } resource = strings.TrimPrefix(resource, "acct:") username, domain, ok := strings.Cut(resource, "@") if !ok { return nil, api.Error{Code: api.ErrBadRequest} } if !anyMatches(validDomains, domain) { return nil, api.Error{Code: api.ErrBadRequest, Details: "Not a local user on this instance"} } blog, err := app.Blog().ByName(ctx, username, "") if err != nil { if err == sql.ErrNotFound { return nil, api.Error{Code: api.ErrNotFound} } log.Err(err).Str("username", username).Msg("looking up user for webfinger request") return nil, err } webFingerHref := fmt.Sprintf("%s/@%s", app.AppConfig.Web.Domain, blog.Name) return WebFinger{ Subject: fmt.Sprintf("acct:%s@%s", blog.Name, validDomains[0]), Aliases: []string{webFingerHref}, Links: []WebFingerLink{ { Rel: "http://webfinger.net/rel/profile-page", Type: "text/html", Href: webFingerHref, }, { Rel: "self", Type: "application/activity+json", Href: webFingerHref, }, }, }, nil } type WebFinger struct { Subject string `json:"subject"` Aliases []string `json:"aliases"` Links []WebFingerLink `json:"links"` } type WebFingerLink struct { Rel string `json:"rel"` Type string `json:"type"` Href string `json:"href"` } func anyMatches[T comparable](slice []T, t T) bool { for _, entry := range slice { if entry == t { return true } } return false }