mirror of
https://github.com/QuantumNous/new-api.git
synced 2026-09-12 23:30:35 +00:00
feat(oidc): 支持自定义 OIDC 登录显示名称 (#6012)
* docs: add design spec for OIDC custom display name Mirrors the existing Custom OAuth Provider name pattern so admins can show a meaningful label instead of the hardcoded "OIDC" on the login page and in related copy. * feat(oidc): add configurable display name with OIDC fallback * feat(oidc): use configured display name in provider name and status API * feat(oidc): add display name field to default-theme OIDC settings Claude-Session: https://claude.ai/code/session_01FDkWJqigJi9yE3HG5pjZP5 * feat(oidc): show configured display name on default-theme login button * feat(oidc): add display name field to classic-theme OIDC settings * feat(oidc): show configured display name on classic-theme login button * fix(oidc): trim whitespace before applying display name fallback * chore: remove internal design doc from PR Design/planning docs are working artifacts for this session and shouldn't be submitted to the upstream project. * fix(oidc): lead with example in classic-theme display name placeholder Reorders the combined placeholder to show the example first, then the fallback note, matching the Custom OAuth Provider Name field's placeholder convention (example-only) that this feature mirrors. * fix(i18n): improve Russian grammar in OIDC display-name placeholder translation Leads each clause with its condition/subject and adds the missing verb, per PR review feedback. * test(web): remove redundant OIDC harness tests
This commit is contained in:
@@ -111,6 +111,7 @@ func GetStatus(c *gin.Context) {
|
|||||||
"oidc_enabled": system_setting.GetOIDCSettings().Enabled,
|
"oidc_enabled": system_setting.GetOIDCSettings().Enabled,
|
||||||
"oidc_client_id": system_setting.GetOIDCSettings().ClientId,
|
"oidc_client_id": system_setting.GetOIDCSettings().ClientId,
|
||||||
"oidc_authorization_endpoint": system_setting.GetOIDCSettings().AuthorizationEndpoint,
|
"oidc_authorization_endpoint": system_setting.GetOIDCSettings().AuthorizationEndpoint,
|
||||||
|
"oidc_display_name": system_setting.GetOIDCSettings().GetEffectiveDisplayName(),
|
||||||
"passkey_login": passkeySetting.Enabled,
|
"passkey_login": passkeySetting.Enabled,
|
||||||
"passkey_display_name": passkeySetting.RPDisplayName,
|
"passkey_display_name": passkeySetting.RPDisplayName,
|
||||||
"passkey_rp_id": passkeySetting.RPID,
|
"passkey_rp_id": passkeySetting.RPID,
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package controller
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/QuantumNous/new-api/common"
|
||||||
|
"github.com/QuantumNous/new-api/setting/system_setting"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetStatusReturnsEffectiveOIDCDisplayName(t *testing.T) {
|
||||||
|
settings := system_setting.GetOIDCSettings()
|
||||||
|
originalDisplayName := settings.DisplayName
|
||||||
|
originalOptionMap := common.OptionMap
|
||||||
|
t.Cleanup(func() {
|
||||||
|
settings.DisplayName = originalDisplayName
|
||||||
|
common.OptionMap = originalOptionMap
|
||||||
|
})
|
||||||
|
common.OptionMap = map[string]string{}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
displayName string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "custom name is trimmed",
|
||||||
|
displayName: " Acme SSO ",
|
||||||
|
want: "Acme SSO",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "whitespace-only name falls back",
|
||||||
|
displayName: " ",
|
||||||
|
want: "OIDC",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
settings.DisplayName = tt.displayName
|
||||||
|
response := httptest.NewRecorder()
|
||||||
|
context, _ := gin.CreateTestContext(response)
|
||||||
|
context.Request = httptest.NewRequest(http.MethodGet, "/api/status", nil)
|
||||||
|
|
||||||
|
GetStatus(context)
|
||||||
|
|
||||||
|
var payload struct {
|
||||||
|
Success bool `json:"success"`
|
||||||
|
Data map[string]any `json:"data"`
|
||||||
|
}
|
||||||
|
require.NoError(t, common.Unmarshal(response.Body.Bytes(), &payload))
|
||||||
|
require.True(t, payload.Success)
|
||||||
|
assert.Equal(t, tt.want, payload.Data["oidc_display_name"])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -41,7 +41,7 @@ type oidcUser struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *OIDCProvider) GetName() string {
|
func (p *OIDCProvider) GetName() string {
|
||||||
return "OIDC"
|
return system_setting.GetOIDCSettings().GetEffectiveDisplayName()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *OIDCProvider) IsEnabled() bool {
|
func (p *OIDCProvider) IsEnabled() bool {
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package oauth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/QuantumNous/new-api/setting/system_setting"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestOIDCProvider_GetName(t *testing.T) {
|
||||||
|
settings := system_setting.GetOIDCSettings()
|
||||||
|
originalDisplayName := settings.DisplayName
|
||||||
|
defer func() { settings.DisplayName = originalDisplayName }()
|
||||||
|
|
||||||
|
p := &OIDCProvider{}
|
||||||
|
|
||||||
|
settings.DisplayName = ""
|
||||||
|
assert.Equal(t, "OIDC", p.GetName())
|
||||||
|
|
||||||
|
settings.DisplayName = " Acme SSO "
|
||||||
|
assert.Equal(t, "Acme SSO", p.GetName())
|
||||||
|
}
|
||||||
@@ -1,9 +1,14 @@
|
|||||||
package system_setting
|
package system_setting
|
||||||
|
|
||||||
import "github.com/QuantumNous/new-api/setting/config"
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/QuantumNous/new-api/setting/config"
|
||||||
|
)
|
||||||
|
|
||||||
type OIDCSettings struct {
|
type OIDCSettings struct {
|
||||||
Enabled bool `json:"enabled"`
|
Enabled bool `json:"enabled"`
|
||||||
|
DisplayName string `json:"display_name"`
|
||||||
ClientId string `json:"client_id"`
|
ClientId string `json:"client_id"`
|
||||||
ClientSecret string `json:"client_secret"`
|
ClientSecret string `json:"client_secret"`
|
||||||
WellKnown string `json:"well_known"`
|
WellKnown string `json:"well_known"`
|
||||||
@@ -23,3 +28,14 @@ func init() {
|
|||||||
func GetOIDCSettings() *OIDCSettings {
|
func GetOIDCSettings() *OIDCSettings {
|
||||||
return &defaultOIDCSettings
|
return &defaultOIDCSettings
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetEffectiveDisplayName returns the admin-configured display name, or the
|
||||||
|
// literal "OIDC" when none has been set. Centralizing this fallback keeps the
|
||||||
|
// default in one place for both the OAuth provider name and the public
|
||||||
|
// status payload.
|
||||||
|
func (s *OIDCSettings) GetEffectiveDisplayName() string {
|
||||||
|
if trimmed := strings.TrimSpace(s.DisplayName); trimmed != "" {
|
||||||
|
return trimmed
|
||||||
|
}
|
||||||
|
return "OIDC"
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package system_setting
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/QuantumNous/new-api/setting/config"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestOIDCSettings_GetEffectiveDisplayName(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
displayName string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{name: "blank falls back to OIDC", displayName: "", want: "OIDC"},
|
||||||
|
{name: "custom name is returned verbatim", displayName: "Acme SSO", want: "Acme SSO"},
|
||||||
|
{name: "whitespace-only falls back to OIDC", displayName: " ", want: "OIDC"},
|
||||||
|
{name: "surrounding whitespace is trimmed", displayName: " Acme SSO ", want: "Acme SSO"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
s := &OIDCSettings{DisplayName: tt.displayName}
|
||||||
|
assert.Equal(t, tt.want, s.GetEffectiveDisplayName())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOIDCSettings_DisplayNamePersistenceRoundTrip(t *testing.T) {
|
||||||
|
settings := &OIDCSettings{DisplayName: " Acme SSO "}
|
||||||
|
manager := config.NewConfigManager()
|
||||||
|
manager.Register("oidc", settings)
|
||||||
|
|
||||||
|
saved := make(map[string]string)
|
||||||
|
require.NoError(t, manager.SaveToDB(func(key, value string) error {
|
||||||
|
saved[key] = value
|
||||||
|
return nil
|
||||||
|
}))
|
||||||
|
require.Equal(t, " Acme SSO ", saved["oidc.display_name"])
|
||||||
|
|
||||||
|
settings.DisplayName = ""
|
||||||
|
require.NoError(t, manager.LoadFromDB(saved))
|
||||||
|
assert.Equal(t, " Acme SSO ", settings.DisplayName)
|
||||||
|
assert.Equal(t, "Acme SSO", settings.GetEffectiveDisplayName())
|
||||||
|
}
|
||||||
@@ -107,9 +107,12 @@ export function OAuthProviders({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (status?.oidc_enabled) {
|
if (status?.oidc_enabled) {
|
||||||
|
const oidcDisplayName = status.oidc_display_name?.trim() || 'OIDC'
|
||||||
providerButtons.push({
|
providerButtons.push({
|
||||||
key: 'oidc',
|
key: 'oidc',
|
||||||
label: t('Continue with OIDC'),
|
label: t('Continue with {{name}}', {
|
||||||
|
name: oidcDisplayName,
|
||||||
|
}),
|
||||||
onClick: handleOIDCLogin,
|
onClick: handleOIDCLogin,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ export interface SystemStatus {
|
|||||||
oidc_enabled?: boolean
|
oidc_enabled?: boolean
|
||||||
oidc_authorization_endpoint?: string
|
oidc_authorization_endpoint?: string
|
||||||
oidc_client_id?: string
|
oidc_client_id?: string
|
||||||
|
oidc_display_name?: string
|
||||||
linuxdo_oauth?: boolean
|
linuxdo_oauth?: boolean
|
||||||
linuxdo_client_id?: string
|
linuxdo_client_id?: string
|
||||||
telegram_oauth?: boolean
|
telegram_oauth?: boolean
|
||||||
@@ -147,6 +148,7 @@ export interface SystemStatus {
|
|||||||
oidc_enabled?: boolean
|
oidc_enabled?: boolean
|
||||||
oidc_authorization_endpoint?: string
|
oidc_authorization_endpoint?: string
|
||||||
oidc_client_id?: string
|
oidc_client_id?: string
|
||||||
|
oidc_display_name?: string
|
||||||
linuxdo_oauth?: boolean
|
linuxdo_oauth?: boolean
|
||||||
linuxdo_client_id?: string
|
linuxdo_client_id?: string
|
||||||
telegram_oauth?: boolean
|
telegram_oauth?: boolean
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ const defaultAuthSettings: AuthSettings = {
|
|||||||
'discord.client_id': '',
|
'discord.client_id': '',
|
||||||
'discord.client_secret': '',
|
'discord.client_secret': '',
|
||||||
'oidc.enabled': false,
|
'oidc.enabled': false,
|
||||||
|
'oidc.display_name': '',
|
||||||
'oidc.client_id': '',
|
'oidc.client_id': '',
|
||||||
'oidc.client_secret': '',
|
'oidc.client_secret': '',
|
||||||
'oidc.well_known': '',
|
'oidc.well_known': '',
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ const oauthSchema = z.object({
|
|||||||
}),
|
}),
|
||||||
oidc: z.object({
|
oidc: z.object({
|
||||||
enabled: z.boolean(),
|
enabled: z.boolean(),
|
||||||
|
display_name: z.string(),
|
||||||
client_id: z.string(),
|
client_id: z.string(),
|
||||||
client_secret: z.string(),
|
client_secret: z.string(),
|
||||||
well_known: z.string(),
|
well_known: z.string(),
|
||||||
@@ -102,6 +103,7 @@ type FlatOAuthDefaults = {
|
|||||||
'discord.client_id': string
|
'discord.client_id': string
|
||||||
'discord.client_secret': string
|
'discord.client_secret': string
|
||||||
'oidc.enabled': boolean
|
'oidc.enabled': boolean
|
||||||
|
'oidc.display_name': string
|
||||||
'oidc.client_id': string
|
'oidc.client_id': string
|
||||||
'oidc.client_secret': string
|
'oidc.client_secret': string
|
||||||
'oidc.well_known': string
|
'oidc.well_known': string
|
||||||
@@ -184,6 +186,7 @@ const buildFormDefaults = (defaults: FlatOAuthDefaults): OAuthFormValues => ({
|
|||||||
},
|
},
|
||||||
oidc: {
|
oidc: {
|
||||||
enabled: defaults['oidc.enabled'],
|
enabled: defaults['oidc.enabled'],
|
||||||
|
display_name: defaults['oidc.display_name'] ?? '',
|
||||||
client_id: defaults['oidc.client_id'] ?? '',
|
client_id: defaults['oidc.client_id'] ?? '',
|
||||||
client_secret: defaults['oidc.client_secret'] ?? '',
|
client_secret: defaults['oidc.client_secret'] ?? '',
|
||||||
well_known: defaults['oidc.well_known'] ?? '',
|
well_known: defaults['oidc.well_known'] ?? '',
|
||||||
@@ -212,6 +215,7 @@ const normalizeFormValues = (values: OAuthFormValues): FlatOAuthDefaults => ({
|
|||||||
'discord.client_id': values.discord.client_id,
|
'discord.client_id': values.discord.client_id,
|
||||||
'discord.client_secret': values.discord.client_secret,
|
'discord.client_secret': values.discord.client_secret,
|
||||||
'oidc.enabled': values.oidc.enabled,
|
'oidc.enabled': values.oidc.enabled,
|
||||||
|
'oidc.display_name': values.oidc.display_name,
|
||||||
'oidc.client_id': values.oidc.client_id,
|
'oidc.client_id': values.oidc.client_id,
|
||||||
'oidc.client_secret': values.oidc.client_secret,
|
'oidc.client_secret': values.oidc.client_secret,
|
||||||
'oidc.well_known': values.oidc.well_known,
|
'oidc.well_known': values.oidc.well_known,
|
||||||
@@ -617,6 +621,33 @@ export function OAuthSection(props: OAuthSectionProps) {
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name='oidc.display_name'
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>{t('OIDC Display Name')}</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
placeholder={t('e.g. Company SSO')}
|
||||||
|
autoComplete='off'
|
||||||
|
value={field.value ?? ''}
|
||||||
|
onChange={(event) =>
|
||||||
|
field.onChange(event.target.value)
|
||||||
|
}
|
||||||
|
name={field.name}
|
||||||
|
onBlur={field.onBlur}
|
||||||
|
ref={field.ref}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormDescription>
|
||||||
|
{t('Defaults to "OIDC" if left blank')}
|
||||||
|
</FormDescription>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name='oidc.client_id'
|
name='oidc.client_id'
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ const AUTH_SECTIONS = [
|
|||||||
'discord.client_id': settings['discord.client_id'],
|
'discord.client_id': settings['discord.client_id'],
|
||||||
'discord.client_secret': settings['discord.client_secret'],
|
'discord.client_secret': settings['discord.client_secret'],
|
||||||
'oidc.enabled': settings['oidc.enabled'],
|
'oidc.enabled': settings['oidc.enabled'],
|
||||||
|
'oidc.display_name': settings['oidc.display_name'],
|
||||||
'oidc.client_id': settings['oidc.client_id'],
|
'oidc.client_id': settings['oidc.client_id'],
|
||||||
'oidc.client_secret': settings['oidc.client_secret'],
|
'oidc.client_secret': settings['oidc.client_secret'],
|
||||||
'oidc.well_known': settings['oidc.well_known'],
|
'oidc.well_known': settings['oidc.well_known'],
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import { updateSystemOption } from '../api'
|
|||||||
import type { UpdateOptionRequest } from '../types'
|
import type { UpdateOptionRequest } from '../types'
|
||||||
|
|
||||||
// Configuration keys that require status refresh
|
// Configuration keys that require status refresh
|
||||||
const STATUS_RELATED_KEYS = [
|
const STATUS_RELATED_KEYS = new Set([
|
||||||
'HeaderNavModules',
|
'HeaderNavModules',
|
||||||
'SidebarModulesAdmin',
|
'SidebarModulesAdmin',
|
||||||
'Notice',
|
'Notice',
|
||||||
@@ -36,7 +36,8 @@ const STATUS_RELATED_KEYS = [
|
|||||||
'general_setting.quota_display_type',
|
'general_setting.quota_display_type',
|
||||||
'general_setting.custom_currency_symbol',
|
'general_setting.custom_currency_symbol',
|
||||||
'general_setting.custom_currency_exchange_rate',
|
'general_setting.custom_currency_exchange_rate',
|
||||||
]
|
'oidc.display_name',
|
||||||
|
])
|
||||||
|
|
||||||
export function useUpdateOption() {
|
export function useUpdateOption() {
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
@@ -49,7 +50,7 @@ export function useUpdateOption() {
|
|||||||
queryClient.invalidateQueries({ queryKey: ['system-options'] })
|
queryClient.invalidateQueries({ queryKey: ['system-options'] })
|
||||||
|
|
||||||
// If updating frontend-display-related config, also refresh status
|
// If updating frontend-display-related config, also refresh status
|
||||||
if (STATUS_RELATED_KEYS.includes(variables.key)) {
|
if (STATUS_RELATED_KEYS.has(variables.key)) {
|
||||||
queryClient.invalidateQueries({ queryKey: ['status'] })
|
queryClient.invalidateQueries({ queryKey: ['status'] })
|
||||||
try {
|
try {
|
||||||
window.localStorage.removeItem('status')
|
window.localStorage.removeItem('status')
|
||||||
|
|||||||
@@ -136,6 +136,7 @@ export type AuthSettings = {
|
|||||||
'discord.client_id': string
|
'discord.client_id': string
|
||||||
'discord.client_secret': string
|
'discord.client_secret': string
|
||||||
'oidc.enabled': boolean
|
'oidc.enabled': boolean
|
||||||
|
'oidc.display_name': string
|
||||||
'oidc.client_id': string
|
'oidc.client_id': string
|
||||||
'oidc.client_secret': string
|
'oidc.client_secret': string
|
||||||
'oidc.well_known': string
|
'oidc.well_known': string
|
||||||
|
|||||||
@@ -1580,6 +1580,9 @@
|
|||||||
"Enable LinuxDO OAuth": "Enable LinuxDO OAuth",
|
"Enable LinuxDO OAuth": "Enable LinuxDO OAuth",
|
||||||
"Enable model performance metrics": "Enable model performance metrics",
|
"Enable model performance metrics": "Enable model performance metrics",
|
||||||
"Enable OIDC": "Enable OIDC",
|
"Enable OIDC": "Enable OIDC",
|
||||||
|
"OIDC Display Name": "OIDC Display Name",
|
||||||
|
"e.g. Company SSO": "e.g. Company SSO",
|
||||||
|
"Defaults to \"OIDC\" if left blank": "Defaults to \"OIDC\" if left blank",
|
||||||
"Enable or disable this channel": "Enable or disable this channel",
|
"Enable or disable this channel": "Enable or disable this channel",
|
||||||
"Enable or disable this model": "Enable or disable this model",
|
"Enable or disable this model": "Enable or disable this model",
|
||||||
"Enable Passkey": "Enable Passkey",
|
"Enable Passkey": "Enable Passkey",
|
||||||
|
|||||||
@@ -1580,6 +1580,9 @@
|
|||||||
"Enable LinuxDO OAuth": "Activer LinuxDO OAuth",
|
"Enable LinuxDO OAuth": "Activer LinuxDO OAuth",
|
||||||
"Enable model performance metrics": "Activer les indicateurs de performance des modèles",
|
"Enable model performance metrics": "Activer les indicateurs de performance des modèles",
|
||||||
"Enable OIDC": "Activer OIDC",
|
"Enable OIDC": "Activer OIDC",
|
||||||
|
"OIDC Display Name": "Nom d'affichage OIDC",
|
||||||
|
"e.g. Company SSO": "ex. SSO de l'entreprise",
|
||||||
|
"Defaults to \"OIDC\" if left blank": "Par défaut « OIDC » si laissé vide",
|
||||||
"Enable or disable this channel": "Activer ou désactiver ce canal",
|
"Enable or disable this channel": "Activer ou désactiver ce canal",
|
||||||
"Enable or disable this model": "Activer ou désactiver ce modèle",
|
"Enable or disable this model": "Activer ou désactiver ce modèle",
|
||||||
"Enable Passkey": "Activer Passkey",
|
"Enable Passkey": "Activer Passkey",
|
||||||
|
|||||||
@@ -1580,6 +1580,9 @@
|
|||||||
"Enable LinuxDO OAuth": "LinuxDO OAuthを有効にする",
|
"Enable LinuxDO OAuth": "LinuxDO OAuthを有効にする",
|
||||||
"Enable model performance metrics": "モデル性能メトリクスを有効化",
|
"Enable model performance metrics": "モデル性能メトリクスを有効化",
|
||||||
"Enable OIDC": "OIDCを有効にする",
|
"Enable OIDC": "OIDCを有効にする",
|
||||||
|
"OIDC Display Name": "OIDC 表示名",
|
||||||
|
"e.g. Company SSO": "例:会社の SSO",
|
||||||
|
"Defaults to \"OIDC\" if left blank": "空欄の場合は「OIDC」がデフォルトで表示されます",
|
||||||
"Enable or disable this channel": "このチャネルを有効または無効にする",
|
"Enable or disable this channel": "このチャネルを有効または無効にする",
|
||||||
"Enable or disable this model": "このモデルを有効または無効にする",
|
"Enable or disable this model": "このモデルを有効または無効にする",
|
||||||
"Enable Passkey": "Passkeyを有効にする",
|
"Enable Passkey": "Passkeyを有効にする",
|
||||||
|
|||||||
@@ -1580,6 +1580,9 @@
|
|||||||
"Enable LinuxDO OAuth": "Включить LinuxDO OAuth",
|
"Enable LinuxDO OAuth": "Включить LinuxDO OAuth",
|
||||||
"Enable model performance metrics": "Включить метрики производительности моделей",
|
"Enable model performance metrics": "Включить метрики производительности моделей",
|
||||||
"Enable OIDC": "Включить OIDC",
|
"Enable OIDC": "Включить OIDC",
|
||||||
|
"OIDC Display Name": "Отображаемое имя OIDC",
|
||||||
|
"e.g. Company SSO": "например, корпоративный SSO",
|
||||||
|
"Defaults to \"OIDC\" if left blank": "Если оставить пустым, будет отображаться «OIDC».",
|
||||||
"Enable or disable this channel": "Включить или отключить этот канал",
|
"Enable or disable this channel": "Включить или отключить этот канал",
|
||||||
"Enable or disable this model": "Включить или отключить эту модель",
|
"Enable or disable this model": "Включить или отключить эту модель",
|
||||||
"Enable Passkey": "Включить Passkey",
|
"Enable Passkey": "Включить Passkey",
|
||||||
|
|||||||
@@ -1580,6 +1580,9 @@
|
|||||||
"Enable LinuxDO OAuth": "Bật LinuxDO OAuth",
|
"Enable LinuxDO OAuth": "Bật LinuxDO OAuth",
|
||||||
"Enable model performance metrics": "Bật chỉ số hiệu năng mô hình",
|
"Enable model performance metrics": "Bật chỉ số hiệu năng mô hình",
|
||||||
"Enable OIDC": "Bật OIDC",
|
"Enable OIDC": "Bật OIDC",
|
||||||
|
"OIDC Display Name": "Tên hiển thị OIDC",
|
||||||
|
"e.g. Company SSO": "ví dụ: SSO công ty",
|
||||||
|
"Defaults to \"OIDC\" if left blank": "Mặc định là \"OIDC\" nếu để trống",
|
||||||
"Enable or disable this channel": "Bật hoặc tắt kênh này",
|
"Enable or disable this channel": "Bật hoặc tắt kênh này",
|
||||||
"Enable or disable this model": "Bật hoặc tắt mô hình này",
|
"Enable or disable this model": "Bật hoặc tắt mô hình này",
|
||||||
"Enable Passkey": "Bật khóa truy cập",
|
"Enable Passkey": "Bật khóa truy cập",
|
||||||
|
|||||||
@@ -1580,6 +1580,9 @@
|
|||||||
"Enable LinuxDO OAuth": "啟用 LinuxDO OAuth",
|
"Enable LinuxDO OAuth": "啟用 LinuxDO OAuth",
|
||||||
"Enable model performance metrics": "啟用模型效能指標",
|
"Enable model performance metrics": "啟用模型效能指標",
|
||||||
"Enable OIDC": "啟用 OIDC",
|
"Enable OIDC": "啟用 OIDC",
|
||||||
|
"OIDC Display Name": "OIDC 顯示名稱",
|
||||||
|
"e.g. Company SSO": "例如:公司 SSO",
|
||||||
|
"Defaults to \"OIDC\" if left blank": "留空則預設顯示為 \"OIDC\"",
|
||||||
"Enable or disable this channel": "啟用或停用此渠道",
|
"Enable or disable this channel": "啟用或停用此渠道",
|
||||||
"Enable or disable this model": "啟用或停用此模型",
|
"Enable or disable this model": "啟用或停用此模型",
|
||||||
"Enable Passkey": "啟用 Passkey",
|
"Enable Passkey": "啟用 Passkey",
|
||||||
|
|||||||
@@ -1580,6 +1580,9 @@
|
|||||||
"Enable LinuxDO OAuth": "启用 LinuxDO OAuth",
|
"Enable LinuxDO OAuth": "启用 LinuxDO OAuth",
|
||||||
"Enable model performance metrics": "启用模型性能指标",
|
"Enable model performance metrics": "启用模型性能指标",
|
||||||
"Enable OIDC": "启用 OIDC",
|
"Enable OIDC": "启用 OIDC",
|
||||||
|
"OIDC Display Name": "OIDC 显示名称",
|
||||||
|
"e.g. Company SSO": "例如:公司 SSO",
|
||||||
|
"Defaults to \"OIDC\" if left blank": "留空则默认显示为 \"OIDC\"",
|
||||||
"Enable or disable this channel": "启用或禁用此渠道",
|
"Enable or disable this channel": "启用或禁用此渠道",
|
||||||
"Enable or disable this model": "启用或禁用此模型",
|
"Enable or disable this model": "启用或禁用此模型",
|
||||||
"Enable Passkey": "启用 Passkey",
|
"Enable Passkey": "启用 Passkey",
|
||||||
|
|||||||
Reference in New Issue
Block a user