Fix issues of public/private projects

This commit is contained in:
Steven Zou 2017-06-14 23:38:21 +08:00
parent 7d595c10c5
commit 1df1a45270
8 changed files with 104 additions and 62 deletions

View File

@ -14,8 +14,9 @@
<div class="option-right"> <div class="option-right">
<div class="select" style="float: left;"> <div class="select" style="float: left;">
<select (change)="doFilterProjects($event)"> <select (change)="doFilterProjects($event)">
<option value="0">{{projectTypes[0] | translate}}</option> <option value="-1" [selected]="currentFilteredType === -1">{{projectTypes[0] | translate}}</option>
<option value="1">{{projectTypes[1] | translate}}</option> <option value="0">{{projectTypes[1] | translate}}</option>
<option value="1">{{projectTypes[2] | translate}}</option>
</select> </select>
</div> </div>
<grid-filter filterPlaceholder='{{"PROJECT.FILTER_PLACEHOLDER" | translate}}' (filter)="doSearchProjects($event)" [currentValue]="projectName"></grid-filter> <grid-filter filterPlaceholder='{{"PROJECT.FILTER_PLACEHOLDER" | translate}}' (filter)="doSearchProjects($event)" [currentValue]="projectName"></grid-filter>

View File

@ -56,13 +56,11 @@ export class ProjectComponent implements OnInit, OnDestroy {
@ViewChild(ListProjectComponent) @ViewChild(ListProjectComponent)
listProject: ListProjectComponent; listProject: ListProjectComponent;
currentFilteredType: number = 0; currentFilteredType: number = -1;//all projects
projectName: string = "";
subscription: Subscription; subscription: Subscription;
projectName: string;
isPublic: number;
constructor( constructor(
private projectService: ProjectService, private projectService: ProjectService,
private messageHandlerService: MessageHandlerService, private messageHandlerService: MessageHandlerService,
@ -83,8 +81,8 @@ export class ProjectComponent implements OnInit, OnDestroy {
this.retrieve(); this.retrieve();
this.statisticHandler.refresh(); this.statisticHandler.refresh();
}, },
error =>{ error => {
if(error && error.status === 412) { if (error && error.status === 412) {
this.messageHandlerService.showError('PROJECT.FAILED_TO_DELETE_PROJECT', ''); this.messageHandlerService.showError('PROJECT.FAILED_TO_DELETE_PROJECT', '');
} else { } else {
this.messageHandlerService.handleError(error); this.messageHandlerService.handleError(error);
@ -97,9 +95,6 @@ export class ProjectComponent implements OnInit, OnDestroy {
} }
ngOnInit(): void { ngOnInit(): void {
this.projectName = '';
this.isPublic = 0;
} }
ngOnDestroy(): void { ngOnDestroy(): void {
@ -110,8 +105,8 @@ export class ProjectComponent implements OnInit, OnDestroy {
get projectCreationRestriction(): boolean { get projectCreationRestriction(): boolean {
let account = this.sessionService.getCurrentUser(); let account = this.sessionService.getCurrentUser();
if(account) { if (account) {
switch(this.appConfigService.getConfig().project_creation_restriction) { switch (this.appConfigService.getConfig().project_creation_restriction) {
case 'adminonly': case 'adminonly':
return (account.has_admin_role === 1); return (account.has_admin_role === 1);
case 'everyone': case 'everyone':
@ -122,8 +117,13 @@ export class ProjectComponent implements OnInit, OnDestroy {
} }
retrieve(state?: State): void { retrieve(state?: State): void {
this.projectName = "";
this.getProjects();
}
getProjects(name?: string, isPublic?: number, page?: number, pageSize?: number): void {
this.projectService this.projectService
.listProjects(this.projectName, this.isPublic) .listProjects(name, isPublic, page, pageSize)
.subscribe( .subscribe(
response => { response => {
this.changedProjects = response.json(); this.changedProjects = response.json();
@ -138,7 +138,6 @@ export class ProjectComponent implements OnInit, OnDestroy {
createProject(created: boolean) { createProject(created: boolean) {
if (created) { if (created) {
this.projectName = '';
this.retrieve(); this.retrieve();
this.statisticHandler.refresh(); this.statisticHandler.refresh();
} }
@ -146,14 +145,26 @@ export class ProjectComponent implements OnInit, OnDestroy {
doSearchProjects(projectName: string): void { doSearchProjects(projectName: string): void {
this.projectName = projectName; this.projectName = projectName;
this.retrieve(); if (projectName === "") {
if (this.currentFilteredType === -1) {
this.getProjects();
} else {
this.getProjects(projectName, this.currentFilteredType);
}
} else {
this.getProjects(projectName);
}
} }
doFilterProjects($event: any): void { doFilterProjects($event: any): void {
if ($event && $event.target && $event.target["value"]) { if ($event && $event.target && $event.target["value"]) {
this.currentFilteredType = $event.target["value"]; this.projectName = "";
this.isPublic = this.currentFilteredType; this.currentFilteredType = +$event.target["value"];
this.retrieve(); if (this.currentFilteredType === -1) {
this.getProjects();
} else {
this.getProjects("", this.currentFilteredType);
}
} }
} }
@ -166,6 +177,7 @@ export class ProjectComponent implements OnInit, OnDestroy {
response => { response => {
this.messageHandlerService.showSuccess('PROJECT.TOGGLED_SUCCESS'); this.messageHandlerService.showSuccess('PROJECT.TOGGLED_SUCCESS');
this.statisticHandler.refresh(); this.statisticHandler.refresh();
this.getProjects("", this.currentFilteredType);
}, },
error => this.messageHandlerService.handleError(error) error => this.messageHandlerService.handleError(error)
); );
@ -185,6 +197,7 @@ export class ProjectComponent implements OnInit, OnDestroy {
} }
refresh(): void { refresh(): void {
this.currentFilteredType = -1;
this.retrieve(); this.retrieve();
this.statisticHandler.refresh(); this.statisticHandler.refresh();
} }

View File

@ -46,8 +46,14 @@ export class ProjectService {
params.set('page', page + ''); params.set('page', page + '');
params.set('page_size', pageSize + ''); params.set('page_size', pageSize + '');
} }
if(name && name.trim() !== ""){
params.set('name', name);
}
if(isPublic !== undefined){
params.set('public', ''+isPublic);
}
return this.http return this.http
.get(`/api/projects?project_name=${name}&is_public=${isPublic}`, {search: params}) .get(`/api/projects`, {search: params})
.map(response=>response) .map(response=>response)
.catch(error=>Observable.throw(error)); .catch(error=>Observable.throw(error));
} }

View File

@ -71,6 +71,6 @@ export const enum ConfirmationButtons {
CONFIRM_CANCEL, YES_NO, DELETE_CANCEL, CLOSE CONFIRM_CANCEL, YES_NO, DELETE_CANCEL, CLOSE
} }
export const ProjectTypes = { 0: 'PROJECT.MY_PROJECTS', 1: 'PROJECT.PUBLIC_PROJECTS' }; export const ProjectTypes = { 0: 'PROJECT.ALL_PROJECTS', 1: 'PROJECT.PRIVATE_PROJECTS', 2: 'PROJECT.PUBLIC_PROJECTS' };
export const RoleInfo = { 1: 'MEMBER.PROJECT_ADMIN', 2: 'MEMBER.DEVELOPER', 3: 'MEMBER.GUEST' }; export const RoleInfo = { 1: 'MEMBER.PROJECT_ADMIN', 2: 'MEMBER.DEVELOPER', 3: 'MEMBER.GUEST' };
export const RoleMapping = { 'projectAdmin': 'MEMBER.PROJECT_ADMIN', 'developer': 'MEMBER.DEVELOPER', 'guest': 'MEMBER.GUEST' }; export const RoleMapping = { 'projectAdmin': 'MEMBER.PROJECT_ADMIN', 'developer': 'MEMBER.DEVELOPER', 'guest': 'MEMBER.GUEST' };

View File

@ -12,10 +12,10 @@
</div> </div>
<div class="statistic-column-block" style="margin-left: 16px;"> <div class="statistic-column-block" style="margin-left: 16px;">
<div> <div>
<statistics [data]='originalCopy.my_project_count' [label]='"STATISTICS.INDEX_MY_PROJECTS" | translate'></statistics> <statistics [data]='originalCopy.my_project_count' [label]='"STATISTICS.INDEX_PRIVATE" | translate'></statistics>
</div> </div>
<div> <div>
<statistics [data]='originalCopy.my_repo_count' [label]='"STATISTICS.INDEX_MY_REPOSITORIES" | translate'></statistics> <statistics [data]='originalCopy.my_repo_count' [label]='"STATISTICS.INDEX_PRIVATE" | translate'></statistics>
</div> </div>
</div> </div>
<div class="statistic-column-block" style="margin-left: 28px;"> <div class="statistic-column-block" style="margin-left: 28px;">

View File

@ -132,7 +132,8 @@
"MAKE": "Make", "MAKE": "Make",
"NEW_POLICY": "New Replication Rule", "NEW_POLICY": "New Replication Rule",
"DELETE": "Delete", "DELETE": "Delete",
"MY_PROJECTS": "All Projects", "ALL_PROJECTS": "All Projects",
"PRIVATE_PROJECTS": "Private Projects",
"PUBLIC_PROJECTS": "Public Projects", "PUBLIC_PROJECTS": "Public Projects",
"PROJECT": "Project", "PROJECT": "Project",
"NEW_PROJECT": "New Project", "NEW_PROJECT": "New Project",
@ -269,7 +270,9 @@
"POLICY_ALREADY_EXISTS": "Replication rule already exists.", "POLICY_ALREADY_EXISTS": "Replication rule already exists.",
"FAILED_TO_DELETE_POLICY_ENABLED": "Cannot delete rule: rule has unfinished job(s) or rule is enabled.", "FAILED_TO_DELETE_POLICY_ENABLED": "Cannot delete rule: rule has unfinished job(s) or rule is enabled.",
"FOUND_ERROR_IN_JOBS": "Found errors in the replication job(s), please check.", "FOUND_ERROR_IN_JOBS": "Found errors in the replication job(s), please check.",
"INVALID_DATE": "Invalid date." "INVALID_DATE": "Invalid date.",
"PLACEHOLDER": "We couldn't find any replication rules!",
"JOB_PLACEHOLDER": "We couldn't find any replication jobs!"
}, },
"DESTINATION": { "DESTINATION": {
"NEW_ENDPOINT": "New Endpoint", "NEW_ENDPOINT": "New Endpoint",
@ -297,7 +300,8 @@
"DELETED_SUCCESS": "Deleted endpoint successfully.", "DELETED_SUCCESS": "Deleted endpoint successfully.",
"DELETED_FAILED": "Deleted endpoint failed.", "DELETED_FAILED": "Deleted endpoint failed.",
"CANNOT_EDIT": "Endpoint cannot be changed while the replication rule is enabled.", "CANNOT_EDIT": "Endpoint cannot be changed while the replication rule is enabled.",
"FAILED_TO_DELETE_TARGET_IN_USED": "Failed to delete the endpoint in use." "FAILED_TO_DELETE_TARGET_IN_USED": "Failed to delete the endpoint in use.",
"PLACEHOLDER": "We couldn't find any endpoints!"
}, },
"REPOSITORY": { "REPOSITORY": {
"COPY_DIGEST_ID": "Copy Digest ID", "COPY_DIGEST_ID": "Copy Digest ID",
@ -329,7 +333,8 @@
"DELETED_REPO_SUCCESS": "Deleted repository successfully.", "DELETED_REPO_SUCCESS": "Deleted repository successfully.",
"DELETED_TAG_SUCCESS": "Deleted tag successfully.", "DELETED_TAG_SUCCESS": "Deleted tag successfully.",
"COPY": "Copy", "COPY": "Copy",
"NOTARY_IS_UNDETERMINED": "Cannot determine the signature of this tag." "NOTARY_IS_UNDETERMINED": "Cannot determine the signature of this tag.",
"PLACEHOLDER": "We couldn't find any repositories!"
}, },
"ALERT": { "ALERT": {
"FORM_CHANGE_CONFIRMATION": "Some changes are not saved yet. Do you want to cancel?" "FORM_CHANGE_CONFIRMATION": "Some changes are not saved yet. Do you want to cancel?"
@ -424,7 +429,7 @@
"TITLE": "STATISTICS", "TITLE": "STATISTICS",
"PRO_ITEM": "PROJECTS", "PRO_ITEM": "PROJECTS",
"REPO_ITEM": "REPOSITORIES", "REPO_ITEM": "REPOSITORIES",
"INDEX_MY": "MY", "INDEX_PRIVATE": "PRIVATE",
"INDEX_MY_PROJECTS": "MY PROJECTS", "INDEX_MY_PROJECTS": "MY PROJECTS",
"INDEX_MY_REPOSITORIES": "MY REPOSITORIES", "INDEX_MY_REPOSITORIES": "MY REPOSITORIES",
"INDEX_PUB": "PUBLIC", "INDEX_PUB": "PUBLIC",
@ -467,7 +472,8 @@
"NONE": "None" "NONE": "None"
}, },
"SINGULAR": "Vulnerability", "SINGULAR": "Vulnerability",
"PLURAL": "Vulnerabilities" "PLURAL": "Vulnerabilities",
"PLACEHOLDER": "Filter Vulnerabilities"
}, },
"PUSH_IMAGE": { "PUSH_IMAGE": {
"TITLE": "Push Image", "TITLE": "Push Image",
@ -483,7 +489,8 @@
"ARCHITECTURE": "Architecture", "ARCHITECTURE": "Architecture",
"OS": "OS", "OS": "OS",
"SCAN_COMPLETION_TIME": "Scan Completed", "SCAN_COMPLETION_TIME": "Scan Completed",
"IMAGE_VULNERABILITIES": "Image Vulnerabilities" "IMAGE_VULNERABILITIES": "Image Vulnerabilities",
"PLACEHOLDER": "We couldn't find any tags!"
}, },
"UNKNOWN_ERROR": "Unknown errors have occurred. Please try again later.", "UNKNOWN_ERROR": "Unknown errors have occurred. Please try again later.",
"UNAUTHORIZED_ERROR": "Your session is invalid or has expired. You need to sign in to continue your action.", "UNAUTHORIZED_ERROR": "Your session is invalid or has expired. You need to sign in to continue your action.",

View File

@ -132,8 +132,9 @@
"MAKE": "Hacer", "MAKE": "Hacer",
"NEW_POLICY": "Nueva regla de replicación", "NEW_POLICY": "Nueva regla de replicación",
"DELETE": "Eliminar", "DELETE": "Eliminar",
"MY_PROJECTS": "Todos los proyectos", "ALL_PROJECTS": "All Projects",
"PUBLIC_PROJECTS": "Proyectos Públicos", "PRIVATE_PROJECTS": "Private Projects",
"PUBLIC_PROJECTS": "Public Projects",
"PROJECT": "Proyecto", "PROJECT": "Proyecto",
"NEW_PROJECT": "Nuevo proyecto", "NEW_PROJECT": "Nuevo proyecto",
"NAME_TOOLTIP": "Project name should be at least 2 characters long with lower case characters, numbers and ._- and must be start with characters or numbers.", "NAME_TOOLTIP": "Project name should be at least 2 characters long with lower case characters, numbers and ._- and must be start with characters or numbers.",
@ -269,7 +270,9 @@
"POLICY_ALREADY_EXISTS": "La regla de replicación ya existe.", "POLICY_ALREADY_EXISTS": "La regla de replicación ya existe.",
"FAILED_TO_DELETE_POLICY_ENABLED": "No se puede eliminar la regla: tiene trabajo(s) sin finalizar o está activa.", "FAILED_TO_DELETE_POLICY_ENABLED": "No se puede eliminar la regla: tiene trabajo(s) sin finalizar o está activa.",
"FOUND_ERROR_IN_JOBS": "Se han encontrado errores en el trabajo de replicación. Por favor, compruébelos.", "FOUND_ERROR_IN_JOBS": "Se han encontrado errores en el trabajo de replicación. Por favor, compruébelos.",
"INVALID_DATE": "Fecha invalida." "INVALID_DATE": "Fecha invalida.",
"PLACEHOLDER": "We couldn't find any replication rules!",
"JOB_PLACEHOLDER": "We couldn't find any replication jobs!"
}, },
"DESTINATION": { "DESTINATION": {
"NEW_ENDPOINT": "Nuevo Endpoint", "NEW_ENDPOINT": "Nuevo Endpoint",
@ -297,7 +300,8 @@
"DELETED_SUCCESS": "Endpoint eliminado satisfactoriamente.", "DELETED_SUCCESS": "Endpoint eliminado satisfactoriamente.",
"DELETED_FAILED": "Ha fallado la eliminación del endpoint.", "DELETED_FAILED": "Ha fallado la eliminación del endpoint.",
"CANNOT_EDIT": "El endpoint no puede ser cambiado mientras la regla de replicación está activa.", "CANNOT_EDIT": "El endpoint no puede ser cambiado mientras la regla de replicación está activa.",
"FAILED_TO_DELETE_TARGET_IN_USED": "Fallo al eliminar el endpoint en uso." "FAILED_TO_DELETE_TARGET_IN_USED": "Fallo al eliminar el endpoint en uso.",
"PLACEHOLDER": "We couldn't find any endpoints!"
}, },
"REPOSITORY": { "REPOSITORY": {
"COPY_ID": "Copiar ID", "COPY_ID": "Copiar ID",
@ -329,7 +333,9 @@
"POP_REPOS": "Repositorios Populares", "POP_REPOS": "Repositorios Populares",
"DELETED_REPO_SUCCESS": "Repositorio eliminado satisfactoriamente.", "DELETED_REPO_SUCCESS": "Repositorio eliminado satisfactoriamente.",
"DELETED_TAG_SUCCESS": "Etiqueta eliminada satisfactoriamente.", "DELETED_TAG_SUCCESS": "Etiqueta eliminada satisfactoriamente.",
"COPY": "Copiar" "COPY": "Copiar",
"NOTARY_IS_UNDETERMINED": "Cannot determine the signature of this tag.",
"PLACEHOLDER": "We couldn't find any repositories!"
}, },
"ALERT": { "ALERT": {
"FORM_CHANGE_CONFIRMATION": "Algunos cambios no se han guardado aún. ¿Quiere cancelar?" "FORM_CHANGE_CONFIRMATION": "Algunos cambios no se han guardado aún. ¿Quiere cancelar?"
@ -424,7 +430,7 @@
"TITLE": "ESTADÍSTICAS", "TITLE": "ESTADÍSTICAS",
"PRO_ITEM": "PROYECTOS", "PRO_ITEM": "PROYECTOS",
"REPO_ITEM": "REPOSITORIOS", "REPO_ITEM": "REPOSITORIOS",
"INDEX_MY": "MI", "INDEX_PRIVATE": "PRIVADO",
"INDEX_PUB": "PÚBLICO", "INDEX_PUB": "PÚBLICO",
"INDEX_TOTAL": "TOTAL", "INDEX_TOTAL": "TOTAL",
"STORAGE": "ALMACENAMIENTO", "STORAGE": "ALMACENAMIENTO",
@ -465,7 +471,8 @@
"NONE": "None" "NONE": "None"
}, },
"SINGULAR": "Vulnerability", "SINGULAR": "Vulnerability",
"PLURAL": "Vulnerabilities" "PLURAL": "Vulnerabilities",
"PLACEHOLDER": "Filter Vulnerabilities"
}, },
"PUSH_IMAGE": { "PUSH_IMAGE": {
"TITLE": "Push Image", "TITLE": "Push Image",
@ -481,7 +488,8 @@
"ARCHITECTURE": "Architecture", "ARCHITECTURE": "Architecture",
"OS": "OS", "OS": "OS",
"SCAN_COMPLETION_TIME": "Scan Completed", "SCAN_COMPLETION_TIME": "Scan Completed",
"IMAGE_VULNERABILITIES": "Image Vulnerabilities" "IMAGE_VULNERABILITIES": "Image Vulnerabilities",
"PLACEHOLDER": "We couldn't find any tags!"
}, },
"UNKNOWN_ERROR": "Ha ocurrido un error desconocido. Por favor, inténtelo de nuevo más tarde.", "UNKNOWN_ERROR": "Ha ocurrido un error desconocido. Por favor, inténtelo de nuevo más tarde.",
"UNAUTHORIZED_ERROR": "La sesión no es válida o ha caducado. Necesita identificarse de nuevo para llevar a cabo esa acción.", "UNAUTHORIZED_ERROR": "La sesión no es válida o ha caducado. Necesita identificarse de nuevo para llevar a cabo esa acción.",

View File

@ -132,7 +132,8 @@
"MAKE": "设为", "MAKE": "设为",
"NEW_POLICY": "新建规则", "NEW_POLICY": "新建规则",
"DELETE": "删除", "DELETE": "删除",
"MY_PROJECTS": "所有项目", "ALL_PROJECTS": "所有项目",
"PRIVATE_PROJECTS": "私有项目",
"PUBLIC_PROJECTS": "公开项目", "PUBLIC_PROJECTS": "公开项目",
"PROJECT": "项目", "PROJECT": "项目",
"NEW_PROJECT": "新建项目", "NEW_PROJECT": "新建项目",
@ -269,7 +270,9 @@
"POLICY_ALREADY_EXISTS": "规则已存在。", "POLICY_ALREADY_EXISTS": "规则已存在。",
"FAILED_TO_DELETE_POLICY_ENABLED": "删除复制规则失败: 仍有未完成的任务或规则未停用。", "FAILED_TO_DELETE_POLICY_ENABLED": "删除复制规则失败: 仍有未完成的任务或规则未停用。",
"FOUND_ERROR_IN_JOBS": "复制任务中包含错误,请检查。", "FOUND_ERROR_IN_JOBS": "复制任务中包含错误,请检查。",
"INVALID_DATE": "无效日期。" "INVALID_DATE": "无效日期。",
"PLACEHOLDER": "未发现任何复制规则!",
"JOB_PLACEHOLDER": "未发现任何复制任务!"
}, },
"DESTINATION": { "DESTINATION": {
"NEW_ENDPOINT": "新建目标", "NEW_ENDPOINT": "新建目标",
@ -297,7 +300,8 @@
"DELETED_SUCCESS": "成功删除目标。", "DELETED_SUCCESS": "成功删除目标。",
"DELETED_FAILED": "删除目标失败。", "DELETED_FAILED": "删除目标失败。",
"CANNOT_EDIT": "当复制规则启用时目标无法修改。", "CANNOT_EDIT": "当复制规则启用时目标无法修改。",
"FAILED_TO_DELETE_TARGET_IN_USED": "无法删除正在使用的目标。" "FAILED_TO_DELETE_TARGET_IN_USED": "无法删除正在使用的目标。",
"PLACEHOLDER": "未发现任何复制目标!"
}, },
"REPOSITORY": { "REPOSITORY": {
"COPY_DIGEST_ID": "复制摘要ID", "COPY_DIGEST_ID": "复制摘要ID",
@ -329,7 +333,8 @@
"DELETED_REPO_SUCCESS": "成功删除镜像仓库。", "DELETED_REPO_SUCCESS": "成功删除镜像仓库。",
"DELETED_TAG_SUCCESS": "成功删除镜像标签。", "DELETED_TAG_SUCCESS": "成功删除镜像标签。",
"COPY": "复制", "COPY": "复制",
"NOTARY_IS_UNDETERMINED": "无法确定镜像标签签名。" "NOTARY_IS_UNDETERMINED": "无法确定镜像标签签名。",
"PLACEHOLDER": "未发现任何镜像库!"
}, },
"ALERT": { "ALERT": {
"FORM_CHANGE_CONFIRMATION": "表单内容改变,确认是否取消?" "FORM_CHANGE_CONFIRMATION": "表单内容改变,确认是否取消?"
@ -424,7 +429,7 @@
"TITLE": "统计", "TITLE": "统计",
"PRO_ITEM": "项目", "PRO_ITEM": "项目",
"REPO_ITEM": "镜像仓库", "REPO_ITEM": "镜像仓库",
"INDEX_MY": "私有", "INDEX_PRIVATE": "私有",
"INDEX_MY_PROJECTS": "我的项目", "INDEX_MY_PROJECTS": "我的项目",
"INDEX_MY_REPOSITORIES": "我的镜像仓库", "INDEX_MY_REPOSITORIES": "我的镜像仓库",
"INDEX_PUB": "公开", "INDEX_PUB": "公开",
@ -467,7 +472,8 @@
"NONE": "无" "NONE": "无"
}, },
"SINGULAR": "缺陷", "SINGULAR": "缺陷",
"PLURAL": "缺陷" "PLURAL": "缺陷",
"PLACEHOLDER": "过滤缺陷"
}, },
"PUSH_IMAGE": { "PUSH_IMAGE": {
"TITLE": "推送镜像", "TITLE": "推送镜像",
@ -483,7 +489,8 @@
"ARCHITECTURE": "架构", "ARCHITECTURE": "架构",
"OS": "操作系统", "OS": "操作系统",
"SCAN_COMPLETION_TIME": "扫描完成时间", "SCAN_COMPLETION_TIME": "扫描完成时间",
"IMAGE_VULNERABILITIES": "镜像缺陷" "IMAGE_VULNERABILITIES": "镜像缺陷",
"PLACEHOLDER": "未发现任何标签!"
}, },
"UNKNOWN_ERROR": "发生未知错误,请稍后再试。", "UNKNOWN_ERROR": "发生未知错误,请稍后再试。",
"UNAUTHORIZED_ERROR": "会话无效或者已经过期, 请重新登录以继续。", "UNAUTHORIZED_ERROR": "会话无效或者已经过期, 请重新登录以继续。",