dev #17
24
DEPLOY.md
24
DEPLOY.md
@ -1,12 +1,20 @@
|
||||
本程序基于 docker 来进行部署,使用 docker-compose 管理服务。
|
||||
|
||||
部署过程如下:
|
||||
**注意,仅在 x86 环境下测试,arm 下不保证可用性(目前测试可用)**
|
||||
|
||||
**注意,仅在 x86 环境下测试,arm下不保证可用性(目前测试可用)**
|
||||
## 首次部署
|
||||
|
||||
1. 安装新版的 docker,docker-compose,zip(注意:以下操作均在项目根目录下执行)
|
||||
2. 修改.env 文件中的参数,改为你的实际配置
|
||||
3. 修改`浏览器插件/bookmarkBrowserPlugin/static/js/config.js`中的 bookmarkHost,改为你的实际部署路径
|
||||
4. 修改`浏览器插件/bookmarkBrowserPlugin/tab/index.html`中的`<meta http-equiv="Refresh" content="0;url=https://bm.fleyx.com" />`,将 url 改为你的实际部署地址
|
||||
5. 执行`build.sh`编译前后端代码
|
||||
6. root 权限运行 `docker-compose up -d` 启动服务。
|
||||
0. 克隆代码`git clone https://github.com/FleyX/bookmark.git`
|
||||
1. 进入文件夹`cd bookmark`
|
||||
2. 安装新版的 docker,docker-compose,zip `apt install docker docker-compose zip`
|
||||
3. 修改.env 文件中的参数,改为你的实际配置
|
||||
4. 修改`浏览器插件/bookmarkBrowserPlugin/static/js/config.js`中的 bookmarkHost,改为你的实际部署路径
|
||||
5. 修改`浏览器插件/bookmarkBrowserPlugin/tab/index.html`中的`<meta http-equiv="Refresh" content="0;url=https://bm.fleyx.com" />`,将 url 改为你的实际部署地址
|
||||
6. 执行`build.sh`编译前后端代码 `bash build.sh`
|
||||
7. root 权限运行 `docker-compose up -d` 启动服务。
|
||||
|
||||
## 更新系统
|
||||
|
||||
0. 代码库更新`cd bookmark;git pull`
|
||||
1. 执行`build.sh`编译前后端代码 `bash build.sh`
|
||||
2. root 权限运行 `docker-compose restart` 启动服务
|
||||
|
@ -29,6 +29,11 @@
|
||||
<artifactId>pinyin</artifactId>
|
||||
<version>0.3.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xerial</groupId>
|
||||
<artifactId>sqlite-jdbc</artifactId>
|
||||
<version>3.44.1.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
|
@ -69,7 +69,7 @@ public class BookmarkController {
|
||||
*/
|
||||
@RequestMapping("/uploadBookmarkFile")
|
||||
public Result uploadFile(@RequestParam("file") MultipartFile file, @RequestParam("path") String path) throws Exception {
|
||||
bookmarkService.parseBookmarkFile(UserContextHolder.get().getUserId(), file.getInputStream(), path);
|
||||
bookmarkService.parseBookmarkFile(UserContextHolder.get().getUserId(), file, path);
|
||||
return Result.success(null);
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@ package com.fanxb.bookmark.business.bookmark.service;
|
||||
import com.fanxb.bookmark.business.bookmark.entity.BookmarkEs;
|
||||
import com.fanxb.bookmark.business.bookmark.entity.MoveNodeBody;
|
||||
import com.fanxb.bookmark.common.entity.po.Bookmark;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
@ -54,7 +55,7 @@ public interface BookmarkService {
|
||||
* @author fanxb
|
||||
* @date 2019/7/9 18:44
|
||||
*/
|
||||
void parseBookmarkFile(int userId, InputStream stream, String path) throws Exception;
|
||||
void parseBookmarkFile(int userId, MultipartFile file, String path) throws Exception;
|
||||
|
||||
/**
|
||||
* Description: 详情
|
||||
|
@ -2,10 +2,8 @@ package com.fanxb.bookmark.business.bookmark.service.impl;
|
||||
|
||||
import cn.hutool.core.codec.Base64Decoder;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.*;
|
||||
import cn.hutool.core.util.HashUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.fanxb.bookmark.business.api.UserApi;
|
||||
import com.fanxb.bookmark.business.bookmark.constant.FileConstant;
|
||||
@ -38,7 +36,9 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.awt.print.Book;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
@ -49,6 +49,10 @@ import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.Statement;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ -81,18 +85,25 @@ public class BookmarkServiceImpl implements BookmarkService {
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void parseBookmarkFile(int userId, InputStream stream, String path) throws Exception {
|
||||
Document doc = Jsoup.parse(stream, "utf-8", "");
|
||||
Elements elements = doc.select("html>body>dl>dt");
|
||||
public void parseBookmarkFile(int userId, MultipartFile file, String path) throws Exception {
|
||||
List<Bookmark> bookmarks = new ArrayList<>();
|
||||
//获取当前层sort最大值
|
||||
Integer sortBase = bookmarkDao.selectMaxSort(userId, path);
|
||||
if (sortBase == null) {
|
||||
sortBase = 0;
|
||||
}
|
||||
List<Bookmark> bookmarks = new ArrayList<>();
|
||||
if (file.getOriginalFilename().endsWith(".db3")) {
|
||||
//处理db文件
|
||||
readFromOneEnv(bookmarks, userId, file, path, sortBase);
|
||||
} else {
|
||||
InputStream stream = file.getInputStream();
|
||||
Document doc = Jsoup.parse(stream, "utf-8", "");
|
||||
Elements elements = doc.select("html>body>dl>dt");
|
||||
for (int i = 0, length = elements.size(); i < length; i++) {
|
||||
dealBookmark(userId, elements.get(i), path, sortBase + i, bookmarks);
|
||||
}
|
||||
}
|
||||
|
||||
//每一千条处理插入一次,批量更新搜索字段
|
||||
List<Bookmark> tempList = new ArrayList<>(1000);
|
||||
for (int i = 0; i < bookmarks.size(); i++) {
|
||||
@ -151,6 +162,57 @@ public class BookmarkServiceImpl implements BookmarkService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理oneenv的导出
|
||||
*
|
||||
* @param bookmarks 书签列表
|
||||
* @param userId 用户id
|
||||
* @param file file
|
||||
* @param path path
|
||||
* @param sort sort
|
||||
*/
|
||||
private void readFromOneEnv(List<Bookmark> bookmarks, int userId, MultipartFile file, String path, int sort) {
|
||||
String filePath = CommonConstant.fileSavePath + "/files/" + IdUtil.simpleUUID() + ".db3";
|
||||
try {
|
||||
file.transferTo(FileUtil.newFile(filePath));
|
||||
try (Connection conn = DriverManager.getConnection("jdbc:sqlite:" + filePath)) {
|
||||
Statement stat = conn.createStatement();
|
||||
ResultSet rs = stat.executeQuery("select * from on_categorys");
|
||||
Map<Long, Bookmark> folderMap = new HashMap<>();
|
||||
Map<Long, Integer> childSortBaseMap = new HashMap<>();
|
||||
while (rs.next()) {
|
||||
long addTime = rs.getLong("add_time");
|
||||
Bookmark folder = new Bookmark(userId, path, StrUtil.nullToEmpty(rs.getString("name")), addTime == 0 ? System.currentTimeMillis() : addTime * 1000, sort++);
|
||||
int childSortBase = 0;
|
||||
if (insertOne(folder)) {
|
||||
childSortBase = ObjectUtil.defaultIfNull(bookmarkDao.selectMaxSort(userId, path), 0);
|
||||
}
|
||||
long id = rs.getLong("id");
|
||||
folderMap.put(id, folder);
|
||||
childSortBaseMap.put(id, childSortBase);
|
||||
}
|
||||
rs.close();
|
||||
rs = stat.executeQuery("select * from on_links");
|
||||
while (rs.next()) {
|
||||
long fId = rs.getLong("fid");
|
||||
long addTime = rs.getLong("add_time");
|
||||
int tempSort = childSortBaseMap.get(fId);
|
||||
childSortBaseMap.put(fId, tempSort + 1);
|
||||
Bookmark folder = folderMap.get(fId);
|
||||
String curPath = folder == null ? "" : folder.getPath() + "." + folder.getBookmarkId();
|
||||
Bookmark bookmark = new Bookmark(userId, curPath, StrUtil.nullToEmpty(rs.getString("title"))
|
||||
, StrUtil.nullToEmpty(rs.getString("url")), "", addTime == 0 ? System.currentTimeMillis() : addTime * 1000, tempSort);
|
||||
bookmarks.add(bookmark);
|
||||
insertOne(bookmark);
|
||||
}
|
||||
rs.close();
|
||||
stat.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Description: 插入一条书签,如果已经存在同名书签将跳过
|
||||
*
|
||||
@ -288,7 +350,7 @@ public class BookmarkServiceImpl implements BookmarkService {
|
||||
int size = 100;
|
||||
int start = 0;
|
||||
List<Bookmark> deal;
|
||||
while ((deal = bookmarkDao.selectUserNoIcon(userId, start, size)).size() > 0) {
|
||||
while (!(deal = bookmarkDao.selectUserNoIcon(userId, start, size)).isEmpty()) {
|
||||
start += size;
|
||||
deal.forEach(item -> {
|
||||
String icon = getIconPath(item.getUrl(), null, null);
|
||||
|
@ -2,8 +2,11 @@ package com.fanxb.bookmark.common.service.impl;
|
||||
|
||||
import cn.hutool.core.date.DateUnit;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.fanxb.bookmark.common.constant.CommonConstant;
|
||||
import com.fanxb.bookmark.common.constant.NumberConstant;
|
||||
import com.fanxb.bookmark.common.constant.RedisConstant;
|
||||
import com.fanxb.bookmark.common.dao.GlobalConfigDao;
|
||||
@ -12,11 +15,16 @@ import com.fanxb.bookmark.common.entity.vo.GlobalConfigVo;
|
||||
import com.fanxb.bookmark.common.service.ConfigService;
|
||||
import com.fanxb.bookmark.common.util.HttpUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
@ -61,21 +69,23 @@ public class ConfigServiceImpl implements ConfigService {
|
||||
return str;
|
||||
}
|
||||
str = getBingImg();
|
||||
if (str != null) {
|
||||
stringRedisTemplate.opsForValue().set(RedisConstant.BING_IMG, str, 2, TimeUnit.HOURS);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
private String getBingImg() {
|
||||
try {
|
||||
JSONObject bingObj = HttpUtil.getObj(bingHost + bingUrl, null, false);
|
||||
String path = bingObj.getJSONArray("images").getJSONObject(0).getString("url");
|
||||
return bingHost + path;
|
||||
String picUrl = bingHost + path;
|
||||
Request request = new Request.Builder().url(picUrl).build();
|
||||
try (Response res = HttpUtil.getClient(false).newCall(request).execute()) {
|
||||
byte[] bytes = res.body().bytes();
|
||||
String filePath = CommonConstant.fileSavePath + "/files/public/bing.jpg";
|
||||
FileUtil.writeBytes(bytes, filePath);
|
||||
} catch (Exception e) {
|
||||
log.error("获取bing每日一图错误:{}", e.getLocalizedMessage(), e);
|
||||
}
|
||||
return null;
|
||||
return "/files/public/bing.jpg";
|
||||
}
|
||||
|
||||
|
||||
|
@ -6,12 +6,21 @@
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "App"
|
||||
name: "App",
|
||||
mounted() {
|
||||
window.qieziStatisticKey = "b74c4b571b644782a837433209827874";
|
||||
let script = document.createElement("script");
|
||||
script.type = "text/javascript";
|
||||
script.defer = true;
|
||||
script.src = "https://qiezi.fleyx.com/qiezijs/1.0/qiezi_statistic.min.js";
|
||||
document.getElementsByTagName("head")[0].appendChild(script);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
@import "./global.less";
|
||||
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
@ -20,6 +29,7 @@ body {
|
||||
background-color: @bgColor;
|
||||
height: initial;
|
||||
}
|
||||
|
||||
#app {
|
||||
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
|
@ -21,6 +21,7 @@
|
||||
:data="{ path: form.path }"
|
||||
:headers="{ 'jwt-token': token }"
|
||||
action="/bookmark/api/bookmark/uploadBookmarkFile"
|
||||
accept=".html,.db3"
|
||||
@change="fileChange"
|
||||
>
|
||||
<p class="ant-upload-drag-icon">
|
||||
|
@ -12,7 +12,7 @@ import router from "../router/index";
|
||||
* @param {*} redirect 接口返回未认证是否跳转到登陆
|
||||
* @returns 数据
|
||||
*/
|
||||
async function request (url, method, params, body, isForm, redirect) {
|
||||
async function request(url, method, params, body, isForm, redirect) {
|
||||
let options = {
|
||||
url,
|
||||
baseURL: "/bookmark/api",
|
||||
@ -68,7 +68,7 @@ async function request (url, method, params, body, isForm, redirect) {
|
||||
* @param {*} params url参数
|
||||
* @param {*} redirect 未登陆是否跳转到登陆页
|
||||
*/
|
||||
async function get (url, params = null, redirect = true) {
|
||||
async function get(url, params = null, redirect = true) {
|
||||
return request(url, "get", params, null, false, redirect);
|
||||
}
|
||||
|
||||
@ -80,7 +80,7 @@ async function get (url, params = null, redirect = true) {
|
||||
* @param {*} isForm 是否表单数据
|
||||
* @param {*} redirect 是否重定向
|
||||
*/
|
||||
async function post (url, params, body, isForm = false, redirect = true) {
|
||||
async function post(url, params, body, isForm = false, redirect = true) {
|
||||
return request(url, "post", params, body, isForm, redirect);
|
||||
}
|
||||
|
||||
@ -92,7 +92,7 @@ async function post (url, params, body, isForm = false, redirect = true) {
|
||||
* @param {*} isForm 是否表单数据
|
||||
* @param {*} redirect 是否重定向
|
||||
*/
|
||||
async function put (url, params, body, isForm = false, redirect = true) {
|
||||
async function put(url, params, body, isForm = false, redirect = true) {
|
||||
return request(url, "put", params, body, isForm, redirect);
|
||||
}
|
||||
|
||||
@ -102,7 +102,7 @@ async function put (url, params, body, isForm = false, redirect = true) {
|
||||
* @param {*} params url参数
|
||||
* @param {*} redirect 是否重定向
|
||||
*/
|
||||
async function deletes (url, params = null, redirect = true) {
|
||||
async function deletes(url, params = null, redirect = true) {
|
||||
return request(url, "delete", params, null, redirect);
|
||||
}
|
||||
|
||||
@ -110,5 +110,6 @@ export default {
|
||||
get,
|
||||
post,
|
||||
put,
|
||||
delete: deletes
|
||||
delete: deletes,
|
||||
http
|
||||
};
|
||||
|
@ -5,18 +5,23 @@
|
||||
源码地址:
|
||||
<a href="https://github.com/FleyX/bookmark" target="_blank">github.com/FleyX/bookmark</a>
|
||||
</div>
|
||||
<div>
|
||||
当前版本:{{ appVersion }}  <a v-if="showNewVersion"
|
||||
href="https://github.com/FleyX/bookmark/blob/master/DEPLOY.md">最新版本:{{ latestVersion
|
||||
}}</a>
|
||||
</div>
|
||||
<div>
|
||||
使用教程:
|
||||
<a href="https://blog.fleyx.com/blog/detail/20220329" target="_blank">点击跳转</a>
|
||||
</div>
|
||||
<div>
|
||||
浏览器插件:
|
||||
<a href="/static/bookmarkBrowserPlugin.zip" download="浏览器插件.zip" target="_blank">最新版本{{ serverConfig.map.pluginVersion }}(注意更新)</a>
|
||||
<a href="/static/bookmarkBrowserPlugin.zip" download="浏览器插件.zip"
|
||||
target="_blank">最新版本{{ serverConfig.map.pluginVersion }}(注意更新)</a>
|
||||
,使用详情请参考使用教程
|
||||
</div>
|
||||
<div>交流反馈qq群:150056494,邮箱:fleyx20@outlook.com</div>
|
||||
<div>
|
||||
统计:
|
||||
<a href="https://qiezi.fleyx.com" style="" target="_blank">
|
||||
<div id="qieziStatisticHtmlHostPv" style="display: none">
|
||||
总访问次数:
|
||||
@ -36,27 +41,46 @@
|
||||
<script>
|
||||
import { mapState } from "vuex";
|
||||
import { GLOBAL_CONFIG, SERVER_CONFIG } from "@/store/modules/globalConfig";
|
||||
import httpUtil from "@/util/HttpUtil";
|
||||
|
||||
export default {
|
||||
name: "about",
|
||||
data() {
|
||||
return { version: "" };
|
||||
return {
|
||||
appVersion: "1.4", //应用版本
|
||||
latestVersion: null,
|
||||
showNewVersion: false
|
||||
};
|
||||
},
|
||||
computed: { ...mapState(GLOBAL_CONFIG, [SERVER_CONFIG]) },
|
||||
mounted() {
|
||||
window.qieziStatisticKey = "b74c4b571b644782a837433209827874";
|
||||
let script = document.createElement("script");
|
||||
script.type = "text/javascript";
|
||||
script.defer = true;
|
||||
script.src = "https://qiezi.fleyx.com/qiezijs/1.0/qiezi_statistic.min.js";
|
||||
document.getElementsByTagName("head")[0].appendChild(script);
|
||||
async mounted() {
|
||||
|
||||
let serverConfig = this.$store.state[GLOBAL_CONFIG + "/" + SERVER_CONFIG];
|
||||
console.log(serverConfig);
|
||||
if (serverConfig.map.pluginVersion) {
|
||||
this.version = serverConfig.map.pluginVersion;
|
||||
}
|
||||
|
||||
//获取最新版本
|
||||
let res = await httpUtil.http.default.get("https://s3.fleyx.com/picbed/bookmark/config.json");
|
||||
console.log(res);
|
||||
this.latestVersion = res.data.latestAppVersion;
|
||||
this.showNewVersion = this.checkVersion(this.appVersion, this.latestVersion);
|
||||
},
|
||||
};
|
||||
methods: {
|
||||
checkVersion: function(version, latestVersion) {
|
||||
let versions = version.split(".");
|
||||
let latestVersions = latestVersion.split(".");
|
||||
for (let i = 0; i < versions.length; i++) {
|
||||
if (i >= latestVersions.length) {
|
||||
return false;
|
||||
}
|
||||
let versionNum = parseInt(versions[i]);
|
||||
let latestVersionNum = parseInt(latestVersions[i]);
|
||||
if (versionNum !== latestVersionNum) {
|
||||
return versionNum < latestVersionNum;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
;
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
|
@ -4,6 +4,7 @@ module.exports = {
|
||||
devtool: "source-map"
|
||||
},
|
||||
devServer: {
|
||||
port: 4531,
|
||||
proxy: {
|
||||
"/bookmark/api": {
|
||||
//这里最好有一个 /
|
||||
|
@ -14,6 +14,7 @@ services:
|
||||
- ./data/mysql/my.cnf:/etc/mysql/my.cnf
|
||||
- /etc/localtime:/etc/localtime
|
||||
- ./data/timezone:/etc/timezone
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- MYSQL_ROOT_PASSWORD=${MYSQL_PASSWORD}
|
||||
- MYSQL_DATABASE=bookmark
|
||||
@ -25,6 +26,7 @@ services:
|
||||
- /etc/localtime:/etc/localtime
|
||||
- ./data/timezone:/etc/timezone
|
||||
- ./data/redis:/data
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- bookmark
|
||||
|
||||
@ -38,6 +40,7 @@ services:
|
||||
- ./bookmark_front/dist:/opt/dist
|
||||
- ./data/nginx/nginx.conf:/etc/nginx/nginx.conf
|
||||
- ${BOOKMARK_FILE_SAVE_PATH}/files/public:/opt/files/public
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 8080:8080
|
||||
|
||||
@ -60,6 +63,7 @@ services:
|
||||
- ./bookMarkService/web/target/bookmark-web-1.0-SNAPSHOT.jar:/opt/app/service.jar
|
||||
- ${BOOKMARK_FILE_SAVE_PATH}:/opt/files
|
||||
working_dir: /opt/app
|
||||
restart: unless-stopped
|
||||
command:
|
||||
- /bin/bash
|
||||
- -c
|
||||
|
Loading…
x
Reference in New Issue
Block a user