-
-
Notifications
You must be signed in to change notification settings - Fork 5.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Display User signin metadata in admin dashboard #33955
Open
techknowlogick
wants to merge
11
commits into
go-gitea:main
Choose a base branch
from
techknowlogick:admin-ip-info
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+267
−2
Open
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
5d716fb
Display Usersign metadata in admin dashboard
techknowlogick 996fae0
Update routers/web/admin/ips.go
techknowlogick 7888a5a
reduce code duplication due to queries
techknowlogick ffb849d
Merge branch 'admin-ip-info' of https://github.com/techknowlogick/git…
techknowlogick abebe11
make fmt
techknowlogick ec44bdb
whoops. left in an extra assign
techknowlogick 2fcb0cb
add final newline
techknowlogick b1b8d9b
Update locale_en-US.ini
techknowlogick f26cb7c
Update navbar.tmpl
techknowlogick bf9fc6c
Update ips.go
techknowlogick 2a49d34
Merge branch 'main' into admin-ip-info
techknowlogick File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
// Copyright 2025 The Gitea Authors. All rights reserved. | ||
// SPDX-License-Identifier: MIT | ||
|
||
package admin | ||
|
||
import ( | ||
"net/http" | ||
"strings" | ||
|
||
"code.gitea.io/gitea/models/db" | ||
user_model "code.gitea.io/gitea/models/user" | ||
"code.gitea.io/gitea/modules/setting" | ||
"code.gitea.io/gitea/modules/templates" | ||
"code.gitea.io/gitea/services/context" | ||
|
||
"xorm.io/xorm" | ||
) | ||
|
||
const ( | ||
tplIPs templates.TplName = "admin/ips/list" | ||
) | ||
|
||
// trimPortFromIP removes the client port from an IP address | ||
// Handles both IPv4 and IPv6 addresses with ports | ||
func trimPortFromIP(ip string) string { | ||
// Handle IPv6 with brackets: [IPv6]:port | ||
if strings.HasPrefix(ip, "[") { | ||
// If there's no port, return as is | ||
if !strings.Contains(ip, "]:") { | ||
return ip | ||
} | ||
// Remove the port part after ]: | ||
return strings.Split(ip, "]:")[0] + "]" | ||
} | ||
|
||
// Count colons to differentiate between IPv4 and IPv6 | ||
colonCount := strings.Count(ip, ":") | ||
|
||
// Handle IPv4 with port (single colon) | ||
if colonCount == 1 { | ||
return strings.Split(ip, ":")[0] | ||
} | ||
|
||
return ip | ||
} | ||
|
||
func buildIPQuery(ctx *context.Context, keyword string) *xorm.Session { | ||
query := db.GetEngine(ctx). | ||
Table("user_setting"). | ||
Join("INNER", "user", "user.id = user_setting.user_id"). | ||
Where("user_setting.setting_key = ?", user_model.SignupIP) | ||
|
||
if len(keyword) > 0 { | ||
query = query.And("(user.lower_name LIKE ? OR user.full_name LIKE ? OR user_setting.setting_value LIKE ?)", | ||
"%"+strings.ToLower(keyword)+"%", "%"+keyword+"%", "%"+keyword+"%") | ||
} | ||
return query | ||
} | ||
|
||
// IPs show all user signup IPs | ||
func IPs(ctx *context.Context) { | ||
ctx.Data["Title"] = ctx.Tr("admin.ips.ip") | ||
ctx.Data["PageIsAdminIPs"] = true | ||
ctx.Data["RecordUserSignupMetadata"] = setting.RecordUserSignupMetadata | ||
|
||
// If record user signup metadata is disabled, don't show the page | ||
if !setting.RecordUserSignupMetadata { | ||
ctx.Redirect(setting.AppSubURL + "/-/admin") | ||
return | ||
} | ||
|
||
page := ctx.FormInt("page") | ||
if page <= 1 { | ||
page = 1 | ||
} | ||
|
||
// Define the user IP result struct | ||
type UserIPResult struct { | ||
UID int64 | ||
Name string | ||
FullName string | ||
IP string | ||
} | ||
|
||
var ( | ||
userIPs []UserIPResult | ||
count int64 | ||
err error | ||
orderBy string | ||
keyword = ctx.FormTrim("q") | ||
sortType = ctx.FormString("sort") | ||
) | ||
|
||
ctx.Data["SortType"] = sortType | ||
switch sortType { | ||
case "ip": | ||
orderBy = "user_setting.setting_value ASC, user.id ASC" | ||
case "reverseip": | ||
orderBy = "user_setting.setting_value DESC, user.id DESC" | ||
case "username": | ||
orderBy = "user.lower_name ASC, user.id ASC" | ||
case "reverseusername": | ||
orderBy = "user.lower_name DESC, user.id DESC" | ||
default: | ||
ctx.Data["SortType"] = "ip" | ||
orderBy = "user_setting.setting_value ASC, user.id ASC" | ||
} | ||
|
||
// Get the count and user IPs for pagination | ||
query := buildIPQuery(ctx, keyword) | ||
|
||
count, err = query.Count(new(user_model.Setting)) | ||
if err != nil { | ||
ctx.ServerError("Count", err) | ||
return | ||
} | ||
|
||
err = buildIPQuery(ctx, keyword). | ||
Select("user.id as uid, user.name, user.full_name, user_setting.setting_value as ip"). | ||
OrderBy(orderBy). | ||
Limit(setting.UI.Admin.UserPagingNum, (page-1)*setting.UI.Admin.UserPagingNum). | ||
Find(&userIPs) | ||
if err != nil { | ||
ctx.ServerError("Find", err) | ||
return | ||
} | ||
|
||
for i := range userIPs { | ||
// Trim the port from the IP | ||
// FIXME: Maybe have a different helper for this? | ||
userIPs[i].IP = trimPortFromIP(userIPs[i].IP) | ||
} | ||
|
||
ctx.Data["UserIPs"] = userIPs | ||
ctx.Data["Total"] = count | ||
ctx.Data["Keyword"] = keyword | ||
|
||
// Setup pagination | ||
ctx.Data["Page"] = context.NewPagination(int(count), setting.UI.Admin.UserPagingNum, page, 5) | ||
|
||
ctx.HTML(http.StatusOK, tplIPs) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
{{template "admin/layout_head" (dict "ctxData" . "pageClass" "admin user")}} | ||
<div class="admin-setting-content"> | ||
<h4 class="ui top attached header"> | ||
{{ctx.Locale.Tr "admin.ips.ip_manage_panel"}} ({{ctx.Locale.Tr "admin.total" .Total}}) | ||
</h4> | ||
<div class="ui attached segment"> | ||
<div class="ui secondary filter menu tw-items-center tw-mx-0"> | ||
<form class="ui form ignore-dirty tw-flex-1"> | ||
{{template "shared/search/combo" dict "Value" .Keyword}} | ||
</form> | ||
<!-- Sort --> | ||
<div class="ui dropdown type jump item tw-mr-0"> | ||
<span class="text"> | ||
{{ctx.Locale.Tr "repo.issues.filter_sort"}} | ||
</span> | ||
{{svg "octicon-triangle-down" 14 "dropdown icon"}} | ||
<div class="menu"> | ||
<a class="{{if or (eq .SortType "ip") (not .SortType)}}active {{end}}item" href="?sort=ip&q={{$.Keyword}}">{{ctx.Locale.Tr "admin.ips.filter_sort.ip"}}</a> | ||
<a class="{{if eq .SortType "reverseip"}}active {{end}}item" href="?sort=reverseip&q={{$.Keyword}}">{{ctx.Locale.Tr "admin.ips.filter_sort.ip_reverse"}}</a> | ||
<a class="{{if eq .SortType "username"}}active {{end}}item" href="?sort=username&q={{$.Keyword}}">{{ctx.Locale.Tr "admin.ips.filter_sort.name"}}</a> | ||
<a class="{{if eq .SortType "reverseusername"}}active {{end}}item" href="?sort=reverseusername&q={{$.Keyword}}">{{ctx.Locale.Tr "admin.ips.filter_sort.name_reverse"}}</a> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
<div class="ui attached table segment"> | ||
<table class="ui very basic striped table unstackable"> | ||
<thead> | ||
<tr> | ||
<th data-sortt-asc="username" data-sortt-desc="reverseusername"> | ||
{{ctx.Locale.Tr "admin.users.name"}} | ||
{{SortArrow "username" "reverseusername" $.SortType false}} | ||
</th> | ||
<th>{{ctx.Locale.Tr "admin.users.full_name"}}</th> | ||
<th data-sortt-asc="ip" data-sortt-desc="reverseip" data-sortt-default="true"> | ||
{{ctx.Locale.Tr "admin.ips.ip"}} | ||
{{SortArrow "ip" "reverseip" $.SortType true}} | ||
</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
{{range .UserIPs}} | ||
<tr> | ||
<td><a href="{{AppSubUrl}}/-/admin/users/{{.UID}}">{{.Name}}</a></td> | ||
<td>{{.FullName}}</td> | ||
<td><a href="?q={{.IP}}&sort={{$.SortType}}">{{.IP}}</a></td> | ||
</tr> | ||
{{else}} | ||
<tr><td class="tw-text-center" colspan="3">{{ctx.Locale.Tr "no_results_found"}}</td></tr> | ||
{{end}} | ||
</tbody> | ||
</table> | ||
</div> | ||
|
||
{{template "base/paginate" .}} | ||
</div> | ||
|
||
{{template "admin/layout_footer" .}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
{{if .HasSignupIP}} | ||
<div class="flex-list"> | ||
<div class="flex-item"> | ||
<div class="flex-item-main"> | ||
<div class="flex-text-block"> | ||
<strong>{{ctx.Locale.Tr "admin.ips.ip"}}:</strong> <a href="{{AppSubUrl}}/-/admin/ips?q={{.SignupIP}}">{{.SignupIP}}</a> | ||
</div> | ||
{{if .HasSignupUserAgent}} | ||
<div class="flex-text-block"> | ||
<strong>{{ctx.Locale.Tr "admin.ips.user_agent"}}:</strong> {{.SignupUserAgent}} | ||
</div> | ||
{{end}} | ||
</div> | ||
</div> | ||
</div> | ||
{{else}} | ||
<div>{{ctx.Locale.Tr "admin.ips.not_available"}}</div> | ||
{{end}} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's better to move them into models package.