关于
ZrLog是使用 Java 开发的博客/CMS程序,具有简约,易用,组件化,内存占用低等特点。自带 Markdown 编辑器,让更多的精力放在写作上,而不是花费大量时间在学习程序的使用上。
ZrLog(Zero Record Log,从0记录日志),旨在希望记录人生琐事,当自己有空闲时间的时候可以通过Zrlog回味自己人生经历。 知识的载体原本是书,但是当今社会还有多少人会去读书呢?互联网的诞生,让人类获取知识的途径变得更加丰富。社交网站的诞生,让人们花费了大量的时间精力。
项目地址:https://github.com/94fzb/zrlog
官网:https://www.zrlog.com/
部署
原始war包
通过官网下载war包,放到Tomcat的webapps目录下,访问页面,按照提示完善信息安装即可使用
注意Tomcat版本,过低可能会导致页面空白
自行打包
通过项目地址下载源码,在
在common/src/main/resources/下添加build.properties,配置版本信息,如:
version=2.2.4-SNAPSHOT
runMode=preview
#commit id
buildId=8851ba8
buildTime=2023-04-29 02:12:00+00:00
补充
字符编码,不使用添加系统环境变量的方式,而是在Tomcat配置,避免覆盖配置导致idea异常
使用
管理
默认首页中管理链接路径为根路径,如果项目部署在非根目录,可通过添加路径访问管理员界面更改链接地址
更新
更新策略
管理员界面可以检查更新,配置更新策略
第一次访问可以正常更新,后面更新时更新时界面闪烁,更新包已下载,但是不能访问执行更新页面执行更新操作,各番探索,找到执行更新链接
localhost:8080/api/admin/upgrade/doUpgrade
进行更新。
更新过程解析
/**
* 更新Zrlog,具体的流程看 run() 里面有详细流程。本质就是合成新war包,然后替换掉war包。
*/
public class WarUpdateVersionThread extends Thread implements Serializable, UpdateVersionHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(WarUpdateVersionThread.class);
private final File file;
private final StringBuilder sb = new StringBuilder();
private boolean finish;
private final File tempFilePath;
public WarUpdateVersionThread(File file) {
this.file = file;
tempFilePath = new File(System.getProperty("java.io.tmpdir") + File.separator + UUID.randomUUID());
tempFilePath.mkdirs();
}
private void updateProcessMsg(String str) {
LOGGER.info(str);
sb.append("<p>").append(str).append("</p>");
}
private void updateProcessErrorMsg(Throwable e) {
LOGGER.error("", e);
sb.append("<pre style='color:red'>").append(ExceptionUtils.recordStackTraceMsg(e)).append("</pre>");
}
@Override
public void run() {
try {
String warName = getWarNameAndBackup();
File upgradeWarFile = generatorUpgradeWarFile(warName);
doUpgrade(warName, upgradeWarFile);
} catch (Exception e) {
LOGGER.error("", e);
updateProcessErrorMsg(e);
} finally {
FileUtils.deleteFile(tempFilePath.toString());
}
finish = true;
}
private void doUpgrade(String warName, File upgradeWarFile) {
File currentRunWarFile = new File(new File(PathKit.getWebRootPath()).getParentFile() + warName);
currentRunWarFile.delete();
FileUtils.moveOrCopyFile(upgradeWarFile.toString(), currentRunWarFile.toString(), true);
updateProcessMsg("覆盖更新包 " + currentRunWarFile);
}
private File generatorUpgradeWarFile(String warName) throws IOException {
ZipUtil.unZip(file.toString(), tempFilePath + File.separator);
updateProcessMsg("解压安装包 " + file);
Map<String, String> copyFileMap = new LinkedHashMap<>();
copyFileMap.put(PathKit.getWebRootPath() + "/WEB-INF/db.properties", tempFilePath + "/WEB-INF/");
copyFileMap.put(PathKit.getWebRootPath() + "/WEB-INF/install.lock", tempFilePath + "/WEB-INF/");
copyFileMap.put(PathKit.getWebRootPath() + Constants.ATTACHED_FOLDER, tempFilePath.toString());
copyFileMap.put(PathKit.getWebRootPath() + "/favicon.ico", tempFilePath.toString());
copyFileMap.put(PathKit.getWebRootPath() + "/error", tempFilePath.toString());
fillTemplateCopyInfo(tempFilePath, copyFileMap);
doCopy(copyFileMap);
List<File> fileList = new ArrayList<>();
fileList.add(tempFilePath);
File tempWarFile = new File(tempFilePath.getParent() + File.separator + warName);
JarPackageUtil.inJar(fileList, tempFilePath + File.separator, tempWarFile.toString());
updateProcessMsg("合成更新包 " + tempWarFile);
return tempWarFile;
}
private void fillTemplateCopyInfo(File tempFilePath, Map<String, String> copyFileMap) {
File templatePath = new File(PathKit.getWebRootPath() + Constants.TEMPLATE_BASE_PATH);
File[] templates = templatePath.listFiles();
if (templates != null && templates.length > 0) {
for (File template : templates) {
if (template.isDirectory() && template.toString().substring(PathKit.getWebRootPath().length()).startsWith(Constants.DEFAULT_TEMPLATE_PATH)) {
//skip default template folder
continue;
}
copyFileMap.put(template.toString(), tempFilePath + File.separator + Constants.TEMPLATE_BASE_PATH);
}
}
}
private void doCopy(Map<String, String> pathMap) {
for (Map.Entry<String, String> entry : pathMap.entrySet()) {
if (new File(entry.getKey()).exists()) {
FileUtils.moveOrCopyFolder(entry.getKey(), entry.getValue(), false);
}
}
}
private String getWarNameAndBackup() {
String warName;
String contextPath = JFinal.me().getServletContext().getContextPath();
if ("/".equals(contextPath) || "".equals(contextPath)) {
warName = "/ROOT.war";
} else {
warName = contextPath + ".war";
}
String backupFolder = new File(PathKit.getWebRootPath()).getParentFile().getParentFile() + File.separator + "backup" + File.separator + new SimpleDateFormat("yyyy-MM-dd_HH_mm").format(new Date()) + File.separator;
new File(backupFolder).mkdirs();
FileUtils.moveOrCopyFolder(PathKit.getWebRootPath(), backupFolder, false);
String warPath = new File(PathKit.getWebRootPath()).getParent() + File.separator + warName;
if (new File(warPath).exists()) {
FileUtils.moveOrCopyFolder(warPath, backupFolder, false);
}
updateProcessMsg("备份当前版本到 " + backupFolder);
return warName;
}
/**
* 提示更新进度
*
* @return
*/
@Override
public String getMessage() {
return sb.toString();
}
@Override
public boolean isFinish() {
return finish;
}
}
可以看到更新会保留db.properties
install.lock
ATTACHED_FOLDER
favicon.ico
error
作者使用自动化工具完成构建、打包及部署,这个过程还需要进一步探索
功能
备份
项目以插件方式支持定时备份数据库数据
界面管理
导航栏自定义
文章发布
默认的Markdown渲染界面有点简洁(editor.md),想整合Md2All进项目,但最终还是放弃了,,,改用自行排版再粘贴,但是会加上代码行号。可以通过修改源码关闭行号,但想着更新等等就放弃了,因为数据库存储Markdown内容,纯文本内容,HTML内容,所以打算后期直接修改数据库的方式去除行号。
还可以自定义分类、标签、封面摘要等信息。
最后
以简单的方式实现想法,别死磕,别浪费太多时间,可以灵活变通,拥抱开源,总结记录进步。
好记性不如烂笔头,记录才不会遗忘所走过的路。
再去看官网发现一段话(真好记性不如烂笔头,哈哈哈):
为什么要写博客
/2012-12-10
小春博客(blog) 程序猿一只,爱技术,爱闲逛,爱妹子,博客的主要目的在于记录自己遇到的问题,让遇到坑的人能更快的爬出来。同时也记录一些自己工作,学习,生活。正在努力让自己成为一个全栈工程师中。
写博客给我带来了什么??
好记性不如烂笔头
- 有些问题google,百度不容易搜到。如果自己恰好通过搜索的方式找到了问题的答案。于是我得记录下,下次也很容易找到问题
- 有些问题可能自己一时没有反应过了,可以先简单的记录下,有时间可以去补充完善
- 学习了新的知识,记录下
- 知识的分享
- 记录生活,学习,工作。 不记录下都不知道这一辈子会发生好多事。人老了,还有的看
就是任性
做为程序员的我自己保存自己的数据,自己就能想怎么玩就可以怎么玩。
人生最大的遗憾 莫过于 轻易放弃不该放弃的,固执的坚持不该坚持的。
转载请注明作者和出处,并添加本页链接。
原文链接: xiaochun.zrlog.com/about.html