Merge branch 'feature/1.7.1'

This commit is contained in:
aoshiguchen
2023-03-20 14:25:11 +08:00
34 changed files with 954 additions and 803 deletions
+1
View File
@@ -8,6 +8,7 @@ nodeVersion=v13.12.0
deployDir="deploy"
serverDeployDir=$deployDir"/server"
adminDeployDir=$serverDeployDir"/neutrino-proxy-admin"
giteePagesDir=$deployDir"/gitee-pages"
#设置nvm生效
export NVM_DIR="$HOME/.nvm"
+8
View File
@@ -0,0 +1,8 @@
# 管理后台问题汇总
## 问题1...
> 描述、解决方式
# 代理服务端问题汇总
# 代理客户端问题汇总
+16
View File
@@ -0,0 +1,16 @@
import request from '@/utils/request'
export function fetchUserFlowReportList(query) {
return request({
url: '/report/user/flow-report/page',
method: 'get',
params: query
})
}
export function fetchLicenseFlowReportList(query) {
return request({
url: '/report/license/flow-report/page',
method: 'get',
params: query
})
}
+8 -2
View File
@@ -56,7 +56,10 @@ export default {
jobLog: '调度日志',
log: '日志管理',
loginLog: '登录日志',
clientConnectLog: '客户端连接日志'
clientConnectLog: '客户端连接日志',
report: '报表管理',
userFlowReport: '用户流量报表',
licenseFlowReport: 'License流量报表'
},
navbar: {
logOut: '退出登录',
@@ -153,7 +156,10 @@ export default {
msg: '消息',
outcome: '结果',
err: '异常信息',
resetKey: '重置Key'
resetKey: '重置Key',
upFlow: '上行流量',
downFlow: '下行流量',
totalFlow: '总流量'
},
button: {
lookOver: '查看'
+14
View File
@@ -80,6 +80,20 @@ export const asyncRouterMap = [
{ path: 'jobManager', component: _import('system/jobManager'), name: 'jobManager', meta: { title: 'jobManager' }}
]
},
{
path: '/report',
component: Layout,
redirect: 'noredirect',
name: 'report',
meta: {
title: 'report',
icon: 'component'
},
children: [
{ path: 'userFlowReport', component: _import('report/userFlowReport'), name: 'userFlowReport', meta: { title: 'userFlowReport' }},
{ path: 'licenseFlowReport', component: _import('report/licenseFlowReport'), name: 'licenseFlowReport', meta: { title: 'licenseFlowReport' }}
]
},
{
path: '/log',
component: Layout,
@@ -0,0 +1,128 @@
<template>
<div class="app-container calendar-list-container">
<div class="filter-container">
<el-select v-model="listQuery.userId" placeholder="请选择用户" clearable>
<el-option v-for="item in userList" :key="item.id" :label="item.name" :value="item.id"/>
</el-select>
<el-button type="primary" v-waves icon="el-icon-search" @click="handleFilter">{{$t('table.search')}}</el-button>
</div>
<el-table :key='tableKey' :data="list" v-loading="listLoading" element-loading-text="给我一点时间" border fit highlight-current-row style="width: 100%">
<el-table-column type="index" width="100" :label="$t('table.id')"></el-table-column>
<el-table-column align="center" :label="$t('table.userName')" min-width="120">
<template slot-scope="scope">
<span>{{scope.row.userName}}</span>
</template>
</el-table-column>
<el-table-column align="center" :label="$t('table.licenseName')" min-width="120">
<template slot-scope="scope">
<span>{{scope.row.licenseName}}</span>
</template>
</el-table-column>
<el-table-column align="center" :label="$t('table.upFlow')" min-width="120">
<template slot-scope="scope">
<span>{{scope.row.upFlowDesc}}</span>
</template>
</el-table-column>
<el-table-column align="center" :label="$t('table.downFlow')" min-width="120">
<template slot-scope="scope">
<span>{{scope.row.downFlowDesc}}</span>
</template>
</el-table-column>
<el-table-column align="center" :label="$t('table.totalFlow')" min-width="120">
<template slot-scope="scope">
<span>{{scope.row.totalFlowDesc}}</span>
</template>
</el-table-column>
</el-table>
<div class="pagination-container">
<el-pagination background @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-pageInfo.sync="listQuery.current"
:pageInfo-sizes="[10,20,30, 50]" :pageInfo-size="listQuery.size" layout="total, sizes, prev, pager, next, jumper" :total="total">
</el-pagination>
</div>
</div>
</template>
<script>
import { fetchLicenseFlowReportList } from '@/api/report'
import { userList } from '@/api/user'
import waves from '@/directive/waves'
export default {
name: 'jobLog',
directives: {
waves
},
data() {
return {
tableKey: 0,
list: null,
total: null,
listLoading: false,
listQuery: {
current: 1,
size: 10,
jobId: undefined
},
userList: [],
dialogVisible: false,
selectRow: {}
}
},
filters: {
},
created() {
this.getList()
this.getUserList()
},
activated() {
this.getUserList()
if (this.$route.query.jobId) {
this.listQuery.jobId = this.$route.query.jobId
this.getList()
}
},
methods: {
getList() {
this.listLoading = true
fetchLicenseFlowReportList(this.listQuery).then(response => {
this.list = response.data.data.records
this.total = response.data.data.total
this.listLoading = false
})
},
getUserList() {
userList().then(response => {
this.userList = response.data.data
})
},
handleFilter() {
this.listQuery.current = 1
this.getList()
},
handleSizeChange(val) {
this.listQuery.size = val
this.listQuery.current = 1
this.getList()
},
handleCurrentChange(val) {
this.listQuery.current = val
this.getList()
},
handleShowClick(row) {
console.log(row)
},
handleLookOver(row) {
this.selectRow = row
this.dialogVisible = true
}
}
}
</script>
<style>
.job-msg-div{
max-height: 400px;
overflow-y: auto;
}
</style>
@@ -0,0 +1,123 @@
<template>
<div class="app-container calendar-list-container">
<div class="filter-container">
<el-select v-model="listQuery.userId" placeholder="请选择用户" clearable>
<el-option v-for="item in userList" :key="item.id" :label="item.name" :value="item.id"/>
</el-select>
<el-button type="primary" v-waves icon="el-icon-search" @click="handleFilter">{{$t('table.search')}}</el-button>
</div>
<el-table :key='tableKey' :data="list" v-loading="listLoading" element-loading-text="给我一点时间" border fit highlight-current-row style="width: 100%">
<el-table-column type="index" width="100" :label="$t('table.id')"></el-table-column>
<el-table-column align="center" :label="$t('table.userName')" min-width="120">
<template slot-scope="scope">
<span>{{scope.row.userName}}</span>
</template>
</el-table-column>
<el-table-column align="center" :label="$t('table.upFlow')" min-width="120">
<template slot-scope="scope">
<span>{{scope.row.upFlowDesc}}</span>
</template>
</el-table-column>
<el-table-column align="center" :label="$t('table.downFlow')" min-width="120">
<template slot-scope="scope">
<span>{{scope.row.downFlowDesc}}</span>
</template>
</el-table-column>
<el-table-column align="center" :label="$t('table.totalFlow')" min-width="120">
<template slot-scope="scope">
<span>{{scope.row.totalFlowDesc}}</span>
</template>
</el-table-column>
</el-table>
<div class="pagination-container">
<el-pagination background @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-pageInfo.sync="listQuery.current"
:pageInfo-sizes="[10,20,30, 50]" :pageInfo-size="listQuery.size" layout="total, sizes, prev, pager, next, jumper" :total="total">
</el-pagination>
</div>
</div>
</template>
<script>
import { fetchUserFlowReportList } from '@/api/report'
import { userList } from '@/api/user'
import waves from '@/directive/waves' // 水波纹指令
export default {
name: 'jobLog',
directives: {
waves
},
data() {
return {
tableKey: 0,
list: null,
total: null,
listLoading: false,
listQuery: {
current: 1,
size: 10,
jobId: undefined
},
userList: [],
dialogVisible: false,
selectRow: {}
}
},
filters: {
},
created() {
this.getList()
this.getUserList()
},
activated() {
this.getUserList()
if (this.$route.query.jobId) {
this.listQuery.jobId = this.$route.query.jobId
this.getList()
}
},
methods: {
getList() {
this.listLoading = true
fetchUserFlowReportList(this.listQuery).then(response => {
this.list = response.data.data.records
this.total = response.data.data.total
this.listLoading = false
})
},
getUserList() {
userList().then(response => {
this.userList = response.data.data
})
},
handleFilter() {
this.listQuery.current = 1
this.getList()
},
handleSizeChange(val) {
this.listQuery.size = val
this.listQuery.current = 1
this.getList()
},
handleCurrentChange(val) {
this.listQuery.current = val
this.getList()
},
handleShowClick(row) {
console.log(row)
},
handleLookOver(row) {
this.selectRow = row
this.dialogVisible = true
}
}
}
</script>
<style>
.job-msg-div{
max-height: 400px;
overflow-y: auto;
}
</style>
@@ -0,0 +1,12 @@
package fun.asgc.neutrino.proxy.server.constant;
/**
* @author: aoshiguchen
* @date: 2023/3/18
*/
public interface Constants {
/**
* 默认的端口分组ID
*/
int DEFAULT_PORT_GROUP_ID = 1;
}
@@ -29,5 +29,6 @@ import lombok.Data;
*/
@Data
public class LicenseFlowReportReq {
private Integer userId;
private Integer licenseId;
}
@@ -29,5 +29,8 @@ import lombok.Data;
*/
@Data
public class UserFlowReportReq {
/**
* 用户ID
*/
private Integer userId;
}
@@ -22,6 +22,7 @@
package fun.asgc.neutrino.proxy.server.controller.res;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Date;
@@ -29,6 +30,7 @@ import java.util.Date;
* @author: aoshiguchen
* @date: 2022/12/21
*/
@Accessors(chain = true)
@Data
public class LicenseFlowReportRes {
/**
@@ -48,31 +50,27 @@ public class LicenseFlowReportRes {
*/
private String licenseName;
/**
* 写入字节数
* 上行流量字节数
*/
private Long writeBytes;
private Long upFlowBytes;
/**
* 读取字节数
* 下行流量字节数
*/
private Long readBytes;
private Long downFlowBytes;
/**
* 写入流量描述
* 总流量字节数
*/
private String writeFlowStr;
private Long totalFlowBytes;
/**
* 读取流量描述
* 上行流量描述
*/
private String readFlowStr;
private String upFlowDesc;
/**
* 流量描述
* 下行流量描述
*/
private String flowStr;
private String downFlowDesc;
/**
* 报表时间
* 总流量描述
*/
private Date date;
/**
* 创建时间
*/
private Date createTime;
private String totalFlowDesc;
}
@@ -22,6 +22,7 @@
package fun.asgc.neutrino.proxy.server.controller.res;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Date;
@@ -29,6 +30,7 @@ import java.util.Date;
* @author: aoshiguchen
* @date: 2022/12/21
*/
@Accessors(chain = true)
@Data
public class UserFlowReportRes {
/**
@@ -40,39 +42,27 @@ public class UserFlowReportRes {
*/
private String userName;
/**
* 历史写入字节数
* 上行流量字节数
*/
private Long historyWriteBytes;
private Long upFlowBytes;
/**
* 历史读取字节数
* 下行流量字节数
*/
private Long historyReadBytes;
private Long downFlowBytes;
/**
* 写入字节数
* 总流量字节数
*/
private Long writeBytes;
private Long totalFlowBytes;
/**
* 读取字节数
* 上行流量描述
*/
private Long readBytes;
private String upFlowDesc;
/**
* 写入流量描述
* 下行流量描述
*/
private String writeFlowStr;
private String downFlowDesc;
/**
* 读取流量描述
* 流量描述
*/
private String readFlowStr;
/**
* 流量描述
*/
private String flowStr;
/**
* 报表时间
*/
private Date date;
/**
* 创建时间
*/
private Date createTime;
private String totalFlowDesc;
}
@@ -0,0 +1,31 @@
package fun.asgc.neutrino.proxy.server.dal;
import fun.asgc.neutrino.proxy.server.controller.req.LicenseFlowReportReq;
import fun.asgc.neutrino.proxy.server.controller.req.UserFlowReportReq;
import fun.asgc.neutrino.proxy.server.controller.res.LicenseFlowReportRes;
import fun.asgc.neutrino.proxy.server.controller.res.UserFlowReportRes;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.Date;
import java.util.List;
/**
* @author: aoshiguchen
* @date: 2023/3/19
*/
@Mapper
public interface ReportMapper {
/**
* 基于用户维度的流量报表
* @param userId
* @return
*/
List<UserFlowReportRes> userFlowReportList(@Param("userId") Integer userId, @Param("curMonthBeginDate") Date curMonthBeginDate, @Param("curDayBeginDate") Date curDayBeginDate, @Param("curDate") Date curDate);
/**
* 基于用户维度的流量报表
* @param userId
* @return
*/
List<LicenseFlowReportRes> licenseFLowReportList(@Param("userId") Integer userId, @Param("curMonthBeginDate") Date curMonthBeginDate, @Param("curDayBeginDate") Date curDayBeginDate, @Param("curDate") Date curDate);
}
@@ -7,6 +7,8 @@ import fun.asgc.neutrino.proxy.server.base.page.PageInfo;
import fun.asgc.neutrino.proxy.server.base.page.PageQuery;
import fun.asgc.neutrino.proxy.server.base.rest.ServiceException;
import fun.asgc.neutrino.proxy.server.base.rest.SystemContextHolder;
import fun.asgc.neutrino.proxy.server.constant.Constants;
import fun.asgc.neutrino.proxy.server.constant.EnableStatusEnum;
import fun.asgc.neutrino.proxy.server.constant.ExceptionConstant;
import fun.asgc.neutrino.proxy.server.controller.req.PortGroupCreateReq;
import fun.asgc.neutrino.proxy.server.controller.req.PortGroupListReq;
@@ -54,7 +56,7 @@ public class PortGroupService {
portGroupDO.setName(req.getName());
portGroupDO.setPossessorType(req.getPossessorType());
portGroupDO.setPossessorId(req.getPossessorId());
portGroupDO.setEnable(1);
portGroupDO.setEnable(EnableStatusEnum.ENABLE.getStatus());
portGroupDO.setCreateTime(now);
portGroupDO.setUpdateTime(now);
portGroupMapper.insert(portGroupDO);
@@ -85,7 +87,7 @@ public class PortGroupService {
}
public void delete(Integer id) {
if (id == 1) {
if (id == Constants.DEFAULT_PORT_GROUP_ID) {
throw ServiceException.create(ExceptionConstant.DEFAULT_GROUP_FORBID_DELETE);
}
PortGroupDO portGroupDO = portGroupMapper.selectById(id);
@@ -96,7 +98,7 @@ public class PortGroupService {
//修改绑定此分组的端口到默认分组
portPoolMapper.update(null, Wrappers.lambdaUpdate(PortPoolDO.class)
.eq(PortPoolDO::getGroupId, portGroupDO.getId())
.set(PortPoolDO::getGroupId, 1)
.set(PortPoolDO::getGroupId, Constants.DEFAULT_PORT_GROUP_ID)
.set(PortPoolDO::getUpdateTime, new Date())
);
}
@@ -1,13 +1,28 @@
package fun.asgc.neutrino.proxy.server.service;
import cn.hutool.core.collection.CollectionUtil;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.google.common.collect.Lists;
import fun.asgc.neutrino.proxy.core.util.DateUtil;
import fun.asgc.neutrino.proxy.server.base.db.DbConfig;
import fun.asgc.neutrino.proxy.server.base.page.PageInfo;
import fun.asgc.neutrino.proxy.server.base.page.PageQuery;
import fun.asgc.neutrino.proxy.server.controller.req.LicenseFlowReportReq;
import fun.asgc.neutrino.proxy.server.controller.req.UserFlowReportReq;
import fun.asgc.neutrino.proxy.server.controller.res.JobLogListRes;
import fun.asgc.neutrino.proxy.server.controller.res.LicenseFlowReportRes;
import fun.asgc.neutrino.proxy.server.controller.res.UserFlowReportRes;
import fun.asgc.neutrino.proxy.server.dal.ReportMapper;
import fun.asgc.neutrino.proxy.server.util.FormatUtil;
import lombok.extern.slf4j.Slf4j;
import ma.glasnost.orika.MapperFacade;
import org.apache.ibatis.solon.annotation.Db;
import org.noear.solon.annotation.Component;
import org.noear.solon.annotation.Inject;
import java.util.Date;
import java.util.List;
/**
* @author: aoshiguchen
@@ -16,6 +31,12 @@ import org.noear.solon.annotation.Component;
@Slf4j
@Component
public class ReportService {
@Inject
private MapperFacade mapperFacade;
@Db
private ReportMapper reportMapper;
@Inject
private DbConfig dbConfig;
/**
* 用户流量报表分页
@@ -24,8 +45,11 @@ public class ReportService {
* @return
*/
public PageInfo<UserFlowReportRes> userFlowReportPage(PageQuery pageQuery, UserFlowReportReq req) {
// TODO
return null;
Page<UserFlowReportRes> result = PageHelper.startPage(pageQuery.getCurrent(), pageQuery.getSize());
Date now = new Date();
List<UserFlowReportRes> list = reportMapper.userFlowReportList(req.getUserId(), DateUtil.getMonthBegin(now), DateUtil.getDayBegin(now), now);
fillUserFlowReport(list);
return PageInfo.of(list, result.getTotal(), pageQuery.getCurrent(), pageQuery.getSize());
}
/**
@@ -35,7 +59,44 @@ public class ReportService {
* @return
*/
public PageInfo<LicenseFlowReportRes> licenseFlowReportPage(PageQuery pageQuery, LicenseFlowReportReq req) {
// TODO
return null;
Page<LicenseFlowReportRes> result = PageHelper.startPage(pageQuery.getCurrent(), pageQuery.getSize());
Date now = new Date();
List<LicenseFlowReportRes> list = reportMapper.licenseFLowReportList(req.getUserId(), DateUtil.getMonthBegin(now), DateUtil.getDayBegin(now), now);
fillLicenseFlowReport(list);
return PageInfo.of(list, result.getTotal(), pageQuery.getCurrent(), pageQuery.getSize());
}
private void fillUserFlowReport(List<UserFlowReportRes> list) {
if (CollectionUtil.isEmpty(list)) {
return;
}
for (UserFlowReportRes item : list) {
long upFlowBytes = (null == item.getUpFlowBytes()) ? 0 : item.getUpFlowBytes();
long downFlowBytes = (null == item.getDownFlowBytes()) ? 0 : item.getDownFlowBytes();
long totalFlowBytes = upFlowBytes + downFlowBytes;
item.setUpFlowBytes(upFlowBytes);
item.setDownFlowBytes(downFlowBytes);
item.setTotalFlowBytes(totalFlowBytes);
item.setUpFlowDesc(FormatUtil.getSizeDescByByteCount(upFlowBytes));
item.setDownFlowDesc(FormatUtil.getSizeDescByByteCount(downFlowBytes));
item.setTotalFlowDesc(FormatUtil.getSizeDescByByteCount(totalFlowBytes));
}
}
private void fillLicenseFlowReport(List<LicenseFlowReportRes> list) {
if (CollectionUtil.isEmpty(list)) {
return;
}
for (LicenseFlowReportRes item : list) {
long upFlowBytes = (null == item.getUpFlowBytes()) ? 0 : item.getUpFlowBytes();
long downFlowBytes = (null == item.getDownFlowBytes()) ? 0 : item.getDownFlowBytes();
long totalFlowBytes = upFlowBytes + downFlowBytes;
item.setUpFlowBytes(upFlowBytes);
item.setDownFlowBytes(downFlowBytes);
item.setTotalFlowBytes(totalFlowBytes);
item.setUpFlowDesc(FormatUtil.getSizeDescByByteCount(upFlowBytes));
item.setDownFlowDesc(FormatUtil.getSizeDescByByteCount(downFlowBytes));
item.setTotalFlowDesc(FormatUtil.getSizeDescByByteCount(totalFlowBytes));
}
}
}
@@ -0,0 +1,49 @@
package fun.asgc.neutrino.proxy.server.util;
/**
* @author: aoshiguchen
* @date: 2023/3/19
*/
public class FormatUtil {
private static final String[] SIZE_UNINTS = {"B", "KB", "MB", "GB", "TB"};
private static final int SIZE_SYSTEM = 1024;
/**
* 根据字节数获取大小描述
* 1、小于1024字节的以B为单位
* 2、小于1024KB的以KB为单位
* 3、小于1024M的以MB为单位
* 4、小于1024G的以GB为单位
* 5、其他以TB为单位
* @param byteCount
* @return
*/
public static String getSizeDescByByteCount(long byteCount){
if(byteCount <= 0){
return "0B";
}
double res = byteCount;
int index = 0;
while (index < SIZE_UNINTS.length && res >= SIZE_SYSTEM){
res /= SIZE_SYSTEM;
index++;
}
if(index >= SIZE_UNINTS.length){
index = SIZE_UNINTS.length - 1;
res *= 1024;
}
return trimZero(String.format("%.2f", res)) + SIZE_UNINTS[index];
}
private static String trimZero(String s) {
if (s.indexOf(".") > 0) {
// 去掉多余的0
s = s.replaceAll("0+?$", "");
// 如最后一位是.则去掉
s = s.replaceAll("[.]$", "");
}
return s;
}
}
@@ -9,7 +9,7 @@
u.`name`
WHEN g.possessor_type = 2 THEN
l.`name`
ELSE ""
ELSE ''
END possessor
FROM port_group g
LEFT JOIN `user` u ON g.possessor_id = u.id
@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="fun.asgc.neutrino.proxy.server.dal.ReportMapper">
<select id="userFlowReportList" resultType="fun.asgc.neutrino.proxy.server.controller.res.UserFlowReportRes">
SELECT
u.id AS 'userId',
u.name AS 'userName',
SUM(frm.write_bytes + frd.write_bytes + frm2.write_bytes) AS 'upFlowBytes',
SUM(frm.read_bytes + frd.read_bytes + frm2.read_bytes) AS 'downFlowBytes',
SUM(frm.write_bytes) monthWriteBytes,
SUM(frm.read_bytes) monthReadBytes,
SUM(frd.write_bytes) dayWriteBytes,
SUM(frd.read_bytes) dayReadBytes,
SUM(frm2.write_bytes) minuteWriteBytes,
SUM(frm2.read_bytes) minuteReadBytes
FROM `user` u
LEFT JOIN (SELECT user_id,sum(write_bytes) write_bytes,sum(read_bytes) read_bytes from flow_report_month GROUP BY user_id) frm ON u.id = frm.user_id
LEFT JOIN (SELECT user_id,sum(write_bytes) write_bytes,sum(read_bytes) read_bytes from flow_report_day
WHERE date >= #{curMonthBeginDate} AND date &lt;= #{curDayBeginDate}
GROUP BY user_id) frd ON u.id = frd.user_id
LEFT JOIN (SELECT user_id,sum(write_bytes) write_bytes,sum(read_bytes) read_bytes from flow_report_minute
WHERE date >= #{curDayBeginDate} AND date &lt;= #{curDate}
GROUP BY user_id) frm2 ON u.id = frm2.user_id
<where>
<if test="userId != null">
AND u.id = #{userId}
</if>
</where>
GROUP BY u.id
</select>
<select id="licenseFLowReportList" resultType="fun.asgc.neutrino.proxy.server.controller.res.LicenseFlowReportRes">
SELECT
l.id AS 'licenseId',
l.name AS 'licenseName',
u.id AS 'userId',
u.name AS 'userName',
SUM(frm.write_bytes + frd.write_bytes + frm2.write_bytes) AS 'upFlowBytes',
SUM(frm.read_bytes + frd.read_bytes + frm2.read_bytes) AS 'downFlowBytes',
SUM(frm.write_bytes) monthWriteBytes,
SUM(frm.read_bytes) monthReadBytes,
SUM(frd.write_bytes) dayWriteBytes,
SUM(frd.read_bytes) dayReadBytes,
SUM(frm2.write_bytes) minuteWriteBytes,
SUM(frm2.read_bytes) minuteReadBytes
FROM `license` l
LEFT JOIN `user` u ON l.user_id = u.id
LEFT JOIN (SELECT license_id,sum(write_bytes) write_bytes,sum(read_bytes) read_bytes from flow_report_month GROUP BY license_id) frm ON l.id = frm.license_id
LEFT JOIN (SELECT license_id,sum(write_bytes) write_bytes,sum(read_bytes) read_bytes from flow_report_day
WHERE date >= #{curMonthBeginDate} AND #{curDayBeginDate}
GROUP BY license_id) frd ON l.id = frd.license_id
LEFT JOIN (SELECT license_id,sum(write_bytes) write_bytes,sum(read_bytes) read_bytes from flow_report_minute
WHERE date >= #{curDayBeginDate} AND date &lt;= #{curDate}
GROUP BY license_id)frm2 ON l.id = frm2.license_id
<where>
<if test="userId != null">
AND u.id = #{userId}
</if>
</where>
GROUP BY l.id
</select>
</mapper>
@@ -0,0 +1,13 @@
ALTER TABLE port_pool ADD group_id INT NOT NULL DEFAULT 1 COMMENT "分组ID";
#
CREATE TABLE IF NOT EXISTS `port_group` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`name` varchar(255) NOT NULL COMMENT '分组名称',
`possessor_type` int NOT NULL DEFAULT '0' COMMENT '所有者类型 (0、全局共享 1、用户所有 2License所有) ',
`possessor_id` int NOT NULL DEFAULT '-1' COMMENT '所有者id(当type为0时 固定为-1、当type为1时为用户id 、当type为2时为licenseid)',
`enable` int NOT NULL COMMENT '是否启用(1、启用 2、禁用)',
`create_time` datetime(3) NOT NULL COMMENT '创建时间',
`update_time` datetime(3) NOT NULL COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='端口分组';
@@ -0,0 +1,12 @@
ALTER TABLE port_pool ADD group_id INTEGER NOT NULL DEFAULT 1;
#
CREATE TABLE IF NOT EXISTS `port_group` (
`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
`name` VARCHAR(255) NOT NULL,
`possessor_type` INTEGER NOT NULL DEFAULT '0',
`possessor_id` INTEGER NOT NULL DEFAULT '-1',
`enable` INTEGER NOT NULL,
`create_time` datetime(3) NOT NULL,
`update_time` datetime(3) NOT NULL
);
+232 -225
View File
@@ -4,236 +4,243 @@ const htmlModules = require('./config/htmlModules.js');
module.exports = {
theme: 'vdoing', // 使用依赖包主题
// theme: require.resolve('../../vdoing'), // 使用本地主题 (先将vdoing主题文件下载到本地:https://github.com/xugaoyi/vuepress-theme-vdoing)
theme: 'vdoing', // 使用依赖包主题
// theme: require.resolve('../../vdoing'), // 使用本地主题 (先将vdoing主题文件下载到本地:https://github.com/xugaoyi/vuepress-theme-vdoing)
title: "中微子代理",
description: '一个基于VuePress的 知识管理&博客 主题',
// base: '/', // 默认'/'。如果你想将你的网站部署到如 https://foo.github.io/bar/,那么 base 应该被设置成 "/bar/",(否则页面将失去样式等文件)
head: [ // 注入到页面<head> 中的标签,格式[tagName, { attrName: attrValue }, innerHTML?]
['link', { rel: 'icon', href: '/img/favicon.ico' }], //favicons,资源放在public文件夹
['meta', { name: 'keywords', content: 'vuepress,theme,blog,vdoing' }],
['meta', { name: 'theme-color', content: '#11a8cd' }], // 移动浏览器主题颜色
title: "中微子代理",
description: '一个基于 netty 的、开源的 java 内网穿透项目',
// base: '/', // 默认'/'。如果你想将你的网站部署到如 https://foo.github.io/bar/,那么 base 应该被设置成 "/bar/",(否则页面将失去样式等文件)
head: [ // 注入到页面<head> 中的标签,格式[tagName, { attrName: attrValue }, innerHTML?]
['link', {rel: 'icon', href: '/img/favicon.ico'}], //favicons,资源放在public文件夹
['meta', {name: 'keywords', content: 'vuepress,theme,blog,vdoing'}],
['meta', {name: 'theme-color', content: '#11a8cd'}], // 移动浏览器主题颜色
['meta', { name: 'wwads-cn-verify', content: '6c4b761a28b734fe93831e3fb400ce87' }], // 广告相关,你可以去掉
['script', { src: 'https://cdn.wwads.cn/js/makemoney.js', type: 'text/javascript' }], // 广告相关,你可以去掉
],
// ['meta', { name: 'wwads-cn-verify', content: '6c4b761a28b734fe93831e3fb400ce87' }], // 广告相关,你可以去掉
// ['script', { src: 'https://cdn.wwads.cn/js/makemoney.js', type: 'text/javascript' }], // 广告相关,你可以去掉
],
// 主题配置
themeConfig: {
// nav: [
// { text: '首页', link: '/' },
// {
// text: '指南', link: '/pages/a2f161/', items: [
// { text: '主题初衷与诞生', link: '/pages/52d5c3/' },
// { text: '介绍', link: '/pages/a2f161/' },
// { text: '快速上手', link: '/pages/793dcb/' },
// { text: '目录结构', link: '/pages/2f674a/' },
// { text: '核心配置和约定', link: '/pages/33d574/' },
// { text: '自动生成front matter', link: '/pages/088c16/' },
// { text: 'Markdown 容器', link: '/pages/d0d7eb/' },
// { text: 'Markdown 中使用组件', link: '/pages/197691/' },
// {
// text: '相关文章', items: [
// { text: '使目录栏支持h2~h6标题', link: '/pages/8dfab5/' },
// { text: '如何让你的笔记更有表现力', link: '/pages/dd027d/' },
// { text: '批量操作front matter工具', link: '/pages/2b8e22/' },
// { text: '部署', link: '/pages/0fc1d2/' },
// { text: '关于写文章和H1标题', link: '/pages/9ae0bd/' },
// { text: '关于博客搭建与管理', link: '/pages/26997d/' },
// { text: '在线编辑和新增文章的方法', link: '/pages/c5a54d/' },
// ]
// }
// ]
// },
// {
// text: '配置', link: '/pages/a20ce8/', items: [
// { text: '主题配置', link: '/pages/a20ce8/' },
// { text: '首页配置', link: '/pages/f14bdb/' },
// { text: 'front matter配置', link: '/pages/3216b0/' },
// { text: '目录页配置', link: '/pages/54651a/' },
// { text: '添加摘要', link: '/pages/1cc523/' },
// { text: '修改主题颜色和样式', link: '/pages/f51918/' },
// { text: '评论栏', link: '/pages/ce175c/' },
// ]
// },
// { text: '资源', link: '/pages/db78e2/' },
// { text: '案例', link: '/pages/5d571c/' },
// { text: '问答', link: '/pages/9cc27d/' },
// { text: '赞助', link: '/pages/1b12ed/' },
// ],
nav: [
{ text: '首页', link: '/' },
{
text: '使用教程', link: '/pages/a2f161/', items: [
{ text: '主题初衷与诞生', link: '/pages/52d5c3/' },
{ text: '介绍', link: '/pages/a2f161/' },
{ text: '快速上手', link: '/pages/793dcb/' },
{ text: '目录结构', link: '/pages/2f674a/' },
{ text: '核心配置和约定', link: '/pages/33d574/' },
{ text: '自动生成front matter', link: '/pages/088c16/' },
{ text: 'Markdown 容器', link: '/pages/d0d7eb/' },
{ text: 'Markdown 中使用组件', link: '/pages/197691/' }
// 主题配置
themeConfig: {
// nav: [
// { text: '首页', link: '/' },
// {
// text: '指南', link: '/pages/a2f161/', items: [
// { text: '主题初衷与诞生', link: '/pages/52d5c3/' },
// { text: '介绍', link: '/pages/a2f161/' },
// { text: '快速上手', link: '/pages/793dcb/' },
// { text: '目录结构', link: '/pages/2f674a/' },
// { text: '核心配置和约定', link: '/pages/33d574/' },
// { text: '自动生成front matter', link: '/pages/088c16/' },
// { text: 'Markdown 容器', link: '/pages/d0d7eb/' },
// { text: 'Markdown 中使用组件', link: '/pages/197691/' },
// {
// text: '相关文章', items: [
// { text: '使目录栏支持h2~h6标题', link: '/pages/8dfab5/' },
// { text: '如何让你的笔记更有表现力', link: '/pages/dd027d/' },
// { text: '批量操作front matter工具', link: '/pages/2b8e22/' },
// { text: '部署', link: '/pages/0fc1d2/' },
// { text: '关于写文章和H1标题', link: '/pages/9ae0bd/' },
// { text: '关于博客搭建与管理', link: '/pages/26997d/' },
// { text: '在线编辑和新增文章的方法', link: '/pages/c5a54d/' },
// ]
// }
// ]
// },
// {
// text: '配置', link: '/pages/a20ce8/', items: [
// { text: '主题配置', link: '/pages/a20ce8/' },
// { text: '首页配置', link: '/pages/f14bdb/' },
// { text: 'front matter配置', link: '/pages/3216b0/' },
// { text: '目录页配置', link: '/pages/54651a/' },
// { text: '添加摘要', link: '/pages/1cc523/' },
// { text: '修改主题颜色和样式', link: '/pages/f51918/' },
// { text: '评论栏', link: '/pages/ce175c/' },
// ]
// },
// { text: '资源', link: '/pages/db78e2/' },
// { text: '案例', link: '/pages/5d571c/' },
// { text: '问答', link: '/pages/9cc27d/' },
// { text: '赞助', link: '/pages/1b12ed/' },
// ],
nav: [
{text: '首页', link: '/'},
{
text: '快速使用', link: '/pages/793dcb/', items: [
{text: '快速上手', link: '/pages/793dcb/'},
{text: '目录结构', link: '/pages/2f674a/'},
{text: 'Markdown 容器', link: '/pages/d0d7eb/'},
{text: 'Markdown 中使用组件', link: '/pages/197691/'},
{
text: '相关文章', items: [
{text: '使目录栏支持h2~h6标题', link: '/pages/8dfab5/'},
{text: '如何让你的笔记更有表现力', link: '/pages/dd027d/'},
{text: '批量操作front matter工具', link: '/pages/2b8e22/'},
{text: '部署', link: '/pages/0fc1d2/'},
{text: '关于写文章和H1标题', link: '/pages/9ae0bd/'},
{text: '关于博客搭建与管理', link: '/pages/26997d/'},
{text: '在线编辑和新增文章的方法', link: '/pages/c5a54d/'},
]
}
]
},
{
text: '常见问题', link: '/pages/a20ce8/', items: [
{text: '主题配置', link: '/pages/a20ce8/'},
{text: '首页配置', link: '/pages/f14bdb/'},
{text: 'front matter配置', link: '/pages/3216b0/'},
{text: '目录页配置', link: '/pages/54651a/'},
{text: '添加摘要', link: '/pages/1cc523/'},
{text: '修改主题颜色和样式', link: '/pages/f51918/'},
{text: '评论栏', link: '/pages/ce175c/'},
]
},
{text: '演示', link: '/pages/db78e2/'},
{text: '案例', link: '/pages/5d571c/'},
{text: '最近更新', link: '/pages/9cc27d/'},
{text: '关于我们', link: '/pages/1b12ed/'},
],
sidebarDepth: 2, // 侧边栏显示深度,默认1,最大2(显示到h3标题)
logo: '/img/logo.png', // 导航栏logo
repo: 'aoshiguchen/neutrino-proxy', // 导航栏右侧生成Github链接
// repo: 'https://gitee.com/dromara/neutrino-proxy', // 导航栏右侧生成Github链接
searchMaxSuggestions: 10, // 搜索结果显示最大数
lastUpdated: '上次更新', // 更新的时间,及前缀文字 string | boolean (取值为git提交时间)
// docsDir: 'docs', // 编辑的文件夹
// editLinks: true, // 编辑链接
// editLinkText: '编辑',
// 以下配置是Vdoing主题改动的和新增的配置
sidebar: {mode: 'structuring', collapsable: false}, // 侧边栏 'structuring' | { mode: 'structuring', collapsable: Boolean} | 'auto' | 自定义 温馨提示:目录页数据依赖于结构化的侧边栏数据,如果你不设置为'structuring',将无法使用目录页
// sidebarOpen: false, // 初始状态是否打开侧边栏,默认true
updateBar: { // 最近更新栏
showToArticle: false, // 显示到文章页底部,默认true
// moreArticle: '/archives' // “更多文章”跳转的页面,默认'/archives'
},
// titleBadge: false, // 文章标题前的图标是否显示,默认true
// titleBadgeIcons: [ // 文章标题前图标的地址,默认主题内置图标
// '图标地址1',
// '图标地址2'
// ],
pageStyle: 'line', // 页面风格,可选值:'card'卡片 | 'line' 线(未设置bodyBgImg时才生效), 默认'card'。 说明:card时背景显示灰色衬托出卡片样式,line时背景显示纯色,并且部分模块带线条边框
// contentBgStyle: 1,
category: false, // 是否打开分类功能,默认true。 如打开,会做的事情有:1. 自动生成的frontmatter包含分类字段 2.页面中显示与分类相关的信息和模块 3.自动生成分类页面(在@pages文件夹)。如关闭,则反之。
tag: false, // 是否打开标签功能,默认true。 如打开,会做的事情有:1. 自动生成的frontmatter包含标签字段 2.页面中显示与标签相关的信息和模块 3.自动生成标签页面(在@pages文件夹)。如关闭,则反之。
// archive: false, // 是否打开归档功能,默认true。 如打开,会做的事情有:1.自动生成归档页面(在@pages文件夹)。如关闭,则反之。
author: { // 文章默认的作者信息,可在md文件中单独配置此信息 String | {name: String, href: String}
name: 'Evan Xu', // 必需
href: 'https://github.com/xugaoyi' // 可选的
},
social: { // 社交图标,显示于博主信息栏和页脚栏
// iconfontCssFile: '//at.alicdn.com/t/font_1678482_u4nrnp8xp6g.css', // 可选,阿里图标库在线css文件地址,对于主题没有的图标可自由添加
icons: [
{
iconClass: 'icon-youjian',
title: '发邮件',
link: 'mailto:[email protected]'
},
{
iconClass: 'icon-gitee',
title: 'Gitee',
link: 'https://gitee.com/dromara/neutrino-proxy'
},
{
iconClass: 'icon-github',
title: 'Github',
link: 'https://github.com/aoshiguchen/neutrino-proxy'
}
]
},
footer: { // 页脚信息
createYear: 2023, // 博客创建年份
copyrightInfo: '傲世孤尘 | MIT License', // 博客版权信息,支持a标签
},
htmlModules,
},
// 插件
plugins: [
// [require('./plugins/love-me'), { // 鼠标点击爱心特效
// color: '#11a8cd', // 爱心颜色,默认随机色
// excludeClassName: 'theme-vdoing-content' // 要排除元素的class, 默认空''
// }],
['fulltext-search'], // 全文搜索
// ['thirdparty-search', { // 可以添加第三方搜索链接的搜索框(原官方搜索框的参数仍可用)
// thirdparty: [ // 可选,默认 []
// {
// title: '在GitHub中搜索',
// frontUrl: 'https://github.com/search?q=', // 搜索链接的前面部分
// behindUrl: '' // 搜索链接的后面部分,可选,默认 ''
// },
// {
// title: '在npm中搜索',
// frontUrl: 'https://www.npmjs.com/search?q=',
// },
// {
// title: '在Bing中搜索',
// frontUrl: 'https://cn.bing.com/search?q='
// }
// ]
// }],
[
'vuepress-plugin-baidu-tongji', // 百度统计
{
hm: baiduCode || '01293bffa6c3962016c08ba685c79d78'
}
],
['one-click-copy', { // 代码块复制按钮
copySelector: ['div[class*="language-"] pre', 'div[class*="aside-code"] aside'], // String or Array
copyMessage: '复制成功', // default is 'Copy successfully and then paste it for use.'
duration: 1000, // prompt message display time.
showInMobile: false // whether to display on the mobile side, default: false.
}],
['demo-block', { // demo演示模块 https://github.com/xiguaxigua/vuepress-plugin-demo-block
settings: {
// jsLib: ['http://xxx'], // 在线示例(jsfiddle, codepen)中的js依赖
// cssLib: ['http://xxx'], // 在线示例中的css依赖
// vue: 'https://fastly.jsdelivr.net/npm/vue/dist/vue.min.js', // 在线示例中的vue依赖
jsfiddle: false, // 是否显示 jsfiddle 链接
codepen: true, // 是否显示 codepen 链接
horizontal: false // 是否展示为横向样式
}
}],
[
'vuepress-plugin-zooming', // 放大图片
{
selector: '.theme-vdoing-content img:not(.no-zoom)',
options: {
bgColor: 'rgba(0,0,0,0.6)'
},
},
],
[
'@vuepress/last-updated', // "上次更新"时间格式
{
transformer: (timestamp, lang) => {
const dayjs = require('dayjs') // https://day.js.org/
return dayjs(timestamp).format('YYYY/MM/DD, HH:mm:ss')
},
}
]
},
{
text: '常见问题', link: '/pages/a20ce8/', items: [
{ text: '主题配置', link: '/pages/a20ce8/' },
{ text: '首页配置', link: '/pages/f14bdb/' },
{ text: 'front matter配置', link: '/pages/3216b0/' },
{ text: '目录页配置', link: '/pages/54651a/' },
{ text: '添加摘要', link: '/pages/1cc523/' },
{ text: '修改主题颜色和样式', link: '/pages/f51918/' },
{ text: '评论栏', link: '/pages/ce175c/' },
]
},
{ text: '演示', link: '/pages/db78e2/' },
{ text: '案例', link: '/pages/5d571c/' },
{ text: '最近更新', link: '/pages/9cc27d/' },
{ text: '仓库地址', link: '/pages/1b12ed/' },
],
sidebarDepth: 2, // 侧边栏显示深度,默认1,最大2(显示到h3标题)
logo: '/img/logo.png', // 导航栏logo
repo: 'xugaoyi/vuepress-theme-vdoing', // 导航栏右侧生成Github链接
// repo: 'https://gitee.com/dromara/neutrino-proxy', // 导航栏右侧生成Github链接
searchMaxSuggestions: 10, // 搜索结果显示最大数
lastUpdated: '上次更新', // 更新的时间,及前缀文字 string | boolean (取值为git提交时间)
// docsDir: 'docs', // 编辑的文件夹
// editLinks: true, // 编辑链接
// editLinkText: '编辑',
// 以下配置是Vdoing主题改动的和新增的配置
sidebar: { mode: 'structuring', collapsable: false }, // 侧边栏 'structuring' | { mode: 'structuring', collapsable: Boolean} | 'auto' | 自定义 温馨提示:目录页数据依赖于结构化的侧边栏数据,如果你不设置为'structuring',将无法使用目录页
// sidebarOpen: false, // 初始状态是否打开侧边栏,默认true
updateBar: { // 最近更新栏
showToArticle: false, // 显示到文章页底部,默认true
// moreArticle: '/archives' // “更多文章”跳转的页面,默认'/archives'
},
// titleBadge: false, // 文章标题前的图标是否显示,默认true
// titleBadgeIcons: [ // 文章标题前图标的地址,默认主题内置图标
// '图标地址1',
// '图标地址2'
// ],
pageStyle: 'line', // 页面风格,可选值:'card'卡片 | 'line' 线(未设置bodyBgImg时才生效), 默认'card'。 说明:card时背景显示灰色衬托出卡片样式,line时背景显示纯色,并且部分模块带线条边框
// contentBgStyle: 1,
category: false, // 是否打开分类功能,默认true。 如打开,会做的事情有:1. 自动生成的frontmatter包含分类字段 2.页面中显示与分类相关的信息和模块 3.自动生成分类页面(在@pages文件夹)。如关闭,则反之。
tag: false, // 是否打开标签功能,默认true。 如打开,会做的事情有:1. 自动生成的frontmatter包含标签字段 2.页面中显示与标签相关的信息和模块 3.自动生成标签页面(在@pages文件夹)。如关闭,则反之。
// archive: false, // 是否打开归档功能,默认true。 如打开,会做的事情有:1.自动生成归档页面(在@pages文件夹)。如关闭,则反之。
author: { // 文章默认的作者信息,可在md文件中单独配置此信息 String | {name: String, href: String}
name: 'Evan Xu', // 必需
href: 'https://github.com/xugaoyi' // 可选的
},
social: { // 社交图标,显示于博主信息栏和页脚栏
// iconfontCssFile: '//at.alicdn.com/t/font_1678482_u4nrnp8xp6g.css', // 可选,阿里图标库在线css文件地址,对于主题没有的图标可自由添加
icons: [
{
iconClass: 'icon-youjian',
title: '发邮件',
link: 'mailto:[email protected]'
},
{
iconClass: 'icon-gitee',
title: 'Gitee',
link: 'https://gitee.com/dromara/neutrino-proxy'
},
{
iconClass: 'icon-github',
title: 'Github',
link: 'https://github.com/aoshiguchen/neutrino-proxy'
}
]
},
footer: { // 页脚信息
createYear: 2023, // 博客创建年份
copyrightInfo: '傲世孤尘 | MIT License', // 博客版权信息,支持a标签
},
htmlModules,
},
// 插件
plugins: [
// [require('./plugins/love-me'), { // 鼠标点击爱心特效
// color: '#11a8cd', // 爱心颜色,默认随机色
// excludeClassName: 'theme-vdoing-content' // 要排除元素的class, 默认空''
// }],
['fulltext-search'], // 全文搜索
// ['thirdparty-search', { // 可以添加第三方搜索链接的搜索框(原官方搜索框的参数仍可用)
// thirdparty: [ // 可选,默认 []
// {
// title: '在GitHub中搜索',
// frontUrl: 'https://github.com/search?q=', // 搜索链接的前面部分
// behindUrl: '' // 搜索链接的后面部分,可选,默认 ''
// },
// {
// title: '在npm中搜索',
// frontUrl: 'https://www.npmjs.com/search?q=',
// },
// {
// title: '在Bing中搜索',
// frontUrl: 'https://cn.bing.com/search?q='
// }
// ]
// }],
[
'vuepress-plugin-baidu-tongji', // 百度统计
{
hm: baiduCode || '01293bffa6c3962016c08ba685c79d78'
}
],
['one-click-copy', { // 代码块复制按钮
copySelector: ['div[class*="language-"] pre', 'div[class*="aside-code"] aside'], // String or Array
copyMessage: '复制成功', // default is 'Copy successfully and then paste it for use.'
duration: 1000, // prompt message display time.
showInMobile: false // whether to display on the mobile side, default: false.
}],
['demo-block', { // demo演示模块 https://github.com/xiguaxigua/vuepress-plugin-demo-block
settings: {
// jsLib: ['http://xxx'], // 在线示例(jsfiddle, codepen)中的js依赖
// cssLib: ['http://xxx'], // 在线示例中的css依赖
// vue: 'https://fastly.jsdelivr.net/npm/vue/dist/vue.min.js', // 在线示例中的vue依赖
jsfiddle: false, // 是否显示 jsfiddle 链接
codepen: true, // 是否显示 codepen 链接
horizontal: false // 是否展示为横向样式
}
}],
[
'vuepress-plugin-zooming', // 放大图片
{
selector: '.theme-vdoing-content img:not(.no-zoom)',
options: {
bgColor: 'rgba(0,0,0,0.6)'
},
},
],
[
'@vuepress/last-updated', // "上次更新"时间格式
{
transformer: (timestamp, lang) => {
const dayjs = require('dayjs') // https://day.js.org/
return dayjs(timestamp).format('YYYY/MM/DD, HH:mm:ss')
},
}
markdown: {
// lineNumbers: true,
extractHeaders: ['h2', 'h3', 'h4', 'h5', 'h6'], // 提取标题到侧边栏的级别,默认['h2', 'h3']
},
// 监听文件变化并重新构建
extraWatchFiles: [
'.vuepress/config.js',
'.vuepress/config/htmlModules.js',
]
],
markdown: {
// lineNumbers: true,
extractHeaders: ['h2', 'h3', 'h4', 'h5', 'h6'], // 提取标题到侧边栏的级别,默认['h2', 'h3']
},
// 监听文件变化并重新构建
extraWatchFiles: [
'.vuepress/config.js',
'.vuepress/config/htmlModules.js',
]
}
@@ -1,59 +0,0 @@
/**
* to主题使用者:你可以去掉本文件的所有代码
*/
export default ({
Vue, // VuePress 正在使用的 Vue 构造函数
options, // 附加到根实例的一些选项
router, // 当前应用的路由实例
siteData, // 站点元数据
isServer // 当前应用配置是处于 服务端渲染 还是 客户端
}) => {
// 用于监控在路由变化时检查广告拦截器 (to主题使用者:你可以去掉本文件的所有代码)
if (!isServer) {
router.afterEach(() => {
//check if wwads' fire function was blocked after document is ready with 3s timeout (waiting the ad loading)
docReady(function () {
setTimeout(function () {
if (window._AdBlockInit === undefined) {
ABDetected();
}
}, 3000);
});
// 删除事件改为隐藏事件
setTimeout(() => {
const pageAD = document.querySelector('.page-wwads');
if (!pageAD) return;
const btnEl = pageAD.querySelector('.wwads-hide');
if (btnEl) {
btnEl.onclick = () => {
pageAD.style.display = 'none';
}
}
// 显示广告模块
if (pageAD.style.display === 'none') {
pageAD.style.display = 'flex';
}
}, 900);
})
}
}
function ABDetected() {
const h = "<style>.wwads-horizontal,.wwads-vertical{background-color:#f4f8fa;padding:5px;min-height:120px;margin-top:20px;box-sizing:border-box;border-radius:3px;font-family:sans-serif;display:flex;min-width:150px;position:relative;overflow:hidden;}.wwads-horizontal{flex-wrap:wrap;justify-content:center}.wwads-vertical{flex-direction:column;align-items:center;padding-bottom:32px}.wwads-horizontal a,.wwads-vertical a{text-decoration:none}.wwads-horizontal .wwads-img,.wwads-vertical .wwads-img{margin:5px}.wwads-horizontal .wwads-content,.wwads-vertical .wwads-content{margin:5px}.wwads-horizontal .wwads-content{flex:130px}.wwads-vertical .wwads-content{margin-top:10px}.wwads-horizontal .wwads-text,.wwads-content .wwads-text{font-size:14px;line-height:1.4;color:#0e1011;-webkit-font-smoothing:antialiased}.wwads-horizontal .wwads-poweredby,.wwads-vertical .wwads-poweredby{display:block;font-size:11px;color:#a6b7bf;margin-top:1em}.wwads-vertical .wwads-poweredby{position:absolute;left:10px;bottom:10px}.wwads-horizontal .wwads-poweredby span,.wwads-vertical .wwads-poweredby span{transition:all 0.2s ease-in-out;margin-left:-1em}.wwads-horizontal .wwads-poweredby span:first-child,.wwads-vertical .wwads-poweredby span:first-child{opacity:0}.wwads-horizontal:hover .wwads-poweredby span,.wwads-vertical:hover .wwads-poweredby span{opacity:1;margin-left:0}.wwads-horizontal .wwads-hide,.wwads-vertical .wwads-hide{position:absolute;right:-23px;bottom:-23px;width:46px;height:46px;border-radius:23px;transition:all 0.3s ease-in-out;cursor:pointer;}.wwads-horizontal .wwads-hide:hover,.wwads-vertical .wwads-hide:hover{background:rgb(0 0 0 /0.05)}.wwads-horizontal .wwads-hide svg,.wwads-vertical .wwads-hide svg{position:absolute;left:10px;top:10px;fill:#a6b7bf}.wwads-horizontal .wwads-hide:hover svg,.wwads-vertical .wwads-hide:hover svg{fill:#3E4546}</style><a href='https://wwads.cn/page/whitelist-wwads' class='wwads-img' target='_blank' rel='nofollow'><img src='https://fastly.jsdelivr.net/gh/xugaoyi/image_store@master/blog/wwads.2a3pidhlh4ys.webp' width='130'></a><div class='wwads-content'><a href='https://wwads.cn/page/whitelist-wwads' class='wwads-text' target='_blank' rel='nofollow'>为了本站的长期运营,请将我们的网站加入广告拦截器的白名单,感谢您的支持!<span style='color: #11a8cd'>如何添加白名单?</span></a><a href='https://wwads.cn/page/end-user-privacy' class='wwads-poweredby' title='万维广告 让广告更优雅,且有用' target='_blank'><span>广告</span></a></div><a class='wwads-hide' onclick='parentNode.remove()' title='隐藏广告'><svg xmlns='http://www.w3.org/2000/svg' width='6' height='7'><path d='M.879.672L3 2.793 5.121.672a.5.5 0 11.707.707L3.708 3.5l2.12 2.121a.5.5 0 11-.707.707l-2.12-2.12-2.122 2.12a.5.5 0 11-.707-.707l2.121-2.12L.172 1.378A.5.5 0 01.879.672z'></path></svg></a>";
const wwadsEl = document.getElementsByClassName("wwads-cn");
const wwadsContentEl = document.querySelector('.wwads-content');
if (wwadsEl[0] && !wwadsContentEl) {
wwadsEl[0].innerHTML = h;
}
};
//check document ready
function docReady(t) {
"complete" === document.readyState ||
"interactive" === document.readyState
? setTimeout(t, 1)
: document.addEventListener("DOMContentLoaded", t);
}
@@ -0,0 +1,98 @@
---
title: 快速上手
date: 2020-05-11 13:54:40
permalink: /pages/793dcb
article: false
---
## 1. 打包
可直接前往Gitee仓库发行版页面下载所需版本已打好的包。若需手动打包,则可参照下面的执行命令:
```
# 服务端打包
mvn clean install -U -pl neutrino-proxy-server -am -Dmaven.test.skip=true
# 客户端打包
clean install -U -pl neutrino-proxy-client -am -Dmaven.test.skip=true
# 管理后台前端项目打包(本地环境,local改为dev则为dev环境,同时需要修改config目录下面的环境配置)
npm run build:local
```
## 2. 部署
### 2.2 服务端部署
- 使用常规的jar包部署方式即可,如:java -jar xxxx
### 2.4 管理后台部署
- Nginx方式部署(推荐):
````
server {
listen 9527;
server_name localhost;
#开启gzip
gzip on;
#低于1kb的资源不压缩
gzip_min_length 1k;
#压缩级别1-9,越大压缩率越高,同时消耗cpu资源也越多,建议设置在5左右。
gzip_comp_level 5;
#需要压缩哪些响应类型的资源,多个空格隔开。不建议压缩图片.
gzip_types text/plain application/javascript application/x-javascript text/javascript text/xml text/css;
#配置禁用gzip条件,支持正则。此处表示ie6及以下不启用gzip(因为ie低版本不支持)
gzip_disable "MSIE [1-6]\.";
#是否添加“Vary: Accept-Encoding”响应头
gzip_vary on;
location / {
root /work/projects/neutrino-proxy-server/neutrino-proxy-admin/dist;
try_files $uri $uri/ /index.html;
add_header Last-Modified $date_gmt;
}
}
````
- 无nginx时
在没有Nginx时,为了快速体验代理效果,可直接使用服务端项目提供的静态资源服务。直接将neutrino-proxy-admin打包后的文件解压放在neutrino-proxy-server.jar同级别目录下即可。例如:服务端配置的web端口为8080,则访问http://服务端IP:8080, 则会直接解析渲染neutrino-proxy-server.jar同级别目录下neutrino-proxy-admin/dist/index.html。
-
::: warning
需要注意的是,使用服务端自带的静态资源服务时,由于框架目前未支持缓存、gzip压缩,所以访问速度没有使用nginx快,正式使用推荐用nginx。
:::
## 使用Vdoing主题
1. 安装最新的Vdoing主题包:
```sh
npm install vuepress-theme-vdoing -D
```
2. 在`.vuepress/config.js`中配置使用主题:
```js
// config.js
module.exports = {
theme: 'vdoing'
}
```
::: tip
1. 不建议在原默认vuepress项目上单独安装使用本主题包,而是clone我的整个项目再替换你自己的内容即可。
2. 修改`config.js`配置后需要重新启动项目才会生效。
3. 更多关于项目上手的问题,请查阅 [问答](/pages/9cc27d/)。
:::
## 版本升级
主题的版本会不定期更新,你只需更新npm主题包即可:
```sh
npm update vuepress-theme-vdoing
```
::: tip
1. 如更新后没起作用或报错,尝试把`node_modules`文件夹删除再`npm i`重新安装。
2. 在.vuepress/config.js中,设置`theme: 'vdoing'`才是使用npm主题依赖包:
```js
// config.js
module.exports = {
theme: 'vdoing', // npm主题依赖包
// theme: require.resolve('../../vdoing'), // 使用本地主题包
}
```
:::
@@ -6,28 +6,20 @@ article: false
---
```
.
├── .github (可选,GitHub 相关文件)
├── workflows
│ │ ├── baiduPush.yml (可选,百度定时自动推送)
│ │ └── ci.yml (可选,自动部署)
├── docs (必须,不要修改文件夹名称)
│ ├── .vuepress (同官方,查看:https://vuepress.vuejs.org/zh/guide/directory-structure.html#目录结构
│ ├── @pages (可选,自动生成的文件夹,存放分类页、标签页、归档页)
│ ├── _posts (可选,专门存放碎片化博客文章的文件夹)
│ ├── <结构化目录>
│ └── index.md (首页)
├── vdoing (可选,本地的vdoing主题)
├── utils (可选,vdoing主题使用的node工具)
│ ├── modules
│ ├── config.yml (可选,批量操作front matter配置)
│ ├── editFrontmatter.js (可选,批量操作front matter工具)
├── baiduPush.sh (可选,百度推送命令脚本)
├── deploy.sh (可选,部署命令脚本)
└── package.json
├── data.db (sqlite数据库文件。若未配置mysql,默认使用sqlite,项目首次启动会自动初始化sqlite数据库。)
├── docs (项目相关的一些文档)
├── ├── Aop.MD (框架层Aop机制、使用说明)
├── └── Channel.MD (内网穿透实现原理、代理实现流程说明)
├── lib (项目开启了将自动生成的类保存到本地后,运行过程中动态生成的类自动保存到此处,方便学习、调试)
├── neutrino-core (一套手写的基于netty的框架,相当于简易版的SpringBoot + Mybatis + xxljob,计划后期分离为单独开源项目维护)
├── neutrino-proxy-admin (基于vue-element-admin开发的一个管理系统,用于可视化操作端口映射、代理数据实时监控)
├── neutrino-proxy-client (基于netty的代理客户端,用于和服务端交互、转发内网数据)
├── neutrino-proxy-core (代理相关的公共代码(协议、常量))
├── neutrino-proxy-server (基于netty的代理服务端,用于和客户段交互,将客户端转发的内网数据转发至外网端口)
└── todolist.MD (近期的开发计划)
```
<!--
* `docs` 文件夹名称请不要修改
* `docs/.vuepress` 用于存放全局的配置、样式、静态资源等,同官方,查看 [详情](https://vuepress.vuejs.org/zh/guide/directory-structure.html#目录结构)
@@ -69,6 +61,7 @@ article: false
**注意**:主题的后续维护升级只对npm主题包负责,就是说你使用本地主题就等于放弃了后续的升级服务。因此,建议能在`docs/.vuepress/`内配置和修改的,就尽量不要改动主题内部代码。
---
-->
::: tip 提示
为了方便您更快的学习和使用本主题,我在代码当中添加了比较多的注释说明。
@@ -1,39 +0,0 @@
---
title: 主题初衷与诞生
date: 2020-05-11 13:59:38
permalink: /pages/52d5c3
article: false
---
这个主题的初衷是打造一个好用的、面向程序员的`知识管理工具`
对于程序员来说,繁杂的知识体系难免会有遗忘的地方。如果有一个方便好用的知识管理工具,可以帮助我们很好的管理知识,并能够快速地把遗忘的知识点找回来。
## Markdown
最初接触[Markdown](https://xugaoyi.com/pages/ad247c4332211551/)的时候,我就被它简洁的语法干净的文本结构吸引住,它的代码块和兼容`html`标签的能力更是让我爱上它,很高兴找到了一个高效记录学习笔记的工具。
## 知识管理
在一段学习的日子里,我尝试过用`txt`记录笔记、云笔记、`Markdown`笔记,并把`Markdown`文件上传到`github`进行管理,但总感觉还是不够方便...直到我发现了`VuePress`,它似乎可以管理我的学习笔记,并且把站点部署到`github pages`不就是一个在线的云笔记网站了吗
## VuePress
[VuePress](https://vuepress.vuejs.org/zh/)是一个 Vue 驱动的静态网站生成器,正是以`Markdown`为中心的项目结构,它简洁至上的理念正合我心。对于我这个对Vue还算有一些了解的前端,迫不及待的想去使用它来搭建一个我的云笔记网站。
## 知识管理&博客主题-Vdoing的诞生
我以前的一个领导和我们说过一个好的知识管理可以帮助我们提高开发质量和开发效率,下面这张图就是他想传达的,我表示赞同:
![知识库](https://fastly.jsdelivr.net/gh/xugaoyi/image_store/blog/20200318125116.jpg)
什么是一个好用的知识管理工具呢?其实我也在不断的寻找和思考中。不过我觉得它至少要结构清晰、管理方便,在查找知识点的过程中可以快速的找到,正像上图所说的要在15秒内找到一个知识点,在添加内容的时候可以很方便的添加,并且保证结构清晰。
VuePress的官方默认主题是专门为写文档而生的,和我心目中的知识库有一些差距,比如在满足结构清晰这点上,每添加一个内容就得手动的添加侧边栏数据,还有在满足15秒内找到一个知识点上还是不够好,因此我觉得需要做一些改造。
在知识管理方面,有三种典型的知识形态:
> * 结构化:看重知识的条理性和实用性,有一定专业背景才能看懂
> * 碎片化:描述具体的知识点,通俗易懂
> * 体系化:对某一领域的完整解读,通常是某类知识的专业之作
从这三种知识形态出发,我给主题添加了自动生成结构化侧边栏、目录页、面包屑等等。在满足了结构化之后,写博客应该算得上是一种碎片化的形态,我就给主题添加了分类、标签、归档等等。在满足更方便添加内容上,有了自动生成front matter、批量操作front matter等,更多关于主题的知识从开始使用它去探索吧~~
### 主题名称
Vdoing,中文大概是维度的意思。起这个名的原因,首先是我看过一个视频《尤雨溪创立vue的心路历程》[B站传送](https://b23.tv/xI9ONW) [youtube传送](https://www.youtube.com/watch?v=OrxmtDw4pVI),里面有讲到vue起名的故事,一开始想起的名其实是Seed.js,但是在npm上被占用了,然后发现vue是一个挺酷的一个词,才决定用vue。发现大佬起名可以这么酷。再者,这个主题我想它可以多维度的快速寻找一个知识点,要么就叫维度吧,那英文名就用Vdoing好了...
@@ -1,73 +0,0 @@
---
title: 介绍
date: 2020-05-11 13:54:03
permalink: /pages/a2f161
article: false
---
Vdoing是[VuePress v1.x](https://vuepress.vuejs.org/zh/)的一个主题,是在[默认主题](https://vuepress.vuejs.org/zh/theme/option-api.html)基础上做的修改和扩展,很多配置仍然沿用[官方配置](https://vuepress.vuejs.org/zh/config/)。使用本主题可以很方便的搭建一个结构化的知识库或博客。
这个主题的初衷是打造一个好用的、面向程序员的知识管理工具:
[**主题初衷与诞生**。](/pages/52d5c3/)
::: warning 注意
1. Node请使用`v14.17.x`或以上版本
2. 在使用本主题前,要求你至少会VuePress v1.x的基本使用和默认主题的基本配置,然后再查看本文档。
3. 本文档仅负责介绍Vdoing主题对默认主题的扩展部分,更多配置请移步 [VuePress v1.x文档](https://vuepress.vuejs.org/zh/)
:::
## 特性
* **知识管理**
包含三种典型的知识管理形态:结构化、碎片化、体系化。轻松打造属于你自己的知识管理平台。
* **结构化**
自动生成侧边栏、目录页、索引页、面包屑等,轻松构建一个结构化知识库。
* **碎片化&个性化**
博客功能提供一种知识的碎片化形态,并提供个性化的博客配置。
* **简洁高效**
以 Markdown 为中心的项目结构,内置自动化工具,以更少的配置完成更多的事。配合多维索引快速定位每个知识点。
* **沉浸式阅读体验**
专为阅读设计的UI,配合多种颜色模式、可关闭的侧边栏和导航栏,带给你一种沉浸式阅读体验。
## 扩展功能
相较于默认主题,添加的功能内容主要有:
* 添加方便管理学习笔记和技术文档的`自动生成结构化侧边栏``自动生成front matter``目录页``扩展的搜索框插件``面包屑``快捷翻页按钮` 等,让你快速定位到任何你想要找的内容。
* 添加博客相关的 `文章信息栏(作者与创建时间)``最近更新栏``博主信息栏``页脚版权栏``分类功能+分类页``标签功能+标签页``归档页``评论插件`等。
* 方便好用的 `Markdown 容器`
* 首页`文章列表``个性化配置``样式美化`等。
* 多种颜色模式供用户选择:`跟随系统``浅色模式``深色模式``阅读模式`
* 提高搬砖效率的辅助工具: `批量操作front matter工具`
* ...
## 安利
* 拥有它你就同时拥有了一个专属你个人的在线知识库(云笔记)、博客、文档库、Demo库、一站式技术搜索工具,内容全部采用Markdown编写,简单高效,各种代码随便贴。
* 你可以在`.md`文件中写html、css、js、甚至是vue组件代码,[markdown天然的就支持vue组件](https://v1.vuepress.vuejs.org/zh/guide/using-vue.html),魔改页面什么的不要太简单。
* 相当多的程序员喜欢深色模式,还有的视力也不太好(🤓),我们有浅色、深色和阅读模式,更有跟随系统自动响应深浅色模式功能,想怎么换就怎么换。
> 点击右下角换肤按钮
* 当你习惯用vdoing主题后,在别处看文档发现是markdown编写的,但所在站点的目录、导航、主题等某个地方用起来不是很爽,你都可以把文档拷贝或把整个专栏下载(如支持下载的话)下来放到vdoing主题,vdoing的自动化工具助你生成一个结构清晰的、拥有目录、页面导航的,而且有多种颜色模式的文档站。让你更专注于内容的学习。
> 参考我博客中的[文档专栏](https://xugaoyi.com/note/typescript-axios/)
* 如果你想和更多的人分享你的文章,那么这款seo友好的主题是一个很不错的选择,更有为了加快百度收录而定制的每天定时[百度推送程序](https://xugaoyi.com/pages/f44d2f9ad04ab8d3/)。
> 参考我的博客[收录情况](https://www.baidu.com/s?word=site%3Axugaoyi.com)。
别犹豫了,赶快上手吧
@@ -1,110 +0,0 @@
---
title: 快速上手
date: 2020-05-11 13:54:40
permalink: /pages/793dcb
article: false
---
## 安装和启动
<code-group>
<code-block title="知识库兼博客风格预设配置" active>
```bash
# clone the project
git clone https://github.com/xugaoyi/vuepress-theme-vdoing.git
# enter the project directory
cd vuepress-theme-vdoing
# install dependency 注意:如安装不成功请关闭淘宝源。
npm install # or yarn install
# develop
npm run dev # or yarn dev
```
</code-block>
<code-block title="文档风格预设配置">
```bash
# clone the project
git clone https://github.com/xugaoyi/vuepress-theme-vdoing-doc.git
# enter the project directory
cd vuepress-theme-vdoing-doc
# install dependency 注意:如安装不成功请关闭淘宝源。
npm install # or yarn install
# develop
npm run dev # or yarn dev
```
> 仓库地址: <https://github.com/xugaoyi/vuepress-theme-vdoing-doc>
</code-block>
<code-block title="简洁模板预设配置(社区提供)">
```bash
# clone the project
git clone https://github.com/u2sb/vuepress-theme-vdoing-template.git
# enter the project directory
cd vuepress-theme-vdoing-template
# install dependency 注意:如安装不成功请关闭淘宝源。
npm install # or yarn install
# develop
npm run dev # or yarn dev
```
> 仓库地址: <https://github.com/u2sb/vuepress-theme-vdoing-template>
</code-block>
</code-group>
::: warning
Node请使用`v14.17.x`或以上版本
:::
## 使用Vdoing主题
1. 安装最新的Vdoing主题包:
```sh
npm install vuepress-theme-vdoing -D
```
2.`.vuepress/config.js`中配置使用主题:
```js
// config.js
module.exports = {
theme: 'vdoing'
}
```
::: tip
1. 不建议在原默认vuepress项目上单独安装使用本主题包,而是clone我的整个项目再替换你自己的内容即可。
2. 修改`config.js`配置后需要重新启动项目才会生效。
3. 更多关于项目上手的问题,请查阅 [问答](/pages/9cc27d/)。
:::
## 版本升级
主题的版本会不定期更新,你只需更新npm主题包即可:
```sh
npm update vuepress-theme-vdoing
```
::: tip
1. 如更新后没起作用或报错,尝试把`node_modules`文件夹删除再`npm i`重新安装。
2. 在.vuepress/config.js中,设置`theme: 'vdoing'`才是使用npm主题依赖包:
```js
// config.js
module.exports = {
theme: 'vdoing', // npm主题依赖包
// theme: require.resolve('../../vdoing'), // 使用本地主题包
}
```
:::
@@ -1,107 +0,0 @@
---
title: 构建结构化站点的核心配置和约定
date: 2020-05-12 11:01:21
permalink: /pages/33d574
article: false
---
本主题遵循“**约定优于配置**”原则,如果你想构建一个结构化的站点,需要遵循下面这些约定,约定可以为你省去很多配置,轻松拥有一个结构清晰的站点。
你需要在`config.js`中有如下配置:
```js
// config.js
module.exports = {
themeConfig: {
sidebar: 'structuring' // 'structuring' | { mode: 'structuring', collapsable: Boolean} | 'auto' | 自定义
}
}
```
在运行开发服务`npm run dev`或打包`npm run build`时主题内部将会按照目录约定自动生成一个结构化的**侧边栏、目录页、面包屑等**数据。
在源目录(一般是`docs`)文件夹中,除了`.vuepress``@pages``_posts`、``index.md 或 README.md``之外的**文件夹**将会为其生成对应的侧边栏。生成的顺序取自序号,标题取自文件(夹)名称。
### 命名约定
* 无论是**文件**还是**文件夹**,请为其名称添加上正确的**正整数序号**和`.`,从`00`或`01`开始累计,如`01.文件夹`、`02.文件.md`,我们将会按照序号的顺序来决定其在侧边栏当中的顺序。
* 同一级别目录别内即使只有一个文件或文件夹也要为其加上序号。
<!-- * 文件或文件夹名称中间不能出现多余的点`.`,如`01.我是.名称.md`中间出现`.`将会导致解析错误。 -->
::: tip 提示
序号只是用于决定先后顺序,并不一定需要连着,如`01、02、03...`,实际工作中可能会在两个文章中间插入一篇新的文章,因此为了方便可以采用间隔序号`10、20、30...`,后面如果需要在`10`和`20`中间插入一篇新文章,可以给定序号`15`。
:::
### 级别说明
源目录(一般是`docs`)底下的级别现在我们称之为`一级目录``一级目录`的下一级为`二级目录`,以此类推,最多只能到`四级目录`。
* **一级目录**
1. `.vuepress`、`@pages`、`_posts`、`index.md 或 README.md` 这些文件(文件夹)不参与数据生成。
2. 序号非必须。(如一些专栏,可以不用序号)
* **二级目录**
1. 该级别下可以同时放文件夹和`.md`文件,但是两者序号要连贯(参考下面的例子中的`其他`)。
2. 必须有序号
* **三级目录**
- (同上)
* **四级目录** <Badge text="v1.6.0 +"/>
1. 该级别下**只能**放`.md`文件。
2. 必须有序号
所有级别内至少有一个文件或文件夹。
### 目录结构例子
```html
.
├── docs
│ │ (不参与数据生成)
│ ├── .vuepress
│ ├── @pages
│ ├── _posts
│ ├── index.md
│ │
│ │ (以下部分参与数据生成)
│ ├── 《JavaScript教程》专栏 (一级目录)
│ │ ├── 01.章节1 (二级目录)
│ │ | ├── 01.js1.md (三级目录-文件)
│ │ | ├── 02.js2.md
│ │ | └── 03.js3.md
│ │ └── 02.章节2 (二级目录)
│ │ | ├── 01.jsa.md
│ │ | ├── 02.小节 (三级目录)
│ │ | | └── 01.jsxx.md (四级目录-文件)
│ ├── 01.前端
│ │ ├── 01.JavaScript
│ │ | ├── 01.js1.md
│ │ | ├── 02.js2.md
│ │ | └── 03.js3.md
│ │ └── 02.vue
│ │ | ├── 01.vue1.md
│ │ | └── 02.vue2.md
│ ├── 02.其他
│ │ ├── 01.学习
│ │ | ├── 01.xxa.md
│ │ | └── 02.xxb.md
│ │ ├── 02.学习笔记
│ │ | ├── 01.xxa.md
│ │ | └── 02.xxb.md
│ │ ├── 03.文件x.md
│ │ └── 04.文件xx.md
│ └── 03.关于我
│ │ └── 01.关于我.md
. .
```
### 如何知道侧边栏数据有没有正确生成?
在运行开发服务时(`npm run dev`),在命令行查看打印记录,如果正确生成会有这样的`绿色`提示记录:
```bash
tip: add sidebar data. 侧边栏数据添加成功。
```
如果有未按约定的文件,会有`黄色`警告记录,如:
```bash
warning: 该文件'xxx'序号出错,请填写正确的序号。
```
@@ -1,108 +0,0 @@
---
title: 自动生成front matter
date: 2020-05-12 11:46:37
permalink: /pages/088c16
article: false
---
当你没有给`.md`文件的[front matter](https://vuepress.vuejs.org/zh/guide/frontmatter.html)指定标题(`title`)、时间(`date`)、永久链接(`permalink`)、分类(`categories`)、标签(`tags`)、主题配置中[extendFrontmatter](/pages/a20ce8/#extendfrontmatte)配置的字段时,在运行开发服务`npm run dev`或打包`npm run build`时将自动为你生成这些数据,你也可以自己手动设置这些数据,当你手动设置之后,相应的数据就不会再自动生成。
### 生成示例
```yaml
---
title: 《JavaScript教程》笔记
date: 2020-01-12 11:51:53
permalink: /pages/d8cae9
categories:
- 前端
- JavaScript
tags:
-
---
```
### title
* 类型: `string`
* 默认:`.md`文件的名称
当前页面的标题
### date
* 类型: `string`
* 格式:`YYYY-MM-DD HH:MM:SS`
* 默认:`.md`文件在系统中创建的时间
当前页面的创建时间,如需手动添加或修改该字段时请按照格式添加或修改
### permalink
* 类型: `string`
* 默认:`/pages/`+ 6位字母加数字的随机码
当前页面的永久链接
> Q:自动生成front matter为什么要包含永久链接?
>
> A:使用永久链接是出于以下几点考虑:
>
> * 在config.js配置nav时使用永久链接,就不会因为文件的路径或名称的改变而改变。
>* 对于博客而言,当别人收藏了你的文章,在未来的时间里都可以通过永久链接来访问到。
>* 主题中的目录页需要通过永久链接来访问文章。
### categories
* 类型: `array`
* 默认:
* `.md`所在的文件夹名称。
* 如果`.md`文件所在的目录是`三级目录`,则会有两个分类值,分别是`二级目录``一级目录`的文件夹名称。如果在`四级目录`,则再多一个`三级目录`的文件夹名称分类。([级别说明](/pages/33d574/#级别说明)
* 如果`.md`文件所在的目录是`_posts`,则默认值是`随笔`,这个默认值可以在`config.js`中修改,参考:[config.js配置](/pages/a20ce8/#碎片化博文默认分类值)
* 如果在 [config.js配置](/pages/a20ce8/#category) 设置了`category: false` 将不会自动生成该字段
当前页面的分类
### tags
* 类型: `array`
* 默认:空数组
* 如果在 [config.js配置]() 设置了`tag: false` 将不会自动生成该字段
当前页面的标签,默认值是空数组,自动生成该字段只是为了方便后面添加标签值。
### 扩展自动生成front matter
当在主题配置中配置了`extendFrontmatter`时,将在自动生成front matter时添加相应配置的字段和数据。详见:[extendFrontmatter](/pages/a20ce8/#extendfrontmatter)
### 碎片化文章‘分类’的自动生成规则 <Badge text="v1.12.5+"/>
> 碎片化文章即放在_posts文件夹的文章,里面的`.md`文件不需要遵循命名约定,不会生成结构化侧边栏和目录页。
当文章在_posts根目录时,分类获取 `themeConfig.categoryText` 的值,如`_posts/foo.md` ,则`foo.md`文件的分类会生成为:
```yaml
categories:
- 随笔
```
> categoryText的默认值是‘随笔’,可在themeConfig修改,详见[categorytext](/pages/a20ce8/#categorytext)。
当文章在非_posts根目录时,获取父文件夹的名称作为分类,如
`_posts/想法/奇思妙想/foo.md` ,则`foo.md`文件的分类会生成为:
```yaml
categories:
- 想法
- 奇思妙想
```
+21 -1
View File
@@ -141,7 +141,27 @@ postList: none
</table>
<br/>
## 🧬贡献代码的步骤
## 🏗️添砖加瓦
### 🎋分支说明
neutrino-proxy主要的源码分为两个分支,功能如下:
| 分支 | 作用 |
|---|---------------------------------------------------------------|
| master | 主分支,不接收任何pr或修改 |
| feature/1.7.1 | 开发分支,默认为下个版本的SNAPSHOT版本,接受修改或pr |
### 🐞提供bug反馈或建议
提交问题反馈请说明正在使用环境以及相关问题
- [Gitee issue](https://gitee.com/dromara/neutrino-proxy/issues)
[//]: # (- [Github issue]&#40;https://github.com/dromara/hutool/issues&#41;)
### 🧬贡献代码的步骤
贡献代码注意事项:
1. 在Gitee或者Github上fork项目到自己的repofork,一定要把项目fork一份。
+6 -8
View File
@@ -1,10 +1,8 @@
# 功能点
- 用户流量报表
- 弹框展示月度明细
- 弹框展示今日流量明细
- License流量报表
- 弹框展示月度明细
- 弹框展示今日流量明细
- [x] 用户流量报表
- [ ] 兼容sqlite
- [x] License流量报表
- [ ] 兼容sqlite
- 首页图表📈
- 1、License在线数
- 2、端口映射在线数
@@ -33,11 +31,11 @@
- 今日流量折线图(上行、下行、总流量,按分钟统计0~24小时)
# Bug
- windows环境下直接运行发布版的jar包,日志输出乱码
- 部份用户windows环境下启动客户端,扫描类个数为0个
- ~~部份用户windows环境下启动客户端,扫描类个数为0个~~
- 代理mysql时,使用未开启远程访问的账号走代理访问mysql,代理客户端出现断开现象
# 2.x规划
- 全面重构:底层更换为Solon + Mybatis Plus
- [x] 全面重构:底层更换为Solon + Mybatis Plus
- 规范协议:代理协议规范化,方便后续更好扩展、支持不同语言客户端接入
- 端口池优化:支持为license设置独占端口。方便后续开发jetbrains插件、solon插件
- 精细化控制:支持针对用户限速、限流