mirror of
https://github.com/Jinnrry/PMail.git
synced 2025-02-20 11:43:09 +08:00
v2.1
This commit is contained in:
parent
d18a665c79
commit
5ef5057032
97
fe/src/components/GroupSettings.vue
Normal file
97
fe/src/components/GroupSettings.vue
Normal file
@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<div id="main">
|
||||
<el-tree :expand-on-click-node="false" :data="data" :props="defaultProps" :defaultExpandAll="true" :class="node">
|
||||
<template #default="{ node, data }">
|
||||
<div>
|
||||
<span v-if="data.id != -1"> {{ data.label }}</span>
|
||||
<el-input v-if="data.id == -1" v-model="data.label" @blur="onInputBlur(data)"></el-input>
|
||||
<el-button v-if="data.id != 0" @click="del(node, data)" size="small" type="danger" circle> -
|
||||
</el-button>
|
||||
<el-button v-if="data.id != 0" @click="add(data)" size="small" type="primary" circle> + </el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-tree>
|
||||
|
||||
<el-button @click="addRoot">{{ lang.add_group }}</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import $http from "../http/http";
|
||||
import { reactive, ref } from 'vue'
|
||||
import lang from '../i18n/i18n';
|
||||
|
||||
const data = reactive([])
|
||||
|
||||
$http.get('/api/group').then(res => {
|
||||
data.push(...res.data)
|
||||
})
|
||||
|
||||
const del = function (node, data) {
|
||||
if (data.id != -1) {
|
||||
$http.post("/api/group/del", { "id": data.id }).then(res => {
|
||||
if (res.errorNo != 0) {
|
||||
ElMessage({
|
||||
message: res.errorMsg,
|
||||
type: 'error',
|
||||
})
|
||||
} else {
|
||||
const pc = node.parent.childNodes
|
||||
for (let i = 0; i < pc.length; i++) {
|
||||
if (pc[i].id == node.id) {
|
||||
pc.splice(i, 1)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
const pc = node.parent.childNodes
|
||||
for (let i = 0; i < pc.length; i++) {
|
||||
if (pc[i].id == node.id) {
|
||||
pc.splice(i, 1)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const add = function (item) {
|
||||
if (item.children == null) {
|
||||
item.children = []
|
||||
}
|
||||
item.children.push({
|
||||
"children": [],
|
||||
"label": "",
|
||||
"id": "-1",
|
||||
"parent_id": item.id
|
||||
})
|
||||
}
|
||||
|
||||
const addRoot = function () {
|
||||
data.push({
|
||||
"children": [],
|
||||
"label": "",
|
||||
"id": "-1",
|
||||
"parent_id": 0
|
||||
})
|
||||
}
|
||||
|
||||
const onInputBlur = function (item) {
|
||||
if (item.label != "") {
|
||||
$http.post("/api/group/add", { "name": item.label, "parent_id": item.parent_id }).then(res => {
|
||||
if (res.errorNo != 0) {
|
||||
ElMessage({
|
||||
message: res.errorMsg,
|
||||
type: 'error',
|
||||
})
|
||||
} else {
|
||||
$http.get('/api/group').then(res => {
|
||||
data.splice(0, data.length)
|
||||
data.push(...res.data)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
@ -61,4 +61,10 @@ const handleNodeClick = function (data) {
|
||||
.el-tree {
|
||||
background-color: #F1F1F1;
|
||||
}
|
||||
|
||||
.add_group{
|
||||
font-size: 14px;
|
||||
text-align: left;
|
||||
padding-left: 15px;
|
||||
}
|
||||
</style>
|
@ -8,11 +8,15 @@
|
||||
<Setting style="color:#FFFFFF" />
|
||||
</el-icon>
|
||||
</div>
|
||||
<el-drawer v-model="openSettings" :title="lang.settings">
|
||||
<el-drawer v-model="openSettings" size="80%" :title="lang.settings">
|
||||
<el-tabs tab-position="left" >
|
||||
<el-tab-pane :label="lang.security">
|
||||
<SecuritySettings/>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane :label="lang.group_settings">
|
||||
<GroupSettings/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-drawer>
|
||||
|
||||
@ -25,6 +29,7 @@ import { ref } from 'vue'
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
import SecuritySettings from '@/components/SecuritySettings.vue'
|
||||
import lang from '../i18n/i18n';
|
||||
import GroupSettings from './GroupSettings.vue';
|
||||
|
||||
|
||||
const openSettings = ref(false)
|
||||
|
@ -58,6 +58,13 @@ var lang = {
|
||||
"ssl_manuallyf": "Manually configure an SSL certificate",
|
||||
"ssl_key_path": "ssl key file path",
|
||||
"ssl_crt_path": "ssl crt file path",
|
||||
"group_settings": "Group",
|
||||
"add_group": "Add Group",
|
||||
"del_email_confirm": "Are you sure you want to delete them?",
|
||||
"move_email_confirm": "Are you sure you want to move them?",
|
||||
"del_btn": "Delete",
|
||||
"move_btn": "Move",
|
||||
"read_btn": "Readed"
|
||||
};
|
||||
|
||||
|
||||
@ -122,6 +129,13 @@ var zhCN = {
|
||||
"ssl_manuallyf": "手动配置SSL证书",
|
||||
"ssl_key_path": "ssl key文件位置",
|
||||
"ssl_crt_path": "ssl crt文件位置",
|
||||
"group_settings": "分组",
|
||||
"add_group": "新建分组",
|
||||
"del_email_confirm": "你确定要删除吗?",
|
||||
"move_email_confirm": "你确定要移动这些邮件吗?",
|
||||
"del_btn": "删除",
|
||||
"move_btn": "移动",
|
||||
"read_btn": "已读"
|
||||
}
|
||||
|
||||
switch (navigator.language) {
|
||||
|
@ -4,11 +4,27 @@
|
||||
<div id="action">
|
||||
<RouterLink to="/editer">+{{ lang.compose }}</RouterLink>
|
||||
</div>
|
||||
<!-- <div id="action">全部标记为已读</div> -->
|
||||
</div>
|
||||
<div id="title">{{ groupStore.name }}</div>
|
||||
<div id="action">
|
||||
<el-button @click="del" size="small">{{ lang.del_btn }}</el-button>
|
||||
<el-button @click="markRead" size="small">{{ lang.read_btn }}</el-button>
|
||||
<el-dropdown style="margin-left: 12px;">
|
||||
<el-button size="small">
|
||||
{{ lang.move_btn }}
|
||||
<el-icon class="el-icon--right"><arrow-down /></el-icon>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item @click="move(group.id)" v-for="group in groupList">{{ group.name
|
||||
}}</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
<div id="table">
|
||||
<el-table :data="data" :show-header="true" :border="false" @row-click="rowClick" :row-style="rowStyle">
|
||||
<el-table ref="taskTableDataRef" @selection-change="selectionLineChange" :data="data" :show-header="true"
|
||||
:border="false" @row-click="rowClick" :row-style="rowStyle">
|
||||
<el-table-column type="selection" width="30" />
|
||||
<el-table-column prop="title" label="" width="50">
|
||||
<template #default="scope">
|
||||
@ -49,7 +65,7 @@
|
||||
</el-table>
|
||||
</div>
|
||||
<div id="pagination">
|
||||
<el-pagination background layout="prev, pager, next" :page-count="totalPage" />
|
||||
<el-pagination background layout="prev, pager, next" :page-count="totalPage" @current-change="pageChange" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -58,19 +74,18 @@
|
||||
|
||||
<script setup>
|
||||
import $http from "../http/http";
|
||||
|
||||
import { ArrowDown } from '@element-plus/icons-vue'
|
||||
import { RouterLink } from 'vue-router'
|
||||
import { reactive, ref, watch } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import router from "@/router"; //根路由对象
|
||||
import useGroupStore from '../stores/group'
|
||||
import lang from '../i18n/i18n';
|
||||
|
||||
const groupStore = useGroupStore()
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
const groupList = ref([])
|
||||
|
||||
const taskTableDataRef = ref(null)
|
||||
|
||||
let tag = groupStore.tag;
|
||||
|
||||
@ -96,29 +111,140 @@ watch(groupStore, async (newV, oldV) => {
|
||||
const data = ref([])
|
||||
const totalPage = ref(0)
|
||||
|
||||
$http.post("/api/email/list", { tag: tag, page_size: 10 }).then(res => {
|
||||
data.value = res.data.list
|
||||
totalPage.value = res.data.total_page
|
||||
})
|
||||
const updateList = function () {
|
||||
$http.post("/api/email/list", { tag: tag, page_size: 10 }).then(res => {
|
||||
data.value = res.data.list
|
||||
totalPage.value = res.data.total_page
|
||||
})
|
||||
}
|
||||
|
||||
const updateGroupList = function () {
|
||||
$http.post("/api/group/list").then(res => {
|
||||
groupList.value = res.data
|
||||
})
|
||||
}
|
||||
|
||||
updateList()
|
||||
updateGroupList()
|
||||
|
||||
const rowClick = function (row, column, event) {
|
||||
router.push("/detail/" + row.id)
|
||||
}
|
||||
|
||||
const markRead = function () {
|
||||
let rows = taskTableDataRef.value?.getSelectionRows()
|
||||
let ids = []
|
||||
rows.forEach(element => {
|
||||
ids.push(element.id)
|
||||
});
|
||||
|
||||
$http.post("/api/email/read", { "ids": ids }).then(res => {
|
||||
if (res.errorNo == 0) {
|
||||
updateList()
|
||||
} else {
|
||||
ElMessage({
|
||||
type: 'error',
|
||||
message: res.errorMsg,
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
const move = function (group_id) {
|
||||
let rows = taskTableDataRef.value?.getSelectionRows()
|
||||
let ids = []
|
||||
rows.forEach(element => {
|
||||
ids.push(element.id)
|
||||
});
|
||||
|
||||
ElMessageBox.confirm(
|
||||
lang.move_email_confirm,
|
||||
'Warning',
|
||||
{
|
||||
confirmButtonText: 'OK',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'warning',
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
$http.post("/api/email/move", { "group_id": group_id, "ids": ids }).then(res => {
|
||||
if (res.errorNo == 0) {
|
||||
updateList()
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: 'Move completed',
|
||||
})
|
||||
} else {
|
||||
ElMessage({
|
||||
type: 'error',
|
||||
message: res.errorMsg,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
const del = function () {
|
||||
let rows = taskTableDataRef.value?.getSelectionRows()
|
||||
let ids = []
|
||||
rows.forEach(element => {
|
||||
ids.push(element.id)
|
||||
});
|
||||
|
||||
ElMessageBox.confirm(
|
||||
lang.del_email_confirm,
|
||||
'Warning',
|
||||
{
|
||||
confirmButtonText: 'OK',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'warning',
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
$http.post("/api/email/del", { "ids": ids }).then(res => {
|
||||
if (res.errorNo == 0) {
|
||||
updateList()
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: 'Delete completed',
|
||||
})
|
||||
} else {
|
||||
ElMessage({
|
||||
type: 'error',
|
||||
message: res.errorMsg,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
const rowStyle = function ({ row, rowIndwx }) {
|
||||
return { 'cursor': 'pointer' }
|
||||
}
|
||||
|
||||
const pageChange = function (p) {
|
||||
$http.post("/api/email/list", { tag: tag, page_size: 10, current_page: p }).then(res => {
|
||||
data.value = res.data.list
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
#action {
|
||||
text-align: left;
|
||||
font-size: 20px;
|
||||
line-height: 40px;
|
||||
padding-left: 10px;
|
||||
margin-right: 5px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div id="main">
|
||||
<div id="form">
|
||||
<el-form :model="form" label-width="120px">
|
||||
<el-form :model="form" label-width="120px" @keyup.enter.native="onSubmit">
|
||||
<el-form-item :label="lang.account">
|
||||
<el-input v-model="form.account" placeholder="User Name" />
|
||||
</el-form-item>
|
||||
|
@ -26,7 +26,6 @@ export default defineConfig({
|
||||
proxy: {
|
||||
"/api": "http://127.0.0.1/",
|
||||
"/attachments":"http://127.0.0.1/"
|
||||
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"logLevel": "debug",
|
||||
"logLevel": "info",
|
||||
"domain": "domain.com",
|
||||
"webDomain": "mail.domain.com",
|
||||
"dkimPrivateKeyPath": "config/dkim/dkim.priv",
|
||||
@ -12,5 +12,6 @@
|
||||
"weChatPushSecret": "",
|
||||
"weChatPushTemplateId": "",
|
||||
"weChatPushUserId": "",
|
||||
"isInit": false
|
||||
"isInit": true,
|
||||
"httpsEnabled": 2
|
||||
}
|
@ -33,7 +33,7 @@ type Config struct {
|
||||
//go:embed tables/*
|
||||
var tableConfig embed.FS
|
||||
|
||||
const Version = "2.0.1"
|
||||
const Version = "2.1.0"
|
||||
|
||||
const DBTypeMySQL = "mysql"
|
||||
const DBTypeSQLite = "sqlite"
|
||||
|
@ -12,6 +12,6 @@
|
||||
"weChatPushSecret": "",
|
||||
"weChatPushTemplateId": "",
|
||||
"weChatPushUserId": "",
|
||||
"isInit": false,
|
||||
"httpsEnabled": 0
|
||||
"isInit": true,
|
||||
"httpsEnabled": 2
|
||||
}
|
17
server/config/config_mysql.json
Normal file
17
server/config/config_mysql.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"logLevel": "info",
|
||||
"domain": "domain.com",
|
||||
"webDomain": "mail.domain.com",
|
||||
"dkimPrivateKeyPath": "config/dkim/dkim.priv",
|
||||
"sslType": "0",
|
||||
"SSLPrivateKeyPath": "config/ssl/private.key",
|
||||
"SSLPublicKeyPath": "config/ssl/public.crt",
|
||||
"dbDSN": "root:root@tcp(127.0.0.1:3306)/pmail?parseTime=True&loc=Local",
|
||||
"dbType": "mysql",
|
||||
"weChatPushAppId": "",
|
||||
"weChatPushSecret": "",
|
||||
"weChatPushTemplateId": "",
|
||||
"weChatPushUserId": "",
|
||||
"isInit": true,
|
||||
"httpsEnabled": 2
|
||||
}
|
1
server/config/dkim/README.md
Normal file
1
server/config/dkim/README.md
Normal file
@ -0,0 +1 @@
|
||||
这里存储的秘钥仅开发测试使用,线上环境请重新生成!
|
@ -0,0 +1,16 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBANgOYlTcSbWqw54y
|
||||
ISRYq6keu/fPj/m7CiVaXtOc9bJhI5uBsd8Jz1eIRzNmqLITL2e5lz2vpQAmpk3z
|
||||
8Qd2iGCbZBwMXcfPVhAasGH367GRnJw3QPybA0Maob8CnfKDH6Pks3bX7xeef/U3
|
||||
ufHYnwgPZUi121ECvPjZLEmXpBrvAgMBAAECgYAxdeGG4cMyBnyvy3QQ2Qe7OKD5
|
||||
Uxf3qJzi/jQ1J3qLsncvU1p/38QKmtUJ7Fd0JLY2faMk6P/R8AckU1L7TWRcpafY
|
||||
fUU4xpuTHpBnMhglOGjEoOfSUFh9iieG8cVreozOOfihFRgRUxu6zfxycymHjGxF
|
||||
WbdK1zoNLgFgM0DWEQJBAOnkLf4P+pDMCeA/60pll39gIuW+AlMpWrjuA3Yhdu4B
|
||||
BvC5ea3fIVCWiksfBKihXDZkLG9PZDipy2WiNypPx0kCQQDsep7qHiBPpcp4a9OU
|
||||
KXolG771iHODId2Nc3zez2xG+2pY5BzoHD2TFdW1/5v9d4q+6u6dUb8v/t2GrMIQ
|
||||
wrh3AkEAiO0Dm+wA1YoN8hGZjqlhArnmVDdjpwnbyc3Viu/Wb0l8par/uDGbkFFB
|
||||
Tu8uzAYDNPh6JwQEeUO2Bp7rysJ/uQJBAJKq7rsX2kRr+Gq9vaksHHS9g693ZOVU
|
||||
8LuVgEIU9fwEXQ4q1P7k3Q/HwBe0JESNiwEkZsAt/l0/PpgTt/17N7sCQBQKcd7e
|
||||
++RuJCiMc0vMSYAKAmiARJHv/YoxS4tLngtrhu0h5uhr+35c0kJvy6nX0VBb5KV8
|
||||
hUK3axAHTSO793o=
|
||||
-----END PRIVATE KEY-----
|
@ -0,0 +1 @@
|
||||
v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDYDmJU3Em1qsOeMiEkWKupHrv3z4/5uwolWl7TnPWyYSObgbHfCc9XiEczZqiyEy9nuZc9r6UAJqZN8/EHdohgm2QcDF3Hz1YQGrBh9+uxkZycN0D8mwNDGqG/Ap3ygx+j5LN21+8Xnn/1N7nx2J8ID2VItdtRArz42SxJl6Qa7wIDAQAB
|
1
server/config/ssl/README.md
Normal file
1
server/config/ssl/README.md
Normal file
@ -0,0 +1 @@
|
||||
这里存储的秘钥仅开发测试使用,线上环境请重新生成!
|
@ -0,0 +1,15 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIICXQIBAAKBgQDhh1fqAdYCSifPaCGfm2CjngcPaS7XehYdojBPPp5oEorWZFo1
|
||||
3Rghfh7qIXYp4nruUwfKgIPM95nYNqTnXolhUm5ywYDAhfJquuquJ7cAPhyx4SwG
|
||||
o58RgcJCM04yVKdxivskMiJvgUDz1ymdf6OA7MMbgGEcdky73TdC3FkfZQIDAQAB
|
||||
AoGAe5shPPj6oVChVxSccQzIv4QqHHEqoiCgpGczEQuh6CpZe72Oj7z4r8qfCPWD
|
||||
/NrLQ3mwaHVdR2ZhJFZ2tPRkWDI6hlK3IrC7A9LGoYCeV0AiDgMvzhQrWFjaw92u
|
||||
V9OS7tgYkewimcDaK1eF1hH+GOh2aToWMKAgCCOoXjuVhsECQQD4lc/4/Km7hrEQ
|
||||
JQGLOnB4qxdFk3HflShsXjnc/ydapQ5zCzCuCO7X89Jr8KpegC1SVmFR3YRjPNG4
|
||||
R36yLR8RAkEA6EF45wYmQfAi9w0AMlmnXanHcWXBauOrbqv2M9cjpsBvxcIjRvHp
|
||||
fca/LjEBf74X4YBhCCN1P7zRPLO2tYJjFQJAFdsGF/wO6D/lXWgDhLw0m0dfmmxm
|
||||
PKQek7iNGdMNILkWViMLuqFqbm4vd/IG6JwYX/7cO5hgRWFZhvwyNXQmIQJBAOPX
|
||||
Dqb79l3rGDHpVA8Qukn8+sV4gBS+wXcxRLY4UCYOU9fZikfXmymi5fuHYaQSNFUo
|
||||
XofgWO4s6co1toA7J70CQQDym7XIGA0GTtUtBpvRU2ew3d6JItMFjzQCjgKJ/4zm
|
||||
VqHIzyGoikq8jvbAqGJqRU72F/jfWEZlQO1KZemhmd/S
|
||||
-----END RSA PRIVATE KEY-----
|
@ -0,0 +1,16 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICcjCCAdsCFDLeR0jPO+9Q/nadNrHGI3EG2eHoMA0GCSqGSIb3DQEBCwUAMHgx
|
||||
CzAJBgNVBAYTAkNOMQswCQYDVQQIDAJCSjEQMA4GA1UEBwwHQmVpSmluZzENMAsG
|
||||
A1UECgwETnVsbDENMAsGA1UECwwETnVsbDEOMAwGA1UEAwwFUE1haWwxHDAaBgkq
|
||||
hkiG9w0BCQEWDWlAamlubnJyeS5jb20wHhcNMjMwODIzMTMyMTM0WhcNMzMwODIw
|
||||
MTMyMTM0WjB4MQswCQYDVQQGEwJDTjELMAkGA1UECAwCQkoxEDAOBgNVBAcMB0Jl
|
||||
aUppbmcxDTALBgNVBAoMBE51bGwxDTALBgNVBAsMBE51bGwxDjAMBgNVBAMMBVBN
|
||||
YWlsMRwwGgYJKoZIhvcNAQkBFg1pQGppbm5ycnkuY29tMIGfMA0GCSqGSIb3DQEB
|
||||
AQUAA4GNADCBiQKBgQDhh1fqAdYCSifPaCGfm2CjngcPaS7XehYdojBPPp5oEorW
|
||||
ZFo13Rghfh7qIXYp4nruUwfKgIPM95nYNqTnXolhUm5ywYDAhfJquuquJ7cAPhyx
|
||||
4SwGo58RgcJCM04yVKdxivskMiJvgUDz1ymdf6OA7MMbgGEcdky73TdC3FkfZQID
|
||||
AQABMA0GCSqGSIb3DQEBCwUAA4GBAMR6M83L2V9YFcYLxUv3Vaf7KrSSvuiGl/6H
|
||||
e2bMxboC8NBdsmRRhuKamti+NOe7i+BXTZ9TSy3zLQGK5LNvNOnWHHGj4vmVXoUV
|
||||
rFBMY1Vf2ZiEtO0OQjEcLOpzXhVWyZuDt2HhMRj92ESeXUSCMPZWT2UfZZTm0fhv
|
||||
ORq+I8O9
|
||||
-----END CERTIFICATE-----
|
@ -2,6 +2,7 @@ CREATE table email
|
||||
(
|
||||
id INT unsigned AUTO_INCREMENT PRIMARY KEY COMMENT '自增id',
|
||||
type tinyint(4) NOT NULL DEFAULT 0 COMMENT '邮件类型,0:收到的邮件,1:发送的邮件',
|
||||
group_id int unsigned NOT NULL DEFAULT 0 COMMENT '分组id',
|
||||
subject varchar(1000) NOT NULL DEFAULT '' COMMENT '邮件标题',
|
||||
reply_to json COMMENT '回复人',
|
||||
from_name varchar(50) NOT NULL DEFAULT '' COMMENT '发件人名称',
|
||||
|
7
server/config/tables/mysql/group.sql
Normal file
7
server/config/tables/mysql/group.sql
Normal file
@ -0,0 +1,7 @@
|
||||
CREATE TABLE `group`
|
||||
(
|
||||
id INT unsigned AUTO_INCREMENT PRIMARY KEY COMMENT '自增id',
|
||||
name varchar(10) NOT NULL DEFAULT '' COMMENT '分组名称',
|
||||
user_id INT unsigned NOT NULL DEFAULT 0 COMMENT '用户id',
|
||||
parent_id INT unsigned COMMENT '父级组ID'
|
||||
)COMMENT='分组信息表'
|
@ -1,17 +1,18 @@
|
||||
CREATE table email
|
||||
(
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
type tinyint(4) NOT NULL DEFAULT 0 ,
|
||||
subject varchar(1000) NOT NULL DEFAULT '' ,
|
||||
reply_to json ,
|
||||
from_name varchar(50) NOT NULL DEFAULT '' ,
|
||||
from_address varchar(150) NOT NULL DEFAULT '' ,
|
||||
`to` json ,
|
||||
bcc json ,
|
||||
cc json ,
|
||||
`text` text ,
|
||||
html text ,
|
||||
sender json ,
|
||||
type tinyint(4) NOT NULL DEFAULT 0,
|
||||
group_id INTEGER NOT NULL DEFAULT 0,
|
||||
subject varchar(1000) NOT NULL DEFAULT '',
|
||||
reply_to json,
|
||||
from_name varchar(50) NOT NULL DEFAULT '',
|
||||
from_address varchar(150) NOT NULL DEFAULT '',
|
||||
`to` json,
|
||||
bcc json,
|
||||
cc json,
|
||||
`text` text,
|
||||
html text,
|
||||
sender json,
|
||||
attachments json ,
|
||||
spf_check tinyint(1) DEFAULT 0 ,
|
||||
dkim_check tinyint(1) DEFAULT 0 ,
|
||||
|
7
server/config/tables/sqlite/group.sql
Normal file
7
server/config/tables/sqlite/group.sql
Normal file
@ -0,0 +1,7 @@
|
||||
CREATE TABLE `group`
|
||||
(
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name varchar(10) NOT NULL DEFAULT '',
|
||||
parent_id INTEGER NOT NULL DEFAULT 0,
|
||||
user_id INTEGER NOT NULL DEFAULT 0
|
||||
)
|
40
server/controllers/email/delete.go
Normal file
40
server/controllers/email/delete.go
Normal file
@ -0,0 +1,40 @@
|
||||
package email
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"io"
|
||||
"net/http"
|
||||
"pmail/dto"
|
||||
"pmail/dto/response"
|
||||
"pmail/services/del_email"
|
||||
)
|
||||
|
||||
type emailDeleteRequest struct {
|
||||
IDs []int `json:"ids"`
|
||||
}
|
||||
|
||||
func EmailDelete(ctx *dto.Context, w http.ResponseWriter, req *http.Request) {
|
||||
reqBytes, err := io.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
log.WithContext(ctx).Errorf("%+v", err)
|
||||
}
|
||||
var reqData emailDeleteRequest
|
||||
err = json.Unmarshal(reqBytes, &reqData)
|
||||
if err != nil {
|
||||
log.WithContext(ctx).Errorf("%+v", err)
|
||||
}
|
||||
|
||||
if len(reqData.IDs) <= 0 {
|
||||
response.NewErrorResponse(response.ParamsError, "ID错误", "").FPrint(w)
|
||||
return
|
||||
}
|
||||
|
||||
err = del_email.DelEmail(ctx, reqData.IDs)
|
||||
if err != nil {
|
||||
response.NewErrorResponse(response.ServerError, err.Error(), "").FPrint(w)
|
||||
return
|
||||
}
|
||||
response.NewSuccessResponse("success").FPrint(w)
|
||||
|
||||
}
|
40
server/controllers/email/move.go
Normal file
40
server/controllers/email/move.go
Normal file
@ -0,0 +1,40 @@
|
||||
package email
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"io"
|
||||
"net/http"
|
||||
"pmail/dto"
|
||||
"pmail/dto/response"
|
||||
"pmail/services/group"
|
||||
)
|
||||
|
||||
type moveRequest struct {
|
||||
GroupId int `json:"group_id"`
|
||||
IDs []int `json:"ids"`
|
||||
}
|
||||
|
||||
func Move(ctx *dto.Context, w http.ResponseWriter, req *http.Request) {
|
||||
reqBytes, err := io.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
log.WithContext(ctx).Errorf("%+v", err)
|
||||
}
|
||||
var reqData moveRequest
|
||||
err = json.Unmarshal(reqBytes, &reqData)
|
||||
if err != nil {
|
||||
log.WithContext(ctx).Errorf("%+v", err)
|
||||
}
|
||||
|
||||
if len(reqData.IDs) <= 0 {
|
||||
response.NewErrorResponse(response.ParamsError, "ID错误", "").FPrint(w)
|
||||
return
|
||||
}
|
||||
|
||||
if !group.MoveMailToGroup(ctx, reqData.IDs, reqData.GroupId) {
|
||||
response.NewErrorResponse(response.ServerError, "Error", "").FPrint(w)
|
||||
return
|
||||
}
|
||||
response.NewSuccessResponse("success").FPrint(w)
|
||||
|
||||
}
|
43
server/controllers/email/read.go
Normal file
43
server/controllers/email/read.go
Normal file
@ -0,0 +1,43 @@
|
||||
package email
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"io"
|
||||
"net/http"
|
||||
"pmail/dto"
|
||||
"pmail/dto/response"
|
||||
"pmail/services/detail"
|
||||
)
|
||||
|
||||
type markReadRequest struct {
|
||||
IDs []int `json:"ids"`
|
||||
}
|
||||
|
||||
func MarkRead(ctx *dto.Context, w http.ResponseWriter, req *http.Request) {
|
||||
reqBytes, err := io.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
log.WithContext(ctx).Errorf("%+v", err)
|
||||
}
|
||||
var reqData markReadRequest
|
||||
err = json.Unmarshal(reqBytes, &reqData)
|
||||
if err != nil {
|
||||
log.WithContext(ctx).Errorf("%+v", err)
|
||||
}
|
||||
|
||||
if len(reqData.IDs) <= 0 {
|
||||
response.NewErrorResponse(response.ParamsError, "ID错误", "").FPrint(w)
|
||||
return
|
||||
}
|
||||
|
||||
for _, id := range reqData.IDs {
|
||||
detail.GetEmailDetail(ctx, id, true)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
response.NewErrorResponse(response.ServerError, err.Error(), "").FPrint(w)
|
||||
return
|
||||
}
|
||||
response.NewSuccessResponse("success").FPrint(w)
|
||||
|
||||
}
|
@ -1,27 +1,32 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"io"
|
||||
"net/http"
|
||||
"pmail/db"
|
||||
"pmail/dto"
|
||||
"pmail/dto/response"
|
||||
"pmail/i18n"
|
||||
"pmail/services/group"
|
||||
"pmail/utils/array"
|
||||
)
|
||||
|
||||
type groupItem struct {
|
||||
Label string `json:"label"`
|
||||
Tag string `json:"tag"`
|
||||
Children []*groupItem `json:"children"`
|
||||
func GetUserGroupList(ctx *dto.Context, w http.ResponseWriter, req *http.Request) {
|
||||
infos := group.GetGroupList(ctx)
|
||||
response.NewSuccessResponse(infos).FPrint(w)
|
||||
}
|
||||
|
||||
func GetUserGroup(ctx *dto.Context, w http.ResponseWriter, req *http.Request) {
|
||||
|
||||
retData := []*groupItem{
|
||||
retData := []*group.GroupItem{
|
||||
{
|
||||
Label: i18n.GetText(ctx.Lang, "all_email"),
|
||||
Children: []*groupItem{
|
||||
Children: []*group.GroupItem{
|
||||
{
|
||||
Label: i18n.GetText(ctx.Lang, "inbox"),
|
||||
Tag: dto.SearchTag{Type: 0, Status: -1}.ToString(),
|
||||
Tag: dto.SearchTag{Type: 0, Status: -1, GroupId: 0}.ToString(),
|
||||
},
|
||||
{
|
||||
Label: i18n.GetText(ctx.Lang, "outbox"),
|
||||
@ -35,5 +40,59 @@ func GetUserGroup(ctx *dto.Context, w http.ResponseWriter, req *http.Request) {
|
||||
},
|
||||
}
|
||||
|
||||
retData = array.Merge(retData, group.GetGroupInfoList(ctx))
|
||||
|
||||
response.NewSuccessResponse(retData).FPrint(w)
|
||||
}
|
||||
|
||||
type addGroupRequest struct {
|
||||
Name string `json:"name"`
|
||||
ParentId int `json:"parent_id"`
|
||||
}
|
||||
|
||||
func AddGroup(ctx *dto.Context, w http.ResponseWriter, req *http.Request) {
|
||||
var reqData *addGroupRequest
|
||||
reqBytes, err := io.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
log.WithContext(ctx).Errorf("%+v", err)
|
||||
}
|
||||
err = json.Unmarshal(reqBytes, &reqData)
|
||||
if err != nil {
|
||||
log.WithContext(ctx).Errorf("%+v", err)
|
||||
}
|
||||
|
||||
res, err := db.Instance.Exec(db.WithContext(ctx, "insert into `group` (name,parent_id,user_id) values (?,?,?)"), reqData.Name, reqData.ParentId, ctx.UserInfo.ID)
|
||||
if err != nil {
|
||||
response.NewErrorResponse(response.ServerError, "DBError", err.Error()).FPrint(w)
|
||||
return
|
||||
}
|
||||
id, err := res.LastInsertId()
|
||||
if err != nil {
|
||||
response.NewErrorResponse(response.ServerError, "DBError", err.Error()).FPrint(w)
|
||||
return
|
||||
}
|
||||
response.NewSuccessResponse(id).FPrint(w)
|
||||
}
|
||||
|
||||
type delGroupRequest struct {
|
||||
Id int `json:"id"`
|
||||
}
|
||||
|
||||
func DelGroup(ctx *dto.Context, w http.ResponseWriter, req *http.Request) {
|
||||
var reqData *delGroupRequest
|
||||
reqBytes, err := io.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
log.WithContext(ctx).Errorf("%+v", err)
|
||||
}
|
||||
err = json.Unmarshal(reqBytes, &reqData)
|
||||
if err != nil {
|
||||
log.WithContext(ctx).Errorf("%+v", err)
|
||||
}
|
||||
succ, err := group.DelGroup(ctx, reqData.Id)
|
||||
|
||||
if err != nil {
|
||||
response.NewErrorResponse(response.ServerError, "DBError", err.Error()).FPrint(w)
|
||||
return
|
||||
}
|
||||
response.NewSuccessResponse(succ).FPrint(w)
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"pmail/config"
|
||||
"pmail/dto"
|
||||
"pmail/utils/errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var Instance *sqlx.DB
|
||||
@ -32,6 +33,8 @@ func Init() error {
|
||||
Instance.SetMaxIdleConns(10)
|
||||
//showMySQLCharacterSet()
|
||||
checkTable()
|
||||
// 处理版本升级带来的数据表变更
|
||||
databaseUpdate()
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -84,21 +87,27 @@ func checkTable() {
|
||||
}
|
||||
}
|
||||
|
||||
func showMySQLCharacterSet() {
|
||||
var res []struct {
|
||||
Variable_name string `db:"Variable_name"`
|
||||
Value string `db:"Value"`
|
||||
}
|
||||
err := Instance.Select(&res, "show variables like '%character%';")
|
||||
log.Debugf("%+v %+v", res, err)
|
||||
|
||||
type tableSQL struct {
|
||||
Table string `db:"Table"`
|
||||
CreateTable string `db:"Create Table"`
|
||||
}
|
||||
|
||||
func testSlowLog() {
|
||||
var res []struct {
|
||||
Value string `db:"Value"`
|
||||
func databaseUpdate() {
|
||||
// 检查email表是否有group id
|
||||
var err error
|
||||
var res []tableSQL
|
||||
if config.Instance.DbType == "sqlite" {
|
||||
err = Instance.Select(&res, "select sql as `Create Table` from sqlite_master where type='table' and tbl_name = 'email'")
|
||||
} else {
|
||||
err = Instance.Select(&res, "show create table `email`")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if len(res) > 0 && !strings.Contains(res[0].CreateTable, "group_id") {
|
||||
Instance.Exec("alter table email add group_id integer default 0 not null;")
|
||||
}
|
||||
err := Instance.Select(&res, "/* asddddasad */select /* this is test */ sleep(4) as Value")
|
||||
log.Debugf("%+v %+v", res, err)
|
||||
|
||||
}
|
||||
|
@ -3,8 +3,9 @@ package dto
|
||||
import "encoding/json"
|
||||
|
||||
type SearchTag struct {
|
||||
Type int `json:"type"`
|
||||
Status int `json:"status"`
|
||||
Type int `json:"type"` // -1 不限
|
||||
Status int `json:"status"` // -1 不限
|
||||
GroupId int `json:"group_id"` // -1 不限
|
||||
}
|
||||
|
||||
func (t SearchTag) ToString() string {
|
||||
|
@ -42,8 +42,14 @@ func HttpStart() {
|
||||
mux.HandleFunc("/api/ping", contextIterceptor(controllers.Ping))
|
||||
mux.HandleFunc("/api/login", contextIterceptor(controllers.Login))
|
||||
mux.HandleFunc("/api/group", contextIterceptor(controllers.GetUserGroup))
|
||||
mux.HandleFunc("/api/group/list", contextIterceptor(controllers.GetUserGroupList))
|
||||
mux.HandleFunc("/api/group/add", contextIterceptor(controllers.AddGroup))
|
||||
mux.HandleFunc("/api/group/del", contextIterceptor(controllers.DelGroup))
|
||||
mux.HandleFunc("/api/email/list", contextIterceptor(email.EmailList))
|
||||
mux.HandleFunc("/api/email/del", contextIterceptor(email.EmailDelete))
|
||||
mux.HandleFunc("/api/email/read", contextIterceptor(email.MarkRead))
|
||||
mux.HandleFunc("/api/email/detail", contextIterceptor(email.EmailDetail))
|
||||
mux.HandleFunc("/api/email/move", contextIterceptor(email.Move))
|
||||
mux.HandleFunc("/api/email/send", contextIterceptor(email.Send))
|
||||
mux.HandleFunc("/api/settings/modify_password", contextIterceptor(controllers.ModifyPassword))
|
||||
mux.HandleFunc("/attachments/", contextIterceptor(controllers.GetAttachments))
|
||||
|
@ -52,9 +52,15 @@ func HttpsStart() {
|
||||
mux.HandleFunc("/api/ping", contextIterceptor(controllers.Ping))
|
||||
mux.HandleFunc("/api/login", contextIterceptor(controllers.Login))
|
||||
mux.HandleFunc("/api/group", contextIterceptor(controllers.GetUserGroup))
|
||||
mux.HandleFunc("/api/group/list", contextIterceptor(controllers.GetUserGroupList))
|
||||
mux.HandleFunc("/api/group/add", contextIterceptor(controllers.AddGroup))
|
||||
mux.HandleFunc("/api/group/del", contextIterceptor(controllers.DelGroup))
|
||||
mux.HandleFunc("/api/email/list", contextIterceptor(email.EmailList))
|
||||
mux.HandleFunc("/api/email/read", contextIterceptor(email.MarkRead))
|
||||
mux.HandleFunc("/api/email/del", contextIterceptor(email.EmailDelete))
|
||||
mux.HandleFunc("/api/email/detail", contextIterceptor(email.EmailDetail))
|
||||
mux.HandleFunc("/api/email/send", contextIterceptor(email.Send))
|
||||
mux.HandleFunc("/api/email/move", contextIterceptor(email.Move))
|
||||
mux.HandleFunc("/api/settings/modify_password", contextIterceptor(controllers.ModifyPassword))
|
||||
mux.HandleFunc("/attachments/", contextIterceptor(controllers.GetAttachments))
|
||||
mux.HandleFunc("/attachments/download/", contextIterceptor(controllers.Download))
|
||||
|
@ -6,7 +6,6 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"os"
|
||||
"pmail/config"
|
||||
"pmail/cron_server"
|
||||
"pmail/dto"
|
||||
"pmail/res_init"
|
||||
"time"
|
||||
@ -80,7 +79,7 @@ func main() {
|
||||
log.Infoln("***************************************************")
|
||||
|
||||
// 定时任务启动
|
||||
go cron_server.Start()
|
||||
//go cron_server.Start()
|
||||
|
||||
// 核心服务启动
|
||||
res_init.Init()
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
type Email struct {
|
||||
Id int `db:"id" json:"id"`
|
||||
Type int8 `db:"type" json:"type"`
|
||||
GroupId int `db:"group_id" json:"group_id"`
|
||||
Subject string `db:"subject" json:"subject"`
|
||||
ReplyTo string `db:"reply_to" json:"reply_to"`
|
||||
FromName string `db:"from_name" json:"from_name"`
|
||||
|
8
server/models/group.go
Normal file
8
server/models/group.go
Normal file
@ -0,0 +1,8 @@
|
||||
package models
|
||||
|
||||
type Group struct {
|
||||
ID int `db:"id" json:"id"`
|
||||
Name string `db:"name" json:"name"`
|
||||
ParentId int `db:"parent_id" json:"parent_id"`
|
||||
UserId int `db:"user_id" json:"-"`
|
||||
}
|
32
server/services/del_email/del_email.go
Normal file
32
server/services/del_email/del_email.go
Normal file
@ -0,0 +1,32 @@
|
||||
package del_email
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"pmail/db"
|
||||
"pmail/dto"
|
||||
"pmail/models"
|
||||
"pmail/services/auth"
|
||||
"pmail/utils/array"
|
||||
"pmail/utils/errors"
|
||||
)
|
||||
|
||||
func DelEmail(ctx *dto.Context, ids []int) error {
|
||||
var emails []*models.Email
|
||||
|
||||
db.Instance.Select(&emails, db.WithContext(ctx, fmt.Sprintf("select * from email where id in (%s)", array.Join(ids, ","))))
|
||||
|
||||
for _, email := range emails {
|
||||
// 检查是否有权限
|
||||
hasAuth := auth.HasAuth(ctx, email)
|
||||
if !hasAuth {
|
||||
return errors.New("No Auth!")
|
||||
}
|
||||
}
|
||||
|
||||
_, err := db.Instance.Exec(db.WithContext(ctx, fmt.Sprintf("delete from email where id in (%s)", array.Join(ids, ","))))
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
114
server/services/group/group.go
Normal file
114
server/services/group/group.go
Normal file
@ -0,0 +1,114 @@
|
||||
package group
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"pmail/db"
|
||||
"pmail/dto"
|
||||
"pmail/models"
|
||||
"pmail/utils/array"
|
||||
"pmail/utils/errors"
|
||||
)
|
||||
|
||||
type GroupItem struct {
|
||||
Id int `json:"id"`
|
||||
Label string `json:"label"`
|
||||
Tag string `json:"tag"`
|
||||
Children []*GroupItem `json:"children"`
|
||||
}
|
||||
|
||||
func DelGroup(ctx *dto.Context, groupId int) (bool, error) {
|
||||
allGroupIds := getAllChildId(ctx, groupId)
|
||||
allGroupIds = append(allGroupIds, groupId)
|
||||
|
||||
// 开启一个事务
|
||||
trans, err := db.Instance.Begin()
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err)
|
||||
}
|
||||
|
||||
res, err := trans.Exec(db.WithContext(ctx, fmt.Sprintf("delete from `group` where id in (%s) and user_id =?", array.Join(allGroupIds, ","))), ctx.UserInfo.ID)
|
||||
if err != nil {
|
||||
trans.Rollback()
|
||||
return false, errors.Wrap(err)
|
||||
}
|
||||
num, err := res.RowsAffected()
|
||||
if err != nil {
|
||||
trans.Rollback()
|
||||
return false, errors.Wrap(err)
|
||||
}
|
||||
|
||||
_, err = trans.Exec(db.WithContext(ctx, fmt.Sprintf("update email set group_id=0 where group_id in (%s)", array.Join(allGroupIds, ","))))
|
||||
if err != nil {
|
||||
trans.Rollback()
|
||||
return false, errors.Wrap(err)
|
||||
}
|
||||
|
||||
trans.Commit()
|
||||
|
||||
return num > 0, nil
|
||||
}
|
||||
|
||||
type id struct {
|
||||
Id int `db:"id"`
|
||||
}
|
||||
|
||||
func getAllChildId(ctx *dto.Context, rootId int) []int {
|
||||
var ids []id
|
||||
var ret []int
|
||||
db.Instance.Select(&ids, db.WithContext(ctx, "select id from `group` where parent_id=? and user_id=?"), rootId, ctx.UserInfo.ID)
|
||||
for _, item := range ids {
|
||||
ret = array.Merge(ret, getAllChildId(ctx, item.Id))
|
||||
ret = append(ret, item.Id)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// GetGroupInfoList 获取全部的分组
|
||||
func GetGroupInfoList(ctx *dto.Context) []*GroupItem {
|
||||
return buildChildren(ctx, 0)
|
||||
}
|
||||
|
||||
// MoveMailToGroup 将某封邮件移动到某个分组中
|
||||
func MoveMailToGroup(ctx *dto.Context, mailId []int, groupId int) bool {
|
||||
res, err := db.Instance.Exec(db.WithContext(ctx, fmt.Sprintf("update email set group_id=? where id in (%s)", array.Join(mailId, ","))), groupId)
|
||||
if err != nil {
|
||||
log.WithContext(ctx).Errorf("SQL Error:%+v", err)
|
||||
return false
|
||||
}
|
||||
rowNum, err := res.RowsAffected()
|
||||
if err != nil {
|
||||
log.WithContext(ctx).Errorf("SQL Error:%+v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
return rowNum > 0
|
||||
}
|
||||
|
||||
func buildChildren(ctx *dto.Context, parentId int) []*GroupItem {
|
||||
var ret []*GroupItem
|
||||
var rootGroup []*models.Group
|
||||
err := db.Instance.Select(&rootGroup, db.WithContext(ctx, "select * from `group` where parent_id=? and user_id=?"), parentId, ctx.UserInfo.ID)
|
||||
|
||||
if err != nil {
|
||||
log.WithContext(ctx).Errorf("SQL Error:%v", err)
|
||||
}
|
||||
|
||||
for _, group := range rootGroup {
|
||||
ret = append(ret, &GroupItem{
|
||||
Id: group.ID,
|
||||
Label: group.Name,
|
||||
Tag: dto.SearchTag{GroupId: group.ID, Status: -1, Type: -1}.ToString(),
|
||||
Children: buildChildren(ctx, group.ID),
|
||||
})
|
||||
}
|
||||
|
||||
return ret
|
||||
|
||||
}
|
||||
|
||||
func GetGroupList(ctx *dto.Context) []*models.Group {
|
||||
var ret []*models.Group
|
||||
db.Instance.Select(&ret, db.WithContext(ctx, "select * from `group` where user_id=?"), ctx.UserInfo.ID)
|
||||
return ret
|
||||
}
|
@ -48,6 +48,11 @@ func genSQL(ctx *dto.Context, counter bool, tag, keyword string, offset, limit i
|
||||
sqlParams = append(sqlParams, tagInfo.Status)
|
||||
}
|
||||
|
||||
if tagInfo.GroupId != -1 {
|
||||
sql += " and group_id=? "
|
||||
sqlParams = append(sqlParams, tagInfo.GroupId)
|
||||
}
|
||||
|
||||
if keyword != "" {
|
||||
sql += " and (subject like ? or text like ? )"
|
||||
sqlParams = append(sqlParams, "%"+keyword+"%", "%"+keyword+"%")
|
||||
|
10
server/services/setup/db_test.go
Normal file
10
server/services/setup/db_test.go
Normal file
@ -0,0 +1,10 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSetAdminPassword(t *testing.T) {
|
||||
|
||||
SetAdminPassword(nil, "admin", "admin")
|
||||
}
|
10
server/utils/password/encode_test.go
Normal file
10
server/utils/password/encode_test.go
Normal file
@ -0,0 +1,10 @@
|
||||
package password
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEncode(t *testing.T) {
|
||||
fmt.Println(Encode("admin"))
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user