commit
96fec3ab27
1
.gitignore
vendored
1
.gitignore
vendored
@ -12,3 +12,4 @@ target
|
||||
node_modules
|
||||
test/derby.log
|
||||
derby.log
|
||||
work
|
||||
|
@ -97,9 +97,12 @@ Contributors are welcomed to join Nacos project. Please check [CONTRIBUTING](./C
|
||||
* users-nacos@googlegroups.com: Nacos usage general discussion.
|
||||
* dev-nacos@googlegroups.com: Nacos developer discussion (APIs, feature design, etc).
|
||||
* commits-nacos@googlegroups.com: Commits notice, very high frequency.
|
||||
* Join us from DingDing.
|
||||
* Join us from DingDing(Group 1: 21708933(full), Group 2: 30438813).
|
||||
|
||||
![cwex](https://img.alicdn.com/tfs/TB1bpBlQmrqK1RjSZK9XXXyypXa-830-972.png_288x480q80.jpg)
|
||||
## Download
|
||||
|
||||
- [Github Release](https://github.com/alibaba/nacos/releases)
|
||||
- [Baidu Netdisk](https://pan.baidu.com/s/1186nmlqPGows9gUZKAx8Zw) Fetch Code : `rest`
|
||||
|
||||
|
||||
## Who is using
|
||||
|
@ -16,7 +16,7 @@
|
||||
<parent>
|
||||
<artifactId>nacos-all</artifactId>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<version>1.2.0-SNAPSHOT</version>
|
||||
<version>1.2.0</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -16,7 +16,7 @@
|
||||
<parent>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-all</artifactId>
|
||||
<version>1.2.0-SNAPSHOT</version>
|
||||
<version>1.2.0</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -96,6 +96,17 @@ public @interface NacosProperties {
|
||||
*/
|
||||
String ENABLE_REMOTE_SYNC_CONFIG = "enableRemoteSyncConfig";
|
||||
|
||||
/**
|
||||
* The property name of "username"
|
||||
*/
|
||||
String USERNAME = "username";
|
||||
|
||||
/**
|
||||
* The property name of "password"
|
||||
*/
|
||||
String PASSWORD = "password";
|
||||
|
||||
|
||||
/**
|
||||
* The placeholder of endpoint, the value is <code>"${nacos.endpoint:}"</code>
|
||||
*/
|
||||
@ -156,6 +167,16 @@ public @interface NacosProperties {
|
||||
*/
|
||||
String ENABLE_REMOTE_SYNC_CONFIG_PLACEHOLDER = "${" + PREFIX + ENABLE_REMOTE_SYNC_CONFIG + ":}";
|
||||
|
||||
/**
|
||||
* The placeholder of endpoint, the value is <code>"${nacos.username:}"</code>
|
||||
*/
|
||||
String USERNAME_PLACEHOLDER = "${" + PREFIX + USERNAME + ":}";
|
||||
|
||||
/**
|
||||
* The placeholder of endpoint, the value is <code>"${nacos.password:}"</code>
|
||||
*/
|
||||
String PASSWORD_PLACEHOLDER = "${" + PREFIX + PASSWORD + ":}";
|
||||
|
||||
/**
|
||||
* The property of "endpoint"
|
||||
*
|
||||
@ -252,4 +273,20 @@ public @interface NacosProperties {
|
||||
*/
|
||||
String enableRemoteSyncConfig() default ENABLE_REMOTE_SYNC_CONFIG_PLACEHOLDER;
|
||||
|
||||
/**
|
||||
* The property of "username"
|
||||
*
|
||||
* @return empty as default value
|
||||
* @see #USERNAME_PLACEHOLDER
|
||||
*/
|
||||
String username() default USERNAME_PLACEHOLDER;
|
||||
|
||||
/**
|
||||
* The property of "password"
|
||||
*
|
||||
* @return empty as default value
|
||||
* @see #PASSWORD_PLACEHOLDER
|
||||
*/
|
||||
String password() default PASSWORD_PLACEHOLDER;
|
||||
|
||||
}
|
||||
|
@ -16,7 +16,7 @@
|
||||
<parent>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-all</artifactId>
|
||||
<version>1.2.0-SNAPSHOT</version>
|
||||
<version>1.2.0</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
@ -232,7 +232,9 @@ public class ServerHttpAgent implements HttpAgent {
|
||||
}
|
||||
|
||||
private String getUrl(String serverAddr, String relativePath) {
|
||||
return serverAddr + "/" + serverListMgr.getContentPath() + relativePath;
|
||||
String contextPath = serverListMgr.getContentPath().startsWith("/") ?
|
||||
serverListMgr.getContentPath() : "/" + serverListMgr.getContentPath();
|
||||
return serverAddr + contextPath + relativePath;
|
||||
}
|
||||
|
||||
public static String getAppname() {
|
||||
@ -274,14 +276,13 @@ public class ServerHttpAgent implements HttpAgent {
|
||||
}
|
||||
|
||||
private void injectSecurityInfo(List<String> params) {
|
||||
ArrayList<String> list = (ArrayList) params;
|
||||
if (StringUtils.isNotBlank(securityProxy.getAccessToken())) {
|
||||
list.add(Constants.ACCESS_TOKEN);
|
||||
list.add(securityProxy.getAccessToken());
|
||||
params.add(Constants.ACCESS_TOKEN);
|
||||
params.add(securityProxy.getAccessToken());
|
||||
}
|
||||
if (StringUtils.isNotBlank(namespaceId)) {
|
||||
list.add("tenant");
|
||||
list.add(namespaceId);
|
||||
if (StringUtils.isNotBlank(namespaceId) && !params.contains(SpasAdapter.TENANT_KEY)) {
|
||||
params.add(SpasAdapter.TENANT_KEY);
|
||||
params.add(namespaceId);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -359,7 +359,10 @@ public class ClientWorker {
|
||||
*/
|
||||
List<String> checkUpdateConfigStr(String probeUpdateString, boolean isInitializingCacheList) throws IOException {
|
||||
|
||||
List<String> params = Arrays.asList(Constants.PROBE_MODIFY_REQUEST, probeUpdateString);
|
||||
|
||||
List<String> params = new ArrayList<String>(2);
|
||||
params.add(Constants.PROBE_MODIFY_REQUEST);
|
||||
params.add(probeUpdateString);
|
||||
|
||||
List<String> headers = new ArrayList<String>(2);
|
||||
headers.add("Long-Pulling-Timeout");
|
||||
@ -516,6 +519,7 @@ public class ClientWorker {
|
||||
|
||||
// check server config
|
||||
List<String> changedGroupKeys = checkUpdateDataIds(cacheDatas, inInitializingCacheList);
|
||||
LOGGER.info("get changedGroupKeys:" + changedGroupKeys);
|
||||
|
||||
for (String groupKey : changedGroupKeys) {
|
||||
String[] key = GroupKey.parseKey(groupKey);
|
||||
|
@ -101,5 +101,5 @@ public class SpasAdapter {
|
||||
}
|
||||
|
||||
private static final String GROUP_KEY = "group";
|
||||
private static final String TENANT_KEY = "tenant";
|
||||
public static final String TENANT_KEY = "tenant";
|
||||
}
|
||||
|
@ -128,6 +128,7 @@ public class NamingProxy {
|
||||
}, 0, securityInfoRefreshIntervalMills, TimeUnit.MILLISECONDS);
|
||||
|
||||
refreshSrvIfNeed();
|
||||
securityProxy.login(getServerList());
|
||||
}
|
||||
|
||||
public List<String> getServerListFromEndpoint() {
|
||||
|
@ -28,10 +28,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.net.HttpURLConnection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
@ -87,6 +84,7 @@ public class SecurityProxy {
|
||||
username = properties.getProperty(PropertyKeyConst.USERNAME, StringUtils.EMPTY);
|
||||
password = properties.getProperty(PropertyKeyConst.PASSWORD, StringUtils.EMPTY);
|
||||
contextPath = properties.getProperty(PropertyKeyConst.CONTEXT_PATH, "/nacos");
|
||||
contextPath = contextPath.startsWith("/") ? contextPath : "/" + contextPath;
|
||||
}
|
||||
|
||||
public boolean login(List<String> servers) {
|
||||
@ -102,7 +100,7 @@ public class SecurityProxy {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
} catch (Throwable ignore) {
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -111,7 +109,9 @@ public class SecurityProxy {
|
||||
public boolean login(String server) {
|
||||
|
||||
if (StringUtils.isNotBlank(username)) {
|
||||
String body = "username=" + username + "&password=" + password;
|
||||
Map<String, String> params = new HashMap<String, String>(2);
|
||||
params.put("username", username);
|
||||
String body = "password=" + password;
|
||||
String url = "http://" + server + contextPath + LOGIN_URL;
|
||||
|
||||
if (server.contains(Constants.HTTP_PREFIX)) {
|
||||
@ -119,7 +119,7 @@ public class SecurityProxy {
|
||||
}
|
||||
|
||||
HttpClient.HttpResult result = HttpClient.request(url, new ArrayList<String>(2),
|
||||
new HashMap<String, String>(2), body, Charsets.UTF_8.name(), HttpMethod.POST);
|
||||
params, body, Charsets.UTF_8.name(), HttpMethod.POST);
|
||||
|
||||
if (result.code != HttpURLConnection.HTTP_OK) {
|
||||
SECURITY_LOGGER.error("login failed: {}", JSON.toJSONString(result));
|
||||
|
@ -20,6 +20,7 @@ import com.alibaba.nacos.api.SystemPropertyKeyConst;
|
||||
import com.alibaba.nacos.api.common.Constants;
|
||||
import com.alibaba.nacos.client.config.impl.HttpSimpleClient;
|
||||
import org.slf4j.Logger;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.Properties;
|
||||
@ -174,12 +175,12 @@ public class ParamUtil {
|
||||
@Override
|
||||
public String call() {
|
||||
String namespace = System.getenv(PropertyKeyConst.SystemEnv.ALIBABA_ALIWARE_NAMESPACE);
|
||||
return org.apache.commons.lang3.StringUtils.isNotBlank(namespace) ? namespace : StringUtils.EMPTY;
|
||||
return StringUtils.isNotBlank(namespace) ? namespace : StringUtils.EMPTY;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (org.apache.commons.lang3.StringUtils.isBlank(namespaceTmp)) {
|
||||
if (StringUtils.isBlank(namespaceTmp)) {
|
||||
namespaceTmp = properties.getProperty(PropertyKeyConst.NAMESPACE);
|
||||
}
|
||||
return StringUtils.isNotBlank(namespaceTmp) ? namespaceTmp.trim() : StringUtils.EMPTY;
|
||||
|
@ -36,14 +36,15 @@ public class NamingTest {
|
||||
public void testServiceList() throws Exception {
|
||||
|
||||
Properties properties = new Properties();
|
||||
properties.put(PropertyKeyConst.SERVER_ADDR, "11.160.165.126:8848");
|
||||
properties.put(PropertyKeyConst.NAMESPACE, "t1");
|
||||
properties.put(PropertyKeyConst.SERVER_ADDR, "127.0.0.1:8848");
|
||||
properties.put(PropertyKeyConst.USERNAME, "nacos");
|
||||
properties.put(PropertyKeyConst.PASSWORD, "nacos");
|
||||
|
||||
NamingService namingService = NacosFactory.createNamingService(properties);
|
||||
|
||||
Instance instance = new Instance();
|
||||
instance.setIp("1.1.1.1");
|
||||
instance.setPort(80);
|
||||
instance.setPort(800);
|
||||
instance.setWeight(2);
|
||||
Map<String, String> map = new HashMap<String, String>();
|
||||
map.put("netType", "external");
|
||||
@ -56,7 +57,6 @@ public class NamingTest {
|
||||
// expressionSelector.setExpression("INSTANCE.metadata.registerSource = 'dubbo'");
|
||||
// ListView<String> serviceList = namingService.getServicesOfServer(1, 10, expressionSelector);
|
||||
|
||||
Thread.sleep(1000000000L);
|
||||
}
|
||||
|
||||
|
||||
|
@ -18,7 +18,7 @@
|
||||
<parent>
|
||||
<artifactId>nacos-all</artifactId>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<version>1.2.0-SNAPSHOT</version>
|
||||
<version>1.2.0</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -18,7 +18,7 @@
|
||||
<parent>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-all</artifactId>
|
||||
<version>1.2.0-SNAPSHOT</version>
|
||||
<version>1.2.0</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -17,7 +17,7 @@
|
||||
<parent>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-all</artifactId>
|
||||
<version>1.2.0-SNAPSHOT</version>
|
||||
<version>1.2.0</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -15,7 +15,6 @@
|
||||
*/
|
||||
package com.alibaba.nacos.config.server.auth;
|
||||
|
||||
import com.alibaba.nacos.api.common.Constants;
|
||||
import com.alibaba.nacos.core.auth.Resource;
|
||||
import com.alibaba.nacos.core.auth.ResourceParser;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@ -39,13 +38,13 @@ public class ConfigResourceParser implements ResourceParser {
|
||||
String groupName = req.getParameter("group");
|
||||
String dataId = req.getParameter("dataId");
|
||||
|
||||
if (StringUtils.isBlank(namespaceId)) {
|
||||
namespaceId = Constants.DEFAULT_NAMESPACE_ID;
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append(namespaceId).append(Resource.SPLITTER);
|
||||
if (StringUtils.isNotBlank(namespaceId)) {
|
||||
sb.append(namespaceId);
|
||||
}
|
||||
|
||||
sb.append(Resource.SPLITTER);
|
||||
|
||||
if (StringUtils.isBlank(dataId)) {
|
||||
sb.append("*")
|
||||
|
@ -177,6 +177,7 @@ public class ConfigController {
|
||||
String tenant,
|
||||
@RequestParam(value = "tag", required = false) String tag)
|
||||
throws IOException, ServletException, NacosException {
|
||||
tenant = processTenant(tenant);
|
||||
// check params
|
||||
ParamUtils.checkParam(dataId, group, "datumId", "content");
|
||||
ParamUtils.checkParam(tag);
|
||||
@ -282,6 +283,8 @@ public class ConfigController {
|
||||
throw new IllegalArgumentException("invalid probeModify");
|
||||
}
|
||||
|
||||
log.info("listen config id:" + probeModify);
|
||||
|
||||
probeModify = URLDecoder.decode(probeModify, Constants.ENCODE);
|
||||
|
||||
Map<String, String> clientMd5Map;
|
||||
@ -291,6 +294,8 @@ public class ConfigController {
|
||||
throw new IllegalArgumentException("invalid probeModify");
|
||||
}
|
||||
|
||||
log.info("listen config id 2:" + probeModify);
|
||||
|
||||
// do long-polling
|
||||
inner.doPollingConfig(request, response, clientMd5Map, probeModify.length());
|
||||
}
|
||||
@ -376,7 +381,7 @@ public class ConfigController {
|
||||
}
|
||||
|
||||
@DeleteMapping(params = "beta=true")
|
||||
@Secured(action = ActionTypes.READ, parser = ConfigResourceParser.class)
|
||||
@Secured(action = ActionTypes.WRITE, parser = ConfigResourceParser.class)
|
||||
public RestResult<Boolean> stopBeta(@RequestParam(value = "dataId") String dataId,
|
||||
@RequestParam(value = "group") String group,
|
||||
@RequestParam(value = "tenant", required = false,
|
||||
@ -428,6 +433,7 @@ public class ConfigController {
|
||||
defaultValue = StringUtils.EMPTY) String tenant,
|
||||
@RequestParam(value = "ids", required = false) List<Long> ids) {
|
||||
ids.removeAll(Collections.singleton(null));
|
||||
tenant = processTenant(tenant);
|
||||
List<ConfigAllInfo> dataList = persistService.findAllConfigInfo4Export(dataId, group, tenant, appName, ids);
|
||||
List<ZipUtils.ZipItem> zipItemList = new ArrayList<>();
|
||||
StringBuilder metaData = null;
|
||||
@ -545,6 +551,7 @@ public class ConfigController {
|
||||
}
|
||||
|
||||
@PostMapping(params = "clone=true")
|
||||
@Secured(action = ActionTypes.WRITE, parser = ConfigResourceParser.class)
|
||||
public RestResult<Map<String, Object>> cloneConfig(HttpServletRequest request,
|
||||
@RequestParam(value = "src_user", required = false) String srcUser,
|
||||
@RequestParam(value = "tenant", required = true) String namespace,
|
||||
@ -615,4 +622,11 @@ public class ConfigController {
|
||||
return ResultBuilder.buildSuccessResult("克隆成功", saveResult);
|
||||
}
|
||||
|
||||
private String processTenant(String tenant){
|
||||
if (StringUtils.isEmpty(tenant) || NAMESPACE_PUBLIC_KEY.equalsIgnoreCase(tenant)) {
|
||||
return "";
|
||||
}
|
||||
return tenant;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ import com.alibaba.nacos.config.server.service.LongPollingService;
|
||||
import com.alibaba.nacos.config.server.service.PersistService;
|
||||
import com.alibaba.nacos.config.server.service.trace.ConfigTraceService;
|
||||
import com.alibaba.nacos.config.server.utils.*;
|
||||
import com.alibaba.nacos.core.utils.Loggers;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
@ -98,6 +99,8 @@ public class ConfigServletInner {
|
||||
request.setAttribute("content", newResult);
|
||||
}
|
||||
|
||||
Loggers.AUTH.info("new content:" + newResult);
|
||||
|
||||
// 禁用缓存
|
||||
response.setHeader("Pragma", "no-cache");
|
||||
response.setDateHeader("Expires", 0);
|
||||
|
@ -20,7 +20,6 @@ import com.alibaba.nacos.config.server.constant.Constants;
|
||||
import com.alibaba.nacos.config.server.monitor.MetricsMonitor;
|
||||
import com.alibaba.nacos.config.server.service.notify.NotifyService;
|
||||
import com.alibaba.nacos.config.server.service.notify.NotifyService.HttpResult;
|
||||
import com.alibaba.nacos.config.server.utils.LogUtil;
|
||||
import com.alibaba.nacos.config.server.utils.PropertyUtil;
|
||||
import com.alibaba.nacos.config.server.utils.RunningConfigUtils;
|
||||
import com.alibaba.nacos.config.server.utils.event.EventDispatcher;
|
||||
@ -32,10 +31,9 @@ import org.apache.http.client.utils.HttpClientUtils;
|
||||
import org.apache.http.concurrent.FutureCallback;
|
||||
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
|
||||
import org.apache.http.impl.nio.client.HttpAsyncClients;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.web.context.WebServerInitializedEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
@ -44,9 +42,14 @@ import javax.servlet.ServletContext;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.alibaba.nacos.config.server.utils.LogUtil.defaultLog;
|
||||
import static com.alibaba.nacos.config.server.utils.LogUtil.fatalLog;
|
||||
@ -60,17 +63,21 @@ import static com.alibaba.nacos.core.utils.SystemUtils.*;
|
||||
@Service
|
||||
public class ServerListService implements ApplicationListener<WebServerInitializedEvent> {
|
||||
|
||||
@Autowired
|
||||
private Environment env;
|
||||
|
||||
@Autowired
|
||||
private ServletContext servletContext;
|
||||
private final ServletContext servletContext;
|
||||
|
||||
@Value("${server.port:8848}")
|
||||
private int port;
|
||||
|
||||
@Value("${useAddressServer}")
|
||||
private Boolean isUseAddressServer = true;
|
||||
|
||||
public ServerListService(ServletContext servletContext) {
|
||||
this.servletContext = servletContext;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
serverPort = System.getProperty("nacos.server.port", "8848");
|
||||
String envDomainName = System.getenv("address_server_domain");
|
||||
if (StringUtils.isBlank(envDomainName)) {
|
||||
domainName = System.getProperty("address.server.domain", "jmenv.tbsite.net");
|
||||
@ -88,55 +95,23 @@ public class ServerListService implements ApplicationListener<WebServerInitializ
|
||||
addressServerUrl = "http://" + domainName + ":" + addressPort + addressUrl;
|
||||
envIdUrl = "http://" + domainName + ":" + addressPort + "/env";
|
||||
|
||||
defaultLog.info("ServerListService address-server port:" + serverPort);
|
||||
defaultLog.info("ServerListService address-server port:" + addressPort);
|
||||
defaultLog.info("ADDRESS_SERVER_URL:" + addressServerUrl);
|
||||
isHealthCheck = PropertyUtil.isHealthCheck();
|
||||
maxFailCount = PropertyUtil.getMaxHealthCheckFailCount();
|
||||
|
||||
try {
|
||||
String val = null;
|
||||
val = env.getProperty("useAddressServer");
|
||||
if (val != null && FALSE_STR.equals(val)) {
|
||||
isUseAddressServer = false;
|
||||
}
|
||||
fatalLog.warn("useAddressServer:{}", isUseAddressServer);
|
||||
} catch (Exception e) {
|
||||
fatalLog.error("read application.properties wrong", e);
|
||||
}
|
||||
fatalLog.warn("useAddressServer:{}", isUseAddressServer);
|
||||
GetServerListTask task = new GetServerListTask();
|
||||
task.run();
|
||||
if (null == serverList || serverList.isEmpty()) {
|
||||
if (CollectionUtils.isEmpty(serverList)) {
|
||||
fatalLog.error("########## cannot get serverlist, so exit.");
|
||||
throw new RuntimeException("cannot get serverlist, so exit.");
|
||||
} else {
|
||||
TimerTaskService.scheduleWithFixedDelay(task, 0L, 5L, TimeUnit.SECONDS);
|
||||
}
|
||||
httpclient.start();
|
||||
CheckServerHealthTask checkServerHealthTask = new CheckServerHealthTask();
|
||||
TimerTaskService.scheduleWithFixedDelay(checkServerHealthTask, 0L, 5L, TimeUnit.SECONDS);
|
||||
|
||||
}
|
||||
|
||||
public String getEnvId() {
|
||||
String envId = "";
|
||||
int i = 0;
|
||||
do {
|
||||
envId = getEnvIdHttp();
|
||||
if (StringUtils.isBlank(envId)) {
|
||||
i++;
|
||||
try {
|
||||
Thread.sleep(500);
|
||||
} catch (InterruptedException e) {
|
||||
LogUtil.defaultLog.error("sleep interrupt");
|
||||
}
|
||||
}
|
||||
} while (StringUtils.isBlank(envId) && i < 5);
|
||||
|
||||
if (!StringUtils.isBlank(envId)) {
|
||||
} else {
|
||||
LogUtil.defaultLog.error("envId is blank");
|
||||
}
|
||||
return envId;
|
||||
}
|
||||
|
||||
public List<String> getServerList() {
|
||||
return new ArrayList<String>(serverList);
|
||||
@ -161,21 +136,15 @@ public class ServerListService implements ApplicationListener<WebServerInitializ
|
||||
/**
|
||||
* serverList has changed
|
||||
*/
|
||||
static public class ServerlistChangeEvent implements EventDispatcher.Event {
|
||||
static public class ServerListChangeEvent implements EventDispatcher.Event {
|
||||
}
|
||||
|
||||
private void updateIfChanged(List<String> newList) {
|
||||
if (newList.isEmpty()) {
|
||||
if (CollectionUtils.isEmpty(newList)||newList.equals(serverList)) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean isContainSelfIp = false;
|
||||
for (String ipPortTmp : newList) {
|
||||
if (ipPortTmp.contains(LOCAL_IP)) {
|
||||
isContainSelfIp = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
boolean isContainSelfIp = newList.stream().anyMatch(ipPortTmp -> ipPortTmp.contains(LOCAL_IP));
|
||||
|
||||
if (isContainSelfIp) {
|
||||
isInIpList = true;
|
||||
@ -186,30 +155,23 @@ public class ServerListService implements ApplicationListener<WebServerInitializ
|
||||
fatalLog.error("########## [serverlist] self ip {} not in serverlist {}", selfAddr, newList);
|
||||
}
|
||||
|
||||
if (newList.equals(serverList)) {
|
||||
return;
|
||||
}
|
||||
|
||||
serverList = new ArrayList<String>(newList);
|
||||
|
||||
List<String> unhealthRemoved = new ArrayList<String>();
|
||||
for (String unhealthIp : serverListUnhealth) {
|
||||
if (!newList.contains(unhealthIp)) {
|
||||
unhealthRemoved.add(unhealthIp);
|
||||
if(!serverListUnhealth.isEmpty()){
|
||||
|
||||
List<String> unhealthyRemoved = serverListUnhealth.stream()
|
||||
.filter(unhealthyIp -> !newList.contains(unhealthyIp)).collect(Collectors.toList());
|
||||
|
||||
serverListUnhealth.removeAll(unhealthyRemoved);
|
||||
|
||||
List<String> unhealthyCountRemoved = serverIp2unhealthCount.keySet().stream()
|
||||
.filter(key -> !newList.contains(key)).collect(Collectors.toList());
|
||||
|
||||
for (String unhealthyCountTmp : unhealthyCountRemoved) {
|
||||
serverIp2unhealthCount.remove(unhealthyCountTmp);
|
||||
}
|
||||
}
|
||||
|
||||
serverListUnhealth.removeAll(unhealthRemoved);
|
||||
|
||||
List<String> unhealthCountRemoved = new ArrayList<String>();
|
||||
for (Map.Entry<String, Integer> ip2UnhealthCountTmp : serverIp2unhealthCount.entrySet()) {
|
||||
if (!newList.contains(ip2UnhealthCountTmp.getKey())) {
|
||||
unhealthCountRemoved.add(ip2UnhealthCountTmp.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
for (String unhealthCountTmp : unhealthCountRemoved) {
|
||||
serverIp2unhealthCount.remove(unhealthCountTmp);
|
||||
}
|
||||
|
||||
defaultLog.warn("[serverlist] updated to {}", serverList);
|
||||
@ -217,7 +179,7 @@ public class ServerListService implements ApplicationListener<WebServerInitializ
|
||||
/**
|
||||
* 非并发fireEvent
|
||||
*/
|
||||
EventDispatcher.fireEvent(new ServerlistChangeEvent());
|
||||
EventDispatcher.fireEvent(new ServerListChangeEvent());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -256,7 +218,7 @@ public class ServerListService implements ApplicationListener<WebServerInitializ
|
||||
|
||||
if (HttpServletResponse.SC_OK == result.code) {
|
||||
isAddressServerHealth = true;
|
||||
addressServerFailCcount = 0;
|
||||
addressServerFailCount = 0;
|
||||
List<String> lines = IoUtils.readLines(new StringReader(result.content));
|
||||
List<String> ips = new ArrayList<String>(lines.size());
|
||||
for (String serverAddr : lines) {
|
||||
@ -266,16 +228,16 @@ public class ServerListService implements ApplicationListener<WebServerInitializ
|
||||
}
|
||||
return ips;
|
||||
} else {
|
||||
addressServerFailCcount++;
|
||||
if (addressServerFailCcount >= maxFailCount) {
|
||||
addressServerFailCount++;
|
||||
if (addressServerFailCount >= maxFailCount) {
|
||||
isAddressServerHealth = false;
|
||||
}
|
||||
defaultLog.error("[serverlist] failed to get serverlist, error code {}", result.code);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
addressServerFailCcount++;
|
||||
if (addressServerFailCcount >= maxFailCount) {
|
||||
addressServerFailCount++;
|
||||
if (addressServerFailCount >= maxFailCount) {
|
||||
isAddressServerHealth = false;
|
||||
}
|
||||
defaultLog.error("[serverlist] exception, " + e.toString(), e);
|
||||
@ -302,23 +264,7 @@ public class ServerListService implements ApplicationListener<WebServerInitializ
|
||||
}
|
||||
}
|
||||
|
||||
private String getEnvIdHttp() {
|
||||
try {
|
||||
// "http://jmenv.tbsite.net:8080/env";
|
||||
HttpResult result = NotifyService.invokeURL(envIdUrl, null, null);
|
||||
|
||||
if (HttpServletResponse.SC_OK == result.code) {
|
||||
return result.content.trim();
|
||||
} else {
|
||||
defaultLog.error("[envId] failed to get envId, error code {}", result.code);
|
||||
return "";
|
||||
}
|
||||
} catch (IOException e) {
|
||||
defaultLog.error("[envId] exception, " + e.toString(), e);
|
||||
return "";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class GetServerListTask implements Runnable {
|
||||
@Override
|
||||
@ -338,18 +284,18 @@ public class ServerListService implements ApplicationListener<WebServerInitializ
|
||||
String url = "http://" + serverIp + servletContext.getContextPath() + Constants.HEALTH_CONTROLLER_PATH;
|
||||
// "/nacos/health";
|
||||
HttpGet request = new HttpGet(url);
|
||||
httpclient.execute(request, new AyscCheckServerHealthCallBack(serverIp));
|
||||
httpclient.execute(request, new AsyncCheckServerHealthCallBack(serverIp));
|
||||
}
|
||||
long endCheckTime = System.currentTimeMillis();
|
||||
long cost = endCheckTime - startCheckTime;
|
||||
defaultLog.debug("checkServerHealth cost: {}", cost);
|
||||
}
|
||||
|
||||
class AyscCheckServerHealthCallBack implements FutureCallback<HttpResponse> {
|
||||
class AsyncCheckServerHealthCallBack implements FutureCallback<HttpResponse> {
|
||||
|
||||
private String serverIp;
|
||||
|
||||
public AyscCheckServerHealthCallBack(String serverIp) {
|
||||
public AsyncCheckServerHealthCallBack(String serverIp) {
|
||||
this.serverIp = serverIp;
|
||||
}
|
||||
|
||||
@ -363,29 +309,16 @@ public class ServerListService implements ApplicationListener<WebServerInitializ
|
||||
|
||||
@Override
|
||||
public void failed(Exception ex) {
|
||||
int failCount = serverIp2unhealthCount.compute(serverIp,(key,oldValue)->{
|
||||
if(oldValue == null){
|
||||
return 1;
|
||||
}
|
||||
return oldValue+1;
|
||||
});
|
||||
if (failCount > maxFailCount) {
|
||||
if (!serverListUnhealth.contains(serverIp)) {
|
||||
serverListUnhealth.add(serverIp);
|
||||
}
|
||||
defaultLog.error("unhealthIp:{}, unhealthCount:{}", serverIp, failCount);
|
||||
MetricsMonitor.getUnhealthException().increment();
|
||||
}
|
||||
computeFailCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelled() {
|
||||
int failCount = serverIp2unhealthCount.compute(serverIp,(key,oldValue)->{
|
||||
if(oldValue == null){
|
||||
return 1;
|
||||
computeFailCount();
|
||||
}
|
||||
return oldValue+1;
|
||||
});
|
||||
|
||||
private void computeFailCount() {
|
||||
int failCount = serverIp2unhealthCount.compute(serverIp,(key,oldValue)->oldValue == null?1:oldValue+1);
|
||||
if (failCount > maxFailCount) {
|
||||
if (!serverListUnhealth.contains(serverIp)) {
|
||||
serverListUnhealth.add(serverIp);
|
||||
@ -435,15 +368,15 @@ public class ServerListService implements ApplicationListener<WebServerInitializ
|
||||
static final int TIMEOUT = 5000;
|
||||
private int maxFailCount = 12;
|
||||
private static volatile List<String> serverList = new ArrayList<String>();
|
||||
private static volatile List<String> serverListUnhealth = new ArrayList<String>();
|
||||
private static volatile List<String> serverListUnhealth = Collections.synchronizedList(new ArrayList<String>());;
|
||||
private static volatile boolean isAddressServerHealth = true;
|
||||
private static volatile int addressServerFailCcount = 0;
|
||||
private static volatile int addressServerFailCount = 0;
|
||||
private static volatile boolean isInIpList = true;
|
||||
|
||||
/**
|
||||
* ip unhealth count
|
||||
*/
|
||||
private static volatile Map<String, Integer> serverIp2unhealthCount = new HashMap<String, Integer>();
|
||||
private static Map<String, Integer> serverIp2unhealthCount = new ConcurrentHashMap<>();
|
||||
private RequestConfig requestConfig = RequestConfig.custom()
|
||||
.setConnectTimeout(PropertyUtil.getNotifyConnectTimeout())
|
||||
.setSocketTimeout(PropertyUtil.getNotifySocketTimeout()).build();
|
||||
@ -451,18 +384,13 @@ public class ServerListService implements ApplicationListener<WebServerInitializ
|
||||
private CloseableHttpAsyncClient httpclient = HttpAsyncClients.custom().setDefaultRequestConfig(requestConfig)
|
||||
.build();
|
||||
|
||||
/**
|
||||
* server之间通信的端口
|
||||
*/
|
||||
public String serverPort;
|
||||
|
||||
public String domainName;
|
||||
public String addressPort;
|
||||
public String addressUrl;
|
||||
public String envIdUrl;
|
||||
public String addressServerUrl;
|
||||
private Boolean isUseAddressServer = true;
|
||||
private boolean isHealthCheck = true;
|
||||
private final static String FALSE_STR = "false";
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(WebServerInitializedEvent event) {
|
||||
@ -474,6 +402,10 @@ public class ServerListService implements ApplicationListener<WebServerInitializ
|
||||
}
|
||||
setServerList(new ArrayList<String>(newList));
|
||||
}
|
||||
httpclient.start();
|
||||
CheckServerHealthTask checkServerHealthTask = new CheckServerHealthTask();
|
||||
TimerTaskService.scheduleWithFixedDelay(checkServerHealthTask, 0L, 5L, TimeUnit.SECONDS);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -186,7 +186,8 @@ CREATE TABLE users (
|
||||
|
||||
CREATE TABLE roles (
|
||||
username varchar(50) NOT NULL,
|
||||
role varchar(50) NOT NULL
|
||||
role varchar(50) NOT NULL,
|
||||
constraint uk_username_role UNIQUE (username,role)
|
||||
);
|
||||
|
||||
CREATE TABLE permissions (
|
||||
@ -198,4 +199,4 @@ CREATE TABLE permissions (
|
||||
|
||||
INSERT INTO users (username, password, enabled) VALUES ('nacos', '$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu', TRUE);
|
||||
|
||||
INSERT INTO roles (username, role) VALUES ('nacos', 'GLOBAL_ADMIN');
|
||||
INSERT INTO roles (username, role) VALUES ('nacos', 'ROLE_ADMIN');
|
||||
|
@ -181,7 +181,8 @@ CREATE TABLE users (
|
||||
|
||||
CREATE TABLE roles (
|
||||
username varchar(50) NOT NULL,
|
||||
role varchar(50) NOT NULL
|
||||
role varchar(50) NOT NULL,
|
||||
constraint uk_username_role UNIQUE (username,role)
|
||||
);
|
||||
|
||||
CREATE TABLE permissions (
|
||||
@ -193,4 +194,4 @@ CREATE TABLE permissions (
|
||||
|
||||
INSERT INTO users (username, password, enabled) VALUES ('nacos', '$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu', TRUE);
|
||||
|
||||
INSERT INTO roles (username, role) VALUES ('nacos', 'GLOBAL_ADMIN');
|
||||
INSERT INTO roles (username, role) VALUES ('nacos', 'ROLE_ADMIN');
|
||||
|
@ -18,7 +18,7 @@
|
||||
<parent>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-all</artifactId>
|
||||
<version>1.2.0-SNAPSHOT</version>
|
||||
<version>1.2.0</version>
|
||||
</parent>
|
||||
<artifactId>nacos-console</artifactId>
|
||||
<!--<packaging>war</packaging>-->
|
||||
|
@ -51,7 +51,7 @@ public class HealthController {
|
||||
* @return HTTP code equal to 200 indicates that Nacos is in right states. HTTP code equal to 500 indicates that
|
||||
* Nacos is in broken states.
|
||||
*/
|
||||
@GetMapping("liveness")
|
||||
@GetMapping("/liveness")
|
||||
public ResponseEntity liveness() {
|
||||
return ResponseEntity.ok().body("OK");
|
||||
}
|
||||
@ -62,7 +62,7 @@ public class HealthController {
|
||||
* @return HTTP code equal to 200 indicates that Nacos is ready. HTTP code equal to 500 indicates that Nacos is not
|
||||
* ready.
|
||||
*/
|
||||
@GetMapping("readiness")
|
||||
@GetMapping("/readiness")
|
||||
public ResponseEntity readiness(HttpServletRequest request) {
|
||||
boolean isConfigReadiness = isConfigReadiness();
|
||||
boolean isNamingReadiness = isNamingReadiness(request);
|
||||
|
@ -20,6 +20,9 @@ import com.alibaba.nacos.config.server.model.TenantInfo;
|
||||
import com.alibaba.nacos.config.server.service.PersistService;
|
||||
import com.alibaba.nacos.console.model.Namespace;
|
||||
import com.alibaba.nacos.console.model.NamespaceAllInfo;
|
||||
import com.alibaba.nacos.console.security.nacos.NacosAuthConfig;
|
||||
import com.alibaba.nacos.core.auth.ActionTypes;
|
||||
import com.alibaba.nacos.core.auth.Secured;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
@ -106,6 +109,7 @@ public class NamespaceController {
|
||||
* @return whether create ok
|
||||
*/
|
||||
@PostMapping
|
||||
@Secured(resource = NacosAuthConfig.CONSOLE_RESOURCE_NAME_PREFIX + "namespaces", action = ActionTypes.WRITE)
|
||||
public Boolean createNamespace(HttpServletRequest request, HttpServletResponse response,
|
||||
@RequestParam("customNamespaceId") String namespaceId,
|
||||
@RequestParam("namespaceName") String namespaceName,
|
||||
@ -154,6 +158,7 @@ public class NamespaceController {
|
||||
* @return whether edit ok
|
||||
*/
|
||||
@PutMapping
|
||||
@Secured(resource = NacosAuthConfig.CONSOLE_RESOURCE_NAME_PREFIX + "namespaces", action = ActionTypes.WRITE)
|
||||
public Boolean editNamespace(@RequestParam("namespace") String namespace,
|
||||
@RequestParam("namespaceShowName") String namespaceShowName,
|
||||
@RequestParam(value = "namespaceDesc", required = false) String namespaceDesc) {
|
||||
@ -171,6 +176,7 @@ public class NamespaceController {
|
||||
* @return whether del ok
|
||||
*/
|
||||
@DeleteMapping
|
||||
@Secured(resource = NacosAuthConfig.CONSOLE_RESOURCE_NAME_PREFIX + "namespaces", action = ActionTypes.WRITE)
|
||||
public Boolean deleteConfig(HttpServletRequest request, HttpServletResponse response,
|
||||
@RequestParam("namespaceId") String namespaceId) {
|
||||
persistService.removeTenantInfoAtomic("1", namespaceId);
|
||||
|
@ -34,7 +34,7 @@ import java.util.Map;
|
||||
@RequestMapping("/v1/console/server")
|
||||
public class ServerStateController {
|
||||
|
||||
@GetMapping("state")
|
||||
@GetMapping("/state")
|
||||
public ResponseEntity serverState() {
|
||||
Map<String,String> serverState = new HashMap<>(3);
|
||||
serverState.put("standalone_mode",SystemUtils.STANDALONE_MODE ?
|
||||
|
@ -17,10 +17,12 @@ package com.alibaba.nacos.console.controller;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alibaba.nacos.api.common.Constants;
|
||||
import com.alibaba.nacos.config.server.auth.RoleInfo;
|
||||
import com.alibaba.nacos.config.server.model.RestResult;
|
||||
import com.alibaba.nacos.config.server.model.User;
|
||||
import com.alibaba.nacos.console.security.nacos.NacosAuthConfig;
|
||||
import com.alibaba.nacos.console.security.nacos.NacosAuthManager;
|
||||
import com.alibaba.nacos.console.security.nacos.roles.NacosRoleServiceImpl;
|
||||
import com.alibaba.nacos.console.security.nacos.users.NacosUser;
|
||||
import com.alibaba.nacos.console.security.nacos.users.NacosUserDetailsServiceImpl;
|
||||
import com.alibaba.nacos.console.utils.JwtTokenUtils;
|
||||
@ -37,6 +39,7 @@ import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* User related methods entry
|
||||
@ -57,6 +60,9 @@ public class UserController {
|
||||
@Autowired
|
||||
private NacosUserDetailsServiceImpl userDetailsService;
|
||||
|
||||
@Autowired
|
||||
private NacosRoleServiceImpl roleService;
|
||||
|
||||
@Autowired
|
||||
private AuthConfigs authConfigs;
|
||||
|
||||
@ -94,7 +100,14 @@ public class UserController {
|
||||
@DeleteMapping
|
||||
@Secured(resource = NacosAuthConfig.CONSOLE_RESOURCE_NAME_PREFIX + "users", action = ActionTypes.WRITE)
|
||||
public Object deleteUser(@RequestParam String username) {
|
||||
|
||||
List<RoleInfo> roleInfoList = roleService.getRoles(username);
|
||||
if (roleInfoList != null) {
|
||||
for (RoleInfo roleInfo : roleInfoList) {
|
||||
if (roleInfo.getRole().equals(NacosRoleServiceImpl.GLOBAL_ADMIN_ROLE)) {
|
||||
throw new IllegalArgumentException("cannot delete admin: " + username);
|
||||
}
|
||||
}
|
||||
}
|
||||
userDetailsService.deleteUser(username);
|
||||
return new RestResult<>(200, "delete user ok!");
|
||||
}
|
||||
|
@ -81,12 +81,15 @@ public class NacosAuthManager implements AuthManager {
|
||||
user.setUserName(username);
|
||||
user.setToken(token);
|
||||
List<RoleInfo> roleInfoList = roleService.getRoles(username);
|
||||
for (RoleInfo roleInfo : roleInfoList) {
|
||||
if (roleInfo.getRole().equals(NacosRoleServiceImpl.GLOBAL_ADMIN_ROLE)) {
|
||||
user.setGlobalAdmin(true);
|
||||
break;
|
||||
if (roleInfoList != null) {
|
||||
for (RoleInfo roleInfo : roleInfoList) {
|
||||
if (roleInfo.getRole().equals(NacosRoleServiceImpl.GLOBAL_ADMIN_ROLE)) {
|
||||
user.setGlobalAdmin(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,7 @@ import com.alibaba.nacos.core.auth.Permission;
|
||||
import com.alibaba.nacos.core.utils.Loggers;
|
||||
import io.jsonwebtoken.lang.Collections;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.mina.util.ConcurrentHashSet;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Service;
|
||||
@ -45,7 +46,7 @@ import java.util.regex.Pattern;
|
||||
@Service
|
||||
public class NacosRoleServiceImpl {
|
||||
|
||||
public static final String GLOBAL_ADMIN_ROLE = "GLOBAL_ADMIN";
|
||||
public static final String GLOBAL_ADMIN_ROLE = "ROLE_ADMIN";
|
||||
|
||||
@Autowired
|
||||
private AuthConfigs authConfigs;
|
||||
@ -59,6 +60,8 @@ public class NacosRoleServiceImpl {
|
||||
@Autowired
|
||||
private PermissionPersistService permissionPersistService;
|
||||
|
||||
private Set<String> roleSet = new ConcurrentHashSet<>();
|
||||
|
||||
private Map<String, List<RoleInfo>> roleInfoMap = new ConcurrentHashMap<>();
|
||||
|
||||
private Map<String, List<PermissionInfo>> permissionInfoMap = new ConcurrentHashMap<>();
|
||||
@ -70,22 +73,23 @@ public class NacosRoleServiceImpl {
|
||||
if (roleInfoPage == null) {
|
||||
return;
|
||||
}
|
||||
Set<String> roleSet = new HashSet<>(16);
|
||||
Set<String> tmpRoleSet = new HashSet<>(16);
|
||||
Map<String, List<RoleInfo>> tmpRoleInfoMap = new ConcurrentHashMap<>(16);
|
||||
for (RoleInfo roleInfo : roleInfoPage.getPageItems()) {
|
||||
if (!tmpRoleInfoMap.containsKey(roleInfo.getUsername())) {
|
||||
tmpRoleInfoMap.put(roleInfo.getUsername(), new ArrayList<>());
|
||||
}
|
||||
tmpRoleInfoMap.get(roleInfo.getUsername()).add(roleInfo);
|
||||
roleSet.add(roleInfo.getRole());
|
||||
tmpRoleSet.add(roleInfo.getRole());
|
||||
}
|
||||
|
||||
Map<String, List<PermissionInfo>> tmpPermissionInfoMap = new ConcurrentHashMap<>(16);
|
||||
for (String role : roleSet) {
|
||||
for (String role : tmpRoleSet) {
|
||||
Page<PermissionInfo> permissionInfoPage = permissionPersistService.getPermissions(role, 1, Integer.MAX_VALUE);
|
||||
tmpPermissionInfoMap.put(role, permissionInfoPage.getPageItems());
|
||||
}
|
||||
|
||||
roleSet = tmpRoleSet;
|
||||
roleInfoMap = tmpRoleInfoMap;
|
||||
permissionInfoMap = tmpPermissionInfoMap;
|
||||
} catch (Exception e) {
|
||||
@ -178,7 +182,11 @@ public class NacosRoleServiceImpl {
|
||||
if (userDetailsService.getUser(username) == null) {
|
||||
throw new IllegalArgumentException("user '" + username + "' not found!");
|
||||
}
|
||||
if (GLOBAL_ADMIN_ROLE.equals(role)) {
|
||||
throw new IllegalArgumentException("role '" + GLOBAL_ADMIN_ROLE + "' is not permitted to create!");
|
||||
}
|
||||
rolePersistService.addRole(role, username);
|
||||
roleSet.add(role);
|
||||
}
|
||||
|
||||
public void deleteRole(String role, String userName) {
|
||||
@ -186,8 +194,8 @@ public class NacosRoleServiceImpl {
|
||||
}
|
||||
|
||||
public void deleteRole(String role) {
|
||||
|
||||
rolePersistService.deleteRole(role);
|
||||
roleSet.remove(role);
|
||||
}
|
||||
|
||||
public Page<PermissionInfo> getPermissionsFromDatabase(String role, int pageNo, int pageSize) {
|
||||
@ -199,6 +207,9 @@ public class NacosRoleServiceImpl {
|
||||
}
|
||||
|
||||
public void addPermission(String role, String resource, String action) {
|
||||
if (!roleSet.contains(role)) {
|
||||
throw new IllegalArgumentException("role " + role + " not found!");
|
||||
}
|
||||
permissionPersistService.addPermission(role, resource, action);
|
||||
}
|
||||
|
||||
|
@ -193,4 +193,4 @@ CREATE TABLE permissions (
|
||||
|
||||
INSERT INTO users (username, password, enabled) VALUES ('nacos', '$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu', TRUE);
|
||||
|
||||
INSERT INTO roles (username, role) VALUES ('nacos', 'GLOBAL_ADMIN');
|
||||
INSERT INTO roles (username, role) VALUES ('nacos', 'ROLE_ADMIN');
|
||||
|
@ -35,3 +35,9 @@ nacos.security.ignore.urls=/,/error,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/
|
||||
nacos.core.auth.system.type=nacos
|
||||
nacos.core.auth.enabled=false
|
||||
nacos.core.auth.default.token.secret.key=SecretKey012345678901234567890123456789012345678901234567890123456789
|
||||
|
||||
nacos.naming.empty-service.auto-clean=true
|
||||
nacos.naming.empty-service.clean.initial-delay-ms=50000
|
||||
nacos.naming.empty-service.clean.period-time-ms=30000
|
||||
|
||||
tldSkipPatterns=derbyLocale_*.jar,jaxb-api.jar,jsr173_1.0_api.jar,jaxb1-impl.jar,activation.jar
|
||||
|
@ -30,6 +30,7 @@
|
||||
"generator-star-spacing": "off",
|
||||
"wrap-iife": "off",
|
||||
"arrow-parens": "off",
|
||||
"indent": "off"
|
||||
"indent": "off",
|
||||
"comma-dangle": "off"
|
||||
}
|
||||
}
|
||||
|
@ -16,14 +16,14 @@ const base = require('./webpack.base.conf');
|
||||
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
|
||||
const CleanWebpackPlugin = require('clean-webpack-plugin');
|
||||
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
|
||||
|
||||
const [cssLoader] = base.module.rules;
|
||||
cssLoader.use.push({
|
||||
loader: '@alifd/next-theme-loader',
|
||||
options: {
|
||||
modifyVars: {
|
||||
'$icon-font-path':'"/nacos/console-fe/public/icons/icon-font"',
|
||||
'$icon-font-path': '"/nacos/console-fe/public/icons/icon-font"',
|
||||
'$font-custom-path': '"/nacos/console-fe/public/fonts/"'
|
||||
}
|
||||
}
|
||||
@ -40,8 +40,10 @@ module.exports = Object.assign({}, base, {
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
new CleanWebpackPlugin(path.resolve(__dirname, '../dist'), {
|
||||
root: path.resolve(__dirname, '../'),
|
||||
new CleanWebpackPlugin({
|
||||
cleanOnceBeforeBuildPatterns:[
|
||||
path.resolve(__dirname, '../dist/**'),
|
||||
]
|
||||
}),
|
||||
...base.plugins,
|
||||
new MiniCssExtractPlugin({
|
||||
|
@ -27,55 +27,56 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@alifd/next-theme-loader": "^1.3.1",
|
||||
"@babel/cli": "^7.2.3",
|
||||
"@babel/core": "^7.2.2",
|
||||
"@babel/plugin-proposal-decorators": "^7.2.3",
|
||||
"@babel/preset-env": "^7.2.3",
|
||||
"@babel/runtime": "^7.2.0",
|
||||
"@babel/cli": "^7.7.7",
|
||||
"@babel/core": "^7.7.7",
|
||||
"@babel/plugin-proposal-decorators": "^7.7.4",
|
||||
"@babel/preset-env": "^7.7.7",
|
||||
"@babel/runtime": "^7.7.7",
|
||||
"babel-eslint": "^10.0.1",
|
||||
"babel-loader": "^8.0.4",
|
||||
"babel-plugin-import": "^1.12.0",
|
||||
"babel-preset-react-app": "^6.1.0",
|
||||
"clean-webpack-plugin": "^0.1.19",
|
||||
"copy-webpack-plugin": "^4.6.0",
|
||||
"cross-env": "^5.2.0",
|
||||
"css-loader": "^2.0.2",
|
||||
"eslint": "^5.11.0",
|
||||
"eslint-config-ali": "^4.1.0",
|
||||
"eslint-config-prettier": "^3.3.0",
|
||||
"eslint-loader": "^2.1.1",
|
||||
"babel-plugin-import": "^1.13.0",
|
||||
"babel-preset-react-app": "^9.1.0",
|
||||
"clean-webpack-plugin": "^3.0.0",
|
||||
"copy-webpack-plugin": "^5.1.1 ",
|
||||
"cross-env": "^6.0.3",
|
||||
"css-loader": "^3.4.0",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-config-ali": "^9.0.2",
|
||||
"eslint-config-prettier": "^6.8.0",
|
||||
"eslint-loader": "^3.0.3",
|
||||
"eslint-plugin-import": "^2.14.0",
|
||||
"eslint-plugin-prettier": "^3.0.0",
|
||||
"eslint-plugin-react": "^7.11.1",
|
||||
"file-loader": "^2.0.0",
|
||||
"eslint-plugin-react": "^7.17.0",
|
||||
"eslint-plugin-react-hooks": "^2.3.0",
|
||||
"file-loader": "^5.0.2",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"husky": "^1.1.4",
|
||||
"lint-staged": "^8.0.4",
|
||||
"mini-css-extract-plugin": "^0.5.0",
|
||||
"node-sass": "^4.11.0",
|
||||
"optimize-css-assets-webpack-plugin": "^5.0.1",
|
||||
"prettier": "1.15.2",
|
||||
"sass-loader": "^7.1.0",
|
||||
"style-loader": "^0.23.1",
|
||||
"uglifyjs-webpack-plugin": "^2.1.0",
|
||||
"url-loader": "^1.1.2",
|
||||
"webpack": "^4.28.2",
|
||||
"webpack-cli": "^3.1.2",
|
||||
"webpack-dev-server": "^3.1.13"
|
||||
"husky": "^3.1.0",
|
||||
"lint-staged": "^9.5.0",
|
||||
"mini-css-extract-plugin": "^0.9.0",
|
||||
"node-sass": "^4.13.0",
|
||||
"optimize-css-assets-webpack-plugin": "^5.0.3",
|
||||
"prettier": "1.19.1",
|
||||
"sass-loader": "^8.0.0",
|
||||
"style-loader": "^1.1.2",
|
||||
"uglifyjs-webpack-plugin": "^2.2.0",
|
||||
"url-loader": "^3.0.0",
|
||||
"webpack": "^4.41.4",
|
||||
"webpack-cli": "^3.3.10",
|
||||
"webpack-dev-server": "^3.10.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@alifd/next": "^1.15.12",
|
||||
"@alifd/next": "^1.17.4",
|
||||
"axios": "^0.18.0",
|
||||
"jquery": "^3.3.1",
|
||||
"moment": "^2.23.0",
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "^16.7.0",
|
||||
"react-dom": "^16.7.0",
|
||||
"react-redux": "^5.1.1",
|
||||
"react-router": "^4.3.1",
|
||||
"react-router-dom": "^4.3.1",
|
||||
"react": "^16.12.0",
|
||||
"react-dom": "^16.12.0",
|
||||
"react-redux": "^7.1.3",
|
||||
"react-router": "^5.1.2",
|
||||
"react-router-dom": "^5.1.2",
|
||||
"react-router-redux": "^4.0.8",
|
||||
"redux": "^4.0.1",
|
||||
"redux": "^4.0.5",
|
||||
"redux-thunk": "^2.3.0",
|
||||
"yamljs": "^0.3.0"
|
||||
}
|
||||
|
@ -125,9 +125,7 @@ class RegionGroup extends React.Component {
|
||||
? false
|
||||
: window.location.search.indexOf('hideTopbar=') === -1,
|
||||
},
|
||||
() => {
|
||||
this.setRegionWidth();
|
||||
}
|
||||
() => this.setRegionWidth()
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -23,3 +23,15 @@ export const GET_STATE = 'GET_STATE';
|
||||
|
||||
export const GET_SUBSCRIBERS = 'GET_SUBSCRIBERS';
|
||||
export const REMOVE_SUBSCRIBERS = 'REMOVE_SUBSCRIBERS';
|
||||
|
||||
export const UPDATE_USER = 'UPDATE_USER';
|
||||
export const SIGN_IN = 'SIGN_IN';
|
||||
export const USER_LIST = 'USER_LIST';
|
||||
|
||||
export const ROLE_LIST = 'ROLE_LIST';
|
||||
|
||||
export const PERMISSIONS_LIST = 'PERMISSIONS_LIST';
|
||||
|
||||
export const GET_NAMESPACES = 'GET_NAMESPACES';
|
||||
|
||||
export const GET_CONFIGURATION = 'GET_CONFIGURATION';
|
||||
|
@ -12,9 +12,16 @@
|
||||
*/
|
||||
|
||||
import projectConfig from './config';
|
||||
import moment from 'moment';
|
||||
import $ from 'jquery';
|
||||
import i18DocObj from './i18ndoc';
|
||||
import { Message } from '@alifd/next';
|
||||
|
||||
function goLogin() {
|
||||
const url = window.location.href;
|
||||
localStorage.removeItem('token');
|
||||
const base_url = url.split('#')[0];
|
||||
console.log('base_url', base_url);
|
||||
window.location = `${base_url}#/login`;
|
||||
}
|
||||
|
||||
const global = window;
|
||||
|
||||
@ -205,120 +212,6 @@ const nacosUtils = (function(_global) {
|
||||
};
|
||||
})(global);
|
||||
|
||||
const aliwareIntl = (function(_global) {
|
||||
/**
|
||||
* 国际化构造方法
|
||||
* @param {Object} options 配置信息
|
||||
*/
|
||||
function AliwareI18n(options) {
|
||||
// let currentLocal = options.currentLocal || navigator.language || navigator.userLanguage;
|
||||
|
||||
const nowData = options.locals;
|
||||
this.nowData = nowData;
|
||||
this.setMomentLocale(this.currentLanguageCode);
|
||||
}
|
||||
|
||||
let aliwareLocal = aliwareGetCookieByKeyName('aliyun_lang') || 'zh';
|
||||
let aliwareLocalSite = aliwareGetCookieByKeyName('aliyun_country') || 'cn';
|
||||
aliwareLocal = aliwareLocal.toLowerCase();
|
||||
aliwareLocalSite = aliwareLocalSite.toLowerCase();
|
||||
// 当前语言
|
||||
AliwareI18n.prototype.currentLocal = aliwareLocal;
|
||||
// 当前地区
|
||||
AliwareI18n.prototype.currentSite = aliwareLocalSite;
|
||||
// 当前语言-地区
|
||||
AliwareI18n.prototype.currentLanguageCode =
|
||||
aliwareGetCookieByKeyName('docsite_language') || `${aliwareLocal}-${aliwareLocalSite}`;
|
||||
/**
|
||||
* 通过key获取对应国际化文案
|
||||
* @param {String} key 国际化key
|
||||
*/
|
||||
AliwareI18n.prototype.get = function(key) {
|
||||
return this.nowData[key];
|
||||
};
|
||||
/**
|
||||
* 修改国际化文案数据
|
||||
* @param {String} local 语言信息
|
||||
*/
|
||||
AliwareI18n.prototype.changeLanguage = function(local) {
|
||||
this.nowData = i18DocObj[local] || {};
|
||||
};
|
||||
/**
|
||||
* 数字国际化
|
||||
* @param {Number} num 数字
|
||||
*/
|
||||
AliwareI18n.prototype.intlNumberFormat = function(num) {
|
||||
if (typeof Intl !== 'object' || typeof Intl.NumberFormat !== 'function') {
|
||||
return num;
|
||||
}
|
||||
try {
|
||||
return new Intl.NumberFormat(this.currentLanguageCode).format(num || 0);
|
||||
} catch (error) {
|
||||
return num;
|
||||
}
|
||||
};
|
||||
/**
|
||||
* 时间戳格式化
|
||||
* @param {Number} num 时间戳
|
||||
* @param {Object} initOption 配置信息
|
||||
*/
|
||||
AliwareI18n.prototype.intlTimeFormat = function(num = Date.now(), initOption = {}) {
|
||||
try {
|
||||
const date = Object.prototype.toString.call(num) === '[object Date]' ? num : new Date(num);
|
||||
const options = Object.assign(
|
||||
{},
|
||||
{
|
||||
// weekday: "short",
|
||||
hour12: false,
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
second: 'numeric',
|
||||
},
|
||||
initOption
|
||||
);
|
||||
return date.toLocaleDateString(this.currentLanguageCode, options);
|
||||
} catch (error) {
|
||||
return typeof moment === 'function' ? moment(num).format() : '--';
|
||||
}
|
||||
};
|
||||
/**
|
||||
* 获取当前时间格式
|
||||
* @param {String} language 语言信息: zh/en
|
||||
*/
|
||||
AliwareI18n.prototype.getIntlTimeFormat = function(_language) {
|
||||
const language = _language || aliwareLocal;
|
||||
const langObj = {
|
||||
zh: 'YYYY年M月D日 HH:mm:ss',
|
||||
en: 'MMM D, YYYY, h:mm:ss A',
|
||||
default: 'YYYY-MM-DD HH:mm:ss',
|
||||
};
|
||||
return langObj[language] ? langObj[language] : langObj.default;
|
||||
};
|
||||
/**
|
||||
* 设置moment的locale
|
||||
* @param {String} languageCode 语言信息: zh-ch/en-us
|
||||
*/
|
||||
AliwareI18n.prototype.setMomentLocale = function(languageCode) {
|
||||
if (Object.prototype.toString.call(moment) === '[object Function]') {
|
||||
moment.locale(languageCode || this.currentLanguageCode);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
return new AliwareI18n({
|
||||
currentLocal: `${aliwareLocal}`,
|
||||
locals:
|
||||
i18DocObj[AliwareI18n.prototype.currentLanguageCode] ||
|
||||
i18DocObj['en-us'] ||
|
||||
i18DocObj['zh-cn'] ||
|
||||
{},
|
||||
});
|
||||
})(global);
|
||||
|
||||
/**
|
||||
* 获取url中的参数
|
||||
*/
|
||||
@ -561,7 +454,7 @@ const request = (function(_global) {
|
||||
} catch (e) {}
|
||||
// 设置自动loading效果
|
||||
if (serviceObj.autoLoading) {
|
||||
nacosUtils.openLoading();
|
||||
// nacosUtils.openLoading();
|
||||
const prevComplete = config.complete;
|
||||
config.complete = function() {
|
||||
nacosUtils.closeLoading();
|
||||
@ -597,11 +490,22 @@ const request = (function(_global) {
|
||||
|
||||
// 处理后置中间件
|
||||
config = handleMiddleWare.apply(this, [config, ...args, middlewareBackList]);
|
||||
let token = {};
|
||||
try {
|
||||
token = JSON.parse(localStorage.token);
|
||||
} catch (e) {
|
||||
console.log('Token Erro', localStorage.token, e);
|
||||
goLogin();
|
||||
}
|
||||
const { accessToken = '' } = token;
|
||||
const [url, paramsStr = ''] = config.url.split('?');
|
||||
const params = paramsStr.split('&');
|
||||
params.push(`accessToken=${accessToken}`);
|
||||
|
||||
return $.ajax(
|
||||
Object.assign({}, config, {
|
||||
type: config.type,
|
||||
url: config.url,
|
||||
url: [url, params.join('&')].join('?'),
|
||||
data: config.data || '',
|
||||
dataType: config.dataType || 'json',
|
||||
beforeSend(xhr) {
|
||||
@ -615,17 +519,17 @@ const request = (function(_global) {
|
||||
success => {},
|
||||
error => {
|
||||
// 处理403 forbidden
|
||||
if (error && (error.status === 403 || error.status === 401)) {
|
||||
// 跳转至login页
|
||||
// TODO: 用 react-router 重写,改造成本比较高,这里先hack
|
||||
const url = window.location.href;
|
||||
// TODO: 后端返回细致的错误码,如果原始密码不对 不应该直接跳到登陆页
|
||||
if (url.includes('password')) {
|
||||
return;
|
||||
}
|
||||
const base_url = url.split('#')[0];
|
||||
window.location = `${base_url}#/login`;
|
||||
const { status, responseJSON = {} } = error || {};
|
||||
if (responseJSON.message) {
|
||||
Message.error(responseJSON.message);
|
||||
}
|
||||
if (
|
||||
[401, 403].includes(status) &&
|
||||
['unknown user!', 'token invalid', 'token expired!'].includes(responseJSON.message)
|
||||
) {
|
||||
goLogin();
|
||||
}
|
||||
return error;
|
||||
}
|
||||
);
|
||||
}
|
||||
@ -645,10 +549,9 @@ export {
|
||||
nacosEvent,
|
||||
nacosUtils,
|
||||
aliwareGetCookieByKeyName,
|
||||
aliwareIntl,
|
||||
removeParams,
|
||||
getParams,
|
||||
setParam,
|
||||
setParams,
|
||||
removeParams,
|
||||
request,
|
||||
};
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -30,7 +30,6 @@ import { LANGUAGE_KEY, REDUX_DEVTOOLS } from './constants';
|
||||
|
||||
import Login from './pages/Login';
|
||||
import Namespace from './pages/NameSpace';
|
||||
import Password from './pages/Password';
|
||||
import Newconfig from './pages/ConfigurationManagement/NewConfig';
|
||||
import Configsync from './pages/ConfigurationManagement/ConfigSync';
|
||||
import Configdetail from './pages/ConfigurationManagement/ConfigDetail';
|
||||
@ -44,6 +43,9 @@ import ServiceList from './pages/ServiceManagement/ServiceList';
|
||||
import ServiceDetail from './pages/ServiceManagement/ServiceDetail';
|
||||
import SubscriberList from './pages/ServiceManagement/SubscriberList';
|
||||
import ClusterNodeList from './pages/ClusterManagement/ClusterNodeList';
|
||||
import UserManagement from './pages/AuthorityControl/UserManagement';
|
||||
import PermissionsManagement from './pages/AuthorityControl/PermissionsManagement';
|
||||
import RolesManagement from './pages/AuthorityControl/RolesManagement';
|
||||
import Welcome from './pages/Welcome/Welcome';
|
||||
|
||||
import reducers from './reducers';
|
||||
@ -65,17 +67,13 @@ const reducer = combineReducers({
|
||||
|
||||
const store = createStore(
|
||||
reducer,
|
||||
compose(
|
||||
applyMiddleware(thunk),
|
||||
window[REDUX_DEVTOOLS] ? window[REDUX_DEVTOOLS]() : f => f
|
||||
)
|
||||
compose(applyMiddleware(thunk), window[REDUX_DEVTOOLS] ? window[REDUX_DEVTOOLS]() : f => f)
|
||||
);
|
||||
|
||||
const MENU = [
|
||||
{ path: '/', exact: true, render: () => <Redirect to="/welcome" /> },
|
||||
{ path: '/welcome', component: Welcome },
|
||||
{ path: '/namespace', component: Namespace },
|
||||
{ path: '/password', component: Password },
|
||||
{ path: '/newconfig', component: Newconfig },
|
||||
{ path: '/configsync', component: Configsync },
|
||||
{ path: '/configdetail', component: Configdetail },
|
||||
@ -89,12 +87,12 @@ const MENU = [
|
||||
{ path: '/serviceDetail', component: ServiceDetail },
|
||||
{ path: '/subscriberList', component: SubscriberList },
|
||||
{ path: '/clusterManagement', component: ClusterNodeList },
|
||||
{ path: '/userManagement', component: UserManagement },
|
||||
{ path: '/rolesManagement', component: RolesManagement },
|
||||
{ path: '/permissionsManagement', component: PermissionsManagement },
|
||||
];
|
||||
|
||||
@connect(
|
||||
state => ({ ...state.locale }),
|
||||
{ changeLanguage }
|
||||
)
|
||||
@connect(state => ({ ...state.locale }), { changeLanguage })
|
||||
class App extends React.Component {
|
||||
static propTypes = {
|
||||
locale: PropTypes.object,
|
||||
|
@ -1217,10 +1217,6 @@ form.vertical-margin-lg .form-group {
|
||||
border-color: #e0e0e0 !important;
|
||||
}
|
||||
|
||||
.main-container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.row-bg-green {
|
||||
background-color: #e4fdda;
|
||||
}
|
||||
|
@ -18,14 +18,13 @@ import { connect } from 'react-redux';
|
||||
import { ConfigProvider, Dropdown, Menu } from '@alifd/next';
|
||||
import siteConfig from '../config';
|
||||
import { changeLanguage } from '@/reducers/locale';
|
||||
import PasswordReset from '../pages/AuthorityControl/UserManagement/PasswordReset';
|
||||
import { passwordReset } from '../reducers/authority';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
@withRouter
|
||||
@connect(
|
||||
state => ({ ...state.locale }),
|
||||
{ changeLanguage }
|
||||
)
|
||||
@connect(state => ({ ...state.locale }), { changeLanguage })
|
||||
@ConfigProvider.config
|
||||
class Header extends React.Component {
|
||||
static displayName = 'Header';
|
||||
@ -38,6 +37,8 @@ class Header extends React.Component {
|
||||
changeLanguage: PropTypes.func,
|
||||
};
|
||||
|
||||
state = { passwordResetUser: '' };
|
||||
|
||||
switchLang = () => {
|
||||
const { language = 'en-US', changeLanguage } = this.props;
|
||||
const currentLanguage = language === 'en-US' ? 'zh-CN' : 'en-US';
|
||||
@ -50,16 +51,23 @@ class Header extends React.Component {
|
||||
};
|
||||
|
||||
changePassword = () => {
|
||||
this.props.history.push('/password');
|
||||
this.setState({
|
||||
passwordResetUser: this.getUsername(),
|
||||
});
|
||||
};
|
||||
|
||||
getUsername = () => {
|
||||
const token = window.localStorage.getItem('token');
|
||||
if (token) {
|
||||
const base64Url = token.split('.')[1];
|
||||
const [, base64Url = ''] = token.split('.');
|
||||
const base64 = base64Url.replace('-', '+').replace('_', '/');
|
||||
const parsedToken = JSON.parse(window.atob(base64));
|
||||
return parsedToken.sub;
|
||||
try {
|
||||
const parsedToken = JSON.parse(window.atob(base64));
|
||||
return parsedToken.sub;
|
||||
} catch (e) {
|
||||
delete localStorage.token;
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
return '';
|
||||
};
|
||||
@ -71,6 +79,7 @@ class Header extends React.Component {
|
||||
location: { pathname },
|
||||
} = this.props;
|
||||
const { home, docs, blog, community, languageSwitchButton } = locale;
|
||||
const { passwordResetUser = '' } = this.state;
|
||||
const BASE_URL = `https://nacos.io/${language.toLocaleLowerCase()}/`;
|
||||
const NAV_MENU = [
|
||||
{ id: 1, title: home, link: BASE_URL },
|
||||
@ -79,45 +88,56 @@ class Header extends React.Component {
|
||||
{ id: 4, title: community, link: `${BASE_URL}community/index.html` },
|
||||
];
|
||||
return (
|
||||
<header className="header-container header-container-primary">
|
||||
<div className="header-body">
|
||||
<a
|
||||
href={`https://nacos.io/${language.toLocaleLowerCase()}/`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<img
|
||||
src="img/logo-2000-390.svg"
|
||||
className="logo"
|
||||
alt={siteConfig.name}
|
||||
title={siteConfig.name}
|
||||
/>
|
||||
</a>
|
||||
{/* if is login page, we will show logout */}
|
||||
{pathname !== '/login' && (
|
||||
<Dropdown trigger={<div className="logout">{this.getUsername()}</div>}>
|
||||
<Menu>
|
||||
<Menu.Item onClick={this.logout}>{locale.logout}</Menu.Item>
|
||||
<Menu.Item onClick={this.changePassword}>{locale.changePassword}</Menu.Item>
|
||||
</Menu>
|
||||
</Dropdown>
|
||||
)}
|
||||
<span className="language-switch language-switch-primary" onClick={this.switchLang}>
|
||||
{languageSwitchButton}
|
||||
</span>
|
||||
<div className="header-menu header-menu-open">
|
||||
<ul>
|
||||
{NAV_MENU.map(item => (
|
||||
<li key={item.id} className="menu-item menu-item-primary">
|
||||
<a href={item.link} target="_blank" rel="noopener noreferrer">
|
||||
{item.title}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<>
|
||||
<header className="header-container header-container-primary">
|
||||
<div className="header-body">
|
||||
<a
|
||||
href={`https://nacos.io/${language.toLocaleLowerCase()}/`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<img
|
||||
src="img/logo-2000-390.svg"
|
||||
className="logo"
|
||||
alt={siteConfig.name}
|
||||
title={siteConfig.name}
|
||||
/>
|
||||
</a>
|
||||
{/* if is login page, we will show logout */}
|
||||
{pathname !== '/login' && (
|
||||
<Dropdown trigger={<div className="logout">{this.getUsername()}</div>}>
|
||||
<Menu>
|
||||
<Menu.Item onClick={this.logout}>{locale.logout}</Menu.Item>
|
||||
<Menu.Item onClick={this.changePassword}>{locale.changePassword}</Menu.Item>
|
||||
</Menu>
|
||||
</Dropdown>
|
||||
)}
|
||||
<span className="language-switch language-switch-primary" onClick={this.switchLang}>
|
||||
{languageSwitchButton}
|
||||
</span>
|
||||
<div className="header-menu header-menu-open">
|
||||
<ul>
|
||||
{NAV_MENU.map(item => (
|
||||
<li key={item.id} className="menu-item menu-item-primary">
|
||||
<a href={item.link} target="_blank" rel="noopener noreferrer">
|
||||
{item.title}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
</header>
|
||||
<PasswordReset
|
||||
username={passwordResetUser}
|
||||
onOk={user =>
|
||||
passwordReset(user).then(res => {
|
||||
return res;
|
||||
})
|
||||
}
|
||||
onCancel={() => this.setState({ passwordResetUser: undefined })}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -13,506 +13,127 @@
|
||||
|
||||
import React from 'react';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import PropTypes from 'prop-types';
|
||||
import { ConfigProvider, Icon } from '@alifd/next';
|
||||
import Header from './Header';
|
||||
import $ from 'jquery';
|
||||
import { connect } from 'react-redux';
|
||||
import { setParams } from '../globalLib';
|
||||
import PropTypes from 'prop-types';
|
||||
import { ConfigProvider, Icon, Menu } from '@alifd/next';
|
||||
import Header from './Header';
|
||||
import { getState } from '../reducers/base';
|
||||
import _menu from '../menu';
|
||||
import getMenuData from './menu';
|
||||
|
||||
import './index.scss';
|
||||
const { SubMenu, Item } = Menu;
|
||||
|
||||
@withRouter
|
||||
@connect(
|
||||
state => ({ ...state.locale, ...state.base }),
|
||||
{ getState }
|
||||
)
|
||||
@connect(state => ({ ...state.locale, ...state.base }), { getState })
|
||||
@ConfigProvider.config
|
||||
class MainLayout extends React.Component {
|
||||
static displayName = 'MainLayout';
|
||||
|
||||
static propTypes = {
|
||||
history: PropTypes.object,
|
||||
location: PropTypes.object,
|
||||
locale: PropTypes.object,
|
||||
children: PropTypes.any,
|
||||
location: PropTypes.object,
|
||||
history: PropTypes.object,
|
||||
version: PropTypes.any,
|
||||
functionMode: PropTypes.any,
|
||||
getState: PropTypes.func,
|
||||
functionMode: PropTypes.string,
|
||||
children: PropTypes.object,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.deepNav = [];
|
||||
this.oneLevelNavArr = {}; // 平行导航map
|
||||
this.state = {
|
||||
navList: [..._menu.data],
|
||||
leftBarClose: false,
|
||||
showLink: null,
|
||||
navRow: [],
|
||||
noChild: false,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.props.getState();
|
||||
this.refreshNav();
|
||||
}
|
||||
|
||||
goBack() {
|
||||
this.props.history.goBack();
|
||||
}
|
||||
|
||||
nacosToggleNav(id, event) {
|
||||
event.preventDefault();
|
||||
const nowNav = document.getElementById(id);
|
||||
const iconClass = nowNav.querySelector('.iconshow');
|
||||
const subNav = nowNav.querySelector('.subnavlist');
|
||||
const { classList } = iconClass;
|
||||
let tmpClassName = 'iconshow ';
|
||||
for (let i = 0; i < classList.length; i++) {
|
||||
if (classList[i] === 'icon-arrow-down') {
|
||||
subNav.style.display = 'none';
|
||||
subNav.className += ' hidden';
|
||||
tmpClassName += 'icon-arrow-right';
|
||||
}
|
||||
if (classList[i] === 'icon-arrow-right') {
|
||||
tmpClassName += 'icon-arrow-down';
|
||||
subNav.className = subNav.className.replace(/hidden/g, '');
|
||||
subNav.style.display = 'block';
|
||||
}
|
||||
}
|
||||
iconClass.className = tmpClassName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Click the back button
|
||||
* TODO: this.props.history.goBack(); ???
|
||||
* @param url
|
||||
*/
|
||||
nacosGoBack(url) {
|
||||
const params = window.location.hash.split('?')[1];
|
||||
const urlArr = params.split('&') || [];
|
||||
const queryParams = [];
|
||||
for (let i = 0; i < urlArr.length; i++) {
|
||||
if (
|
||||
urlArr[i].split('=')[0] !== '_k' &&
|
||||
urlArr[i].split('=')[0] !== 'dataId' &&
|
||||
urlArr[i].split('=')[0] !== 'group'
|
||||
) {
|
||||
if (urlArr[i].split('=')[0] === 'searchDataId') {
|
||||
queryParams.push(`dataId=${urlArr[i].split('=')[1]}`);
|
||||
} else if (urlArr[i].split('=')[0] === 'searchGroup') {
|
||||
queryParams.push(`group=${urlArr[i].split('=')[1]}`);
|
||||
} else {
|
||||
queryParams.push(urlArr[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (localStorage.getItem('namespace')) {
|
||||
queryParams.push(`namespace=${localStorage.getItem('namespace')}`);
|
||||
}
|
||||
this.props.history.push(`/${url}?${queryParams.join('&')}`);
|
||||
}
|
||||
|
||||
nacosEnterBack() {
|
||||
document.getElementById('backarrow').style.color = '#09c';
|
||||
}
|
||||
|
||||
nacosOutBack() {
|
||||
document.getElementById('backarrow').style.color = '#546478';
|
||||
}
|
||||
|
||||
nacosToggleLeftBar() {
|
||||
if (!this.nacosOutDom) return;
|
||||
if (!this.state.leftBarClose) {
|
||||
// 关闭
|
||||
this.nacosOutDom.className = 'viewFramework-product';
|
||||
this.nacosLeftBarDom.style.width = 0;
|
||||
this.nacosBodyDom.style.left = 0;
|
||||
this.nacosToggleIconDom.style.left = 0;
|
||||
} else {
|
||||
this.nacosOutDom.className = 'viewFramework-product viewFramework-product-col-1';
|
||||
this.nacosLeftBarDom.style.width = '180px';
|
||||
this.nacosBodyDom.style.left = '180px';
|
||||
this.nacosToggleIconDom.style.left = '160px';
|
||||
}
|
||||
|
||||
this.setState({
|
||||
leftBarClose: !this.state.leftBarClose,
|
||||
});
|
||||
}
|
||||
|
||||
navTo(url) {
|
||||
if (url !== '/configdetail' && url !== '/configeditor') {
|
||||
// 二级菜单不清空
|
||||
setParams({
|
||||
dataId: '',
|
||||
group: '',
|
||||
});
|
||||
}
|
||||
|
||||
const params = window.location.hash.split('?')[1];
|
||||
const urlArr = params.split('&') || [];
|
||||
const queryParams = [];
|
||||
for (let i = 0; i < urlArr.length; i++) {
|
||||
if (urlArr[i].split('=')[0] !== '_k') {
|
||||
queryParams.push(urlArr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
this.props.history.push(`${url}?${queryParams.join('&')}`);
|
||||
const { search } = this.props.location;
|
||||
this.props.history.push([url, search].join(''));
|
||||
}
|
||||
|
||||
nacosSetSpecialNav(item) {
|
||||
item.children.forEach(_item => {
|
||||
const obj = _item;
|
||||
isCurrentPath(url) {
|
||||
const { location } = this.props;
|
||||
return url === location.pathname ? 'current-path' : undefined;
|
||||
}
|
||||
|
||||
if (obj.dontUseChild === true) {
|
||||
obj.parentName = item.title;
|
||||
obj.parentId = item.id;
|
||||
obj.parentPath = `/${item.id}`;
|
||||
this.deepNav.push(obj);
|
||||
}
|
||||
if (_item.children) {
|
||||
this.nacosSetSpecialNav(_item);
|
||||
defaultOpenKeys() {
|
||||
const MenuData = getMenuData(this.props.functionMode);
|
||||
for (let i = 0, len = MenuData.length; i < len; i++) {
|
||||
const { children } = MenuData[i];
|
||||
if (children && children.filter(({ url }) => url === this.props.location.pathname).length) {
|
||||
return String(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isShowGoBack() {
|
||||
const urls = [];
|
||||
const MenuData = getMenuData(this.props.functionMode);
|
||||
MenuData.forEach(item => {
|
||||
if (item.url) urls.push(item.url);
|
||||
if (item.children) item.children.forEach(({ url }) => urls.push(url));
|
||||
});
|
||||
}
|
||||
|
||||
nacosNavAct(serviceName, match, location) {
|
||||
if (!match) {
|
||||
const formatpath = location.pathname.substr(1); // 得到当前路径
|
||||
const nowpathobj = this.oneLevelNavArr[formatpath]; // 根据平行导航匹配父类
|
||||
if (nowpathobj) {
|
||||
if (nowpathobj.parent === serviceName) {
|
||||
// 如果父类等于当前的导航则高亮
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
nacosLoopNavDeeply(data, parentServiceName) {
|
||||
// 深度遍历获取所有的导航数据
|
||||
data.forEach(item => {
|
||||
if (item) {
|
||||
const navObj = item;
|
||||
|
||||
const _parentServiceName = item.serviceName;
|
||||
navObj.parentServiceName = parentServiceName;
|
||||
this.oneLevelNavArr[item.serviceName] = navObj; // 得到每一个层级的导航映射
|
||||
if (item.children && item.children.length > 0) {
|
||||
this.nacosLoopNavDeeply(item.children, _parentServiceName);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
activeNav(id) {
|
||||
if (this.preActNav) {
|
||||
this.preActNav.removeClass('active');
|
||||
}
|
||||
const nowNav = $(`#${id}`);
|
||||
nowNav.addClass('active');
|
||||
this.preActNav = nowNav;
|
||||
}
|
||||
|
||||
nacosLoopNav(data, _index = 0, parent) {
|
||||
const { locale = {}, location = {} } = this.props;
|
||||
const { pathname } = location;
|
||||
let index = _index;
|
||||
// 遍历导航,只显示2级
|
||||
const self = this;
|
||||
return data.map(item => {
|
||||
if (!item) return '';
|
||||
index++;
|
||||
if (item.dontUseChild === true) return '';
|
||||
if (item.children && item.children.length > 0) {
|
||||
if (item.isVirtual) {
|
||||
// 如果是虚拟菜单需要增加展开箭头
|
||||
const icon = item.isExtend ? (
|
||||
<span className="icon-arrow-down iconshow" />
|
||||
) : (
|
||||
<span className="icon-arrow-right iconshow" />
|
||||
);
|
||||
const hiddenClass = item.isExtend ? '' : 'hidden';
|
||||
return (
|
||||
<li
|
||||
style={{ display: item.enable ? 'block' : 'none' }}
|
||||
key={`${item.serviceName}`}
|
||||
data-spm-click={`gostr=/aliyun;locaid=${item.serviceName}`}
|
||||
id={`${item.serviceName}`}
|
||||
>
|
||||
<div>
|
||||
<a href="" onClick={this.nacosToggleNav.bind(this, item.serviceName)}>
|
||||
<div className="nav-icon">{icon}</div>
|
||||
<div className="nav-title">{locale[item.serviceName]}</div>
|
||||
</a>
|
||||
</div>
|
||||
<ul className={`subnavlist ${hiddenClass}`}>
|
||||
{self.nacosLoopNav(item.children, index)}
|
||||
</ul>
|
||||
</li>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<li
|
||||
className={pathname === `/${item.serviceName}` ? 'selected' : ''}
|
||||
key={`${item.serviceName}`}
|
||||
data-spm-click={`gostr=/aliyun;locaid=${item.serviceName}`}
|
||||
onClick={this.navTo.bind(this, `/${item.serviceName}`)}
|
||||
>
|
||||
<a
|
||||
href="javascript:;"
|
||||
id={`${item.serviceName}`}
|
||||
onClick={this.activeNav.bind(this, `nav${index}`)}
|
||||
>
|
||||
<div className="nav-icon" />
|
||||
<div className="nav-title">{locale[item.serviceName]}</div>
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
}
|
||||
return (
|
||||
<li
|
||||
className={pathname === `/${item.serviceName}` ? 'selected' : ''}
|
||||
key={`${item.serviceName}`}
|
||||
data-spm-click={`gostr=/aliyun;locaid=${item.serviceName}`}
|
||||
onClick={this.navTo.bind(this, `/${item.serviceName}`)}
|
||||
>
|
||||
<a
|
||||
href={'javascript:;'}
|
||||
id={`${item.serviceName}`}
|
||||
onClick={this.activeNav.bind(this, `nav${index}`)}
|
||||
>
|
||||
<div className="nav-icon" />
|
||||
<div className="nav-title">{locale[item.serviceName]}</div>
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
nacosGetNav(navList) {
|
||||
let navRow = ''; // 导航
|
||||
if (navList.length > 0) {
|
||||
navRow = <ul>{this.nacosLoopNav(navList)}</ul>;
|
||||
this.nacosLoopNavDeeply(navList); // 深度遍历导航树获得平行map
|
||||
}
|
||||
return navRow;
|
||||
}
|
||||
|
||||
renderNav() {
|
||||
const { navList } = this.state;
|
||||
this.nacosLeftBarDom = document.getElementById('viewFramework-product-navbar');
|
||||
this.nacosBodyDom = document.getElementById('viewFramework-product-body');
|
||||
this.nacosToggleIconDom = document.getElementById('viewFramework-product-navbar-collapse');
|
||||
this.nacosOutDom = document.getElementById('page-header-mask');
|
||||
const defaultNav = '/configurationManagement';
|
||||
this.props.history.listen(location => {
|
||||
if (this.preSimplePath && this.preSimplePath !== '/') {
|
||||
if (location.pathname.indexOf(this.preSimplePath) !== -1) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
const simplePath = window.location.hash.split('?')[0];
|
||||
const navName = simplePath.substr('2');
|
||||
this.preSimplePath = simplePath;
|
||||
|
||||
if (navName === '') {
|
||||
this.props.history.push(defaultNav);
|
||||
setTimeout(() => {
|
||||
this.activeNav('configurationManagement');
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const nowNavObj = this.oneLevelNavArr[navName];
|
||||
if (!nowNavObj) {
|
||||
this.setState({
|
||||
noChild: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
const { parentServiceName } = nowNavObj;
|
||||
|
||||
const parentNav = this.oneLevelNavArr[parentServiceName];
|
||||
if (simplePath !== '/' && nowNavObj && parentNav && !parentNav.isVirtual) {
|
||||
this.setState({
|
||||
showLink: (
|
||||
<div>
|
||||
<Icon
|
||||
type="arrow-left"
|
||||
onClick={this.nacosGoBack.bind(this, parentServiceName)}
|
||||
id={'backarrow'}
|
||||
onMouseOver={this.nacosEnterBack.bind(this)}
|
||||
onMouseLeave={this.nacosOutBack.bind(this)}
|
||||
style={{
|
||||
marginLeft: 77,
|
||||
marginTop: 0,
|
||||
fontWeight: 'bold',
|
||||
cursor: 'pointer',
|
||||
color: '#546478',
|
||||
fontSize: '20px',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
|
||||
navRow: <ul>{this.nacosLoopNav([nowNavObj])}</ul>,
|
||||
});
|
||||
setTimeout(() => {
|
||||
const navid = navName;
|
||||
this.activeNav(navid);
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
showLink: null,
|
||||
navRow: <ul>{this.nacosLoopNav(navList)}</ul>,
|
||||
});
|
||||
setTimeout(() => {
|
||||
const navid = navName;
|
||||
this.activeNav(navid);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
refreshNav() {
|
||||
const { navList } = this.state;
|
||||
const { location, history, functionMode } = this.props;
|
||||
const [configUrl, serviceUrl, clusterUrl] = [
|
||||
'/configurationManagement',
|
||||
'/serviceManagement',
|
||||
'/clusterManagement',
|
||||
];
|
||||
this.setState(
|
||||
{
|
||||
navList: navList.map(item => {
|
||||
if (
|
||||
item.serviceName === 'configurationManagementVirtual' &&
|
||||
(functionMode === null || functionMode === 'config')
|
||||
) {
|
||||
item.enable = true;
|
||||
}
|
||||
if (
|
||||
item.serviceName === 'serviceManagementVirtual' &&
|
||||
(functionMode === null || functionMode === 'naming')
|
||||
) {
|
||||
item.enable = true;
|
||||
}
|
||||
if (
|
||||
item.serviceName === 'clusterManagementVirtual' &&
|
||||
(functionMode === null || functionMode === 'cluster')
|
||||
) {
|
||||
item.enable = true;
|
||||
}
|
||||
return item;
|
||||
}),
|
||||
},
|
||||
() => this.setState({ navRow: this.nacosGetNav(navList) }, () => this.renderNav())
|
||||
);
|
||||
if (functionMode === 'config' && location.pathname === serviceUrl) {
|
||||
history.push(configUrl);
|
||||
}
|
||||
if (functionMode === 'naming' && location.pathname === configUrl) {
|
||||
history.push(serviceUrl);
|
||||
}
|
||||
if (functionMode === 'cluster' && location.pathname === clusterUrl) {
|
||||
history.push(clusterUrl);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps() {
|
||||
setTimeout(() => this.refreshNav());
|
||||
return !urls.includes(this.props.location.pathname);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { locale = {}, version } = this.props;
|
||||
const { nacosName, doesNotExist } = locale;
|
||||
const { showLink, navRow, leftBarClose, noChild } = this.state;
|
||||
const { locale = {}, version, functionMode } = this.props;
|
||||
const MenuData = getMenuData(functionMode);
|
||||
return (
|
||||
<div className="viewFramework-product" style={{ top: 66 }}>
|
||||
<>
|
||||
<Header />
|
||||
<div
|
||||
className="viewFramework-product-navbar"
|
||||
style={{ width: 180, marginLeft: 0 }}
|
||||
id="viewFramework-product-navbar"
|
||||
data-spm="acm_nav"
|
||||
>
|
||||
<div className="viewFramework-product-navbar-removed">
|
||||
<div>
|
||||
<div className="product-nav-scene product-nav-main-scene">
|
||||
{showLink ? (
|
||||
<div className="product-nav-icon env" style={{ height: 80, paddingTop: 25 }}>
|
||||
{showLink}
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
style={{ textIndent: 0, display: !version ? 'none' : 'block' }}
|
||||
className="product-nav-title"
|
||||
title={nacosName}
|
||||
>
|
||||
<span>{nacosName}</span>
|
||||
<span style={{ marginLeft: 5 }}>{version}</span>
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className="product-nav-list"
|
||||
style={{ position: 'relative', top: 0, height: '100%' }}
|
||||
>
|
||||
{navRow}
|
||||
</div>
|
||||
<div className="main-container">
|
||||
<div className="left-panel">
|
||||
{this.isShowGoBack() ? (
|
||||
<div className="go-back" onClick={() => this.goBack()}>
|
||||
<Icon type="arrow-left" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="viewFramework-product-navbar-collapse"
|
||||
id="viewFramework-product-navbar-collapse"
|
||||
onClick={this.nacosToggleLeftBar.bind(this)}
|
||||
>
|
||||
<div className="product-navbar-collapse-inner">
|
||||
<div className="product-navbar-collapse-bg" />
|
||||
<div className="product-navbar-collapse">
|
||||
{leftBarClose ? (
|
||||
<span className="icon-collapse-right" style={{ display: 'block' }} />
|
||||
) : (
|
||||
<span className="icon-collapse-left" />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="viewFramework-product-body"
|
||||
style={{ marginLeft: 180 }}
|
||||
id="viewFramework-product-body"
|
||||
>
|
||||
<div>
|
||||
{!noChild ? (
|
||||
<div>{this.props.children}</div>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
height: 300,
|
||||
lineHeight: 300,
|
||||
textAlign: 'center',
|
||||
fontSize: 18,
|
||||
}}
|
||||
>
|
||||
{doesNotExist}
|
||||
</div>
|
||||
<>
|
||||
<h1 className="nav-title">
|
||||
{locale.nacosName}
|
||||
<span>{version}</span>
|
||||
</h1>
|
||||
<Menu
|
||||
defaultOpenKeys={this.defaultOpenKeys()}
|
||||
className="nav-menu"
|
||||
openMode="single"
|
||||
>
|
||||
{MenuData.map((subMenu, idx) => {
|
||||
if (subMenu.children) {
|
||||
return (
|
||||
<SubMenu key={String(idx)} label={locale[subMenu.key]}>
|
||||
{subMenu.children.map((item, i) => (
|
||||
<Item
|
||||
key={[idx, i].join('-')}
|
||||
onClick={() => this.navTo(item.url)}
|
||||
className={this.isCurrentPath(item.url)}
|
||||
>
|
||||
{locale[item.key]}
|
||||
</Item>
|
||||
))}
|
||||
</SubMenu>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Item
|
||||
key={idx}
|
||||
className={['first-menu', this.isCurrentPath(subMenu.url)]
|
||||
.filter(c => c)
|
||||
.join(' ')}
|
||||
onClick={() => this.navTo(subMenu.url)}
|
||||
>
|
||||
{locale[subMenu.key]}
|
||||
</Item>
|
||||
);
|
||||
})}
|
||||
</Menu>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div className="right-panel">{this.props.children}</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -11,14 +11,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
.header-container {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
z-index: 1000;
|
||||
background-color: #fff;
|
||||
}
|
||||
.header-container-primary {
|
||||
background: #252a2f;
|
||||
}
|
||||
@ -1396,5 +1388,63 @@ h6 {
|
||||
}
|
||||
|
||||
.product-nav-list li.selected a {
|
||||
background-color: #F4F6F8;
|
||||
background-color: #f4f6f8;
|
||||
}
|
||||
|
||||
.main-container {
|
||||
height: calc(100vh - 66px);
|
||||
.left-panel,
|
||||
.right-panel {
|
||||
float: left;
|
||||
height: 100%;
|
||||
}
|
||||
.left-panel {
|
||||
width: 180px;
|
||||
background-color: #eaedf1;
|
||||
}
|
||||
.right-panel {
|
||||
width: calc(100% - 180px);
|
||||
padding: 10px;
|
||||
overflow: scroll;
|
||||
}
|
||||
.nav-title {
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
line-height: 70px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
background-color: #d9dee4;
|
||||
span {
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
.nav-menu {
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
line-height: 40px;
|
||||
div.next-menu-item,
|
||||
.first-menu > .next-menu-item-inner {
|
||||
color: #333;
|
||||
}
|
||||
.next-menu-item-inner {
|
||||
height: 40px;
|
||||
color: #666;
|
||||
}
|
||||
.current-path {
|
||||
background-color: #f2f3f7;
|
||||
}
|
||||
}
|
||||
.go-back {
|
||||
text-align: center;
|
||||
color: rgb(84, 100, 120);
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
padding: 10px 0;
|
||||
margin-top: 14px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,75 @@
|
||||
import { isJsonString } from '../utils/nacosutil';
|
||||
|
||||
const configurationMenu = {
|
||||
key: 'configurationManagementVirtual',
|
||||
children: [
|
||||
{
|
||||
key: 'configurationManagement',
|
||||
url: '/configurationManagement',
|
||||
},
|
||||
{
|
||||
key: 'historyRollback',
|
||||
url: '/historyRollback',
|
||||
},
|
||||
{
|
||||
key: 'listeningToQuery',
|
||||
url: '/listeningToQuery',
|
||||
},
|
||||
],
|
||||
};
|
||||
/**
|
||||
* 权限控制相关
|
||||
*/
|
||||
const authorityControlMenu = {
|
||||
key: 'authorityControl',
|
||||
children: [
|
||||
{
|
||||
key: 'userList',
|
||||
url: '/userManagement',
|
||||
},
|
||||
{
|
||||
key: 'roleManagement',
|
||||
url: '/rolesManagement',
|
||||
},
|
||||
{
|
||||
key: 'privilegeManagement',
|
||||
url: '/permissionsManagement',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default function(model) {
|
||||
const { token = '{}' } = localStorage;
|
||||
const { globalAdmin } = isJsonString(token) ? JSON.parse(token) || {} : {};
|
||||
|
||||
return [
|
||||
model === 'naming' ? undefined : configurationMenu,
|
||||
{
|
||||
key: 'serviceManagementVirtual',
|
||||
children: [
|
||||
{
|
||||
key: 'serviceManagement',
|
||||
url: '/serviceManagement',
|
||||
},
|
||||
{
|
||||
key: 'subscriberList',
|
||||
url: '/subscriberList',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'clusterManagementVirtual',
|
||||
children: [
|
||||
{
|
||||
key: 'clusterManagement',
|
||||
url: '/clusterManagement',
|
||||
},
|
||||
],
|
||||
},
|
||||
globalAdmin ? authorityControlMenu : undefined,
|
||||
{
|
||||
key: 'namespace',
|
||||
url: '/namespace',
|
||||
},
|
||||
].filter(item => item);
|
||||
}
|
@ -50,6 +50,10 @@ const I18N_CONF = {
|
||||
namespace: 'Namespace',
|
||||
clusterManagementVirtual: 'ClusterManagement',
|
||||
clusterManagement: 'Cluster Node List',
|
||||
authorityControl: 'Authority Control',
|
||||
userList: 'User List',
|
||||
roleManagement: 'Role Management',
|
||||
privilegeManagement: 'Privilege Management',
|
||||
},
|
||||
Password: {
|
||||
passwordNotConsistent: 'The passwords are not consistent',
|
||||
@ -505,6 +509,86 @@ const I18N_CONF = {
|
||||
update: 'Update',
|
||||
insert: 'Insert',
|
||||
},
|
||||
UserManagement: {
|
||||
userManagement: 'User Management',
|
||||
createUser: 'Create user',
|
||||
resetPassword: 'Edit',
|
||||
deleteUser: 'Delete',
|
||||
deleteUserTip: 'Do you want to delete this user?',
|
||||
username: 'Username',
|
||||
password: 'Password',
|
||||
operation: 'Operation',
|
||||
},
|
||||
NewUser: {
|
||||
createUser: 'Create user',
|
||||
username: 'Username',
|
||||
password: 'Password',
|
||||
rePassword: 'Repeat',
|
||||
usernamePlaceholder: 'Please Enter Username',
|
||||
passwordPlaceholder: 'Please Enter Password',
|
||||
rePasswordPlaceholder: 'Please Enter Repeat Password',
|
||||
usernameError: 'User name cannot be empty!',
|
||||
passwordError: 'Password cannot be empty!',
|
||||
rePasswordError: 'Repeat Password cannot be empty!',
|
||||
rePasswordError2: 'Passwords are inconsistent!',
|
||||
},
|
||||
PasswordReset: {
|
||||
resetPassword: 'Password Reset',
|
||||
username: 'Username',
|
||||
password: 'Password',
|
||||
rePassword: 'Repeat',
|
||||
passwordPlaceholder: 'Please Enter Password',
|
||||
rePasswordPlaceholder: 'Please Enter Repeat Password',
|
||||
passwordError: 'Password cannot be empty!',
|
||||
rePasswordError: 'Repeat Password cannot be empty!',
|
||||
rePasswordError2: 'Passwords are inconsistent!',
|
||||
},
|
||||
RolesManagement: {
|
||||
roleManagement: 'Role management',
|
||||
bindingRoles: 'Binding roles',
|
||||
role: 'Role',
|
||||
username: 'Username',
|
||||
operation: 'Operation',
|
||||
deleteRole: 'Delete',
|
||||
deleteRoleTip: 'Do you want to delete this role?',
|
||||
},
|
||||
NewRole: {
|
||||
bindingRoles: 'Binding roles',
|
||||
username: 'Username',
|
||||
role: 'Role',
|
||||
usernamePlaceholder: 'Please Enter Username',
|
||||
rolePlaceholder: 'Please Enter Role',
|
||||
usernameError: 'User name cannot be empty!',
|
||||
roleError: 'Role cannot be empty!',
|
||||
},
|
||||
PermissionsManagement: {
|
||||
privilegeManagement: 'Permissions Management',
|
||||
addPermission: 'Add Permission',
|
||||
role: 'Role',
|
||||
resource: 'Resource',
|
||||
action: 'Action',
|
||||
operation: 'Operation',
|
||||
deletePermission: 'Delete',
|
||||
deletePermissionTip: 'Do you want to delete this permission?',
|
||||
readOnly: 'read only',
|
||||
writeOnly: 'write only',
|
||||
readWrite: 'Read and write',
|
||||
},
|
||||
NewPermissions: {
|
||||
addPermission: 'Add Permission',
|
||||
role: 'Role',
|
||||
resource: 'Resource',
|
||||
action: 'Action',
|
||||
resourcePlaceholder: 'Please select resources',
|
||||
rolePlaceholder: 'Please enter Role',
|
||||
actionPlaceholder: 'Please select Action',
|
||||
resourceError: 'Resource cannot be empty!',
|
||||
roleError: 'Role cannot be empty!',
|
||||
actionError: 'Action cannot be empty!',
|
||||
readOnly: 'read only',
|
||||
writeOnly: 'write only',
|
||||
readWrite: 'Read and write',
|
||||
},
|
||||
};
|
||||
|
||||
export default I18N_CONF;
|
||||
|
@ -50,6 +50,10 @@ const I18N_CONF = {
|
||||
namespace: '命名空间',
|
||||
clusterManagementVirtual: '集群管理',
|
||||
clusterManagement: '节点列表',
|
||||
authorityControl: '权限控制',
|
||||
userList: '用户列表',
|
||||
roleManagement: '角色管理',
|
||||
privilegeManagement: '权限管理',
|
||||
},
|
||||
Password: {
|
||||
passwordNotConsistent: '两次输入密码不一致',
|
||||
@ -502,6 +506,86 @@ const I18N_CONF = {
|
||||
update: '更新',
|
||||
insert: '插入',
|
||||
},
|
||||
UserManagement: {
|
||||
userManagement: '用户管理',
|
||||
createUser: '创建用户',
|
||||
resetPassword: '修改',
|
||||
deleteUser: '删除',
|
||||
deleteUserTip: '是否要删除该用户?',
|
||||
username: '用户名',
|
||||
password: '密码',
|
||||
operation: '操作',
|
||||
},
|
||||
NewUser: {
|
||||
createUser: '创建用户',
|
||||
username: '用户名',
|
||||
password: '密码',
|
||||
rePassword: '确认密码',
|
||||
usernamePlaceholder: '请输入用户名',
|
||||
passwordPlaceholder: '请输入密码',
|
||||
rePasswordPlaceholder: '请输入确认密码',
|
||||
usernameError: '用户名不能为空!',
|
||||
passwordError: '密码不能为空!',
|
||||
rePasswordError: '确认密码不能为空!',
|
||||
rePasswordError2: '两次输入密码不一致!',
|
||||
},
|
||||
PasswordReset: {
|
||||
resetPassword: '密码重置',
|
||||
username: '用户名',
|
||||
password: '密码',
|
||||
rePassword: '确认密码',
|
||||
passwordError: '密码不能为空!',
|
||||
passwordPlaceholder: '请输入密码',
|
||||
rePasswordPlaceholder: '请输入确认密码',
|
||||
rePasswordError: '确认密码不能为空!',
|
||||
rePasswordError2: '两次输入密码不一致!',
|
||||
},
|
||||
RolesManagement: {
|
||||
roleManagement: '角色管理',
|
||||
bindingRoles: '绑定角色',
|
||||
role: '角色名',
|
||||
username: '用户名',
|
||||
operation: '操作',
|
||||
deleteRole: '删除',
|
||||
deleteRoleTip: '是否要删除该角色?',
|
||||
},
|
||||
NewRole: {
|
||||
bindingRoles: '绑定角色',
|
||||
username: '用户名',
|
||||
role: '角色名',
|
||||
usernamePlaceholder: '请输入用户名',
|
||||
rolePlaceholder: '请输入角色名',
|
||||
usernameError: '用户名不能为空!',
|
||||
roleError: '角色名不能为空!',
|
||||
},
|
||||
PermissionsManagement: {
|
||||
privilegeManagement: '权限管理',
|
||||
addPermission: '添加权限',
|
||||
role: '角色名',
|
||||
resource: '资源',
|
||||
action: '动作',
|
||||
operation: '操作',
|
||||
deletePermission: '删除',
|
||||
deletePermissionTip: '是否要删除该权限?',
|
||||
readOnly: '只读',
|
||||
writeOnly: '只写',
|
||||
readWrite: '读写',
|
||||
},
|
||||
NewPermissions: {
|
||||
addPermission: '添加权限',
|
||||
role: '角色名',
|
||||
resource: '资源',
|
||||
action: '动作',
|
||||
resourcePlaceholder: '请选择资源',
|
||||
rolePlaceholder: '请输入角色名',
|
||||
actionPlaceholder: '请选择动作',
|
||||
resourceError: '资源不能为空!',
|
||||
roleError: '角色名不能为空!',
|
||||
actionError: '动作不能为空!',
|
||||
readOnly: '只读',
|
||||
writeOnly: '只写',
|
||||
readWrite: '读写',
|
||||
},
|
||||
};
|
||||
|
||||
export default I18N_CONF;
|
||||
|
@ -1,295 +0,0 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
data: [
|
||||
{
|
||||
enable: false,
|
||||
isExtend: true,
|
||||
name: '配置管理',
|
||||
title: '配置管理',
|
||||
isVirtual: true,
|
||||
projectName: 'nacos',
|
||||
serviceName: 'configurationManagementVirtual',
|
||||
link: 'configurationManagementVirtual',
|
||||
hasFusion: true,
|
||||
template: '',
|
||||
registerName: 'com.alibaba.nacos.page.configurationManagementVirtual',
|
||||
useRouter: false,
|
||||
id: 'com.alibaba.nacos.page.configurationManagementVirtual',
|
||||
children: [
|
||||
{
|
||||
isExtend: false,
|
||||
name: '配置列表',
|
||||
title: '配置列表',
|
||||
isVirtual: false,
|
||||
projectName: 'nacos',
|
||||
serviceName: 'configurationManagement',
|
||||
link: 'configurationManagement',
|
||||
hasFusion: true,
|
||||
template: '',
|
||||
dontUseChild: false,
|
||||
registerName: 'com.alibaba.nacos.page.configurationManagement',
|
||||
useRouter: false,
|
||||
id: 'configurationManagement',
|
||||
children: [
|
||||
{
|
||||
isExtend: false,
|
||||
name: '配置详情',
|
||||
title: '配置详情',
|
||||
isVirtual: false,
|
||||
projectName: 'nacos',
|
||||
serviceName: 'configdetail',
|
||||
link: 'Configdetail',
|
||||
hasFusion: true,
|
||||
template: '',
|
||||
dontUseChild: false,
|
||||
registerName: 'com.alibaba.nacos.page.configdetail',
|
||||
useRouter: false,
|
||||
id: 'configdetail',
|
||||
},
|
||||
{
|
||||
isExtend: false,
|
||||
name: '同步配置',
|
||||
title: '同步配置',
|
||||
isVirtual: false,
|
||||
projectName: 'nacos',
|
||||
serviceName: 'configsync',
|
||||
link: 'configsync',
|
||||
hasFusion: true,
|
||||
template: '',
|
||||
dontUseChild: true,
|
||||
registerName: 'com.alibaba.nacos.page.configsync',
|
||||
useRouter: false,
|
||||
id: 'configsync',
|
||||
},
|
||||
{
|
||||
isExtend: false,
|
||||
name: '配置编辑',
|
||||
title: '配置编辑',
|
||||
isVirtual: false,
|
||||
projectName: 'nacos',
|
||||
serviceName: 'configeditor',
|
||||
link: 'configeditor',
|
||||
hasFusion: true,
|
||||
template: '',
|
||||
registerName: 'com.alibaba.nacos.page.configeditor',
|
||||
useRouter: false,
|
||||
id: 'configeditor',
|
||||
},
|
||||
{
|
||||
isExtend: false,
|
||||
name: '新建配置',
|
||||
title: '新建配置',
|
||||
isVirtual: false,
|
||||
projectName: 'nacos',
|
||||
serviceName: 'newconfig',
|
||||
link: 'newconfig',
|
||||
hasFusion: true,
|
||||
template: '',
|
||||
registerName: 'com.alibaba.nacos.page.newconfig',
|
||||
useRouter: false,
|
||||
id: 'newconfig',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
isExtend: false,
|
||||
name: '历史版本',
|
||||
title: '历史版本',
|
||||
isVirtual: false,
|
||||
projectName: 'nacos',
|
||||
children: [
|
||||
{
|
||||
isExtend: false,
|
||||
name: '配置回滚',
|
||||
title: '配置回滚',
|
||||
isVirtual: false,
|
||||
projectName: 'nacos',
|
||||
serviceName: 'configRollback',
|
||||
link: 'configRollback',
|
||||
hasFusion: true,
|
||||
template: '',
|
||||
registerName: 'com.alibaba.nacos.page.configRollback',
|
||||
useRouter: false,
|
||||
id: 'configRollback',
|
||||
},
|
||||
{
|
||||
isExtend: false,
|
||||
name: '历史详情',
|
||||
title: '历史详情',
|
||||
isVirtual: false,
|
||||
projectName: 'nacos',
|
||||
serviceName: 'historyDetail',
|
||||
link: 'historyDetail',
|
||||
hasFusion: true,
|
||||
template: '',
|
||||
registerName: 'com.alibaba.nacos.page.historyDetail',
|
||||
useRouter: false,
|
||||
id: 'historyDetail',
|
||||
},
|
||||
],
|
||||
serviceName: 'historyRollback',
|
||||
link: 'historyRollback',
|
||||
hasFusion: true,
|
||||
template: '',
|
||||
dontUseChild: false,
|
||||
registerName: 'com.alibaba.nacos.page.historyRollback',
|
||||
useRouter: false,
|
||||
id: 'historyRollback',
|
||||
},
|
||||
{
|
||||
isExtend: false,
|
||||
name: '监听查询',
|
||||
title: '监听查询',
|
||||
isVirtual: false,
|
||||
projectName: 'nacos',
|
||||
serviceName: 'listeningToQuery',
|
||||
link: 'listeningToQuery',
|
||||
hasFusion: true,
|
||||
template: '',
|
||||
registerName: 'com.alibaba.nacos.page.listeningToQuery',
|
||||
useRouter: false,
|
||||
id: 'listeningToQuery',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
enable: false,
|
||||
isExtend: true,
|
||||
name: '服务管理',
|
||||
title: '服务管理',
|
||||
isVirtual: true,
|
||||
projectName: 'nacos',
|
||||
serviceName: 'serviceManagementVirtual',
|
||||
link: 'serviceManagementVirtual',
|
||||
hasFusion: true,
|
||||
template: '',
|
||||
registerName: 'com.alibaba.nacos.page.serviceManagementVirtual',
|
||||
useRouter: false,
|
||||
id: 'com.alibaba.nacos.page.serviceManagementVirtual',
|
||||
children: [
|
||||
{
|
||||
isExtend: false,
|
||||
name: '服务列表',
|
||||
title: '服务列表',
|
||||
isVirtual: false,
|
||||
projectName: 'nacos',
|
||||
serviceName: 'serviceManagement',
|
||||
link: 'serviceManagement',
|
||||
hasFusion: true,
|
||||
template: '',
|
||||
registerName: 'com.alibaba.nacos.page.serviceManagement',
|
||||
useRouter: false,
|
||||
id: 'serviceManagement',
|
||||
children: [
|
||||
{
|
||||
isExtend: true,
|
||||
name: '服务详情',
|
||||
title: '服务详情',
|
||||
isVirtual: true,
|
||||
projectName: 'nacos',
|
||||
serviceName: 'serviceDetail',
|
||||
link: 'serviceDetail',
|
||||
hasFusion: true,
|
||||
template: '',
|
||||
registerName: 'com.alibaba.nacos.page.ServiceDetail',
|
||||
useRouter: false,
|
||||
id: 'serviceDetail',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
isExtend: false,
|
||||
name: '订阅者列表',
|
||||
title: '订阅者列表',
|
||||
isVirtual: false,
|
||||
projectName: 'nacos',
|
||||
serviceName: 'subscriberList',
|
||||
link: 'subscriberList',
|
||||
hasFusion: true,
|
||||
template: '',
|
||||
registerName: 'com.alibaba.nacos.page.subscriberList',
|
||||
useRouter: false,
|
||||
id: 'subscriberList',
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
enable: true,
|
||||
isExtend: false,
|
||||
name: '命名空间',
|
||||
title: '命名空间',
|
||||
isVirtual: false,
|
||||
projectName: 'nacos',
|
||||
serviceName: 'namespace',
|
||||
link: 'namespace',
|
||||
hasFusion: true,
|
||||
template: '',
|
||||
dontUseChild: false,
|
||||
registerName: 'com.alibaba.nacos.page.namespace',
|
||||
useRouter: false,
|
||||
id: 'namespace',
|
||||
},
|
||||
{
|
||||
enable: true,
|
||||
isExtend: false,
|
||||
name: '修改密码',
|
||||
title: '修改密码',
|
||||
isVirtual: false,
|
||||
projectName: 'nacos',
|
||||
serviceName: 'password',
|
||||
link: 'password',
|
||||
hasFusion: true,
|
||||
template: '',
|
||||
dontUseChild: true,
|
||||
registerName: 'com.alibaba.nacos.page.password',
|
||||
useRouter: false,
|
||||
id: 'password',
|
||||
},
|
||||
{
|
||||
enable: false,
|
||||
isExtend: true,
|
||||
name: '集群管理',
|
||||
title: '集群管理',
|
||||
isVirtual: true,
|
||||
projectName: 'nacos',
|
||||
serviceName: 'clusterManagementVirtual',
|
||||
link: 'clusterManagementVirtual',
|
||||
hasFusion: true,
|
||||
template: '',
|
||||
registerName: 'com.alibaba.nacos.page.clusterManagementVirtual',
|
||||
useRouter: false,
|
||||
id: 'com.alibaba.nacos.page.clusterManagementVirtual',
|
||||
children: [
|
||||
{
|
||||
isExtend: false,
|
||||
name: '节点状态',
|
||||
title: '节点状态',
|
||||
isVirtual: false,
|
||||
projectName: 'nacos',
|
||||
serviceName: 'clusterManagement',
|
||||
link: 'clusterManagement',
|
||||
hasFusion: true,
|
||||
template: '',
|
||||
registerName: 'com.alibaba.nacos.page.clusterManagement',
|
||||
useRouter: false,
|
||||
id: 'clusterManagement',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
defaultKey: 'configurationManagement',
|
||||
projectName: 'nacos',
|
||||
};
|
@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Field, Form, Input, Select, Dialog, ConfigProvider } from '@alifd/next';
|
||||
import { connect } from 'react-redux';
|
||||
import { getNamespaces } from '../../../reducers/namespace';
|
||||
|
||||
const FormItem = Form.Item;
|
||||
const { Option } = Select;
|
||||
|
||||
const formItemLayout = {
|
||||
labelCol: { fixedSpan: 4 },
|
||||
wrapperCol: { span: 19 },
|
||||
};
|
||||
|
||||
@connect(state => ({ namespaces: state.namespace.namespaces }), { getNamespaces })
|
||||
@ConfigProvider.config
|
||||
class NewPermissions extends React.Component {
|
||||
static displayName = 'NewPermissions';
|
||||
|
||||
field = new Field(this);
|
||||
|
||||
static propTypes = {
|
||||
locale: PropTypes.object,
|
||||
visible: PropTypes.bool,
|
||||
getNamespaces: PropTypes.func,
|
||||
onOk: PropTypes.func,
|
||||
onCancel: PropTypes.func,
|
||||
namespaces: PropTypes.array,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.props.getNamespaces();
|
||||
}
|
||||
|
||||
check() {
|
||||
const { locale } = this.props;
|
||||
const errors = {
|
||||
role: locale.roleError,
|
||||
resource: locale.resourceError,
|
||||
action: locale.actionError,
|
||||
};
|
||||
const vals = Object.keys(errors).map(key => {
|
||||
const val = this.field.getValue(key);
|
||||
if (!val) {
|
||||
this.field.setError(key, errors[key]);
|
||||
}
|
||||
return val;
|
||||
});
|
||||
if (vals.filter(v => v).length === 3) {
|
||||
return vals;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { getError } = this.field;
|
||||
const { visible, onOk, onCancel, locale, namespaces } = this.props;
|
||||
return (
|
||||
<>
|
||||
<Dialog
|
||||
title={locale.addPermission}
|
||||
visible={visible}
|
||||
onOk={() => {
|
||||
const vals = this.check();
|
||||
if (vals) {
|
||||
onOk(vals).then(() => onCancel());
|
||||
}
|
||||
}}
|
||||
onClose={onCancel}
|
||||
onCancel={onCancel}
|
||||
afterClose={() => this.field.reset()}
|
||||
>
|
||||
<Form style={{ width: 400 }} {...formItemLayout} field={this.field}>
|
||||
<FormItem label={locale.role} required help={getError('role')}>
|
||||
<Input name="role" trim placeholder={locale.rolePlaceholder} />
|
||||
</FormItem>
|
||||
<FormItem label={locale.resource} required help={getError('resource')}>
|
||||
<Select
|
||||
name="resource"
|
||||
placeholder={locale.resourcePlaceholder}
|
||||
style={{ width: '100%' }}
|
||||
>
|
||||
{namespaces.map(({ namespace, namespaceShowName }) => (
|
||||
<Option value={`${namespace}:*:*`}>
|
||||
{namespaceShowName} {namespace ? `(${namespace})` : ''}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem label={locale.action} required help={getError('action')}>
|
||||
<Select
|
||||
name="action"
|
||||
placeholder={locale.actionPlaceholder}
|
||||
style={{ width: '100%' }}
|
||||
>
|
||||
<Option value="r">{locale.readOnly}(r)</Option>
|
||||
<Option value="w">{locale.writeOnly}(w)</Option>
|
||||
<Option value="rw">{locale.readWrite}(rw)</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default NewPermissions;
|
@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Button, Dialog, Pagination, Table, ConfigProvider } from '@alifd/next';
|
||||
import { connect } from 'react-redux';
|
||||
import { getPermissions, createPermission, deletePermission } from '../../../reducers/authority';
|
||||
import { getNamespaces } from '../../../reducers/namespace';
|
||||
import RegionGroup from '../../../components/RegionGroup';
|
||||
import NewPermissions from './NewPermissions';
|
||||
|
||||
import './PermissionsManagement.scss';
|
||||
|
||||
@connect(
|
||||
state => ({
|
||||
permissions: state.authority.permissions,
|
||||
namespaces: state.namespace.namespaces,
|
||||
}),
|
||||
{ getPermissions, getNamespaces }
|
||||
)
|
||||
@ConfigProvider.config
|
||||
class PermissionsManagement extends React.Component {
|
||||
static displayName = 'PermissionsManagement';
|
||||
|
||||
static propTypes = {
|
||||
locale: PropTypes.object,
|
||||
permissions: PropTypes.object,
|
||||
namespaces: PropTypes.object,
|
||||
getPermissions: PropTypes.func,
|
||||
getNamespaces: PropTypes.func,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
loading: true,
|
||||
pageNo: 1,
|
||||
pageSize: 9,
|
||||
createPermission: false,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.getPermissions();
|
||||
this.props.getNamespaces();
|
||||
}
|
||||
|
||||
getPermissions() {
|
||||
const { pageNo, pageSize } = this.state;
|
||||
this.props
|
||||
.getPermissions({ pageNo, pageSize })
|
||||
.then(() => {
|
||||
if (this.state.loading) {
|
||||
this.setState({ loading: false });
|
||||
}
|
||||
})
|
||||
.catch(() => this.setState({ loading: false }));
|
||||
}
|
||||
|
||||
colseCreatePermission() {
|
||||
this.setState({ createPermissionVisible: false });
|
||||
}
|
||||
|
||||
getActionText(action) {
|
||||
const { locale } = this.props;
|
||||
return {
|
||||
r: `${locale.readOnly} (r)`,
|
||||
w: `${locale.writeOnly} (w)`,
|
||||
rw: `${locale.readWrite} (rw)`,
|
||||
}[action];
|
||||
}
|
||||
|
||||
render() {
|
||||
const { permissions, namespaces = [], locale } = this.props;
|
||||
const { loading, pageSize, pageNo, createPermissionVisible } = this.state;
|
||||
return (
|
||||
<>
|
||||
<RegionGroup left={locale.privilegeManagement} />
|
||||
<div className="filter-panel">
|
||||
<Button type="primary" onClick={() => this.setState({ createPermissionVisible: true })}>
|
||||
{locale.addPermission}
|
||||
</Button>
|
||||
</div>
|
||||
<Table dataSource={permissions.pageItems} loading={loading} maxBodyHeight={476} fixedHeader>
|
||||
<Table.Column title={locale.role} dataIndex="role" />
|
||||
<Table.Column
|
||||
title={locale.resource}
|
||||
dataIndex="resource"
|
||||
cell={value => {
|
||||
const [item = {}] = namespaces.filter(({ namespace }) => {
|
||||
const [itemNamespace] = value.split(':');
|
||||
return itemNamespace === namespace;
|
||||
});
|
||||
const { namespaceShowName = '', namespace = '' } = item;
|
||||
return namespaceShowName + (namespace ? ` (${namespace})` : '');
|
||||
}}
|
||||
/>
|
||||
<Table.Column
|
||||
title={locale.action}
|
||||
dataIndex="action"
|
||||
cell={action => this.getActionText(action)}
|
||||
/>
|
||||
<Table.Column
|
||||
title={locale.operation}
|
||||
cell={(value, index, record) => (
|
||||
<>
|
||||
<Button
|
||||
type="primary"
|
||||
warning
|
||||
onClick={() =>
|
||||
Dialog.confirm({
|
||||
title: locale.deletePermission,
|
||||
content: locale.deletePermissionTip,
|
||||
onOk: () =>
|
||||
deletePermission(record).then(() => {
|
||||
this.setState({ pageNo: 1 }, () => this.getPermissions());
|
||||
}),
|
||||
})
|
||||
}
|
||||
>
|
||||
{locale.deletePermission}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</Table>
|
||||
{permissions.totalCount > pageSize && (
|
||||
<Pagination
|
||||
className="users-pagination"
|
||||
current={pageNo}
|
||||
total={permissions.totalCount}
|
||||
pageSize={pageSize}
|
||||
onChange={pageNo => this.setState({ pageNo }, () => this.getPermissions())}
|
||||
/>
|
||||
)}
|
||||
<NewPermissions
|
||||
visible={createPermissionVisible}
|
||||
onOk={permission =>
|
||||
createPermission(permission).then(res => {
|
||||
this.setState({ pageNo: 1 }, () => this.getPermissions());
|
||||
return res;
|
||||
})
|
||||
}
|
||||
onCancel={() => this.colseCreatePermission()}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default PermissionsManagement;
|
@ -9,4 +9,4 @@
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
*/
|
@ -11,14 +11,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
set(key, value) {
|
||||
window.localStorage.setItem(key, value);
|
||||
},
|
||||
get(key) {
|
||||
return window.localStorage.getItem(key);
|
||||
},
|
||||
remove(key) {
|
||||
window.localStorage.removeItem(key);
|
||||
},
|
||||
};
|
||||
import PermissionsManagement from './PermissionsManagement';
|
||||
|
||||
export default PermissionsManagement;
|
@ -0,0 +1,7 @@
|
||||
# 权限控制
|
||||
|
||||
> AuthorityControl
|
||||
|
||||
1. UserManagement => 用户管理
|
||||
2. RolesManagement => 角色管理
|
||||
3. PermissionsManagement => 权限管理
|
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Field, Form, Input, Dialog, ConfigProvider } from '@alifd/next';
|
||||
|
||||
const FormItem = Form.Item;
|
||||
|
||||
const formItemLayout = {
|
||||
labelCol: { fixedSpan: 4 },
|
||||
wrapperCol: { span: 19 },
|
||||
};
|
||||
|
||||
@ConfigProvider.config
|
||||
class NewRole extends React.Component {
|
||||
static displayName = 'NewRole';
|
||||
|
||||
field = new Field(this);
|
||||
|
||||
static propTypes = {
|
||||
locale: PropTypes.object,
|
||||
visible: PropTypes.bool,
|
||||
onOk: PropTypes.func,
|
||||
onCancel: PropTypes.func,
|
||||
};
|
||||
|
||||
check() {
|
||||
const { locale } = this.props;
|
||||
const errors = {
|
||||
role: locale.roleError,
|
||||
username: locale.usernameError,
|
||||
};
|
||||
const vals = Object.keys(errors).map(key => {
|
||||
const val = this.field.getValue(key);
|
||||
if (!val) {
|
||||
this.field.setError(key, errors[key]);
|
||||
}
|
||||
return val;
|
||||
});
|
||||
if (vals.filter(v => v).length === 2) {
|
||||
return vals;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { locale } = this.props;
|
||||
const { getError } = this.field;
|
||||
const { visible, onOk, onCancel } = this.props;
|
||||
return (
|
||||
<>
|
||||
<Dialog
|
||||
title={locale.bindingRoles}
|
||||
visible={visible}
|
||||
onOk={() => {
|
||||
const vals = this.check();
|
||||
if (vals) {
|
||||
onOk(vals).then(() => onCancel());
|
||||
}
|
||||
}}
|
||||
onClose={onCancel}
|
||||
onCancel={onCancel}
|
||||
afterClose={() => this.field.reset()}
|
||||
>
|
||||
<Form style={{ width: 400 }} {...formItemLayout} field={this.field}>
|
||||
<FormItem label={locale.role} required help={getError('role')}>
|
||||
<Input name="role" trim placeholder={locale.rolePlaceholder} />
|
||||
</FormItem>
|
||||
<FormItem label={locale.username} required help={getError('username')}>
|
||||
<Input name="username" placeholder={locale.usernamePlaceholder} />
|
||||
</FormItem>
|
||||
</Form>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default NewRole;
|
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Button, Dialog, Pagination, Table, ConfigProvider } from '@alifd/next';
|
||||
import { connect } from 'react-redux';
|
||||
import { getRoles, createRole, deleteRole } from '../../../reducers/authority';
|
||||
import RegionGroup from '../../../components/RegionGroup';
|
||||
import NewRole from './NewRole';
|
||||
|
||||
import './RolesManagement.scss';
|
||||
|
||||
@connect(state => ({ roles: state.authority.roles }), { getRoles })
|
||||
@ConfigProvider.config
|
||||
class RolesManagement extends React.Component {
|
||||
static displayName = 'RolesManagement';
|
||||
|
||||
static propTypes = {
|
||||
locale: PropTypes.object,
|
||||
roles: PropTypes.object,
|
||||
getRoles: PropTypes.func,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
loading: true,
|
||||
pageNo: 1,
|
||||
pageSize: 9,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.getRoles();
|
||||
}
|
||||
|
||||
getRoles() {
|
||||
const { pageNo, pageSize } = this.state;
|
||||
this.props
|
||||
.getRoles({ pageNo, pageSize })
|
||||
.then(() => {
|
||||
if (this.state.loading) {
|
||||
this.setState({ loading: false });
|
||||
}
|
||||
})
|
||||
.catch(() => this.setState({ loading: false }));
|
||||
}
|
||||
|
||||
colseCreateRole() {
|
||||
this.setState({ createRoleVisible: false });
|
||||
}
|
||||
|
||||
render() {
|
||||
const { roles, locale } = this.props;
|
||||
const { loading, pageSize, pageNo, createRoleVisible, passwordResetUser } = this.state;
|
||||
return (
|
||||
<>
|
||||
<RegionGroup left={locale.roleManagement} />
|
||||
<div className="filter-panel">
|
||||
<Button type="primary" onClick={() => this.setState({ createRoleVisible: true })}>
|
||||
{locale.bindingRoles}
|
||||
</Button>
|
||||
</div>
|
||||
<Table dataSource={roles.pageItems} loading={loading} maxBodyHeight={476} fixedHeader>
|
||||
<Table.Column title={locale.role} dataIndex="role" />
|
||||
<Table.Column title={locale.username} dataIndex="username" />
|
||||
<Table.Column
|
||||
title={locale.operation}
|
||||
dataIndex="role"
|
||||
cell={(value, index, record) => {
|
||||
if (value === 'ROLE_ADMIN') {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Button
|
||||
type="primary"
|
||||
warning
|
||||
onClick={() =>
|
||||
Dialog.confirm({
|
||||
title: locale.deleteRole,
|
||||
content: locale.deleteRoleTip,
|
||||
onOk: () =>
|
||||
deleteRole(record).then(() => {
|
||||
this.setState({ pageNo: 1 }, () => this.getRoles());
|
||||
}),
|
||||
})
|
||||
}
|
||||
>
|
||||
{locale.deleteRole}
|
||||
</Button>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Table>
|
||||
{roles.totalCount > pageSize && (
|
||||
<Pagination
|
||||
className="users-pagination"
|
||||
current={pageNo}
|
||||
total={roles.totalCount}
|
||||
pageSize={pageSize}
|
||||
onChange={pageNo => this.setState({ pageNo }, () => this.getRoles())}
|
||||
/>
|
||||
)}
|
||||
<NewRole
|
||||
visible={createRoleVisible}
|
||||
onOk={role =>
|
||||
createRole(role).then(res => {
|
||||
this.getRoles();
|
||||
return res;
|
||||
})
|
||||
}
|
||||
onCancel={() => this.colseCreateRole()}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default RolesManagement;
|
@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import RolesManagement from './RolesManagement';
|
||||
|
||||
export default RolesManagement;
|
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Field, Form, Input, Dialog, ConfigProvider } from '@alifd/next';
|
||||
import './UserManagement.scss';
|
||||
|
||||
const FormItem = Form.Item;
|
||||
|
||||
const formItemLayout = {
|
||||
labelCol: { fixedSpan: 4 },
|
||||
wrapperCol: { span: 19 },
|
||||
};
|
||||
|
||||
@ConfigProvider.config
|
||||
class NewUser extends React.Component {
|
||||
static displayName = 'NewUser';
|
||||
|
||||
field = new Field(this);
|
||||
|
||||
static propTypes = {
|
||||
locale: PropTypes.object,
|
||||
visible: PropTypes.bool,
|
||||
onOk: PropTypes.func,
|
||||
onCancel: PropTypes.func,
|
||||
};
|
||||
|
||||
check() {
|
||||
const { locale } = this.props;
|
||||
const errors = {
|
||||
username: locale.usernameError,
|
||||
password: locale.passwordError,
|
||||
rePassword: locale.rePasswordError,
|
||||
};
|
||||
const vals = Object.keys(errors).map(key => {
|
||||
const val = this.field.getValue(key);
|
||||
if (!val) {
|
||||
this.field.setError(key, errors[key]);
|
||||
}
|
||||
return val;
|
||||
});
|
||||
if (vals.filter(v => v).length !== 3) {
|
||||
return null;
|
||||
}
|
||||
const [password, rePassword] = ['password', 'rePassword'].map(k => this.field.getValue(k));
|
||||
if (password !== rePassword) {
|
||||
this.field.setError('rePassword', locale.rePasswordError2);
|
||||
return null;
|
||||
}
|
||||
return vals;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { locale } = this.props;
|
||||
const { getError } = this.field;
|
||||
const { visible, onOk, onCancel } = this.props;
|
||||
return (
|
||||
<>
|
||||
<Dialog
|
||||
title={locale.createUser}
|
||||
visible={visible}
|
||||
onOk={() => {
|
||||
const vals = this.check();
|
||||
if (vals) {
|
||||
onOk(vals).then(() => onCancel());
|
||||
}
|
||||
}}
|
||||
onClose={onCancel}
|
||||
onCancel={onCancel}
|
||||
afterClose={() => this.field.reset()}
|
||||
>
|
||||
<Form style={{ width: 400 }} {...formItemLayout} field={this.field}>
|
||||
<FormItem label={locale.username} required help={getError('username')}>
|
||||
<Input name="username" trim placeholder={locale.usernamePlaceholder} />
|
||||
</FormItem>
|
||||
<FormItem label={locale.password} required help={getError('password')}>
|
||||
<Input name="password" htmlType="password" placeholder={locale.passwordPlaceholder} />
|
||||
</FormItem>
|
||||
<FormItem label={locale.rePassword} required help={getError('rePassword')}>
|
||||
<Input
|
||||
name="rePassword"
|
||||
htmlType="password"
|
||||
placeholder={locale.rePasswordPlaceholder}
|
||||
/>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default NewUser;
|
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Field, Form, Input, Dialog, ConfigProvider } from '@alifd/next';
|
||||
import './UserManagement.scss';
|
||||
|
||||
const FormItem = Form.Item;
|
||||
|
||||
const formItemLayout = {
|
||||
labelCol: { fixedSpan: 4 },
|
||||
wrapperCol: { span: 19 },
|
||||
};
|
||||
|
||||
@ConfigProvider.config
|
||||
class PasswordReset extends React.Component {
|
||||
static displayName = 'PasswordReset';
|
||||
|
||||
field = new Field(this);
|
||||
|
||||
static propTypes = {
|
||||
locale: PropTypes.object,
|
||||
visible: PropTypes.bool,
|
||||
username: PropTypes.string,
|
||||
onCancel: PropTypes.func,
|
||||
onOk: PropTypes.func,
|
||||
};
|
||||
|
||||
check() {
|
||||
const { locale } = this.props;
|
||||
const errors = {
|
||||
password: locale.passwordError,
|
||||
rePassword: locale.rePasswordError,
|
||||
};
|
||||
const vals = Object.keys(errors).map(key => {
|
||||
const val = this.field.getValue(key);
|
||||
if (!val) {
|
||||
this.field.setError(key, errors[key]);
|
||||
}
|
||||
return val;
|
||||
});
|
||||
if (vals.filter(v => v).length !== 2) {
|
||||
return null;
|
||||
}
|
||||
const [password, rePassword] = ['password', 'rePassword'].map(k => this.field.getValue(k));
|
||||
if (password !== rePassword) {
|
||||
this.field.setError('rePassword', locale.rePasswordError2);
|
||||
return null;
|
||||
}
|
||||
return [this.props.username, ...vals];
|
||||
}
|
||||
|
||||
render() {
|
||||
const { locale } = this.props;
|
||||
const { getError } = this.field;
|
||||
const { username, onOk, onCancel } = this.props;
|
||||
return (
|
||||
<>
|
||||
<Dialog
|
||||
title={locale.resetPassword}
|
||||
visible={username}
|
||||
onOk={() => {
|
||||
const vals = this.check();
|
||||
if (vals) {
|
||||
onOk(vals).then(() => onCancel());
|
||||
}
|
||||
}}
|
||||
onClose={onCancel}
|
||||
onCancel={onCancel}
|
||||
afterClose={() => this.field.reset()}
|
||||
>
|
||||
<Form style={{ width: 400 }} {...formItemLayout} field={this.field}>
|
||||
<FormItem label={locale.username} required>
|
||||
<p>{username}</p>
|
||||
</FormItem>
|
||||
<FormItem label={locale.password} required help={getError('password')}>
|
||||
<Input name="password" htmlType="password" placeholder={locale.passwordPlaceholder} />
|
||||
</FormItem>
|
||||
<FormItem label={locale.rePassword} required help={getError('rePassword')}>
|
||||
<Input
|
||||
name="rePassword"
|
||||
htmlType="password"
|
||||
placeholder={locale.rePasswordPlaceholder}
|
||||
/>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default PasswordReset;
|
@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Button, Dialog, Pagination, Table, ConfigProvider } from '@alifd/next';
|
||||
import { connect } from 'react-redux';
|
||||
import { getUsers, createUser, deleteUser, passwordReset } from '../../../reducers/authority';
|
||||
import RegionGroup from '../../../components/RegionGroup';
|
||||
import NewUser from './NewUser';
|
||||
import PasswordReset from './PasswordReset';
|
||||
|
||||
import './UserManagement.scss';
|
||||
|
||||
@connect(state => ({ users: state.authority.users }), { getUsers })
|
||||
@ConfigProvider.config
|
||||
class UserManagement extends React.Component {
|
||||
static displayName = 'UserManagement';
|
||||
|
||||
static propTypes = {
|
||||
locale: PropTypes.object,
|
||||
users: PropTypes.object,
|
||||
getUsers: PropTypes.func,
|
||||
createUser: PropTypes.func,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
loading: true,
|
||||
pageNo: 1,
|
||||
pageSize: 9,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.getUsers();
|
||||
}
|
||||
|
||||
getUsers() {
|
||||
const { pageNo, pageSize } = this.state;
|
||||
this.props
|
||||
.getUsers({ pageNo, pageSize })
|
||||
.then(() => {
|
||||
if (this.state.loading) {
|
||||
this.setState({ loading: false });
|
||||
}
|
||||
})
|
||||
.catch(() => this.setState({ loading: false }));
|
||||
}
|
||||
|
||||
colseCreateUser() {
|
||||
this.setState({ createUserVisible: false });
|
||||
}
|
||||
|
||||
render() {
|
||||
const { users, locale } = this.props;
|
||||
const { loading, pageSize, pageNo, createUserVisible, passwordResetUser } = this.state;
|
||||
return (
|
||||
<>
|
||||
<RegionGroup left={locale.userManagement} />
|
||||
<div className="filter-panel">
|
||||
<Button type="primary" onClick={() => this.setState({ createUserVisible: true })}>
|
||||
{locale.createUser}
|
||||
</Button>
|
||||
</div>
|
||||
<Table dataSource={users.pageItems} loading={loading} maxBodyHeight={476} fixedHeader>
|
||||
<Table.Column title={locale.username} dataIndex="username" />
|
||||
<Table.Column
|
||||
title={locale.password}
|
||||
dataIndex="password"
|
||||
cell={value => value.replace(/\S/g, '*')}
|
||||
/>
|
||||
<Table.Column
|
||||
title={locale.operation}
|
||||
dataIndex="username"
|
||||
cell={username => (
|
||||
<>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => this.setState({ passwordResetUser: username })}
|
||||
>
|
||||
{locale.resetPassword}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
type="primary"
|
||||
warning
|
||||
onClick={() =>
|
||||
Dialog.confirm({
|
||||
title: locale.deleteUser,
|
||||
content: locale.deleteUserTip,
|
||||
onOk: () =>
|
||||
deleteUser(username).then(() => {
|
||||
this.setState({ pageNo: 1 }, () => this.getUsers());
|
||||
}),
|
||||
})
|
||||
}
|
||||
>
|
||||
{locale.deleteUser}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</Table>
|
||||
{users.totalCount > pageSize && (
|
||||
<Pagination
|
||||
className="users-pagination"
|
||||
current={pageNo}
|
||||
total={users.totalCount}
|
||||
pageSize={pageSize}
|
||||
onChange={pageNo => this.setState({ pageNo }, () => this.getUsers())}
|
||||
/>
|
||||
)}
|
||||
<NewUser
|
||||
visible={createUserVisible}
|
||||
onOk={user =>
|
||||
createUser(user).then(res => {
|
||||
this.setState({ pageNo: 1 }, () => this.getUsers());
|
||||
return res;
|
||||
})
|
||||
}
|
||||
onCancel={() => this.colseCreateUser()}
|
||||
/>
|
||||
<PasswordReset
|
||||
username={passwordResetUser}
|
||||
onOk={user =>
|
||||
passwordReset(user).then(res => {
|
||||
this.getUsers();
|
||||
return res;
|
||||
})
|
||||
}
|
||||
onCancel={() => this.setState({ passwordResetUser: undefined })}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default UserManagement;
|
@ -10,15 +10,9 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
@import '../authority.scss';
|
||||
|
||||
import $ from 'jquery';
|
||||
|
||||
export default function ajaxrequest(options) {
|
||||
const promise = $.ajax({
|
||||
url: options.url,
|
||||
timeout: options.timeout, // 超时时间设置,单位毫秒设置为1小时
|
||||
dataType: options.dataType, // 返回的数据格式
|
||||
type: options.type,
|
||||
});
|
||||
return promise.done(data => ({ data }));
|
||||
.users-pagination {
|
||||
float: right;
|
||||
margin-top: 20px;
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import UserManagement from './UserManagement';
|
||||
|
||||
export default UserManagement;
|
@ -11,6 +11,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import Password from './Password';
|
||||
|
||||
export default Password;
|
||||
.filter-panel {
|
||||
text-align: right;
|
||||
padding: 10px 0;
|
||||
}
|
@ -14,6 +14,7 @@
|
||||
import React from 'react';
|
||||
import { Button, ConfigProvider, Dialog, Field, Form, Input, Loading, Tab } from '@alifd/next';
|
||||
import { getParams, request } from '../../../globalLib';
|
||||
import { generateUrl } from '../../../utils/nacosutil';
|
||||
|
||||
import './index.scss';
|
||||
import PropTypes from 'prop-types';
|
||||
@ -140,9 +141,12 @@ class ConfigDetail extends React.Component {
|
||||
|
||||
goList() {
|
||||
this.props.history.push(
|
||||
`/configurationManagement?serverId=${this.serverId}&group=${this.searchGroup}&dataId=${
|
||||
this.searchDataId
|
||||
}&namespace=${this.tenant}`
|
||||
generateUrl('/configurationManagement', {
|
||||
serverId: this.serverId,
|
||||
group: this.searchGroup,
|
||||
dataId: this.searchDataId,
|
||||
namespace: this.tenant,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { getParams } from '../../../globalLib';
|
||||
import { generateUrl } from '../../../utils/nacosutil';
|
||||
import request from '../../../utils/request';
|
||||
import validateContent from 'utils/validateContent';
|
||||
import SuccessDialog from '../../../components/SuccessDialog';
|
||||
@ -96,7 +97,7 @@ class ConfigEditor extends React.Component {
|
||||
dataId: getParams('dataId').trim(),
|
||||
group,
|
||||
},
|
||||
() =>
|
||||
() => {
|
||||
this.getConfig(true).then(res => {
|
||||
if (!res) {
|
||||
this.getConfig();
|
||||
@ -107,7 +108,8 @@ class ConfigEditor extends React.Component {
|
||||
tabActiveKey: 'beta',
|
||||
betaPublishSuccess: true,
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
);
|
||||
} else {
|
||||
if (group) {
|
||||
@ -173,7 +175,6 @@ class ConfigEditor extends React.Component {
|
||||
}
|
||||
|
||||
clickTab(tabActiveKey) {
|
||||
console.log('tabActiveKey', tabActiveKey, tabActiveKey === 'beta');
|
||||
this.setState({ tabActiveKey }, () => this.getConfig(tabActiveKey === 'beta'));
|
||||
}
|
||||
|
||||
@ -215,26 +216,20 @@ class ConfigEditor extends React.Component {
|
||||
}
|
||||
|
||||
_publishConfig(beta = false) {
|
||||
const { locale } = this.props;
|
||||
const { betaIps, isNewConfig } = this.state;
|
||||
const headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
|
||||
if (beta) {
|
||||
headers.betaIps = betaIps;
|
||||
}
|
||||
const data = { ...this.state.form, content: this.getCodeVal() };
|
||||
const form = { ...this.state.form, content: this.getCodeVal() };
|
||||
const data = new FormData();
|
||||
Object.keys(form).forEach(key => {
|
||||
data.append(key, form[key]);
|
||||
});
|
||||
return request({
|
||||
url: 'v1/cs/configs',
|
||||
method: 'post',
|
||||
data,
|
||||
transformRequest: [
|
||||
function(data) {
|
||||
let ret = '';
|
||||
for (let it in data) {
|
||||
ret += encodeURIComponent(it) + '=' + encodeURIComponent(data[it]) + '&';
|
||||
}
|
||||
return ret;
|
||||
},
|
||||
],
|
||||
headers,
|
||||
}).then(res => {
|
||||
if (res) {
|
||||
@ -315,11 +310,11 @@ class ConfigEditor extends React.Component {
|
||||
|
||||
goBack() {
|
||||
const serverId = getParams('serverId') || '';
|
||||
const tenant = getParams('namespace');
|
||||
const searchGroup = getParams('searchGroup') || '';
|
||||
const searchDataId = getParams('searchDataId') || '';
|
||||
const namespace = getParams('namespace');
|
||||
const group = getParams('searchGroup') || '';
|
||||
const dataId = getParams('searchDataId') || '';
|
||||
this.props.history.push(
|
||||
`/configurationManagement?serverId=${serverId}&group=${searchGroup}&dataId=${searchDataId}&namespace=${tenant}`
|
||||
generateUrl('/configurationManagement', { serverId, group, dataId, namespace })
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { getParams, request } from '../../../globalLib';
|
||||
import { generateUrl } from '../../../utils/nacosutil';
|
||||
import { Button, ConfigProvider, Dialog, Field, Form, Input } from '@alifd/next';
|
||||
|
||||
import './index.scss';
|
||||
@ -96,11 +97,10 @@ class ConfigRollback extends React.Component {
|
||||
}
|
||||
|
||||
goList() {
|
||||
const tenant = getParams('namespace');
|
||||
const namespace = getParams('namespace');
|
||||
const { serverId, dataId, group } = this;
|
||||
this.props.history.push(
|
||||
`/historyRollback?serverId=${this.serverId}&group=${this.group}&dataId=${
|
||||
this.dataId
|
||||
}&namespace=${tenant}`
|
||||
generateUrl('/historyRollback', { serverId, dataId, group, namespace })
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,7 @@ import PropTypes from 'prop-types';
|
||||
import { Button, Checkbox, ConfigProvider, Dialog, Field, Form, Input, Loading } from '@alifd/next';
|
||||
import SuccessDialog from '../../../components/SuccessDialog';
|
||||
import { getParams, request } from '../../../globalLib';
|
||||
import { generateUrl } from '../../../utils/nacosutil';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
@ -91,13 +92,9 @@ class ConfigSync extends React.Component {
|
||||
const { locale = {} } = this.props;
|
||||
this.tenant = getParams('namespace') || '';
|
||||
this.serverId = getParams('serverId') || 'center';
|
||||
let url = `/diamond-ops/configList/detail/serverId/${this.serverId}/dataId/${
|
||||
this.dataId
|
||||
}/group/${this.group}/tenant/${this.tenant}?id=`;
|
||||
let url = `/diamond-ops/configList/detail/serverId/${this.serverId}/dataId/${this.dataId}/group/${this.group}/tenant/${this.tenant}?id=`;
|
||||
if (this.tenant === 'global' || !this.tenant) {
|
||||
url = `/diamond-ops/configList/detail/serverId/${this.serverId}/dataId/${this.dataId}/group/${
|
||||
this.group
|
||||
}?id=`;
|
||||
url = `/diamond-ops/configList/detail/serverId/${this.serverId}/dataId/${this.dataId}/group/${this.group}?id=`;
|
||||
}
|
||||
request({
|
||||
url,
|
||||
@ -168,9 +165,7 @@ class ConfigSync extends React.Component {
|
||||
request({
|
||||
type: 'put',
|
||||
contentType: 'application/json',
|
||||
url: `/diamond-ops/configList/serverId/${this.serverId}/dataId/${payload.dataId}/group/${
|
||||
payload.group
|
||||
}?id=`,
|
||||
url: `/diamond-ops/configList/serverId/${this.serverId}/dataId/${payload.dataId}/group/${payload.group}?id=`,
|
||||
data: JSON.stringify(payload),
|
||||
success(res) {
|
||||
const _payload = {};
|
||||
@ -193,7 +188,7 @@ class ConfigSync extends React.Component {
|
||||
const dataId = this.field.getValue('dataId');
|
||||
const gruop = this.field.getValue('group');
|
||||
this.props.history.push(
|
||||
`/diamond-ops/static/pages/config-sync/index.html?serverId=center&dataId=${dataId}&group=${gruop}`
|
||||
generateUrl('/diamond-ops/static/pages/config-sync/index.html', { dataId, gruop })
|
||||
);
|
||||
}
|
||||
|
||||
@ -209,9 +204,8 @@ class ConfigSync extends React.Component {
|
||||
}
|
||||
|
||||
goResult() {
|
||||
this.props.history.push(
|
||||
`/consistencyEfficacy?serverId=${this.serverId}&dataId=${this.dataId}&group=${this.group}`
|
||||
);
|
||||
const { serverId, dataId, group } = this;
|
||||
this.props.history.push(generateUrl('/consistencyEfficacy', { serverId, dataId, group }));
|
||||
}
|
||||
|
||||
openLoading() {
|
||||
|
@ -39,15 +39,20 @@ import ShowCodeing from 'components/ShowCodeing';
|
||||
import DeleteDialog from 'components/DeleteDialog';
|
||||
import DashboardCard from './DashboardCard';
|
||||
import { getParams, setParams, request, aliwareIntl } from '@/globalLib';
|
||||
import axios from 'axios';
|
||||
import { connect } from 'react-redux';
|
||||
import { getConfigs } from '../../../reducers/configuration';
|
||||
|
||||
import './index.scss';
|
||||
import { LANGUAGE_KEY } from '../../../constants';
|
||||
|
||||
const { Panel } = Collapse;
|
||||
const { Row, Col } = Grid;
|
||||
const configsTableSelected = new Map();
|
||||
|
||||
@connect(
|
||||
state => ({
|
||||
configurations: state.configuration.configurations,
|
||||
}),
|
||||
{ getConfigs }
|
||||
)
|
||||
@ConfigProvider.config
|
||||
class ConfigurationManagement extends React.Component {
|
||||
static displayName = 'ConfigurationManagement';
|
||||
@ -133,7 +138,8 @@ class ConfigurationManagement extends React.Component {
|
||||
<div>
|
||||
<div style={{ fontSize: '15px', lineHeight: '22px' }}>
|
||||
{locale.ad}
|
||||
<a href={'https://survey.aliyun.com/survey/k0BjJ2ARC'} target={'_blank'}>
|
||||
{/* eslint-disable */}
|
||||
<a href="https://survey.aliyun.com/survey/k0BjJ2ARC" target="_blank">
|
||||
{locale.questionnaire2}
|
||||
</a>
|
||||
</div>
|
||||
@ -195,25 +201,6 @@ class ConfigurationManagement extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 回车事件
|
||||
*/
|
||||
keyDownSearch(e) {
|
||||
const theEvent = e || window.event;
|
||||
const code = theEvent.keyCode || theEvent.which || theEvent.charCode;
|
||||
if (this.state.isPageEnter) {
|
||||
this.setState({
|
||||
isPageEnter: false,
|
||||
});
|
||||
return false;
|
||||
}
|
||||
if (code === 13) {
|
||||
this.getData();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
navTo(url, record) {
|
||||
this.serverId = getParams('serverId') || '';
|
||||
this.tenant = getParams('namespace') || ''; // 为当前实例保存tenant参数
|
||||
@ -236,18 +223,6 @@ class ConfigurationManagement extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
UNSAFE_componentWillMount() {
|
||||
window.addEventListener('keydown', this.keyDownSearch.bind(this), false);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener('keydown', this.keyDownSearch.bind(this));
|
||||
}
|
||||
|
||||
onSearch() {}
|
||||
|
||||
onChange() {}
|
||||
|
||||
cleanAndGetData(needclean = false) {
|
||||
if (needclean) {
|
||||
this.dataId = '';
|
||||
@ -269,54 +244,33 @@ class ConfigurationManagement extends React.Component {
|
||||
}
|
||||
|
||||
getData(pageNo = 1, clearSelect = true) {
|
||||
const self = this;
|
||||
if (this.state.loading) {
|
||||
return;
|
||||
}
|
||||
this.tenant = getParams('namespace') || ''; // 为当前实例保存tenant参数
|
||||
this.serverId = getParams('serverId') || '';
|
||||
let urlPrefix = '';
|
||||
const params = {
|
||||
dataId: this.dataId,
|
||||
group: this.group,
|
||||
appName: this.appName,
|
||||
config_tags: this.state.config_tags.join(','),
|
||||
pageNo,
|
||||
pageSize: this.state.pageSize,
|
||||
};
|
||||
if (this.dataId.indexOf('*') !== -1 || this.group.indexOf('*') !== -1) {
|
||||
urlPrefix = 'v1/cs/configs?search=blur';
|
||||
params.search = 'blur';
|
||||
} else {
|
||||
urlPrefix = 'v1/cs/configs?search=accurate';
|
||||
params.search = 'accurate';
|
||||
}
|
||||
|
||||
request({
|
||||
url: `${urlPrefix}&dataId=${this.dataId}&group=${this.group}&appName=${
|
||||
this.appName
|
||||
}&config_tags=${this.state.config_tags || ''}&pageNo=${pageNo}&pageSize=${
|
||||
this.state.pageSize
|
||||
}`,
|
||||
beforeSend() {
|
||||
self.openLoading();
|
||||
},
|
||||
success(data) {
|
||||
if (data != null) {
|
||||
self.setState({
|
||||
dataSource: data.pageItems,
|
||||
total: data.totalCount,
|
||||
currentPage: data.pageNumber,
|
||||
});
|
||||
if (clearSelect) {
|
||||
self.setState({
|
||||
selectedRecord: [],
|
||||
selectedKeys: [],
|
||||
});
|
||||
}
|
||||
}
|
||||
self.setState({
|
||||
tenant: self.tenant,
|
||||
});
|
||||
},
|
||||
error(data) {
|
||||
self.setState({
|
||||
dataSource: [],
|
||||
total: 0,
|
||||
currentPage: 0,
|
||||
});
|
||||
},
|
||||
complete() {
|
||||
self.closeLoading();
|
||||
},
|
||||
});
|
||||
this.setState({ loading: true });
|
||||
this.props.getConfigs(params).then(() =>
|
||||
this.setState({
|
||||
loading: false,
|
||||
selectedRecord: [],
|
||||
selectedKeys: [],
|
||||
tenant: this.tenant,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
showMore() {}
|
||||
@ -435,28 +389,17 @@ class ConfigurationManagement extends React.Component {
|
||||
changePage(value, e) {
|
||||
this.setState(
|
||||
{
|
||||
isPageEnter: e.keyCode && e.keyCode === 13,
|
||||
isPageEnter: e && e.keyCode && e.keyCode === 13,
|
||||
currentPage: value,
|
||||
},
|
||||
() => {
|
||||
this.getData(value, false);
|
||||
}
|
||||
() => this.getData(value, false)
|
||||
);
|
||||
}
|
||||
|
||||
handlePageSizeChange(pageSize) {
|
||||
this.setState(
|
||||
{
|
||||
pageSize,
|
||||
},
|
||||
() => {
|
||||
this.changePage(1);
|
||||
}
|
||||
);
|
||||
this.setState({ pageSize }, () => this.changePage(1));
|
||||
}
|
||||
|
||||
onInputUpdate() {}
|
||||
|
||||
chooseFieldChange(fieldValue) {
|
||||
this.setState({
|
||||
fieldValue,
|
||||
@ -502,16 +445,10 @@ class ConfigurationManagement extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
getDataId(value) {
|
||||
this.dataId = value;
|
||||
this.setState({
|
||||
dataId: value,
|
||||
});
|
||||
}
|
||||
|
||||
setConfigTags(value) {
|
||||
this.setState({
|
||||
config_tags: value,
|
||||
config_tags: value || [],
|
||||
tagLst: value,
|
||||
});
|
||||
}
|
||||
|
||||
@ -618,8 +555,6 @@ class ConfigurationManagement extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
onPageSelectAll(selected, records) {}
|
||||
|
||||
getBatchFailedContent(res) {
|
||||
const { locale = {} } = this.props;
|
||||
return (
|
||||
@ -629,7 +564,7 @@ class ConfigurationManagement extends React.Component {
|
||||
<Collapse style={{ width: '500px' }}>
|
||||
{'failedItems' in res.data && res.data.failedItems.length > 0 ? (
|
||||
<Panel title={locale.failedEntry + res.data.failedItems.length}>
|
||||
<Table dataSource={res.data.failedItems} fixedHeader maxBodyHeight={400}>
|
||||
<Table dataSource={res.data.failedItems} fixedHeader>
|
||||
<Table.Column title={'Data ID'} dataIndex={'dataId'} />
|
||||
<Table.Column title={'Group'} dataIndex={'group'} />
|
||||
</Table>
|
||||
@ -639,7 +574,7 @@ class ConfigurationManagement extends React.Component {
|
||||
)}
|
||||
{'succeededItems' in res.data && res.data.succeededItems.length > 0 ? (
|
||||
<Panel title={locale.successfulEntry + res.data.succeededItems.length}>
|
||||
<Table dataSource={res.data.succeededItems} fixedHeader maxBodyHeight={400}>
|
||||
<Table dataSource={res.data.succeededItems} fixedHeader>
|
||||
<Table.Column title={'Data ID'} dataIndex={'dataId'} />
|
||||
<Table.Column title={'Group'} dataIndex={'group'} />
|
||||
</Table>
|
||||
@ -649,7 +584,7 @@ class ConfigurationManagement extends React.Component {
|
||||
)}
|
||||
{'unprocessedItems' in res.data && res.data.unprocessedItems.length > 0 ? (
|
||||
<Panel title={locale.unprocessedEntry + res.data.unprocessedItems.length}>
|
||||
<Table dataSource={res.data.unprocessedItems} fixedHeader maxBodyHeight={400}>
|
||||
<Table dataSource={res.data.unprocessedItems} fixedHeader>
|
||||
<Table.Column title={'Data ID'} dataIndex={'dataId'} />
|
||||
<Table.Column title={'Group'} dataIndex={'group'} />
|
||||
</Table>
|
||||
@ -689,28 +624,51 @@ class ConfigurationManagement extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
openUri(url, params) {
|
||||
window.open(
|
||||
[
|
||||
url,
|
||||
Object.keys(params)
|
||||
.map(key => `${key}=${params[key]}`)
|
||||
.join('&'),
|
||||
].join('?')
|
||||
);
|
||||
}
|
||||
|
||||
exportData() {
|
||||
let url = `v1/cs/configs?export=true&group=${this.group}&tenant=${getParams(
|
||||
'namespace'
|
||||
)}&appName=${this.appName}&ids=&dataId=${this.dataId}`;
|
||||
window.location.href = url;
|
||||
const { group, appName, dataId, openUri } = this;
|
||||
const { accessToken = '' } = JSON.parse(localStorage.token || '{}');
|
||||
openUri('v1/cs/configs', {
|
||||
export: 'true',
|
||||
tenant: getParams('namespace'),
|
||||
group,
|
||||
appName,
|
||||
dataId,
|
||||
ids: '',
|
||||
accessToken,
|
||||
});
|
||||
}
|
||||
|
||||
exportSelectedData() {
|
||||
const ids = [];
|
||||
const { locale = {} } = this.props;
|
||||
if (configsTableSelected.size === 0) {
|
||||
const { accessToken = '' } = JSON.parse(localStorage.token || '{}');
|
||||
if (!configsTableSelected.size) {
|
||||
Dialog.alert({
|
||||
title: locale.exportSelectedAlertTitle,
|
||||
content: locale.exportSelectedAlertContent,
|
||||
});
|
||||
} else {
|
||||
let idsStr = '';
|
||||
configsTableSelected.forEach((value, key, map) => {
|
||||
idsStr = `${idsStr + key},`;
|
||||
});
|
||||
let url = `v1/cs/configs?export=true&group=&tenant=&appName=&ids=${idsStr}`;
|
||||
window.location.href = url;
|
||||
return;
|
||||
}
|
||||
configsTableSelected.forEach((value, key, map) => ids.push(key));
|
||||
this.openUri('v1/cs/configs', {
|
||||
export: 'true',
|
||||
tenant: '',
|
||||
group: '',
|
||||
appName: '',
|
||||
ids: ids.join(','),
|
||||
accessToken,
|
||||
});
|
||||
}
|
||||
|
||||
multipleSelectionDeletion() {
|
||||
@ -835,7 +793,7 @@ class ConfigurationManagement extends React.Component {
|
||||
title: locale.cloningConfiguration,
|
||||
footer: false,
|
||||
content: (
|
||||
<div>
|
||||
<>
|
||||
<div style={{ marginBottom: 10 }}>
|
||||
<span style={{ color: '#999', marginRight: 5 }}>{locale.source}</span>
|
||||
<span style={{ color: '#49D2E7' }}>{self.state.nownamespace_name} </span>|{' '}
|
||||
@ -953,25 +911,19 @@ class ConfigurationManagement extends React.Component {
|
||||
</Button>
|
||||
</div>
|
||||
<div style={{ marginBottom: 10 }}>
|
||||
<span style={{ color: '#00AA00', 'font-weight': 'bold' }}>
|
||||
<span style={{ color: '#00AA00', fontWeight: 'bold' }}>
|
||||
{locale.cloneEditableTitle}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<Table dataSource={editableTableData}>
|
||||
<Table.Column
|
||||
title="Data Id"
|
||||
dataIndex="dataId"
|
||||
cell={renderEditableTableCellDataId}
|
||||
/>
|
||||
<Table.Column
|
||||
title="Group"
|
||||
dataIndex="group"
|
||||
cell={renderEditableTableCellGroup}
|
||||
/>
|
||||
</Table>
|
||||
</div>
|
||||
</div>
|
||||
<Table dataSource={editableTableData}>
|
||||
<Table.Column
|
||||
title="Data Id"
|
||||
dataIndex="dataId"
|
||||
cell={renderEditableTableCellDataId}
|
||||
/>
|
||||
<Table.Column title="Group" dataIndex="group" cell={renderEditableTableCellGroup} />
|
||||
</Table>
|
||||
</>
|
||||
),
|
||||
});
|
||||
},
|
||||
@ -1158,273 +1110,268 @@ class ConfigurationManagement extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { locale = {} } = this.props;
|
||||
const { locale = {}, configurations = {} } = this.props;
|
||||
return (
|
||||
<div>
|
||||
<>
|
||||
<BatchHandle ref={ref => (this.batchHandle = ref)} />
|
||||
<Loading
|
||||
shape={'flower'}
|
||||
style={{ position: 'relative', width: '100%', overflow: 'auto' }}
|
||||
visible={this.state.loading}
|
||||
tip={'Loading...'}
|
||||
color={'#333'}
|
||||
>
|
||||
<div className={this.state.hasdash ? 'dash-page-container' : ''}>
|
||||
<div
|
||||
className={this.state.hasdash ? 'dash-left-container' : ''}
|
||||
style={{ position: 'relative', padding: 10 }}
|
||||
>
|
||||
<div style={{ display: this.inApp ? 'none' : 'block', marginTop: -15 }}>
|
||||
<RegionGroup
|
||||
namespaceCallBack={this.cleanAndGetData.bind(this)}
|
||||
setNowNameSpace={this.setNowNameSpace.bind(this)}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: this.inApp ? 'none' : 'block',
|
||||
position: 'relative',
|
||||
width: '100%',
|
||||
overflow: 'hidden',
|
||||
height: '40px',
|
||||
}}
|
||||
>
|
||||
<h3
|
||||
style={{
|
||||
height: 30,
|
||||
width: '100%',
|
||||
lineHeight: '30px',
|
||||
padding: 0,
|
||||
margin: 0,
|
||||
paddingLeft: 10,
|
||||
borderLeft: '3px solid #09c',
|
||||
color: '#ccc',
|
||||
fontSize: '12px',
|
||||
}}
|
||||
>
|
||||
<span style={{ fontSize: '14px', color: '#000', marginRight: 8 }}>
|
||||
{locale.configurationManagement8}
|
||||
</span>
|
||||
<span style={{ fontSize: '14px', color: '#000', marginRight: 8 }}>|</span>
|
||||
<span style={{ fontSize: '14px', color: '#000', marginRight: 8 }}>
|
||||
{this.state.nownamespace_name}
|
||||
</span>
|
||||
<span style={{ fontSize: '14px', color: '#000', marginRight: 18 }}>
|
||||
{this.state.nownamespace_id}
|
||||
</span>
|
||||
{locale.queryResults}
|
||||
<strong style={{ fontWeight: 'bold' }}> {this.state.total} </strong>
|
||||
{locale.articleMeetRequirements}
|
||||
</h3>
|
||||
<div
|
||||
style={{ position: 'absolute', textAlign: 'right', zIndex: 2, right: 0, top: 0 }}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
position: 'relative',
|
||||
marginTop: 10,
|
||||
height: this.state.isAdvancedQuery ? 'auto' : 42,
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
<Form inline>
|
||||
<Form.Item label={'Data ID:'}>
|
||||
<Input
|
||||
htmlType={'text'}
|
||||
placeholder={locale.fuzzyd}
|
||||
style={{ width: 200 }}
|
||||
value={this.state.dataId}
|
||||
onChange={this.getDataId.bind(this)}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label={'Group:'}>
|
||||
<Select.AutoComplete
|
||||
style={{ width: 200 }}
|
||||
size={'medium'}
|
||||
placeholder={locale.fuzzyg}
|
||||
dataSource={this.state.groups}
|
||||
value={this.state.group}
|
||||
onChange={this.setGroup.bind(this)}
|
||||
hasClear
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label={''}>
|
||||
<Button
|
||||
type={'primary'}
|
||||
style={{ marginRight: 10 }}
|
||||
onClick={this.selectAll.bind(this)}
|
||||
data-spm-click={'gostr=/aliyun;locaid=dashsearch'}
|
||||
>
|
||||
{locale.query}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
style={
|
||||
this.inApp
|
||||
? { display: 'none' }
|
||||
: { verticalAlign: 'middle', marginTop: 0, marginLeft: 10 }
|
||||
}
|
||||
>
|
||||
<div
|
||||
style={{ color: '#33cde5', fontSize: 12, cursor: 'pointer' }}
|
||||
onClick={this.changeAdvancedQuery}
|
||||
>
|
||||
<span style={{ marginRight: 5, lineHeight: '28px' }}>
|
||||
{locale.advancedQuery9}
|
||||
</span>
|
||||
<Icon
|
||||
type={
|
||||
this.state.isAdvancedQuery ? 'arrow-up-filling' : 'arrow-down-filling'
|
||||
}
|
||||
size={'xs'}
|
||||
/>
|
||||
</div>
|
||||
</Form.Item>
|
||||
<Form.Item label={''}>
|
||||
<Button
|
||||
type={'primary'}
|
||||
style={{ marginRight: 10 }}
|
||||
onClick={this.exportData.bind(this)}
|
||||
data-spm-click={'gostr=/aliyun;locaid=configsExport'}
|
||||
>
|
||||
{locale.export}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
<Form.Item label={''}>
|
||||
<Button
|
||||
type={'primary'}
|
||||
style={{ marginRight: 10 }}
|
||||
onClick={this.importData.bind(this)}
|
||||
data-spm-click={'gostr=/aliyun;locaid=configsExport'}
|
||||
>
|
||||
{locale.import}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
<br />
|
||||
<Form.Item
|
||||
style={this.inApp ? { display: 'none' } : {}}
|
||||
label={locale.application0}
|
||||
>
|
||||
<Input
|
||||
htmlType={'text'}
|
||||
placeholder={locale.app1}
|
||||
style={{ width: 200 }}
|
||||
value={this.state.appName}
|
||||
onChange={this.setAppName.bind(this)}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label={locale.tags}>
|
||||
<Select
|
||||
style={{ width: 200 }}
|
||||
size={'medium'}
|
||||
hasArrow
|
||||
mode="tag"
|
||||
filterLocal={false}
|
||||
placeholder={locale.pleaseEnterTag}
|
||||
dataSource={this.state.tagLst}
|
||||
value={this.state.config_tags}
|
||||
onChange={this.setConfigTags.bind(this)}
|
||||
hasClear
|
||||
/>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
<div style={{ position: 'absolute', right: 10, top: 4 }}>
|
||||
<Icon
|
||||
type={'add'}
|
||||
size={'medium'}
|
||||
style={{
|
||||
color: 'black',
|
||||
marginRight: 0,
|
||||
verticalAlign: 'middle',
|
||||
cursor: 'pointer',
|
||||
backgroundColor: '#eee',
|
||||
border: '1px solid #ddd',
|
||||
padding: '3px 6px',
|
||||
}}
|
||||
onClick={this.chooseEnv.bind(this)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Table
|
||||
dataSource={this.state.dataSource}
|
||||
locale={{ empty: locale.pubNoData }}
|
||||
fixedHeader
|
||||
maxBodyHeight={400}
|
||||
ref={'dataTable'}
|
||||
rowSelection={this.state.rowSelection}
|
||||
>
|
||||
<Table.Column title={'Data Id'} dataIndex={'dataId'} />
|
||||
<Table.Column title={'Group'} dataIndex={'group'} />
|
||||
{!this.inApp ? (
|
||||
<Table.Column title={locale.application} dataIndex={'appName'} />
|
||||
) : (
|
||||
<div />
|
||||
)}
|
||||
<Table.Column title={locale.operation} cell={this.renderCol.bind(this)} />
|
||||
</Table>
|
||||
{this.state.dataSource.length > 0 && (
|
||||
<div style={{ marginTop: 10, overflow: 'hidden' }}>
|
||||
<div style={{ float: 'left' }}>
|
||||
<Button
|
||||
type={'primary'}
|
||||
warning
|
||||
style={{ marginRight: 10 }}
|
||||
onClick={this.multipleSelectionDeletion.bind(this)}
|
||||
data-spm-click={'gostr=/aliyun;locaid=configsDelete'}
|
||||
>
|
||||
{locale.deleteAction}
|
||||
</Button>
|
||||
<Button
|
||||
type={'primary'}
|
||||
style={{ marginRight: 10 }}
|
||||
onClick={this.exportSelectedData.bind(this)}
|
||||
data-spm-click={'gostr=/aliyun;locaid=configsExport'}
|
||||
>
|
||||
{locale.exportSelected}
|
||||
</Button>
|
||||
<Button
|
||||
type={'primary'}
|
||||
style={{ marginRight: 10 }}
|
||||
onClick={this.cloneSelectedDataConfirm.bind(this)}
|
||||
data-spm-click={'gostr=/aliyun;locaid=configsClone'}
|
||||
>
|
||||
{locale.clone}
|
||||
</Button>
|
||||
</div>
|
||||
<div style={{ float: 'right' }}>
|
||||
<Pagination
|
||||
style={{ float: 'right' }}
|
||||
pageSizeList={[10, 20, 30]}
|
||||
pageSizeSelector={'dropdown'}
|
||||
onPageSizeChange={this.handlePageSizeChange.bind(this)}
|
||||
current={this.state.currentPage}
|
||||
total={this.state.total}
|
||||
pageSize={this.state.pageSize}
|
||||
onChange={this.changePage.bind(this)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<ShowCodeing ref={this.showcode} />
|
||||
<DeleteDialog ref={this.deleteDialog} />
|
||||
<div className={this.state.hasdash ? 'dash-page-container' : ''}>
|
||||
<div
|
||||
className={this.state.hasdash ? 'dash-left-container' : ''}
|
||||
style={{ position: 'relative' }}
|
||||
>
|
||||
<div style={{ display: this.inApp ? 'none' : 'block', marginTop: -15 }}>
|
||||
<RegionGroup
|
||||
namespaceCallBack={this.cleanAndGetData.bind(this)}
|
||||
setNowNameSpace={this.setNowNameSpace.bind(this)}
|
||||
/>
|
||||
</div>
|
||||
{this.state.hasdash && (
|
||||
<div
|
||||
className={'dash-right-container'}
|
||||
style={{ overflow: 'auto', height: window.innerHeight - 40 }}
|
||||
<div
|
||||
style={{
|
||||
display: this.inApp ? 'none' : 'block',
|
||||
position: 'relative',
|
||||
width: '100%',
|
||||
overflow: 'hidden',
|
||||
height: '40px',
|
||||
}}
|
||||
>
|
||||
<h3
|
||||
style={{
|
||||
height: 30,
|
||||
width: '100%',
|
||||
lineHeight: '30px',
|
||||
padding: 0,
|
||||
margin: 0,
|
||||
paddingLeft: 10,
|
||||
borderLeft: '3px solid #09c',
|
||||
color: '#ccc',
|
||||
fontSize: '12px',
|
||||
}}
|
||||
>
|
||||
{this.state.contentList.map((v, i) => (
|
||||
<DashboardCard data={v} height={'auto'} key={`show${i}`} />
|
||||
))}
|
||||
<span style={{ fontSize: '14px', color: '#000', marginRight: 8 }}>
|
||||
{locale.configurationManagement8}
|
||||
</span>
|
||||
<span style={{ fontSize: '14px', color: '#000', marginRight: 8 }}>|</span>
|
||||
<span style={{ fontSize: '14px', color: '#000', marginRight: 8 }}>
|
||||
{this.state.nownamespace_name}
|
||||
</span>
|
||||
<span style={{ fontSize: '14px', color: '#000', marginRight: 18 }}>
|
||||
{this.state.nownamespace_id}
|
||||
</span>
|
||||
{locale.queryResults}
|
||||
<strong style={{ fontWeight: 'bold' }}> {configurations.totalCount} </strong>
|
||||
{locale.articleMeetRequirements}
|
||||
</h3>
|
||||
<div
|
||||
style={{ position: 'absolute', textAlign: 'right', zIndex: 2, right: 0, top: 0 }}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
position: 'relative',
|
||||
marginTop: 10,
|
||||
height: this.state.isAdvancedQuery ? 'auto' : 42,
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
<Form inline>
|
||||
<Form.Item label="Data ID:">
|
||||
<Input
|
||||
htmlType="text"
|
||||
placeholder={locale.fuzzyd}
|
||||
style={{ width: 200 }}
|
||||
onChange={dataId => {
|
||||
this.dataId = dataId;
|
||||
this.setState({ dataId });
|
||||
}}
|
||||
onPressEnter={() => this.getData()}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="Group:">
|
||||
<Select.AutoComplete
|
||||
style={{ width: 200 }}
|
||||
size={'medium'}
|
||||
placeholder={locale.fuzzyg}
|
||||
dataSource={this.state.groups}
|
||||
value={this.state.group}
|
||||
onChange={this.setGroup.bind(this)}
|
||||
onPressEnter={() => this.getData()}
|
||||
hasClear
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label={''}>
|
||||
<Button
|
||||
type={'primary'}
|
||||
style={{ marginRight: 10 }}
|
||||
onClick={this.selectAll.bind(this)}
|
||||
data-spm-click={'gostr=/aliyun;locaid=dashsearch'}
|
||||
>
|
||||
{locale.query}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
style={
|
||||
this.inApp
|
||||
? { display: 'none' }
|
||||
: { verticalAlign: 'middle', marginTop: 0, marginLeft: 10 }
|
||||
}
|
||||
>
|
||||
<div
|
||||
style={{ color: '#33cde5', fontSize: 12, cursor: 'pointer' }}
|
||||
onClick={this.changeAdvancedQuery}
|
||||
>
|
||||
<span style={{ marginRight: 5, lineHeight: '28px' }}>
|
||||
{locale.advancedQuery9}
|
||||
</span>
|
||||
<Icon
|
||||
type={this.state.isAdvancedQuery ? 'arrow-up-filling' : 'arrow-down-filling'}
|
||||
size={'xs'}
|
||||
/>
|
||||
</div>
|
||||
</Form.Item>
|
||||
<Form.Item label={''}>
|
||||
<Button
|
||||
type={'primary'}
|
||||
style={{ marginRight: 10 }}
|
||||
onClick={this.exportData.bind(this)}
|
||||
data-spm-click={'gostr=/aliyun;locaid=configsExport'}
|
||||
>
|
||||
{locale.export}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
<Form.Item label={''}>
|
||||
<Button
|
||||
type={'primary'}
|
||||
style={{ marginRight: 10 }}
|
||||
onClick={this.importData.bind(this)}
|
||||
data-spm-click={'gostr=/aliyun;locaid=configsExport'}
|
||||
>
|
||||
{locale.import}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
<br />
|
||||
<Form.Item
|
||||
style={this.inApp ? { display: 'none' } : {}}
|
||||
label={locale.application0}
|
||||
>
|
||||
<Input
|
||||
htmlType={'text'}
|
||||
placeholder={locale.app1}
|
||||
style={{ width: 200 }}
|
||||
value={this.state.appName}
|
||||
onChange={this.setAppName.bind(this)}
|
||||
onPressEnter={() => this.getData()}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label={locale.tags}>
|
||||
<Select
|
||||
style={{ width: 200 }}
|
||||
size="medium"
|
||||
hasArrow
|
||||
mode="tag"
|
||||
placeholder={locale.pleaseEnterTag}
|
||||
dataSource={this.state.tagLst}
|
||||
value={this.state.config_tags}
|
||||
onChange={this.setConfigTags.bind(this)}
|
||||
showSearch
|
||||
onSearch={val => {
|
||||
const { tagLst } = this.state;
|
||||
if (!tagLst.includes(val)) {
|
||||
this.setState({ tagLst: tagLst.concat(val) });
|
||||
}
|
||||
}}
|
||||
hasClear
|
||||
/>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
<div style={{ position: 'absolute', right: 10, top: 4 }}>
|
||||
<Icon
|
||||
type="add"
|
||||
size="medium"
|
||||
style={{
|
||||
color: 'black',
|
||||
marginRight: 0,
|
||||
verticalAlign: 'middle',
|
||||
cursor: 'pointer',
|
||||
backgroundColor: '#eee',
|
||||
border: '1px solid #ddd',
|
||||
padding: '3px 6px',
|
||||
}}
|
||||
onClick={this.chooseEnv.bind(this)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Table
|
||||
className="configuration-table"
|
||||
dataSource={configurations.pageItems}
|
||||
locale={{ empty: locale.pubNoData }}
|
||||
ref="dataTable"
|
||||
loading={this.state.loading}
|
||||
rowSelection={this.state.rowSelection}
|
||||
>
|
||||
<Table.Column title={'Data Id'} dataIndex={'dataId'} />
|
||||
<Table.Column title={'Group'} dataIndex={'group'} />
|
||||
{!this.inApp && <Table.Column title={locale.application} dataIndex="appName" />}
|
||||
<Table.Column title={locale.operation} cell={this.renderCol.bind(this)} />
|
||||
</Table>
|
||||
{configurations.totalCount > 0 && (
|
||||
<>
|
||||
<div style={{ float: 'left' }}>
|
||||
{[
|
||||
{
|
||||
warning: true,
|
||||
text: locale.deleteAction,
|
||||
locaid: 'configsDelete',
|
||||
onClick: () => this.multipleSelectionDeletion(),
|
||||
},
|
||||
{
|
||||
text: locale.exportSelected,
|
||||
locaid: 'configsExport',
|
||||
onClick: () => this.exportSelectedData(),
|
||||
},
|
||||
{
|
||||
text: locale.clone,
|
||||
locaid: 'configsDelete',
|
||||
onClick: () => this.cloneSelectedDataConfirm(),
|
||||
},
|
||||
].map(item => (
|
||||
<Button
|
||||
warning={item.warning}
|
||||
type="primary"
|
||||
style={{ marginRight: 10 }}
|
||||
onClick={item.onClick}
|
||||
data-spm-click={`gostr=/aliyun;locaid=${item.locaid}`}
|
||||
>
|
||||
{item.text}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
<Pagination
|
||||
style={{ float: 'right' }}
|
||||
pageSizeList={[10, 20, 30]}
|
||||
pageSizePosition="start"
|
||||
pageSizeSelector="dropdown"
|
||||
popupProps={{ align: 'bl tl' }}
|
||||
onPageSizeChange={val => this.handlePageSizeChange(val)}
|
||||
current={configurations.pageNumber}
|
||||
total={configurations.totalCount}
|
||||
pageSize={this.state.pageSize}
|
||||
onChange={this.changePage.bind(this)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<ShowCodeing ref={this.showcode} />
|
||||
<DeleteDialog ref={this.deleteDialog} />
|
||||
</div>
|
||||
</Loading>
|
||||
</div>
|
||||
{this.state.hasdash && (
|
||||
<div className="dash-right-container">
|
||||
{this.state.contentList.map((v, i) => (
|
||||
<DashboardCard data={v} height={'auto'} key={`show${i}`} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -46,10 +46,11 @@ class DashboardCard extends React.Component {
|
||||
</strong>
|
||||
<strong>
|
||||
<span>
|
||||
{/* eslint-disable */}
|
||||
<a
|
||||
style={{ marginLeft: 10, color: '#33cde5' }}
|
||||
href={item.url}
|
||||
target={'_blank'}
|
||||
target="_blank"
|
||||
>
|
||||
{locale.viewDetails1}
|
||||
</a>
|
||||
|
@ -11,9 +11,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
.next-pagination-size-selector{
|
||||
.next-pagination-size-selector {
|
||||
position: static !important;
|
||||
}
|
||||
.next-overlay-inner{
|
||||
top:154px !important;
|
||||
.configuration-table {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
@ -81,9 +81,7 @@ class HistoryDetail extends React.Component {
|
||||
|
||||
goList() {
|
||||
this.props.history.push(
|
||||
`/historyRollback?serverId=${this.serverId}&group=${this.group}&dataId=${
|
||||
this.dataId
|
||||
}&namespace=${this.tenant}`
|
||||
`/historyRollback?serverId=${this.serverId}&group=${this.group}&dataId=${this.dataId}&namespace=${this.tenant}`
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,7 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { ConfigProvider, Field, Form, Input, Loading, Pagination, Table } from '@alifd/next';
|
||||
import RegionGroup from 'components/RegionGroup';
|
||||
import { getParams, setParams, request, aliwareIntl } from '@/globalLib';
|
||||
import { getParams, setParams, request } from '@/globalLib';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
@ -83,31 +83,6 @@ class HistoryRollback extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 回车事件
|
||||
*/
|
||||
keyDownSearch(e) {
|
||||
const theEvent = e || window.event;
|
||||
const code = theEvent.keyCode || theEvent.which || theEvent.charCode;
|
||||
if (code === 13) {
|
||||
this.getData();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
UNSAFE_componentWillMount() {
|
||||
window.addEventListener('keydown', this.keyDownSearch.bind(this), false);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener('keydown', this.keyDownSearch.bind(this));
|
||||
}
|
||||
|
||||
onSearch() {}
|
||||
|
||||
onChange() {}
|
||||
|
||||
cleanAndGetData(needclean = false) {
|
||||
if (needclean) {
|
||||
this.dataId = '';
|
||||
@ -133,9 +108,7 @@ class HistoryRollback extends React.Component {
|
||||
beforeSend() {
|
||||
self.openLoading();
|
||||
},
|
||||
url: `v1/cs/history?search=accurate&dataId=${this.dataId}&group=${
|
||||
this.group
|
||||
}&&pageNo=${pageNo}&pageSize=${this.state.pageSize}`,
|
||||
url: `v1/cs/history?search=accurate&dataId=${this.dataId}&group=${this.group}&&pageNo=${pageNo}&pageSize=${this.state.pageSize}`,
|
||||
success(data) {
|
||||
if (data != null) {
|
||||
self.setState({
|
||||
@ -151,8 +124,6 @@ class HistoryRollback extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
showMore() {}
|
||||
|
||||
renderCol(value, index, record) {
|
||||
const { locale = {} } = this.props;
|
||||
return (
|
||||
@ -175,60 +146,12 @@ class HistoryRollback extends React.Component {
|
||||
this.getData(value);
|
||||
}
|
||||
|
||||
onInputUpdate() {}
|
||||
|
||||
chooseFieldChange(fieldValue) {
|
||||
this.setState({
|
||||
fieldValue,
|
||||
});
|
||||
}
|
||||
|
||||
showSelect(value) {
|
||||
this.setState({
|
||||
selectValue: value,
|
||||
});
|
||||
if (value.indexOf('appName') !== -1) {
|
||||
this.setState({
|
||||
showAppName: true,
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
showAppName: false,
|
||||
});
|
||||
}
|
||||
if (value.indexOf('group') !== -1) {
|
||||
this.setState({
|
||||
showgroup: true,
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
showgroup: false,
|
||||
});
|
||||
}
|
||||
this.chooseFieldChange(value);
|
||||
}
|
||||
|
||||
getAppName(value) {
|
||||
this.appName = value;
|
||||
this.setState({
|
||||
appName: value,
|
||||
});
|
||||
}
|
||||
|
||||
getDataId(value) {
|
||||
this.dataId = value;
|
||||
this.setState({
|
||||
dataId: value,
|
||||
});
|
||||
}
|
||||
|
||||
getGroup(value) {
|
||||
this.group = value;
|
||||
this.setState({
|
||||
group: value,
|
||||
});
|
||||
}
|
||||
|
||||
selectAll() {
|
||||
this.dataId = this.field.getValue('dataId');
|
||||
this.group = this.field.getValue('group');
|
||||
@ -265,10 +188,6 @@ class HistoryRollback extends React.Component {
|
||||
|
||||
chooseEnv(value) {}
|
||||
|
||||
renderLastTime(value, index, record) {
|
||||
return aliwareIntl.intlTimeFormat(record.lastModifiedTime);
|
||||
}
|
||||
|
||||
goDetail(record) {
|
||||
this.serverId = getParams('serverId') || 'center';
|
||||
this.tenant = getParams('namespace') || ''; // 为当前实例保存tenant参数
|
||||
@ -373,8 +292,18 @@ class HistoryRollback extends React.Component {
|
||||
<Table.Column title="Group" dataIndex="group" />
|
||||
<Table.Column
|
||||
title={locale.lastUpdateTime}
|
||||
dataIndex="time"
|
||||
cell={this.renderLastTime.bind(this)}
|
||||
dataIndex="lastModifiedTime"
|
||||
cell={val => {
|
||||
if (!val) {
|
||||
return '';
|
||||
}
|
||||
try {
|
||||
const date = new Date(val);
|
||||
return date.toLocaleString();
|
||||
} catch (e) {
|
||||
return '';
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Table.Column title={locale.operation} cell={this.renderCol.bind(this)} />
|
||||
</Table>
|
||||
|
@ -183,7 +183,7 @@ class ListeningToQuery extends React.Component {
|
||||
},
|
||||
];
|
||||
return (
|
||||
<div style={{ padding: 10 }}>
|
||||
<>
|
||||
<Loading
|
||||
shape="flower"
|
||||
style={{ position: 'relative' }}
|
||||
@ -320,7 +320,7 @@ class ListeningToQuery extends React.Component {
|
||||
,
|
||||
</div>
|
||||
</Loading>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import SuccessDialog from '../../../components/SuccessDialog';
|
||||
import { getParams, setParams, request, aliwareIntl } from '../../../globalLib';
|
||||
import { generateUrl } from '../../../utils/nacosutil';
|
||||
import {
|
||||
Balloon,
|
||||
Button,
|
||||
@ -144,6 +145,13 @@ class NewConfig extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
tagSearch(value) {
|
||||
const { tagLst } = this.state;
|
||||
if (!tagLst.includes(value)) {
|
||||
this.setState({ tagLst: [value, ...tagLst] });
|
||||
}
|
||||
}
|
||||
|
||||
setConfigTags(value) {
|
||||
if (value.length > 5) {
|
||||
value.pop();
|
||||
@ -154,6 +162,7 @@ class NewConfig extends React.Component {
|
||||
}
|
||||
});
|
||||
this.setState({
|
||||
tagLst: value,
|
||||
config_tags: value,
|
||||
});
|
||||
}
|
||||
@ -192,9 +201,12 @@ class NewConfig extends React.Component {
|
||||
this.tenant = getParams('namespace') || '';
|
||||
this.serverId = getParams('serverId') || '';
|
||||
this.props.history.push(
|
||||
`/configurationManagement?serverId=${this.serverId}&group=${this.searchGroup}&dataId=${
|
||||
this.searchDataId
|
||||
}&namespace=${this.tenant}`
|
||||
generateUrl('/configurationManagement', {
|
||||
serverId: this.serverId,
|
||||
group: this.searchGroup,
|
||||
dataId: this.searchDataId,
|
||||
namespace: this.tenant,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@ -331,15 +343,15 @@ class NewConfig extends React.Component {
|
||||
}
|
||||
self.successDialog.current.getInstance().openDialog(_payload);
|
||||
},
|
||||
complete() {
|
||||
self.closeLoading();
|
||||
complete: () => {
|
||||
this.closeLoading();
|
||||
},
|
||||
error(res) {
|
||||
error: res => {
|
||||
this.closeLoading();
|
||||
Dialog.alert({
|
||||
language: aliwareIntl.currentLanguageCode || 'zh-cn',
|
||||
content: locale.publishFailed,
|
||||
});
|
||||
self.closeLoading();
|
||||
},
|
||||
});
|
||||
};
|
||||
@ -493,6 +505,7 @@ class NewConfig extends React.Component {
|
||||
>
|
||||
<Select
|
||||
size={'medium'}
|
||||
showSearch
|
||||
hasArrow
|
||||
style={{ width: '100%', height: '100%!important' }}
|
||||
autoWidth
|
||||
@ -503,6 +516,7 @@ class NewConfig extends React.Component {
|
||||
dataSource={this.state.tagLst}
|
||||
value={this.state.config_tags}
|
||||
onChange={this.setConfigTags.bind(this)}
|
||||
onSearch={val => this.tagSearch(val)}
|
||||
hasClear
|
||||
/>
|
||||
</FormItem>
|
||||
|
@ -4,8 +4,8 @@ import { withRouter } from 'react-router-dom';
|
||||
|
||||
import './index.scss';
|
||||
import Header from '../../layouts/Header';
|
||||
import { request } from '../../globalLib';
|
||||
import PropTypes from 'prop-types';
|
||||
import { login } from '../../reducers/base';
|
||||
|
||||
const FormItem = Form.Item;
|
||||
|
||||
@ -24,35 +24,29 @@ class Login extends React.Component {
|
||||
this.field = new Field(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (localStorage.getItem('token')) {
|
||||
const [baseUrl] = location.href.split('#');
|
||||
location.href = `${baseUrl}#/`;
|
||||
}
|
||||
}
|
||||
|
||||
handleSubmit = () => {
|
||||
const { locale = {} } = this.props;
|
||||
this.field.validate((errors, values) => {
|
||||
if (errors) {
|
||||
return;
|
||||
}
|
||||
request({
|
||||
type: 'post',
|
||||
url: 'v1/auth/login',
|
||||
data: values,
|
||||
success: ({ code, data }) => {
|
||||
if (code === 200) {
|
||||
// TODO: 封装一个方法存储、读取token
|
||||
localStorage.setItem('token', data);
|
||||
// TODO: 使用react router
|
||||
this.props.history.push('/');
|
||||
}
|
||||
if (code === 401) {
|
||||
Message.error({
|
||||
content: locale.invalidUsernameOrPassword,
|
||||
});
|
||||
}
|
||||
},
|
||||
error: () => {
|
||||
login(values)
|
||||
.then(res => {
|
||||
localStorage.setItem('token', JSON.stringify(res));
|
||||
this.props.history.push('/');
|
||||
})
|
||||
.catch(() => {
|
||||
Message.error({
|
||||
content: locale.invalidUsernameOrPassword,
|
||||
});
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -281,14 +281,6 @@ class NameSpace extends React.Component {
|
||||
return <div>{name}</div>;
|
||||
}
|
||||
|
||||
renderConfigCount(value, index, record) {
|
||||
return (
|
||||
<div>
|
||||
{value} / {record.quota}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { locale = {} } = this.props;
|
||||
const {
|
||||
@ -301,7 +293,7 @@ class NameSpace extends React.Component {
|
||||
namespaceOperation,
|
||||
} = locale;
|
||||
return (
|
||||
<div style={{ padding: 10 }} className="clearfix">
|
||||
<>
|
||||
<RegionGroup left={namespace} />
|
||||
<div className="fusion-demo">
|
||||
<Loading
|
||||
@ -329,12 +321,7 @@ class NameSpace extends React.Component {
|
||||
cell={this.renderName.bind(this)}
|
||||
/>
|
||||
<Table.Column title={namespaceNumber} dataIndex="namespace" />
|
||||
<Table.Column
|
||||
title={configuration}
|
||||
dataIndex="configCount"
|
||||
cell={this.renderConfigCount.bind(this)}
|
||||
/>
|
||||
|
||||
<Table.Column title={configuration} dataIndex="configCount" />
|
||||
<Table.Column
|
||||
title={namespaceOperation}
|
||||
dataIndex="time"
|
||||
@ -343,12 +330,11 @@ class NameSpace extends React.Component {
|
||||
</Table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<NewNameSpace ref={this.newnamespace} getNameSpaces={this.getNameSpaces.bind(this)} />
|
||||
<EditorNameSpace ref={this.editgroup} getNameSpaces={this.getNameSpaces.bind(this)} />
|
||||
</Loading>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,178 +0,0 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import RegionGroup from 'components/RegionGroup';
|
||||
import { ConfigProvider, Input, Field, Form, Message } from '@alifd/next';
|
||||
import { getParams, setParams, request } from '../../globalLib';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
const FormItem = Form.Item;
|
||||
|
||||
@ConfigProvider.config
|
||||
class Password extends React.Component {
|
||||
static displayName = 'Password';
|
||||
|
||||
static propTypes = {
|
||||
locale: PropTypes.object,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.field = new Field(this);
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
componentDidMount() {}
|
||||
|
||||
validatePassword(rule, value, callback) {
|
||||
const { locale = {} } = this.props;
|
||||
if (this.field.getValue('newPassword') !== this.field.getValue('confirmNewPassword')) {
|
||||
callback(locale.passwordNotConsistent);
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
handleSubmit = () => {
|
||||
const { locale = {} } = this.props;
|
||||
this.field.validate((errors, values) => {
|
||||
if (errors) {
|
||||
return;
|
||||
}
|
||||
request({
|
||||
type: 'post',
|
||||
url: 'v1/auth/login',
|
||||
data: values,
|
||||
success: ({ code, data }) => {
|
||||
if (code === 200) {
|
||||
// TODO: 封装一个方法存储、读取token
|
||||
localStorage.setItem('token', data);
|
||||
// TODO: 使用react router
|
||||
this.props.history.push('/');
|
||||
}
|
||||
if (code === 401) {
|
||||
Message.error({
|
||||
content: locale.invalidUsernameOrPassword,
|
||||
});
|
||||
}
|
||||
},
|
||||
error: () => {
|
||||
Message.error({
|
||||
content: locale.invalidUsernameOrPassword,
|
||||
});
|
||||
},
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
changePassword() {
|
||||
const { locale = {} } = this.props;
|
||||
this.field.validate((errors, values) => {
|
||||
if (errors) {
|
||||
return;
|
||||
}
|
||||
request({
|
||||
type: 'put',
|
||||
url: 'v1/auth/password',
|
||||
data: values,
|
||||
success: ({ code, data }) => {
|
||||
if (code === 200) {
|
||||
window.localStorage.clear();
|
||||
this.props.history.push('/login');
|
||||
}
|
||||
if (code === 401) {
|
||||
Message.error({
|
||||
content: locale.invalidPassword,
|
||||
});
|
||||
}
|
||||
},
|
||||
error: () => {
|
||||
Message.error({
|
||||
content: locale.invalidPassword,
|
||||
});
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { locale = {} } = this.props;
|
||||
const formItemLayout = {
|
||||
labelCol: { fixedSpan: 6 },
|
||||
wrapperCol: { span: 18 },
|
||||
};
|
||||
return (
|
||||
<div style={{ padding: 10 }}>
|
||||
<RegionGroup left={locale.changePassword} />
|
||||
<Form style={{ width: '300px' }} field={this.field}>
|
||||
<FormItem label={locale.oldPassword} required {...formItemLayout}>
|
||||
<Input
|
||||
htmlType="password"
|
||||
placeholder={locale.pleaseInputOldPassword}
|
||||
{...this.field.init('oldPassword', {
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: locale.passwordRequired,
|
||||
},
|
||||
],
|
||||
})}
|
||||
disabled={this.state.type === 0}
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label={locale.newPassword} required {...formItemLayout}>
|
||||
<Input
|
||||
htmlType="password"
|
||||
placeholder={locale.pleaseInputNewPassword}
|
||||
{...this.field.init('newPassword', {
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: locale.passwordRequired,
|
||||
},
|
||||
],
|
||||
})}
|
||||
disabled={this.state.type === 0}
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label={locale.checkPassword} required {...formItemLayout}>
|
||||
<Input
|
||||
htmlType="password"
|
||||
placeholder={locale.pleaseInputNewPasswordAgain}
|
||||
{...this.field.init('confirmNewPassword', {
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: locale.passwordRequired,
|
||||
},
|
||||
{ validator: this.validatePassword.bind(this) },
|
||||
],
|
||||
})}
|
||||
disabled={this.state.type === 0}
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label=" " {...formItemLayout}>
|
||||
<Form.Submit type="primary" onClick={this.changePassword.bind(this)}>
|
||||
{locale.changePassword}
|
||||
</Form.Submit>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Password;
|
@ -28,6 +28,7 @@ import {
|
||||
Switch,
|
||||
} from '@alifd/next';
|
||||
import { request } from '../../../globalLib';
|
||||
import { generateUrl } from '../../../utils/nacosutil';
|
||||
import RegionGroup from '../../../components/RegionGroup';
|
||||
import EditServiceDialog from '../ServiceDetail/EditServiceDialog';
|
||||
import ShowServiceCodeing from 'components/ShowCodeing/ShowServiceCodeing';
|
||||
@ -92,7 +93,6 @@ class ServiceList extends React.Component {
|
||||
];
|
||||
request({
|
||||
url: `v1/ns/catalog/services?${parameter.join('&')}`,
|
||||
beforeSend: () => this.openLoading(),
|
||||
success: ({ count = 0, serviceList = [] } = {}) => {
|
||||
this.setState({
|
||||
dataSource: serviceList,
|
||||
@ -105,7 +105,6 @@ class ServiceList extends React.Component {
|
||||
total: 0,
|
||||
currentPage: 0,
|
||||
}),
|
||||
complete: () => this.closeLoading(),
|
||||
});
|
||||
}
|
||||
|
||||
@ -293,11 +292,12 @@ class ServiceList extends React.Component {
|
||||
*/
|
||||
<div>
|
||||
<a
|
||||
onClick={() =>
|
||||
onClick={() => {
|
||||
const { name, groupName } = record;
|
||||
this.props.history.push(
|
||||
`/serviceDetail?name=${record.name}&groupName=${record.groupName}`
|
||||
)
|
||||
}
|
||||
generateUrl('/serviceDetail', { name, groupName })
|
||||
);
|
||||
}}
|
||||
style={{ marginRight: 5 }}
|
||||
>
|
||||
{detail}
|
||||
|
@ -37,10 +37,7 @@ const FormItem = Form.Item;
|
||||
const { Row, Col } = Grid;
|
||||
const { Column } = Table;
|
||||
|
||||
@connect(
|
||||
state => ({ subscriberData: state.subscribers }),
|
||||
{ getSubscribers, removeSubscribers }
|
||||
)
|
||||
@connect(state => ({ subscriberData: state.subscribers }), { getSubscribers, removeSubscribers })
|
||||
@ConfigProvider.config
|
||||
class SubscriberList extends React.Component {
|
||||
static displayName = 'SubscriberList';
|
||||
|
@ -24,15 +24,8 @@ class Welcome extends React.Component {
|
||||
|
||||
render() {
|
||||
const { functionMode } = this.props;
|
||||
return (
|
||||
<div>
|
||||
{functionMode !== '' && (
|
||||
<Redirect
|
||||
to={`/${functionMode === 'naming' ? 'serviceManagement' : 'configurationManagement'}`}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
const path = functionMode === 'naming' ? 'serviceManagement' : 'configurationManagement';
|
||||
return <>{functionMode !== '' && <Redirect to={`/${path}`} />}</>;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Message } from '@alifd/next';
|
||||
import request from '../utils/request';
|
||||
import { UPDATE_USER, SIGN_IN, USER_LIST, ROLE_LIST, PERMISSIONS_LIST } from '../constants';
|
||||
|
||||
const initialState = {
|
||||
users: {
|
||||
totalCount: 0,
|
||||
pageNumber: 1,
|
||||
pagesAvailable: 1,
|
||||
pageItems: [],
|
||||
},
|
||||
roles: {
|
||||
totalCount: 0,
|
||||
pageNumber: 1,
|
||||
pagesAvailable: 1,
|
||||
pageItems: [],
|
||||
},
|
||||
permissions: {
|
||||
totalCount: 0,
|
||||
pageNumber: 1,
|
||||
pagesAvailable: 1,
|
||||
pageItems: [],
|
||||
},
|
||||
};
|
||||
|
||||
const successMsg = res => {
|
||||
if (res.code === 200) {
|
||||
Message.success(res.message);
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
/**
|
||||
* 用户列表
|
||||
* @param {*} params
|
||||
*/
|
||||
const getUsers = params => dispatch =>
|
||||
request.get('v1/auth/users', { params }).then(data => dispatch({ type: USER_LIST, data }));
|
||||
|
||||
/**
|
||||
* 创建用户
|
||||
* @param {*} param0
|
||||
*/
|
||||
const createUser = ([username, password]) =>
|
||||
request.post('v1/auth/users', { username, password }).then(res => successMsg(res));
|
||||
|
||||
/**
|
||||
* 删除用户
|
||||
* @param {*} username
|
||||
*/
|
||||
const deleteUser = username =>
|
||||
request.delete('v1/auth/users', { params: { username } }).then(res => successMsg(res));
|
||||
|
||||
/**
|
||||
* 重置密码
|
||||
* @param {*} param0
|
||||
*/
|
||||
const passwordReset = ([username, newPassword]) =>
|
||||
request.put('v1/auth/users', { username, newPassword }).then(res => successMsg(res));
|
||||
|
||||
/**
|
||||
* 角色列表
|
||||
* @param {*} params
|
||||
*/
|
||||
|
||||
const getRoles = params => dispatch =>
|
||||
request.get('v1/auth/roles', { params }).then(data => dispatch({ type: ROLE_LIST, data }));
|
||||
|
||||
/**
|
||||
* 创建角色
|
||||
* @param {*} param0
|
||||
*/
|
||||
const createRole = ([role, username]) =>
|
||||
request.post('v1/auth/roles', { role, username }).then(res => successMsg(res));
|
||||
|
||||
/**
|
||||
* 删除角色
|
||||
* @param {*} param0
|
||||
*/
|
||||
const deleteRole = role =>
|
||||
request.delete('v1/auth/roles', { params: role }).then(res => successMsg(res));
|
||||
|
||||
/**
|
||||
* 权限列表
|
||||
* @param {*} params
|
||||
*/
|
||||
const getPermissions = params => dispatch =>
|
||||
request
|
||||
.get('v1/auth/permissions', { params })
|
||||
.then(data => dispatch({ type: PERMISSIONS_LIST, data }));
|
||||
|
||||
/**
|
||||
* 给角色添加权限
|
||||
* @param {*} param0
|
||||
*/
|
||||
const createPermission = ([role, resource, action]) =>
|
||||
request.post('v1/auth/permissions', { role, resource, action }).then(res => successMsg(res));
|
||||
|
||||
/**
|
||||
* 删除权限
|
||||
* @param {*} param0
|
||||
*/
|
||||
const deletePermission = permission =>
|
||||
request.delete('v1/auth/permissions', { params: permission }).then(res => successMsg(res));
|
||||
|
||||
export default (state = initialState, action) => {
|
||||
switch (action.type) {
|
||||
case USER_LIST:
|
||||
return { ...state, users: { ...action.data } };
|
||||
case ROLE_LIST:
|
||||
return { ...state, roles: { ...action.data } };
|
||||
case PERMISSIONS_LIST:
|
||||
return { ...state, permissions: { ...action.data } };
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export {
|
||||
getUsers,
|
||||
createUser,
|
||||
deleteUser,
|
||||
passwordReset,
|
||||
getRoles,
|
||||
createRole,
|
||||
deleteRole,
|
||||
getPermissions,
|
||||
createPermission,
|
||||
deletePermission,
|
||||
};
|
@ -20,6 +20,12 @@ const initialState = {
|
||||
functionMode: '',
|
||||
};
|
||||
|
||||
/**
|
||||
* 用户登录
|
||||
* @param {*} param0
|
||||
*/
|
||||
const login = user => request.post('v1/auth/users/login', user);
|
||||
|
||||
const getState = () => dispatch =>
|
||||
request
|
||||
.get('v1/console/server/state')
|
||||
@ -52,4 +58,4 @@ export default (state = initialState, action) => {
|
||||
}
|
||||
};
|
||||
|
||||
export { getState };
|
||||
export { getState, login };
|
||||
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import request from '../utils/request';
|
||||
import { GET_CONFIGURATION } from '../constants';
|
||||
|
||||
const initialState = {
|
||||
configurations: [],
|
||||
};
|
||||
|
||||
const getConfigs = params => dispatch =>
|
||||
request
|
||||
.get('v1/cs/configs', { params })
|
||||
.then(data => dispatch({ type: GET_CONFIGURATION, data }));
|
||||
|
||||
export default (state = initialState, action) => {
|
||||
switch (action.type) {
|
||||
case GET_CONFIGURATION:
|
||||
return { ...state, configurations: action.data };
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export { getConfigs };
|
@ -14,5 +14,8 @@
|
||||
import locale from './locale';
|
||||
import base from './base';
|
||||
import subscribers from './subscribers';
|
||||
import authority from './authority';
|
||||
import namespace from './namespace';
|
||||
import configuration from './configuration';
|
||||
|
||||
export default { locale, base, subscribers };
|
||||
export default { locale, base, subscribers, authority, namespace, configuration };
|
||||
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import request from '../utils/request';
|
||||
import { GET_NAMESPACES } from '../constants';
|
||||
|
||||
const initialState = {
|
||||
namespaces: [],
|
||||
};
|
||||
|
||||
const getNamespaces = params => dispatch =>
|
||||
request.get('v1/console/namespaces', { params }).then(response => {
|
||||
const { code, data } = response;
|
||||
dispatch({
|
||||
type: GET_NAMESPACES,
|
||||
data: code === 200 ? data : [],
|
||||
});
|
||||
});
|
||||
|
||||
export default (state = initialState, action) => {
|
||||
switch (action.type) {
|
||||
case GET_NAMESPACES:
|
||||
return { ...state, namespaces: action.data };
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export { getNamespaces };
|
@ -1,15 +0,0 @@
|
||||
function getValue(key) {
|
||||
if (!document.cookie) return null;
|
||||
const list = document.cookie.split(';') || [];
|
||||
for (const item of list) {
|
||||
const [k = '', v = ''] = item.split('=');
|
||||
if (k.trim() === key) return v;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function setValue(key, value) {
|
||||
document.cookie = `${key}=${value}`;
|
||||
}
|
||||
|
||||
export default { getValue, setValue };
|
@ -46,3 +46,26 @@ export const getParameter = (search, name) => {
|
||||
const [, value = ''] = hit.split('=');
|
||||
return value;
|
||||
};
|
||||
|
||||
export const isJsonString = str => {
|
||||
try {
|
||||
if (typeof JSON.parse(str) === 'object') {
|
||||
return true;
|
||||
}
|
||||
} catch (e) {}
|
||||
return false;
|
||||
};
|
||||
|
||||
export const generateUrl = (url, params) => {
|
||||
return [
|
||||
url,
|
||||
'?',
|
||||
Object.keys(params)
|
||||
.map(key => [key, params[key]].join('='))
|
||||
.join('&'),
|
||||
].join('');
|
||||
};
|
||||
|
||||
export const isPlainObject = obj => {
|
||||
return Object.prototype.toString.call(obj) === '[object Object]';
|
||||
};
|
||||
|
@ -1,12 +1,52 @@
|
||||
import axios from 'axios';
|
||||
import qs from 'qs';
|
||||
import { Message } from '@alifd/next';
|
||||
import { browserHistory } from 'react-router';
|
||||
import { isPlainObject } from './nacosutil';
|
||||
// import { SUCCESS_RESULT_CODE } from '../constants';
|
||||
|
||||
const API_GENERAL_ERROR_MESSAGE = 'Request error, please try again later!';
|
||||
|
||||
function goLogin() {
|
||||
const url = window.location.href;
|
||||
localStorage.removeItem('token');
|
||||
const base_url = url.split('#')[0];
|
||||
window.location.href = `${base_url}#/login`;
|
||||
}
|
||||
|
||||
const request = () => {
|
||||
const instance = axios.create();
|
||||
|
||||
instance.interceptors.request.use(
|
||||
config => {
|
||||
const { url, params, data, method, headers } = config;
|
||||
if (!params) {
|
||||
config.params = {};
|
||||
}
|
||||
if (!url.includes('auth/users/login')) {
|
||||
let token = {};
|
||||
try {
|
||||
token = JSON.parse(localStorage.token);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
goLogin();
|
||||
}
|
||||
const { accessToken = '' } = token;
|
||||
config.params.accessToken = accessToken;
|
||||
config.headers = Object.assign({}, headers, { accessToken });
|
||||
}
|
||||
if (data && isPlainObject(data) && ['post', 'put'].includes(method)) {
|
||||
config.data = qs.stringify(data);
|
||||
if (!headers) {
|
||||
config.headers = {};
|
||||
}
|
||||
config.headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
||||
}
|
||||
return config;
|
||||
},
|
||||
error => Promise.reject(error)
|
||||
);
|
||||
|
||||
instance.interceptors.response.use(
|
||||
response => {
|
||||
const { success, resultCode, resultMessage = API_GENERAL_ERROR_MESSAGE } = response.data;
|
||||
@ -18,11 +58,24 @@ const request = () => {
|
||||
},
|
||||
error => {
|
||||
if (error.response) {
|
||||
const { data, status } = error.response;
|
||||
Message.error(data && typeof data === 'string' ? data : `HTTP ERROR: ${status}`);
|
||||
} else {
|
||||
Message.error(API_GENERAL_ERROR_MESSAGE);
|
||||
const { data = {}, status } = error.response;
|
||||
let message = `HTTP ERROR: ${status}`;
|
||||
if (typeof data === 'string') {
|
||||
message = data;
|
||||
} else if (typeof data === 'object') {
|
||||
message = data.message;
|
||||
}
|
||||
Message.error(message);
|
||||
|
||||
if (
|
||||
[401, 403].includes(status) &&
|
||||
['unknown user!', 'token invalid', 'token expired!'].includes(message)
|
||||
) {
|
||||
goLogin();
|
||||
}
|
||||
return Promise.reject(error.response);
|
||||
}
|
||||
Message.error(API_GENERAL_ERROR_MESSAGE);
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -18,7 +18,7 @@
|
||||
<parent>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-all</artifactId>
|
||||
<version>1.2.0-SNAPSHOT</version>
|
||||
<version>1.2.0</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
@ -17,6 +17,7 @@ package com.alibaba.nacos.core.auth;
|
||||
|
||||
import com.alibaba.nacos.core.env.ReloadableConfigs;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
@ -70,6 +71,11 @@ public class AuthConfigs {
|
||||
}
|
||||
|
||||
public boolean isAuthEnabled() {
|
||||
// Runtime -D parameter has higher priority:
|
||||
String enabled = System.getProperty("nacos.core.auth.enabled");
|
||||
if (StringUtils.isNotBlank(enabled)) {
|
||||
return BooleanUtils.toBoolean(enabled);
|
||||
}
|
||||
return BooleanUtils.toBoolean(reloadableConfigs.getProperties()
|
||||
.getProperty("nacos.core.auth.enabled", "false"));
|
||||
}
|
||||
|
@ -66,21 +66,22 @@ public class AuthFilter implements Filter {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Loggers.AUTH.isDebugEnabled()) {
|
||||
Loggers.AUTH.debug("auth filter start, request: {}", req.getRequestURI());
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
String path = new URI(req.getRequestURI()).getPath();
|
||||
Method method = methodsCache.getMethod(req.getMethod(), path);
|
||||
|
||||
if (method == null) {
|
||||
throw new NoSuchMethodException();
|
||||
chain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
if (method.isAnnotationPresent(Secured.class) && authConfigs.isAuthEnabled()) {
|
||||
|
||||
if (Loggers.AUTH.isDebugEnabled()) {
|
||||
Loggers.AUTH.debug("auth start, request: {} {}", req.getMethod(), req.getRequestURI());
|
||||
}
|
||||
|
||||
Secured secured = method.getAnnotation(Secured.class);
|
||||
String action = secured.action().toString();
|
||||
String resource = secured.resource();
|
||||
@ -100,12 +101,11 @@ public class AuthFilter implements Filter {
|
||||
}
|
||||
|
||||
} catch (AccessException e) {
|
||||
if (Loggers.AUTH.isDebugEnabled()) {
|
||||
Loggers.AUTH.debug("access denied, request: {} {}, reason: {}", req.getMethod(), req.getRequestURI(), e.getErrMsg());
|
||||
}
|
||||
resp.sendError(HttpServletResponse.SC_FORBIDDEN, e.getErrMsg());
|
||||
return;
|
||||
} catch (NoSuchMethodException e) {
|
||||
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED,
|
||||
"no such api:" + req.getMethod() + ":" + req.getRequestURI());
|
||||
return;
|
||||
} catch (IllegalArgumentException e) {
|
||||
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, ExceptionUtil.getAllExceptionMsg(e));
|
||||
return;
|
||||
|
@ -15,6 +15,8 @@
|
||||
*/
|
||||
package com.alibaba.nacos.core.auth;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
|
||||
/**
|
||||
* Permission to auth
|
||||
*
|
||||
@ -57,4 +59,9 @@ public class Permission {
|
||||
public void setAction(String action) {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JSON.toJSONString(this);
|
||||
}
|
||||
}
|
||||
|
@ -150,7 +150,7 @@ public class StartingSpringApplicationRunListener implements SpringApplicationRu
|
||||
private void logFilePath() {
|
||||
String[] dirNames = new String[]{"logs", "conf", "data"};
|
||||
for (String dirName: dirNames) {
|
||||
LOGGER.info("Nacos Log files: {}{}{}{}", NACOS_HOME, File.separatorChar, dirName, File.separatorChar);
|
||||
LOGGER.info("Nacos {} files: {}{}{}{}", dirName, NACOS_HOME, File.separatorChar, dirName, File.separatorChar);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -104,7 +104,6 @@ else
|
||||
fi
|
||||
|
||||
JAVA_OPT="${JAVA_OPT} -Dloader.path=${BASE_DIR}/plugins/health,${BASE_DIR}/plugins/cmdb,${BASE_DIR}/plugins/mysql"
|
||||
JAVA_OPT="${JAVA_OPT} -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000"
|
||||
JAVA_OPT="${JAVA_OPT} -Dnacos.home=${BASE_DIR}"
|
||||
JAVA_OPT="${JAVA_OPT} -jar ${BASE_DIR}/target/${SERVER}.jar"
|
||||
JAVA_OPT="${JAVA_OPT} ${JAVA_OPT_EXT}"
|
||||
|
@ -43,6 +43,14 @@ server.port=8848
|
||||
# nacos.naming.expireInstance=true
|
||||
|
||||
|
||||
### If enable the empty service auto clean, services with an empty instance are automatically cleared
|
||||
nacos.naming.empty-service.auto-clean=false
|
||||
### The empty service cleanup task delays startup time in milliseconds
|
||||
nacos.naming.empty-service.clean.initial-delay-ms=60000
|
||||
### The empty service cleanup task cycle execution time in milliseconds
|
||||
nacos.naming.empty-service.clean.period-time-ms=20000
|
||||
|
||||
|
||||
#*************** CMDB Module Related Configurations ***************#
|
||||
### The interval to dump external CMDB in seconds:
|
||||
# nacos.cmdb.dumpTaskInterval=3600
|
||||
@ -105,7 +113,7 @@ nacos.core.auth.default.token.expire.seconds=18000
|
||||
nacos.core.auth.default.token.secret.key=SecretKey012345678901234567890123456789012345678901234567890123456789
|
||||
|
||||
### Turn on/off caching of auth information. By turning on this switch, the update of auth information would have a 15 seconds delay.
|
||||
nacos.core.auth.caching.enabled=true
|
||||
nacos.core.auth.caching.enabled=false
|
||||
|
||||
|
||||
#*************** Istio Related Configurations ***************#
|
||||
|
@ -192,12 +192,11 @@ CREATE TABLE `roles` (
|
||||
|
||||
CREATE TABLE `permissions` (
|
||||
`role` varchar(50) NOT NULL,
|
||||
`permission` varchar(512) NOT NULL,
|
||||
`gmt_create` bigint NULL,
|
||||
`gmt_modified` bigint NULL,
|
||||
UNIQUE INDEX `idx_role_resource` (`role` ASC, `permission` ASC) USING BTREE
|
||||
`resource` varchar(512) NOT NULL,
|
||||
`action` varchar(8) NOT NULL,
|
||||
UNIQUE INDEX `uk_role_permission` (`role`,`resource`,`action`) USING BTREE
|
||||
);
|
||||
|
||||
INSERT INTO users (username, password, enabled) VALUES ('nacos', '$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu', TRUE);
|
||||
|
||||
INSERT INTO roles (username, role) VALUES ('nacos', 'GLOBAL_ADMIN');
|
||||
INSERT INTO roles (username, role) VALUES ('nacos', 'ROLE_ADMIN');
|
||||
|
@ -193,4 +193,4 @@ CREATE TABLE permissions (
|
||||
|
||||
INSERT INTO users (username, password, enabled) VALUES ('nacos', '$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu', TRUE);
|
||||
|
||||
INSERT INTO roles (username, role) VALUES ('nacos', 'GLOBAL_ADMIN');
|
||||
INSERT INTO roles (username, role) VALUES ('nacos', 'ROLE_ADMIN');
|
||||
|
@ -18,7 +18,7 @@
|
||||
<parent>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-all</artifactId>
|
||||
<version>1.2.0-SNAPSHOT</version>
|
||||
<version>1.2.0</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
@ -206,6 +206,7 @@
|
||||
<descriptors>
|
||||
<descriptor>release-nacos.xml</descriptor>
|
||||
</descriptors>
|
||||
<tarLongFileMode>posix</tarLongFileMode>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user