commit
2c10ec4831
16
HELP.md
16
HELP.md
@ -97,3 +97,19 @@ enter/回车: 未选中书签情况下,用于发起网页搜索。选中书签
|
|||||||
### 个人中心
|
### 个人中心
|
||||||
|
|
||||||
通过右上角悬浮菜单进入个人中心页面,可进行头像更换,密码修改等操作
|
通过右上角悬浮菜单进入个人中心页面,可进行头像更换,密码修改等操作
|
||||||
|
|
||||||
|
### 浏览器插件
|
||||||
|
|
||||||
|
浏览器插件功能终于有 0.1 版本,支持鼠标右键菜单添加书签。
|
||||||
|
|
||||||
|
#### 安装插件
|
||||||
|
|
||||||
|
1. 首先下载插件压缩包,下载地址:[点击下载](https://fleyx.com/static/bookmarkBrowserPlugin.7z)
|
||||||
|
|
||||||
|
2. 安装插件(以 chrome 浏览器为例,其他支持插件的浏览器差不多)进入插件管理页面->开启开发者模式->加载已解压的拓展程序
|
||||||
|
|
||||||
|
![](https://qiniupic.fleyx.com/blog/202204151605709.png)
|
||||||
|
|
||||||
|
3. 之后页面点击右键->添加到书签,即可
|
||||||
|
|
||||||
|
![](https://qiniupic.fleyx.com/blog/202204151607593.png)
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
package com.fanxb.bookmark.business.bookmark.constant;
|
||||||
|
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO
|
||||||
|
*
|
||||||
|
* @author fanxb
|
||||||
|
*/
|
||||||
|
public class FileConstant {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网站icon存储路径
|
||||||
|
*/
|
||||||
|
public static final String FAVICON_PATH = Paths.get("files", "public", "favicon").toString();
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package com.fanxb.bookmark.business.bookmark.dao;
|
||||||
|
|
||||||
|
import org.apache.ibatis.annotations.Insert;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
import org.apache.ibatis.annotations.Select;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author fanxb
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface HostIconDao {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 插入一条数据
|
||||||
|
*
|
||||||
|
* @param host host
|
||||||
|
* @param iconPath path
|
||||||
|
* @author fanxb
|
||||||
|
*/
|
||||||
|
@Insert("insert into host_icon(host,iconPath) value(#{host},#{iconPath})")
|
||||||
|
void insert(@Param("host") String host, @Param("iconPath") String iconPath);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据host获取iconPath
|
||||||
|
*
|
||||||
|
* @param host host
|
||||||
|
* @return {@link String}
|
||||||
|
* @author fanxb
|
||||||
|
*/
|
||||||
|
@Select("select iconPath from host_icon where host=#{host}")
|
||||||
|
String selectByHost(String host);
|
||||||
|
}
|
@ -1,21 +1,29 @@
|
|||||||
package com.fanxb.bookmark.business.bookmark.service.impl;
|
package com.fanxb.bookmark.business.bookmark.service.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.io.FileUtil;
|
||||||
import cn.hutool.core.util.ArrayUtil;
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
|
import cn.hutool.core.util.CharsetUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSON;
|
||||||
import com.fanxb.bookmark.business.api.UserApi;
|
import com.fanxb.bookmark.business.api.UserApi;
|
||||||
|
import com.fanxb.bookmark.business.bookmark.constant.FileConstant;
|
||||||
import com.fanxb.bookmark.business.bookmark.dao.BookmarkDao;
|
import com.fanxb.bookmark.business.bookmark.dao.BookmarkDao;
|
||||||
|
import com.fanxb.bookmark.business.bookmark.dao.HostIconDao;
|
||||||
import com.fanxb.bookmark.business.bookmark.entity.BookmarkEs;
|
import com.fanxb.bookmark.business.bookmark.entity.BookmarkEs;
|
||||||
import com.fanxb.bookmark.business.bookmark.entity.MoveNodeBody;
|
import com.fanxb.bookmark.business.bookmark.entity.MoveNodeBody;
|
||||||
import com.fanxb.bookmark.business.bookmark.entity.redis.BookmarkDeleteMessage;
|
import com.fanxb.bookmark.business.bookmark.entity.redis.BookmarkDeleteMessage;
|
||||||
import com.fanxb.bookmark.business.bookmark.entity.redis.VisitNumPlus;
|
import com.fanxb.bookmark.business.bookmark.entity.redis.VisitNumPlus;
|
||||||
import com.fanxb.bookmark.business.bookmark.service.BookmarkService;
|
import com.fanxb.bookmark.business.bookmark.service.BookmarkService;
|
||||||
import com.fanxb.bookmark.business.bookmark.service.PinYinService;
|
import com.fanxb.bookmark.business.bookmark.service.PinYinService;
|
||||||
|
import com.fanxb.bookmark.common.constant.CommonConstant;
|
||||||
import com.fanxb.bookmark.common.constant.EsConstant;
|
import com.fanxb.bookmark.common.constant.EsConstant;
|
||||||
import com.fanxb.bookmark.common.constant.RedisConstant;
|
import com.fanxb.bookmark.common.constant.RedisConstant;
|
||||||
import com.fanxb.bookmark.common.entity.po.Bookmark;
|
import com.fanxb.bookmark.common.entity.po.Bookmark;
|
||||||
|
import com.fanxb.bookmark.common.exception.CustomException;
|
||||||
import com.fanxb.bookmark.common.util.*;
|
import com.fanxb.bookmark.common.util.*;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import okhttp3.Request;
|
||||||
|
import okhttp3.Response;
|
||||||
import org.elasticsearch.index.query.BoolQueryBuilder;
|
import org.elasticsearch.index.query.BoolQueryBuilder;
|
||||||
import org.elasticsearch.index.query.QueryBuilders;
|
import org.elasticsearch.index.query.QueryBuilders;
|
||||||
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
||||||
@ -28,9 +36,15 @@ import org.springframework.beans.factory.annotation.Value;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@ -51,13 +65,15 @@ public class BookmarkServiceImpl implements BookmarkService {
|
|||||||
private final PinYinService pinYinService;
|
private final PinYinService pinYinService;
|
||||||
private final UserApi userApi;
|
private final UserApi userApi;
|
||||||
private final EsUtil esUtil;
|
private final EsUtil esUtil;
|
||||||
|
private final HostIconDao hostIconDao;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public BookmarkServiceImpl(BookmarkDao bookmarkDao, PinYinService pinYinService, UserApi userApi, EsUtil esUtil) {
|
public BookmarkServiceImpl(BookmarkDao bookmarkDao, PinYinService pinYinService, UserApi userApi, EsUtil esUtil, HostIconDao hostIconDao) {
|
||||||
this.bookmarkDao = bookmarkDao;
|
this.bookmarkDao = bookmarkDao;
|
||||||
this.pinYinService = pinYinService;
|
this.pinYinService = pinYinService;
|
||||||
this.userApi = userApi;
|
this.userApi = userApi;
|
||||||
this.esUtil = esUtil;
|
this.esUtil = esUtil;
|
||||||
|
this.hostIconDao = hostIconDao;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -201,7 +217,7 @@ public class BookmarkServiceImpl implements BookmarkService {
|
|||||||
bookmark.setUserId(userId);
|
bookmark.setUserId(userId);
|
||||||
bookmark.setCreateTime(System.currentTimeMillis());
|
bookmark.setCreateTime(System.currentTimeMillis());
|
||||||
bookmark.setAddTime(bookmark.getCreateTime());
|
bookmark.setAddTime(bookmark.getCreateTime());
|
||||||
bookmark.setIcon(getIconBase64(bookmark.getUrl()));
|
bookmark.setIcon(getIconPath(bookmark.getUrl()));
|
||||||
//文件夹和书签都建立搜索key
|
//文件夹和书签都建立搜索key
|
||||||
pinYinService.changeBookmark(bookmark);
|
pinYinService.changeBookmark(bookmark);
|
||||||
bookmarkDao.insertOne(bookmark);
|
bookmarkDao.insertOne(bookmark);
|
||||||
@ -215,7 +231,7 @@ public class BookmarkServiceImpl implements BookmarkService {
|
|||||||
bookmark.setUserId(userId);
|
bookmark.setUserId(userId);
|
||||||
if (bookmark.getType() == 0) {
|
if (bookmark.getType() == 0) {
|
||||||
pinYinService.changeBookmark(bookmark);
|
pinYinService.changeBookmark(bookmark);
|
||||||
bookmark.setIcon(getIconBase64(bookmark.getUrl()));
|
bookmark.setIcon(getIconPath(bookmark.getUrl()));
|
||||||
}
|
}
|
||||||
bookmarkDao.editBookmark(bookmark);
|
bookmarkDao.editBookmark(bookmark);
|
||||||
userApi.versionPlus(userId);
|
userApi.versionPlus(userId);
|
||||||
@ -274,7 +290,7 @@ public class BookmarkServiceImpl implements BookmarkService {
|
|||||||
while ((deal = bookmarkDao.selectUserNoIcon(userId, start, size)).size() > 0) {
|
while ((deal = bookmarkDao.selectUserNoIcon(userId, start, size)).size() > 0) {
|
||||||
start += size;
|
start += size;
|
||||||
deal.forEach(item -> {
|
deal.forEach(item -> {
|
||||||
String icon = getIconBase64(item.getUrl());
|
String icon = getIconPath(item.getUrl());
|
||||||
if (StrUtil.isNotEmpty(icon)) {
|
if (StrUtil.isNotEmpty(icon)) {
|
||||||
bookmarkDao.updateIcon(item.getBookmarkId(), icon);
|
bookmarkDao.updateIcon(item.getBookmarkId(), icon);
|
||||||
}
|
}
|
||||||
@ -305,21 +321,56 @@ public class BookmarkServiceImpl implements BookmarkService {
|
|||||||
return resPath;
|
return resPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getIconBase64(String url) {
|
/**
|
||||||
|
* 获取icon
|
||||||
|
*
|
||||||
|
* @param url url
|
||||||
|
* @return {@link String}
|
||||||
|
* @author fanxb
|
||||||
|
*/
|
||||||
|
private String getIconPath(String url) {
|
||||||
if (StrUtil.isEmpty(url)) {
|
if (StrUtil.isEmpty(url)) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
String host;
|
||||||
try {
|
try {
|
||||||
URL urlObj = new URL(url);
|
URL urlObj = new URL(url);
|
||||||
byte[] data = HttpUtil.download(urlIconAddress + "/icon?url=" + urlObj.getHost() + "&size=8..16..64", false);
|
host = urlObj.getHost();
|
||||||
String base64 = new String(Base64.getEncoder().encode(data));
|
} catch (Exception e) {
|
||||||
if (StrUtil.isNotEmpty(base64)) {
|
|
||||||
return "data:image/png;base64," + base64;
|
|
||||||
} else {
|
|
||||||
log.warn("url无法获取icon:{}", url);
|
|
||||||
}
|
|
||||||
} catch (MalformedURLException e) {
|
|
||||||
log.warn("url无法解析出domain:{}", url);
|
log.warn("url无法解析出domain:{}", url);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
String iconPath = hostIconDao.selectByHost(host);
|
||||||
|
if (iconPath != null) {
|
||||||
|
return iconPath;
|
||||||
|
}
|
||||||
|
iconPath = saveFile(host, urlIconAddress + "/icon?url=" + host + "&size=16..64..256");
|
||||||
|
if (StrUtil.isNotEmpty(iconPath)) {
|
||||||
|
hostIconDao.insert(host, iconPath);
|
||||||
|
}
|
||||||
|
return iconPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String saveFile(String host, String url) {
|
||||||
|
try {
|
||||||
|
try (Response res = HttpUtil.getClient(false).newCall(new Request.Builder().url(url)
|
||||||
|
.header("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.75 Safari/537.36 Edg/100.0.1185.36")
|
||||||
|
.get().build()).execute()) {
|
||||||
|
assert res.body() != null;
|
||||||
|
if (!HttpUtil.checkIsOk(res.code())) {
|
||||||
|
throw new CustomException("请求错误:" + res.code());
|
||||||
|
}
|
||||||
|
byte[] data = res.body().byteStream().readAllBytes();
|
||||||
|
if (data.length > 0) {
|
||||||
|
String iconUrl = res.request().url().toString();
|
||||||
|
String fileName = URLEncoder.encode(host, StandardCharsets.UTF_8) + iconUrl.substring(iconUrl.lastIndexOf("."));
|
||||||
|
String filePath = Paths.get(FileConstant.FAVICON_PATH, host.substring(0, 2), fileName).toString();
|
||||||
|
FileUtil.writeBytes(data, Paths.get(CommonConstant.fileSavePath, filePath).toString());
|
||||||
|
return File.separator + filePath;
|
||||||
|
} else {
|
||||||
|
log.info("未获取到icon:{}", url);
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("url获取icon故障:{}", url, e);
|
log.error("url获取icon故障:{}", url, e);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.fanxb.bookmark.business.user.constant;
|
package com.fanxb.bookmark.business.user.constant;
|
||||||
|
|
||||||
|
import com.fanxb.bookmark.common.constant.CommonConstant;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
@ -17,6 +18,6 @@ public class FileConstant {
|
|||||||
/**
|
/**
|
||||||
* 用户头像目录
|
* 用户头像目录
|
||||||
*/
|
*/
|
||||||
public static String iconPath = Paths.get("files", "public", "icon").toString();
|
public static String iconPath = Paths.get(CommonConstant.fileSavePath, "files", "public", "icon").toString();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -12,8 +12,6 @@ import org.springframework.stereotype.Component;
|
|||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.Proxy;
|
import java.net.Proxy;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -51,42 +49,33 @@ public class HttpUtil {
|
|||||||
/**
|
/**
|
||||||
* 无代理环境
|
* 无代理环境
|
||||||
*/
|
*/
|
||||||
private static final OkHttpClient CLIENT = new OkHttpClient.Builder().connectTimeout(2, TimeUnit.SECONDS)
|
private static final OkHttpClient CLIENT = new OkHttpClient.Builder().connectTimeout(1, TimeUnit.SECONDS)
|
||||||
.readTimeout(60, TimeUnit.SECONDS)
|
.readTimeout(60, TimeUnit.SECONDS)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取客户端
|
||||||
|
*
|
||||||
|
* @param proxy 是否代理
|
||||||
|
* @return {@link OkHttpClient}
|
||||||
|
* @author fanxb
|
||||||
|
*/
|
||||||
|
public static OkHttpClient getClient(boolean proxy) {
|
||||||
|
return proxy ? PROXY_CLIENT : CLIENT;
|
||||||
|
}
|
||||||
|
|
||||||
public static final MediaType JSON = MediaType.get("application/json; charset=utf-8");
|
public static final MediaType JSON = MediaType.get("application/json; charset=utf-8");
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void init() {
|
public void init() {
|
||||||
OkHttpClient.Builder builder = new OkHttpClient.Builder();
|
OkHttpClient.Builder builder = new OkHttpClient.Builder().connectTimeout(1, TimeUnit.SECONDS).readTimeout(60, TimeUnit.SECONDS);
|
||||||
log.info("代理配置,ip:{},port:{}", proxyIp, proxyPort);
|
log.info("代理配置,ip:{},port:{}", proxyIp, proxyPort);
|
||||||
if (StrUtil.isNotBlank(proxyIp) && StrUtil.isNotBlank(proxyPort)) {
|
if (StrUtil.isNotBlank(proxyIp) && StrUtil.isNotBlank(proxyPort)) {
|
||||||
builder.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyIp, Integer.parseInt(proxyPort))));
|
builder.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyIp, Integer.parseInt(proxyPort))));
|
||||||
proxyExist = true;
|
proxyExist = true;
|
||||||
}
|
PROXY_CLIENT = builder.build();
|
||||||
PROXY_CLIENT = builder.connectTimeout(10, TimeUnit.SECONDS)
|
|
||||||
.readTimeout(60, TimeUnit.SECONDS)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
|
||||||
* 下载文件
|
|
||||||
* @author fanxb
|
|
||||||
* @param url 下载链接
|
|
||||||
* @param proxy 是否使用代理
|
|
||||||
* @return java.io.InputStream
|
|
||||||
* @date 2021/3/12
|
|
||||||
**/
|
|
||||||
public static byte[] download(String url, boolean proxy) {
|
|
||||||
try (Response res = (proxy ? PROXY_CLIENT : CLIENT).newCall(new Request.Builder().url(url).build()).execute()) {
|
|
||||||
assert res.body() != null;
|
|
||||||
if (checkIsOk(res.code())) {
|
|
||||||
return res.body().byteStream().readAllBytes();
|
|
||||||
} else {
|
} else {
|
||||||
throw new CustomException("下载出现问题:" + res.body().string());
|
PROXY_CLIENT = CLIENT;
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new CustomException(e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,6 +259,8 @@ public class HttpUtil {
|
|||||||
}
|
}
|
||||||
return ipAddress;
|
return ipAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
<!DOCTYPE NETSCAPE-Bookmark-file-1>
|
|
||||||
<!-- This is an automatically generated file.
|
|
||||||
It will be read and overwritten.
|
|
||||||
DO NOT EDIT! -->
|
|
||||||
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
|
|
||||||
<TITLE>Bookmarks</TITLE>
|
|
||||||
<H1>Bookmarks Menu</H1>
|
|
||||||
|
|
||||||
<DL><p>
|
|
||||||
<DT><A HREF="http://0.0.0.1/" ADD_DATE="1614837460" LAST_MODIFIED="1614837465">1</A>
|
|
||||||
<DT><A HREF="http://0.0.0.2/" ADD_DATE="1614837471" LAST_MODIFIED="1614837474">2</A>
|
|
||||||
<DT><H3 ADD_DATE="1614837478" LAST_MODIFIED="1614837497">f1</H3>
|
|
||||||
<DL><p>
|
|
||||||
<DT><A HREF="http://asdf/" ADD_DATE="1614837485" LAST_MODIFIED="1614837493" TAGS="ww">f11</A>
|
|
||||||
<DT><A HREF="http://f12/" ADD_DATE="1614837497" LAST_MODIFIED="1614837502">f12</A>
|
|
||||||
</DL><p>
|
|
||||||
</DL>
|
|
@ -1,17 +0,0 @@
|
|||||||
<!DOCTYPE NETSCAPE-Bookmark-file-1>
|
|
||||||
<!-- This is an automatically generated file.
|
|
||||||
It will be read and overwritten.
|
|
||||||
DO NOT EDIT! -->
|
|
||||||
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
|
|
||||||
<TITLE>Bookmarks</TITLE>
|
|
||||||
<H1>Bookmarks Menu</H1>
|
|
||||||
|
|
||||||
<DL><p>
|
|
||||||
<DT><A HREF="http://0.0.0.1/" ADD_DATE="1614837460" LAST_MODIFIED="1614837465">1</A>
|
|
||||||
<DT><A HREF="http://0.0.0.2/" ADD_DATE="1614837471" LAST_MODIFIED="1614837474">2</A>
|
|
||||||
<DT><H3 ADD_DATE="1614837478" LAST_MODIFIED="1614837497">f1</H3>
|
|
||||||
<DL><p>
|
|
||||||
<DT><A HREF="http://asdf/" ADD_DATE="1614837485" LAST_MODIFIED="1614837493" TAGS="ww">f11</A>
|
|
||||||
<DT><A HREF="http://f12/" ADD_DATE="1614837497" LAST_MODIFIED="1614837502">f12</A>
|
|
||||||
</DL><p>
|
|
||||||
</DL>
|
|
@ -0,0 +1,10 @@
|
|||||||
|
CREATE TABLE bookmark.host_icon (
|
||||||
|
id INT UNSIGNED auto_increment NOT NULL,
|
||||||
|
host varchar(300) NOT NULL COMMENT 'host',
|
||||||
|
iconPath varchar(330) NOT NULL,
|
||||||
|
CONSTRAINT host_icon_pk PRIMARY KEY (id)
|
||||||
|
)
|
||||||
|
ENGINE=InnoDB
|
||||||
|
DEFAULT CHARSET=utf8mb4
|
||||||
|
COLLATE=utf8mb4_0900_ai_ci;
|
||||||
|
CREATE INDEX host_icon_host_IDX USING BTREE ON bookmark.host_icon (host(20));
|
1
bookmark_front/.gitignore
vendored
1
bookmark_front/.gitignore
vendored
@ -3,6 +3,7 @@ node_modules
|
|||||||
/dist
|
/dist
|
||||||
package-lock.json
|
package-lock.json
|
||||||
yarn.lock
|
yarn.lock
|
||||||
|
public/files
|
||||||
|
|
||||||
# local env files
|
# local env files
|
||||||
.env.local
|
.env.local
|
||||||
|
BIN
bookmark_front/public/static/bookmarkBrowserPlugin.7z
Normal file
BIN
bookmark_front/public/static/bookmarkBrowserPlugin.7z
Normal file
Binary file not shown.
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: "App",
|
name: "App"
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
import Vue from "vue";
|
import Vue from "vue";
|
||||||
import VueRouter from "vue-router";
|
import VueRouter from "vue-router";
|
||||||
import * as vuex from "../store/index.js";
|
import * as vuex from "../store/index.js";
|
||||||
import { GLOBAL_CONFIG, SUPPORT_NO_LOGIN, TOKEN } from "@/store/modules/globalConfig";
|
import { GLOBAL_CONFIG, SUPPORT_NO_LOGIN, TOKEN, setToken } from "@/store/modules/globalConfig";
|
||||||
import { checkJwtValid } from "@/util/UserUtil";
|
import { checkJwtValid } from "@/util/UserUtil";
|
||||||
|
|
||||||
Vue.use(VueRouter);
|
Vue.use(VueRouter);
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{ path: "/", component: () => import("@/views/home/index") },
|
{ path: "/", component: () => import("@/views/home/index") },
|
||||||
|
{ path: "/noHead/addBookmark", component: () => import("@/views/noHead/addBookmark/index") },
|
||||||
{
|
{
|
||||||
path: "/manage",
|
path: "/manage",
|
||||||
component: () => import("@/views/manage/index"),
|
component: () => import("@/views/manage/index"),
|
||||||
@ -15,6 +16,7 @@ const routes = [
|
|||||||
{ path: "", redirect: "/manage/bookmarkTree" },
|
{ path: "", redirect: "/manage/bookmarkTree" },
|
||||||
{ path: "bookmarkTree", component: () => import("@/views/manage/bookmarkTree/index") },
|
{ path: "bookmarkTree", component: () => import("@/views/manage/bookmarkTree/index") },
|
||||||
{ path: "personSpace/userInfo", component: () => import("@/views/manage/personSpace/index") },
|
{ path: "personSpace/userInfo", component: () => import("@/views/manage/personSpace/index") },
|
||||||
|
{ path: "sso/auth", component: () => import("@/views/manage/sso/auth/index") }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -26,7 +28,7 @@ const routes = [
|
|||||||
{ path: "resetPassword", component: () => import("@/views/public/passwordReset/index") },
|
{ path: "resetPassword", component: () => import("@/views/public/passwordReset/index") },
|
||||||
{ path: "oauth/github", component: () => import("@/views/public/oauth/github/index") },
|
{ path: "oauth/github", component: () => import("@/views/public/oauth/github/index") },
|
||||||
{ path: "about", component: () => import("@/views/public/about/index") },
|
{ path: "about", component: () => import("@/views/public/about/index") },
|
||||||
{ path: "404", component: () => import("@/views/public/notFound/index") },
|
{ path: "404", component: () => import("@/views/public/notFound/index") }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{ path: "*", redirect: "/public/404" }
|
{ path: "*", redirect: "/public/404" }
|
||||||
@ -41,11 +43,15 @@ const router = new VueRouter({
|
|||||||
* 在此进行登录信息判断,以及重定向到登录页面
|
* 在此进行登录信息判断,以及重定向到登录页面
|
||||||
*/
|
*/
|
||||||
router.beforeEach(async (to, from, next) => {
|
router.beforeEach(async (to, from, next) => {
|
||||||
//进入主页面/管理页面时,确认已经进行初始化操作
|
if (to.query.token && checkJwtValid(to.query.token)) {
|
||||||
if (to.path === '/' || to.path.startsWith("/manage")) {
|
console.log("获取到页面token", to.query.token);
|
||||||
|
await vuex.default.dispatch(GLOBAL_CONFIG + "/" + setToken, to.query.token);
|
||||||
|
}
|
||||||
|
//进入除/public以外的路由,确认已经进行初始化操作
|
||||||
|
if (!to.path.startsWith("/public")) {
|
||||||
await vuex.loginInit();
|
await vuex.loginInit();
|
||||||
}
|
}
|
||||||
let supportNoLogin = to.path === '/' || to.path.startsWith("/public");
|
let supportNoLogin = to.path === "/" || to.path.startsWith("/public");
|
||||||
vuex.default.commit(GLOBAL_CONFIG + "/" + SUPPORT_NO_LOGIN, supportNoLogin);
|
vuex.default.commit(GLOBAL_CONFIG + "/" + SUPPORT_NO_LOGIN, supportNoLogin);
|
||||||
if (!supportNoLogin && !checkJwtValid(vuex.default.state[GLOBAL_CONFIG][TOKEN])) {
|
if (!supportNoLogin && !checkJwtValid(vuex.default.state[GLOBAL_CONFIG][TOKEN])) {
|
||||||
//如不支持未登录进入,切jwt已过期,直接跳转到登录页面,并清理缓存
|
//如不支持未登录进入,切jwt已过期,直接跳转到登录页面,并清理缓存
|
||||||
@ -58,8 +64,6 @@ router.beforeEach(async (to, from, next) => {
|
|||||||
} else {
|
} else {
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -11,7 +11,14 @@ export const IS_PHONE = "isPhone";
|
|||||||
|
|
||||||
export const noLoginInit = "noLoginInit";
|
export const noLoginInit = "noLoginInit";
|
||||||
export const loginInit = "loginInit";
|
export const loginInit = "loginInit";
|
||||||
|
/**
|
||||||
|
* 登出清除数据
|
||||||
|
*/
|
||||||
export const clear = "clear";
|
export const clear = "clear";
|
||||||
|
/**
|
||||||
|
* 设置token
|
||||||
|
*/
|
||||||
|
export const setToken = "setToken";
|
||||||
/**
|
/**
|
||||||
* 存储全局配置
|
* 存储全局配置
|
||||||
*/
|
*/
|
||||||
@ -63,18 +70,17 @@ const actions = {
|
|||||||
context.commit(USER_INFO, userInfo);
|
context.commit(USER_INFO, userInfo);
|
||||||
context.commit(IS_INIT, true);
|
context.commit(IS_INIT, true);
|
||||||
},
|
},
|
||||||
async setToken ({ commit }, token) {
|
async [setToken]({ commit }, token) {
|
||||||
await localforage.setItem(TOKEN, token);
|
await localforage.setItem(TOKEN, token);
|
||||||
window.jwtToken = token;
|
window.jwtToken = token;
|
||||||
commit(TOKEN, token);
|
commit(TOKEN, token);
|
||||||
},
|
},
|
||||||
//登出清除数据
|
|
||||||
async [clear](context) {
|
async [clear](context) {
|
||||||
await localforage.removeItem(TOKEN);
|
await localforage.removeItem(TOKEN);
|
||||||
context.commit(USER_INFO, null);
|
context.commit(USER_INFO, null);
|
||||||
context.commit(TOKEN, null);
|
context.commit(TOKEN, null);
|
||||||
context.commit(IS_INIT, false);
|
context.commit(IS_INIT, false);
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const mutations = {
|
const mutations = {
|
||||||
@ -95,7 +101,6 @@ const mutations = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const store = {
|
export const store = {
|
||||||
namespaced: true,
|
namespaced: true,
|
||||||
state,
|
state,
|
||||||
|
@ -30,6 +30,10 @@ export const clear = "clear";
|
|||||||
* 删除书签数据
|
* 删除书签数据
|
||||||
*/
|
*/
|
||||||
export const deleteData = "deleteData";
|
export const deleteData = "deleteData";
|
||||||
|
/**
|
||||||
|
* 新增节点
|
||||||
|
*/
|
||||||
|
export const addNode = "addNode";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 版本检查定时调度
|
* 版本检查定时调度
|
||||||
@ -72,9 +76,7 @@ const getters = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const actions = {
|
const actions = {
|
||||||
async [noLoginInit] () {
|
async [noLoginInit]() {},
|
||||||
|
|
||||||
},
|
|
||||||
async [loginInit](context) {
|
async [loginInit](context) {
|
||||||
if (context.state.isInit || context.state.isIniting) {
|
if (context.state.isInit || context.state.isIniting) {
|
||||||
return;
|
return;
|
||||||
@ -213,9 +215,8 @@ const actions = {
|
|||||||
let list = await HttpUtil.get("/home/pin");
|
let list = await HttpUtil.get("/home/pin");
|
||||||
commit(HOME_PIN_LIST, list);
|
commit(HOME_PIN_LIST, list);
|
||||||
let map = {};
|
let map = {};
|
||||||
list.filter(item => item.id).forEach(item => map[item.bookmarkId] = true);
|
list.filter(item => item.id).forEach(item => (map[item.bookmarkId] = true));
|
||||||
commit(HOME_PIN_BOOKMARK_ID_MAP, map);
|
commit(HOME_PIN_BOOKMARK_ID_MAP, map);
|
||||||
|
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* 更新版本数据
|
* 更新版本数据
|
||||||
@ -227,7 +228,7 @@ const actions = {
|
|||||||
/**
|
/**
|
||||||
* 新增书签、文件夹
|
* 新增书签、文件夹
|
||||||
*/
|
*/
|
||||||
async addNode (context, { sourceNode, targetNode }) {
|
async [addNode](context, { sourceNode, targetNode }) {
|
||||||
if (sourceNode === null) {
|
if (sourceNode === null) {
|
||||||
if (context.state[TOTAL_TREE_DATA][""] === undefined) {
|
if (context.state[TOTAL_TREE_DATA][""] === undefined) {
|
||||||
context.state[TOTAL_TREE_DATA][""] = [];
|
context.state[TOTAL_TREE_DATA][""] = [];
|
||||||
@ -314,7 +315,6 @@ const mutations = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查书签缓存是否最新
|
* 检查书签缓存是否最新
|
||||||
*
|
*
|
||||||
@ -338,7 +338,7 @@ async function treeDataCheck (context, isFirst) {
|
|||||||
maskClosable: false,
|
maskClosable: false,
|
||||||
onOk() {
|
onOk() {
|
||||||
toastShow = false;
|
toastShow = false;
|
||||||
return new Promise(async (resolve) => {
|
return new Promise(async resolve => {
|
||||||
await context.dispatch(refresh);
|
await context.dispatch(refresh);
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
@ -361,4 +361,3 @@ export const store = {
|
|||||||
actions,
|
actions,
|
||||||
mutations
|
mutations
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@
|
|||||||
<a-menu-item v-if="!rec.dataRef.isLeaf" key="add">新增</a-menu-item>
|
<a-menu-item v-if="!rec.dataRef.isLeaf" key="add">新增</a-menu-item>
|
||||||
<a-menu-item v-else key="copy" class="copy-to-board" :data="rec.dataRef.url">复制URL</a-menu-item>
|
<a-menu-item v-else key="copy" class="copy-to-board" :data="rec.dataRef.url">复制URL</a-menu-item>
|
||||||
<a-menu-item v-if="rec.dataRef.isLeaf" key="pin">
|
<a-menu-item v-if="rec.dataRef.isLeaf" key="pin">
|
||||||
{{ homePinList.filter((item) => item.id && item.bookmarkId == rec.dataRef.bookmarkId).length > 0 ? "从首页移除" : "固定到首页" }}
|
{{ homePinList.filter(item => item.id && item.bookmarkId == rec.dataRef.bookmarkId).length > 0 ? "从首页移除" : "固定到首页" }}
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
<a-menu-item key="edit">编辑</a-menu-item>
|
<a-menu-item key="edit">编辑</a-menu-item>
|
||||||
<a-menu-item key="delete">删除</a-menu-item>
|
<a-menu-item key="delete">删除</a-menu-item>
|
||||||
@ -114,7 +114,7 @@ export default {
|
|||||||
loadedKeys: [], // 已加载数据
|
loadedKeys: [], // 已加载数据
|
||||||
replaceFields: {
|
replaceFields: {
|
||||||
title: "name",
|
title: "name",
|
||||||
key: "bookmarkId",
|
key: "bookmarkId"
|
||||||
},
|
},
|
||||||
mulSelect: false, // 多选框是否显示
|
mulSelect: false, // 多选框是否显示
|
||||||
currentSelect: null, // 当前树的选择项
|
currentSelect: null, // 当前树的选择项
|
||||||
@ -126,19 +126,19 @@ export default {
|
|||||||
// 新增、修改目标数据,null说明向根节点增加数据
|
// 新增、修改目标数据,null说明向根节点增加数据
|
||||||
targetNode: null,
|
targetNode: null,
|
||||||
// 是否为新增动作
|
// 是否为新增动作
|
||||||
isAdd: false,
|
isAdd: false
|
||||||
},
|
},
|
||||||
copyBoard: null, //剪贴板对象
|
copyBoard: null //剪贴板对象
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState("treeData", ["totalTreeData", HOME_PIN_LIST]),
|
...mapState("treeData", ["totalTreeData", HOME_PIN_LIST]),
|
||||||
...mapState("globalConfig", ["isPhone"]),
|
...mapState("globalConfig", ["isPhone"])
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
totalTreeData(newVal, oldVal) {
|
totalTreeData(newVal, oldVal) {
|
||||||
this.resetData();
|
this.resetData();
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
this.$store.commit(TREE_DATA + "/" + SHOW_REFRESH_TOAST, true);
|
this.$store.commit(TREE_DATA + "/" + SHOW_REFRESH_TOAST, true);
|
||||||
@ -149,12 +149,19 @@ export default {
|
|||||||
this.copyBoard = new ClipboardJS(".copy-to-board", {
|
this.copyBoard = new ClipboardJS(".copy-to-board", {
|
||||||
text: function(trigger) {
|
text: function(trigger) {
|
||||||
return trigger.attributes.data.nodeValue;
|
return trigger.attributes.data.nodeValue;
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
this.copyBoard.on("success", (e) => {
|
this.copyBoard.on("success", e => {
|
||||||
this.$message.success("复制成功");
|
this.$message.success("复制成功");
|
||||||
e.clearSelection();
|
e.clearSelection();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
window.onblur = e => {
|
||||||
|
console.log("窗口非激活");
|
||||||
|
};
|
||||||
|
window.onfocus = e => {
|
||||||
|
console.log("窗口激活");
|
||||||
|
};
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
this.$store.commit(TREE_DATA + "/" + SHOW_REFRESH_TOAST, false);
|
this.$store.commit(TREE_DATA + "/" + SHOW_REFRESH_TOAST, false);
|
||||||
@ -168,7 +175,7 @@ export default {
|
|||||||
*/
|
*/
|
||||||
loadData(treeNode) {
|
loadData(treeNode) {
|
||||||
console.log("加载数据", treeNode);
|
console.log("加载数据", treeNode);
|
||||||
return new Promise((resolve) => {
|
return new Promise(resolve => {
|
||||||
const data = typeof treeNode === "number" ? this.$store.getters["treeData/getById"](treeNode) : treeNode.dataRef;
|
const data = typeof treeNode === "number" ? this.$store.getters["treeData/getById"](treeNode) : treeNode.dataRef;
|
||||||
let newPath = data.path + "." + data.bookmarkId;
|
let newPath = data.path + "." + data.bookmarkId;
|
||||||
if (!this.totalTreeData[newPath]) {
|
if (!this.totalTreeData[newPath]) {
|
||||||
@ -212,9 +219,9 @@ export default {
|
|||||||
this.expandedKeys = [
|
this.expandedKeys = [
|
||||||
...item.path
|
...item.path
|
||||||
.split(".")
|
.split(".")
|
||||||
.filter((item) => item.length > 0)
|
.filter(item => item.length > 0)
|
||||||
.map((item) => parseInt(item)),
|
.map(item => parseInt(item)),
|
||||||
item.bookmarkId,
|
item.bookmarkId
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
this.expandedKeys.pop();
|
this.expandedKeys.pop();
|
||||||
@ -228,7 +235,7 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
this.checkedKeys.splice(this.checkedKeys.indexOf(item.bookmarkId), 1);
|
this.checkedKeys.splice(this.checkedKeys.indexOf(item.bookmarkId), 1);
|
||||||
this.checkedNodes.splice(
|
this.checkedNodes.splice(
|
||||||
this.checkedNodes.findIndex((item1) => item1.bookmarkId === item.bookmarkId),
|
this.checkedNodes.findIndex(item1 => item1.bookmarkId === item.bookmarkId),
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -248,9 +255,9 @@ export default {
|
|||||||
this.expandedKeys = [
|
this.expandedKeys = [
|
||||||
...item.path
|
...item.path
|
||||||
.split(".")
|
.split(".")
|
||||||
.filter((item) => item.length > 0)
|
.filter(item => item.length > 0)
|
||||||
.map((item) => parseInt(item)),
|
.map(item => parseInt(item)),
|
||||||
item.bookmarkId,
|
item.bookmarkId
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -273,7 +280,7 @@ export default {
|
|||||||
const bookmarkIdList = [];
|
const bookmarkIdList = [];
|
||||||
const pathList = [];
|
const pathList = [];
|
||||||
if (this.checkedNodes) {
|
if (this.checkedNodes) {
|
||||||
this.checkedNodes.forEach((item) =>
|
this.checkedNodes.forEach(item =>
|
||||||
item.type === 1 ? pathList.push(item.path + "." + item.bookmarkId) : bookmarkIdList.push(item.bookmarkId)
|
item.type === 1 ? pathList.push(item.path + "." + item.bookmarkId) : bookmarkIdList.push(item.bookmarkId)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -290,7 +297,7 @@ export default {
|
|||||||
await HttpUtil.post("/bookmark/batchDelete", null, { pathList, bookmarkIdList });
|
await HttpUtil.post("/bookmark/batchDelete", null, { pathList, bookmarkIdList });
|
||||||
await this.$store.dispatch(TREE_DATA + "/" + deleteData, { pathList, bookmarkIdList });
|
await this.$store.dispatch(TREE_DATA + "/" + deleteData, { pathList, bookmarkIdList });
|
||||||
//删除已经被删除的数据
|
//删除已经被删除的数据
|
||||||
pathList.forEach((item) => {
|
pathList.forEach(item => {
|
||||||
const id = parseInt(item.split(".").reverse()[0]);
|
const id = parseInt(item.split(".").reverse()[0]);
|
||||||
let index = this.loadedKeys.indexOf(id);
|
let index = this.loadedKeys.indexOf(id);
|
||||||
if (index > -1) {
|
if (index > -1) {
|
||||||
@ -322,13 +329,13 @@ export default {
|
|||||||
this.refresh(false);
|
this.refresh(false);
|
||||||
this.expandedKeys = item.path
|
this.expandedKeys = item.path
|
||||||
.split(".")
|
.split(".")
|
||||||
.filter((one) => one.length > 0)
|
.filter(one => one.length > 0)
|
||||||
.map((one) => parseInt(one));
|
.map(one => parseInt(one));
|
||||||
this.loadedKeys = item.path
|
this.loadedKeys = item.path
|
||||||
.split(".")
|
.split(".")
|
||||||
.filter((one) => one.length > 0)
|
.filter(one => one.length > 0)
|
||||||
.map((one) => parseInt(one));
|
.map(one => parseInt(one));
|
||||||
this.expandedKeys.forEach(async (one) => await this.loadData(one));
|
this.expandedKeys.forEach(async one => await this.loadData(one));
|
||||||
this.currentSelect = item;
|
this.currentSelect = item;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@ -344,7 +351,7 @@ export default {
|
|||||||
this.addModal = {
|
this.addModal = {
|
||||||
show: false,
|
show: false,
|
||||||
targetNode: null,
|
targetNode: null,
|
||||||
isAdd: false,
|
isAdd: false
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
async onDrop(info) {
|
async onDrop(info) {
|
||||||
@ -388,12 +395,12 @@ export default {
|
|||||||
await this.deleteBookmarks();
|
await this.deleteBookmarks();
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
} else if (key === "edit") {
|
} else if (key === "edit") {
|
||||||
this.editData();
|
this.editData();
|
||||||
} else if (key === "pin") {
|
} else if (key === "pin") {
|
||||||
let pin = this.homePinList.filter((one) => one.id && one.bookmarkId == item.bookmarkId);
|
let pin = this.homePinList.filter(one => one.id && one.bookmarkId == item.bookmarkId);
|
||||||
if (pin.length > 0) {
|
if (pin.length > 0) {
|
||||||
await HttpUtil.delete("/home/pin", { id: pin[0].id });
|
await HttpUtil.delete("/home/pin", { id: pin[0].id });
|
||||||
} else {
|
} else {
|
||||||
@ -411,8 +418,8 @@ export default {
|
|||||||
dealList(root, map[""], map);
|
dealList(root, map[""], map);
|
||||||
let content = exportFileHead + root.outerHTML;
|
let content = exportFileHead + root.outerHTML;
|
||||||
downloadFile(moment().format("YYYY-MM-DD") + "导出书签.html", content);
|
downloadFile(moment().format("YYYY-MM-DD") + "导出书签.html", content);
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
39
bookmark_front/src/views/manage/sso/auth/index.vue
Normal file
39
bookmark_front/src/views/manage/sso/auth/index.vue
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<template>
|
||||||
|
<div class="ssoMain">{{ message }}</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { GLOBAL_CONFIG, TOKEN } from "@/store/modules/globalConfig";
|
||||||
|
export default {
|
||||||
|
name: "ssoPage",
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
message: "loading"
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
window.addEventListener("message", event => {
|
||||||
|
if (!event.data.code) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log("收到content消息", event);
|
||||||
|
if (event.data.code == "setTokenOk") {
|
||||||
|
this.message = "登陆成功,3s后关闭本页面";
|
||||||
|
setTimeout(() => window.close(), 3000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let token = this.$store.state[GLOBAL_CONFIG][TOKEN];
|
||||||
|
window.postMessage({ code: "setToken", data: token }, "*");
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
window.removeEventListener("message");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.ssoMain {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
63
bookmark_front/src/views/noHead/addBookmark/index.vue
Normal file
63
bookmark_front/src/views/noHead/addBookmark/index.vue
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<template>
|
||||||
|
<div class="ssoAddBookmark">
|
||||||
|
正在添加,请稍后!!!
|
||||||
|
<!-- <button @click="closeIframe">关闭</button> -->
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import HttpUtil from "@/util/HttpUtil";
|
||||||
|
import { TREE_DATA, addNode } from "@/store/modules/treeData";
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
form: {
|
||||||
|
name: null,
|
||||||
|
url: null,
|
||||||
|
type: 0,
|
||||||
|
path: ""
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
//接受父节点传递的书签信息
|
||||||
|
window.addEventListener("message", event => {
|
||||||
|
if (!event.data.code) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log("收到content消息", event);
|
||||||
|
if (event.data.code == "addBookmarkAction") {
|
||||||
|
console.log("新增书签");
|
||||||
|
this.form.name = event.data.data.name;
|
||||||
|
this.form.url = event.data.data.url;
|
||||||
|
this.addBookmark();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
console.log("向父节点获取数据");
|
||||||
|
window.parent.postMessage({ code: "getBookmarkData", receiver: "content" }, "*");
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
closeIframe() {
|
||||||
|
window.parent.postMessage({ code: "closeIframe", receiver: "content" }, "*");
|
||||||
|
},
|
||||||
|
//新增书签
|
||||||
|
async addBookmark() {
|
||||||
|
let res = await HttpUtil.put("/bookmark", null, this.form);
|
||||||
|
this.$message.success("添加成功");
|
||||||
|
await this.$store.dispatch(TREE_DATA + "/" + addNode, { sourceNode: null, targetNode: res });
|
||||||
|
setTimeout(this.closeIframe, 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.ssoAddBookmark {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
background: white;
|
||||||
|
width: 100%;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
</style>
|
@ -9,6 +9,11 @@
|
|||||||
使用教程:
|
使用教程:
|
||||||
<a href="https://blog.fleyx.com/blog/detail/20220329" target="_blank">点击跳转</a>
|
<a href="https://blog.fleyx.com/blog/detail/20220329" target="_blank">点击跳转</a>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
浏览器插件:
|
||||||
|
<a href="/static/bookmarkBrowserPlugin.7z" download="浏览器插件.7z" target="_blank">点击下载</a>
|
||||||
|
,使用详情请参考使用教程
|
||||||
|
</div>
|
||||||
<div>交流反馈qq群:150056494,邮箱:fleyx20@outlook.com</div>
|
<div>交流反馈qq群:150056494,邮箱:fleyx20@outlook.com</div>
|
||||||
<div>
|
<div>
|
||||||
统计:
|
统计:
|
||||||
@ -38,7 +43,7 @@ export default {
|
|||||||
script.defer = true;
|
script.defer = true;
|
||||||
script.src = "https://qiezi.fleyx.com/qiezijs/1.0/qiezi_statistic.min.js";
|
script.src = "https://qiezi.fleyx.com/qiezijs/1.0/qiezi_statistic.min.js";
|
||||||
document.getElementsByTagName("head")[0].appendChild(script);
|
document.getElementsByTagName("head")[0].appendChild(script);
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
100
浏览器插件/bookmarkBrowserPlugin/background.js
Normal file
100
浏览器插件/bookmarkBrowserPlugin/background.js
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
chrome.runtime.onInstalled.addListener(() => {
|
||||||
|
chrome.contextMenus.create(
|
||||||
|
{
|
||||||
|
title: '添加到书签',
|
||||||
|
id: "addBookmark",
|
||||||
|
},
|
||||||
|
() => console.log("创建右键菜单成功")
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
chrome.contextMenus.onClicked.addListener(async function (info, tab) {
|
||||||
|
console.log(info, tab);
|
||||||
|
let body = {
|
||||||
|
name: tab.title,
|
||||||
|
url: tab.url,
|
||||||
|
};
|
||||||
|
sendToContent(tab.id, { code: "addBookmark", data: body, token: await getVal("token") });
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// 接收content/popup发送的消息
|
||||||
|
chrome.runtime.onMessage.addListener(async (data, sender, sendResponse) => {
|
||||||
|
if (!data.code || !data.receiver == 'background') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sendResponse("ok");
|
||||||
|
console.log("收到消息:", data, sender);
|
||||||
|
if (data.code == 'setToken') {
|
||||||
|
await setVal("token", data.data);
|
||||||
|
// sendToContent
|
||||||
|
await sendToContent(sender.tab.id, { code: "setTokenOk" });
|
||||||
|
} else if (data.code == 'getToken') {
|
||||||
|
let token = await getVal("token");
|
||||||
|
sendToPopup({ code: "setToken", data: await getVal("token") });
|
||||||
|
} else if (data.code == "clearToken") {
|
||||||
|
await clearVal("token");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 向content发送消息
|
||||||
|
* @param {*} tabId
|
||||||
|
* @param {*} data
|
||||||
|
*/
|
||||||
|
function sendToContent (tabId, data) {
|
||||||
|
console.log(tabId, data);
|
||||||
|
data.receiver = "content";
|
||||||
|
chrome.tabs.sendMessage(tabId, data, res => {
|
||||||
|
console.log("接受响应", res);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 向popup发送消息
|
||||||
|
* @param {*} data
|
||||||
|
*/
|
||||||
|
function sendToPopup (data) {
|
||||||
|
data.receiver = "popup";
|
||||||
|
chrome.runtime.sendMessage(data, res => console.log(res));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置值
|
||||||
|
* @param {*} key
|
||||||
|
* @param {*} val
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function setVal (key, val) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
chrome.storage.local.set({ [key]: val }, function () {
|
||||||
|
console.log("设置值成功:", key, val)
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取值
|
||||||
|
* @param {*} key
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function getVal (key) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
chrome.storage.local.get([key], function (res) {
|
||||||
|
console.log("取值成功", res);
|
||||||
|
resolve(res[key]);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearVal (key) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
chrome.storage.local.remove(key, function () {
|
||||||
|
console.log("remove成功", key);
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
26
浏览器插件/bookmarkBrowserPlugin/manifest.json
Normal file
26
浏览器插件/bookmarkBrowserPlugin/manifest.json
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"name": "签签世界",
|
||||||
|
"description": "云书签管理平台",
|
||||||
|
"version": "1.0",
|
||||||
|
"manifest_version": 3,
|
||||||
|
"permissions": ["contextMenus", "storage"],
|
||||||
|
"action": {
|
||||||
|
"default_popup": "popup/index.html"
|
||||||
|
},
|
||||||
|
"icons": {
|
||||||
|
"48": "static/icons/favicon.png",
|
||||||
|
"128": "static/icons/favicon.png"
|
||||||
|
},
|
||||||
|
"background": {
|
||||||
|
"service_worker": "background.js"
|
||||||
|
},
|
||||||
|
"options_ui": {
|
||||||
|
"page": "options/index.html"
|
||||||
|
},
|
||||||
|
"content_scripts": [
|
||||||
|
{
|
||||||
|
"matches": ["*://*/*"],
|
||||||
|
"js": ["static/js/axios.min.js", "static/js/config.js", "static/js/content.js"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
16
浏览器插件/bookmarkBrowserPlugin/options/index.html
Normal file
16
浏览器插件/bookmarkBrowserPlugin/options/index.html
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Document</title>
|
||||||
|
<style>
|
||||||
|
#content {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>option.html</h1>
|
||||||
|
</body>
|
||||||
|
</html>
|
31
浏览器插件/bookmarkBrowserPlugin/popup/index.html
Normal file
31
浏览器插件/bookmarkBrowserPlugin/popup/index.html
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Document</title>
|
||||||
|
<style>
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
padding: 0.2em;
|
||||||
|
width: 8em;
|
||||||
|
}
|
||||||
|
#content {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<a id="login" href="https://fleyx.com/userSpace/ssoAuth" target="_blank">点击登录</a>
|
||||||
|
<div id="action" style="display: none">
|
||||||
|
<button id="logout">退出登陆</button>
|
||||||
|
<!-- <button title="同步云端书签到浏览器(覆盖本地)">同步云端书签</button> -->
|
||||||
|
<!-- <button title="同步浏览器书签到云端(覆盖云端)">同步浏览器</button> -->
|
||||||
|
</div>
|
||||||
|
<p id="content"></p>
|
||||||
|
<div class="bottom">插件版本:<span id="version"></span></div>
|
||||||
|
<script type="text/javascript" src="/static/js/axios.min.js"></script>
|
||||||
|
<script type="text/javascript" src="/static/js/config.js"></script>
|
||||||
|
<script type="text/javascript" src="/popup/index.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
50
浏览器插件/bookmarkBrowserPlugin/popup/index.js
Normal file
50
浏览器插件/bookmarkBrowserPlugin/popup/index.js
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
console.log("asdf");
|
||||||
|
console.log(bookmarkHost);
|
||||||
|
|
||||||
|
var token;
|
||||||
|
var login = document.getElementById("login");
|
||||||
|
var action = document.getElementById("action");
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
//初始化
|
||||||
|
login.href = bookmarkHost + "/manage/sso/auth";
|
||||||
|
document.getElementById("version").innerText = version;
|
||||||
|
sendToBg("getToken", null);
|
||||||
|
})();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 退出登陆
|
||||||
|
*/
|
||||||
|
document.getElementById("logout").addEventListener("click", () => {
|
||||||
|
console.log("click");
|
||||||
|
sendToBg("clearToken", null);
|
||||||
|
action.style.display = "none";
|
||||||
|
login.style.display = "block";
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送消息到后台
|
||||||
|
* @param {*} data
|
||||||
|
*/
|
||||||
|
function sendToBg (code, data) {
|
||||||
|
chrome.runtime.sendMessage({ code, data, receiver: "background" }, res => console.log(res));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 接收content/background发送的消息
|
||||||
|
chrome.runtime.onMessage.addListener(async (data, sender, sendResponse) => {
|
||||||
|
if (!data.code || !data.receiver == 'popup') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sendResponse("ok");
|
||||||
|
console.log("popup收到消息:", data);
|
||||||
|
if (data.code == 'setToken') {
|
||||||
|
token = data.data;
|
||||||
|
if (token) {
|
||||||
|
action.style.display = "block";
|
||||||
|
login.style.display = "none";
|
||||||
|
} else {
|
||||||
|
login.style.display = "block";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
BIN
浏览器插件/bookmarkBrowserPlugin/static/icons/favicon.ico
Normal file
BIN
浏览器插件/bookmarkBrowserPlugin/static/icons/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
BIN
浏览器插件/bookmarkBrowserPlugin/static/icons/favicon.png
Normal file
BIN
浏览器插件/bookmarkBrowserPlugin/static/icons/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 62 KiB |
2
浏览器插件/bookmarkBrowserPlugin/static/js/axios.min.js
vendored
Normal file
2
浏览器插件/bookmarkBrowserPlugin/static/js/axios.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
10
浏览器插件/bookmarkBrowserPlugin/static/js/config.js
Normal file
10
浏览器插件/bookmarkBrowserPlugin/static/js/config.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
var bookmarkHost = "https://fleyx.com";
|
||||||
|
// var bookmarkHost = "http://localhost:8080";
|
||||||
|
|
||||||
|
var version = "0.1";
|
||||||
|
|
||||||
|
window.token = localStorage.getItem('token');
|
||||||
|
axios.defaults.baseURL = bookmarkHost + '/bookmark/api';
|
||||||
|
axios.defaults.headers.common['jwt-token'] = window.token;
|
||||||
|
axios.defaults.headers.post['Content-Type'] = 'application/json';
|
||||||
|
axios.defaults.headers.put['Content-Type'] = 'application/json';
|
98
浏览器插件/bookmarkBrowserPlugin/static/js/content.js
Normal file
98
浏览器插件/bookmarkBrowserPlugin/static/js/content.js
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
console.log('注入了页面');
|
||||||
|
|
||||||
|
var bookmarkInfo = null;
|
||||||
|
var addBlockDiv = null;
|
||||||
|
var iframe = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接收当前注入页面传来的消息
|
||||||
|
*/
|
||||||
|
window.addEventListener('message', function (event) {
|
||||||
|
console.log(event);
|
||||||
|
if (event.data.code === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log('接受到网页消息:', event.data);
|
||||||
|
if (event.data.code === 'getBookmarkData') {
|
||||||
|
iframe.contentWindow.postMessage({ code: "addBookmarkAction", data: bookmarkInfo }, "*");
|
||||||
|
} else if (event.data.code === 'setToken') {
|
||||||
|
sendToBg(event.data);
|
||||||
|
} else if (event.data.code == 'closeIframe') {
|
||||||
|
addBlockDiv.remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接收content/background发送的消息
|
||||||
|
*/
|
||||||
|
chrome.runtime.onMessage.addListener(async (data, sender, sendResponse) => {
|
||||||
|
if (!data || !data.code || data.receiver != "content") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sendResponse("ok");
|
||||||
|
console.log('收到消息:', data);
|
||||||
|
if (data.code == 'setTokenOk') {
|
||||||
|
sendToPage(data);
|
||||||
|
} else if (data.code == 'addBookmark') {
|
||||||
|
await addBookmark(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
async function addBookmark (data) {
|
||||||
|
if (!checkTokenValid(data.token)) {
|
||||||
|
alert("登陆失效,请登陆后,重试");
|
||||||
|
window.open(bookmarkHost + "/manage/sso/auth");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//新增书签
|
||||||
|
console.log("新增书签", data.data);
|
||||||
|
bookmarkInfo = data.data;
|
||||||
|
addBlockDiv = document.createElement("div");
|
||||||
|
addBlockDiv.setAttribute("style", "position:fixed;width:100%;height:100vh;z-index:100000;left:0;top:0;background:rgba(211, 211, 205, 0.8)");
|
||||||
|
document.getElementsByTagName("body")[0].appendChild(addBlockDiv);
|
||||||
|
iframe = document.createElement("iframe");
|
||||||
|
iframe.src = bookmarkHost + "/noHead/addBookmark?token=" + data.token;
|
||||||
|
iframe.setAttribute("style", "width:70%;min-height:60vh;margin-left:15%;margin-top:10vh;padding:0;border:0;border-radius:10px");
|
||||||
|
addBlockDiv.appendChild(iframe);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送消息给bg
|
||||||
|
* @param {*} data
|
||||||
|
*/
|
||||||
|
function sendToBg (data) {
|
||||||
|
data.receiver = "background";
|
||||||
|
chrome.runtime.sendMessage(data, response => {
|
||||||
|
console.log(response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发消息到页面
|
||||||
|
* @param {*} data
|
||||||
|
*/
|
||||||
|
function sendToPage (data) {
|
||||||
|
data.receiver = "page";
|
||||||
|
window.postMessage(data, "*");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查token是否有效
|
||||||
|
* @param {*} token
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function checkTokenValid (token) {
|
||||||
|
try {
|
||||||
|
if (token && token.trim().length > 0) {
|
||||||
|
//检查token是否还有效
|
||||||
|
let content = JSON.parse(window.atob(token.split(".")[1]));
|
||||||
|
if (content.exp > Date.now() / 1000) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
2
浏览器插件/bookmarkBrowserPlugin/static/js/jquery.js
vendored
Normal file
2
浏览器插件/bookmarkBrowserPlugin/static/js/jquery.js
vendored
Normal file
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user