commit 53f4629963069b1872a368c23e3e67c6e76a13e6 Author: Matt Baer Date: Mon Jul 23 19:49:38 2018 +0200 Create nodeinfo library diff --git a/nodeinfo.go b/nodeinfo.go new file mode 100644 index 0000000..f80b248 --- /dev/null +++ b/nodeinfo.go @@ -0,0 +1,80 @@ +package nodeinfo + +type ( + NodeProtocol string + NodeService string +) + +const ( + // Protocols that can be supported by this node. + ProtocolActivityPub NodeProtocol = "activitypub" + ProtocolOStatus = "ostatus" + + // Services that can be supported (inbound or outbound) by this node's API. + ServiceTwitter NodeService = "twitter" + ServiceTumblr = "tumblr" +) + +type Config struct { + InfoURL string + Metadata Metadata + Protocols []NodeProtocol + Services Services + Software SoftwareInfo +} + +type ( + // NodeInfo includes all required node info. + NodeInfo struct { + Metadata Metadata `json:"metadata"` + OpenRegistrations bool `json:"openRegistrations"` + Protocols []NodeProtocol `json:"protocols"` + Services Services `json:"services"` + Software SoftwareInfo `json:"software"` + Usage Usage `json:"usage"` + Version string `json:"version"` + } + + // Metadata for nodeinfo. Properties are based on what Pleroma uses. + // + // From the spec: Free form key value pairs for software specific values. + // Clients should not rely on any specific key present. + Metadata struct { + NodeName string `json:"nodeName,omitempty"` + NodeDescription string `json:"nodeDescription,omitempty"` + Private bool `json:"private,omitempty"` + } + + Services struct { + Inbound []NodeService `json:"inbound"` + Outbound []NodeService `json:"outbound"` + } + + SoftwareInfo struct { + // Name (canonical) of this server software. + Name string `json:"name"` + // Version of this server software. + Version string `json:"version"` + } + + // Usage is usage statistics for this server. + Usage struct { + Users UsageUsers `json:"users"` + LocalPosts int `json:"localPosts,omitempty"` + LocalComments int `json:"localComments,omitempty"` + } + + UsageUsers struct { + Total int `json:"total,omitempty"` + ActiveHalfYear int `json:"activeHalfyear,omitempty"` + ActiveMonth int `json:"activeMonth,omitempty"` + } +) + +func (s Service) BuildInfo() NodeInfo { + ni := s.Info + ni.OpenRegistrations, _ = s.resolver.IsOpenRegistration() + ni.Usage, _ = s.resolver.Usage() + ni.Version = profileVer + return ni +} diff --git a/resolver.go b/resolver.go new file mode 100644 index 0000000..4fc4bf7 --- /dev/null +++ b/resolver.go @@ -0,0 +1,8 @@ +package nodeinfo + +type Resolver interface { + // IsOpenRegistration returns whether or not registration is open on this node. + IsOpenRegistration() (bool, error) + // Usage returns usage stats for this node. + Usage() (Usage, error) +} diff --git a/routes.go b/routes.go new file mode 100644 index 0000000..dd11b45 --- /dev/null +++ b/routes.go @@ -0,0 +1,64 @@ +package nodeinfo + +import ( + "encoding/json" + "github.com/writeas/go-webfinger" + "log" + "net/http" +) + +// NodeInfoPath defines the default path of the nodeinfo handler. +const NodeInfoPath = "/.well-known/nodeinfo" + +type discoverInfo struct { + Links []webfinger.Link `json:"links"` +} + +func (s *Service) NodeInfoDiscover(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json; charset=UTF-8") + + i := discoverInfo{ + Links: []webfinger.Link{ + { + Rel: profile, + HRef: s.InfoURL, + }, + }, + } + + body, err := json.Marshal(i) + if err != nil { + log.Printf("Unable to marshal nodeinfo discovery: %v", err) + w.WriteHeader(http.StatusInternalServerError) + return + } + + w.WriteHeader(http.StatusOK) + + _, err = w.Write(body) + if err != nil { + log.Printf("Unable to write body: %v", err) + return + } +} + +func (s *Service) NodeInfo(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json; profile="+profile+"#") + + i := s.BuildInfo() + + body, err := json.Marshal(i) + if err != nil { + log.Printf("Unable to marshal nodeinfo: %v", err) + w.WriteHeader(http.StatusInternalServerError) + return + } + + w.WriteHeader(http.StatusOK) + + _, err = w.Write(body) + if err != nil { + log.Printf("Unable to write body: %v", err) + return + } +} diff --git a/service.go b/service.go new file mode 100644 index 0000000..52c6293 --- /dev/null +++ b/service.go @@ -0,0 +1,26 @@ +package nodeinfo + +const ( + profileVer = "2.0" + profile = "http://nodeinfo.diaspora.software/ns/schema/" + profileVer +) + +type Service struct { + InfoURL string + Info NodeInfo + + resolver Resolver +} + +func NewService(cfg Config, r Resolver) *Service { + return &Service{ + InfoURL: cfg.InfoURL, + Info: NodeInfo{ + Metadata: cfg.Metadata, + Protocols: cfg.Protocols, + Services: cfg.Services, + Software: cfg.Software, + }, + resolver: r, + } +}