mirror of
https://github.com/go-gitea/gitea.git
synced 2026-05-13 08:55:40 +02:00
Merge branch 'main' into feature/make-sqlite-watch
This commit is contained in:
commit
4017b2a3e6
@ -3722,6 +3722,8 @@
|
||||
"actions.runs.workflow_run_count_1": "%d workflow run",
|
||||
"actions.runs.workflow_run_count_n": "%d workflow runs",
|
||||
"actions.runs.commit": "Commit",
|
||||
"actions.runs.run_details": "Run Details",
|
||||
"actions.runs.workflow_file": "Workflow file",
|
||||
"actions.runs.scheduled": "Scheduled",
|
||||
"actions.runs.pushed_by": "pushed by",
|
||||
"actions.runs.invalid_workflow_helper": "Workflow config file is invalid. Please check your config file: %s",
|
||||
|
||||
@ -218,18 +218,50 @@ func performAutoLogin(ctx *context.Context) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func prepareSignInPageData(ctx *context.Context) {
|
||||
func performAutoLoginOAuth2(ctx *context.Context, data *preparedSignInData) bool {
|
||||
// If only 1 OAuth provider is present and other login methods are disabled, redirect to the OAuth provider.
|
||||
onlySingleOAuth2 := len(data.oauth2Providers) == 1 &&
|
||||
!setting.Service.EnablePasswordSignInForm &&
|
||||
!setting.Service.EnableOpenIDSignIn &&
|
||||
!setting.Service.EnablePasskeyAuth &&
|
||||
!data.enableSSPI
|
||||
|
||||
if !onlySingleOAuth2 {
|
||||
return false
|
||||
}
|
||||
|
||||
skipToOAuthURL := setting.AppSubURL + "/user/oauth2/" + url.QueryEscape(data.oauth2Providers[0].DisplayName())
|
||||
if redirectTo := ctx.FormString("redirect_to"); redirectTo != "" {
|
||||
skipToOAuthURL += "?redirect_to=" + url.QueryEscape(redirectTo)
|
||||
}
|
||||
ctx.Redirect(skipToOAuthURL)
|
||||
return true
|
||||
}
|
||||
|
||||
type preparedSignInData struct {
|
||||
oauth2Providers []oauth2.Provider
|
||||
enableSSPI bool
|
||||
}
|
||||
|
||||
func prepareSignInPageData(ctx *context.Context) (ret preparedSignInData) {
|
||||
var err error
|
||||
ret.enableSSPI = auth.IsSSPIEnabled(ctx)
|
||||
ret.oauth2Providers, err = oauth2.GetOAuth2Providers(ctx, optional.Some(true))
|
||||
if err != nil {
|
||||
log.Error("Failed to get OAuth2 providers: %v", err)
|
||||
}
|
||||
ctx.Data["Title"] = ctx.Tr("sign_in")
|
||||
ctx.Data["OAuth2Providers"], _ = oauth2.GetOAuth2Providers(ctx, optional.Some(true))
|
||||
ctx.Data["OAuth2Providers"] = ret.oauth2Providers
|
||||
ctx.Data["Title"] = ctx.Tr("sign_in")
|
||||
ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login"
|
||||
ctx.Data["PageIsSignIn"] = true
|
||||
ctx.Data["PageIsLogin"] = true
|
||||
ctx.Data["EnableSSPI"] = auth.IsSSPIEnabled(ctx)
|
||||
ctx.Data["EnableSSPI"] = ret.enableSSPI
|
||||
|
||||
prepareCommonAuthPageData(ctx, CommonAuthOptions{
|
||||
EnableCaptcha: setting.Service.EnableCaptcha && setting.Service.RequireCaptchaForLogin,
|
||||
})
|
||||
return ret
|
||||
}
|
||||
|
||||
// SignIn render sign in page
|
||||
@ -241,7 +273,10 @@ func SignIn(ctx *context.Context) {
|
||||
redirectAfterAuth(ctx)
|
||||
return
|
||||
}
|
||||
prepareSignInPageData(ctx)
|
||||
data := prepareSignInPageData(ctx)
|
||||
if performAutoLoginOAuth2(ctx, &data) {
|
||||
return
|
||||
}
|
||||
ctx.HTML(http.StatusOK, tplSignIn)
|
||||
}
|
||||
|
||||
@ -471,6 +506,7 @@ func prepareSignUpPageData(ctx *context.Context) bool {
|
||||
ctx.Data["Title"] = ctx.Tr("sign_up")
|
||||
ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/sign_up"
|
||||
ctx.Data["PageIsSignUp"] = true
|
||||
ctx.Data["EnableSSPI"] = auth.IsSSPIEnabled(ctx)
|
||||
|
||||
hasUsers, err := user_model.HasUsers(ctx)
|
||||
if err != nil {
|
||||
|
||||
@ -96,6 +96,37 @@ func TestWebAuthOAuth2(t *testing.T) {
|
||||
assert.Contains(t, ctx.Flash.ErrorMsg, "auth.oauth.signin.error.general")
|
||||
})
|
||||
|
||||
t.Run("RedirectSingleProvider", func(t *testing.T) {
|
||||
enablePassword := &setting.Service.EnablePasswordSignInForm
|
||||
enableOpenID := &setting.Service.EnableOpenIDSignIn
|
||||
enablePasskey := &setting.Service.EnablePasskeyAuth
|
||||
defer test.MockVariableValue(enablePassword, false)()
|
||||
defer test.MockVariableValue(enableOpenID, false)()
|
||||
defer test.MockVariableValue(enablePasskey, false)()
|
||||
|
||||
testSignIn := func(t *testing.T, link string, expectedCode int, expectedRedirect string) {
|
||||
ctx, resp := contexttest.MockContext(t, link)
|
||||
SignIn(ctx)
|
||||
assert.Equal(t, expectedCode, resp.Code)
|
||||
if expectedCode == http.StatusSeeOther {
|
||||
assert.Equal(t, expectedRedirect, test.RedirectURL(resp))
|
||||
}
|
||||
}
|
||||
testSignIn(t, "/user/login", http.StatusSeeOther, "/user/oauth2/dummy-auth-source")
|
||||
testSignIn(t, "/user/login?redirect_to=/", http.StatusSeeOther, "/user/oauth2/dummy-auth-source?redirect_to=%2F")
|
||||
|
||||
*enablePassword, *enableOpenID, *enablePasskey = true, false, false
|
||||
testSignIn(t, "/user/login", http.StatusOK, "")
|
||||
*enablePassword, *enableOpenID, *enablePasskey = false, true, false
|
||||
testSignIn(t, "/user/login", http.StatusOK, "")
|
||||
*enablePassword, *enableOpenID, *enablePasskey = false, false, true
|
||||
testSignIn(t, "/user/login", http.StatusOK, "")
|
||||
|
||||
*enablePassword, *enableOpenID, *enablePasskey = false, false, false
|
||||
addOAuth2Source(t, "dummy-auth-source-2", oauth2.Source{})
|
||||
testSignIn(t, "/user/login", http.StatusOK, "")
|
||||
})
|
||||
|
||||
t.Run("OIDCLogout", func(t *testing.T) {
|
||||
var mockServer *httptest.Server
|
||||
mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
@ -62,7 +62,7 @@ func (p *AuthSourceProvider) DisplayName() string {
|
||||
|
||||
func (p *AuthSourceProvider) IconHTML(size int) template.HTML {
|
||||
if p.iconURL != "" {
|
||||
img := fmt.Sprintf(`<img class="tw-object-contain tw-mr-2" width="%d" height="%d" src="%s" alt="%s">`,
|
||||
img := fmt.Sprintf(`<img class="tw-object-contain" width="%d" height="%d" src="%s" alt="%s">`,
|
||||
size,
|
||||
size,
|
||||
html.EscapeString(p.iconURL), html.EscapeString(p.DisplayName()),
|
||||
|
||||
@ -42,10 +42,10 @@ func (b *BaseProvider) IconHTML(size int) template.HTML {
|
||||
case "github":
|
||||
svgName = "octicon-mark-github"
|
||||
}
|
||||
svgHTML := svg.RenderHTML(svgName, size, "tw-mr-2")
|
||||
svgHTML := svg.RenderHTML(svgName, size)
|
||||
if svgHTML == "" {
|
||||
log.Error("No SVG icon for oauth2 provider %q", b.name)
|
||||
svgHTML = svg.RenderHTML("gitea-openid", size, "tw-mr-2")
|
||||
svgHTML = svg.RenderHTML("gitea-openid", size)
|
||||
}
|
||||
return svgHTML
|
||||
}
|
||||
|
||||
@ -33,7 +33,7 @@ func (o *OpenIDProvider) DisplayName() string {
|
||||
|
||||
// IconHTML returns icon HTML for this provider
|
||||
func (o *OpenIDProvider) IconHTML(size int) template.HTML {
|
||||
return svg.RenderHTML("gitea-openid", size, "tw-mr-2")
|
||||
return svg.RenderHTML("gitea-openid", size)
|
||||
}
|
||||
|
||||
// CreateGothProvider creates a GothProvider from this Provider
|
||||
|
||||
@ -16,6 +16,8 @@
|
||||
data-locale-all-jobs="{{ctx.Locale.Tr "actions.runs.all_jobs"}}"
|
||||
data-locale-triggered-via="{{ctx.Locale.Tr "actions.runs.triggered_via"}}"
|
||||
data-locale-total-duration="{{ctx.Locale.Tr "actions.runs.total_duration"}}"
|
||||
data-locale-run-details="{{ctx.Locale.Tr "actions.runs.run_details"}}"
|
||||
data-locale-workflow-file="{{ctx.Locale.Tr "actions.runs.workflow_file"}}"
|
||||
data-locale-status-unknown="{{ctx.Locale.Tr "actions.status.unknown"}}"
|
||||
data-locale-status-waiting="{{ctx.Locale.Tr "actions.status.waiting"}}"
|
||||
data-locale-status-running="{{ctx.Locale.Tr "actions.status.running"}}"
|
||||
|
||||
18
templates/user/auth/external_auth_methods.tmpl
Normal file
18
templates/user/auth/external_auth_methods.tmpl
Normal file
@ -0,0 +1,18 @@
|
||||
<div id="external-login-navigator" class="tw-py-1 tw-flex tw-flex-col tw-gap-3">
|
||||
{{range $provider := .OAuth2Providers}}
|
||||
{{/* use QueryEscape for consistent with frontend urlQueryEscape, it is right for a path component */}}
|
||||
<a class="ui button external-login-link tw-gap-3" data-require-appurl-check="true" rel="nofollow" href="{{AppSubUrl}}/user/oauth2/{{$provider.DisplayName | QueryEscape}}">
|
||||
{{$provider.IconHTML 24}} {{ctx.Locale.Tr "sign_in_with_provider" $provider.DisplayName}}
|
||||
</a>
|
||||
{{end}}
|
||||
{{if .EnableOpenIDSignIn}}
|
||||
<a class="ui button external-login-link tw-gap-3" data-require-appurl-check="true" rel="nofollow" href="{{AppSubUrl}}/user/login/openid">
|
||||
{{svg "fontawesome-openid" 24}} {{ctx.Locale.Tr "sign_in_with_provider" "OpenID"}}
|
||||
</a>
|
||||
{{end}}
|
||||
{{if .EnableSSPI}}
|
||||
<a class="ui button external-login-link tw-gap-3" rel="nofollow" href="{{AppSubUrl}}/user/login?auth_with_sspi=1">
|
||||
{{svg "fontawesome-windows" 24}} Windows SSPI
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
@ -1,24 +0,0 @@
|
||||
<div id="oauth2-login-navigator" class="tw-py-1">
|
||||
<div class="tw-flex tw-flex-col tw-justify-center">
|
||||
<div id="oauth2-login-navigator-inner" class="tw-flex tw-flex-col tw-flex-wrap tw-items-center tw-gap-2">
|
||||
{{range $provider := .OAuth2Providers}}
|
||||
<a class="{{$provider.Name}} ui button tw-flex tw-items-center tw-justify-center tw-py-2 tw-w-full oauth-login-link" href="{{AppSubUrl}}/user/oauth2/{{$provider.DisplayName}}">
|
||||
{{$provider.IconHTML 28}}
|
||||
{{ctx.Locale.Tr "sign_in_with_provider" $provider.DisplayName}}
|
||||
</a>
|
||||
{{end}}
|
||||
{{if .EnableOpenIDSignIn}}
|
||||
<a class="openid ui button tw-flex tw-items-center tw-justify-center tw-py-2 tw-w-full" href="{{AppSubUrl}}/user/login/openid">
|
||||
{{svg "fontawesome-openid" 28 "tw-mr-2"}}
|
||||
{{ctx.Locale.Tr "sign_in_with_provider" "OpenID"}}
|
||||
</a>
|
||||
{{end}}
|
||||
{{if .EnableSSPI}}
|
||||
<a class="ui button tw-flex tw-items-center tw-justify-center tw-py-2 tw-w-full" rel="nofollow" href="{{AppSubUrl}}/user/login?auth_with_sspi=1">
|
||||
{{svg "fontawesome-windows"}}
|
||||
SSPI
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -46,14 +46,13 @@
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
{{end}}{{/*if .EnablePasswordSignInForm*/}}
|
||||
{{/* "oauth_container" contains not only "oauth2" methods, but also "OIDC" and "SSPI" methods */}}
|
||||
{{$showOAuth2Methods := or .OAuth2Providers .EnableOpenIDSignIn .EnableSSPI}}
|
||||
{{if and $showOAuth2Methods .EnablePasswordSignInForm}}
|
||||
{{end}}{{/*end if .EnablePasswordSignInForm*/}}
|
||||
{{$showExternalAuthMethods := or .OAuth2Providers .EnableOpenIDSignIn .EnableSSPI}}
|
||||
{{if and $showExternalAuthMethods .EnablePasswordSignInForm}}
|
||||
<div class="divider divider-text">{{ctx.Locale.Tr "sign_in_or"}}</div>
|
||||
{{end}}
|
||||
{{if $showOAuth2Methods}}
|
||||
{{template "user/auth/oauth_container" .}}
|
||||
{{if $showExternalAuthMethods}}
|
||||
{{template "user/auth/external_auth_methods" .}}
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -49,12 +49,10 @@
|
||||
</button>
|
||||
</div>
|
||||
{{end}}
|
||||
{{/* "oauth_container" contains not only "oauth2" methods, but also "OIDC" and "SSPI" methods */}}
|
||||
{{/* TODO: it seems that "EnableSSPI" is only set in "sign-in" handlers, but it should use the same logic to control its display */}}
|
||||
{{$showOAuth2Methods := or .OAuth2Providers .EnableOpenIDSignIn .EnableSSPI}}
|
||||
{{if $showOAuth2Methods}}
|
||||
{{$showExternalAuthMethods := or .OAuth2Providers .EnableOpenIDSignIn .EnableSSPI}}
|
||||
{{if $showExternalAuthMethods}}
|
||||
<div class="divider divider-text">{{ctx.Locale.Tr "sign_in_or"}}</div>
|
||||
{{template "user/auth/oauth_container" .}}
|
||||
{{template "user/auth/external_auth_methods" .}}
|
||||
{{end}}
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@ -92,52 +92,62 @@ async function deleteArtifact(name: string) {
|
||||
</div>
|
||||
<div class="action-view-body">
|
||||
<div class="action-view-left">
|
||||
<div class="job-group-section">
|
||||
<a class="job-brief-item" :href="run.link" :class="!props.jobId ? 'selected' : ''">
|
||||
<div class="job-brief-item-left">
|
||||
<SvgIcon name="octicon-list-unordered" class="tw-mr-2"/>
|
||||
<span class="job-brief-name tw-mx-2 gt-ellipsis">{{ locale.summary }}</span>
|
||||
</div>
|
||||
</a>
|
||||
<div class="ui divider"/>
|
||||
<div class="job-brief-list">
|
||||
<div class="left-list-header">{{ locale.allJobs }}</div>
|
||||
<a class="job-brief-item" :href="run.link+'/jobs/'+job.id" :class="props.jobId === job.id ? 'selected' : ''" v-for="job in run.jobs" :key="job.id">
|
||||
<div class="job-brief-item-left">
|
||||
<ActionRunStatus :locale-status="locale.status[job.status]" :status="job.status"/>
|
||||
<span class="job-brief-name tw-mx-2 gt-ellipsis">{{ job.name }}</span>
|
||||
</div>
|
||||
<span class="job-brief-item-right">
|
||||
<SvgIcon name="octicon-sync" role="button" :data-tooltip-content="locale.rerun" class="job-brief-rerun tw-mx-2 link-action interact-fg" :data-url="`${run.link}/jobs/${job.id}/rerun`" v-if="job.canRerun"/>
|
||||
<span class="step-summary-duration">{{ job.duration }}</span>
|
||||
</span>
|
||||
<!-- summary -->
|
||||
<a class="job-brief-item silenced" :href="run.link" :class="!props.jobId ? 'selected' : ''">
|
||||
<SvgIcon name="octicon-list-unordered"/>
|
||||
<span class="gt-ellipsis">{{ locale.summary }}</span>
|
||||
</a>
|
||||
|
||||
<!-- jobs list -->
|
||||
<div class="ui divider"/>
|
||||
<div class="left-list-header">{{ locale.allJobs }}</div>
|
||||
<!-- unlike other lists, the items have paddings already -->
|
||||
<ul class="ui relaxed list flex-items-block tw-p-0">
|
||||
<li class="item job-brief-item" v-for="job in run.jobs" :key="job.id" :class="props.jobId === job.id ? 'selected' : ''">
|
||||
<a class="tw-contents silenced" :href="run.link+'/jobs/'+job.id">
|
||||
<ActionRunStatus :locale-status="locale.status[job.status]" :status="job.status"/>
|
||||
<span class="tw-flex-1 gt-ellipsis">{{ job.name }}</span>
|
||||
<SvgIcon name="octicon-sync" role="button" :data-tooltip-content="locale.rerun" class="tw-cursor-pointer link-action interact-fg" :data-url="`${run.link}/jobs/${job.id}/rerun`" v-if="job.canRerun"/>
|
||||
<span>{{ job.duration }}</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="job-artifacts" v-if="artifacts.length > 0">
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!-- artifacts list -->
|
||||
<template v-if="artifacts.length > 0">
|
||||
<div class="ui divider"/>
|
||||
<div class="left-list-header">{{ locale.artifactsTitle }} ({{ artifacts.length }})</div>
|
||||
<ul class="job-artifacts-list">
|
||||
<template v-for="artifact in artifacts" :key="artifact.name">
|
||||
<li class="job-artifacts-item">
|
||||
<template v-if="artifact.status !== 'expired'">
|
||||
<a class="flex-text-inline" target="_blank" :href="run.link+'/artifacts/'+artifact.name">
|
||||
<SvgIcon name="octicon-file" class="tw-text-text"/>
|
||||
<span class="gt-ellipsis">{{ artifact.name }}</span>
|
||||
</a>
|
||||
<a v-if="run.canDeleteArtifact" @click="deleteArtifact(artifact.name)">
|
||||
<SvgIcon name="octicon-trash" class="tw-text-text"/>
|
||||
</a>
|
||||
</template>
|
||||
<span v-else class="flex-text-inline tw-text-grey-light">
|
||||
<SvgIcon name="octicon-file"/>
|
||||
<span class="gt-ellipsis">{{ artifact.name }}</span>
|
||||
<span class="ui label tw-text-grey-light tw-flex-shrink-0">{{ locale.artifactExpired }}</span>
|
||||
</span>
|
||||
</li>
|
||||
</template>
|
||||
<ul class="ui relaxed list flex-items-block">
|
||||
<li class="item" v-for="artifact in artifacts" :key="artifact.name">
|
||||
<template v-if="artifact.status !== 'expired'">
|
||||
<a class="tw-flex-1 flex-text-block" target="_blank" :href="run.link+'/artifacts/'+artifact.name">
|
||||
<SvgIcon name="octicon-file" class="tw-text-text"/>
|
||||
<span class="tw-flex-1 gt-ellipsis">{{ artifact.name }}</span>
|
||||
</a>
|
||||
<a v-if="run.canDeleteArtifact" @click="deleteArtifact(artifact.name)">
|
||||
<SvgIcon name="octicon-trash" class="tw-text-text"/>
|
||||
</a>
|
||||
</template>
|
||||
<span v-else class="flex-text-block tw-flex-1 tw-text-grey-light">
|
||||
<SvgIcon name="octicon-file"/>
|
||||
<span class="tw-flex-1 gt-ellipsis">{{ artifact.name }}</span>
|
||||
<span class="ui label tw-text-grey-light tw-flex-shrink-0">{{ locale.artifactExpired }}</span>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- run details -->
|
||||
<div class="ui divider"/>
|
||||
<div class="left-list-header">{{ locale.runDetails }}</div>
|
||||
<ul class="ui relaxed list">
|
||||
<li class="item">
|
||||
<a class="flex-text-block" :href="`${run.link}/workflow`">
|
||||
<SvgIcon name="octicon-file-code" class="tw-text-text"/>
|
||||
<span class="gt-ellipsis">{{ locale.workflowFile }}</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="action-view-right">
|
||||
@ -244,34 +254,18 @@ async function deleteArtifact(name: string) {
|
||||
color: var(--color-text-light-2);
|
||||
}
|
||||
|
||||
.job-artifacts-item {
|
||||
margin: 5px 0;
|
||||
padding: 6px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.job-artifacts-list {
|
||||
padding-left: 4px;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.job-brief-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
.action-view-left .ui.relaxed.list {
|
||||
margin: var(--gap-block) 0;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.job-brief-item {
|
||||
padding: 6px 10px;
|
||||
border-radius: var(--border-radius);
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
color: var(--color-text);
|
||||
gap: var(--gap-block);
|
||||
}
|
||||
|
||||
.job-brief-item:hover {
|
||||
@ -283,34 +277,6 @@ async function deleteArtifact(name: string) {
|
||||
background-color: var(--color-active);
|
||||
}
|
||||
|
||||
.job-brief-item:first-of-type {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.job-brief-item .job-brief-rerun {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.job-brief-item .job-brief-item-left {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.job-brief-item .job-brief-item-left span {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.job-brief-item .job-brief-item-left .job-brief-name {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.job-brief-item .job-brief-item-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* ================ */
|
||||
/* action view right */
|
||||
|
||||
|
||||
@ -50,6 +50,8 @@ export function initRepositoryActionView() {
|
||||
},
|
||||
logsAlwaysAutoScroll: el.getAttribute('data-locale-logs-always-auto-scroll'),
|
||||
logsAlwaysExpandRunning: el.getAttribute('data-locale-logs-always-expand-running'),
|
||||
workflowFile: el.getAttribute('data-locale-workflow-file'),
|
||||
runDetails: el.getAttribute('data-locale-run-details'),
|
||||
},
|
||||
});
|
||||
view.mount(el);
|
||||
|
||||
@ -5,23 +5,23 @@ export function initUserCheckAppUrl() {
|
||||
checkAppUrlScheme();
|
||||
}
|
||||
|
||||
export function initUserAuthOauth2() {
|
||||
const outer = document.querySelector('#oauth2-login-navigator');
|
||||
if (!outer) return;
|
||||
const inner = document.querySelector('#oauth2-login-navigator-inner')!;
|
||||
export function initUserExternalLogins() {
|
||||
const container = document.querySelector('#external-login-navigator');
|
||||
if (!container) return;
|
||||
|
||||
checkAppUrl();
|
||||
|
||||
for (const link of outer.querySelectorAll('.oauth-login-link')) {
|
||||
// whether the auth method requires app url check (need consistent ROOT_URL with visited URL)
|
||||
let needCheckAppUrl = false;
|
||||
for (const link of container.querySelectorAll('.external-login-link')) {
|
||||
needCheckAppUrl = needCheckAppUrl || link.getAttribute('data-require-appurl-check') === 'true';
|
||||
link.addEventListener('click', () => {
|
||||
inner.classList.add('tw-invisible');
|
||||
outer.classList.add('is-loading');
|
||||
container.classList.add('is-loading');
|
||||
setTimeout(() => {
|
||||
// recover previous content to let user try again
|
||||
// usually redirection will be performed before this action
|
||||
outer.classList.remove('is-loading');
|
||||
inner.classList.remove('tw-invisible');
|
||||
// recover previous content to let user try again, usually redirection will be performed before this action
|
||||
container.classList.remove('is-loading');
|
||||
}, 5000);
|
||||
});
|
||||
}
|
||||
if (needCheckAppUrl) {
|
||||
checkAppUrl();
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,7 +20,7 @@ import {initStopwatch} from './features/stopwatch.ts';
|
||||
import {initRepoFileSearch} from './features/repo-findfile.ts';
|
||||
import {initMarkupContent} from './markup/content.ts';
|
||||
import {initRepoFileView} from './features/file-view.ts';
|
||||
import {initUserAuthOauth2, initUserCheckAppUrl} from './features/user-auth.ts';
|
||||
import {initUserExternalLogins, initUserCheckAppUrl} from './features/user-auth.ts';
|
||||
import {initRepoPullRequestReview, initRepoIssueFilterItemLabel} from './features/repo-issue.ts';
|
||||
import {initRepoEllipsisButton, initCommitStatuses} from './features/repo-commit.ts';
|
||||
import {initRepoTopicBar} from './features/repo-home.ts';
|
||||
@ -149,7 +149,7 @@ const initPerformanceTracer = callInitFunctions([
|
||||
initCaptcha,
|
||||
|
||||
initUserCheckAppUrl,
|
||||
initUserAuthOauth2,
|
||||
initUserExternalLogins,
|
||||
initUserAuthWebAuthn,
|
||||
initUserAuthWebAuthnRegister,
|
||||
initUserSettings,
|
||||
|
||||
@ -29,6 +29,7 @@ import octiconDotFill from '../../public/assets/img/svg/octicon-dot-fill.svg';
|
||||
import octiconDownload from '../../public/assets/img/svg/octicon-download.svg';
|
||||
import octiconEye from '../../public/assets/img/svg/octicon-eye.svg';
|
||||
import octiconFile from '../../public/assets/img/svg/octicon-file.svg';
|
||||
import octiconFileCode from '../../public/assets/img/svg/octicon-file-code.svg';
|
||||
import octiconFileDirectoryFill from '../../public/assets/img/svg/octicon-file-directory-fill.svg';
|
||||
import octiconFileDirectoryOpenFill from '../../public/assets/img/svg/octicon-file-directory-open-fill.svg';
|
||||
import octiconFileSubmodule from '../../public/assets/img/svg/octicon-file-submodule.svg';
|
||||
@ -112,6 +113,7 @@ const svgs = {
|
||||
'octicon-download': octiconDownload,
|
||||
'octicon-eye': octiconEye,
|
||||
'octicon-file': octiconFile,
|
||||
'octicon-file-code': octiconFileCode,
|
||||
'octicon-file-directory-fill': octiconFileDirectoryFill,
|
||||
'octicon-file-directory-open-fill': octiconFileDirectoryOpenFill,
|
||||
'octicon-file-submodule': octiconFileSubmodule,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user