summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cmd/admin.go2
-rw-r--r--custom/conf/app.example.ini3
-rw-r--r--docs/content/doc/advanced/config-cheat-sheet.en-us.md1
-rw-r--r--integrations/api_nodeinfo_test.go4
-rw-r--r--models/issue_test.go7
-rw-r--r--models/statistic.go2
-rw-r--r--models/user/user.go23
-rw-r--r--modules/context/api.go2
-rw-r--r--modules/setting/federation.go6
-rw-r--r--routers/api/v1/misc/nodeinfo.go42
-rw-r--r--routers/web/auth/auth.go2
11 files changed, 78 insertions, 16 deletions
diff --git a/cmd/admin.go b/cmd/admin.go
index 36cac50a47..fcf331751c 100644
--- a/cmd/admin.go
+++ b/cmd/admin.go
@@ -556,7 +556,7 @@ func runCreateUser(c *cli.Context) error {
// If this is the first user being created.
// Take it as the admin and don't force a password update.
- if n := user_model.CountUsers(); n == 0 {
+ if n := user_model.CountUsers(nil); n == 0 {
changePassword = false
}
diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini
index cbfde9c8d8..0d0d809755 100644
--- a/custom/conf/app.example.ini
+++ b/custom/conf/app.example.ini
@@ -2240,6 +2240,9 @@ PATH =
;;
;; Enable/Disable federation capabilities
; ENABLED = true
+;;
+;; Enable/Disable user statistics for nodeinfo if federation is enabled
+; SHARE_USER_STATISTICS = true
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
diff --git a/docs/content/doc/advanced/config-cheat-sheet.en-us.md b/docs/content/doc/advanced/config-cheat-sheet.en-us.md
index 84703aa563..5400f3208e 100644
--- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md
+++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md
@@ -1085,6 +1085,7 @@ Task queue configuration has been moved to `queue.task`. However, the below conf
## Federation (`federation`)
- `ENABLED`: **true**: Enable/Disable federation capabilities
+- `SHARE_USER_STATISTICS`: **true**: Enable/Disable user statistics for nodeinfo if federation is enabled
## Packages (`packages`)
diff --git a/integrations/api_nodeinfo_test.go b/integrations/api_nodeinfo_test.go
index 1d25dc0269..822dbf3f0e 100644
--- a/integrations/api_nodeinfo_test.go
+++ b/integrations/api_nodeinfo_test.go
@@ -26,6 +26,10 @@ func TestNodeinfo(t *testing.T) {
resp := MakeRequest(t, req, http.StatusOK)
var nodeinfo api.NodeInfo
DecodeJSON(t, resp, &nodeinfo)
+ assert.True(t, nodeinfo.OpenRegistrations)
assert.Equal(t, "gitea", nodeinfo.Software.Name)
+ assert.Equal(t, 23, nodeinfo.Usage.Users.Total)
+ assert.Equal(t, 15, nodeinfo.Usage.LocalPosts)
+ assert.Equal(t, 2, nodeinfo.Usage.LocalComments)
})
}
diff --git a/models/issue_test.go b/models/issue_test.go
index 19e1295264..5382c04f43 100644
--- a/models/issue_test.go
+++ b/models/issue_test.go
@@ -590,3 +590,10 @@ func TestLoadTotalTrackedTime(t *testing.T) {
assert.Equal(t, int64(3682), milestone.TotalTrackedTime)
}
+
+func TestCountIssues(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+ count, err := CountIssues(&IssuesOptions{})
+ assert.NoError(t, err)
+ assert.EqualValues(t, 15, count)
+}
diff --git a/models/statistic.go b/models/statistic.go
index 87c1bd6d75..d858102be8 100644
--- a/models/statistic.go
+++ b/models/statistic.go
@@ -49,7 +49,7 @@ type IssueByRepositoryCount struct {
// GetStatistic returns the database statistics
func GetStatistic() (stats Statistic) {
e := db.GetEngine(db.DefaultContext)
- stats.Counter.User = user_model.CountUsers()
+ stats.Counter.User = user_model.CountUsers(nil)
stats.Counter.Org = organization.CountOrganizations()
stats.Counter.PublicKey, _ = e.Count(new(asymkey_model.PublicKey))
stats.Counter.Repo = repo_model.CountRepositories(true)
diff --git a/models/user/user.go b/models/user/user.go
index 5b556c9884..6aa63a0a56 100644
--- a/models/user/user.go
+++ b/models/user/user.go
@@ -744,16 +744,25 @@ func CreateUser(u *User, overwriteDefault ...*CreateUserOverwriteOptions) (err e
return committer.Commit()
}
-func countUsers(e db.Engine) int64 {
- count, _ := e.
- Where("type=0").
- Count(new(User))
- return count
+// CountUserFilter represent optional filters for CountUsers
+type CountUserFilter struct {
+ LastLoginSince *int64
}
// CountUsers returns number of users.
-func CountUsers() int64 {
- return countUsers(db.GetEngine(db.DefaultContext))
+func CountUsers(opts *CountUserFilter) int64 {
+ return countUsers(db.DefaultContext, opts)
+}
+
+func countUsers(ctx context.Context, opts *CountUserFilter) int64 {
+ sess := db.GetEngine(ctx).Where(builder.Eq{"type": "0"})
+
+ if opts != nil && opts.LastLoginSince != nil {
+ sess = sess.Where(builder.Gte{"last_login_unix": *opts.LastLoginSince})
+ }
+
+ count, _ := sess.Count(new(User))
+ return count
}
// GetVerifyUser get user by verify code
diff --git a/modules/context/api.go b/modules/context/api.go
index 7d281b998a..20a3e05177 100644
--- a/modules/context/api.go
+++ b/modules/context/api.go
@@ -14,6 +14,7 @@ import (
"code.gitea.io/gitea/models/auth"
repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
@@ -247,6 +248,7 @@ func APIContexter() func(http.Handler) http.Handler {
Resp: NewResponse(w),
Data: map[string]interface{}{},
Locale: locale,
+ Cache: cache.GetCache(),
Repo: &Repository{
PullRequest: &PullRequest{},
},
diff --git a/modules/setting/federation.go b/modules/setting/federation.go
index c300060789..fd39e5c7c2 100644
--- a/modules/setting/federation.go
+++ b/modules/setting/federation.go
@@ -9,9 +9,11 @@ import "code.gitea.io/gitea/modules/log"
// Federation settings
var (
Federation = struct {
- Enabled bool
+ Enabled bool
+ ShareUserStatistics bool
}{
- Enabled: true,
+ Enabled: true,
+ ShareUserStatistics: true,
}
)
diff --git a/routers/api/v1/misc/nodeinfo.go b/routers/api/v1/misc/nodeinfo.go
index bc36fa1be1..ce1f9ec0f7 100644
--- a/routers/api/v1/misc/nodeinfo.go
+++ b/routers/api/v1/misc/nodeinfo.go
@@ -6,12 +6,17 @@ package misc
import (
"net/http"
+ "time"
+ "code.gitea.io/gitea/models"
+ user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
)
+const cacheKeyNodeInfoUsage = "API_NodeInfoUsage"
+
// NodeInfo returns the NodeInfo for the Gitea instance to allow for federation
func NodeInfo(ctx *context.APIContext) {
// swagger:operation GET /nodeinfo miscellaneous getNodeInfo
@@ -23,6 +28,37 @@ func NodeInfo(ctx *context.APIContext) {
// "200":
// "$ref": "#/responses/NodeInfo"
+ nodeInfoUsage := structs.NodeInfoUsage{}
+ if setting.Federation.ShareUserStatistics {
+ info, ok := ctx.Cache.Get(cacheKeyNodeInfoUsage).(structs.NodeInfoUsage)
+ if !ok {
+ usersTotal := int(user_model.CountUsers(nil))
+ now := time.Now()
+ timeOneMonthAgo := now.AddDate(0, -1, 0).Unix()
+ timeHaveYearAgo := now.AddDate(0, -6, 0).Unix()
+ usersActiveMonth := int(user_model.CountUsers(&user_model.CountUserFilter{LastLoginSince: &timeOneMonthAgo}))
+ usersActiveHalfyear := int(user_model.CountUsers(&user_model.CountUserFilter{LastLoginSince: &timeHaveYearAgo}))
+
+ allIssues, _ := models.CountIssues(&models.IssuesOptions{})
+ allComments, _ := models.CountComments(&models.FindCommentsOptions{})
+
+ info = structs.NodeInfoUsage{
+ Users: structs.NodeInfoUsageUsers{
+ Total: usersTotal,
+ ActiveMonth: usersActiveMonth,
+ ActiveHalfyear: usersActiveHalfyear,
+ },
+ LocalPosts: int(allIssues),
+ LocalComments: int(allComments),
+ }
+ if err := ctx.Cache.Put(cacheKeyNodeInfoUsage, nodeInfoUsage, 180); err != nil {
+ ctx.InternalServerError(err)
+ return
+ }
+ }
+ nodeInfoUsage = info
+ }
+
nodeInfo := &structs.NodeInfo{
Version: "2.1",
Software: structs.NodeInfoSoftware{
@@ -34,12 +70,10 @@ func NodeInfo(ctx *context.APIContext) {
Protocols: []string{"activitypub"},
Services: structs.NodeInfoServices{
Inbound: []string{},
- Outbound: []string{},
+ Outbound: []string{"rss2.0"},
},
OpenRegistrations: setting.Service.ShowRegistrationButton,
- Usage: structs.NodeInfoUsage{
- Users: structs.NodeInfoUsageUsers{},
- },
+ Usage: nodeInfoUsage,
}
ctx.JSON(http.StatusOK, nodeInfo)
}
diff --git a/routers/web/auth/auth.go b/routers/web/auth/auth.go
index e8250616ab..be936d2230 100644
--- a/routers/web/auth/auth.go
+++ b/routers/web/auth/auth.go
@@ -600,7 +600,7 @@ func createUserInContext(ctx *context.Context, tpl base.TplName, form interface{
// sends a confirmation email if required.
func handleUserCreated(ctx *context.Context, u *user_model.User, gothUser *goth.User) (ok bool) {
// Auto-set admin for the only user.
- if user_model.CountUsers() == 1 {
+ if user_model.CountUsers(nil) == 1 {
u.IsAdmin = true
u.IsActive = true
u.SetLastLogin()