From 1a6f61690eb83b07e2b1b2661ffdbca07595f7f2 Mon Sep 17 00:00:00 2001 From: Matt Baer Date: Wed, 17 Oct 2018 18:57:37 -0400 Subject: [PATCH] Add NodeInfo endpoints Includes new instance stats and the new option to show user stats, as well. --- app.go | 4 +++ config/config.go | 3 ++ nodeinfo.go | 87 ++++++++++++++++++++++++++++++++++++++++++++++++ routes.go | 10 ++++++ 4 files changed, 104 insertions(+) create mode 100644 nodeinfo.go diff --git a/app.go b/app.go index 82548e9..76aae55 100644 --- a/app.go +++ b/app.go @@ -18,6 +18,10 @@ import ( const ( staticDir = "static/" + + serverSoftware = "Write Freely" + softwareURL = "https://writefreely.org" + softwareVer = "0.1" ) type app struct { diff --git a/config/config.go b/config/config.go index 630dfe3..77a1f58 100644 --- a/config/config.go +++ b/config/config.go @@ -27,6 +27,8 @@ type ( MultiUser bool `ini:"multiuser"` OpenSignups bool `ini:"open_signups"` Federation bool `ini:"federation"` + PublicStats bool `ini:"public_stats"` + Private bool `ini:"private"` Name string `ini:"site_name"` @@ -56,6 +58,7 @@ func New() *Config { }, App: AppCfg{ Federation: true, + PublicStats: true, MinUsernameLen: 3, }, } diff --git a/nodeinfo.go b/nodeinfo.go new file mode 100644 index 0000000..ae50e9f --- /dev/null +++ b/nodeinfo.go @@ -0,0 +1,87 @@ +package writefreely + +import ( + "fmt" + "github.com/writeas/go-nodeinfo" + "github.com/writeas/web-core/log" + "github.com/writeas/writefreely/config" +) + +type nodeInfoResolver struct { + cfg *config.Config + db *datastore +} + +func nodeInfoConfig(cfg *config.Config) *nodeinfo.Config { + name := cfg.App.SiteName + return &nodeinfo.Config{ + BaseURL: cfg.Server.Host, + InfoURL: "/api/nodeinfo", + + Metadata: nodeinfo.Metadata{ + NodeName: name, + NodeDescription: "Minimal, federated blogging platform.", + Private: cfg.App.Private, + Software: nodeinfo.SoftwareMeta{ + HomePage: softwareURL, + GitHub: "https://github.com/writeas/writefreely", + Follow: "https://writing.exchange/@write_as", + }, + }, + Protocols: []nodeinfo.NodeProtocol{ + nodeinfo.ProtocolActivityPub, + }, + Services: nodeinfo.Services{ + Inbound: []nodeinfo.NodeService{}, + Outbound: []nodeinfo.NodeService{}, + }, + Software: nodeinfo.SoftwareInfo{ + Name: serverSoftware, + Version: softwareVer, + }, + } +} + +func (r nodeInfoResolver) IsOpenRegistration() (bool, error) { + return !r.cfg.App.Private, nil +} + +func (r nodeInfoResolver) Usage() (nodeinfo.Usage, error) { + var collCount, postCount, activeHalfYear, activeMonth int + err := r.db.QueryRow(`SELECT COUNT(*) FROM collections`).Scan(&collCount) + if err != nil { + collCount = 0 + } + err = r.db.QueryRow(`SELECT COUNT(*) FROM posts`).Scan(&postCount) + if err != nil { + log.Error("Unable to fetch post counts: %v", err) + } + + if r.cfg.App.PublicStats { + // Display bi-yearly / monthly stats + err = r.db.QueryRow(fmt.Sprintf(`SELECT COUNT(*) FROM ( +SELECT DISTINCT collection_id +FROM posts +INNER JOIN collections c +ON collection_id = c.id +WHERE collection_id IS NOT NULL + AND updated > DATE_SUB(NOW(), INTERVAL 6 MONTH)) co`, CollPublic)).Scan(&activeHalfYear) + + err = r.db.QueryRow(fmt.Sprintf(`SELECT COUNT(*) FROM ( +SELECT DISTINCT collection_id +FROM posts +INNER JOIN FROM collections c +ON collection_id = c.id +WHERE collection_id IS NOT NULL + AND updated > DATE_SUB(NOW(), INTERVAL 1 MONTH)) co`, CollPublic)).Scan(&activeMonth) + } + + return nodeinfo.Usage{ + Users: nodeinfo.UsageUsers{ + Total: collCount, + ActiveHalfYear: activeHalfYear, + ActiveMonth: activeMonth, + }, + LocalPosts: postCount, + }, nil +} diff --git a/routes.go b/routes.go index 545fc78..45e3be9 100644 --- a/routes.go +++ b/routes.go @@ -2,8 +2,10 @@ package writefreely import ( "github.com/gorilla/mux" + "github.com/writeas/go-nodeinfo" "github.com/writeas/web-core/log" "github.com/writeas/writefreely/config" + "net/http" "strings" ) @@ -28,4 +30,12 @@ func initRoutes(handler *Handler, r *mux.Router, cfg *config.Config, db *datasto // Primary app routes log.Info("Adding %s routes (multi-user)...", hostSubroute) + write := r.Host(hostSubroute).Subrouter() + + // Federation endpoints + // nodeinfo + niCfg := nodeInfoConfig(cfg) + ni := nodeinfo.NewService(*niCfg, nodeInfoResolver{cfg, db}) + write.HandleFunc(nodeinfo.NodeInfoPath, handler.LogHandlerFunc(http.HandlerFunc(ni.NodeInfoDiscover))) + write.HandleFunc(niCfg.InfoURL, handler.LogHandlerFunc(http.HandlerFunc(ni.NodeInfo))) }