mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-09-21 16:00:51 +00:00
ref:Optimize and improve the panel API key (#13880)
This commit is contained in:
@@ -29,8 +29,6 @@ type APIKeySearch struct {
|
||||
type APIKeyPage struct {
|
||||
Items []APIKeyItem `json:"items"`
|
||||
Total int `json:"total"`
|
||||
Used int `json:"used"`
|
||||
Limit int `json:"limit"`
|
||||
}
|
||||
type APIKeyFields struct {
|
||||
Name string `json:"name"`
|
||||
|
||||
@@ -30,8 +30,6 @@ import (
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
const APIKeyLimit = 20
|
||||
|
||||
var apiKeyMutationMu sync.Mutex
|
||||
|
||||
type APIKeyService struct {
|
||||
@@ -96,14 +94,10 @@ func (s *APIKeyService) Search(c *gin.Context, req dto.APIKeySearch) (*dto.APIKe
|
||||
if config.ApiKey != "" {
|
||||
items = append(items, legacy)
|
||||
}
|
||||
used := 0
|
||||
for _, key := range keys {
|
||||
items = append(items, apiKeyItem(key))
|
||||
if key.Status != "Revoked" {
|
||||
used++
|
||||
}
|
||||
}
|
||||
result := &dto.APIKeyPage{Items: []dto.APIKeyItem{}, Total: len(items), Used: used, Limit: APIKeyLimit}
|
||||
result := &dto.APIKeyPage{Items: []dto.APIKeyItem{}, Total: len(items)}
|
||||
start := (req.Page - 1) * req.PageSize
|
||||
if start >= 0 && start < len(items) {
|
||||
result.Items = items[start:min(start+req.PageSize, len(items))]
|
||||
@@ -187,13 +181,6 @@ func (s *APIKeyService) Create(c *gin.Context, req dto.APIKeyCreate) (*dto.APIKe
|
||||
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return err
|
||||
}
|
||||
var count int64
|
||||
if err = repo.APIKeyOwnerQuery(tx.Model(&model.APIKey{}), owner.Type, owner.ID).Where("status <> ?", "Revoked").Count(&count).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if count >= APIKeyLimit {
|
||||
return buserr.New("ErrAPIKeyLimit")
|
||||
}
|
||||
if err = ensureAPIKeyName(tx, owner, req.Name, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -134,6 +134,7 @@ var WebUrlMap = map[string]struct{}{
|
||||
|
||||
"/settings": {},
|
||||
"/settings/panel": {},
|
||||
"/settings/apikeys": {},
|
||||
"/settings/backupaccount": {},
|
||||
"/settings/license": {},
|
||||
"/settings/about": {},
|
||||
|
||||
@@ -531,7 +531,6 @@ ErrSAML2LocalCredentialsReadonly: "SAML2 usernames and passwords are managed by
|
||||
ErrSAML2PasswordExpirationUnsupported: "SAML2 users do not use the local password expiration policy."
|
||||
|
||||
# API key management
|
||||
ErrAPIKeyLimit: "The limit of 20 unrevoked API keys per user has been reached"
|
||||
ErrAPIKeyConflict: "The API key changed. Refresh and retry; repeated creation must use the original request parameters"
|
||||
ErrAPIKeyNameExists: "An unrevoked API key with this name already exists"
|
||||
ErrAPIKeyNotFound: "The API key does not exist or does not belong to the current user"
|
||||
|
||||
@@ -531,7 +531,6 @@ ErrSAML2LocalCredentialsReadonly: "Los nombres de usuario y las contraseñas SAM
|
||||
ErrSAML2PasswordExpirationUnsupported: "Los usuarios SAML2 no utilizan la política local de caducidad de contraseñas."
|
||||
|
||||
# API key management
|
||||
ErrAPIKeyLimit: "Se alcanzó el límite de 20 claves API no revocadas por usuario"
|
||||
ErrAPIKeyConflict: "La clave API cambió. Actualice y reintente; al repetir la creación, use los parámetros originales"
|
||||
ErrAPIKeyNameExists: "Ya existe una clave API no revocada con este nombre"
|
||||
ErrAPIKeyNotFound: "La clave API no existe o no pertenece al usuario actual"
|
||||
|
||||
@@ -531,7 +531,6 @@ ErrSAML2LocalCredentialsReadonly: "نامهای کاربری و گذرواژ
|
||||
ErrSAML2PasswordExpirationUnsupported: "کاربران SAML2 از سیاست محلی انقضای گذرواژه استفاده نمیکنند."
|
||||
|
||||
# API key management
|
||||
ErrAPIKeyLimit: "به سقف ۲۰ کلید API لغونشده برای هر کاربر رسیدهاید"
|
||||
ErrAPIKeyConflict: "کلید API تغییر کرده است. صفحه را تازهسازی و دوباره تلاش کنید؛ برای تکرار ایجاد، از پارامترهای اصلی استفاده کنید"
|
||||
ErrAPIKeyNameExists: "یک کلید API لغونشده با این نام از قبل وجود دارد"
|
||||
ErrAPIKeyNotFound: "کلید API وجود ندارد یا متعلق به کاربر فعلی نیست"
|
||||
|
||||
@@ -531,7 +531,6 @@ ErrSAML2LocalCredentialsReadonly: "SAML2 のユーザー名とパスワードは
|
||||
ErrSAML2PasswordExpirationUnsupported: "SAML2 ユーザーにはローカルのパスワード有効期限ポリシーが適用されません。"
|
||||
|
||||
# API key management
|
||||
ErrAPIKeyLimit: "ユーザーあたりの未失効 API キー数の上限(20 個)に達しました"
|
||||
ErrAPIKeyConflict: "API キーが変更されました。更新して再試行してください。作成の再送には元のパラメーターを使用してください"
|
||||
ErrAPIKeyNameExists: "同じ名前の未失効 API キーが既に存在します"
|
||||
ErrAPIKeyNotFound: "API キーが存在しないか、現在のユーザーに属していません"
|
||||
|
||||
@@ -531,7 +531,6 @@ ErrSAML2LocalCredentialsReadonly: "SAML2 사용자 이름과 비밀번호는 ID
|
||||
ErrSAML2PasswordExpirationUnsupported: "SAML2 사용자는 로컬 비밀번호 만료 정책을 사용하지 않습니다."
|
||||
|
||||
# API key management
|
||||
ErrAPIKeyLimit: "사용자당 폐기되지 않은 API Key 최대 개수(20개)에 도달했습니다"
|
||||
ErrAPIKeyConflict: "API Key가 변경되었습니다. 새로 고친 후 다시 시도하세요. 생성 요청을 재전송할 때는 원래 매개변수를 사용하세요"
|
||||
ErrAPIKeyNameExists: "같은 이름의 폐기되지 않은 API Key가 이미 있습니다"
|
||||
ErrAPIKeyNotFound: "API Key가 없거나 현재 사용자 소유가 아닙니다"
|
||||
|
||||
@@ -531,7 +531,6 @@ ErrSAML2LocalCredentialsReadonly: "ຊື່ຜູ້ໃຊ້ ແລະລະ
|
||||
ErrSAML2PasswordExpirationUnsupported: "ຜູ້ໃຊ້ SAML2 ບໍ່ໄດ້ໃຊ້ນະໂຍບາຍການໝົດອາຍຸລະຫັດຜ່ານທ້ອງຖິ່ນ."
|
||||
|
||||
# API key management
|
||||
ErrAPIKeyLimit: "ຮອດຂີດຈຳກັດ 20 API Key ທີ່ຍັງບໍ່ຖືກຖອນຕໍ່ຜູ້ໃຊ້ແລ້ວ"
|
||||
ErrAPIKeyConflict: "API Key ມີການປ່ຽນແປງ. ໂຫຼດໃໝ່ແລ້ວລອງອີກ; ໃຊ້ພາລາມິເຕີເດີມເມື່ອສົ່ງຄຳຂໍສ້າງຊ້ຳ"
|
||||
ErrAPIKeyNameExists: "ມີ API Key ທີ່ຍັງບໍ່ຖືກຖອນທີ່ໃຊ້ຊື່ນີ້ແລ້ວ"
|
||||
ErrAPIKeyNotFound: "ບໍ່ພົບ API Key ຫຼືບໍ່ແມ່ນຂອງຜູ້ໃຊ້ປັດຈຸບັນ"
|
||||
|
||||
@@ -531,7 +531,6 @@ ErrSAML2LocalCredentialsReadonly: "Nama pengguna dan kata laluan SAML2 diurus ol
|
||||
ErrSAML2PasswordExpirationUnsupported: "Pengguna SAML2 tidak menggunakan dasar tamat tempoh kata laluan setempat."
|
||||
|
||||
# API key management
|
||||
ErrAPIKeyLimit: "Had 20 kunci API yang belum dibatalkan bagi setiap pengguna telah dicapai"
|
||||
ErrAPIKeyConflict: "Kunci API telah berubah. Muat semula dan cuba lagi; gunakan parameter asal untuk mengulangi penciptaan"
|
||||
ErrAPIKeyNameExists: "Kunci API yang belum dibatalkan dengan nama ini sudah wujud"
|
||||
ErrAPIKeyNotFound: "Kunci API tidak wujud atau bukan milik pengguna semasa"
|
||||
|
||||
@@ -531,7 +531,6 @@ ErrSAML2LocalCredentialsReadonly: "Os nomes de usuário e as senhas SAML2 são g
|
||||
ErrSAML2PasswordExpirationUnsupported: "Os usuários SAML2 não usam a política local de expiração de senha."
|
||||
|
||||
# API key management
|
||||
ErrAPIKeyLimit: "O limite de 20 chaves API não revogadas por usuário foi atingido"
|
||||
ErrAPIKeyConflict: "A chave API foi alterada. Atualize e tente novamente; ao repetir a criação, use os parâmetros originais"
|
||||
ErrAPIKeyNameExists: "Já existe uma chave API não revogada com esse nome"
|
||||
ErrAPIKeyNotFound: "A chave API não existe ou não pertence ao usuário atual"
|
||||
|
||||
@@ -531,7 +531,6 @@ ErrSAML2LocalCredentialsReadonly: "Имена пользователей и па
|
||||
ErrSAML2PasswordExpirationUnsupported: "Для пользователей SAML2 не применяется локальная политика срока действия паролей."
|
||||
|
||||
# API key management
|
||||
ErrAPIKeyLimit: "Достигнут лимит в 20 неотозванных ключей API на пользователя"
|
||||
ErrAPIKeyConflict: "Ключ API изменён. Обновите данные и повторите попытку; при повторном создании используйте исходные параметры"
|
||||
ErrAPIKeyNameExists: "Неотозванный ключ API с таким именем уже существует"
|
||||
ErrAPIKeyNotFound: "Ключ API не существует или не принадлежит текущему пользователю"
|
||||
|
||||
@@ -531,7 +531,6 @@ ErrSAML2LocalCredentialsReadonly: "SAML2 kullanıcı adları ve parolaları kiml
|
||||
ErrSAML2PasswordExpirationUnsupported: "SAML2 kullanıcıları yerel parola süresi dolma ilkesini kullanmaz."
|
||||
|
||||
# API key management
|
||||
ErrAPIKeyLimit: "Kullanıcı başına iptal edilmemiş 20 API anahtarı sınırına ulaşıldı"
|
||||
ErrAPIKeyConflict: "API anahtarı değişti. Yenileyip tekrar deneyin; oluşturma isteğini tekrarlarken ilk parametreleri kullanın"
|
||||
ErrAPIKeyNameExists: "Bu adda iptal edilmemiş bir API anahtarı zaten var"
|
||||
ErrAPIKeyNotFound: "API anahtarı mevcut değil veya geçerli kullanıcıya ait değil"
|
||||
|
||||
@@ -531,7 +531,6 @@ ErrSAML2LocalCredentialsReadonly: "SAML2 使用者名稱和密碼由身分提供
|
||||
ErrSAML2PasswordExpirationUnsupported: "SAML2 使用者不使用本機密碼到期原則。"
|
||||
|
||||
# API key management
|
||||
ErrAPIKeyLimit: "已達每個使用者 20 把未撤銷 API Key 的上限"
|
||||
ErrAPIKeyConflict: "API Key 已變更,請重新整理後重試;重複建立請使用原請求參數"
|
||||
ErrAPIKeyNameExists: "已存在同名且未撤銷的 API Key"
|
||||
ErrAPIKeyNotFound: "API Key 不存在或不屬於目前使用者"
|
||||
|
||||
@@ -531,7 +531,6 @@ ErrSAML2LocalCredentialsReadonly: "SAML2 用户名和密码由身份提供商管
|
||||
ErrSAML2PasswordExpirationUnsupported: "SAML2 用户不使用本地密码过期策略。"
|
||||
|
||||
# API key management
|
||||
ErrAPIKeyLimit: "已达到每个用户 20 把未撤销 API Key 的上限"
|
||||
ErrAPIKeyConflict: "API Key 已发生变化,请刷新后重试;重复创建请使用原请求参数"
|
||||
ErrAPIKeyNameExists: "已存在同名的未撤销 API Key"
|
||||
ErrAPIKeyNotFound: "API Key 不存在或不属于当前用户"
|
||||
|
||||
@@ -20,8 +20,6 @@ export namespace APIKey {
|
||||
export interface Search {
|
||||
items: Item[];
|
||||
total: number;
|
||||
used: number;
|
||||
limit: number;
|
||||
}
|
||||
export interface Created {
|
||||
item: Item;
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
v-model="visible"
|
||||
:header="item ? $t('commons.button.edit') : $t('apiKeyManagement.create')"
|
||||
size="min(640px, 100vw)"
|
||||
:auto-close="false"
|
||||
:confirm-before-close="true"
|
||||
@before-close="beforeClose"
|
||||
>
|
||||
@@ -25,9 +24,6 @@
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<p v-if="created.allowAppBinding" class="input-help mb-4 leading-5">
|
||||
{{ $t('apiKeyManagement.bindingException') }}
|
||||
</p>
|
||||
<el-checkbox v-if="secret" v-model="saved">{{ $t('apiKeyManagement.saved') }}</el-checkbox>
|
||||
</template>
|
||||
<el-form v-else ref="formRef" :model="form" :rules="rules" label-position="top" @submit.prevent="submit">
|
||||
@@ -131,6 +127,7 @@
|
||||
type="datetime"
|
||||
:disabled="busy || uncertain"
|
||||
:clearable="false"
|
||||
:show-now="false"
|
||||
class="!w-full"
|
||||
/>
|
||||
</el-form-item>
|
||||
@@ -138,7 +135,6 @@
|
||||
<el-checkbox v-model="form.allowAppBinding" :disabled="busy || uncertain">
|
||||
{{ $t('apiKeyManagement.allowAppBinding') }}
|
||||
</el-checkbox>
|
||||
<span class="input-help mt-2 leading-5">{{ $t('apiKeyManagement.bindingException') }}</span>
|
||||
<span class="input-help mt-2 leading-5">{{ $t('apiKeyManagement.bindingDisableHelp') }}</span>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
@@ -328,6 +324,15 @@ const submit = async () => {
|
||||
if (ticket === editorVersion) busy.value = false;
|
||||
}
|
||||
};
|
||||
watch(
|
||||
ipMode,
|
||||
async () => {
|
||||
// Clear after the hidden input's pending blur validation has settled.
|
||||
await nextTick();
|
||||
formRef.value?.clearValidate('ipWhiteList');
|
||||
},
|
||||
{ flush: 'post' },
|
||||
);
|
||||
watch(
|
||||
() => globalStore.isLogin,
|
||||
(loggedIn) => {
|
||||
|
||||
@@ -2,14 +2,13 @@ import { getFuLocaleMessage } from '@/lang/fu';
|
||||
|
||||
const message = {
|
||||
apiKeyManagement: {
|
||||
title: 'Panel API',
|
||||
title: 'API Key',
|
||||
manage: 'Manage API Keys',
|
||||
personalEntry: 'Manage your keys, access conditions, and APP binding in Settings.',
|
||||
create: 'Create API Key',
|
||||
legacy: 'Legacy Key',
|
||||
identifier: 'Key identifier',
|
||||
details: 'API Key details',
|
||||
quota: '{0} / {1} new keys (disabled and expired keys count; the legacy key is excluded)',
|
||||
statusEnable: 'Enabled',
|
||||
statusDisable: 'Disabled',
|
||||
statusRevoked: 'Revoked',
|
||||
@@ -22,17 +21,16 @@ const message = {
|
||||
never: 'Never expires',
|
||||
futureExpiry: 'Select an expiration time in the future.',
|
||||
allowAppBinding: 'Allow APP QR binding',
|
||||
bindingException:
|
||||
'Allow this key to be delivered again to a phone through a one-time QR code generated in your signed-in panel session.',
|
||||
bindingDisableHelp:
|
||||
'Turning off QR binding blocks future delivery. Existing connections remain valid until the key is disabled, revoked, or expires.',
|
||||
'Disabling QR binding prevents new device bindings. Devices already bound can continue to use the key until it is disabled, revoked, or expires.',
|
||||
saveSecret: 'Copy and save this secret now. After closing, ordinary key management cannot display it again.',
|
||||
saved: 'I have saved the secret',
|
||||
alreadyCreated:
|
||||
'This key was already created. Its secret will not be shown again. If the original response was lost, revoke and recreate it, or use QR binding if allowed.',
|
||||
uncertain:
|
||||
'The request result is uncertain. Retry with the same details to avoid duplicate keys, or close and check the list before creating another.',
|
||||
legacyHelp: 'The legacy key keeps existing connections compatible. Its switch and reset affect only that key.',
|
||||
legacyHelp:
|
||||
'The legacy key supports legacy API connections. Enabling, disabling, or resetting it affects only this key.',
|
||||
showLegacy: 'Show legacy secret',
|
||||
revoke: 'Revoke',
|
||||
revokeConfirm:
|
||||
@@ -2925,7 +2923,7 @@ const message = {
|
||||
apiKeyValidityTime: 'Validity period of interface key',
|
||||
apiKeyValidityTimeEgs: 'Validity period of interface key (in minutes)',
|
||||
apiKeyValidityTimeHelper:
|
||||
'The interface timestamp is valid if its difference from the current timestamp (in minutes) is within the allowed range. A value of 0 disables verification.',
|
||||
'The request signature validity period, in minutes, measured from the request timestamp. Set to 0 to disable timestamp validation.',
|
||||
apiKeyReset: 'Interface key reset',
|
||||
apiKeyResetHelper: 'the associated key service will become invalid. Please add a new key to the service',
|
||||
confDockerProxy: 'Configure docker proxy',
|
||||
|
||||
@@ -2,14 +2,13 @@ import { getFuLocaleMessage } from '@/lang/fu';
|
||||
|
||||
const message = {
|
||||
apiKeyManagement: {
|
||||
title: 'API del panel',
|
||||
title: 'API Key',
|
||||
manage: 'Gestionar claves API',
|
||||
personalEntry: 'Gestiona tus claves, condiciones de acceso y vinculación de la APP en Ajustes.',
|
||||
create: 'Crear clave API',
|
||||
legacy: 'Clave anterior',
|
||||
identifier: 'Identificador de clave',
|
||||
details: 'Detalles de la clave API',
|
||||
quota: '{0} / {1} claves nuevas (incluye desactivadas y caducadas; excluye la clave anterior)',
|
||||
statusEnable: 'Activada',
|
||||
statusDisable: 'Desactivada',
|
||||
statusRevoked: 'Revocada',
|
||||
@@ -22,10 +21,8 @@ const message = {
|
||||
never: 'Nunca caduca',
|
||||
futureExpiry: 'Selecciona una fecha de caducidad futura.',
|
||||
allowAppBinding: 'Permitir vinculación de APP por QR',
|
||||
bindingException:
|
||||
'Permite volver a entregar esta clave al teléfono mediante un QR de un solo uso generado desde tu sesión del panel.',
|
||||
bindingDisableHelp:
|
||||
'Desactivar la vinculación impide nuevas entregas. Las conexiones existentes siguen siendo válidas hasta desactivar, revocar o caducar la clave.',
|
||||
'Desactivar la vinculación por QR impide nuevas vinculaciones. Los dispositivos ya vinculados pueden seguir usando la clave hasta que se desactive, se revoque o caduque.',
|
||||
saveSecret: 'Copia y guarda la clave ahora. Tras cerrar, la gestión normal no podrá mostrarla de nuevo.',
|
||||
saved: 'He guardado la clave',
|
||||
alreadyCreated:
|
||||
@@ -33,7 +30,7 @@ const message = {
|
||||
uncertain:
|
||||
'El resultado es incierto. Reintenta con los mismos datos para evitar duplicados, o cierra y revisa la lista antes de crear otra.',
|
||||
legacyHelp:
|
||||
'La clave anterior mantiene la compatibilidad con las conexiones existentes. Su interruptor y restablecimiento solo afectan a esa clave.',
|
||||
'La clave anterior mantiene la compatibilidad con las conexiones API heredadas. Activarla, desactivarla o restablecerla solo afecta a esta clave.',
|
||||
showLegacy: 'Mostrar clave anterior',
|
||||
revoke: 'Revocar',
|
||||
revokeConfirm:
|
||||
@@ -2976,7 +2973,7 @@ const message = {
|
||||
apiKeyValidityTime: 'Periodo de validez de la clave de interfaz',
|
||||
apiKeyValidityTimeEgs: 'Periodo de validez de la clave de interfaz (en minutos)',
|
||||
apiKeyValidityTimeHelper:
|
||||
'La marca de tiempo de la interfaz es válida si la diferencia con la actual (en minutos) está dentro del rango permitido. Un valor 0 desactiva la verificación.',
|
||||
'Periodo de validez de la firma de la solicitud, en minutos, contado desde la marca de tiempo de la solicitud. El valor 0 desactiva la validación de la marca de tiempo.',
|
||||
apiKeyReset: 'Restablecer clave de interfaz',
|
||||
apiKeyResetHelper:
|
||||
'El servicio asociado con la clave quedará inválido. Por favor añade una nueva clave al servicio',
|
||||
|
||||
@@ -2,14 +2,13 @@ import { getFuLocaleMessage } from '@/lang/fu';
|
||||
|
||||
const message = {
|
||||
apiKeyManagement: {
|
||||
title: 'API پنل',
|
||||
title: 'API Key',
|
||||
manage: 'مدیریت کلیدهای API',
|
||||
personalEntry: 'کلیدها، شرایط دسترسی و اتصال برنامه را در تنظیمات مدیریت کنید.',
|
||||
create: 'ایجاد کلید API',
|
||||
legacy: 'کلید قدیمی',
|
||||
identifier: 'شناسه کلید',
|
||||
details: 'جزئیات کلید API',
|
||||
quota: 'کلیدهای جدید: {0} / {1} (کلیدهای غیرفعال و منقضی حساب میشوند؛ کلید قدیمی مستثنا است)',
|
||||
statusEnable: 'فعال',
|
||||
statusDisable: 'غیرفعال',
|
||||
statusRevoked: 'لغوشده',
|
||||
@@ -22,10 +21,8 @@ const message = {
|
||||
never: 'بدون انقضا',
|
||||
futureExpiry: 'زمان انقضایی در آینده انتخاب کنید.',
|
||||
allowAppBinding: 'اجازه اتصال برنامه با QR',
|
||||
bindingException:
|
||||
'اجازه میدهد این کلید دوباره از طریق QR یکبارمصرف ساختهشده در نشست واردشده پنل به تلفن تحویل شود.',
|
||||
bindingDisableHelp:
|
||||
'خاموش کردن اتصال QR فقط تحویلهای بعدی را مسدود میکند. اتصالهای موجود تا غیرفعالسازی، لغو یا انقضای کلید معتبر میمانند.',
|
||||
'با غیرفعال کردن اتصال از طریق QR، اتصال دستگاههای جدید متوقف میشود. دستگاههای متصل میتوانند تا زمان غیرفعالسازی، لغو یا انقضای کلید به استفاده از آن ادامه دهند.',
|
||||
saveSecret: 'اکنون کلید را کپی و ذخیره کنید. پس از بستن، مدیریت عادی نمیتواند کلید کامل را دوباره نشان دهد.',
|
||||
saved: 'کلید را ذخیره کردم',
|
||||
alreadyCreated:
|
||||
@@ -33,7 +30,7 @@ const message = {
|
||||
uncertain:
|
||||
'نتیجه درخواست نامشخص است. برای جلوگیری از تکرار، با همان اطلاعات دوباره تلاش کنید یا ببندید و پیش از ایجاد کلید دیگر فهرست را بررسی کنید.',
|
||||
legacyHelp:
|
||||
'کلید قدیمی سازگاری اتصالهای موجود را حفظ میکند. تغییر وضعیت و بازنشانی فقط بر همان کلید اثر دارد.',
|
||||
'کلید قدیمی سازگاری با اتصالهای API قدیمی را حفظ میکند. فعالسازی، غیرفعالسازی و بازنشانی فقط بر همین کلید اثر میگذارند.',
|
||||
showLegacy: 'نمایش کلید قدیمی',
|
||||
revoke: 'لغو',
|
||||
revokeConfirm:
|
||||
@@ -2896,7 +2893,7 @@ const message = {
|
||||
apiKeyValidityTime: 'مدت اعتبار کلید رابط',
|
||||
apiKeyValidityTimeEgs: 'مدت اعتبار کلید رابط (به دقیقه)',
|
||||
apiKeyValidityTimeHelper:
|
||||
'مهر زمانی رابط در صورتی معتبر است که اختلاف آن با مهر زمانی فعلی (به دقیقه) در محدوده مجاز باشد. مقدار ۰ تأیید را غیرفعال میکند.',
|
||||
'مدت اعتبار امضای درخواست، بر حسب دقیقه، از مهر زمانی درخواست محاسبه میشود. با تنظیم مقدار 0، اعتبارسنجی مهر زمانی غیرفعال میشود.',
|
||||
apiKeyReset: 'بازنشانی کلید رابط',
|
||||
apiKeyResetHelper: 'سرویس کلید مرتبط بیاعتبار میشود. لطفاً کلید جدیدی به سرویس اضافه کنید',
|
||||
confDockerProxy: 'پیکربندی پروکسی Docker',
|
||||
|
||||
@@ -2,14 +2,13 @@ import { getFuLocaleMessage } from '@/lang/fu';
|
||||
|
||||
const message = {
|
||||
apiKeyManagement: {
|
||||
title: 'パネル API',
|
||||
title: 'API Key',
|
||||
manage: 'API Key を管理',
|
||||
personalEntry: '設定で自分のキー、アクセス条件、APP の QR 連携を管理します。',
|
||||
create: 'API Key を作成',
|
||||
legacy: '旧版キー',
|
||||
identifier: 'キー識別子',
|
||||
details: 'API Key の詳細',
|
||||
quota: '新規キー {0} / {1}(無効・期限切れも含む。旧版キーは除く)',
|
||||
statusEnable: '有効',
|
||||
statusDisable: '無効',
|
||||
statusRevoked: '失効済み',
|
||||
@@ -22,16 +21,15 @@ const message = {
|
||||
never: '無期限',
|
||||
futureExpiry: '現在より後の有効期限を選択してください。',
|
||||
allowAppBinding: 'APP の QR 連携を許可',
|
||||
bindingException:
|
||||
'ログイン中のパネルで生成した使い捨て QR コードを通じて、このキーを再び携帯端末に渡すことを許可します。',
|
||||
bindingDisableHelp:
|
||||
'オフにすると今後の QR 配布のみ停止します。既存の接続はキーの無効化、失効、期限切れまで利用できます。',
|
||||
'QR 連携を無効にすると、新規の連携を受け付けなくなります。連携済みの端末は、キーが無効化、失効、または期限切れになるまで引き続き利用できます。',
|
||||
saveSecret: '今すぐキーをコピーして保存してください。閉じると通常のキー管理から完全なキーを再表示できません。',
|
||||
saved: 'キーを保存しました',
|
||||
alreadyCreated:
|
||||
'このキーは作成済みで、再表示できません。最初の応答を受け取れなかった場合は失効させて再作成するか、許可されていれば QR を使用してください。',
|
||||
uncertain: 'リクエスト結果が不明です。重複を避けるため同じ設定で再試行するか、閉じて一覧を確認してください。',
|
||||
legacyHelp: '旧版キーは既存接続との互換性を保ちます。切り替えとリセットはこのキーだけに影響します。',
|
||||
legacyHelp:
|
||||
'旧版キーは従来の API 接続との互換性を維持します。有効化、無効化、リセットはこのキーにのみ適用されます。',
|
||||
showLegacy: '旧版キーを表示',
|
||||
revoke: '失効させる',
|
||||
revokeConfirm:
|
||||
@@ -3249,7 +3247,8 @@ const message = {
|
||||
componentSize: 'コンポーネントサイズ',
|
||||
apiKeyValidityTime: 'インターフェースキー有効期間',
|
||||
apiKeyValidityTimeEgs: 'インターフェースキー有効期間(分)',
|
||||
apiKeyValidityTimeHelper: '現在時刻との差(分)が許容範囲内なら有効です。0 は検証無効。',
|
||||
apiKeyValidityTimeHelper:
|
||||
'リクエストのタイムスタンプから計算する署名の有効期間を分単位で指定します。0 に設定すると、タイムスタンプの検証を行いません。',
|
||||
scope: '範囲',
|
||||
public: 'パブリック',
|
||||
publicHelper: 'パブリック型バックアップアカウントは各子ノードへ同期され共同利用できます',
|
||||
|
||||
@@ -2,14 +2,13 @@ import { getFuLocaleMessage } from '@/lang/fu';
|
||||
|
||||
const message = {
|
||||
apiKeyManagement: {
|
||||
title: '패널 API',
|
||||
title: 'API Key',
|
||||
manage: 'API Key 관리',
|
||||
personalEntry: '설정에서 내 키, 접근 조건 및 APP QR 연결을 관리합니다.',
|
||||
create: 'API Key 만들기',
|
||||
legacy: '기존 키',
|
||||
identifier: '키 식별자',
|
||||
details: 'API Key 상세 정보',
|
||||
quota: '새 키 {0} / {1} (비활성 및 만료 키 포함, 기존 키 제외)',
|
||||
statusEnable: '활성',
|
||||
statusDisable: '비활성',
|
||||
statusRevoked: '폐기됨',
|
||||
@@ -22,16 +21,16 @@ const message = {
|
||||
never: '만료 없음',
|
||||
futureExpiry: '현재 이후의 만료 시간을 선택하세요.',
|
||||
allowAppBinding: 'APP QR 연결 허용',
|
||||
bindingException: '로그인한 패널 세션에서 만든 일회용 QR 코드를 통해 이 키를 휴대폰에 다시 전달할 수 있습니다.',
|
||||
bindingDisableHelp:
|
||||
'QR 연결을 끄면 이후 전달만 차단됩니다. 기존 연결은 키를 비활성화하거나 폐기하거나 만료될 때까지 유효합니다.',
|
||||
'QR 연결을 비활성화하면 새로운 기기를 연결할 수 없습니다. 이미 연결된 기기는 키가 비활성화, 폐기 또는 만료될 때까지 계속 사용할 수 있습니다.',
|
||||
saveSecret: '지금 키를 복사하여 저장하세요. 닫으면 일반 키 관리에서 전체 키를 다시 표시할 수 없습니다.',
|
||||
saved: '키를 저장했습니다',
|
||||
alreadyCreated:
|
||||
'이 키는 이미 생성되어 다시 표시할 수 없습니다. 원래 응답을 잃었다면 폐기 후 다시 만들거나 허용된 QR 연결을 사용하세요.',
|
||||
uncertain:
|
||||
'요청 결과를 확인할 수 없습니다. 중복 생성을 피하려면 같은 설정으로 재시도하거나 닫고 목록을 확인하세요.',
|
||||
legacyHelp: '기존 키는 기존 연결의 호환성을 유지합니다. 스위치와 재설정은 이 키에만 적용됩니다.',
|
||||
legacyHelp:
|
||||
'기존 키는 이전에 설정한 API 연결과의 호환성을 유지합니다. 활성화, 비활성화 및 재설정은 이 키에만 적용됩니다.',
|
||||
showLegacy: '기존 키 표시',
|
||||
revoke: '폐기',
|
||||
revokeConfirm:
|
||||
@@ -2860,7 +2859,7 @@ const message = {
|
||||
apiKeyValidityTime: '인터페이스 키 유효 기간',
|
||||
apiKeyValidityTimeEgs: '인터페이스 키 유효 기간 (분 단위)',
|
||||
apiKeyValidityTimeHelper:
|
||||
'인터페이스 타임스탬프가 현재 타임스탬프와의 차이가 허용 범위 내에 있을 경우 유효합니다. 값이 0이면 검증이 비활성화됩니다.',
|
||||
'요청 타임스탬프부터 계산되는 요청 서명의 유효 기간이며, 단위는 분입니다. 0으로 설정하면 타임스탬프를 검사하지 않습니다.',
|
||||
apiKeyReset: '인터페이스 키 재설정',
|
||||
apiKeyResetHelper: '연관된 키 서비스가 무효화됩니다. 서비스에 새 키를 추가하십시오.',
|
||||
confDockerProxy: 'Docker 프록시 구성',
|
||||
|
||||
@@ -2,14 +2,13 @@ import { getFuLocaleMessage } from '@/lang/fu';
|
||||
|
||||
const message = {
|
||||
apiKeyManagement: {
|
||||
title: 'API ພາເນລ',
|
||||
title: 'API Key',
|
||||
manage: 'ຈັດການ API Key',
|
||||
personalEntry: 'ຈັດການກະແຈ, ເງື່ອນໄຂເຂົ້າໃຊ້ ແລະ ການຜູກ APP ໃນການຕັ້ງຄ່າ.',
|
||||
create: 'ສ້າງ API Key',
|
||||
legacy: 'ກະແຈເກົ່າ',
|
||||
identifier: 'ລະຫັດກະແຈ',
|
||||
details: 'ລາຍລະອຽດ API Key',
|
||||
quota: 'ກະແຈໃໝ່ {0} / {1} (ນັບກະແຈປິດໃຊ້ ແລະ ໝົດອາຍຸ; ບໍ່ນັບກະແຈເກົ່າ)',
|
||||
statusEnable: 'ເປີດໃຊ້',
|
||||
statusDisable: 'ປິດໃຊ້',
|
||||
statusRevoked: 'ຖອນແລ້ວ',
|
||||
@@ -22,14 +21,14 @@ const message = {
|
||||
never: 'ບໍ່ໝົດອາຍຸ',
|
||||
futureExpiry: 'ເລືອກເວລາໝົດອາຍຸໃນອະນາຄົດ.',
|
||||
allowAppBinding: 'ອະນຸຍາດຜູກ APP ຜ່ານ QR',
|
||||
bindingException: 'ອະນຸຍາດສົ່ງກະແຈນີ້ໃຫ້ໂທລະສັບອີກຄັ້ງ ຜ່ານ QR ໃຊ້ຄັ້ງດຽວທີ່ສ້າງໃນເຊດຊັນແຜງທີ່ເຂົ້າລະບົບ.',
|
||||
bindingDisableHelp:
|
||||
'ປິດການຜູກ QR ຈະຢຸດສະເພາະການສົ່ງຄັ້ງໃໝ່. ການເຊື່ອມຕໍ່ເກົ່າຍັງໃຊ້ໄດ້ຈົນກະແຈຖືກປິດ, ຖອນ ຫຼື ໝົດອາຍຸ.',
|
||||
'ເມື່ອປິດການຜູກມັດຜ່ານ QR, ລະບົບຈະບໍ່ຮັບການຜູກມັດໃໝ່. ອຸປະກອນທີ່ຜູກມັດແລ້ວຍັງສາມາດໃຊ້ງານໄດ້ ຈົນກວ່າກະແຈຈະຖືກປິດໃຊ້, ຖອນ ຫຼື ໝົດອາຍຸ.',
|
||||
saveSecret: 'ຄັດລອກ ແລະ ບັນທຶກກະແຈດຽວນີ້. ຫຼັງປິດແລ້ວ ການຈັດການປົກກະຕິຈະບໍ່ສະແດງກະແຈເຕັມອີກ.',
|
||||
saved: 'ຂ້ອຍບັນທຶກກະແຈແລ້ວ',
|
||||
alreadyCreated: 'ກະແຈນີ້ສ້າງແລ້ວ ແລະ ບໍ່ສະແດງອີກ. ຖ້າຄຳຕອບເກົ່າຫາຍ ໃຫ້ຖອນແລ້ວສ້າງໃໝ່ ຫຼື ໃຊ້ QR ຖ້າອະນຸຍາດ.',
|
||||
uncertain: 'ຍັງບໍ່ແນ່ໃຈຜົນຄຳຮ້ອງ. ລອງໃໝ່ດ້ວຍຂໍ້ມູນເດີມເພື່ອບໍ່ສ້າງຊ້ຳ ຫຼື ປິດແລ້ວກວດລາຍການກ່ອນສ້າງອີກ.',
|
||||
legacyHelp: 'ກະແຈເກົ່າຮັກສາການເຊື່ອມຕໍ່ເກົ່າ. ສະວິດ ແລະ ການຣີເຊັດມີຜົນສະເພາະກະແຈນັ້ນ.',
|
||||
legacyHelp:
|
||||
'ກະແຈເກົ່າໃຊ້ເພື່ອຮອງຮັບການເຊື່ອມຕໍ່ API ເກົ່າ. ການເປີດໃຊ້, ປິດໃຊ້ ແລະ ຣີເຊັດ ມີຜົນສະເພາະກະແຈນີ້ເທົ່ານັ້ນ.',
|
||||
showLegacy: 'ສະແດງກະແຈເກົ່າ',
|
||||
revoke: 'ຖອນ',
|
||||
revokeConfirm:
|
||||
@@ -2841,7 +2840,7 @@ const message = {
|
||||
apiKeyValidityTime: 'ໄລຍະເວລາທີ່ API key ໃຊ້ງານໄດ້',
|
||||
apiKeyValidityTimeEgs: 'ໄລຍະເວລາທີ່ API key ໃຊ້ງານໄດ້ (ນາທີ)',
|
||||
apiKeyValidityTimeHelper:
|
||||
'timestamp ຂອງ API ຈະໃຊ້ງານໄດ້ຫາກຄວາມແຕກຕ່າງຈາກເວລາປັດຈຸບັນ (ນາທີ) ຢູ່ໃນຊ່ວງທີ່ກຳນົດ. ຄ່າ 0 ໝາຍເຖິງປິດການກວດສອບ.',
|
||||
'ໄລຍະເວລາທີ່ລາຍເຊັນຂອງຄຳຮ້ອງຂໍມີຜົນ, ໜ່ວຍເປັນນາທີ, ນັບຈາກ timestamp ຂອງຄຳຮ້ອງຂໍ. ຕັ້ງເປັນ 0 ເພື່ອປິດການກວດສອບ timestamp.',
|
||||
apiKeyReset: 'ຕັ້ງຄ່າ API key ໃໝ່',
|
||||
apiKeyResetHelper: 'ບໍລິການຄີທີ່ກ່ຽວຂ້ອງຈະໃຊ້ງານບໍ່ໄດ້. ກະລຸນາເພີ່ມຄີໃໝ່ໃຫ້ກັບບໍລິການ',
|
||||
confDockerProxy: 'ຕັ້ງຄ່າ Docker Proxy',
|
||||
|
||||
@@ -2,14 +2,13 @@ import { getFuLocaleMessage } from '@/lang/fu';
|
||||
|
||||
const message = {
|
||||
apiKeyManagement: {
|
||||
title: 'API Panel',
|
||||
title: 'API Key',
|
||||
manage: 'Urus kunci API',
|
||||
personalEntry: 'Urus kunci, syarat akses dan pautan APP anda dalam Tetapan.',
|
||||
create: 'Cipta kunci API',
|
||||
legacy: 'Kunci lama',
|
||||
identifier: 'Pengecam kunci',
|
||||
details: 'Butiran kunci API',
|
||||
quota: '{0} / {1} kunci baharu (termasuk kunci dinyahdayakan dan tamat tempoh; tidak termasuk kunci lama)',
|
||||
statusEnable: 'Didayakan',
|
||||
statusDisable: 'Dinyahdayakan',
|
||||
statusRevoked: 'Dibatalkan',
|
||||
@@ -22,10 +21,8 @@ const message = {
|
||||
never: 'Tidak tamat tempoh',
|
||||
futureExpiry: 'Pilih masa luput pada masa hadapan.',
|
||||
allowAppBinding: 'Benarkan pautan APP melalui QR',
|
||||
bindingException:
|
||||
'Benarkan kunci ini dihantar semula ke telefon melalui kod QR sekali guna yang dijana dalam sesi panel anda yang telah log masuk.',
|
||||
bindingDisableHelp:
|
||||
'Mematikan pautan QR menyekat penghantaran baharu sahaja. Sambungan sedia ada kekal sah sehingga kunci dinyahdayakan, dibatalkan atau tamat tempoh.',
|
||||
'Menyahdayakan pautan QR menghalang pautan peranti baharu. Peranti yang telah dipautkan boleh terus digunakan sehingga kunci dinyahdayakan, dibatalkan atau tamat tempoh.',
|
||||
saveSecret:
|
||||
'Salin dan simpan kunci sekarang. Selepas ditutup, pengurusan biasa tidak dapat memaparkan kunci penuh lagi.',
|
||||
saved: 'Saya telah menyimpan kunci',
|
||||
@@ -34,7 +31,7 @@ const message = {
|
||||
uncertain:
|
||||
'Hasil permintaan belum pasti. Cuba semula dengan butiran sama untuk mengelakkan pendua, atau tutup dan semak senarai sebelum mencipta lagi.',
|
||||
legacyHelp:
|
||||
'Kunci lama mengekalkan keserasian sambungan sedia ada. Suis dan tetapan semulanya hanya mempengaruhi kunci itu.',
|
||||
'Kunci lama mengekalkan keserasian dengan sambungan API terdahulu. Mendayakan, menyahdayakan atau menetapkan semula hanya mempengaruhi kunci ini.',
|
||||
showLegacy: 'Tunjukkan kunci lama',
|
||||
revoke: 'Batalkan',
|
||||
revokeConfirm:
|
||||
@@ -2972,7 +2969,7 @@ const message = {
|
||||
apiKeyValidityTime: 'Tempoh sah kunci antara muka',
|
||||
apiKeyValidityTimeEgs: 'Tempoh sah kunci antara muka (dalam minit)',
|
||||
apiKeyValidityTimeHelper:
|
||||
'Cap waktu antara muka sah jika perbezaannya dengan cap waktu semasa (dalam minit) berada dalam julat yang dibenarkan. Nilai 0 melumpuhkan pengesahan.',
|
||||
'Tempoh sah tandatangan permintaan, dalam minit, dikira dari cap masa permintaan. Tetapkan kepada 0 untuk menyahdayakan pengesahan cap masa.',
|
||||
apiKeyReset: 'Tetapkan semula kunci antara muka',
|
||||
apiKeyResetHelper:
|
||||
'Perkhidmatan kunci yang berkaitan akan menjadi tidak sah. Sila tambah kunci baru pada perkhidmatan',
|
||||
|
||||
@@ -2,14 +2,13 @@ import { getFuLocaleMessage } from '@/lang/fu';
|
||||
|
||||
const message = {
|
||||
apiKeyManagement: {
|
||||
title: 'API do painel',
|
||||
title: 'API Key',
|
||||
manage: 'Gerenciar chaves API',
|
||||
personalEntry: 'Gerencie suas chaves, condições de acesso e vínculo do APP nas Configurações.',
|
||||
create: 'Criar chave API',
|
||||
legacy: 'Chave legada',
|
||||
identifier: 'Identificador da chave',
|
||||
details: 'Detalhes da chave API',
|
||||
quota: '{0} / {1} novas chaves (inclui desativadas e expiradas; exclui a chave legada)',
|
||||
statusEnable: 'Ativada',
|
||||
statusDisable: 'Desativada',
|
||||
statusRevoked: 'Revogada',
|
||||
@@ -22,10 +21,8 @@ const message = {
|
||||
never: 'Nunca expira',
|
||||
futureExpiry: 'Selecione uma data de expiração futura.',
|
||||
allowAppBinding: 'Permitir vínculo do APP por QR',
|
||||
bindingException:
|
||||
'Permite entregar esta chave novamente ao celular por um QR de uso único gerado na sua sessão autenticada do painel.',
|
||||
bindingDisableHelp:
|
||||
'Desativar o vínculo impede novas entregas. Conexões existentes continuam válidas até a chave ser desativada, revogada ou expirar.',
|
||||
'Desativar a vinculação por QR impede novas vinculações. Os dispositivos já vinculados podem continuar usando a chave até que ela seja desativada, revogada ou expire.',
|
||||
saveSecret: 'Copie e salve a chave agora. Após fechar, o gerenciamento normal não poderá exibi-la novamente.',
|
||||
saved: 'Salvei a chave',
|
||||
alreadyCreated:
|
||||
@@ -33,7 +30,7 @@ const message = {
|
||||
uncertain:
|
||||
'O resultado é incerto. Tente novamente com os mesmos dados para evitar duplicações ou feche e verifique a lista antes de criar outra.',
|
||||
legacyHelp:
|
||||
'A chave legada mantém conexões existentes compatíveis. Ativar, desativar ou redefinir afeta somente essa chave.',
|
||||
'A chave legada mantém a compatibilidade com as conexões API legadas. Ativar, desativar ou redefinir afeta somente esta chave.',
|
||||
showLegacy: 'Mostrar chave legada',
|
||||
revoke: 'Revogar',
|
||||
revokeConfirm:
|
||||
@@ -3330,7 +3327,7 @@ const message = {
|
||||
apiKeyValidityTime: 'Período de validade da chave da API',
|
||||
apiKeyValidityTimeEgs: 'Período de validade da chave da API (em minutos)',
|
||||
apiKeyValidityTimeHelper:
|
||||
'O timestamp da interface é válido se a diferença para o timestamp atual (em minutos) estiver no intervalo permitido. Valor 0 desativa a verificação.',
|
||||
'Período de validade da assinatura da solicitação, em minutos, contado a partir do timestamp da solicitação. Defina como 0 para desativar a validação do timestamp.',
|
||||
scope: 'Escopo',
|
||||
public: 'Público',
|
||||
publicHelper: 'Contas de backup públicas serão sincronizadas para cada subnó e podem ser usadas em conjunto',
|
||||
|
||||
@@ -2,14 +2,13 @@ import { getFuLocaleMessage } from '@/lang/fu';
|
||||
|
||||
const message = {
|
||||
apiKeyManagement: {
|
||||
title: 'API панели',
|
||||
title: 'API Key',
|
||||
manage: 'Управление API-ключами',
|
||||
personalEntry: 'Управляйте своими ключами, условиями доступа и привязкой приложения в настройках.',
|
||||
create: 'Создать API-ключ',
|
||||
legacy: 'Прежний ключ',
|
||||
identifier: 'Идентификатор ключа',
|
||||
details: 'Сведения об API-ключе',
|
||||
quota: 'Новых ключей: {0} / {1} (отключённые и истёкшие учитываются; прежний ключ исключён)',
|
||||
statusEnable: 'Включён',
|
||||
statusDisable: 'Отключён',
|
||||
statusRevoked: 'Отозван',
|
||||
@@ -22,10 +21,8 @@ const message = {
|
||||
never: 'Бессрочно',
|
||||
futureExpiry: 'Выберите время окончания в будущем.',
|
||||
allowAppBinding: 'Разрешить привязку приложения по QR',
|
||||
bindingException:
|
||||
'Разрешает повторно передавать ключ телефону через одноразовый QR-код, созданный в вашей авторизованной сессии панели.',
|
||||
bindingDisableHelp:
|
||||
'Отключение привязки блокирует только будущую передачу. Существующие подключения действуют до отключения, отзыва или истечения ключа.',
|
||||
'Отключение привязки по QR-коду запрещает новые привязки. Уже привязанные устройства могут продолжать работу, пока ключ не будет отключён, отозван или не истечёт срок его действия.',
|
||||
saveSecret:
|
||||
'Скопируйте и сохраните ключ сейчас. После закрытия обычное управление не сможет показать полный ключ снова.',
|
||||
saved: 'Ключ сохранён',
|
||||
@@ -34,7 +31,7 @@ const message = {
|
||||
uncertain:
|
||||
'Результат запроса неизвестен. Повторите с теми же данными, чтобы избежать дублей, или закройте и проверьте список.',
|
||||
legacyHelp:
|
||||
'Прежний ключ сохраняет совместимость подключений. Его переключение и сброс влияют только на этот ключ.',
|
||||
'Прежний ключ обеспечивает совместимость с ранее настроенными API-подключениями. Включение, отключение и сброс действуют только на этот ключ.',
|
||||
showLegacy: 'Показать прежний ключ',
|
||||
revoke: 'Отозвать',
|
||||
revokeConfirm:
|
||||
@@ -2944,7 +2941,7 @@ const message = {
|
||||
apiKeyValidityTime: 'Срок действия ключа интерфейса',
|
||||
apiKeyValidityTimeEgs: 'Срок действия ключа интерфейса (в единицах)',
|
||||
apiKeyValidityTimeHelper:
|
||||
'Интерфейс времени метки между текущей меткой времени на момент запроса действителен (в единицах), установлен как 0, не проводится проверка метки времени',
|
||||
'Срок действия подписи запроса в минутах, отсчитываемый от метки времени запроса. Значение 0 отключает проверку метки времени.',
|
||||
apiKeyReset: 'Сброс ключа интерфейса',
|
||||
apiKeyResetHelper:
|
||||
'связанный ключевой сервис станет недействительным. Пожалуйста, добавьте новый ключ к сервису',
|
||||
|
||||
@@ -2,14 +2,13 @@ import { getFuLocaleMessage } from '@/lang/fu';
|
||||
|
||||
const message = {
|
||||
apiKeyManagement: {
|
||||
title: 'Panel API',
|
||||
title: 'API Key',
|
||||
manage: 'API anahtarlarını yönet',
|
||||
personalEntry: 'Ayarlar bölümünde anahtarlarınızı, erişim koşullarını ve uygulama QR bağlantısını yönetin.',
|
||||
create: 'API anahtarı oluştur',
|
||||
legacy: 'Eski anahtar',
|
||||
identifier: 'Anahtar kimliği',
|
||||
details: 'API anahtarı ayrıntıları',
|
||||
quota: '{0} / {1} yeni anahtar (devre dışı ve süresi dolmuş anahtarlar dahil, eski anahtar hariç)',
|
||||
statusEnable: 'Etkin',
|
||||
statusDisable: 'Devre dışı',
|
||||
statusRevoked: 'İptal edildi',
|
||||
@@ -22,10 +21,8 @@ const message = {
|
||||
never: 'Süresiz',
|
||||
futureExpiry: 'Gelecekte bir sona erme zamanı seçin.',
|
||||
allowAppBinding: 'Uygulama QR bağlantısına izin ver',
|
||||
bindingException:
|
||||
'Oturum açılmış panelinizde oluşturulan tek kullanımlık QR koduyla bu anahtarın telefona yeniden iletilmesine izin verir.',
|
||||
bindingDisableHelp:
|
||||
'QR bağlantısını kapatmak gelecekteki iletimi engeller. Mevcut bağlantılar anahtar devre dışı bırakılana, iptal edilene veya süresi dolana kadar geçerlidir.',
|
||||
'QR eşleştirmesini devre dışı bırakmak yeni eşleştirmeleri engeller. Eşleştirilmiş cihazlar, anahtar devre dışı bırakılana, iptal edilene veya süresi dolana kadar kullanılmaya devam edebilir.',
|
||||
saveSecret:
|
||||
'Anahtarı şimdi kopyalayıp kaydedin. Kapattıktan sonra normal anahtar yönetimi tam anahtarı yeniden gösteremez.',
|
||||
saved: 'Anahtarı kaydettim',
|
||||
@@ -34,7 +31,7 @@ const message = {
|
||||
uncertain:
|
||||
'İstek sonucu belirsiz. Tekrarları önlemek için aynı bilgilerle yeniden deneyin veya kapatıp yeni anahtar oluşturmadan listeyi kontrol edin.',
|
||||
legacyHelp:
|
||||
'Eski anahtar mevcut bağlantıların uyumluluğunu korur. Anahtarı açma, kapatma ve sıfırlama yalnızca onu etkiler.',
|
||||
'Eski anahtar, önceki API bağlantılarıyla uyumluluğu korur. Etkinleştirme, devre dışı bırakma ve sıfırlama işlemleri yalnızca bu anahtarı etkiler.',
|
||||
showLegacy: 'Eski anahtarı göster',
|
||||
revoke: 'İptal et',
|
||||
revokeConfirm:
|
||||
@@ -2962,7 +2959,7 @@ const message = {
|
||||
apiKeyValidityTime: 'Arayüz anahtarının geçerlilik süresi',
|
||||
apiKeyValidityTimeEgs: 'Arayüz anahtarının geçerlilik süresi (dakika cinsinden)',
|
||||
apiKeyValidityTimeHelper:
|
||||
'Arayüz zaman damgası, mevcut zaman damgasıyla farkı (dakika cinsinden) izin verilen aralıktaysa geçerlidir. 0 değeri doğrulamayı devre dışı bırakır.',
|
||||
'İstek imzasının, istek zaman damgasından itibaren hesaplanan geçerlilik süresidir; birimi dakikadır. 0 olarak ayarlandığında zaman damgası doğrulanmaz.',
|
||||
apiKeyReset: 'Arayüz anahtarı sıfırlama',
|
||||
apiKeyResetHelper: 'İlişkili anahtar servisi geçersiz hale gelecektir. Lütfen servise yeni bir anahtar ekleyin',
|
||||
confDockerProxy: 'Docker vekilini yapılandır',
|
||||
|
||||
@@ -2,14 +2,13 @@ import { getFuLocaleMessage } from '@/lang/fu';
|
||||
|
||||
const message = {
|
||||
apiKeyManagement: {
|
||||
title: '面板 API',
|
||||
title: 'API Key',
|
||||
manage: '管理 API Key',
|
||||
personalEntry: '前往設定管理自己的金鑰、存取條件與 APP 掃碼綁定。',
|
||||
create: '建立 API Key',
|
||||
legacy: '舊版 Key',
|
||||
identifier: '金鑰識別',
|
||||
details: 'API Key 詳情',
|
||||
quota: '新 Key {0} / {1}(停用、到期的仍計數,舊版 Key 不計入)',
|
||||
statusEnable: '已啟用',
|
||||
statusDisable: '已停用',
|
||||
statusRevoked: '已撤銷',
|
||||
@@ -22,14 +21,13 @@ const message = {
|
||||
never: '永不到期',
|
||||
futureExpiry: '請選擇晚於目前時間的到期時間。',
|
||||
allowAppBinding: '允許 APP 掃碼綁定',
|
||||
bindingException: '允許在目前帳號的面板登入工作階段中產生一次性 QR Code,再次向手機交付這把金鑰。',
|
||||
bindingDisableHelp: '關閉掃碼綁定後,已綁定裝置仍可使用,直到金鑰停用、撤銷或到期。',
|
||||
bindingDisableHelp: '關閉掃碼綁定後,將停止接受新的綁定;已綁定裝置可繼續使用,直至金鑰被停用、撤銷或到期。',
|
||||
saveSecret: '請立即複製並儲存金鑰。關閉後,一般金鑰管理無法再次顯示完整金鑰。',
|
||||
saved: '我已儲存金鑰',
|
||||
alreadyCreated:
|
||||
'這把金鑰已建立,無法再次顯示明文。若原回應遺失,請撤銷後重新建立;允許掃碼綁定時,也可透過掃碼交付。',
|
||||
uncertain: '請求結果尚不確定。請保持原設定重試,避免重複建立;或關閉並檢查清單後再建立其他金鑰。',
|
||||
legacyHelp: '舊版 Key 用於相容已有連線,其開關與重設只影響這把 Key。',
|
||||
legacyHelp: '舊版 Key 用於相容歷史 API 連線,啟用、停用及重設操作僅對該 Key 生效。',
|
||||
showLegacy: '檢視舊版金鑰',
|
||||
revoke: '撤銷',
|
||||
revokeConfirm:
|
||||
@@ -2748,7 +2746,7 @@ const message = {
|
||||
'使用反向代理時,請填寫代理伺服器的 IP 或網段;留空時不信任任何代理,並使用請求的直接來源 IP。不支援 0.0.0.0/0 和 ::/0。',
|
||||
apiKeyValidityTime: '介面金鑰有效期',
|
||||
apiKeyValidityTimeEgs: '介面金鑰有效期(組織分)',
|
||||
apiKeyValidityTimeHelper: '介面時間戳記到請求時的目前時間戳之間有效(組織分),設定為0時,不做時間戳記校驗',
|
||||
apiKeyValidityTimeHelper: '請求簽章的有效時長,單位為分鐘,自請求時間戳記起計算。設為 0 時不驗證時間戳記。',
|
||||
apiKeyReset: '介面金鑰重設',
|
||||
apiKeyResetHelper: '重設金鑰後,已關聯金鑰服務將失效,請重新新增新金鑰至服務。',
|
||||
confDockerProxy: '設定 Docker 代理',
|
||||
|
||||
@@ -2,14 +2,13 @@ import { getFuLocaleMessage } from '@/lang/fu';
|
||||
|
||||
const message = {
|
||||
apiKeyManagement: {
|
||||
title: '面板 API',
|
||||
title: 'API Key',
|
||||
manage: '管理 API Key',
|
||||
personalEntry: '前往设置管理自己的密钥、访问条件和 APP 扫码绑定。',
|
||||
create: '创建 API Key',
|
||||
legacy: '旧版 Key',
|
||||
identifier: '密钥标识',
|
||||
details: 'API Key 详情',
|
||||
quota: '新 Key {0} / {1}(停用、到期的仍计数,旧版 Key 不计入)',
|
||||
statusEnable: '已启用',
|
||||
statusDisable: '已停用',
|
||||
statusRevoked: '已撤销',
|
||||
@@ -22,14 +21,13 @@ const message = {
|
||||
never: '永不过期',
|
||||
futureExpiry: '请选择晚于当前时间的到期时间。',
|
||||
allowAppBinding: '允许 APP 扫码绑定',
|
||||
bindingException: '允许在当前账号的面板登录会话中生成一次性二维码,再次向手机交付这把密钥。',
|
||||
bindingDisableHelp: '关闭扫码绑定后,已绑定设备仍可使用,直到密钥停用、撤销或到期。',
|
||||
bindingDisableHelp: '关闭扫码绑定后,将停止接受新的绑定;已绑定设备可继续使用,直至密钥被停用、撤销或到期。',
|
||||
saveSecret: '请立即复制并保存密钥。关闭后,普通密钥管理无法再次显示完整密钥。',
|
||||
saved: '我已保存密钥',
|
||||
alreadyCreated:
|
||||
'这把密钥已创建,无法再次展示明文。若原响应丢失,请撤销后重新创建;允许扫码绑定时,也可通过扫码交付。',
|
||||
uncertain: '请求结果尚不确定。请保持原配置重试,避免重复创建;或关闭并检查列表后再创建其他密钥。',
|
||||
legacyHelp: '旧版 Key 用于兼容已有连接,其开关和重置只影响这把 Key。',
|
||||
legacyHelp: '旧版 Key 用于兼容历史 API 连接,启用、停用和重置操作仅对该 Key 生效。',
|
||||
showLegacy: '查看旧版密钥',
|
||||
revoke: '撤销',
|
||||
revokeConfirm:
|
||||
@@ -2779,7 +2777,7 @@ const message = {
|
||||
'使用反向代理时,请填写代理服务器的 IP 或网段;留空时不信任任何代理,并使用请求的直接来源 IP。不支持 0.0.0.0/0 和 ::/0。',
|
||||
apiKeyValidityTime: '接口密钥有效期',
|
||||
apiKeyValidityTimeEgs: '接口密钥有效期(单位分)',
|
||||
apiKeyValidityTimeHelper: '接口时间戳到请求时的当前时间戳之间有效(单位分),设置为 0 时,不做时间戳校验',
|
||||
apiKeyValidityTimeHelper: '请求签名的有效时长,单位为分钟,自请求时间戳起计算。设为 0 时不校验时间戳。',
|
||||
apiKeyReset: '接口密钥重置',
|
||||
apiKeyResetHelper: '重置密钥后,已关联密钥服务将失效,请重新添加新密钥至服务。',
|
||||
confDockerProxy: '配置 Docker 代理',
|
||||
|
||||
@@ -788,7 +788,7 @@ const handleMFA = async () => {
|
||||
|
||||
const openApiManagement = () => {
|
||||
open.value = false;
|
||||
router.push('/settings/apikeys');
|
||||
router.push({ path: '/settings/apikeys', query: { uncached: 'true' } });
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
|
||||
@@ -2,14 +2,9 @@
|
||||
<div>
|
||||
<LayoutContent :title="$t('apiKeyManagement.title')" v-loading="loading">
|
||||
<template #leftToolBar>
|
||||
<div class="flex flex-wrap items-center gap-3">
|
||||
<el-button type="primary" :disabled="used >= limit" @click="editor?.open()">
|
||||
{{ $t('apiKeyManagement.create') }}
|
||||
</el-button>
|
||||
<el-tooltip :content="$t('apiKeyManagement.quota', [used, limit])" placement="top" trigger="click">
|
||||
<el-button link type="info" class="whitespace-nowrap">{{ used }} / {{ limit }}</el-button>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<el-button type="primary" @click="editor?.open()">
|
||||
{{ $t('apiKeyManagement.create') }}
|
||||
</el-button>
|
||||
</template>
|
||||
<template #rightToolBar><TableRefresh @search="search" /></template>
|
||||
<template #main>
|
||||
@@ -61,14 +56,57 @@
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
v-if="!isMobile"
|
||||
:label="$t('setting.ipWhiteList')"
|
||||
min-width="180"
|
||||
show-overflow-tooltip
|
||||
>
|
||||
<el-table-column v-if="!isMobile" :label="$t('setting.ipWhiteList')" min-width="180">
|
||||
<template #default="{ row }">
|
||||
{{ isAnyAPIKeyIP(row.ipWhiteList) ? $t('apiKeyManagement.anyIP') : row.ipWhiteList || '—' }}
|
||||
<span v-if="isAnyAPIKeyIP(row.ipWhiteList)">{{ $t('apiKeyManagement.anyIP') }}</span>
|
||||
<div
|
||||
v-else-if="whitelistEntries(row.ipWhiteList).length"
|
||||
class="flex min-w-0 items-center gap-2"
|
||||
>
|
||||
<div class="min-w-0">
|
||||
<div
|
||||
v-for="(ip, index) in whitelistEntries(row.ipWhiteList).slice(0, 2)"
|
||||
:key="index"
|
||||
class="break-all"
|
||||
>
|
||||
{{ ip }}
|
||||
</div>
|
||||
</div>
|
||||
<el-popover
|
||||
v-if="whitelistEntries(row.ipWhiteList).length > 2"
|
||||
placement="top"
|
||||
trigger="click"
|
||||
:width="360"
|
||||
:title="$t('setting.ipWhiteList')"
|
||||
:popper-style="{ maxWidth: 'calc(100vw - 32px)' }"
|
||||
>
|
||||
<template #reference>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
size="small"
|
||||
class="shrink-0"
|
||||
:aria-label="
|
||||
$t('setting.ipWhiteList') +
|
||||
': ' +
|
||||
$t('commons.table.total', [whitelistEntries(row.ipWhiteList).length])
|
||||
"
|
||||
>
|
||||
+{{ whitelistEntries(row.ipWhiteList).length - 2 }}
|
||||
</el-button>
|
||||
</template>
|
||||
<ul class="m-0 max-h-64 list-none overflow-y-auto p-0">
|
||||
<li
|
||||
v-for="(ip, index) in whitelistEntries(row.ipWhiteList)"
|
||||
:key="index"
|
||||
class="break-all py-1"
|
||||
>
|
||||
{{ ip }}
|
||||
</li>
|
||||
</ul>
|
||||
</el-popover>
|
||||
</div>
|
||||
<span v-else>—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="!isMobile" :label="$t('apiKeyManagement.expiresAt')" min-width="170">
|
||||
@@ -136,7 +174,7 @@ import APIKeyEditor from '@/components/api-key-management/editor.vue';
|
||||
import APIKeySummary from '@/components/api-key-management/summary.vue';
|
||||
import DrawerPro from '@/components/drawer-pro/index.vue';
|
||||
import type { FuTableOperationButton } from '@/components/table/shared';
|
||||
import { isAnyAPIKeyIP } from '@/utils/api-key';
|
||||
import { isAnyAPIKeyIP, normalizeAPIKeyIPs } from '@/utils/api-key';
|
||||
import { MsgSuccess, MsgWarning } from '@/utils/message';
|
||||
import i18n from '@/lang';
|
||||
import { useGlobalStore } from '@/composables/useGlobalStore';
|
||||
@@ -145,8 +183,6 @@ const { isMobile } = useGlobalStore();
|
||||
|
||||
const items = ref<APIKey.Item[]>([]);
|
||||
const loading = ref(false);
|
||||
const used = ref(0);
|
||||
const limit = ref(20);
|
||||
const editor = ref<InstanceType<typeof APIKeyEditor>>();
|
||||
const detail = ref<APIKey.Item>();
|
||||
const detailVisible = ref(false);
|
||||
@@ -157,6 +193,11 @@ let searchVersion = 0;
|
||||
let secretRequestVersion = 0;
|
||||
let active = false;
|
||||
|
||||
const whitelistEntries = (value: string) =>
|
||||
normalizeAPIKeyIPs(value || '')
|
||||
.split('\n')
|
||||
.filter(Boolean);
|
||||
|
||||
const clearLegacySecret = () => {
|
||||
secretRequestVersion++;
|
||||
legacySecret.value = '';
|
||||
@@ -177,8 +218,6 @@ const search = async (preserveLegacyRequest?: number) => {
|
||||
}
|
||||
items.value = response.data.items || [];
|
||||
pagination.total = response.data.total;
|
||||
used.value = response.data.used;
|
||||
limit.value = response.data.limit;
|
||||
if (detail.value) {
|
||||
const previousRevision = detail.value.revision;
|
||||
detail.value = items.value.find((entry) => entry.id === detail.value?.id);
|
||||
|
||||
@@ -27,10 +27,6 @@ const buttons = computed<RouterButton[]>(() => {
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
label: i18n.global.t('apiKeyManagement.title'),
|
||||
path: '/settings/apikeys',
|
||||
},
|
||||
...(globalStore.hasPermission('alert_view')
|
||||
? [
|
||||
{
|
||||
@@ -65,6 +61,10 @@ const buttons = computed<RouterButton[]>(() => {
|
||||
path: isEnterprise.value ? '/enterprise/license' : '/settings/license',
|
||||
},
|
||||
]),
|
||||
{
|
||||
label: i18n.global.t('apiKeyManagement.title'),
|
||||
path: '/settings/apikeys',
|
||||
},
|
||||
...(isFxplay.value || !isAdmin.value
|
||||
? []
|
||||
: [
|
||||
|
||||
Reference in New Issue
Block a user