拥抱开源-ZrLog

关于

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) 程序猿一只,爱技术,爱闲逛,爱妹子,博客的主要目的在于记录自己遇到的问题,让遇到坑的人能更快的爬出来。同时也记录一些自己工作,学习,生活。正在努力让自己成为一个全栈工程师中。

写博客给我带来了什么??
好记性不如烂笔头

  1. 有些问题google,百度不容易搜到。如果自己恰好通过搜索的方式找到了问题的答案。于是我得记录下,下次也很容易找到问题
  2. 有些问题可能自己一时没有反应过了,可以先简单的记录下,有时间可以去补充完善
  3. 学习了新的知识,记录下
  4. 知识的分享
  5. 记录生活,学习,工作。 不记录下都不知道这一辈子会发生好多事。人老了,还有的看

就是任性

做为程序员的我自己保存自己的数据,自己就能想怎么玩就可以怎么玩。

人生最大的遗憾 莫过于 轻易放弃不该放弃的,固执的坚持不该坚持的。

转载请注明作者和出处,并添加本页链接。
原文链接: xiaochun.zrlog.com/about.html

猜你喜欢

转载自blog.csdn.net/m0_75186659/article/details/130548863