Merge pull request #110 from getrebuild/systems-config-527

RB-527 Systems config
This commit is contained in:
RB 2019-12-23 00:14:26 +08:00 committed by GitHub
commit 5e9d45ab23
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 433 additions and 214 deletions

1
.github/CODEOWNERS vendored Normal file
View file

@ -0,0 +1 @@
* @getrebuild @devezhao

View file

@ -1 +0,0 @@
-- The file has been moved to `src/main/resources/scripts/db-init.sql`

View file

@ -116,7 +116,7 @@
<dependency>
<groupId>com.github.devezhao</groupId>
<artifactId>commons</artifactId>
<version>1.1.5</version>
<version>af6088998f</version>
<exclusions>
<exclusion>
<artifactId>httpclient</artifactId>
@ -127,7 +127,7 @@
<dependency>
<groupId>com.github.devezhao</groupId>
<artifactId>persist4j</artifactId>
<version>1.3.3</version>
<version>1c66159c86</version>
</dependency>
<dependency>
<groupId>junit</groupId>

View file

@ -127,7 +127,7 @@ public final class ServerStatus {
if (!test.exists()) {
return Status.error(name, "Couldn't create file in temp Directory");
} else {
test.delete();
FileUtils.deleteQuietly(test);
}
} catch (Exception ex) {

View file

@ -80,6 +80,10 @@ public class AesPreferencesConfigurer extends PreferencesPlaceholderConfigurer i
props.put("db.url", dbUrl);
}
// MUST NOT BE NULL
setIfEmpty(props, ConfigurableItem.CacheHost, "127.0.0.1");
setIfEmpty(props, ConfigurableItem.CachePort, "16379");
propsHold = (Properties) props.clone();
if (propsHold.getProperty("db.url").contains("jdbc:h2:")) {
@ -108,6 +112,17 @@ public class AesPreferencesConfigurer extends PreferencesPlaceholderConfigurer i
return new Properties();
}
/**
* @param props
* @param item
* @param defaultValue
*/
private void setIfEmpty(Properties props, ConfigurableItem item, String defaultValue) {
if (StringUtils.isBlank(props.getProperty(item.name()))) {
props.put(item.name(), defaultValue);
}
}
/**
* 获取配置项
*

View file

@ -57,14 +57,33 @@ public final class Lisence {
* @return
*/
public static JSON queryAuthority() {
String queryUrl = "https://getrebuild.com/authority/query?k=IjkMHgq94T7s7WkP&sn=" + SN();
JSON result = rbApi("authority/query");
if (result == null) {
result = JSONUtils.toJSONObject(
new String[]{ "sn", "authType" },
new String[]{ SN(), "开源社区版" });
}
return result;
}
/**
* 调用 RB 官方服务 API
*
* @param apiPath
* @return
*/
public static JSON rbApi(String apiPath) {
String apiUrl = "https://getrebuild.com/" + apiPath;
apiUrl += apiPath.contains("\\?") ? "&" : "?";
apiUrl += "k=IjkMHgq94T7s7WkP&sn=" + SN();
try {
String result = CommonsUtils.get(queryUrl);
String result = CommonsUtils.get(apiUrl);
if (StringUtils.isNotBlank(result) && JSONUtils.wellFormat(result)) {
return JSON.parseObject(result);
}
} catch (Exception ignored) {
}
return JSONUtils.toJSONObject(new String[]{"sn", "authType"}, new String[]{SN(), "开源社区版"});
return null;
}
}

View file

@ -30,6 +30,7 @@ import com.qiniu.util.Auth;
import com.qiniu.util.StringMap;
import com.rebuild.server.RebuildException;
import com.rebuild.utils.CommonsUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@ -49,8 +50,12 @@ import java.util.UUID;
public class QiniuCloud {
private static final Log LOG = LogFactory.getLog(QiniuCloud.class);
private final Configuration CONFIGURATION = new Configuration(Region.autoRegion());
/**
* 默认配置
*/
public static final Configuration CONFIGURATION = new Configuration(Region.autoRegion());
private final UploadManager UPLOAD_MANAGER = new UploadManager(CONFIGURATION);
private Auth auth;
@ -70,7 +75,6 @@ public class QiniuCloud {
* @return
*/
public boolean available() {
// return false; // TEST
return this.auth != null;
}
@ -111,7 +115,7 @@ public class QiniuCloud {
try {
return upload(tmp);
} finally {
tmp.delete();
FileUtils.deleteQuietly(tmp);
}
}

View file

@ -122,17 +122,7 @@ public final class SysConfiguration extends KVStorage {
return getsNoUnset(false,
ConfigurableItem.StorageApiKey, ConfigurableItem.StorageApiSecret, ConfigurableItem.StorageBucket, ConfigurableItem.StorageURL);
}
/**
* 缓存账号
*
* @return returns [CacheHost, CachePort, CachePassword]
*/
public static String[] getCacheAccount() {
return getsNoUnset(false,
ConfigurableItem.CacheHost, ConfigurableItem.CachePort, ConfigurableItem.CachePassword);
}
/**
* 邮件账号
*
@ -153,23 +143,30 @@ public final class SysConfiguration extends KVStorage {
ConfigurableItem.SmsUser, ConfigurableItem.SmsPassword, ConfigurableItem.SmsSign);
}
/**
* @return
*/
public static String getHomeUrl() {
return getHomeUrl(null);
}
/**
* 获取绝对 URL
*
* @param path 可带有路径会自动拼接
* @return
*/
public static String getHomeUrl(String...path) {
public static String getHomeUrl(String path) {
String homeUrl = get(ConfigurableItem.HomeURL);
if (!homeUrl.endsWith("/")) {
homeUrl += "/";
}
if (path.length > 0) {
if (path[0].startsWith("/")) {
path[0] = path[0].substring(1);
if (path != null) {
if (path.startsWith("/")) {
path = path.substring(1);
}
return homeUrl + path[0];
return homeUrl + path;
}
return homeUrl;
}

View file

@ -258,7 +258,8 @@ public class Entity2Schema extends Field2Schema {
private boolean schema2Database(Entity entity) {
Dialect dialect = Application.getPersistManagerFactory().getDialect();
Table table = new Table(entity, dialect);
String[] ddls = table.generateDDL(false, false);
String[] ddls = table.generateDDL(false, false, false);
try {
Application.getSQLExecutor().executeBatch(ddls);
} catch (Throwable ex) {

View file

@ -18,19 +18,30 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
package com.rebuild.web.admin;
import com.rebuild.server.Application;
import cn.devezhao.commons.RegexUtils;
import cn.devezhao.commons.ThrowableUtils;
import cn.devezhao.commons.web.ServletUtils;
import com.alibaba.fastjson.JSONObject;
import com.qiniu.common.QiniuException;
import com.qiniu.storage.BucketManager;
import com.qiniu.util.Auth;
import com.rebuild.server.helper.ConfigurableItem;
import com.rebuild.server.helper.Lisence;
import com.rebuild.server.helper.QiniuCloud;
import com.rebuild.server.helper.SysConfiguration;
import com.rebuild.utils.CommonsUtils;
import com.rebuild.web.BasePageControll;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
/**
* 系统配置
@ -51,6 +62,29 @@ public class SysConfigurationControll extends BasePageControll {
}
return mv;
}
@RequestMapping(value = "systems", method = RequestMethod.POST)
public void postSystems(HttpServletRequest request, HttpServletResponse response) throws IOException {
JSONObject data = (JSONObject) ServletUtils.getRequestJson(request);
String dHomeURL = defaultIfBlank(data, ConfigurableItem.HomeURL);
if (!RegexUtils.isUrl(dHomeURL)) {
writeFailure(response, "无效主页地址/域名");
return;
}
String dRecycleBinKeepingDays = defaultIfBlank(data, ConfigurableItem.RecycleBinKeepingDays);
if (!NumberUtils.isNumber(dRecycleBinKeepingDays)) {
data.put(ConfigurableItem.RecycleBinKeepingDays.name(), ConfigurableItem.RecycleBinKeepingDays.getDefaultValue());
}
setValues(data);
// @see ServerListener
request.getServletContext().setAttribute("appName", SysConfiguration.get(ConfigurableItem.AppName));
request.getServletContext().setAttribute("homeUrl", SysConfiguration.get(ConfigurableItem.HomeURL));
writeSuccess(response);
}
@RequestMapping("integration/storage")
public ModelAndView pageIntegrationStorage() {
@ -60,26 +94,64 @@ public class SysConfigurationControll extends BasePageControll {
mv.getModel().put("storageStatus", QiniuCloud.instance().available());
return mv;
}
@RequestMapping("integration/cache")
public ModelAndView pageIntegrationCache() {
ModelAndView mv = createModelAndView("/admin/integration/cache-redis.jsp");
mv.getModel().put("cacheAccount",
starsAccount(SysConfiguration.getCacheAccount(), 2));
mv.getModel().put("cacheStatus", Application.getCommonCache().isUseRedis());
return mv;
}
@RequestMapping(value = "integration/storage", method = RequestMethod.POST)
public void postIntegrationStorage(HttpServletRequest request, HttpServletResponse response) throws IOException {
JSONObject data = (JSONObject) ServletUtils.getRequestJson(request);
String dStorageURL = defaultIfBlank(data, ConfigurableItem.StorageURL);
String dStorageBucket = defaultIfBlank(data, ConfigurableItem.StorageBucket);
String dStorageApiKey = defaultIfBlank(data, ConfigurableItem.StorageApiKey);
String dStorageApiSecret = defaultIfBlank(data, ConfigurableItem.StorageApiSecret);
if (dStorageURL.startsWith("//")) {
dStorageURL = "https:" + dStorageURL;
}
if (!RegexUtils.isUrl(dStorageURL)) {
writeFailure(response, "无效访问域名");
return;
}
try {
// Test
Auth auth = Auth.create(dStorageApiKey, dStorageApiSecret);
BucketManager bucketManager = new BucketManager(auth, QiniuCloud.CONFIGURATION);
bucketManager.getBucketInfo(dStorageBucket);
setValues(data);
writeSuccess(response);
} catch (QiniuException ex) {
writeFailure(response, "无效配置参数 : " + ex.response.error);
} catch (Exception ex) {
writeFailure(response, ThrowableUtils.getRootCause(ex).getLocalizedMessage());
}
}
@RequestMapping("integration/submail")
public ModelAndView pageIntegrationSubmail() {
ModelAndView mv = createModelAndView("/admin/integration/submail.jsp");
mv.getModel().put("smsAccount",
mv.getModel().put("smsAccount",
starsAccount(SysConfiguration.getSmsAccount(), 1));
mv.getModel().put("mailAccount",
starsAccount(SysConfiguration.getMailAccount(), 1));
return mv;
}
@RequestMapping(value = "integration/submail", method = RequestMethod.POST)
public void postIntegrationSubmail(HttpServletRequest request, HttpServletResponse response) throws IOException {
JSONObject data = (JSONObject) ServletUtils.getRequestJson(request);
String dMailAddr = defaultIfBlank(data, ConfigurableItem.MailAddr);
if (!RegexUtils.isEMail(dMailAddr)) {
writeFailure(response, "无效发件人地址");
return;
}
setValues(data);
writeSuccess(response);
}
@RequestMapping("systems/query-authority")
public void queryAuthority(HttpServletResponse response) throws IOException {
writeSuccess(response, Lisence.queryAuthority());
@ -94,4 +166,19 @@ public class SysConfigurationControll extends BasePageControll {
}
return account;
}
private String defaultIfBlank(JSONObject data, ConfigurableItem item) {
return StringUtils.defaultIfBlank(data.getString(item.name()), SysConfiguration.get(item));
}
private void setValues(JSONObject data) {
for (Map.Entry<String, Object> e : data.entrySet()) {
try {
ConfigurableItem item = ConfigurableItem.valueOf(e.getKey());
SysConfiguration.set(item, e.getValue());
} catch (Exception ex) {
LOG.error("Invalid item : " + e.getKey() + " = " + e.getValue());
}
}
}
}

View file

@ -26,10 +26,14 @@ import cn.devezhao.commons.web.ServletUtils;
import cn.devezhao.commons.web.WebUtils;
import cn.devezhao.persist4j.Record;
import cn.devezhao.persist4j.engine.ID;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.rebuild.api.LoginToken;
import com.rebuild.server.Application;
import com.rebuild.server.helper.ConfigurableItem;
import com.rebuild.server.helper.Lisence;
import com.rebuild.server.helper.SMSender;
import com.rebuild.server.helper.SysConfiguration;
import com.rebuild.server.helper.VCode;
import com.rebuild.server.helper.cache.CommonCache;
import com.rebuild.server.metadata.EntityHelper;
@ -304,4 +308,19 @@ public class LoginControll extends BasePageControll {
Application.getSessionStore().clean();
}
}
@RequestMapping("live-wallpaper")
public void getLiveWallpaper(HttpServletRequest request, HttpServletResponse response) throws IOException {
if (!SysConfiguration.getBool(ConfigurableItem.LiveWallpaper)) {
writeFailure(response);
return;
}
JSON ret = Lisence.rbApi("api/misc/bgimg");
if (ret == null) {
writeFailure(response);
} else {
writeSuccess(response, ((JSONObject) ret).getString("url"));
}
}
}

View file

@ -5,16 +5,45 @@
<%@ include file="/_include/Head.jsp"%>
<title>实体管理</title>
<style type="text/css">
.card.entity{position:relative;margin-bottom:20px}
.card.entity:hover{background-color:rgba(255,255,255,.7)}
.card.entity .card-body{padding:12px 20px;color:#333;}
.card.entity .card-body .float-left{width:30px;text-align:center;}
.card.entity .icon{font-size:32px;color:#4285f4;}
.card.entity .badge{position:absolute;top:11px;right:11px}
.card.entity span{margin-top:2px;display:block;}
#entityList{margin:0 -10px}
#entityList>div{padding-left:10px;padding-right:10px}
.card.ph{margin-left:10px;margin-top:0}
.card.entity {
position: relative;
margin-bottom: 20px
}
.card.entity:hover {
background-color: rgba(255, 255, 255, .7)
}
.card.entity .card-body {
padding: 12px 20px;
color: #333;
}
.card.entity .card-body .float-left {
width: 30px;
text-align: center;
}
.card.entity .icon {
font-size: 32px;
color: #4285f4;
}
.card.entity .badge {
position: absolute;
top: 11px;
right: 11px
}
.card.entity span {
margin-top: 2px;
display: block;
}
#entityList {
margin: 0 -10px
}
#entityList > div {
padding-left: 10px;
padding-right: 10px
}
.card.ph {
margin-left: 10px;
margin-top: 0
}
</style>
</head>
<body>
@ -26,7 +55,7 @@
<jsp:param value="entities" name="activeNav"/>
</jsp:include>
<div class="rb-content">
<div class="main-content container-fluid">
<div class="main-content container-fluid" style="padding-bottom:5px">
<div class="row" id="entityList">
<div class="card ph">
<div class="card-body">

View file

@ -1,57 +0,0 @@
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html>
<head>
<%@ include file="/_include/Head.jsp"%>
<title>缓存系统配置</title>
</head>
<body>
<div class="rb-wrapper rb-fixed-sidebar rb-collapsible-sidebar rb-collapsible-sidebar-hide-logo rb-color-header">
<jsp:include page="/_include/NavTop.jsp">
<jsp:param value="缓存系统配置" name="pageTitle"/>
</jsp:include>
<jsp:include page="/_include/NavLeftAdmin.jsp">
<jsp:param value="integration-cache" name="activeNav"/>
</jsp:include>
<div class="rb-content">
<div class="main-content container-fluid syscfg">
<div class="row">
<div class="col-md-9 col-12">
<div class="card">
<div class="card-header card-header-divider">缓存系统</div>
<div class="card-body">
<h5><a class="cl-base" href="https://redis.io/?utm_source=getrebuild.com" target="_blank" rel="noopener noreferrer">Redis</a></h5>
<table class="table">
<tbody>
<tr>
<td width="40%">缓存服务器</td>
<td>
<c:if test="${cacheAccount == null}">未配置</c:if>
<c:if test="${cacheAccount != null}">${cacheAccount[0]}:${cacheAccount[1]}</c:if>
</td>
</tr>
<tr>
<td>访问秘钥</td>
<td>${cacheAccount == null ? "未配置" : cacheAccount[2]}</td>
</tr>
</tbody>
</table>
<c:if test="${!cacheStatus}">
<div class="alert alert-warning alert-icon mt-6">
<div class="icon"><span class="zmdi zmdi-alert-triangle"></span></div>
<div class="message">REDIS 未配置或配置有误,当前已启用 EHCACHE 内建缓存</div>
</div>
</c:if>
</div>
</div>
</div>
<div class="col-md-3 col-12">
</div>
</div>
</div>
</div>
</div>
<%@ include file="/_include/Foot.jsp"%>
</body>
</html>

View file

@ -19,26 +19,29 @@
<div class="row">
<div class="col-md-9 col-12">
<div class="card">
<div class="card-header card-header-divider">云存储</div>
<div class="card-header card-header-divider">
云存储
<a href="#modfiy" class="float-right"><i class="icon zmdi zmdi-edit"></i> 修改</a>
</div>
<div class="card-body">
<h5><a class="cl-base" href="https://portal.qiniu.com/signup?utm_source=getrebuild.com&code=3letk048wdsnm" target="_blank" rel="noopener noreferrer">七牛云</a></h5>
<table class="table">
<tbody>
<tr>
<td width="40%">访问域名</td>
<td>${storageAccount == null ? "未配置" : storageAccount[3]}</td>
<td data-id="StorageURL">${storageAccount == null ? "未配置" : storageAccount[3]}</td>
</tr>
<tr>
<td>存储空间</td>
<td>${storageAccount == null ? "未配置" : storageAccount[2]}</td>
<td data-id="StorageBucket">${storageAccount == null ? "未配置" : storageAccount[2]}</td>
</tr>
<tr>
<td>秘钥 AK</td>
<td>${storageAccount == null ? "未配置" : storageAccount[0]}</td>
<td data-id="StorageApiKey">${storageAccount == null ? "未配置" : storageAccount[0]}</td>
</tr>
<tr>
<td>秘钥 SK</td>
<td>${storageAccount == null ? "未配置" : storageAccount[1]}</td>
<td data-id="StorageApiSecret">${storageAccount == null ? "未配置" : storageAccount[1]}</td>
</tr>
</tbody>
</table>
@ -48,6 +51,10 @@
<div class="message">七牛云存储账户未配置或配置有误,当前已启用本地文件存储</div>
</div>
</c:if>
<div class="edit-footer">
<button class="btn btn-link">取消</button>
<button class="btn btn-primary">保存</button>
</div>
</div>
</div>
</div>
@ -58,5 +65,6 @@
</div>
</div>
<%@ include file="/_include/Foot.jsp"%>
<script src="${baseUrl}/assets/js/admin/syscfg.jsx" type="text/babel"></script>
</body>
</html>

View file

@ -19,66 +19,65 @@
<div class="row">
<div class="col-md-9 col-12">
<div class="card">
<div class="card-header card-header-divider"><a class="cl-base" href="https://www.mysubmail.com/?utm_source=getrebuild.com" target="_blank" rel="noopener noreferrer">赛邮 SUBMAIL</a></div>
<div class="card-header card-header-divider">
赛邮 SUBMAIL
<a href="#modfiy" class="float-right"><i class="icon zmdi zmdi-edit"></i> 修改</a>
</div>
<div class="card-body">
<h5>邮件服务</h5>
<c:choose>
<c:when test="${mailAccount != null}">
<table class="table">
<tbody>
<tr>
<td width="40%">APPID</td>
<td>${mailAccount[0]}</td>
</tr>
<tr>
<td>APPKEY</td>
<td>${mailAccount[1]}</td>
</tr>
<tr>
<td>发件人地址</td>
<td>${mailAccount[2]}</td>
</tr>
<tr>
<td>发件人名称</td>
<td>${mailAccount[3]}</td>
</tr>
</tbody>
</table>
</c:when>
<c:otherwise>
<div class="alert alert-danger alert-icon mt-6 mb-6">
<div class="icon"><span class="zmdi zmdi-close-circle-o"></span></div>
<div class="message">邮件账号未配置,邮件相关功能不可用</div>
</div>
</c:otherwise>
</c:choose>
<table class="table">
<tbody>
<tr>
<td width="40%">APPID</td>
<td data-id="MailUser">${mailAccount == null ? "未配置" : mailAccount[0]}</td>
</tr>
<tr>
<td>APPKEY</td>
<td data-id="MailPassword">${mailAccount == null ? "未配置" : mailAccount[1]}</td>
</tr>
<tr>
<td>发件人地址</td>
<td data-id="MailAddr">${mailAccount == null ? "未配置" : mailAccount[2]}</td>
</tr>
<tr>
<td>发件人名称</td>
<td data-id="MailName">${mailAccount == null ? "未配置" : mailAccount[3]}</td>
</tr>
</tbody>
</table>
<c:if test="${mailAccount == null}">
<div class="alert alert-danger alert-icon mt-6 mb-6">
<div class="icon"><span class="zmdi zmdi-close-circle-o"></span></div>
<div class="message">邮件账号未配置,邮件相关功能不可用</div>
</div>
</c:if>
<h5>短信服务</h5>
<c:choose>
<c:when test="${smsAccount != null}">
<table class="table">
<tbody>
<tr>
<td width="40%">APPID</td>
<td>${smsAccount[0]}</td>
</tr>
<tr>
<td>APPKEY</td>
<td>${smsAccount[1]}</td>
</tr>
<tr>
<td>短信签名</td>
<td>${smsAccount[2]}</td>
</tr>
</tbody>
</table>
</c:when>
<c:otherwise>
<div class="alert alert-danger alert-icon mt-6">
<div class="icon"><span class="zmdi zmdi-close-circle-o"></span></div>
<div class="message">短信账号未配置,短信相关功能不可用</div>
</div>
</c:otherwise>
</c:choose>
<table class="table">
<tbody>
<tr>
<td width="40%">APPID</td>
<td data-id="SmsUser">${smsAccount == null ? "未配置" : smsAccount[0]}</td>
</tr>
<tr>
<td>APPKEY</td>
<td data-id="SmsPassword">${smsAccount == null ? "未配置" : smsAccount[1]}</td>
</tr>
<tr>
<td>短信签名</td>
<td data-id="SmsSign">${smsAccount == null ? "未配置" : smsAccount[2]}</td>
</tr>
</tbody>
</table>
<c:if test="${smsAccount == null}">
<div class="alert alert-danger alert-icon mt-6">
<div class="icon"><span class="zmdi zmdi-close-circle-o"></span></div>
<div class="message">短信账号未配置,短信相关功能不可用</div>
</div>
</c:if>
<div class="edit-footer">
<button class="btn btn-link">取消</button>
<button class="btn btn-primary">保存</button>
</div>
</div>
</div>
</div>
@ -89,5 +88,6 @@
</div>
</div>
<%@ include file="/_include/Foot.jsp"%>
<script src="${baseUrl}/assets/js/admin/syscfg.jsx" type="text/babel"></script>
</body>
</html>

View file

@ -7,11 +7,17 @@
<%@ include file="/_include/Head.jsp"%>
<title>通用配置</title>
<style type="text/css">
.syscfg a.img-thumbnail{display:inline-block;padding:0.3rem 0;background-color:#fff;line-height:1;font-size:0;cursor:default;}
.syscfg a.img-thumbnail .logo-img{transform: scale(0.8);}
.syscfg h5{background-color:#eee;margin:0;padding:10px;}
.syscfg .table td{padding:10px;}
.syscfg .table td p{margin:0;color:#999;font-weight:normal;font-size:12px;}
.syscfg a.img-thumbnail {
display: inline-block;
padding: 0.3rem 0;
background-color: #fff;
line-height: 1;
font-size: 0;
cursor: default;
}
.syscfg a.img-thumbnail .logo-img {
transform: scale(0.8);
}
</style>
</head>
<body>
@ -27,14 +33,17 @@
<div class="row">
<div class="col-lg-9 col-12">
<div class="card">
<div class="card-header card-header-divider">通用配置</div>
<div class="card-header card-header-divider">
通用配置
<a href="#modfiy" class="float-right"><i class="icon zmdi zmdi-edit"></i> 修改</a>
</div>
<div class="card-body">
<h5>通用</h5>
<table class="table">
<tbody>
<tr>
<td width="40%">名称</td>
<td>${appName}</td>
<td data-id="AppName">${appName}</td>
</tr>
<tr>
<td>LOGO</td>
@ -45,15 +54,15 @@
</tr>
<tr>
<td>主页地址/域名<p>基础 URL 所有链接将以此作为前缀</p></td>
<td><a href="${HomeURL}" class="link" target="_blank">${HomeURL}</a></td>
<td data-id="HomeURL">${HomeURL}</td>
</tr>
<tr>
<td>公开注册</td>
<td>${OpenSignUp ? "是" : "否"}</td>
<td data-id="OpenSignUp" data-options="true:是;false:否">${OpenSignUp ? "是" : "否"}</td>
</tr>
<tr>
<td>登录页每日一图</td>
<td>${LiveWallpaper ? "是" : "否"}</td>
<td data-id="LiveWallpaper" data-options="true:是;false:否">${LiveWallpaper ? "是" : "否"}</td>
</tr>
</tbody>
</table>
@ -62,7 +71,7 @@
<tbody>
<tr>
<td width="40%">登录密码安全策略</td>
<td>
<td data-id="PasswordPolicy" data-options="1:低;2:中;3:高">
<c:choose>
<c:when test="${PasswordPolicy >= 3}">高 (最低8位必须同时包含数字、字母、特殊字符)</c:when>
<c:when test="${PasswordPolicy == 2}">中 (最低6位必须同时包含数字、字母)</c:when>
@ -71,11 +80,15 @@
</td>
</tr>
<tr>
<td>回收站数据保留时间</td>
<td>${RecycleBinKeepingDays}</td>
<td>回收站数据保留天数</td>
<td data-id="RecycleBinKeepingDays">${RecycleBinKeepingDays}</td>
</tr>
</tbody>
</table>
<div class="edit-footer">
<button class="btn btn-link">取消</button>
<button class="btn btn-primary">保存</button>
</div>
</div>
</div>
</div>
@ -101,6 +114,7 @@
</div>
</div>
<%@ include file="/_include/Foot.jsp"%>
<script src="${baseUrl}/assets/js/admin/syscfg.jsx" type="text/babel"></script>
<script>
$(document).ready(function () {
$.get('systems/query-authority', function (res) { $('#authType').text(res.data.authType) })

View file

@ -3092,6 +3092,31 @@ a.icon-link>.zmdi {
font-size: 12px;
}
.syscfg .card {
margin-bottom: 0;
}
.syscfg .card .card-header-divider>a {
font-size: 1rem;
margin-top: 4px;
display: none;
}
.syscfg .card:hover .card-header-divider>a {
display: inline-block;
}
.syscfg .edit-footer {
background-color: #eee;
padding: 10px;
text-align: right;
display: none;
}
.syscfg.edit .edit-footer {
display: block;
}
.announcement-wrapper>div {
margin: 0;
border-radius: 0;

View file

@ -0,0 +1,58 @@
// -
$(document).ready(() => {
$('.card-header-divider>a').click((e) => {
e.preventDefault()
editMode()
})
$('.edit-footer>.btn-link').click(() => location.reload())
$('.edit-footer>.btn-primary').click(() => post(__data))
})
let __data = {}
const changeValue = function (e) {
let name = e.target.name
__data[name] = e.target.value
}
//
const editMode = function () {
$('.syscfg table td[data-id]').each(function () {
let $item = $(this)
let val = $item.text()
if (val === '未配置') val = ''
let name = $item.data('id')
let options = $item.data('options')
if (options) {
options = options.split(';')
let comp = <select name={name} className="form-control form-control-sm" onChange={changeValue}>
{options.map((item) => {
let kv = item.split(':')
return <option value={kv[0]} key={kv[0]}>{kv[1]}</option>
})}
</select>
renderRbcomp(comp, $item)
} else {
renderRbcomp(<input defaultValue={val} name={name} className="form-control form-control-sm" onChange={changeValue} />, $item)
}
})
$('.syscfg').addClass('edit')
}
//
const post = function (data) {
for (let k in data) {
if (!data[k]) {
let field = $('td[data-id=' + k + ']').prev().text()
RbHighbar.create(field + '不能为空')
return false
}
}
const btn = $('.edit-footer>.btn-primary').button('loading')
$.post(location.href, JSON.stringify(data), (res) => {
btn.button('reset')
if (res.error_code === 0) location.reload()
else RbHighbar.error(res.error_msg)
})
}

View file

@ -19,15 +19,15 @@
<style type="text/css">
.block{max-width:1000px;padding:0 14px;margin:30px auto 0;}
.error{background-color:#ea4335;color:#fff;padding:18px 0;}
.error a{color:#fff;text-decoration:underline;}
.error a{color:#fff}
</style>
</head>
<body>
<% if (!ServerStatus.isStatusOK()) { %>
<% if (!ServerStatus.isStatusOK() || !Application.serversReady()) { %>
<div class="error">
<div class="block mt-0">
<h2 class="mt-0">系统故障</h2>
<div>部分服务未能正常启动,请通过快速检查列表排除故障,故障排除后重启服务。你也可以获取 <a href="https://getrebuild.com/">技术支持</a></div>
<div>服务未能正常启动,请通过快速检查列表排除故障,故障排除后请重启服务。你也可以 <a href="https://getrebuild.com/report-issue?title=boot-error" target="_blank">报告此问题</a></div>
</div>
</div>
<% } %>
@ -35,9 +35,13 @@
<h5 class="text-bold">快速检查</h5>
<table class="table table-bordered table-sm table-hover">
<tbody>
<tr>
<th width="30%">Master Service</th>
<td class="text-danger"><%=Application.serversReady() ? "<span class='text-success'>OK<span>" : "启动失败"%></td>
</tr>
<% for (Status s : ServerStatus.getLastStatus()) { %>
<tr>
<th width="30%"><%=s.name%></th>
<th><%=s.name%></th>
<td class="text-danger"><%=s.success ? "<span class='text-success'>OK<span>" : ("ERROR : " + s.error)%></td>
</tr>
<% } %>
@ -92,7 +96,7 @@
<% } %>
<div class="block">
<div class="text-muted">
&copy; 2019 <a href="https://getrebuild.com/?utm_source=rebuild">REBUILD</a>
&copy; 2020 <a href="https://getrebuild.com/?utm_source=rebuild">REBUILD</a>
<% if (AppUtils.getRequestUser(request) != null) { %>
&nbsp;·&nbsp;
<a href="server-status.json">Status Api</a>

View file

@ -119,9 +119,6 @@
</div>
<%@ include file="/_include/Foot.jsp"%>
<script src="${baseUrl}/assets/js/feeds/announcement.jsx" type="text/babel"></script>
<script>
useLiveWallpaper = <%=SysConfiguration.getBool(ConfigurableItem.LiveWallpaper)%>
</script>
<script type="text/babel">
$(document).ready(function() {
if (top != self) { parent.location.reload(); return }
@ -161,20 +158,15 @@ $(document).ready(function() {
})
})
if (useLiveWallpaper) {
$.get('https://getrebuild.com/api/misc/bgimg?k=IjkMHgq94T7s7WkP', (res) => {
if (!res.url) return
let bgimg = new Image()
bgimg.src = res.url
bgimg.onload = function() {
$('.rb-bgimg').animate({ opacity: 0 })
setTimeout(() => {
$('.rb-bgimg').css('background-image', 'url(' + res.url + ')').animate({ opacity: 1 })
}, 400)
if (res.copyright) $('.rb-bgimg').attr('alt', res.copyright + ' (' + res.source + ')')
}
}).fail(function () { /* NOOP */ })
}
$.get('live-wallpaper', (res) => {
if (res.error_code != 0 || !res.data) return
let bgimg = new Image()
bgimg.src = res.data
bgimg.onload = function() {
$('.rb-bgimg').animate({ opacity: 0 })
setTimeout(() => $('.rb-bgimg').css('background-image', 'url(' + res.data + ')').animate({ opacity: 1 }), 400)
}
})
})
</script>
</body>

View file

@ -35,12 +35,11 @@ import org.springframework.context.support.ClassPathXmlApplicationContext;
* @since 0.2, 2014-4-10
*/
public class SchemaGen {
private static ApplicationContext CTX;
private static PersistManagerFactory PMF;
private static boolean DROP_EXISTS = false;
private static boolean TEMPSTAMP_ZERO = false;
public static void main(String[] args) {
CTX = new ClassPathXmlApplicationContext(new String[] { "application-ctx.xml", });
@ -51,13 +50,21 @@ public class SchemaGen {
System.exit(0);
}
static void genAll() {
/**
* 生成全部实体
*/
static void genAll() {
for (Entity entity : PMF.getMetadataFactory().getEntities()) {
gen(entity.getEntityCode());
}
}
/**
* 生成指定实体
*
* @param e
*/
static void gen(int e) {
Entity entity = PMF.getMetadataFactory().getEntity(e);
Element root = ((ConfigurationMetadataFactory) PMF.getMetadataFactory()).getConfigDocument().getRootElement();
@ -65,15 +72,12 @@ public class SchemaGen {
entity,
PMF.getDialect(),
root.selectSingleNode("//entity[@name='" + entity.getName() + "']").selectNodes("index"));
String[] ddl = table.generateDDL(DROP_EXISTS, false);
String[] ddl = table.generateDDL(DROP_EXISTS, false, false);
StringBuffer sb = new StringBuffer();
sb.append("-- ************ Entity [" + entity.getName() + "] DDL ************\n");
for (String d : ddl) {
if (!TEMPSTAMP_ZERO) {
d = d.replace(" default '0000-00-00 00:00:00'", " default current_timestamp");
}
sb.append(d).append("\n");
}
System.out.println(sb);