[feature] Change instance-stats-randomize to instance-stats-mode with multiple options; implement nodeinfo 2.1 (#3734)

* [feature] Change `instance-stats-randomize` to `instance-stats-mode` with multiple options; implement nodeinfo 2.1

* swaggalaggadingdong
This commit is contained in:
tobi 2025-02-04 16:52:42 +01:00 committed by GitHub
commit 07d2770995
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 283 additions and 77 deletions

View file

@ -60,10 +60,21 @@ func (m *Module) InstanceInformationGETHandlerV1(c *gin.Context) {
return
}
if config.GetInstanceStatsRandomize() {
switch config.GetInstanceStatsMode() {
case config.InstanceStatsModeBaffle:
// Replace actual stats with cached randomized ones.
instance.Stats["user_count"] = util.Ptr(int(instance.RandomStats.TotalUsers))
instance.Stats["status_count"] = util.Ptr(int(instance.RandomStats.Statuses))
case config.InstanceStatsModeZero:
// Replace actual stats with zero.
instance.Stats["user_count"] = new(int)
instance.Stats["status_count"] = new(int)
default:
// serve or default.
// Leave stats alone.
}
apiutil.JSON(c, http.StatusOK, instance)
@ -101,9 +112,19 @@ func (m *Module) InstanceInformationGETHandlerV2(c *gin.Context) {
return
}
if config.GetInstanceStatsRandomize() {
switch config.GetInstanceStatsMode() {
case config.InstanceStatsModeBaffle:
// Replace actual stats with cached randomized ones.
instance.Usage.Users.ActiveMonth = int(instance.RandomStats.MonthlyActiveUsers)
case config.InstanceStatsModeZero:
// Replace actual stats with zero.
instance.Usage.Users.ActiveMonth = 0
default:
// serve or default.
// Leave stats alone.
}
apiutil.JSON(c, http.StatusOK, instance)

View file

@ -70,6 +70,12 @@ type NodeInfoSoftware struct {
Name string `json:"name"`
// example: 0.1.2 1234567
Version string `json:"version"`
// Repository for the software. Omitted in version 2.0.
// example: https://codeberg.org/superseriousbusiness/gotosocial
Repository string `json:"repository,omitempty"`
// Homepage for the software. Omitted in version 2.0.
// example: https://docs.gotosocial.org
Homepage string `json:"homepage,omitempty"`
}
// NodeInfoServices represents inbound and outbound services that this node offers connections to.
@ -80,13 +86,16 @@ type NodeInfoServices struct {
// NodeInfoUsage represents usage information about this server, such as number of users.
type NodeInfoUsage struct {
Users NodeInfoUsers `json:"users"`
LocalPosts int `json:"localPosts"`
Users NodeInfoUsers `json:"users"`
LocalPosts int `json:"localPosts,omitempty"`
LocalComments int `json:"localComments,omitempty"`
}
// NodeInfoUsers represents aggregate information about the users on the server.
type NodeInfoUsers struct {
Total int `json:"total"`
Total int `json:"total"`
ActiveHalfYear int `json:"activeHalfYear,omitempty"`
ActiveMonth int `json:"activeMonth,omitempty"`
}
// HostMeta represents a hostmeta document.

View file

@ -36,9 +36,9 @@ func (w *NodeInfo) Route(r *router.Router, m ...gin.HandlerFunc) {
// attach middlewares appropriate for this group
nodeInfoGroup.Use(m...)
nodeInfoGroup.Use(
// Allow public cache for 2 minutes.
// Allow public cache for 24 hours.
middleware.CacheControl(middleware.CacheControlConfig{
Directives: []string{"public", "max-age=120"},
Directives: []string{"public", "max-age=86400"},
Vary: []string{"Accept-Encoding"},
}),
)

View file

@ -25,9 +25,12 @@ import (
)
const (
NodeInfo2Version = "2.0"
NodeInfo2Path = "/" + NodeInfo2Version
NodeInfo2ContentType = "application/json; profile=\"http://nodeinfo.diaspora.software/ns/schema/" + NodeInfo2Version + "#\""
NodeInfo20 = "2.0"
NodeInfo20ContentType = "application/json; profile=\"http://nodeinfo.diaspora.software/ns/schema/" + NodeInfo20 + "#\""
NodeInfo21 = "2.1"
NodeInfo21ContentType = "application/json; profile=\"http://nodeinfo.diaspora.software/ns/schema/" + NodeInfo21 + "#\""
NodeInfoSchema = "schema"
NodeInfoPath = "/:" + NodeInfoSchema
)
type Module struct {
@ -41,5 +44,5 @@ func New(processor *processing.Processor) *Module {
}
func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
attachHandler(http.MethodGet, NodeInfo2Path, m.NodeInfo2GETHandler)
attachHandler(http.MethodGet, NodeInfoPath, m.NodeInfo2GETHandler)
}

View file

@ -18,6 +18,7 @@
package nodeinfo
import (
"errors"
"net/http"
"github.com/gin-gonic/gin"
@ -25,7 +26,7 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
)
// NodeInfo2GETHandler swagger:operation GET /nodeinfo/2.0 nodeInfoGet
// NodeInfo2GETHandler swagger:operation GET /nodeinfo/{schema_version} nodeInfoGet
//
// Returns a compliant nodeinfo response to node info queries.
//
@ -35,8 +36,17 @@ import (
// tags:
// - nodeinfo
//
// parameters:
// -
// name: schema_version
// type: string
// description: Schema version of nodeinfo to request. 2.0 and 2.1 are currently supported.
// in: path
// required: true
//
// produces:
// - application/json; profile="http://nodeinfo.diaspora.software/ns/schema/2.0#"
// - application/json; profile="http://nodeinfo.diaspora.software/ns/schema/2.1#"
//
// responses:
// '200':
@ -48,7 +58,23 @@ func (m *Module) NodeInfo2GETHandler(c *gin.Context) {
return
}
nodeInfo, errWithCode := m.processor.Fedi().NodeInfoGet(c.Request.Context())
var (
contentType string
schemaVersion = c.Param(NodeInfoSchema)
)
switch schemaVersion {
case NodeInfo20:
contentType = NodeInfo20ContentType
case NodeInfo21:
contentType = NodeInfo21ContentType
default:
const errText = "only nodeinfo 2.0 and 2.1 are supported"
apiutil.ErrorHandler(c, gtserror.NewErrorNotFound(errors.New(errText), errText), m.processor.InstanceGetV1)
return
}
nodeInfo, errWithCode := m.processor.Fedi().NodeInfoGet(c.Request.Context(), schemaVersion)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
@ -59,7 +85,7 @@ func (m *Module) NodeInfo2GETHandler(c *gin.Context) {
c.Writer,
c.Request,
http.StatusOK,
NodeInfo2ContentType,
contentType,
nodeInfo,
)
}