Merge branch 'origin/main' into session-management

Conflict resolutions:
- models/migrations/v1_26/v328.go: kept origin/main's TokenPermissions
  migration; moved AddUserSessionTable to v1_27/v332.go (1.26.0 closed
  at migration 330; 1.27 starts at 331).
- models/migrations/migrations.go: registered session migration as 332
  using v1_27.AddUserSessionTable.
- tests/integration/api_admin_test.go: adopted new DecodeJSON return
  style; cron count is 30 (added cleanup_user_sessions task).

Co-Authored-By: Claude (Opus 4.7) <noreply@anthropic.com>
This commit is contained in:
silverwind
2026-05-08 08:48:32 +02:00
co-authored by Claude
1841 changed files with 83555 additions and 29241 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings account")}}
{{template "user/settings/layout_head" (dict "pageClass" "user settings account")}}
<div class="user-setting-content">
<h4 class="ui top attached header">
{{ctx.Locale.Tr "settings.password"}}
+1 -1
View File
@@ -1,4 +1,4 @@
{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings actions")}}
{{template "user/settings/layout_head" (dict "pageClass" "user settings actions")}}
<div class="user-setting-content">
{{if eq .PageType "secrets"}}
{{template "shared/secrets/add_list" .}}
@@ -0,0 +1,3 @@
{{template "user/settings/layout_head" (dict)}}
{{template "shared/actions/owner_general_settings" .}}
{{template "user/settings/layout_footer" .}}
+1 -1
View File
@@ -1,4 +1,4 @@
{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings")}}
{{template "user/settings/layout_head" (dict "pageClass" "user settings")}}
<div class="user-setting-content">
<!-- Theme -->
+9 -9
View File
@@ -1,23 +1,23 @@
{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings applications")}}
{{template "user/settings/layout_head" (dict "pageClass" "user settings applications")}}
<div class="user-setting-content">
<h4 class="ui top attached header">
{{ctx.Locale.Tr "settings.manage_access_token"}}
</h4>
<div class="ui attached segment">
<div class="flex-list">
<div class="flex-item">
<div class="flex-divided-list items-with-main">
<div class="item">
{{ctx.Locale.Tr "settings.tokens_desc"}}
</div>
{{range .Tokens}}
<div class="flex-item">
<div class="flex-item-leading">
<div class="item">
<div class="item-leading">
<span class="{{if .HasRecentActivity}}tw-text-green{{end}}" {{if .HasRecentActivity}}data-tooltip-content="{{ctx.Locale.Tr "settings.token_state_desc"}}"{{end}}>
{{svg "fontawesome-send" 32}}
</span>
</div>
<div class="flex-item-main">
<div class="item-main">
<details>
<summary><span class="flex-item-title">{{.Name}}</span></summary>
<summary><span class="item-title">{{.Name}}</span></summary>
<p class="tw-my-1">
{{ctx.Locale.Tr "settings.repo_and_org_access"}}:
{{if .DisplayPublicOnly}}
@@ -35,11 +35,11 @@
{{end}}
</ul>
</details>
<div class="flex-item-body">
<div class="item-body">
<i>{{ctx.Locale.Tr "settings.added_on" (DateUtils.AbsoluteShort .CreatedUnix)}} — {{svg "octicon-info"}} {{if .HasUsed}}{{ctx.Locale.Tr "settings.last_used"}} <span {{if .HasRecentActivity}}class="tw-text-green"{{end}}>{{DateUtils.AbsoluteShort .UpdatedUnix}}</span>{{else}}{{ctx.Locale.Tr "settings.no_activity"}}{{end}}</i>
</div>
</div>
<div class="flex-item-trailing">
<div class="item-trailing">
<button class="ui red tiny button delete-button" data-modal-id="delete-token" data-url="{{$.Link}}/delete" data-id="{{.ID}}">
{{svg "octicon-trash"}}
{{ctx.Locale.Tr "settings.delete_token"}}
@@ -1,4 +1,4 @@
{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings applications")}}
{{template "user/settings/layout_head" (dict "pageClass" "user settings applications")}}
<div class="user-setting-content">
{{template "user/settings/applications_oauth2_edit_form" .}}
@@ -1,22 +1,22 @@
<div class="ui attached segment">
<div class="flex-list">
<div class="flex-item">
<div class="flex-divided-list items-with-main">
<div class="item">
{{ctx.Locale.Tr "settings.oauth2_application_create_description"}}
</div>
{{range .Applications}}
<div class="flex-item tw-items-center">
<div class="flex-item-leading">
<div class="item tw-items-center">
<div class="item-leading">
{{svg "octicon-apps" 32}}
</div>
<div class="flex-item-main">
<div class="flex-item-title">{{.Name}}</div>
<div class="flex-item-body">
<div class="item-main">
<div class="item-title">{{.Name}}</div>
<div class="item-body">
{{ctx.Locale.Tr "settings.oauth2_client_id"}}
<span class="ui label">{{.ClientID}}</span>
</div>
</div>
{{$isBuiltin := and $.BuiltinApplications (index $.BuiltinApplications .ClientID)}}
<div class="flex-item-trailing">
<div class="item-trailing">
{{if $isBuiltin}}
<span class="ui basic label" data-tooltip-content="{{ctx.Locale.Tr "settings.oauth2_application_locked"}}">{{ctx.Locale.Tr "locked"}}</span>
{{else}}
+1 -1
View File
@@ -1,4 +1,4 @@
{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings blocked_users")}}
{{template "user/settings/layout_head" (dict "pageClass" "user settings blocked_users")}}
<div class="user-setting-content">
{{template "shared/user/blocked_users" .}}
</div>
+8 -8
View File
@@ -2,22 +2,22 @@
{{ctx.Locale.Tr "settings.authorized_oauth2_applications"}}
</h4>
<div class="ui attached segment">
<div class="flex-list">
<div class="flex-item">
<div class="flex-divided-list items-with-main">
<div class="item">
{{ctx.Locale.Tr "settings.authorized_oauth2_applications_description"}}
</div>
{{range .Grants}}
<div class="flex-item">
<div class="flex-item-leading">
<div class="item">
<div class="item-leading">
{{svg "octicon-key" 32}}
</div>
<div class="flex-item-main">
<div class="flex-item-title">{{.Application.Name}}</div>
<div class="flex-item-body">
<div class="item-main">
<div class="item-title">{{.Application.Name}}</div>
<div class="item-body">
<i>{{ctx.Locale.Tr "settings.added_on" (DateUtils.AbsoluteShort .CreatedUnix)}}</i>
</div>
</div>
<div class="flex-item-trailing">
<div class="item-trailing">
<button class="ui red tiny button delete-button" data-modal-id="revoke-gitea-oauth2-grant"
data-url="{{AppSubUrl}}/user/settings/applications/oauth2/{{.ApplicationID}}/revoke/{{.ID}}">
{{ctx.Locale.Tr "settings.revoke_key"}}
+2 -2
View File
@@ -1,7 +1,7 @@
{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings new webhook")}}
{{template "user/settings/layout_head" (dict "pageClass" "user settings new webhook")}}
<div class="user-setting-content">
{{$CustomHeaderTitle := ctx.Locale.Tr "repo.settings.update_webhook"}}
{{if .PageIsSettingsHooksNew}}{{$CustomHeaderTitle = ctx.Locale.Tr "repo.settings.add_webhook"}}{{end}}
{{template "webhook/new" (dict "ctxData" . "CustomHeaderTitle" $CustomHeaderTitle)}}
{{template "webhook/new" (dict "CustomHeaderTitle" $CustomHeaderTitle)}}
</div>
{{template "user/settings/layout_footer" .}}
+1 -1
View File
@@ -1,4 +1,4 @@
{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings webhooks")}}
{{template "user/settings/layout_head" (dict "pageClass" "user settings webhooks")}}
<div class="user-setting-content">
{{template "repo/settings/webhook/base_list" .}}
</div>
+1 -1
View File
@@ -1,4 +1,4 @@
{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings sshkeys")}}
{{template "user/settings/layout_head" (dict "pageClass" "user settings sshkeys")}}
<div class="user-setting-content">
{{if not ($.UserDisabledFeatures.Contains "manage_ssh_keys")}}
{{template "user/settings/keys_ssh" .}}
+8 -8
View File
@@ -38,36 +38,36 @@
</button>
</form>
</div>
<div class="flex-list">
<div class="flex-item">
<div class="flex-divided-list items-with-main">
<div class="item">
<p>
{{ctx.Locale.Tr "settings.gpg_desc"}}<br>
{{ctx.Locale.Tr "settings.gpg_helper" "https://docs.github.com/en/free-pro-team@latest/github/authenticating-to-github/about-commit-signature-verification#gpg-commit-signature-verification"}}
</p>
</div>
{{range .GPGKeys}}
<div class="flex-item">
<div class="flex-item-leading">
<div class="item">
<div class="item-leading">
<span class="{{if or .ExpiredUnix.IsZero ($.PageStartTime.Before .ExpiredUnix.AsTime)}}tw-text-green{{end}}">{{svg "octicon-key" 32}}</span>
</div>
<div class="flex-item-main">
<div class="item-main">
{{if .Verified}}
<span class="flex-text-block" data-tooltip-content="{{ctx.Locale.Tr "settings.gpg_key_verified_long"}}">{{svg "octicon-verified"}} <strong>{{ctx.Locale.Tr "settings.gpg_key_verified"}}</strong></span>
{{end}}
{{if .Emails}}
<span class="flex-text-block" data-tooltip-content="{{ctx.Locale.Tr "settings.gpg_key_matched_identities_long"}}">{{svg "octicon-mail"}} {{ctx.Locale.Tr "settings.gpg_key_matched_identities"}} {{range .Emails}}<strong>{{.Email}} </strong>{{end}}</span>
{{end}}
<div class="flex-item-body">
<div class="item-body">
<b>{{ctx.Locale.Tr "settings.key_id"}}:</b> {{.PaddedKeyID}}
<b>{{ctx.Locale.Tr "settings.subkeys"}}:</b> {{range .SubsKey}} {{.PaddedKeyID}} {{end}}
</div>
<div class="flex-item-body">
<div class="item-body">
<i>{{ctx.Locale.Tr "settings.added_on" (DateUtils.AbsoluteShort .AddedUnix)}}</i>
-
<i>{{if not .ExpiredUnix.IsZero}}{{ctx.Locale.Tr "settings.valid_until_date" (DateUtils.AbsoluteShort .ExpiredUnix)}}{{else}}{{ctx.Locale.Tr "settings.valid_forever"}}{{end}}</i>
</div>
</div>
<div class="flex-item-trailing">
<div class="item-trailing">
<button class="ui red tiny button delete-button" data-modal-id="delete-gpg" data-url="{{$.Link}}/delete?type=gpg" data-id="{{.ID}}">
{{ctx.Locale.Tr "settings.delete_key"}}
</button>
+8 -8
View File
@@ -10,22 +10,22 @@
</div>
</h4>
<div class="ui attached segment">
<div class="flex-list">
<div class="flex-item">
<div class="flex-divided-list items-with-main">
<div class="item">
{{ctx.Locale.Tr "settings.principal_desc"}}
</div>
{{range .Principals}}
<div class="flex-item">
<div class="flex-item-leading">
<div class="item">
<div class="item-leading">
<span class="{{if .HasRecentActivity}}tw-text-green{{end}}" {{if .HasRecentActivity}}data-tooltip-content="{{ctx.Locale.Tr "settings.principal_state_desc"}}"{{end}}>{{svg "octicon-key" 32}}</span>
</div>
<div class="flex-item-main">
<div class="flex-item-title">{{.Name}}</div>
<div class="flex-item-body">
<div class="item-main">
<div class="item-title">{{.Name}}</div>
<div class="item-body">
<i>{{ctx.Locale.Tr "settings.added_on" (DateUtils.AbsoluteShort .CreatedUnix)}} — {{svg "octicon-info" 16}} {{if .HasUsed}}{{ctx.Locale.Tr "settings.last_used"}} <span {{if .HasRecentActivity}}class="green"{{end}}>{{DateUtils.AbsoluteShort .UpdatedUnix}}</span>{{else}}{{ctx.Locale.Tr "settings.no_activity"}}{{end}}</i>
</div>
</div>
<div class="flex-item-trailing">
<div class="item-trailing">
<button class="ui red tiny button delete-button" data-modal-id="delete-principal" data-url="{{$.Link}}/delete?type=principal" data-id="{{.ID}}">
{{ctx.Locale.Tr "settings.delete_key"}}
</button>
+11 -11
View File
@@ -26,36 +26,36 @@
</button>
</form>
</div>
<div id="keys-ssh" class="flex-list">
<div class="flex-item">
<div id="keys-ssh" class="flex-divided-list items-with-main">
<div class="item">
<p>
{{ctx.Locale.Tr "settings.ssh_desc"}}<br>
{{ctx.Locale.Tr "settings.ssh_helper" "https://docs.github.com/en/free-pro-team@latest/github/authenticating-to-github/connecting-to-github-with-ssh" "https://docs.github.com/en/free-pro-team@latest/github/authenticating-to-github/troubleshooting-ssh"}}
</p>
</div>
{{if .DisableSSH}}
<div class="flex-item">
<div class="item">
{{ctx.Locale.Tr "settings.ssh_signonly"}}
</div>
{{end}}
{{range $index, $key := .Keys}}
<div class="flex-item">
<div class="flex-item-leading">
<div class="item">
<div class="item-leading">
<span class="{{if .HasRecentActivity}}tw-text-green{{end}}" {{if .HasRecentActivity}}data-tooltip-content="{{ctx.Locale.Tr "settings.key_state_desc"}}"{{end}}>{{svg "octicon-key" 32}}</span>
</div>
<div class="flex-item-main">
<div class="item-main">
{{if .Verified}}
<div class="flex-item-title flex-text-block" data-tooltip-content="{{ctx.Locale.Tr "settings.ssh_key_verified_long"}}">{{svg "octicon-verified"}}{{ctx.Locale.Tr "settings.ssh_key_verified"}}</div>
<div class="item-title flex-text-block" data-tooltip-content="{{ctx.Locale.Tr "settings.ssh_key_verified_long"}}">{{svg "octicon-verified"}}{{ctx.Locale.Tr "settings.ssh_key_verified"}}</div>
{{end}}
<div class="flex-item-title">{{.Name}}</div>
<div class="flex-item-body">
<div class="item-title">{{.Name}}</div>
<div class="item-body">
{{.Fingerprint}}
</div>
<div class="flex-item-body">
<div class="item-body">
<i>{{ctx.Locale.Tr "settings.added_on" (DateUtils.AbsoluteShort .CreatedUnix)}} — {{svg "octicon-info"}} {{if .HasUsed}}{{ctx.Locale.Tr "settings.last_used"}} <span {{if .HasRecentActivity}}class="tw-text-green"{{end}}>{{DateUtils.AbsoluteShort .UpdatedUnix}}</span>{{else}}{{ctx.Locale.Tr "settings.no_activity"}}{{end}}</i>
</div>
</div>
<div class="flex-item-trailing">
<div class="item-trailing">
<button class="ui red tiny button delete-button{{if index $.ExternalKeys $index}} disabled{{end}}" data-modal-id="delete-ssh" data-url="{{$.Link}}/delete?type=ssh" data-id="{{.ID}}"{{if index $.ExternalKeys $index}} title="{{ctx.Locale.Tr "settings.ssh_externally_managed"}}"{{end}}>
{{ctx.Locale.Tr "settings.delete_key"}}
</button>
+4 -4
View File
@@ -1,9 +1,9 @@
{{template "base/head" .ctxData}}
<div role="main" aria-label="{{.ctxData.Title}}" class="page-content {{.pageClass}}">
{{template "base/head" ctx.RootData}}
<div role="main" aria-label="{{ctx.RootData.Title}}" class="page-content {{.pageClass}}">
<div class="ui container flex-container">
{{template "user/settings/navbar" .ctxData}}
{{template "user/settings/navbar" ctx.RootData}}
<div class="flex-container-main">
{{template "base/alert" .ctxData}}
{{template "base/alert" ctx.RootData}}
{{/* block: user-setting-content */}}
{{if false}}{{/* to make html structure "likely" complete to prevent IDE warnings */}}
+4 -1
View File
@@ -34,9 +34,12 @@
</a>
{{end}}
{{if .EnableActions}}
<details class="item toggleable-item" {{if or .PageIsSharedSettingsRunners .PageIsSharedSettingsSecrets .PageIsSharedSettingsVariables}}open{{end}}>
<details class="item toggleable-item" {{if or .PageIsUserSettingsActionsGeneral .PageIsSharedSettingsRunners .PageIsSharedSettingsSecrets .PageIsSharedSettingsVariables}}open{{end}}>
<summary>{{ctx.Locale.Tr "actions.actions"}}</summary>
<div class="menu">
<a class="{{if .PageIsUserSettingsActionsGeneral}}active {{end}}item" href="{{AppSubUrl}}/user/settings/actions/general">
{{ctx.Locale.Tr "actions.general"}}
</a>
<a class="{{if .PageIsSharedSettingsRunners}}active {{end}}item" href="{{AppSubUrl}}/user/settings/actions/runners">
{{ctx.Locale.Tr "actions.runners"}}
</a>
+1 -1
View File
@@ -1,4 +1,4 @@
{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings")}}
{{template "user/settings/layout_head" (dict "pageClass" "user settings")}}
<div class="user-setting-content">
<h4 class="ui top attached header">
{{ctx.Locale.Tr "notifications"}}
+7 -7
View File
@@ -1,4 +1,4 @@
{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings organization")}}
{{template "user/settings/layout_head" (dict "pageClass" "user settings organization")}}
<div class="user-setting-content">
<h4 class="ui top attached header">
{{ctx.Locale.Tr "settings.orgs"}}
@@ -10,19 +10,19 @@
</h4>
<div class="ui attached segment orgs">
{{if .Orgs}}
<div class="flex-list">
<div class="flex-divided-list items-with-main">
{{range .Orgs}}
<div class="flex-item">
<div class="flex-item-leading">
<div class="item">
<div class="item-leading">
{{ctx.AvatarUtils.Avatar . 28 "mini"}}
</div>
<div class="flex-item-main">
<div class="flex-item-title">{{template "shared/user/name" .}}</div>
<div class="item-main">
<div class="item-title">{{template "shared/user/name" .}}</div>
<div class="flex-text-body">
{{.Description}}
</div>
</div>
<div class="flex-item-trailing">
<div class="item-trailing">
<form>
<button class="ui red button delete-button" data-modal-id="leave-organization"
data-url="{{.OrganisationLink}}/members/action/leave" data-datauid="{{$.SignedUser.ID}}"
+1 -1
View File
@@ -1,4 +1,4 @@
{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings packages")}}
{{template "user/settings/layout_head" (dict "pageClass" "user settings packages")}}
<div class="user-setting-content">
{{template "package/shared/cleanup_rules/list" .}}
{{template "package/shared/cargo" .}}
@@ -1,4 +1,4 @@
{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings packages")}}
{{template "user/settings/layout_head" (dict "pageClass" "user settings packages")}}
<div class="user-setting-content">
{{template "package/shared/cleanup_rules/edit" .}}
</div>
@@ -1,4 +1,4 @@
{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user packages admin")}}
{{template "user/settings/layout_head" (dict "pageClass" "user packages admin")}}
<div class="user-setting-content">
{{template "package/shared/cleanup_rules/preview" .}}
</div>
+1 -1
View File
@@ -1,4 +1,4 @@
{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings profile")}}
{{template "user/settings/layout_head" (dict "pageClass" "user settings profile")}}
<div class="user-setting-content">
<h4 class="ui top attached header">
{{ctx.Locale.Tr "settings.public_profile"}}
+1 -1
View File
@@ -1,4 +1,4 @@
{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings repos")}}
{{template "user/settings/layout_head" (dict "pageClass" "user settings repos")}}
<div class="user-setting-content">
<h4 class="ui top attached header">
{{ctx.Locale.Tr "settings.repos"}}
+1 -1
View File
@@ -1,4 +1,4 @@
{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings runners")}}
{{template "user/settings/layout_head" (dict "pageClass" "user settings runners")}}
<div class="user-setting-content">
{{template "shared/actions/runner_edit" .}}
</div>
@@ -9,7 +9,7 @@
<div class="menu">
{{range $key := .OrderedOAuth2Names}}
{{$provider := index $.OAuth2Providers $key}}
<a class="item" href="{{AppSubUrl}}/user/oauth2/{{$key}}">
<a class="item" href="{{AppSubUrl}}/user/oauth2/{{$key | PathEscape}}">
{{$provider.IconHTML 20}}
{{$provider.DisplayName}}
</a>
@@ -21,25 +21,25 @@
</h4>
<div class="ui attached segment">
<div class="flex-list">
<div class="flex-item">
<div class="flex-divided-list items-with-main">
<div class="item">
{{ctx.Locale.Tr "settings.manage_account_links_desc"}}
</div>
{{range $loginSource, $provider := .AccountLinks}}
<div class="flex-item">
<div class="item">
{{$providerData := index $.OAuth2Providers $loginSource.Name}}
<div class="flex-item-leading">
<div class="item-leading">
{{$providerData.IconHTML 20}}
</div>
<div class="flex-item-main">
<span class="flex-item-title" data-tooltip-content="{{$provider}}">
<div class="item-main">
<span class="item-title" data-tooltip-content="{{$provider}}">
{{$loginSource.Name}}
</span>
{{if $loginSource.IsActive}}
<span class="flex-text-body tw-text-primary">{{ctx.Locale.Tr "repo.settings.active"}}</span>
{{end}}
</div>
<div class="flex-item-trailing">
<div class="item-trailing">
<button class="ui red tiny button delete-button" data-modal-id="delete-account-link" data-url="{{AppSubUrl}}/user/settings/security/account_link" data-id="{{$loginSource.ID}}">
{{ctx.Locale.Tr "settings.delete_key"}}
</button>
+7 -7
View File
@@ -2,19 +2,19 @@
{{ctx.Locale.Tr "settings.manage_openid"}}
</h4>
<div class="ui attached segment">
<div class="flex-list">
<div class="flex-item">
<div class="flex-divided-list items-with-main">
<div class="item">
{{ctx.Locale.Tr "settings.openid_desc"}}
</div>
{{range .OpenIDs}}
<div class="flex-item tw-items-center">
<div class="flex-item-leading">
<div class="item tw-items-center">
<div class="item-leading">
{{svg "fontawesome-openid" 20}}
</div>
<div class="flex-item-main">
<div class="flex-item-title">{{.URI}}</div>
<div class="item-main">
<div class="item-title">{{.URI}}</div>
</div>
<div class="flex-item-trailing">
<div class="item-trailing">
<form action="{{AppSubUrl}}/user/settings/security/openid/toggle_visibility" method="post">
<input name="id" type="hidden" value="{{.ID}}">
{{if .Show}}
@@ -1,4 +1,4 @@
{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings security")}}
{{template "user/settings/layout_head" (dict "pageClass" "user settings security")}}
{{if not ($.UserDisabledFeatures.Contains "manage_mfa" "manage_credentials")}}
<div class="user-setting-content">
@@ -1,4 +1,4 @@
{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings twofa")}}
{{template "user/settings/layout_head" (dict "pageClass" "user settings twofa")}}
<div class="user-setting-content">
<h4 class="ui top attached header">
{{ctx.Locale.Tr "settings.twofa_enroll"}}
@@ -3,19 +3,19 @@
<p>{{ctx.Locale.Tr "settings.webauthn_desc" "https://w3c.github.io/webauthn/#webauthn-authenticator"}}</p>
<p>{{ctx.Locale.Tr "settings.webauthn_key_loss_warning"}} {{ctx.Locale.Tr "settings.webauthn_alternative_tip"}}</p>
{{template "user/auth/webauthn_error" .}}
<div class="flex-list">
<div class="flex-divided-list items-with-main">
{{range .WebAuthnCredentials}}
<div class="flex-item">
<div class="flex-item-leading">
<div class="item">
<div class="item-leading">
{{svg "octicon-key" 32}}
</div>
<div class="flex-item-main">
<div class="flex-item-title">{{.Name}}</div>
<div class="flex-item-body">
<div class="item-main">
<div class="item-title">{{.Name}}</div>
<div class="item-body">
<i>{{ctx.Locale.Tr "settings.added_on" (DateUtils.AbsoluteShort .CreatedUnix)}}</i>
</div>
</div>
<div class="flex-item-trailing">
<div class="item-trailing">
<button class="ui red tiny button delete-button" data-modal-id="delete-registration" data-url="{{$.Link}}/webauthn/delete" data-id="{{.ID}}">
{{ctx.Locale.Tr "settings.delete_key"}}
</button>