提交 87129adc 编写于 作者: J Jason Song 提交者: GitHub

Merge pull request #542 from lepdou/modify_app_info

admin can modify app information
......@@ -20,6 +20,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Objects;
@RestController
public class AppController {
......@@ -47,7 +48,7 @@ public class AppController {
return dto;
}
@RequestMapping(path = "/apps/{appId}", method = RequestMethod.DELETE)
@RequestMapping(value = "/apps/{appId}", method = RequestMethod.DELETE)
public void delete(@PathVariable("appId") String appId, @RequestParam String operator) {
App entity = appService.findOne(appId);
if (entity == null) {
......@@ -56,6 +57,15 @@ public class AppController {
appService.delete(entity.getId(), operator);
}
@RequestMapping(value = "/apps/{appId}", method = RequestMethod.PUT)
public void update(@PathVariable String appId, @RequestBody App app) {
if (!Objects.equals(appId, app.getAppId())) {
throw new BadRequestException("The App Id of path variable and request body is different");
}
appService.update(app);
}
@RequestMapping(value = "/apps", method = RequestMethod.GET)
public List<AppDTO> find(@RequestParam(value = "name", required = false) String name,
Pageable pageable) {
......
......@@ -3,6 +3,7 @@ package com.ctrip.framework.apollo.biz.service;
import com.ctrip.framework.apollo.biz.entity.Audit;
import com.ctrip.framework.apollo.biz.repository.AppRepository;
import com.ctrip.framework.apollo.common.entity.App;
import com.ctrip.framework.apollo.common.exception.BadRequestException;
import com.ctrip.framework.apollo.common.exception.ServiceException;
import com.ctrip.framework.apollo.common.utils.BeanUtils;
......@@ -71,14 +72,25 @@ public class AppService {
}
@Transactional
public App update(App app) {
App managedApp = appRepository.findByAppId(app.getAppId());
BeanUtils.copyEntityProperties(app, managedApp);
public void update(App app) {
String appId = app.getAppId();
App managedApp = appRepository.findByAppId(appId);
if (managedApp == null) {
throw new BadRequestException(String.format("App not exists. AppId = %s", appId));
}
managedApp.setName(app.getName());
managedApp.setOrgId(app.getOrgId());
managedApp.setOrgName(app.getOrgName());
managedApp.setOwnerName(app.getOwnerName());
managedApp.setOwnerEmail(app.getOwnerEmail());
managedApp.setDataChangeLastModifiedBy(app.getDataChangeLastModifiedBy());
managedApp = appRepository.save(managedApp);
auditService.audit(App.class.getSimpleName(), managedApp.getId(), Audit.OP.UPDATE,
managedApp.getDataChangeLastModifiedBy());
return managedApp;
}
}
......@@ -58,6 +58,10 @@ public class AdminServiceAPI {
public AppDTO createApp(Env env, AppDTO app) {
return restTemplate.post(env, "apps", app, AppDTO.class);
}
public void updateApp(Env env, AppDTO app) {
restTemplate.put(env, "apps/{appId}", app, app.getAppId());
}
}
......
......@@ -65,6 +65,10 @@ public class PermissionValidator {
}
}
public boolean isAppAdmin(String appId) {
return isSuperAdmin() || hasAssignRolePermission(appId);
}
public boolean isSuperAdmin() {
return rolePermissionService.isSuperAdmin(userInfoHolder.getUser().getUserId());
}
......
......@@ -14,6 +14,7 @@ import com.ctrip.framework.apollo.portal.component.PortalSettings;
import com.ctrip.framework.apollo.portal.entity.model.AppModel;
import com.ctrip.framework.apollo.portal.entity.vo.EnvClusterInfo;
import com.ctrip.framework.apollo.portal.listener.AppCreationEvent;
import com.ctrip.framework.apollo.portal.listener.AppInfoChangedEvent;
import com.ctrip.framework.apollo.portal.service.AppService;
import com.ctrip.framework.apollo.portal.service.RolePermissionService;
import com.ctrip.framework.apollo.portal.spi.UserInfoHolder;
......@@ -24,6 +25,7 @@ import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PathVariable;
......@@ -35,6 +37,7 @@ import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.HttpClientErrorException;
import java.util.List;
import java.util.Objects;
import java.util.Set;
......@@ -69,6 +72,38 @@ public class AppController {
return appService.findByOwnerName(owner, page);
}
@RequestMapping(value = "", method = RequestMethod.POST)
public App create(@RequestBody AppModel appModel) {
App app = transformToApp(appModel);
App createdApp = appService.createAppInLocal(app);
publisher.publishEvent(new AppCreationEvent(createdApp));
Set<String> admins = appModel.getAdmins();
if (!CollectionUtils.isEmpty(admins)) {
rolePermissionService.assignRoleToUsers(RoleUtils.buildAppMasterRoleName(createdApp.getAppId()),
admins, userInfoHolder.getUser().getUserId());
}
return createdApp;
}
@PreAuthorize(value = "@permissionValidator.isAppAdmin(#appId)")
@RequestMapping(value = "/{appId}", method = RequestMethod.PUT)
public void update(@PathVariable String appId, @RequestBody AppModel appModel) {
if (!Objects.equals(appId, appModel.getAppId())) {
throw new BadRequestException("The App Id of path variable and request body is different");
}
App app = transformToApp(appModel);
App updatedApp = appService.updateAppInLocal(app);
publisher.publishEvent(new AppInfoChangedEvent(updatedApp));
}
@RequestMapping(value = "/{appId}/navtree", method = RequestMethod.GET)
public MultiResponseEntity<EnvClusterInfo> nav(@PathVariable String appId) {
......@@ -86,41 +121,6 @@ public class AppController {
return response;
}
@RequestMapping(value = "", method = RequestMethod.POST)
public App create(@RequestBody AppModel appModel) {
String appId = appModel.getAppId();
String appName = appModel.getName();
String ownerName = appModel.getOwnerName();
String orgId = appModel.getOrgId();
String orgName = appModel.getOrgName();
RequestPrecondition.checkArgumentsNotEmpty(appId, appName, ownerName, orgId, orgName);
if (!InputValidator.isValidClusterNamespace(appModel.getAppId())) {
throw new BadRequestException(String.format("AppId格式错误: %s", InputValidator.INVALID_CLUSTER_NAMESPACE_MESSAGE));
}
App app = new App();
app.setAppId(appId);
app.setName(appName);
app.setOwnerName(ownerName);
app.setOrgId(orgId);
app.setOrgName(orgName);
App createdApp = appService.create(app);
publisher.publishEvent(new AppCreationEvent(createdApp));
Set<String> admins = appModel.getAdmins();
if (!CollectionUtils.isEmpty(admins)) {
rolePermissionService
.assignRoleToUsers(RoleUtils.buildAppMasterRoleName(appId), admins, userInfoHolder.getUser().getUserId());
}
return createdApp;
}
@RequestMapping(value = "/envs/{env}", method = RequestMethod.POST, consumes = {
"application/json"})
public ResponseEntity<Void> create(@PathVariable String env, @RequestBody App app) {
......@@ -131,7 +131,7 @@ public class AppController {
throw new BadRequestException(InputValidator.INVALID_CLUSTER_NAMESPACE_MESSAGE);
}
appService.createApp(Env.valueOf(env), app);
appService.createAppInRemote(Env.valueOf(env), app);
return ResponseEntity.ok().build();
}
......@@ -155,7 +155,8 @@ public class AppController {
response.addResponseEntity(RichResponseEntity.ok(env));
} else {
response.addResponseEntity(RichResponseEntity.error(HttpStatus.INTERNAL_SERVER_ERROR,
String.format("load appId:%s from env %s error.", appId,env)
String.format("load appId:%s from env %s error.", appId,
env)
+ e.getMessage()));
}
}
......@@ -164,4 +165,27 @@ public class AppController {
return response;
}
private App transformToApp(AppModel appModel) {
String appId = appModel.getAppId();
String appName = appModel.getName();
String ownerName = appModel.getOwnerName();
String orgId = appModel.getOrgId();
String orgName = appModel.getOrgName();
RequestPrecondition.checkArgumentsNotEmpty(appId, appName, ownerName, orgId, orgName);
if (!InputValidator.isValidClusterNamespace(appModel.getAppId())) {
throw new BadRequestException(String.format("AppId格式错误: %s", InputValidator.INVALID_CLUSTER_NAMESPACE_MESSAGE));
}
App app = new App();
app.setAppId(appId);
app.setName(appName);
app.setOwnerName(ownerName);
app.setOrgId(orgId);
app.setOrgName(orgName);
return app;
}
}
package com.ctrip.framework.apollo.portal.listener;
import com.google.common.base.Preconditions;
import com.ctrip.framework.apollo.common.entity.App;
import org.springframework.context.ApplicationEvent;
public class AppInfoChangedEvent extends ApplicationEvent{
public AppInfoChangedEvent(Object source) {
super(source);
}
public App getApp() {
Preconditions.checkState(source != null);
return (App) this.source;
}
}
package com.ctrip.framework.apollo.portal.listener;
import com.ctrip.framework.apollo.common.dto.AppDTO;
import com.ctrip.framework.apollo.common.utils.BeanUtils;
import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.portal.api.AdminServiceAPI;
import com.ctrip.framework.apollo.portal.component.PortalSettings;
import com.ctrip.framework.apollo.tracer.Tracer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class AppInfoChangedListener {
private static final Logger logger = LoggerFactory.getLogger(AppInfoChangedListener.class);
@Autowired
private AdminServiceAPI.AppAPI appAPI;
@Autowired
private PortalSettings portalSettings;
@EventListener
public void onAppInfoChange(AppInfoChangedEvent event) {
AppDTO appDTO = BeanUtils.transfrom(AppDTO.class, event.getApp());
String appId = appDTO.getAppId();
List<Env> envs = portalSettings.getActiveEnvs();
for (Env env : envs) {
try {
appAPI.updateApp(env, appDTO);
} catch (Throwable e) {
logger.error("Update app's info failed. Env = {}, AppId = {}", env, appId, e);
Tracer.logError(String.format("Update app's info failed. Env = {}, AppId = {}", env, appId), e);
}
}
}
}
......@@ -36,8 +36,8 @@ public class CreationListener {
try {
appAPI.createApp(env, appDTO);
} catch (Throwable e) {
logger.error("call appAPI.createApp error.(appId={appId}, env={env})", appDTO.getAppId(), env, e);
Tracer.logError(String.format("call appAPI.createApp error. (appId=%s, env=%s)", appDTO.getAppId(), env), e);
logger.error("Create app failed. appId = {}, env = {})", appDTO.getAppId(), env, e);
Tracer.logError(String.format("Create app failed. appId = %s, env = %s", appDTO.getAppId(), env), e);
}
}
}
......@@ -50,8 +50,8 @@ public class CreationListener {
try {
namespaceAPI.createAppNamespace(env, appNamespace);
} catch (Throwable e) {
logger.error("call appAPI.createApp error.(appId={appId}, env={env})", appNamespace.getAppId(), env, e);
Tracer.logError(String.format("call appAPI.createApp error. (appId=%s, env=%s)", appNamespace.getAppId(), env), e);
logger.error("Create appNamespace failed. appId = {}, env = {}", appNamespace.getAppId(), env, e);
Tracer.logError(String.format("Create appNamespace failed. appId = %s, env = %s", appNamespace.getAppId(), env), e);
}
}
}
......
......@@ -6,7 +6,6 @@ import com.ctrip.framework.apollo.common.dto.AppDTO;
import com.ctrip.framework.apollo.common.entity.App;
import com.ctrip.framework.apollo.common.exception.BadRequestException;
import com.ctrip.framework.apollo.common.utils.BeanUtils;
import com.ctrip.framework.apollo.common.utils.ExceptionUtils;
import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.portal.api.AdminServiceAPI;
import com.ctrip.framework.apollo.portal.constant.CatEventType;
......@@ -17,13 +16,10 @@ import com.ctrip.framework.apollo.portal.spi.UserInfoHolder;
import com.ctrip.framework.apollo.portal.spi.UserService;
import com.ctrip.framework.apollo.tracer.Tracer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.HttpStatusCodeException;
import java.util.Collections;
import java.util.List;
......@@ -32,8 +28,6 @@ import java.util.Set;
@Service
public class AppService {
private Logger logger = LoggerFactory.getLogger(AppService.class);
@Autowired
private UserInfoHolder userInfoHolder;
@Autowired
......@@ -78,26 +72,17 @@ public class AppService {
return appAPI.loadApp(env, appId);
}
public void createApp(Env env, App app) {
enrichUserInfo(app);
try {
AppDTO appDTO = BeanUtils.transfrom(AppDTO.class, app);
appAPI.createApp(env, appDTO);
} catch (HttpStatusCodeException e) {
logger.error(ExceptionUtils.toString(e));
throw e;
}
}
public void enrichUserInfo(App app) {
public void createAppInRemote(Env env, App app) {
String username = userInfoHolder.getUser().getUserId();
app.setDataChangeCreatedBy(username);
app.setDataChangeLastModifiedBy(username);
}
AppDTO appDTO = BeanUtils.transfrom(AppDTO.class, app);
appAPI.createApp(env, appDTO);
}
@Transactional
public App create(App app) {
public App createAppInLocal(App app) {
String appId = app.getAppId();
App managedApp = appRepository.findByAppId(appId);
......@@ -125,6 +110,33 @@ public class AppService {
return createdApp;
}
@Transactional
public App updateAppInLocal(App app) {
String appId = app.getAppId();
App managedApp = appRepository.findByAppId(appId);
if (managedApp == null) {
throw new BadRequestException(String.format("App not exists. AppId = %s", appId));
}
managedApp.setName(app.getName());
managedApp.setOrgId(app.getOrgId());
managedApp.setOrgName(app.getOrgName());
String ownerName = app.getOwnerName();
UserInfo owner = userService.findByUserId(ownerName);
if (owner == null) {
throw new BadRequestException(String.format("App's owner not exists. owner = %s", ownerName));
}
managedApp.setOwnerName(owner.getUserId());
managedApp.setOwnerEmail(owner.getEmail());
String operator = userInfoHolder.getUser().getUserId();
managedApp.setDataChangeLastModifiedBy(operator);
return appRepository.save(managedApp);
}
public EnvClusterInfo createEnvNavNode(Env env, String appId) {
EnvClusterInfo node = new EnvClusterInfo(env);
node.setClusters(clusterService.findClusters(env, appId));
......
......@@ -25,71 +25,71 @@
创建项目
</header>
<div class="panel-body">
<form class="form-horizontal" name="appForm" ng-controller="CreateAppController" valdr-type="App"
ng-submit="create()">
<div class="form-group">
<label class="col-sm-3 control-label">
<apollorequiredfield></apollorequiredfield>
部门</label>
<div class="col-sm-3">
<select id="organization">
<option></option>
</select>
</div>
<form class="form-horizontal panel-body" name="appForm" ng-controller="CreateAppController"
valdr-type="App"
ng-submit="create()">
<div class="form-group">
<label class="col-sm-3 control-label">
<apollorequiredfield></apollorequiredfield>
部门</label>
<div class="col-sm-3">
<select id="organization">
<option></option>
</select>
</div>
<div class="form-group" valdr-form-group>
<label class="col-sm-3 control-label">
<apollorequiredfield></apollorequiredfield>
应用Id</label>
<div class="col-sm-3">
<input type="text" class="form-control" name="appId" ng-model="app.appId">
<small>(CMS上申请的App Id)</small>
</div>
</div>
<div class="form-group" valdr-form-group>
<label class="col-sm-3 control-label">
<apollorequiredfield></apollorequiredfield>
应用Id</label>
<div class="col-sm-3">
<input type="text" class="form-control" name="appId" ng-model="app.appId">
<small>(CMS上申请的App Id)</small>
</div>
<div class="form-group" valdr-form-group>
<label class="col-sm-3 control-label">
<apollorequiredfield></apollorequiredfield>
应用名称</label>
<div class="col-sm-5">
<input type="text" class="form-control" name="appName" ng-model="app.name">
<small>(建议格式 xx-yy-zz 例:apollo-server)</small>
</div>
</div>
<div class="form-group" valdr-form-group>
<label class="col-sm-3 control-label">
<apollorequiredfield></apollorequiredfield>
应用名称</label>
<div class="col-sm-5">
<input type="text" class="form-control" name="appName" ng-model="app.name">
<small>(建议格式 xx-yy-zz 例:apollo-server)</small>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">
<apollorequiredfield></apollorequiredfield>
应用负责人</label>
<div class="col-sm-6 J_ownerSelectorPanel">
<apollouserselector apollo-id="'ownerSelector'"></apollouserselector>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">
<apollorequiredfield></apollorequiredfield>
应用负责人</label>
<div class="col-sm-6 J_ownerSelectorPanel">
<apollouserselector apollo-id="'ownerSelector'"></apollouserselector>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">项目管理员<br>
<div class="form-group">
<label class="col-sm-3 control-label">项目管理员<br>
</label>
<div class="col-sm-9 J_adminSelectorPanel">
</label>
<div class="col-sm-9 J_adminSelectorPanel">
<apollomultipleuserselector apollo-id="'adminSelector'"></apollomultipleuserselector>
<br>
<small>(应用负责人默认具有项目管理员权限,</small>
<br>
<small>项目管理员可以创建Namespace和集群、分配用户权限)</small>
<apollomultipleuserselector apollo-id="'adminSelector'"></apollomultipleuserselector>
<br>
<small>(应用负责人默认具有项目管理员权限,</small>
<br>
<small>项目管理员可以创建Namespace和集群、分配用户权限)</small>
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-3 col-sm-9">
<div class="form-group">
<div class="col-sm-offset-3 col-sm-9">
<button type="submit" class="btn btn-primary"
ng-disabled="appForm.$invalid || submitBtnDisabled">提交
</button>
</div>
<button type="submit" class="btn btn-primary"
ng-disabled="appForm.$invalid || submitBtnDisabled">提交
</button>
</div>
</form>
</div>
</div>
</form>
</div>
</div>
</div>
......
<!doctype html>
<html ng-app="role">
<html ng-app="setting">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="icon" href="../img/config.png">
......@@ -9,19 +9,19 @@
<link rel="stylesheet" type="text/css" media='all' href="../vendor/angular/loading-bar.min.css">
<link rel="stylesheet" type="text/css" href="../styles/common-style.css">
<link rel="stylesheet" type="text/css" href="../vendor/select2/select2.min.css">
<title>权限管理</title>
<title>项目管理</title>
</head>
<body>
<apollonav></apollonav>
<div class="container-fluid apollo-container">
<section class="panel col-md-offset-1 col-md-10" ng-controller="AppRoleController">
<div class="container-fluid apollo-container project-setting" ng-controller="SettingController">
<section class="col-md-10 col-md-offset-1 panel hidden">
<header class="panel-heading">
<div class="row">
<div class="col-md-7">
<h4 class="modal-title">权限管理<small>(AppId:<label ng-bind="pageContext.appId"></label> )</small>
<h4 class="modal-title">项目管理 ( AppId:<label ng-bind="pageContext.appId"></label> )
</h4>
</div>
<div class="col-md-5 text-right">
......@@ -31,42 +31,127 @@
</div>
</div>
</header>
<div class="panel-body" ng-show="hasAssignUserPermission">
<div class="row">
<div class="form-horizontal">
<div class="form-group">
<label class="col-sm-2 control-label">管理员<br><small>(可以创建Namespace<br>可以创建集群<br>可以分配用户权限)</small></label>
<div class="col-sm-8">
<form class="form-inline" ng-submit="assignMasterRoleToUser()">
<div class="form-group">
<apollouserselector apollo-id="userSelectWidgetId"></apollouserselector>
</div>
<button type="submit" class="btn btn-default" style="margin-left: 20px;" ng-disabled="submitBtnDisabled">添加</button>
</form>
<!-- Split button -->
<div class="item-container">
<div class="btn-group item-info" ng-repeat="user in appRoleUsers.masterUsers">
<button type="button" class="btn btn-default" ng-bind="user.userId"></button>
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false" ng-click="removeMasterRoleFromUser(user.userId)">
<span class="glyphicon glyphicon-remove"></span>
</button>
</div>
</div>
<div class="panel-body row">
<section class="context" ng-show="hasAssignUserPermission">
<!--project admin-->
<section class="form-horizontal">
<h5>管理员
<small>
(项目管理员具有以下权限: 1. 创建Namespace 2. 创建集群 3. 管理项目、Namespace权限)
</small>
</h5>
<hr>
<div class="col-md-offset-1">
<form class="form-inline" ng-submit="assignMasterRoleToUser()">
<div class="form-group" style="padding-left: 15px">
<apollouserselector apollo-id="userSelectWidgetId"></apollouserselector>
</div>
<button type="submit" class="btn btn-default" style="margin-left: 20px;"
ng-disabled="submitBtnDisabled">添加
</button>
</form>
<!-- Split button -->
<div class="item-container">
<div class="btn-group item-info" ng-repeat="user in appRoleUsers.masterUsers">
<button type="button" class="btn btn-default" ng-bind="user.userId"></button>
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false"
ng-click="removeMasterRoleFromUser(user.userId)">
<span class="glyphicon glyphicon-remove"></span>
</button>
</div>
</div>
</div>
</div>
</div>
</section>
<!--application info-->
<section>
<h5>基本信息</h5>
<hr>
<form class="form-horizontal" name="appForm" valdr-type="App"
ng-submit="updateAppInfo()">
<div class="form-group" valdr-form-group>
<label class="col-sm-2 control-label">
<apollorequiredfield></apollorequiredfield>
AppId</label>
<div class="col-sm-3">
<label class="form-control-static" ng-bind="pageContext.appId">
</label>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">
<apollorequiredfield></apollorequiredfield>
部门</label>
<div class="col-sm-3">
<select id="organization" ng-disabled="!display.app.edit">
<option></option>
</select>
</div>
</div>
<div class="form-group" valdr-form-group>
<label class="col-sm-2 control-label">
<apollorequiredfield></apollorequiredfield>
项目名称</label>
<div class="col-sm-4">
<input type="text" class="form-control" name="appName" ng-model="viewApp.name"
ng-disabled="!display.app.edit">
<small>(建议格式 xx-yy-zz 例:apollo-server)</small>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">
<apollorequiredfield></apollorequiredfield>
项目负责人</label>
<div class="col-sm-6 J_ownerSelectorPanel">
<apollouserselector apollo-id="'ownerSelector'"
disabled="!display.app.edit"></apollouserselector>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-9">
<button type="button" class="btn btn-primary"
ng-show="!display.app.edit"
ng-click="toggleEditStatus()">
修改项目信息
</button>
<button type="button" class="btn btn-warning"
ng-show="display.app.edit"
ng-click="toggleEditStatus()">
取消修改
</button>
<button type="submit" class="btn btn-primary"
ng-show="display.app.edit"
ng-disabled="appForm.$invalid || submitBtnDisabled">
提交
</button>
</div>
</div>
</form>
</section>
</section>
<section class="context" ng-show="!hasAssignUserPermission">
<div class="panel-body text-center">
<h4>您没有权限操作,请找 [{{admins.join(',')}}] 开通权限</h4>
</div>
</section>
</div>
<div class="panel-body text-center" ng-show="!hasAssignUserPermission">
<h2>您没有权限哟!</h2>
</div>
<apolloconfirmdialog apollo-dialog-id="'warning'" apollo-title="'删除管理员'"
apollo-detail="'不能删除所有的管理员'" apollo-confirm=""
apollo-detail="'不能删除所有的管理员'"
apollo-show-cancel-btn="false"></apolloconfirmdialog>
</section>
</div>
......@@ -83,12 +168,16 @@
<script src="../vendor/angular/angular-toastr-1.4.1.tpls.min.js"></script>
<script src="../vendor/angular/loading-bar.min.js"></script>
<!--valdr-->
<script src="../vendor/valdr/valdr.min.js" type="text/javascript"></script>
<script src="../vendor/valdr/valdr-message.min.js" type="text/javascript"></script>
<!-- bootstrap.js -->
<script src="../vendor/bootstrap/js/bootstrap.min.js" type="text/javascript"></script>
<!--nicescroll-->
<script src="../vendor/jquery.nicescroll.min.js"></script>
<script src="../vendor/lodash.min.js"></script>
<script src="../vendor/select2/select2.min.js" type="text/javascript"></script>
<!--biz-->
......@@ -98,12 +187,14 @@
<script type="application/javascript" src="../scripts/services/EnvService.js"></script>
<script type="application/javascript" src="../scripts/services/UserService.js"></script>
<script type="application/javascript" src="../scripts/services/PermissionService.js"></script>
<script type="application/javascript" src="../scripts/services/OrganizationService.js"></script>
<script type="application/javascript" src="../scripts/AppUtils.js"></script>
<script type="application/javascript" src="../scripts/PageCommon.js"></script>
<script type="application/javascript" src="../scripts/directive/directive.js"></script>
<script type="application/javascript" src="../scripts/valdr.js"></script>
<script type="application/javascript" src="../scripts/controller/role/AppRoleController.js"></script>
<script type="application/javascript" src="../scripts/controller/SettingController.js"></script>
</body>
</html>
......@@ -34,7 +34,7 @@
<!--env list-->
<section class="panel">
<header class="panel-heading">
<img src="img/env.png" class="i-20">&nbsp;环境列表
环境列表
<span class="pull-right"
data-tooltip="tooltip" data-placement="bottom" title="通过切换环境、集群来管理不同环境、集群的配置">
<img src="img/question.png" class="i-20"/>
......@@ -47,16 +47,22 @@
<!--app info-->
<section class="panel">
<header class="panel-heading">
<img src="img/info.png" class="i-25-20"/> 项目信息
<span class="pull-right cursor-pointer">
<a ng-if="!favoriteId" ng-click="addFavorite()"
data-tooltip="tooltip" data-placement="bottom" title="收藏">
<img src="img/unlike.png" class="i-20"/>
</a>
<a ng-if="favoriteId" ng-click="deleteFavorite()"
data-tooltip="tooltip" data-placement="bottom" title="取消收藏">
<img src="img/like.png" class="i-20"/>
项目信息
<span class="pull-right">
<a href="/app/setting.html?#/appid={{pageContext.appId}}"
style="margin-right: 5px;text-decoration:none;">
<img src="img/edit.png" class="i-20 cursor-pointer"
data-tooltip="tooltip" data-placement="bottom" title="修改项目基本信息"/>
</a>
<img src="img/unlike.png" class="i-20 cursor-pointer"
ng-if="!favoriteId" ng-click="addFavorite()"
data-tooltip="tooltip" data-placement="bottom" title="收藏"/>
<img src="img/like.png" class="i-20 cursor-pointer"
ng-if="favoriteId" ng-click="deleteFavorite()"
data-tooltip="tooltip" data-placement="bottom" title="取消收藏"/>
</span>
</header>
<div class="panel-body">
......@@ -101,6 +107,9 @@
<!--operation entrance-->
<section>
<apolloentrance apollo-title="'管理项目'" apollo-img-src="'project-manage'"
apollo-href="'/app/setting.html?#/appid=' + pageContext.appId"></apolloentrance>
<a class="list-group-item" ng-show="missEnvs.length > 0" ng-click="createAppInMissEnv()">
<div class="row icon-text icon-plus-orange">
<p class="btn-title ng-binding">补缺环境</p>
......@@ -129,9 +138,7 @@
</div>
</div>
<apolloentrance apollo-title="'项目权限'" apollo-img-src="'user-manage'"
apollo-href="'/app/role.html?#/appid=' + pageContext.appId"
ng-show="hasAssignUserPermission"></apolloentrance>
</section>
</div>
......@@ -169,14 +176,14 @@
<span ng-bind="pageContext.clusterName"></span>
集群的实例只会使用
<span ng-bind="pageContext.clusterName"></span>
集群(当前页面)的配置,只有当对应namespace在当前集群没有发布过配置时,才会使用default集群的配置
集群(当前页面)的配置,只有当对应namespace在当前集群没有发布过配置时,才会使用default集群的配置
</div>
</div>
<div class="alert alert-info"
ng-if="hasNotPublishNamespace">
<p><b>注意:</b>以下环境/集群有未发布的配置,客户端获取不到未发布的配置,请及时发布。</p>
<p><b>注意:</b> 以下环境/集群有未发布的配置,客户端获取不到未发布的配置,请及时发布。</p>
<p>
<mark ng-bind="namespacePublishInfo.join(',')"></mark>
</p>
......
<svg height="1024" width="896" xmlns="http://www.w3.org/2000/svg">
<path d="M128 768h256v64H128v-64z m320-384H128v64h320v-64z m128 192V448L384 640l192 192V704h320V576H576z m-288-64H128v64h160v-64zM128 704h160v-64H128v64z m576 64h64v128c-1 18-7 33-19 45s-27 18-45 19H64c-35 0-64-29-64-64V192c0-35 29-64 64-64h192C256 57 313 0 384 0s128 57 128 128h192c35 0 64 29 64 64v320h-64V320H64v576h640V768zM128 256h512c0-35-29-64-64-64h-64c-35 0-64-29-64-64s-29-64-64-64-64 29-64 64-29 64-64 64h-64c-35 0-64 29-64 64z" />
</svg>
......@@ -28,6 +28,12 @@ appUtil.service('AppUtil', ['toastr', '$window', function (toastr, $window) {
if (query.indexOf('/') == 0) {
query = query.substring(1, query.length);
}
var anchorIndex = query.indexOf('#');
if (anchorIndex >= 0) {
query = query.substring(0, anchorIndex);
}
var params = query.split("&");
var result = {};
params.forEach(function (param) {
......
......@@ -20,6 +20,8 @@ var sync_item_module = angular.module('sync_item', ['app.service', 'apollo.direc
var namespace_module = angular.module('namespace', ['app.service', 'apollo.directive', 'app.util', 'toastr', 'angular-loading-bar', 'valdr']);
//server config
var server_config_module = angular.module('server_config', ['app.service', 'apollo.directive', 'app.util', 'toastr', 'angular-loading-bar']);
//setting
var setting_module = angular.module('setting', ['app.service', 'apollo.directive', 'app.util', 'toastr', 'angular-loading-bar', 'valdr']);
//role
var role_module = angular.module('role', ['app.service', 'apollo.directive', 'app.util', 'toastr', 'angular-loading-bar']);
//cluster
......
......@@ -51,7 +51,7 @@ function createAppController($scope, $window, toastr, AppService, AppUtil, Organ
// owner
var owner = $('.ownerSelector').select2('data')[0];
if (!owner) {
toastr.warning("输入应用负责人");
toastr.warning("选择应用负责人");
return;
}
$scope.app.ownerName = owner.id;
......
setting_module.controller('SettingController',
['$scope', '$location', 'toastr',
'AppService', 'AppUtil', 'PermissionService',
'OrganizationService',
SettingController]);
function SettingController($scope, $location, toastr,
AppService, AppUtil, PermissionService,
OrganizationService) {
var params = AppUtil.parseParams($location.$$url);
var $orgWidget = $('#organization');
$scope.pageContext = {
appId: params.appid
};
$scope.display = {
app: {
edit: false
}
};
$scope.submitBtnDisabled = false;
$scope.userSelectWidgetId = 'toAssignMasterRoleUser';
$scope.assignMasterRoleToUser = assignMasterRoleToUser;
$scope.removeMasterRoleFromUser = removeMasterRoleFromUser;
$scope.toggleEditStatus = toggleEditStatus;
$scope.updateAppInfo = updateAppInfo;
init();
function init() {
initOrganization();
initPermission();
initAdmins();
initApplication();
}
function initOrganization() {
OrganizationService.find_organizations().then(function (result) {
var organizations = [];
result.forEach(function (item) {
var org = {};
org.id = item.orgId;
org.text = item.orgName + '(' + item.orgId + ')';
org.name = item.orgName;
organizations.push(org);
});
$orgWidget.select2({
placeholder: '请选择部门',
width: '100%',
data: organizations
});
}, function (result) {
toastr.error(AppUtil.errorMsg(result), "load organizations error");
});
}
function initPermission() {
PermissionService.has_assign_user_permission($scope.pageContext.appId)
.then(function (result) {
$scope.hasAssignUserPermission = result.hasPermission;
});
}
function initAdmins() {
PermissionService.get_app_role_users($scope.pageContext.appId)
.then(function (result) {
$scope.appRoleUsers = result;
$scope.admins = [];
$scope.appRoleUsers.masterUsers.forEach(function (user) {
$scope.admins.push(user.userId);
});
});
}
function initApplication() {
AppService.load($scope.pageContext.appId).then(function (app) {
$scope.app = app;
$scope.viewApp = _.clone(app);
initAppForm(app);
$('.project-setting .panel').removeClass('hidden');
})
}
function initAppForm(app) {
$orgWidget.val(app.orgId).trigger("change");
var $ownerSelector = $('.ownerSelector');
var defaultSelectedDOM = '<option value="' + app.ownerName + '" selected="selected">' + app.ownerName
+ '</option>';
$ownerSelector.append(defaultSelectedDOM);
$ownerSelector.trigger('change');
}
function assignMasterRoleToUser() {
var user = $('.' + $scope.userSelectWidgetId).select2('data')[0];
if (!user) {
toastr.warning("请选择用户");
return;
}
var toAssignMasterRoleUser = user.id;
$scope.submitBtnDisabled = true;
PermissionService.assign_master_role($scope.pageContext.appId,
toAssignMasterRoleUser)
.then(function (result) {
$scope.submitBtnDisabled = false;
toastr.success("添加成功");
$scope.appRoleUsers.masterUsers.push({userId: toAssignMasterRoleUser});
$('.' + $scope.userSelectWidgetId).select2("val", "");
}, function (result) {
$scope.submitBtnDisabled = false;
toastr.error(AppUtil.errorMsg(result), "添加失败");
});
}
function removeMasterRoleFromUser(user) {
if ($scope.appRoleUsers.masterUsers.length <= 1) {
$('#warning').modal('show');
return;
}
PermissionService.remove_master_role($scope.pageContext.appId, user)
.then(function (result) {
toastr.success("删除成功");
removeUserFromList($scope.appRoleUsers.masterUsers, user);
}, function (result) {
toastr.error(AppUtil.errorMsg(result), "删除失败");
});
}
function removeUserFromList(list, user) {
var index = 0;
for (var i = 0; i < list.length; i++) {
if (list[i].userId == user) {
index = i;
break;
}
}
list.splice(index, 1);
}
function toggleEditStatus() {
if ($scope.display.app.edit) {//cancel edit
$scope.viewApp = _.clone($scope.app);
initAppForm($scope.viewApp);
} else {//edit
}
$scope.display.app.edit = !$scope.display.app.edit;
}
function updateAppInfo() {
$scope.submitBtnDisabled = true;
var app = $scope.viewApp;
var selectedOrg = $orgWidget.select2('data')[0];
if (!selectedOrg.id) {
toastr.warning("请选择部门");
return;
}
app.orgId = selectedOrg.id;
app.orgName = selectedOrg.name;
// owner
var owner = $('.ownerSelector').select2('data')[0];
if (!owner) {
toastr.warning("请选择应用负责人");
return;
}
app.ownerName = owner.id;
AppService.update(app).then(function (app) {
toastr.success("修改成功");
initApplication();
$scope.display.app.edit = false;
$scope.submitBtnDisabled = false;
}, function (result) {
AppUtil.showErrorMsg(result);
$scope.submitBtnDisabled = false;
})
}
}
role_module.controller('AppRoleController',
['$scope', '$location', '$window', 'toastr', 'AppService', 'AppUtil', 'PermissionService',
function ($scope, $location, $window, toastr, AppService, AppUtil, PermissionService) {
var params = AppUtil.parseParams($location.$$url);
$scope.pageContext = {
appId: params.appid
};
$scope.submitBtnDisabled = false;
$scope.userSelectWidgetId = 'toAssignMasterRoleUser';
PermissionService.has_assign_user_permission($scope.pageContext.appId)
.then(function (result) {
$scope.hasAssignUserPermission = result.hasPermission;
}, function (reslt) {
});
PermissionService.get_app_role_users($scope.pageContext.appId)
.then(function (result) {
$scope.appRoleUsers = result;
}, function (result) {
});
$scope.assignMasterRoleToUser = function () {
var user = $('.' + $scope.userSelectWidgetId).select2('data')[0];
if (!user){
toastr.warning("请选择用户");
return;
}
var toAssignMasterRoleUser = user.id;
$scope.submitBtnDisabled = true;
PermissionService.assign_master_role($scope.pageContext.appId,
toAssignMasterRoleUser)
.then(function (result) {
$scope.submitBtnDisabled = false;
toastr.success("添加成功");
$scope.appRoleUsers.masterUsers.push({userId: toAssignMasterRoleUser});
$('.' + $scope.userSelectWidgetId).select2("val", "");
}, function (result) {
$scope.submitBtnDisabled = false;
toastr.error(AppUtil.errorMsg(result), "添加失败");
});
};
$scope.removeMasterRoleFromUser = function (user) {
if ($scope.appRoleUsers.masterUsers.length <= 1) {
$('#warning').modal('show');
return;
}
PermissionService.remove_master_role($scope.pageContext.appId, user)
.then(function (result) {
toastr.success("删除成功");
removeUserFromList($scope.appRoleUsers.masterUsers, user);
}, function (result) {
toastr.error(AppUtil.errorMsg(result), "删除失败");
});
};
function removeUserFromList(list, user) {
var index = 0;
for (var i = 0; i < list.length; i++) {
if (list[i].userId == user) {
index = i;
break;
}
}
list.splice(index, 1);
}
}]);
......@@ -275,13 +275,14 @@ directive_module.directive('apollouserselector', function ($compile, $window) {
transclude: true,
replace: true,
scope: {
id: '=apolloId'
id: '=apolloId',
disabled: '='
},
link: function (scope, element, attrs) {
scope.$watch("id", initSelect2);
var searchUsersAjax = {
var select2Options = {
ajax: {
url: '/users',
dataType: 'json',
......@@ -311,8 +312,9 @@ directive_module.directive('apollouserselector', function ($compile, $window) {
};
function initSelect2() {
$('.' + scope.id).select2(searchUsersAjax);
$('.' + scope.id).select2(select2Options);
}
}
}
......
......@@ -11,7 +11,7 @@ appService.service('AppService', ['$resource', '$q', function ($resource, $q) {
url: '/apps/by-owner'
},
load_navtree: {
methode: 'GET',
method: 'GET',
isArray: false,
url: '/apps/:appId/navtree'
},
......@@ -23,6 +23,10 @@ appService.service('AppService', ['$resource', '$q', function ($resource, $q) {
method: 'POST',
url: '/apps'
},
update_app: {
method: 'PUT',
url: '/apps/:appId'
},
create_app_remote: {
method: 'POST',
url: '/apps/envs/:env'
......@@ -78,6 +82,17 @@ appService.service('AppService', ['$resource', '$q', function ($resource, $q) {
});
return d.promise;
},
update: function (app) {
var d = $q.defer();
app_resource.update_app({
appId: app.appId
}, app, function (result) {
d.resolve(result);
}, function (result) {
d.reject(result);
});
return d.promise;
},
create_remote: function (env, app) {
var d = $q.defer();
app_resource.create_app_remote({env: env}, app, function (result) {
......
app_module.config(function (valdrProvider) {
app_module.config(appValdr);
setting_module.config(appValdr);
function appValdr(valdrProvider) {
valdrProvider.addConstraints({
'App': {
'appId': {
......@@ -21,7 +24,7 @@ app_module.config(function (valdrProvider) {
}
}
})
});
}
cluster_module.config(function (valdrProvider) {
valdrProvider.addConstraints({
......
......@@ -144,6 +144,7 @@ pre {
.footer {
width: 100%;
height: 75px;
margin-top: 50px;
padding-top: 25px;
}
......@@ -494,14 +495,15 @@ table th {
margin: 0;
}
.list-group-item .icon-plus-orange {
background: url(../img/plus-orange.png) no-repeat;
.list-group-item .icon-project-manage {
background: url(../img/manage.png) no-repeat;
}
.list-group-item .icon-user-manage {
background: url(../img/user_manage.png) no-repeat;
.list-group-item .icon-plus-orange {
background: url(../img/add.png) no-repeat;
}
.list-group-item .icon-text {
background-size: 20px;
background-position: 5% 50%;
......@@ -790,3 +792,11 @@ table th {
background: #fff;
}
.project-setting .panel-body {
padding-top: 35px
}
.project-setting .panel-body .context {
padding-left: 30px;
}
......@@ -22,14 +22,14 @@
<ul class="nav navbar-nav navbar-right">
<li>
<a href="http://conf.ctripcorp.com/display/FRAM/Apollo" target="_blank">
<span class="glyphicon glyphicon-question-sign"></span>帮助
<span class="glyphicon glyphicon-question-sign"></span> 帮助
</a>
</li>
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
aria-expanded="false"><span class="glyphicon glyphicon-user"></span>{{userName}} <span
aria-expanded="false"><span class="glyphicon glyphicon-user"></span> {{userName}} <span
class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="/user/logout">退出</a></li>
......@@ -39,7 +39,7 @@
<div class="navbar-form navbar-right form-inline" role="search">
<div class="form-group">
<input type="text" class="form-control search-input" placeholder="搜索项目(项目ID、项目名)"
<input type="text" class="form-control search-input" placeholder="搜索项目(AppId、项目名)"
style="width: 350px"
ng-model="searchKey" ng-change="changeSearchKey()" ng-focus="changeSearchKey()">
......
<select class="{{id}}" style="width: 450px;">
<select class="{{id}}" style="width: 450px;" ng-disabled="disabled">
</select>
......@@ -51,7 +51,7 @@ public class ServiceExceptionTest extends AbstractUnitTest {
new HttpServerErrorException(HttpStatus.INTERNAL_SERVER_ERROR, "admin server error",
new Gson().toJson(errorAttributes).getBytes(), Charset.defaultCharset());
when(appService.create(any())).thenThrow(adminException);
when(appService.createAppInLocal(any())).thenThrow(adminException);
AppModel app = generateSampleApp();
try {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册