Commit 8d4d2ee6 authored by Yogi_Wang's avatar Yogi_Wang
Browse files

Add new cli secret ui in profile


Signed-off-by: default avatarYogi_Wang <yawang@vmware.com>
parent 285d3e1e
......@@ -10,8 +10,8 @@ import { throwError as observableThrowError, Observable } from 'rxjs';
export class AccountSettingsModalService {
constructor(private http: HttpClient) { }
generateCli(userId): Observable<any> {
return this.http.post(`/api/users/${userId}/gen_cli_secret`, {}).pipe( map(response => response)
saveNewCli(userId, secretObj): Observable<any> {
return this.http.put(`/api/users/${userId}/cli_secret`, secretObj).pipe( map(response => response)
, catchError(error => observableThrowError(error)));
}
}
......@@ -4,10 +4,11 @@
<inline-alert (confirmEvt)="confirmYes($event)" (closeEvt)="confirmNo($event)"></inline-alert>
<form #accountSettingsFrom="ngForm" class="clr-form clr-form-horizontal">
<div class="clr-form-control">
<label for="account_settings_username" aria-haspopup="true" class="clr-control-label">{{'PROFILE.USER_NAME' | translate}}</label>
<label for="account_settings_username" aria-haspopup="true"
class="clr-control-label">{{'PROFILE.USER_NAME' | translate}}</label>
<div class="clr-control-container display-flex">
<input class="clr-input" type="text" name="account_settings_username" [(ngModel)]="account.username" disabled id="account_settings_username"
size="30">
<input class="clr-input" type="text" name="account_settings_username" [(ngModel)]="account.username"
disabled id="account_settings_username" size="30">
<div *ngIf="canRename" class="rename-tool">
<button [disabled]="RenameOnGoing" (click)="onRename()" class="btn btn-outline btn-sm">
{{'PROFILE.ADMIN_RENAME_BUTTON' | translate}}
......@@ -22,11 +23,13 @@
</div>
</div>
<div class="clr-form-control">
<label for="account_settings_email" class="required clr-control-label">{{'PROFILE.EMAIL' | translate}}</label>
<label for="account_settings_email"
class="required clr-control-label">{{'PROFILE.EMAIL' | translate}}</label>
<div class="clr-control-container" [class.clr-error]="!getValidationState('account_settings_email')">
<div class="clr-input-wrapper">
<input name="account_settings_email" type="text" #eamilInput="ngModel" class="clr-input" [(ngModel)]="account.email" required
email id="account_settings_email" size="30" (input)='handleValidation("account_settings_email", false)'
<input name="account_settings_email" type="text" #eamilInput="ngModel" class="clr-input"
[(ngModel)]="account.email" required email id="account_settings_email" size="30"
(input)='handleValidation("account_settings_email", false)'
(blur)='handleValidation("account_settings_email", true)'>
<clr-icon class="clr-validate-icon" shape="exclamation-circle"></clr-icon>
<span class="spinner spinner-inline" [hidden]="!checkProgress"></span>
......@@ -38,8 +41,10 @@
</div>
<clr-input-container>
<label [class.required]="!account.oidc_user_meta">{{'PROFILE.FULL_NAME' | translate}}</label>
<input clrInput type="text" name="account_settings_full_name" #fullNameInput="ngModel" [(ngModel)]="account.realname" [required]="!account.oidc_user_meta"
maxLengthExt="20" id="account_settings_full_name" size="30" (input)='handleValidation("account_settings_full_name", false)'
<input clrInput type="text" name="account_settings_full_name" #fullNameInput="ngModel"
[(ngModel)]="account.realname" [required]="!account.oidc_user_meta" maxLengthExt="20"
id="account_settings_full_name" size="30"
(input)='handleValidation("account_settings_full_name", false)'
(blur)='handleValidation("account_settings_full_name", true)'>
<clr-control-error *ngIf="!getValidationState('account_settings_full_name')">
{{'TOOLTIP.FULL_NAME' | translate}}
......@@ -47,14 +52,14 @@
</clr-input-container>
<clr-input-container>
<label>{{'PROFILE.COMMENT' | translate}}</label>
<input clrInput type="text" #commentInput="ngModel" maxlength="30" size="30" name="account_settings_comments" [(ngModel)]="account.comment"
id="account_settings_comments">
<input clrInput type="text" #commentInput="ngModel" maxlength="30" size="30"
name="account_settings_comments" [(ngModel)]="account.comment" id="account_settings_comments">
<clr-control-error *ngIf="commentInput.invalid && (commentInput.dirty || commentInput.touched)">
{{'TOOLTIP.COMMENT' | translate}}
</clr-control-error>
</clr-input-container>
<div class="clr-form-control cli-secret" *ngIf="account.oidc_user_meta">
<div class="clr-form-control cli-secret" *ngIf="account.oidc_user_meta">
<label class="clr-control-label">{{'PROFILE.CLI_PASSWORD' | translate}}
<clr-tooltip>
<clr-icon clrTooltipTrigger shape="info-circle" size="20"></clr-icon>
......@@ -63,21 +68,59 @@
</clr-tooltip-content>
</clr-tooltip>
</label>
<input class="clr-input" type="password" name="cli_password" disabled [ngModel]="'account.oidc_user_meta.secret'" size="33">
<button (click)="generateCli(account.user_id)" id="generate-cli-btn" class="btn btn-outline btn-sm btn-padding-less" *ngIf="showGenerateCli">
<input class="clr-input input-cli" type="password" name="cli_password" disabled
[ngModel]="'account.oidc_user_meta.secret'" size="33">
<button (click)="generateCli(account.user_id)" id="generate-cli-btn"
class="btn btn-outline btn-sm btn-padding-less" *ngIf="showGenerateCli">
{{'PROFILE.ADMIN_CIL_SECRET_BUTTON' | translate}}
</button>
<button (click)="showSecretDetail=true" id="reset-cli-btn" class="btn btn-outline btn-sm btn-padding-less"
*ngIf="showGenerateCli">
{{'PROFILE.ADMIN_CIL_SECRET_RESET_BUTTON' | translate}}
</button>
<div class="rename-tool reset-cli">
<hbr-copy-input #copyInput (onCopySuccess)="onSuccess($event)" (onCopyError)="onError($event)" iconMode="true" [defaultValue]="account.oidc_user_meta.secret"></hbr-copy-input>
<hbr-copy-input #copyInput (onCopySuccess)="onSuccess($event)" (onCopyError)="onError($event)"
iconMode="true" [defaultValue]="account.oidc_user_meta.secret"></hbr-copy-input>
</div>
<div (click)="showGenerateCliFn()" *ngIf="!showGenerateCli" id="hidden-generate-cli" class="hidden-generate-cli">···</div>
<div (click)="showGenerateCliFn()" *ngIf="!showGenerateCli" id="hidden-generate-cli"
class="hidden-generate-cli">···</div>
</div>
</form>
</div>
<div class="modal-footer">
<span class="spinner spinner-inline loading-top" [hidden]="showProgress === false"></span>
<button type="button" class="btn btn-outline" (click)="close()">{{'BUTTON.CANCEL' | translate}}</button>
<button type="button" class="btn btn-primary" [disabled]="!isValid || showProgress" (click)="submit()">{{'BUTTON.OK' | translate}}</button>
<button type="button" class="btn btn-primary" [disabled]="!isValid || showProgress"
(click)="submit()">{{'BUTTON.OK' | translate}}</button>
</div>
</clr-modal>
<clr-modal [(clrModalOpen)]="showSecretDetail" [clrModalSize]="'sm'" [clrModalStaticBackdrop]="staticBackdrop"
[clrModalClosable]="false">
<h3 class="modal-title">{{'PROFILE.ADMIN_CIL_SECRET_RESET_BUTTON' | translate}}</h3>
<div class="modal-body">
<form #resetSecretFrom="ngForm" class="clr-form reset-cli-form clr-form-horizontal">
<clr-input-container>
<label>{{'PROFILE.NEW_SECRET' | translate}}</label>
<input clrInput type="password" maxlength="30" size="30" required pattern="^(?=.*\d)(?=.*[a-zA-Z]).{8,}$"
name="input_secret" [(ngModel)]="resetForms.input_secret" id="input-secret">
<clr-control-error>
{{'TOOLTIP.NEW_SECRET' | translate}}
</clr-control-error>
</clr-input-container>
<clr-input-container>
<label>{{'PROFILE.CONFIRM_SECRET' | translate}}</label>
<input clrInput type="password" maxlength="30" size="30"
[(ngModel)]="resetForms.confirm_secret" name="confirm_secret" id="confirm-secret">
</clr-input-container>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline" (click)="closeReset()">{{'BUTTON.CANCEL' | translate}}</button>
<button type="button" class="btn btn-primary" [disabled]="disableChangeCliSecret()" (click)="resetCliSecret(resetSecretFrom.value.input_secret)">{{'BUTTON.CONFIRM' | translate}}</button>
</div>
</clr-modal>
<confirmation-dialog #confirmationDialog (confirmAction)="confirmGenerate($event)"></confirmation-dialog>
\ No newline at end of file
......@@ -5,6 +5,7 @@ clr-modal {
.rename-tool {
.btn {
margin-right: 6px;
margin-left: 5px;
padding-left: 3px;
padding-right: 3px;
}
......@@ -18,10 +19,15 @@ clr-modal {
align-items: center;
.reset-cli {
height: 30px;
padding-top: 8px;
}
.btn-padding-less {
padding-left: 5px;
padding-right: 5px;
margin-left: 5px;
}
.input-cli {
width: 9.5rem;
}
}
.hidden-generate-cli {
......@@ -32,7 +38,12 @@ clr-modal {
line-height: 30px;
}
}
.reset-cli-form {
width: 19.5rem;
}
.display-flex {
display: flex;
}
.set-btns {
display: flex;
}
\ No newline at end of file
......@@ -29,6 +29,8 @@ import {
ConfirmationTargets,
ConfirmationButtons
} from "../../shared/shared.const";
import { randomWord } from '../../shared/shared.utils';
import { ResetSecret } from './account';
@Component({
selector: "account-settings-modal",
templateUrl: "account-settings-modal.component.html",
......@@ -49,13 +51,15 @@ export class AccountSettingsModalComponent implements OnInit, AfterViewChecked {
originAdminName = "admin";
newAdminName = "admin@harbor.local";
renameConfirmation = false;
// confirmRename = false;
showSecretDetail = false;
resetForms = new ResetSecret();
showGenerateCli: boolean = false;
@ViewChild("confirmationDialog", {static: false})
confirmationDialogComponent: ConfirmationDialogComponent;
accountFormRef: NgForm;
@ViewChild("accountSettingsFrom", {static: true}) accountForm: NgForm;
@ViewChild("resetSecretFrom", {static: true}) resetSecretFrom: NgForm;
@ViewChild(InlineAlertComponent, {static: false}) inlineAlert: InlineAlertComponent;
@ViewChild("copyInput", {static: false}) copyInput: CopyInputComponent;
......@@ -350,13 +354,26 @@ export class AccountSettingsModalComponent implements OnInit, AfterViewChecked {
showGenerateCliFn() {
this.showGenerateCli = !this.showGenerateCli;
}
confirmGenerate(confirmData): void {
let userId = confirmData.data;
this.accountSettingsService.generateCli(userId).subscribe(cliSecret => {
this.account.oidc_user_meta.secret = cliSecret.secret;
confirmGenerate(event): void {
this.account.oidc_user_meta.secret = randomWord(9);
this.resetCliSecret(this.account.oidc_user_meta.secret);
}
resetCliSecret(secret) {
let userId = this.account.user_id;
this.accountSettingsService.saveNewCli(userId, {secret: secret}).subscribe(cliSecret => {
this.account.oidc_user_meta.secret = secret;
this.closeReset();
this.inlineAlert.showInlineSuccess({message: 'PROFILE.GENERATE_SUCCESS'});
}, error => {
this.inlineAlert.showInlineError({message: 'PROFILE.GENERATE_ERROR'});
});
}
disableChangeCliSecret() {
return this.resetSecretFrom.invalid || (this.resetSecretFrom.value.input_secret !== this.resetSecretFrom.value.confirm_secret);
}
closeReset() {
this.showSecretDetail = false;
this.resetSecretFrom.resetForm(new ResetSecret());
}
}
export class ResetSecret {
input_secret: string;
confirm_secret: string;
constructor() {
this.confirm_secret = "";
this.input_secret = "";
}
}
......@@ -75,4 +75,39 @@ export const maintainUrlQueryParmas = function (uri: string, key: string, value:
}
}
};
/**
* the password or secret must longer than 8 chars with at least 1 uppercase letter, 1 lowercase letter and 1 number
* @param randomFlag
* @param min
* @param max
* @returns {string}
*/
export function randomWord(max) {
let str = "";
let contentArray = [['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'],
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l'
, 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x'
, 'y', 'z'],
['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'
, 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']];
for (let i = 0; i < max; i++) {
let randomNumber = getRandomInt(contentArray.length);
str += contentArray[randomNumber][getRandomInt(contentArray[randomNumber].length)];
}
if (!str.match(/\d+/g)) {
str += contentArray[0][getRandomInt(contentArray[0].length)];
}
if (!str.match(/[a-z]+/g)) {
str += contentArray[1][getRandomInt(contentArray[1].length)];
}
if (!str.match(/[A-Z]+/g)) {
str += contentArray[1][getRandomInt(contentArray[1].length)];
}
return str;
}
function getRandomInt(max) {
return Math.floor(Math.random() * Math.floor(max));
}
......@@ -99,7 +99,8 @@
"OIDC_SCOPE": "The scope sent to OIDC server during authentication. It has to contain “openid”, and “offline_access”. If you are using google, please remove “offline_access” from this field.",
"OIDC_VERIFYCERT": "Uncheck this box if your OIDC server is hosted via self-signed certificate.",
"OIDC_GROUP_CLAIM": "The name of Claim in the ID token whose value is the list of group names.",
"OIDC_GROUP_CLAIM_WARNING": "It can only contain letters, numbers, underscores, and the input length is no more than 256 characters."
"OIDC_GROUP_CLAIM_WARNING": "It can only contain letters, numbers, underscores, and the input length is no more than 256 characters.",
"NEW_SECRET": "The secret must longer than 8 chars with at least 1 uppercase letter, 1 lowercase letter and 1 number"
},
"PLACEHOLDER": {
"CURRENT_PWD": "Enter current password",
......@@ -128,8 +129,11 @@
"COPY_SUCCESS": "copy success",
"COPY_ERROR": "copy failed",
"ADMIN_CIL_SECRET_BUTTON": "GENERATE SECRET",
"GENERATE_SUCCESS": "generate CLI secret success",
"GENERATE_ERROR": "generate CLI secret failed",
"ADMIN_CIL_SECRET_RESET_BUTTON": "Upload Your Own Secret",
"NEW_SECRET": "Secret",
"CONFIRM_SECRET": "Re-enter Secret",
"GENERATE_SUCCESS": "Cli secret setting is successful",
"GENERATE_ERROR": "Cli secret setting is failed",
"CONFIRM_TITLE_CLI_GENERATE": "Are you sure you can regenerate secret?",
"CONFIRM_BODY_CLI_GENERATE": "If you regenerate cli secret, the old cli secret will be discarded"
},
......
......@@ -99,7 +99,8 @@
"OIDC_SCOPE": "El ámbito de aplicación enviada a OIDC Server durante la autenticación.Tiene que contener 'Openid', y 'offline_access'.Si usted esta usando Google, por favor quitar 'offline_access' de este campo",
"OIDC_VERIFYCERT": "Desmarque esta casilla si tu OIDC servidor está alojado a través de certificado autofirmado.",
"OIDC_GROUP_CLAIM": "The name of Claim in the ID token whose value is the list of group names.",
"OIDC_GROUP_CLAIM_WARNING": "It can only contain letters, numbers, underscores, and the input length is no more than 256 characters."
"OIDC_GROUP_CLAIM_WARNING": "It can only contain letters, numbers, underscores, and the input length is no more than 256 characters.",
"NEW_SECRET": "The secret must longer than 8 chars with at least 1 uppercase letter, 1 lowercase letter and 1 number."
},
"PLACEHOLDER": {
"CURRENT_PWD": "Introduzca la contraseña actual",
......@@ -128,8 +129,11 @@
"COPY_SUCCESS": "Copiar el éxito",
"COPY_ERROR": "Copia no",
"ADMIN_CIL_SECRET_BUTTON": "GENERATE SECRET",
"GENERATE_SUCCESS": "generate CLI secret success",
"GENERATE_ERROR": "generate CLI secret failed",
"ADMIN_CIL_SECRET_RESET_BUTTON": "Upload Your Own Secret",
"NEW_SECRET": "Secret",
"CONFIRM_SECRET": "Re-enter Secret",
"GENERATE_SUCCESS": "Cli secret setting is successful",
"GENERATE_ERROR": "Cli secret setting is failed",
"CONFIRM_TITLE_CLI_GENERATE": "Are you sure you can regenerate secret?",
"CONFIRM_BODY_CLI_GENERATE": "If you regenerate cli secret, the old cli secret will be discarded"
},
......
......@@ -94,7 +94,8 @@
"OIDC_SCOPE": "le champ envoyés au serveur au cours oidc l'authentification.il doit contenir 'openid', et 'offline_access'.si vous utilisez google, veuillez supprimer 'offline_access' dans ce domaine",
"OIDC_VERIFYCERT": "décocher cette case si votre oidc serveur est accueilli par auto - certificat signé.",
"OIDC_GROUP_CLAIM": "The name of Claim in the ID token whose value is the list of group names.",
"OIDC_GROUP_CLAIM_WARNING": "It can only contain letters, numbers, underscores, and the input length is no more than 256 characters."
"OIDC_GROUP_CLAIM_WARNING": "It can only contain letters, numbers, underscores, and the input length is no more than 256 characters.",
"NEW_SECRET": "The secret must longer than 8 chars with at least 1 uppercase letter, 1 lowercase letter and 1 number."
},
"PLACEHOLDER": {
"CURRENT_PWD": "Entrez le mot de passe actuel",
......@@ -123,8 +124,11 @@
"COPY_SUCCESS": "copie de succès",
"COPY_ERROR": "copie a échoué",
"ADMIN_CIL_SECRET_BUTTON": "GENERATE SECRET",
"GENERATE_SUCCESS": "generate CLI secret success",
"GENERATE_ERROR": "generate CLI secret failed",
"ADMIN_CIL_SECRET_RESET_BUTTON": "Upload Your Own Secret",
"NEW_SECRET": "Secret",
"CONFIRM_SECRET": "Re-enter Secret",
"GENERATE_SUCCESS": "Cli secret setting is successful",
"GENERATE_ERROR": "Cli secret setting is failed",
"CONFIRM_TITLE_CLI_GENERATE": "Are you sure you can regenerate secret?",
"CONFIRM_BODY_CLI_GENERATE": "If you regenerate cli secret, the old cli secret will be discarded"
},
......
......@@ -97,7 +97,8 @@
"OIDC_SCOPE": "O âmbito de aplicação enviada Ao servidor oidc Durante a autenticação.TEM que conter 'openid' e 'offline_access'.Se você está usando o Google, por favor remova 'offline_access' desse Campo.",
"OIDC_VERIFYCERT": "Desmarque esta opção se o SEU servidor está hospedado oidc via self - signed certificate.",
"OIDC_GROUP_CLAIM": "The name of Claim in the ID token whose value is the list of group names.",
"OIDC_GROUP_CLAIM_WARNING": "It can only contain letters, numbers, underscores, and the input length is no more than 256 characters."
"OIDC_GROUP_CLAIM_WARNING": "It can only contain letters, numbers, underscores, and the input length is no more than 256 characters.",
"NEW_SECRET": "The secret must longer than 8 chars with at least 1 uppercase letter, 1 lowercase letter and 1 number."
},
"PLACEHOLDER": {
"CURRENT_PWD": "Insira a senha atual",
......@@ -126,8 +127,11 @@
"COPY_SUCCESS": "SUCESSO de cópia",
"COPY_ERROR": "Cópia falhou",
"ADMIN_CIL_SECRET_BUTTON": "GENERATE SECRET",
"GENERATE_SUCCESS": "generate CLI secret success",
"GENERATE_ERROR": "generate CLI secret failed",
"ADMIN_CIL_SECRET_RESET_BUTTON": "Upload Your Own Secret",
"NEW_SECRET": "Secret",
"CONFIRM_SECRET": "Re-enter Secret",
"GENERATE_SUCCESS": "Cli secret setting is successful",
"GENERATE_ERROR": "Cli secret setting is failed",
"CONFIRM_TITLE_CLI_GENERATE": "Are you sure you can regenerate secret?",
"CONFIRM_BODY_CLI_GENERATE": "If you regenerate cli secret, the old cli secret will be discarded"
},
......
......@@ -99,7 +99,8 @@
"OIDC_SCOPE": "Kapsam, kimlik doğrulama sırasında OIDC sunucusuna gönderildi. “Openid” ve “offline_access” içermelidir. Google kullanıyorsanız, lütfen “offline_access“'i bu alandan kaldırın.",
"OIDC_VERIFYCERT": "OIDC sunucunuz kendinden imzalı sertifika ile barındırılıyorsa bu kutunun işaretini kaldırın.",
"OIDC_GROUP_CLAIM": "The name of Claim in the ID token whose value is the list of group names.",
"OIDC_GROUP_CLAIM_WARNING": "It can only contain letters, numbers, underscores, and the input length is no more than 256 characters."
"OIDC_GROUP_CLAIM_WARNING": "It can only contain letters, numbers, underscores, and the input length is no more than 256 characters.",
"NEW_SECRET": "The secret must longer than 8 chars with at least 1 uppercase letter, 1 lowercase letter and 1 number."
},
"PLACEHOLDER": {
"CURRENT_PWD": "Güncel şifrenizi giriniz",
......@@ -128,8 +129,11 @@
"COPY_SUCCESS": "kopyalama başarılı",
"COPY_ERROR": "kopyalama başarısız",
"ADMIN_CIL_SECRET_BUTTON": "ŞİFRE OLUŞTUR",
"GENERATE_SUCCESS": "CLI şifresi oluşturma başarılı",
"GENERATE_ERROR": "CLI şifresi oluşturma başarısız",
"ADMIN_CIL_SECRET_RESET_BUTTON": "Upload Your Own Secret",
"NEW_SECRET": "Secret",
"CONFIRM_SECRET": "Re-enter Secret",
"GENERATE_SUCCESS": "Cli secret setting is successful",
"GENERATE_ERROR": "Cli secret setting is failed",
"CONFIRM_TITLE_CLI_GENERATE": "Şifreyi yeniden oluşturabileceğine emin misin?",
"CONFIRM_BODY_CLI_GENERATE": "Eğer cli şifresini yeniden oluşturursanız, eski cli şifresi atılır"
},
......
......@@ -98,7 +98,8 @@
"OIDC_SCOPE": "在身份验证期间发送到OIDC服务器的scope。它必须包含“openid”和“offline_access”。如果您使用Google,请从此字段中删除“脱机访问”。",
"OIDC_VERIFYCERT": "如果您的OIDC服务器是通过自签名证书托管的,请取消选中此框。",
"OIDC_GROUP_CLAIM": "ID和token中的Claim名称,在组的名称列表中。",
"OIDC_GROUP_CLAIM_WARNING": "它只能包含字母、数字、下划线,且输入长度不超过256字符。"
"OIDC_GROUP_CLAIM_WARNING": "它只能包含字母、数字、下划线,且输入长度不超过256字符。",
"NEW_SECRET": "Cli secret 必须超过8个字符,并至少包含1个大写字母,1个小写字母和1个数字。"
},
"PLACEHOLDER": {
"CURRENT_PWD": "输入当前密码",
......@@ -127,8 +128,11 @@
"COPY_SUCCESS": "复制成功",
"COPY_ERROR": "复制失败",
"ADMIN_CIL_SECRET_BUTTON": "生成新的CLI密码",
"GENERATE_SUCCESS": "成功生成新的CLI密码",
"GENERATE_ERROR": "生成新的CLI密码失败",
"ADMIN_CIL_SECRET_RESET_BUTTON": "输入你自己的CLI密码",
"NEW_SECRET": "密码",
"CONFIRM_SECRET": "重复出入密码",
"GENERATE_SUCCESS": "成功设置新的CLI密码",
"GENERATE_ERROR": "设置新的CLI密码失败",
"CONFIRM_TITLE_CLI_GENERATE": "您确定需要重新生成cli secret吗?",
"CONFIRM_BODY_CLI_GENERATE": "如果您重新生成cli secret,那么旧的cli secret将会被弃用"
},
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment