From 303af554c9bc7b22d5182a6fe9e22192a546cd41 Mon Sep 17 00:00:00 2001 From: Guillaume Date: Thu, 27 Feb 2025 14:40:12 -0500 Subject: [PATCH] Improve "generate new access token" form (#33730) Fix: https://github.com/go-gitea/gitea/issues/33519 As discussed in [PR #33614](https://github.com/go-gitea/gitea/pull/33614), the ScopedAccessTokenSelector Vue component is not particularly useful. This PR removes the component and reverts to using HTML templates. It also introduces some (hopefully) useful refactoring. The Vue component was causing the UX bug reported in the linked issue. Required form fields are now properly working, as expected (see screenshot). ![Screenshot from 2025-02-25 22-00-28](https://github.com/user-attachments/assets/41167854-0718-48b0-a3ee-75ca3a7b8b20) --------- Co-authored-by: wxiaoguang --- models/auth/access_token_scope.go | 14 +++- models/auth/access_token_scope_test.go | 5 +- options/locale/locale_en-US.ini | 1 - routers/web/user/setting/applications.go | 34 ++++++-- services/context/context.go | 11 ++- services/forms/user_form.go | 11 +-- services/forms/user_form_test.go | 27 ------- templates/user/settings/applications.tmpl | 76 ++++++++--------- .../settings/applications_oauth2_list.tmpl | 54 ++++++------- tests/integration/api_admin_org_test.go | 2 +- tests/integration/api_admin_test.go | 4 +- .../api_helper_for_declarative_test.go | 4 + tests/integration/api_repo_git_blobs_test.go | 2 +- tests/integration/api_repo_git_trees_test.go | 2 +- tests/integration/integration_test.go | 50 ++---------- web_src/css/index.css | 1 - web_src/css/modules/checkbox.css | 10 +++ web_src/css/modules/select.css | 25 ------ .../components/ScopedAccessTokenSelector.vue | 81 ------------------- web_src/js/features/scoped-access-token.ts | 20 ----- web_src/js/index.ts | 2 - 21 files changed, 138 insertions(+), 298 deletions(-) delete mode 100644 web_src/css/modules/select.css delete mode 100644 web_src/js/components/ScopedAccessTokenSelector.vue delete mode 100644 web_src/js/features/scoped-access-token.ts diff --git a/models/auth/access_token_scope.go b/models/auth/access_token_scope.go index 897ff3fc9ee..0e5b2e96e66 100644 --- a/models/auth/access_token_scope.go +++ b/models/auth/access_token_scope.go @@ -5,6 +5,7 @@ package auth import ( "fmt" + "slices" "strings" "code.gitea.io/gitea/models/perm" @@ -14,7 +15,7 @@ import ( type AccessTokenScopeCategory int const ( - AccessTokenScopeCategoryActivityPub = iota + AccessTokenScopeCategoryActivityPub AccessTokenScopeCategory = iota AccessTokenScopeCategoryAdmin AccessTokenScopeCategoryMisc // WARN: this is now just a placeholder, don't remove it which will change the following values AccessTokenScopeCategoryNotification @@ -193,6 +194,14 @@ var accessTokenScopes = map[AccessTokenScopeLevel]map[AccessTokenScopeCategory]A }, } +func GetAccessTokenCategories() (res []string) { + for _, cat := range accessTokenScopes[Read] { + res = append(res, strings.TrimPrefix(string(cat), "read:")) + } + slices.Sort(res) + return res +} + // GetRequiredScopes gets the specific scopes for a given level and categories func GetRequiredScopes(level AccessTokenScopeLevel, scopeCategories ...AccessTokenScopeCategory) []AccessTokenScope { scopes := make([]AccessTokenScope, 0, len(scopeCategories)) @@ -270,6 +279,9 @@ func (s AccessTokenScope) parse() (accessTokenScopeBitmap, error) { // StringSlice returns the AccessTokenScope as a []string func (s AccessTokenScope) StringSlice() []string { + if s == "" { + return nil + } return strings.Split(string(s), ",") } diff --git a/models/auth/access_token_scope_test.go b/models/auth/access_token_scope_test.go index a6097e45d7f..9e4aa836337 100644 --- a/models/auth/access_token_scope_test.go +++ b/models/auth/access_token_scope_test.go @@ -17,6 +17,7 @@ type scopeTestNormalize struct { } func TestAccessTokenScope_Normalize(t *testing.T) { + assert.Equal(t, []string{"activitypub", "admin", "issue", "misc", "notification", "organization", "package", "repository", "user"}, GetAccessTokenCategories()) tests := []scopeTestNormalize{ {"", "", nil}, {"write:misc,write:notification,read:package,write:notification,public-only", "public-only,write:misc,write:notification,read:package", nil}, @@ -25,7 +26,7 @@ func TestAccessTokenScope_Normalize(t *testing.T) { {"write:activitypub,write:admin,write:misc,write:notification,write:organization,write:package,write:issue,write:repository,write:user,public-only", "public-only,all", nil}, } - for _, scope := range []string{"activitypub", "admin", "misc", "notification", "organization", "package", "issue", "repository", "user"} { + for _, scope := range GetAccessTokenCategories() { tests = append(tests, scopeTestNormalize{AccessTokenScope(fmt.Sprintf("read:%s", scope)), AccessTokenScope(fmt.Sprintf("read:%s", scope)), nil}, scopeTestNormalize{AccessTokenScope(fmt.Sprintf("write:%s", scope)), AccessTokenScope(fmt.Sprintf("write:%s", scope)), nil}, @@ -59,7 +60,7 @@ func TestAccessTokenScope_HasScope(t *testing.T) { {"public-only", "read:issue", false, nil}, } - for _, scope := range []string{"activitypub", "admin", "misc", "notification", "organization", "package", "issue", "repository", "user"} { + for _, scope := range GetAccessTokenCategories() { tests = append(tests, scopeTestHasScope{ AccessTokenScope(fmt.Sprintf("read:%s", scope)), diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 4c25ba32055..5aa6d19daee 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -917,7 +917,6 @@ delete_token_success = The token has been deleted. Applications using it no long repo_and_org_access = Repository and Organization Access permissions_public_only = Public only permissions_access_all = All (public, private, and limited) -select_permissions = Select permissions permission_not_set = Not set permission_no_access = No Access permission_read = Read diff --git a/routers/web/user/setting/applications.go b/routers/web/user/setting/applications.go index cf71d01dc16..1f6c97a5cc6 100644 --- a/routers/web/user/setting/applications.go +++ b/routers/web/user/setting/applications.go @@ -6,12 +6,14 @@ package setting import ( "net/http" + "strings" auth_model "code.gitea.io/gitea/models/auth" "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/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" @@ -39,18 +41,29 @@ func ApplicationsPost(ctx *context.Context) { ctx.Data["PageIsSettingsApplications"] = true ctx.Data["UserDisabledFeatures"] = user_model.DisabledFeaturesWithLoginType(ctx.Doer) - if ctx.HasError() { - loadApplicationsData(ctx) - - ctx.HTML(http.StatusOK, tplSettingsApplications) - return + _ = ctx.Req.ParseForm() + var scopeNames []string + for k, v := range ctx.Req.Form { + if strings.HasPrefix(k, "scope-") { + scopeNames = append(scopeNames, v...) + } } - scope, err := form.GetScope() + scope, err := auth_model.AccessTokenScope(strings.Join(scopeNames, ",")).Normalize() if err != nil { ctx.ServerError("GetScope", err) return } + if scope == "" || scope == auth_model.AccessTokenScopePublicOnly { + ctx.Flash.Error(ctx.Tr("settings.at_least_one_permission"), true) + } + + if ctx.HasError() { + loadApplicationsData(ctx) + ctx.HTML(http.StatusOK, tplSettingsApplications) + return + } + t := &auth_model.AccessToken{ UID: ctx.Doer.ID, Name: form.Name, @@ -99,7 +112,14 @@ func loadApplicationsData(ctx *context.Context) { } ctx.Data["Tokens"] = tokens ctx.Data["EnableOAuth2"] = setting.OAuth2.Enabled - ctx.Data["IsAdmin"] = ctx.Doer.IsAdmin + + // Handle specific ordered token categories for admin or non-admin users + tokenCategoryNames := auth_model.GetAccessTokenCategories() + if !ctx.Doer.IsAdmin { + tokenCategoryNames = util.SliceRemoveAll(tokenCategoryNames, "admin") + } + ctx.Data["TokenCategories"] = tokenCategoryNames + if setting.OAuth2.Enabled { ctx.Data["Applications"], err = db.Find[auth_model.OAuth2Application](ctx, auth_model.FindOAuth2ApplicationsOptions{ OwnerID: ctx.Doer.ID, diff --git a/services/context/context.go b/services/context/context.go index 5e08fba4426..f3a0f0bb5f5 100644 --- a/services/context/context.go +++ b/services/context/context.go @@ -213,13 +213,16 @@ func Contexter() func(next http.Handler) http.Handler { // Attention: this function changes ctx.Data and ctx.Flash // If HasError is called, then before Redirect, the error message should be stored by ctx.Flash.Error(ctx.GetErrMsg()) again. func (ctx *Context) HasError() bool { - hasErr, ok := ctx.Data["HasError"] - if !ok { + hasErr, _ := ctx.Data["HasError"].(bool) + hasErr = hasErr || ctx.Flash.ErrorMsg != "" + if !hasErr { return false } - ctx.Flash.ErrorMsg = ctx.GetErrMsg() + if ctx.Flash.ErrorMsg == "" { + ctx.Flash.ErrorMsg = ctx.GetErrMsg() + } ctx.Data["Flash"] = ctx.Flash - return hasErr.(bool) + return hasErr } // GetErrMsg returns error message in form validation. diff --git a/services/forms/user_form.go b/services/forms/user_form.go index ed79936add6..c9ce71e8861 100644 --- a/services/forms/user_form.go +++ b/services/forms/user_form.go @@ -7,9 +7,7 @@ package forms import ( "mime/multipart" "net/http" - "strings" - auth_model "code.gitea.io/gitea/models/auth" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web/middleware" @@ -347,8 +345,7 @@ func (f *EditVariableForm) Validate(req *http.Request, errs binding.Errors) bind // NewAccessTokenForm form for creating access token type NewAccessTokenForm struct { - Name string `binding:"Required;MaxSize(255)" locale:"settings.token_name"` - Scope []string + Name string `binding:"Required;MaxSize(255)" locale:"settings.token_name"` } // Validate validates the fields @@ -357,12 +354,6 @@ func (f *NewAccessTokenForm) Validate(req *http.Request, errs binding.Errors) bi return middleware.Validate(errs, ctx.Data, f, ctx.Locale) } -func (f *NewAccessTokenForm) GetScope() (auth_model.AccessTokenScope, error) { - scope := strings.Join(f.Scope, ",") - s, err := auth_model.AccessTokenScope(scope).Normalize() - return s, err -} - // EditOAuth2ApplicationForm form for editing oauth2 applications type EditOAuth2ApplicationForm struct { Name string `binding:"Required;MaxSize(255)" form:"application_name"` diff --git a/services/forms/user_form_test.go b/services/forms/user_form_test.go index 66050187c9f..b4120f20ed8 100644 --- a/services/forms/user_form_test.go +++ b/services/forms/user_form_test.go @@ -4,10 +4,8 @@ package forms import ( - "strconv" "testing" - auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/modules/setting" "github.com/gobwas/glob" @@ -104,28 +102,3 @@ func TestRegisterForm_IsDomainAllowed_BlockedEmail(t *testing.T) { assert.Equal(t, v.valid, form.IsEmailDomainAllowed()) } } - -func TestNewAccessTokenForm_GetScope(t *testing.T) { - tests := []struct { - form NewAccessTokenForm - scope auth_model.AccessTokenScope - expectedErr error - }{ - { - form: NewAccessTokenForm{Name: "test", Scope: []string{"read:repository"}}, - scope: "read:repository", - }, - { - form: NewAccessTokenForm{Name: "test", Scope: []string{"read:repository", "write:user"}}, - scope: "read:repository,write:user", - }, - } - - for i, test := range tests { - t.Run(strconv.Itoa(i), func(t *testing.T) { - scope, err := test.form.GetScope() - assert.Equal(t, test.expectedErr, err) - assert.Equal(t, test.scope, scope) - }) - } -} diff --git a/templates/user/settings/applications.tmpl b/templates/user/settings/applications.tmpl index 31d1a2ac5b2..501f238c7a3 100644 --- a/templates/user/settings/applications.tmpl +++ b/templates/user/settings/applications.tmpl @@ -50,49 +50,41 @@
-
- {{ctx.Locale.Tr "settings.generate_new_token"}} -
-
- {{.CsrfTokenHtml}} -
- - -
-
- - - -
-
- - {{ctx.Locale.Tr "settings.select_permissions"}} - -

- {{ctx.Locale.Tr "settings.access_token_desc" (HTMLFormat `href="%s/api/swagger" target="_blank"` AppSubUrl) (`href="https://docs.gitea.com/development/oauth2-provider#scopes" target="_blank"`|SafeHTML)}} -

-
+
+

{{ctx.Locale.Tr "settings.generate_new_token"}}

+ + {{.CsrfTokenHtml}} +
+ +
-
- - {{/* Fomantic ".ui.form .warning.message" is hidden by default, so put the warning message out of the form*/}} -
- {{ctx.Locale.Tr "settings.at_least_one_permission"}} -
+
+
{{ctx.Locale.Tr "settings.repo_and_org_access"}}
+ + +
+
+
{{ctx.Locale.Tr "settings.access_token_desc" (HTMLFormat `href="%s/api/swagger" target="_blank"` AppSubUrl) (`href="https://docs.gitea.com/development/oauth2-provider#scopes" target="_blank"`|SafeHTML)}}
+ + {{range $category := .TokenCategories}} + + + + + + + {{end}} +
{{$category}}
+
+ + +
{{if .EnableOAuth2}} diff --git a/templates/user/settings/applications_oauth2_list.tmpl b/templates/user/settings/applications_oauth2_list.tmpl index 61098e118b4..418d8e9cfc1 100644 --- a/templates/user/settings/applications_oauth2_list.tmpl +++ b/templates/user/settings/applications_oauth2_list.tmpl @@ -48,33 +48,33 @@
-
- {{ctx.Locale.Tr "settings.create_oauth2_application"}} -
-
- {{.CsrfTokenHtml}} -
- - -
-
- - -
-
-
- - +
+

{{ctx.Locale.Tr "settings.create_oauth2_application"}}

+ + {{.CsrfTokenHtml}} +
+ +
-
-
-
- - +
+ +
-
- - +
+
+ + +
+
+
+
+ + +
+
+ + +
diff --git a/tests/integration/api_admin_org_test.go b/tests/integration/api_admin_org_test.go index a29d0ba1d74..b243856127d 100644 --- a/tests/integration/api_admin_org_test.go +++ b/tests/integration/api_admin_org_test.go @@ -76,7 +76,7 @@ func TestAPIAdminOrgCreateNotAdmin(t *testing.T) { defer tests.PrepareTestEnv(t)() nonAdminUsername := "user2" session := loginUser(t, nonAdminUsername) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAll) org := api.CreateOrgOption{ UserName: "user2_org", FullName: "User2's organization", diff --git a/tests/integration/api_admin_test.go b/tests/integration/api_admin_test.go index 66209ee4e0c..b42f05fc550 100644 --- a/tests/integration/api_admin_test.go +++ b/tests/integration/api_admin_test.go @@ -76,7 +76,7 @@ func TestAPIAdminDeleteUnauthorizedKey(t *testing.T) { var newPublicKey api.PublicKey DecodeJSON(t, resp, &newPublicKey) - token = getUserToken(t, normalUsername) + token = getUserToken(t, normalUsername, auth_model.AccessTokenScopeAll) req = NewRequestf(t, "DELETE", "/api/v1/admin/users/%s/keys/%d", adminUsername, newPublicKey.ID). AddTokenAuth(token) MakeRequest(t, req, http.StatusForbidden) @@ -139,7 +139,7 @@ func TestAPIListUsersNotLoggedIn(t *testing.T) { func TestAPIListUsersNonAdmin(t *testing.T) { defer tests.PrepareTestEnv(t)() nonAdminUsername := "user2" - token := getUserToken(t, nonAdminUsername) + token := getUserToken(t, nonAdminUsername, auth_model.AccessTokenScopeAll) req := NewRequest(t, "GET", "/api/v1/admin/users"). AddTokenAuth(token) MakeRequest(t, req, http.StatusForbidden) diff --git a/tests/integration/api_helper_for_declarative_test.go b/tests/integration/api_helper_for_declarative_test.go index 96669b46f02..f3a595540f2 100644 --- a/tests/integration/api_helper_for_declarative_test.go +++ b/tests/integration/api_helper_for_declarative_test.go @@ -33,6 +33,10 @@ type APITestContext struct { func NewAPITestContext(t *testing.T, username, reponame string, scope ...auth.AccessTokenScope) APITestContext { session := loginUser(t, username) + if len(scope) == 0 { + // FIXME: legacy logic: no scope means all + scope = []auth.AccessTokenScope{auth.AccessTokenScopeAll} + } token := getTokenForLoggedInUser(t, session, scope...) return APITestContext{ Session: session, diff --git a/tests/integration/api_repo_git_blobs_test.go b/tests/integration/api_repo_git_blobs_test.go index 184362e7e32..9c4be313967 100644 --- a/tests/integration/api_repo_git_blobs_test.go +++ b/tests/integration/api_repo_git_blobs_test.go @@ -72,7 +72,7 @@ func TestAPIReposGitBlobs(t *testing.T) { // Login as User4. session = loginUser(t, user4.Name) - token4 := getTokenForLoggedInUser(t, session) + token4 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAll) // Test using org repo "org3/repo3" where user4 is a NOT collaborator req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/git/blobs/d56a3073c1dbb7b15963110a049d50cdb5db99fc?access=%s", org3.Name, repo3.Name, token4) diff --git a/tests/integration/api_repo_git_trees_test.go b/tests/integration/api_repo_git_trees_test.go index 8eec6d8d220..47063d90912 100644 --- a/tests/integration/api_repo_git_trees_test.go +++ b/tests/integration/api_repo_git_trees_test.go @@ -69,7 +69,7 @@ func TestAPIReposGitTrees(t *testing.T) { // Login as User4. session = loginUser(t, user4.Name) - token4 := getTokenForLoggedInUser(t, session) + token4 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAll) // Test using org repo "org3/repo3" where user4 is a NOT collaborator req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/git/trees/d56a3073c1dbb7b15963110a049d50cdb5db99fc?access=%s", org3.Name, repo3.Name, token4) diff --git a/tests/integration/integration_test.go b/tests/integration/integration_test.go index 9e487924d1f..2f6b7eae316 100644 --- a/tests/integration/integration_test.go +++ b/tests/integration/integration_test.go @@ -249,55 +249,19 @@ func loginUserWithPassword(t testing.TB, userName, password string) *TestSession // token has to be unique this counter take care of var tokenCounter int64 -// getTokenForLoggedInUser returns a token for a logged in user. -// The scope is an optional list of snake_case strings like the frontend form fields, -// but without the "scope_" prefix. +// getTokenForLoggedInUser returns a token for a logged-in user. func getTokenForLoggedInUser(t testing.TB, session *TestSession, scopes ...auth.AccessTokenScope) string { t.Helper() - var token string - req := NewRequest(t, "GET", "/user/settings/applications") - resp := session.MakeRequest(t, req, http.StatusOK) - var csrf string - for _, cookie := range resp.Result().Cookies() { - if cookie.Name != "_csrf" { - continue - } - csrf = cookie.Value - break - } - if csrf == "" { - doc := NewHTMLParser(t, resp.Body) - csrf = doc.GetCSRF() - } - assert.NotEmpty(t, csrf) urlValues := url.Values{} - urlValues.Add("_csrf", csrf) + urlValues.Add("_csrf", GetUserCSRFToken(t, session)) urlValues.Add("name", fmt.Sprintf("api-testing-token-%d", atomic.AddInt64(&tokenCounter, 1))) for _, scope := range scopes { - urlValues.Add("scope", string(scope)) + urlValues.Add("scope-dummy", string(scope)) // it only needs to start with "scope-" to be accepted } - req = NewRequestWithURLValues(t, "POST", "/user/settings/applications", urlValues) - resp = session.MakeRequest(t, req, http.StatusSeeOther) - - // Log the flash values on failure - if !assert.Equal(t, []string{"/user/settings/applications"}, resp.Result().Header["Location"]) { - for _, cookie := range resp.Result().Cookies() { - if cookie.Name != gitea_context.CookieNameFlash { - continue - } - flash, _ := url.ParseQuery(cookie.Value) - for key, value := range flash { - t.Logf("Flash %q: %q", key, value) - } - } - } - - req = NewRequest(t, "GET", "/user/settings/applications") - resp = session.MakeRequest(t, req, http.StatusOK) - htmlDoc := NewHTMLParser(t, resp.Body) - token = htmlDoc.doc.Find(".ui.info p").Text() - assert.NotEmpty(t, token) - return token + req := NewRequestWithURLValues(t, "POST", "/user/settings/applications", urlValues) + session.MakeRequest(t, req, http.StatusSeeOther) + flashes := session.GetCookieFlashMessage() + return flashes.InfoMsg } type RequestWrapper struct { diff --git a/web_src/css/index.css b/web_src/css/index.css index ce1a23b245c..630aa3c2ef0 100644 --- a/web_src/css/index.css +++ b/web_src/css/index.css @@ -19,7 +19,6 @@ @import "./modules/dimmer.css"; @import "./modules/modal.css"; -@import "./modules/select.css"; @import "./modules/tippy.css"; @import "./modules/breadcrumb.css"; @import "./modules/comment.css"; diff --git a/web_src/css/modules/checkbox.css b/web_src/css/modules/checkbox.css index 0a3a71acaa6..f7e61ba360f 100644 --- a/web_src/css/modules/checkbox.css +++ b/web_src/css/modules/checkbox.css @@ -119,3 +119,13 @@ input[type="radio"] { .ui.toggle.checkbox input:focus:checked ~ label::before { background: var(--color-primary) !important; } + +label.gt-checkbox { + display: inline-flex; + align-items: center; + gap: 0.25em; +} + +.ui.form .field > label.gt-checkbox { + display: flex; +} diff --git a/web_src/css/modules/select.css b/web_src/css/modules/select.css deleted file mode 100644 index 1d7d749d4ad..00000000000 --- a/web_src/css/modules/select.css +++ /dev/null @@ -1,25 +0,0 @@ -.gitea-select { - position: relative; -} - -.gitea-select select { - appearance: none; /* hide default triangle */ -} - -/* ::before and ::after pseudo elements don't work on select elements, - so we need to put it on the parent. */ -.gitea-select::after { - position: absolute; - top: 12px; - right: 8px; - pointer-events: none; - content: ""; - width: 14px; - height: 14px; - mask-size: cover; - -webkit-mask-size: cover; - mask-image: var(--octicon-chevron-right); - -webkit-mask-image: var(--octicon-chevron-right); - transform: rotate(90deg); /* point the chevron down */ - background: currentcolor; -} diff --git a/web_src/js/components/ScopedAccessTokenSelector.vue b/web_src/js/components/ScopedAccessTokenSelector.vue deleted file mode 100644 index 9eaf8240358..00000000000 --- a/web_src/js/components/ScopedAccessTokenSelector.vue +++ /dev/null @@ -1,81 +0,0 @@ - - - diff --git a/web_src/js/features/scoped-access-token.ts b/web_src/js/features/scoped-access-token.ts deleted file mode 100644 index c498d4c0111..00000000000 --- a/web_src/js/features/scoped-access-token.ts +++ /dev/null @@ -1,20 +0,0 @@ -import {createApp} from 'vue'; - -export async function initScopedAccessTokenCategories() { - const el = document.querySelector('#scoped-access-token-selector'); - if (!el) return; - - const {default: ScopedAccessTokenSelector} = await import(/* webpackChunkName: "scoped-access-token-selector" */'../components/ScopedAccessTokenSelector.vue'); - try { - const View = createApp(ScopedAccessTokenSelector, { - isAdmin: JSON.parse(el.getAttribute('data-is-admin')), - noAccessLabel: el.getAttribute('data-no-access-label'), - readLabel: el.getAttribute('data-read-label'), - writeLabel: el.getAttribute('data-write-label'), - }); - View.mount(el); - } catch (err) { - console.error('ScopedAccessTokenSelector failed to load', err); - el.textContent = el.getAttribute('data-locale-component-failed-to-load'); - } -} diff --git a/web_src/js/index.ts b/web_src/js/index.ts index b89e5960470..032e6ffe1bd 100644 --- a/web_src/js/index.ts +++ b/web_src/js/index.ts @@ -68,7 +68,6 @@ import {initColorPickers} from './features/colorpicker.ts'; import {initAdminSelfCheck} from './features/admin/selfcheck.ts'; import {initOAuth2SettingsDisableCheckbox} from './features/oauth2-settings.ts'; import {initGlobalFetchAction} from './features/common-fetch-action.ts'; -import {initScopedAccessTokenCategories} from './features/scoped-access-token.ts'; import { initFootLanguageMenu, initGlobalDropdown, @@ -209,7 +208,6 @@ onDomReady(() => { initUserSettings, initRepoDiffView, initPdfViewer, - initScopedAccessTokenCategories, initColorPickers, initOAuth2SettingsDisableCheckbox,