Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
陈曦
sub2api
Commits
a1dc0089
Unverified
Commit
a1dc0089
authored
Mar 14, 2026
by
Wesley Liddick
Committed by
GitHub
Mar 14, 2026
Browse files
Merge pull request #944 from miraserver/feat/backend-mode
feat: add Backend Mode toggle to disable user self-service
parents
dfbcc363
6826149a
Changes
27
Hide whitespace changes
Inline
Side-by-side
frontend/src/i18n/locales/zh.ts
View file @
a1dc0089
...
@@ -4094,6 +4094,9 @@ export default {
...
@@ -4094,6 +4094,9 @@ export default {
site
:
{
site
:
{
title
:
'
站点设置
'
,
title
:
'
站点设置
'
,
description
:
'
自定义站点品牌
'
,
description
:
'
自定义站点品牌
'
,
backendMode
:
'
Backend 模式
'
,
backendModeDescription
:
'
禁用用户注册、公开页面和自助服务功能。仅管理员可以登录和管理平台。
'
,
siteName
:
'
站点名称
'
,
siteName
:
'
站点名称
'
,
siteNameHint
:
'
显示在邮件和页面标题中
'
,
siteNameHint
:
'
显示在邮件和页面标题中
'
,
siteNamePlaceholder
:
'
Sub2API
'
,
siteNamePlaceholder
:
'
Sub2API
'
,
...
...
frontend/src/router/__tests__/guards.spec.ts
View file @
a1dc0089
...
@@ -51,6 +51,7 @@ interface MockAuthState {
...
@@ -51,6 +51,7 @@ interface MockAuthState {
isAuthenticated
:
boolean
isAuthenticated
:
boolean
isAdmin
:
boolean
isAdmin
:
boolean
isSimpleMode
:
boolean
isSimpleMode
:
boolean
backendModeEnabled
:
boolean
}
}
/**
/**
...
@@ -70,8 +71,17 @@ function simulateGuard(
...
@@ -70,8 +71,17 @@ function simulateGuard(
authState
.
isAuthenticated
&&
authState
.
isAuthenticated
&&
(
toPath
===
'
/login
'
||
toPath
===
'
/register
'
)
(
toPath
===
'
/login
'
||
toPath
===
'
/register
'
)
)
{
)
{
if
(
authState
.
backendModeEnabled
&&
!
authState
.
isAdmin
)
{
return
null
}
return
authState
.
isAdmin
?
'
/admin/dashboard
'
:
'
/dashboard
'
return
authState
.
isAdmin
?
'
/admin/dashboard
'
:
'
/dashboard
'
}
}
if
(
authState
.
backendModeEnabled
&&
!
authState
.
isAuthenticated
)
{
const
allowed
=
[
'
/login
'
,
'
/key-usage
'
,
'
/setup
'
]
if
(
!
allowed
.
some
((
path
)
=>
toPath
===
path
||
toPath
.
startsWith
(
path
)))
{
return
'
/login
'
}
}
return
null
// 允许通过
return
null
// 允许通过
}
}
...
@@ -99,6 +109,17 @@ function simulateGuard(
...
@@ -99,6 +109,17 @@ function simulateGuard(
}
}
}
}
// Backend mode: admin gets full access, non-admin blocked
if
(
authState
.
backendModeEnabled
)
{
if
(
authState
.
isAuthenticated
&&
authState
.
isAdmin
)
{
return
null
}
const
allowed
=
[
'
/login
'
,
'
/key-usage
'
,
'
/setup
'
]
if
(
!
allowed
.
some
((
path
)
=>
toPath
===
path
||
toPath
.
startsWith
(
path
)))
{
return
'
/login
'
}
}
return
null
// 允许通过
return
null
// 允许通过
}
}
...
@@ -114,6 +135,7 @@ describe('路由守卫逻辑', () => {
...
@@ -114,6 +135,7 @@ describe('路由守卫逻辑', () => {
isAuthenticated
:
false
,
isAuthenticated
:
false
,
isAdmin
:
false
,
isAdmin
:
false
,
isSimpleMode
:
false
,
isSimpleMode
:
false
,
backendModeEnabled
:
false
,
}
}
it
(
'
访问需要认证的页面重定向到 /login
'
,
()
=>
{
it
(
'
访问需要认证的页面重定向到 /login
'
,
()
=>
{
...
@@ -144,6 +166,7 @@ describe('路由守卫逻辑', () => {
...
@@ -144,6 +166,7 @@ describe('路由守卫逻辑', () => {
isAuthenticated
:
true
,
isAuthenticated
:
true
,
isAdmin
:
false
,
isAdmin
:
false
,
isSimpleMode
:
false
,
isSimpleMode
:
false
,
backendModeEnabled
:
false
,
}
}
it
(
'
访问 /login 重定向到 /dashboard
'
,
()
=>
{
it
(
'
访问 /login 重定向到 /dashboard
'
,
()
=>
{
...
@@ -179,6 +202,7 @@ describe('路由守卫逻辑', () => {
...
@@ -179,6 +202,7 @@ describe('路由守卫逻辑', () => {
isAuthenticated
:
true
,
isAuthenticated
:
true
,
isAdmin
:
true
,
isAdmin
:
true
,
isSimpleMode
:
false
,
isSimpleMode
:
false
,
backendModeEnabled
:
false
,
}
}
it
(
'
访问 /login 重定向到 /admin/dashboard
'
,
()
=>
{
it
(
'
访问 /login 重定向到 /admin/dashboard
'
,
()
=>
{
...
@@ -205,6 +229,7 @@ describe('路由守卫逻辑', () => {
...
@@ -205,6 +229,7 @@ describe('路由守卫逻辑', () => {
isAuthenticated
:
true
,
isAuthenticated
:
true
,
isAdmin
:
false
,
isAdmin
:
false
,
isSimpleMode
:
true
,
isSimpleMode
:
true
,
backendModeEnabled
:
false
,
}
}
const
redirect
=
simulateGuard
(
'
/subscriptions
'
,
{},
authState
)
const
redirect
=
simulateGuard
(
'
/subscriptions
'
,
{},
authState
)
expect
(
redirect
).
toBe
(
'
/dashboard
'
)
expect
(
redirect
).
toBe
(
'
/dashboard
'
)
...
@@ -215,6 +240,7 @@ describe('路由守卫逻辑', () => {
...
@@ -215,6 +240,7 @@ describe('路由守卫逻辑', () => {
isAuthenticated
:
true
,
isAuthenticated
:
true
,
isAdmin
:
false
,
isAdmin
:
false
,
isSimpleMode
:
true
,
isSimpleMode
:
true
,
backendModeEnabled
:
false
,
}
}
const
redirect
=
simulateGuard
(
'
/redeem
'
,
{},
authState
)
const
redirect
=
simulateGuard
(
'
/redeem
'
,
{},
authState
)
expect
(
redirect
).
toBe
(
'
/dashboard
'
)
expect
(
redirect
).
toBe
(
'
/dashboard
'
)
...
@@ -225,6 +251,7 @@ describe('路由守卫逻辑', () => {
...
@@ -225,6 +251,7 @@ describe('路由守卫逻辑', () => {
isAuthenticated
:
true
,
isAuthenticated
:
true
,
isAdmin
:
true
,
isAdmin
:
true
,
isSimpleMode
:
true
,
isSimpleMode
:
true
,
backendModeEnabled
:
false
,
}
}
const
redirect
=
simulateGuard
(
'
/admin/groups
'
,
{
requiresAdmin
:
true
},
authState
)
const
redirect
=
simulateGuard
(
'
/admin/groups
'
,
{
requiresAdmin
:
true
},
authState
)
expect
(
redirect
).
toBe
(
'
/admin/dashboard
'
)
expect
(
redirect
).
toBe
(
'
/admin/dashboard
'
)
...
@@ -235,6 +262,7 @@ describe('路由守卫逻辑', () => {
...
@@ -235,6 +262,7 @@ describe('路由守卫逻辑', () => {
isAuthenticated
:
true
,
isAuthenticated
:
true
,
isAdmin
:
true
,
isAdmin
:
true
,
isSimpleMode
:
true
,
isSimpleMode
:
true
,
backendModeEnabled
:
false
,
}
}
const
redirect
=
simulateGuard
(
const
redirect
=
simulateGuard
(
'
/admin/subscriptions
'
,
'
/admin/subscriptions
'
,
...
@@ -249,6 +277,7 @@ describe('路由守卫逻辑', () => {
...
@@ -249,6 +277,7 @@ describe('路由守卫逻辑', () => {
isAuthenticated
:
true
,
isAuthenticated
:
true
,
isAdmin
:
false
,
isAdmin
:
false
,
isSimpleMode
:
true
,
isSimpleMode
:
true
,
backendModeEnabled
:
false
,
}
}
const
redirect
=
simulateGuard
(
'
/dashboard
'
,
{},
authState
)
const
redirect
=
simulateGuard
(
'
/dashboard
'
,
{},
authState
)
expect
(
redirect
).
toBeNull
()
expect
(
redirect
).
toBeNull
()
...
@@ -259,9 +288,111 @@ describe('路由守卫逻辑', () => {
...
@@ -259,9 +288,111 @@ describe('路由守卫逻辑', () => {
isAuthenticated
:
true
,
isAuthenticated
:
true
,
isAdmin
:
false
,
isAdmin
:
false
,
isSimpleMode
:
true
,
isSimpleMode
:
true
,
backendModeEnabled
:
false
,
}
}
const
redirect
=
simulateGuard
(
'
/keys
'
,
{},
authState
)
const
redirect
=
simulateGuard
(
'
/keys
'
,
{},
authState
)
expect
(
redirect
).
toBeNull
()
expect
(
redirect
).
toBeNull
()
})
})
})
})
describe
(
'
Backend Mode
'
,
()
=>
{
it
(
'
unauthenticated: /home redirects to /login
'
,
()
=>
{
const
authState
:
MockAuthState
=
{
isAuthenticated
:
false
,
isAdmin
:
false
,
isSimpleMode
:
false
,
backendModeEnabled
:
true
,
}
const
redirect
=
simulateGuard
(
'
/home
'
,
{
requiresAuth
:
false
},
authState
)
expect
(
redirect
).
toBe
(
'
/login
'
)
})
it
(
'
unauthenticated: /login is allowed
'
,
()
=>
{
const
authState
:
MockAuthState
=
{
isAuthenticated
:
false
,
isAdmin
:
false
,
isSimpleMode
:
false
,
backendModeEnabled
:
true
,
}
const
redirect
=
simulateGuard
(
'
/login
'
,
{
requiresAuth
:
false
},
authState
)
expect
(
redirect
).
toBeNull
()
})
it
(
'
unauthenticated: /key-usage is allowed
'
,
()
=>
{
const
authState
:
MockAuthState
=
{
isAuthenticated
:
false
,
isAdmin
:
false
,
isSimpleMode
:
false
,
backendModeEnabled
:
true
,
}
const
redirect
=
simulateGuard
(
'
/key-usage
'
,
{
requiresAuth
:
false
},
authState
)
expect
(
redirect
).
toBeNull
()
})
it
(
'
unauthenticated: /setup is allowed
'
,
()
=>
{
const
authState
:
MockAuthState
=
{
isAuthenticated
:
false
,
isAdmin
:
false
,
isSimpleMode
:
false
,
backendModeEnabled
:
true
,
}
const
redirect
=
simulateGuard
(
'
/setup
'
,
{
requiresAuth
:
false
},
authState
)
expect
(
redirect
).
toBeNull
()
})
it
(
'
admin: /admin/dashboard is allowed
'
,
()
=>
{
const
authState
:
MockAuthState
=
{
isAuthenticated
:
true
,
isAdmin
:
true
,
isSimpleMode
:
false
,
backendModeEnabled
:
true
,
}
const
redirect
=
simulateGuard
(
'
/admin/dashboard
'
,
{
requiresAdmin
:
true
},
authState
)
expect
(
redirect
).
toBeNull
()
})
it
(
'
admin: /login redirects to /admin/dashboard
'
,
()
=>
{
const
authState
:
MockAuthState
=
{
isAuthenticated
:
true
,
isAdmin
:
true
,
isSimpleMode
:
false
,
backendModeEnabled
:
true
,
}
const
redirect
=
simulateGuard
(
'
/login
'
,
{
requiresAuth
:
false
},
authState
)
expect
(
redirect
).
toBe
(
'
/admin/dashboard
'
)
})
it
(
'
non-admin authenticated: /dashboard redirects to /login
'
,
()
=>
{
const
authState
:
MockAuthState
=
{
isAuthenticated
:
true
,
isAdmin
:
false
,
isSimpleMode
:
false
,
backendModeEnabled
:
true
,
}
const
redirect
=
simulateGuard
(
'
/dashboard
'
,
{},
authState
)
expect
(
redirect
).
toBe
(
'
/login
'
)
})
it
(
'
non-admin authenticated: /login is allowed (no redirect loop)
'
,
()
=>
{
const
authState
:
MockAuthState
=
{
isAuthenticated
:
true
,
isAdmin
:
false
,
isSimpleMode
:
false
,
backendModeEnabled
:
true
,
}
const
redirect
=
simulateGuard
(
'
/login
'
,
{
requiresAuth
:
false
},
authState
)
expect
(
redirect
).
toBeNull
()
})
it
(
'
non-admin authenticated: /key-usage is allowed
'
,
()
=>
{
const
authState
:
MockAuthState
=
{
isAuthenticated
:
true
,
isAdmin
:
false
,
isSimpleMode
:
false
,
backendModeEnabled
:
true
,
}
const
redirect
=
simulateGuard
(
'
/key-usage
'
,
{
requiresAuth
:
false
},
authState
)
expect
(
redirect
).
toBeNull
()
})
})
})
})
frontend/src/router/index.ts
View file @
a1dc0089
...
@@ -423,6 +423,7 @@ let authInitialized = false
...
@@ -423,6 +423,7 @@ let authInitialized = false
const
navigationLoading
=
useNavigationLoadingState
()
const
navigationLoading
=
useNavigationLoadingState
()
// 延迟初始化预加载,传入 router 实例
// 延迟初始化预加载,传入 router 实例
let
routePrefetch
:
ReturnType
<
typeof
useRoutePrefetch
>
|
null
=
null
let
routePrefetch
:
ReturnType
<
typeof
useRoutePrefetch
>
|
null
=
null
const
BACKEND_MODE_ALLOWED_PATHS
=
[
'
/login
'
,
'
/key-usage
'
,
'
/setup
'
]
router
.
beforeEach
((
to
,
_from
,
next
)
=>
{
router
.
beforeEach
((
to
,
_from
,
next
)
=>
{
// 开始导航加载状态
// 开始导航加载状态
...
@@ -463,10 +464,24 @@ router.beforeEach((to, _from, next) => {
...
@@ -463,10 +464,24 @@ router.beforeEach((to, _from, next) => {
if
(
!
requiresAuth
)
{
if
(
!
requiresAuth
)
{
// If already authenticated and trying to access login/register, redirect to appropriate dashboard
// If already authenticated and trying to access login/register, redirect to appropriate dashboard
if
(
authStore
.
isAuthenticated
&&
(
to
.
path
===
'
/login
'
||
to
.
path
===
'
/register
'
))
{
if
(
authStore
.
isAuthenticated
&&
(
to
.
path
===
'
/login
'
||
to
.
path
===
'
/register
'
))
{
// In backend mode, non-admin users should NOT be redirected away from login
// (they are blocked from all protected routes, so redirecting would cause a loop)
if
(
appStore
.
backendModeEnabled
&&
!
authStore
.
isAdmin
)
{
next
()
return
}
// Admin users go to admin dashboard, regular users go to user dashboard
// Admin users go to admin dashboard, regular users go to user dashboard
next
(
authStore
.
isAdmin
?
'
/admin/dashboard
'
:
'
/dashboard
'
)
next
(
authStore
.
isAdmin
?
'
/admin/dashboard
'
:
'
/dashboard
'
)
return
return
}
}
// Backend mode: block public pages for unauthenticated users (except login, key-usage, setup)
if
(
appStore
.
backendModeEnabled
&&
!
authStore
.
isAuthenticated
)
{
const
isAllowed
=
BACKEND_MODE_ALLOWED_PATHS
.
some
((
p
)
=>
to
.
path
===
p
||
to
.
path
.
startsWith
(
p
))
if
(
!
isAllowed
)
{
next
(
'
/login
'
)
return
}
}
next
()
next
()
return
return
}
}
...
@@ -505,6 +520,19 @@ router.beforeEach((to, _from, next) => {
...
@@ -505,6 +520,19 @@ router.beforeEach((to, _from, next) => {
}
}
}
}
// Backend mode: admin gets full access, non-admin blocked
if
(
appStore
.
backendModeEnabled
)
{
if
(
authStore
.
isAuthenticated
&&
authStore
.
isAdmin
)
{
next
()
return
}
const
isAllowed
=
BACKEND_MODE_ALLOWED_PATHS
.
some
((
p
)
=>
to
.
path
===
p
||
to
.
path
.
startsWith
(
p
))
if
(
!
isAllowed
)
{
next
(
'
/login
'
)
return
}
}
// All checks passed, allow navigation
// All checks passed, allow navigation
next
()
next
()
})
})
...
...
frontend/src/stores/app.ts
View file @
a1dc0089
...
@@ -47,6 +47,7 @@ export const useAppStore = defineStore('app', () => {
...
@@ -47,6 +47,7 @@ export const useAppStore = defineStore('app', () => {
// ==================== Computed ====================
// ==================== Computed ====================
const
hasActiveToasts
=
computed
(()
=>
toasts
.
value
.
length
>
0
)
const
hasActiveToasts
=
computed
(()
=>
toasts
.
value
.
length
>
0
)
const
backendModeEnabled
=
computed
(()
=>
cachedPublicSettings
.
value
?.
backend_mode_enabled
??
false
)
const
loadingCount
=
ref
<
number
>
(
0
)
const
loadingCount
=
ref
<
number
>
(
0
)
...
@@ -331,6 +332,7 @@ export const useAppStore = defineStore('app', () => {
...
@@ -331,6 +332,7 @@ export const useAppStore = defineStore('app', () => {
custom_menu_items
:
[],
custom_menu_items
:
[],
linuxdo_oauth_enabled
:
false
,
linuxdo_oauth_enabled
:
false
,
sora_client_enabled
:
false
,
sora_client_enabled
:
false
,
backend_mode_enabled
:
false
,
version
:
siteVersion
.
value
version
:
siteVersion
.
value
}
}
}
}
...
@@ -404,6 +406,7 @@ export const useAppStore = defineStore('app', () => {
...
@@ -404,6 +406,7 @@ export const useAppStore = defineStore('app', () => {
// Computed
// Computed
hasActiveToasts
,
hasActiveToasts
,
backendModeEnabled
,
// Actions
// Actions
toggleSidebar
,
toggleSidebar
,
...
...
frontend/src/types/index.ts
View file @
a1dc0089
...
@@ -106,6 +106,7 @@ export interface PublicSettings {
...
@@ -106,6 +106,7 @@ export interface PublicSettings {
custom_menu_items
:
CustomMenuItem
[]
custom_menu_items
:
CustomMenuItem
[]
linuxdo_oauth_enabled
:
boolean
linuxdo_oauth_enabled
:
boolean
sora_client_enabled
:
boolean
sora_client_enabled
:
boolean
backend_mode_enabled
:
boolean
version
:
string
version
:
string
}
}
...
...
frontend/src/views/admin/SettingsView.vue
View file @
a1dc0089
...
@@ -1070,6 +1070,21 @@
...
@@ -1070,6 +1070,21 @@
<
/p
>
<
/p
>
<
/div
>
<
/div
>
<
div
class
=
"
space-y-6 p-6
"
>
<
div
class
=
"
space-y-6 p-6
"
>
<!--
Backend
Mode
-->
<
div
class
=
"
flex items-center justify-between rounded-lg border border-amber-200 bg-amber-50 p-4 dark:border-amber-800 dark:bg-amber-900/20
"
>
<
div
>
<
h3
class
=
"
text-sm font-medium text-gray-900 dark:text-white
"
>
{{
t
(
'
admin.settings.site.backendMode
'
)
}}
<
/h3
>
<
p
class
=
"
mt-1 text-xs text-gray-500 dark:text-gray-400
"
>
{{
t
(
'
admin.settings.site.backendModeDescription
'
)
}}
<
/p
>
<
/div
>
<
Toggle
v
-
model
=
"
form.backend_mode_enabled
"
/>
<
/div
>
<
div
class
=
"
grid grid-cols-1 gap-6 md:grid-cols-2
"
>
<
div
class
=
"
grid grid-cols-1 gap-6 md:grid-cols-2
"
>
<
div
>
<
div
>
<
label
class
=
"
mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300
"
>
<
label
class
=
"
mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300
"
>
...
@@ -1785,6 +1800,7 @@ const form = reactive<SettingsForm>({
...
@@ -1785,6 +1800,7 @@ const form = reactive<SettingsForm>({
contact_info
:
''
,
contact_info
:
''
,
doc_url
:
''
,
doc_url
:
''
,
home_content
:
''
,
home_content
:
''
,
backend_mode_enabled
:
false
,
hide_ccs_import_button
:
false
,
hide_ccs_import_button
:
false
,
purchase_subscription_enabled
:
false
,
purchase_subscription_enabled
:
false
,
purchase_subscription_url
:
''
,
purchase_subscription_url
:
''
,
...
@@ -1962,6 +1978,7 @@ async function loadSettings() {
...
@@ -1962,6 +1978,7 @@ async function loadSettings() {
try
{
try
{
const
settings
=
await
adminAPI
.
settings
.
getSettings
()
const
settings
=
await
adminAPI
.
settings
.
getSettings
()
Object
.
assign
(
form
,
settings
)
Object
.
assign
(
form
,
settings
)
form
.
backend_mode_enabled
=
settings
.
backend_mode_enabled
form
.
default_subscriptions
=
Array
.
isArray
(
settings
.
default_subscriptions
)
form
.
default_subscriptions
=
Array
.
isArray
(
settings
.
default_subscriptions
)
?
settings
.
default_subscriptions
?
settings
.
default_subscriptions
.
filter
((
item
)
=>
item
.
group_id
>
0
&&
item
.
validity_days
>
0
)
.
filter
((
item
)
=>
item
.
group_id
>
0
&&
item
.
validity_days
>
0
)
...
@@ -2060,6 +2077,7 @@ async function saveSettings() {
...
@@ -2060,6 +2077,7 @@ async function saveSettings() {
contact_info
:
form
.
contact_info
,
contact_info
:
form
.
contact_info
,
doc_url
:
form
.
doc_url
,
doc_url
:
form
.
doc_url
,
home_content
:
form
.
home_content
,
home_content
:
form
.
home_content
,
backend_mode_enabled
:
form
.
backend_mode_enabled
,
hide_ccs_import_button
:
form
.
hide_ccs_import_button
,
hide_ccs_import_button
:
form
.
hide_ccs_import_button
,
purchase_subscription_enabled
:
form
.
purchase_subscription_enabled
,
purchase_subscription_enabled
:
form
.
purchase_subscription_enabled
,
purchase_subscription_url
:
form
.
purchase_subscription_url
,
purchase_subscription_url
:
form
.
purchase_subscription_url
,
...
...
frontend/src/views/auth/LoginView.vue
View file @
a1dc0089
...
@@ -12,7 +12,7 @@
...
@@ -12,7 +12,7 @@
</div>
</div>
<!-- LinuxDo Connect OAuth 登录 -->
<!-- LinuxDo Connect OAuth 登录 -->
<LinuxDoOAuthSection
v-if=
"linuxdoOAuthEnabled"
:disabled=
"isLoading"
/>
<LinuxDoOAuthSection
v-if=
"linuxdoOAuthEnabled
&& !backendModeEnabled
"
:disabled=
"isLoading"
/>
<!-- Login Form -->
<!-- Login Form -->
<form
@
submit.prevent=
"handleLogin"
class=
"space-y-5"
>
<form
@
submit.prevent=
"handleLogin"
class=
"space-y-5"
>
...
@@ -78,7 +78,7 @@
...
@@ -78,7 +78,7 @@
</p>
</p>
<span
v-else
></span>
<span
v-else
></span>
<router-link
<router-link
v-if=
"passwordResetEnabled"
v-if=
"passwordResetEnabled
&& !backendModeEnabled
"
to=
"/forgot-password"
to=
"/forgot-password"
class=
"text-sm font-medium text-primary-600 transition-colors hover:text-primary-500 dark:text-primary-400 dark:hover:text-primary-300"
class=
"text-sm font-medium text-primary-600 transition-colors hover:text-primary-500 dark:text-primary-400 dark:hover:text-primary-300"
>
>
...
@@ -151,7 +151,7 @@
...
@@ -151,7 +151,7 @@
</div>
</div>
<!-- Footer -->
<!-- Footer -->
<template
#footer
>
<template
v-if=
"!backendModeEnabled"
#footer
>
<p
class=
"text-gray-500 dark:text-dark-400"
>
<p
class=
"text-gray-500 dark:text-dark-400"
>
{{
t
(
'
auth.dontHaveAccount
'
)
}}
{{
t
(
'
auth.dontHaveAccount
'
)
}}
<router-link
<router-link
...
@@ -206,6 +206,7 @@ const showPassword = ref<boolean>(false)
...
@@ -206,6 +206,7 @@ const showPassword = ref<boolean>(false)
const
turnstileEnabled
=
ref
<
boolean
>
(
false
)
const
turnstileEnabled
=
ref
<
boolean
>
(
false
)
const
turnstileSiteKey
=
ref
<
string
>
(
''
)
const
turnstileSiteKey
=
ref
<
string
>
(
''
)
const
linuxdoOAuthEnabled
=
ref
<
boolean
>
(
false
)
const
linuxdoOAuthEnabled
=
ref
<
boolean
>
(
false
)
const
backendModeEnabled
=
ref
<
boolean
>
(
false
)
const
passwordResetEnabled
=
ref
<
boolean
>
(
false
)
const
passwordResetEnabled
=
ref
<
boolean
>
(
false
)
// Turnstile
// Turnstile
...
@@ -245,6 +246,7 @@ onMounted(async () => {
...
@@ -245,6 +246,7 @@ onMounted(async () => {
turnstileEnabled
.
value
=
settings
.
turnstile_enabled
turnstileEnabled
.
value
=
settings
.
turnstile_enabled
turnstileSiteKey
.
value
=
settings
.
turnstile_site_key
||
''
turnstileSiteKey
.
value
=
settings
.
turnstile_site_key
||
''
linuxdoOAuthEnabled
.
value
=
settings
.
linuxdo_oauth_enabled
linuxdoOAuthEnabled
.
value
=
settings
.
linuxdo_oauth_enabled
backendModeEnabled
.
value
=
settings
.
backend_mode_enabled
passwordResetEnabled
.
value
=
settings
.
password_reset_enabled
passwordResetEnabled
.
value
=
settings
.
password_reset_enabled
}
catch
(
error
)
{
}
catch
(
error
)
{
console
.
error
(
'
Failed to load public settings:
'
,
error
)
console
.
error
(
'
Failed to load public settings:
'
,
error
)
...
...
Prev
1
2
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment