mirror of
https://github.com/Jinnrry/PMail.git
synced 2025-02-20 11:43:09 +08:00
v2.6.2 (#177)
1、修复邮件插入失败 2、插件支持设置页面 3、修复pop3邮箱拉取权限判断 4、修复非管理员账户的附件权限判断 Co-authored-by: jinnrry <i@jinnrry.com>
This commit is contained in:
parent
729eb9658a
commit
e6a56b199d
@ -14,7 +14,6 @@ COPY --from=febuild /work/dist /work/server/http_server/dist
|
|||||||
RUN apk update && apk add git
|
RUN apk update && apk add git
|
||||||
RUN cd /work/server && go build -ldflags "-s -w -X 'main.version=${VERSION}' -X 'main.goVersion=$(go version)' -X 'main.gitHash=$(git show -s --format=%H)' -X 'main.buildTime=$(TZ=UTC-8 date +%Y-%m-%d" "%H:%M:%S)'" -o pmail main.go
|
RUN cd /work/server && go build -ldflags "-s -w -X 'main.version=${VERSION}' -X 'main.goVersion=$(go version)' -X 'main.gitHash=$(git show -s --format=%H)' -X 'main.buildTime=$(TZ=UTC-8 date +%Y-%m-%d" "%H:%M:%S)'" -o pmail main.go
|
||||||
RUN cd /work/server/hooks/telegram_push && go build -ldflags "-s -w" -o output/telegram_push telegram_push.go
|
RUN cd /work/server/hooks/telegram_push && go build -ldflags "-s -w" -o output/telegram_push telegram_push.go
|
||||||
RUN cd /work/server/hooks/web_push && go build -ldflags "-s -w" -o output/web_push web_push.go
|
|
||||||
RUN cd /work/server/hooks/wechat_push && go build -ldflags "-s -w" -o output/wechat_push wechat_push.go
|
RUN cd /work/server/hooks/wechat_push && go build -ldflags "-s -w" -o output/wechat_push wechat_push.go
|
||||||
RUN cd /work/server/hooks/spam_block && go build -ldflags "-s -w" -o output/spam_block spam_block.go
|
RUN cd /work/server/hooks/spam_block && go build -ldflags "-s -w" -o output/spam_block spam_block.go
|
||||||
|
|
||||||
@ -32,7 +31,6 @@ RUN apk add --no-cache tzdata \
|
|||||||
|
|
||||||
COPY --from=serverbuild /work/server/pmail .
|
COPY --from=serverbuild /work/server/pmail .
|
||||||
COPY --from=serverbuild /work/server/hooks/telegram_push/output/* ./plugins/
|
COPY --from=serverbuild /work/server/hooks/telegram_push/output/* ./plugins/
|
||||||
COPY --from=serverbuild /work/server/hooks/web_push/output/* ./plugins/
|
|
||||||
COPY --from=serverbuild /work/server/hooks/wechat_push/output/* ./plugins/
|
COPY --from=serverbuild /work/server/hooks/wechat_push/output/* ./plugins/
|
||||||
COPY --from=serverbuild /work/server/hooks/spam_block/output/* ./plugins/
|
COPY --from=serverbuild /work/server/hooks/spam_block/output/* ./plugins/
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@ COPY server .
|
|||||||
RUN apk update && apk add git
|
RUN apk update && apk add git
|
||||||
RUN go build -ldflags "-s -w -X 'main.version=${VERSION}' -X 'main.goVersion=$(go version)' -X 'main.gitHash=${GITHASH}' -X 'main.buildTime=$(TZ=UTC-8 date +%Y-%m-%d" "%H:%M:%S)'" -o pmail main.go
|
RUN go build -ldflags "-s -w -X 'main.version=${VERSION}' -X 'main.goVersion=$(go version)' -X 'main.gitHash=${GITHASH}' -X 'main.buildTime=$(TZ=UTC-8 date +%Y-%m-%d" "%H:%M:%S)'" -o pmail main.go
|
||||||
RUN cd /work/hooks/telegram_push && go build -ldflags "-s -w" -o output/telegram_push telegram_push.go
|
RUN cd /work/hooks/telegram_push && go build -ldflags "-s -w" -o output/telegram_push telegram_push.go
|
||||||
RUN cd /work/hooks/web_push && go build -ldflags "-s -w" -o output/web_push web_push.go
|
|
||||||
RUN cd /work/hooks/wechat_push && go build -ldflags "-s -w" -o output/wechat_push wechat_push.go
|
RUN cd /work/hooks/wechat_push && go build -ldflags "-s -w" -o output/wechat_push wechat_push.go
|
||||||
RUN cd /work/hooks/spam_block && go build -ldflags "-s -w" -o output/spam_block spam_block.go
|
RUN cd /work/hooks/spam_block && go build -ldflags "-s -w" -o output/spam_block spam_block.go
|
||||||
|
|
||||||
@ -26,7 +25,6 @@ RUN apk add --no-cache tzdata \
|
|||||||
|
|
||||||
COPY --from=serverbuild /work/pmail .
|
COPY --from=serverbuild /work/pmail .
|
||||||
COPY --from=serverbuild /work/hooks/telegram_push/output/* ./plugins/
|
COPY --from=serverbuild /work/hooks/telegram_push/output/* ./plugins/
|
||||||
COPY --from=serverbuild /work/hooks/web_push/output/* ./plugins/
|
|
||||||
COPY --from=serverbuild /work/hooks/wechat_push/output/* ./plugins/
|
COPY --from=serverbuild /work/hooks/wechat_push/output/* ./plugins/
|
||||||
COPY --from=serverbuild /work/hooks/spam_block/output/* ./plugins/
|
COPY --from=serverbuild /work/hooks/spam_block/output/* ./plugins/
|
||||||
|
|
||||||
|
8
Makefile
8
Makefile
@ -21,11 +21,6 @@ telegram_push:
|
|||||||
cd server/hooks/telegram_push && CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "-s -w" -o output/telegram_push_mac_amd64 telegram_push.go
|
cd server/hooks/telegram_push && CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "-s -w" -o output/telegram_push_mac_amd64 telegram_push.go
|
||||||
cd server/hooks/telegram_push && CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -ldflags "-s -w" -o output/telegram_push_mac_arm64 telegram_push.go
|
cd server/hooks/telegram_push && CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -ldflags "-s -w" -o output/telegram_push_mac_arm64 telegram_push.go
|
||||||
|
|
||||||
web_push:
|
|
||||||
cd server/hooks/web_push && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "-s -w" -o output/web_push_linux_amd64 web_push.go
|
|
||||||
cd server/hooks/web_push && CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags "-s -w" -o output/web_push_windows_amd64.exe web_push.go
|
|
||||||
cd server/hooks/web_push && CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "-s -w" -o output/web_push_mac_amd64 web_push.go
|
|
||||||
cd server/hooks/web_push && CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -ldflags "-s -w" -o output/web_push_mac_arm64 web_push.go
|
|
||||||
|
|
||||||
wechat_push:
|
wechat_push:
|
||||||
cd server/hooks/wechat_push && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "-s -w" -o output/wechat_push_linux_amd64 wechat_push.go
|
cd server/hooks/wechat_push && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "-s -w" -o output/wechat_push_linux_amd64 wechat_push.go
|
||||||
@ -41,7 +36,7 @@ spam_block:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
plugin: telegram_push wechat_push web_push
|
plugin: telegram_push wechat_push
|
||||||
|
|
||||||
|
|
||||||
package: clean
|
package: clean
|
||||||
@ -53,7 +48,6 @@ package: clean
|
|||||||
cp -r server/config/ssl output/config/
|
cp -r server/config/ssl output/config/
|
||||||
cp -r server/config/config.json output/config/
|
cp -r server/config/config.json output/config/
|
||||||
mv server/hooks/telegram_push/output/* output/plugins
|
mv server/hooks/telegram_push/output/* output/plugins
|
||||||
mv server/hooks/web_push/output/* output/plugins
|
|
||||||
mv server/hooks/wechat_push/output/* output/plugins
|
mv server/hooks/wechat_push/output/* output/plugins
|
||||||
cp README.md output/
|
cp README.md output/
|
||||||
|
|
||||||
|
@ -25,6 +25,11 @@
|
|||||||
<el-tab-pane v-if="$userInfos.is_admin" :label="lang.user_management">
|
<el-tab-pane v-if="$userInfos.is_admin" :label="lang.user_management">
|
||||||
<UserManagement />
|
<UserManagement />
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
|
||||||
|
<el-tab-pane :label="lang.plugin_settings">
|
||||||
|
<PluginSettings />
|
||||||
|
</el-tab-pane>
|
||||||
|
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
</el-drawer>
|
</el-drawer>
|
||||||
|
|
||||||
@ -41,6 +46,7 @@ import GroupSettings from './GroupSettings.vue';
|
|||||||
import RuleSettings from './RuleSettings.vue';
|
import RuleSettings from './RuleSettings.vue';
|
||||||
import UserManagement from './UserManagement.vue';
|
import UserManagement from './UserManagement.vue';
|
||||||
import { getCurrentInstance } from 'vue'
|
import { getCurrentInstance } from 'vue'
|
||||||
|
import PluginSettings from './PluginSettings.vue';
|
||||||
const app = getCurrentInstance()
|
const app = getCurrentInstance()
|
||||||
const $http = app.appContext.config.globalProperties.$http
|
const $http = app.appContext.config.globalProperties.$http
|
||||||
const $isLogin = app.appContext.config.globalProperties.$isLogin
|
const $isLogin = app.appContext.config.globalProperties.$isLogin
|
||||||
|
35
fe/src/components/PluginSettings.vue
Normal file
35
fe/src/components/PluginSettings.vue
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<template>
|
||||||
|
<div id="main">
|
||||||
|
<el-tabs>
|
||||||
|
<el-tab-pane v-for="(src, name) in pluginList" :label="name">
|
||||||
|
<iframe :src="src"></iframe>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { reactive, ref, getCurrentInstance } from 'vue'
|
||||||
|
const app = getCurrentInstance()
|
||||||
|
const $http = app.appContext.config.globalProperties.$http
|
||||||
|
const pluginList = reactive({})
|
||||||
|
|
||||||
|
$http.get('/api/plugin/list').then(res => {
|
||||||
|
if (res.data != null && res.data.length > 0) {
|
||||||
|
for (let i = 0; i < res.data.length; i++) {
|
||||||
|
let name = res.data[i];
|
||||||
|
pluginList[name] = "/api/plugin/settings/"+ name +"/index.html";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
iframe{
|
||||||
|
width: 100%;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
@ -7,6 +7,7 @@ var lang = {
|
|||||||
"editUser": "Edit Account",
|
"editUser": "Edit Account",
|
||||||
"user_name": "User Name",
|
"user_name": "User Name",
|
||||||
"user_management": "user management",
|
"user_management": "user management",
|
||||||
|
"plugin_settings": "Plugin Settings",
|
||||||
"lang": "en",
|
"lang": "en",
|
||||||
"submit": "submit",
|
"submit": "submit",
|
||||||
"compose": "compose",
|
"compose": "compose",
|
||||||
@ -117,6 +118,7 @@ var zhCN = {
|
|||||||
"newUser": "新增用户",
|
"newUser": "新增用户",
|
||||||
"editUser": "编辑用户",
|
"editUser": "编辑用户",
|
||||||
"user_management": "用户管理",
|
"user_management": "用户管理",
|
||||||
|
"plugin_settings": "插件设置",
|
||||||
"lang": "zhCn",
|
"lang": "zhCn",
|
||||||
"submit": "提交",
|
"submit": "提交",
|
||||||
"compose": "发件",
|
"compose": "发件",
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
"SSLPublicKeyPath": "./config/ssl/public.crt",
|
"SSLPublicKeyPath": "./config/ssl/public.crt",
|
||||||
"dbDSN": "./config/pmail_temp.db",
|
"dbDSN": "./config/pmail_temp.db",
|
||||||
"dbType": "sqlite",
|
"dbType": "sqlite",
|
||||||
"httpsEnabled": 1,
|
"httpsEnabled": 2,
|
||||||
"spamFilterLevel": 0,
|
"spamFilterLevel": 0,
|
||||||
"httpPort": 80,
|
"httpPort": 80,
|
||||||
"httpsPort": 443,
|
"httpsPort": 443,
|
||||||
|
46
server/controllers/plugin.go
Normal file
46
server/controllers/plugin.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Jinnrry/pmail/dto/response"
|
||||||
|
"github.com/Jinnrry/pmail/hooks"
|
||||||
|
"github.com/Jinnrry/pmail/utils/context"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetPluginList(ctx *context.Context, w http.ResponseWriter, req *http.Request) {
|
||||||
|
ret := []string{}
|
||||||
|
for s, _ := range hooks.HookList {
|
||||||
|
ret = append(ret, s)
|
||||||
|
}
|
||||||
|
response.NewSuccessResponse(ret).FPrint(w)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func SettingsHtml(ctx *context.Context, w http.ResponseWriter, req *http.Request) {
|
||||||
|
args := strings.Split(req.RequestURI, "/")
|
||||||
|
if len(args) < 4 {
|
||||||
|
response.NewErrorResponse(response.ParamsError, "404", "").FPrint(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pluginName := args[4]
|
||||||
|
if plugin, ok := hooks.HookList[pluginName]; ok {
|
||||||
|
dt, err := io.ReadAll(req.Body)
|
||||||
|
if err != nil {
|
||||||
|
response.NewErrorResponse(response.ParamsError, err.Error(), "").FPrint(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
html := plugin.SettingsHtml(ctx,
|
||||||
|
strings.Join(args[4:], "/"),
|
||||||
|
string(dt),
|
||||||
|
)
|
||||||
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
|
|
||||||
|
w.Write([]byte(html))
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
response.NewErrorResponse(response.ParamsError, "404", "")
|
||||||
|
}
|
@ -54,15 +54,17 @@ type Email struct {
|
|||||||
Date string
|
Date string
|
||||||
Status int // 0未发送,1已发送,2发送失败,3删除
|
Status int // 0未发送,1已发送,2发送失败,3删除
|
||||||
MessageId int64
|
MessageId int64
|
||||||
|
Size int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewEmailFromReader(to []string, r io.Reader) *Email {
|
func NewEmailFromReader(to []string, r io.Reader, size int) *Email {
|
||||||
ret := &Email{}
|
ret := &Email{}
|
||||||
m, err := message.Read(r)
|
m, err := message.Read(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("email解析错误! Error %+v", err)
|
log.Errorf("email解析错误! Error %+v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret.Size = size
|
||||||
ret.From = buildUser(m.Header.Get("From"))
|
ret.From = buildUser(m.Header.Get("From"))
|
||||||
|
|
||||||
if len(to) > 0 {
|
if len(to) > 0 {
|
||||||
|
@ -137,6 +137,47 @@ func (h *HookSender) ReceiveParseAfter(ctx *context.Context, email *parsemail.Em
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetName 获取插件名称
|
||||||
|
func (h *HookSender) GetName(ctx *context.Context) string {
|
||||||
|
|
||||||
|
dto := framework.HookDTO{
|
||||||
|
Ctx: ctx,
|
||||||
|
}
|
||||||
|
body, _ := json.Marshal(dto)
|
||||||
|
|
||||||
|
ret, errL := h.httpc.Post("http://plugin/GetName", "application/json", strings.NewReader(string(body)))
|
||||||
|
if errL != nil {
|
||||||
|
log.WithContext(ctx).Errorf("[%s] Error! %v", h.name, errL)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
body, _ = io.ReadAll(ret.Body)
|
||||||
|
|
||||||
|
return string(body)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SettingsHtml 插件页面
|
||||||
|
func (h *HookSender) SettingsHtml(ctx *context.Context, url string, requestData string) string {
|
||||||
|
|
||||||
|
dto := framework.SettingsHtmlRequest{
|
||||||
|
Ctx: ctx,
|
||||||
|
URL: url,
|
||||||
|
RequestData: requestData,
|
||||||
|
}
|
||||||
|
body, _ := json.Marshal(dto)
|
||||||
|
|
||||||
|
ret, errL := h.httpc.Post("http://plugin/SettingsHtml", "application/json", strings.NewReader(string(body)))
|
||||||
|
if errL != nil {
|
||||||
|
log.WithContext(ctx).Errorf("[%s] Error! %v", h.name, errL)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
body, _ = io.ReadAll(ret.Body)
|
||||||
|
|
||||||
|
return string(body)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func NewHookSender(socketPath string, name string, serverVersion string) *HookSender {
|
func NewHookSender(socketPath string, name string, serverVersion string) *HookSender {
|
||||||
httpc := http.Client{
|
httpc := http.Client{
|
||||||
Timeout: time.Second * 10,
|
Timeout: time.Second * 10,
|
||||||
@ -214,8 +255,10 @@ func Init(serverVersion string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if loadSucc {
|
if loadSucc {
|
||||||
HookList[info.Name()] = NewHookSender(socketPath, info.Name(), serverVersion)
|
hk := NewHookSender(socketPath, info.Name(), serverVersion)
|
||||||
log.Infof("[%s] Plugin Load Success!", info.Name())
|
hkName := hk.GetName(&context.Context{})
|
||||||
|
HookList[hkName] = hk
|
||||||
|
log.Infof("[%s] Plugin Load Success!", hkName)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -27,6 +28,10 @@ type EmailHook interface {
|
|||||||
ReceiveParseAfter(ctx *context.Context, email *parsemail.Email)
|
ReceiveParseAfter(ctx *context.Context, email *parsemail.Email)
|
||||||
// ReceiveSaveAfter 邮件落库以后执行(收信规则后执行) 异步执行
|
// ReceiveSaveAfter 邮件落库以后执行(收信规则后执行) 异步执行
|
||||||
ReceiveSaveAfter(ctx *context.Context, email *parsemail.Email, ue []*models.UserEmail)
|
ReceiveSaveAfter(ctx *context.Context, email *parsemail.Email, ue []*models.UserEmail)
|
||||||
|
// GetName 获取插件名称
|
||||||
|
GetName(ctx *context.Context) string
|
||||||
|
// SettingsHtml 插件页面
|
||||||
|
SettingsHtml(ctx *context.Context, url string, requestData string) string
|
||||||
}
|
}
|
||||||
|
|
||||||
// HookDTO PMail 主程序和插件通信的结构体
|
// HookDTO PMail 主程序和插件通信的结构体
|
||||||
@ -39,6 +44,12 @@ type HookDTO struct {
|
|||||||
UserEmail []*models.UserEmail
|
UserEmail []*models.UserEmail
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SettingsHtmlRequest struct {
|
||||||
|
Ctx *context.Context // 上下文
|
||||||
|
URL string
|
||||||
|
RequestData string
|
||||||
|
}
|
||||||
|
|
||||||
type Plugin struct {
|
type Plugin struct {
|
||||||
name string
|
name string
|
||||||
hook EmailHook
|
hook EmailHook
|
||||||
@ -160,6 +171,32 @@ func (p *Plugin) Run() {
|
|||||||
writer.Write(body)
|
writer.Write(body)
|
||||||
log.Debugf("[%s] ReceiveSaveAfter End", p.name)
|
log.Debugf("[%s] ReceiveSaveAfter End", p.name)
|
||||||
})
|
})
|
||||||
|
mux.HandleFunc("/GetName", func(writer http.ResponseWriter, request *http.Request) {
|
||||||
|
log.Debugf("[%s] GetName Start", p.name)
|
||||||
|
var hookDTO HookDTO
|
||||||
|
body, _ := io.ReadAll(request.Body)
|
||||||
|
err := json.Unmarshal(body, &hookDTO)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("params error %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
name := strings.ReplaceAll(p.hook.GetName(hookDTO.Ctx), " ", "")
|
||||||
|
writer.Write([]byte(name))
|
||||||
|
log.Debugf("[%s] GetName End", p.name)
|
||||||
|
})
|
||||||
|
mux.HandleFunc("/SettingsHtml", func(writer http.ResponseWriter, request *http.Request) {
|
||||||
|
log.Debugf("[%s] SettingsHtml Start", p.name)
|
||||||
|
var hookDTO SettingsHtmlRequest
|
||||||
|
body, _ := io.ReadAll(request.Body)
|
||||||
|
err := json.Unmarshal(body, &hookDTO)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("params error %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
html := p.hook.SettingsHtml(hookDTO.Ctx, hookDTO.URL, hookDTO.RequestData)
|
||||||
|
writer.Write([]byte(html))
|
||||||
|
log.Debugf("[%s] SettingsHtml End", p.name)
|
||||||
|
})
|
||||||
|
|
||||||
server := http.Server{
|
server := http.Server{
|
||||||
ReadTimeout: 5 * time.Second,
|
ReadTimeout: 5 * time.Second,
|
||||||
|
@ -52,16 +52,11 @@ curl -X POST http://localhost:8501/v1/models/emotion_model:predict -d '{
|
|||||||
|
|
||||||
4、将spam_block插件移动到pmail插件目录
|
4、将spam_block插件移动到pmail插件目录
|
||||||
|
|
||||||
5、在插件位置新建配置文件`spam_block_config.json`内容类似
|
5、设置插件
|
||||||
|
|
||||||
```json
|
PMail后台->右上角设置按钮->插件设置->SpamBlock
|
||||||
{
|
|
||||||
"apiURL": "http://localhost:8501/v1/models/emotion_model:predict",
|
|
||||||
"apiTimeout": 3000
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
apiURL表示模型api访问地址,如果你是使用Docker部署,PMail和tensorflow/serving容器需要设置为相同网络才能通信,并且需要把localhost替换为tensorflow/serving的容器名称
|
接口地址表示模型api访问地址,如果你是使用Docker部署,PMail和tensorflow/serving容器需要设置为相同网络才能通信,并且需要把localhost替换为tensorflow/serving的容器名称
|
||||||
|
|
||||||
# 模型效果
|
# 模型效果
|
||||||
|
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
datasets==2.20.0
|
datasets==2.20.0
|
||||||
numpy==1.20.3
|
numpy==1.20.3
|
||||||
retvec==1.0.1
|
retvec==1.0.1
|
||||||
tensorflow==2.8.4
|
tensorflow==2.12.1
|
||||||
|
beautifulsoup4
|
||||||
|
lxml
|
@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
_ "embed"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/Jinnrry/pmail/dto/parsemail"
|
"github.com/Jinnrry/pmail/dto/parsemail"
|
||||||
@ -9,6 +10,7 @@ import (
|
|||||||
"github.com/Jinnrry/pmail/models"
|
"github.com/Jinnrry/pmail/models"
|
||||||
"github.com/Jinnrry/pmail/utils/context"
|
"github.com/Jinnrry/pmail/utils/context"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/spf13/cast"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
@ -21,15 +23,64 @@ type SpamBlock struct {
|
|||||||
hc *http.Client
|
hc *http.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s SpamBlock) SendBefore(ctx *context.Context, email *parsemail.Email) {
|
func (s *SpamBlock) SendBefore(ctx *context.Context, email *parsemail.Email) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s SpamBlock) SendAfter(ctx *context.Context, email *parsemail.Email, err map[string]error) {
|
func (s *SpamBlock) SendAfter(ctx *context.Context, email *parsemail.Email, err map[string]error) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s SpamBlock) ReceiveParseBefore(ctx *context.Context, email *[]byte) {
|
func (s *SpamBlock) ReceiveParseBefore(ctx *context.Context, email *[]byte) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetName 获取插件名称
|
||||||
|
func (s *SpamBlock) GetName(ctx *context.Context) string {
|
||||||
|
return "SpamBlock"
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:embed static/index.html
|
||||||
|
var index string
|
||||||
|
|
||||||
|
//go:embed static/jquery.js
|
||||||
|
var jquery string
|
||||||
|
|
||||||
|
// SettingsHtml 插件页面
|
||||||
|
func (s *SpamBlock) SettingsHtml(ctx *context.Context, url string, requestData string) string {
|
||||||
|
|
||||||
|
if strings.Contains(url, "jquery.js") {
|
||||||
|
return jquery
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(url, "index.html") {
|
||||||
|
if !ctx.IsAdmin {
|
||||||
|
return fmt.Sprintf(`
|
||||||
|
<div>
|
||||||
|
Please contact the administrator for configuration.
|
||||||
|
</div>
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf(index, s.cfg.ApiURL, s.cfg.ApiTimeout, s.cfg.Threshold)
|
||||||
|
}
|
||||||
|
|
||||||
|
var cfg SpamBlockConfig
|
||||||
|
var tempCfg map[string]string
|
||||||
|
err := json.Unmarshal([]byte(requestData), &tempCfg)
|
||||||
|
if err != nil {
|
||||||
|
return err.Error()
|
||||||
|
}
|
||||||
|
cfg.ApiURL = tempCfg["url"]
|
||||||
|
cfg.Threshold = cast.ToFloat64(tempCfg["threshold"])
|
||||||
|
cfg.ApiTimeout = cast.ToInt(tempCfg["timeout"])
|
||||||
|
err = saveConfig(cfg)
|
||||||
|
if err != nil {
|
||||||
|
return err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
s.cfg = cfg
|
||||||
|
|
||||||
|
return "success"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,7 +96,11 @@ type InstanceItem struct {
|
|||||||
Token []string `json:"token"`
|
Token []string `json:"token"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s SpamBlock) ReceiveParseAfter(ctx *context.Context, email *parsemail.Email) {
|
func (s *SpamBlock) ReceiveParseAfter(ctx *context.Context, email *parsemail.Email) {
|
||||||
|
|
||||||
|
if s.cfg.ApiURL == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
reqData := ApiRequest{
|
reqData := ApiRequest{
|
||||||
Instances: []InstanceItem{
|
Instances: []InstanceItem{
|
||||||
@ -94,25 +149,26 @@ func (s SpamBlock) ReceiveParseAfter(ctx *context.Context, email *parsemail.Emai
|
|||||||
|
|
||||||
switch maxClass {
|
switch maxClass {
|
||||||
case 0:
|
case 0:
|
||||||
log.WithContext(ctx).Infof("[Spam Check Result: Normal] %s", email.Subject)
|
log.WithContext(ctx).Infof("[Spam Check Result: %f Normal] %s", maxScore, email.Subject)
|
||||||
case 1:
|
case 1:
|
||||||
log.WithContext(ctx).Infof("[Spam Check Result: Spam ] %s", email.Subject)
|
log.WithContext(ctx).Infof("[Spam Check Result: %f Spam ] %s", maxScore, email.Subject)
|
||||||
case 2:
|
case 2:
|
||||||
log.WithContext(ctx).Infof("[Spam Check Result: Blackmail ] %s", email.Subject)
|
log.WithContext(ctx).Infof("[Spam Check Result: %f Blackmail ] %s", maxScore, email.Subject)
|
||||||
}
|
}
|
||||||
|
|
||||||
if maxClass != 0 {
|
if maxClass != 0 && maxScore > s.cfg.Threshold/100 {
|
||||||
email.Status = 3
|
email.Status = 3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s SpamBlock) ReceiveSaveAfter(ctx *context.Context, email *parsemail.Email, ue []*models.UserEmail) {
|
func (s *SpamBlock) ReceiveSaveAfter(ctx *context.Context, email *parsemail.Email, ue []*models.UserEmail) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type SpamBlockConfig struct {
|
type SpamBlockConfig struct {
|
||||||
ApiURL string `json:"apiURL"`
|
ApiURL string `json:"apiURL"`
|
||||||
ApiTimeout int `json:"apiTimeout"` // 单位毫秒
|
ApiTimeout int `json:"apiTimeout"` // 单位毫秒
|
||||||
|
Threshold float64 `json:"threshold"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSpamBlockHook() *SpamBlock {
|
func NewSpamBlockHook() *SpamBlock {
|
||||||
@ -123,20 +179,18 @@ func NewSpamBlockHook() *SpamBlock {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
json.Unmarshal(cfgData, &pluginConfig)
|
json.Unmarshal(cfgData, &pluginConfig)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
log.Infof("No Config file found")
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("Config: %+v", pluginConfig)
|
log.Infof("Config: %+v", pluginConfig)
|
||||||
if pluginConfig.ApiURL == "" {
|
|
||||||
pluginConfig.ApiURL = "http://localhost:8501/v1/models/emotion_model:predict"
|
|
||||||
}
|
|
||||||
|
|
||||||
if pluginConfig.ApiTimeout == 0 {
|
if pluginConfig.ApiTimeout == 0 {
|
||||||
pluginConfig.ApiTimeout = 3000
|
pluginConfig.ApiTimeout = 3000
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if pluginConfig.Threshold == 0 {
|
||||||
|
pluginConfig.Threshold = 80
|
||||||
|
}
|
||||||
|
|
||||||
hc := &http.Client{
|
hc := &http.Client{
|
||||||
Timeout: time.Duration(pluginConfig.ApiTimeout) * time.Millisecond,
|
Timeout: time.Duration(pluginConfig.ApiTimeout) * time.Millisecond,
|
||||||
}
|
}
|
||||||
@ -147,6 +201,12 @@ func NewSpamBlockHook() *SpamBlock {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func saveConfig(cfg SpamBlockConfig) error {
|
||||||
|
data, _ := json.Marshal(cfg)
|
||||||
|
err := os.WriteFile("./plugins/spam_block_config.json", data, 0777)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
log.Infof("SpamBlockPlug Star Success")
|
log.Infof("SpamBlockPlug Star Success")
|
||||||
instance := NewSpamBlockHook()
|
instance := NewSpamBlockHook()
|
||||||
|
53
server/hooks/spam_block/static/index.html
Normal file
53
server/hooks/spam_block/static/index.html
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<html>
|
||||||
|
<header>
|
||||||
|
<script src="/api/plugin/settings/SpamBlock/jquery.js"></script>
|
||||||
|
<script>
|
||||||
|
$(function () {
|
||||||
|
$("#submit").click(function () {
|
||||||
|
let data = {};
|
||||||
|
let value = $('#form').serializeArray();
|
||||||
|
$.each(value, function (index, item) {
|
||||||
|
data[item.name] = item.value;
|
||||||
|
});
|
||||||
|
let jsonData = JSON.stringify(data);
|
||||||
|
console.log(jsonData);
|
||||||
|
|
||||||
|
$.post("/api/plugin/settings/SpamBlock/save", jsonData, function (data) {
|
||||||
|
alert(data)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
</header>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<form id="form" action="/api/plugin/settings/SpamBlock/save" method="post">
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="url"> 模型API接口地址: </label>
|
||||||
|
<input id="url" style="width: 600px;" name="url" value="%s"
|
||||||
|
placeholder="http://localhost:8501/v1/models/emotion_model:predict">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="timeout"> 超时时间: </label>
|
||||||
|
<input id="timeout" style="width: 600px;" name="timeout" value="%d" placeholder="单位毫秒">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="threshold"> 识别阈值: </label>
|
||||||
|
<input id="threshold" name="threshold" type="number" value="%f" style="width: 600px;"
|
||||||
|
step=".01" max="100" min="0" placeholder="识别阈值,值越低过滤越严格,越容易误判">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<input id="submit" type="button" value="Submit"/>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
|
||||||
|
</html>
|
2
server/hooks/spam_block/static/jquery.js
vendored
Normal file
2
server/hooks/spam_block/static/jquery.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -24,6 +24,8 @@ def getData(folder_path):
|
|||||||
# 读取csv文件内容
|
# 读取csv文件内容
|
||||||
with open(file_path, 'r', errors='ignore') as csv_file:
|
with open(file_path, 'r', errors='ignore') as csv_file:
|
||||||
for line in csv_file:
|
for line in csv_file:
|
||||||
|
if line[0] == '' or line[0]==' ':
|
||||||
|
continue
|
||||||
labels.append([int(str.strip(line[0]))])
|
labels.append([int(str.strip(line[0]))])
|
||||||
msgs.append(line[3:])
|
msgs.append(line[3:])
|
||||||
return np.array(msgs), np.array(labels)
|
return np.array(msgs), np.array(labels)
|
||||||
|
File diff suppressed because one or more lines are too long
57
server/hooks/spam_block/trec07p_format.py
Normal file
57
server/hooks/spam_block/trec07p_format.py
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import os
|
||||||
|
from email.parser import Parser
|
||||||
|
from email.policy import default
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
|
||||||
|
|
||||||
|
# 该脚本用于整理trec06c数据集,可以生成训练集和测试集数据格式
|
||||||
|
|
||||||
|
def getData(path):
|
||||||
|
f = open(path, 'r', errors='ignore')
|
||||||
|
data = f.read()
|
||||||
|
headers = Parser(policy=default).parsestr(data)
|
||||||
|
body = ""
|
||||||
|
if headers.is_multipart():
|
||||||
|
for part in headers.iter_parts():
|
||||||
|
tbody = part.get_payload()
|
||||||
|
if isinstance(tbody, list):
|
||||||
|
for item in tbody:
|
||||||
|
txt = item.get_payload()
|
||||||
|
if isinstance(tbody, list):
|
||||||
|
return "", ""
|
||||||
|
bsObj = BeautifulSoup(txt, 'lxml')
|
||||||
|
body += bsObj.get_text()
|
||||||
|
else:
|
||||||
|
bsObj = BeautifulSoup(tbody, 'lxml')
|
||||||
|
body += bsObj.get_text()
|
||||||
|
else:
|
||||||
|
tbody = headers.get_payload()
|
||||||
|
bsObj = BeautifulSoup(tbody, 'lxml')
|
||||||
|
body += bsObj.get_text()
|
||||||
|
return headers["subject"], body.replace("\n", "")
|
||||||
|
|
||||||
|
|
||||||
|
num = 0
|
||||||
|
|
||||||
|
# getData("../data/000/000")
|
||||||
|
with open("index", "r") as f:
|
||||||
|
with open("trec07p_train.csv", "w") as w:
|
||||||
|
with open("trec07p_test.csv", "w") as wt:
|
||||||
|
while True:
|
||||||
|
line = f.readline()
|
||||||
|
if not line:
|
||||||
|
break
|
||||||
|
infos = line.split(" ")
|
||||||
|
subject, body = getData(infos[1].strip())
|
||||||
|
if subject == "":
|
||||||
|
continue
|
||||||
|
tp = 0
|
||||||
|
if infos[0].lower() == "spam":
|
||||||
|
tp = 1
|
||||||
|
data = "{} \t{} {}\n".format(tp, subject, body)
|
||||||
|
if num < 55000:
|
||||||
|
w.write(data)
|
||||||
|
else:
|
||||||
|
wt.write(data)
|
||||||
|
num += 1
|
||||||
|
print(num)
|
@ -35,6 +35,20 @@ func (w *TelegramPushHook) ReceiveSaveAfter(ctx *context.Context, email *parsema
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetName 获取插件名称
|
||||||
|
func (w *TelegramPushHook) GetName(ctx *context.Context) string {
|
||||||
|
return "TgPush"
|
||||||
|
}
|
||||||
|
|
||||||
|
// SettingsHtml 插件页面
|
||||||
|
func (w *TelegramPushHook) SettingsHtml(ctx *context.Context, url string, requestData string) string {
|
||||||
|
return fmt.Sprintf(`
|
||||||
|
<div>
|
||||||
|
TG push No Settings Page
|
||||||
|
</div>
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
func (w *TelegramPushHook) SendBefore(ctx *context.Context, email *parsemail.Email) {
|
func (w *TelegramPushHook) SendBefore(ctx *context.Context, email *parsemail.Email) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
## How To Ues
|
|
||||||
|
|
||||||
|
|
||||||
Copy plugin binary file to `/plugins`
|
|
||||||
|
|
||||||
add config.json to `/plugins/config.com` like this:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"webPushUrl": "", // webhook push URL
|
|
||||||
"webPushToken": "", // webhook push token
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
@ -1,136 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"github.com/Jinnrry/pmail/config"
|
|
||||||
"github.com/Jinnrry/pmail/dto/parsemail"
|
|
||||||
"github.com/Jinnrry/pmail/hooks/framework"
|
|
||||||
"github.com/Jinnrry/pmail/models"
|
|
||||||
"github.com/Jinnrry/pmail/utils/context"
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
type WebPushHook struct {
|
|
||||||
url string
|
|
||||||
token string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *WebPushHook) ReceiveSaveAfter(ctx *context.Context, email *parsemail.Email, ue []*models.UserEmail) {
|
|
||||||
if w.url == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
content := string(email.Text)
|
|
||||||
|
|
||||||
if content == "" {
|
|
||||||
content = email.Subject
|
|
||||||
}
|
|
||||||
|
|
||||||
webhookURL := w.url // 替换为您的 Webhook URL
|
|
||||||
|
|
||||||
to := make([]string, len(email.To))
|
|
||||||
for i, user := range email.To {
|
|
||||||
to[i] = user.EmailAddress
|
|
||||||
}
|
|
||||||
|
|
||||||
data := EmailData{
|
|
||||||
From: email.From.EmailAddress,
|
|
||||||
To: to,
|
|
||||||
Subject: email.Subject,
|
|
||||||
Body: content,
|
|
||||||
Token: w.token,
|
|
||||||
}
|
|
||||||
|
|
||||||
jsonData, err := json.Marshal(data)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.WithContext(ctx).Errorf("web push error %+v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := http.Post(webhookURL, "application/json", bytes.NewBuffer(jsonData))
|
|
||||||
if err != nil {
|
|
||||||
log.WithContext(ctx).Errorf("web push error %+v", err)
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// EmailData 用于存储解析后的邮件数据
|
|
||||||
type EmailData struct {
|
|
||||||
From string `json:"from"`
|
|
||||||
To []string `json:"to"`
|
|
||||||
Subject string `json:"subject"`
|
|
||||||
Body string `json:"body"`
|
|
||||||
Token string `json:"token"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *WebPushHook) SendBefore(ctx *context.Context, email *parsemail.Email) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *WebPushHook) SendAfter(ctx *context.Context, email *parsemail.Email, err map[string]error) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *WebPushHook) ReceiveParseBefore(ctx *context.Context, email *[]byte) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *WebPushHook) ReceiveParseAfter(ctx *context.Context, email *parsemail.Email) {
|
|
||||||
}
|
|
||||||
|
|
||||||
type Config struct {
|
|
||||||
WebPushUrl string `json:"webPushUrl"`
|
|
||||||
WebPushToken string `json:"webPushToken"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewWebPushHook() *WebPushHook {
|
|
||||||
var cfgData []byte
|
|
||||||
var err error
|
|
||||||
|
|
||||||
cfgData, err = os.ReadFile("./config/config.json")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
var mainConfig *config.Config
|
|
||||||
err = json.Unmarshal(cfgData, &mainConfig)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var pluginConfig *Config
|
|
||||||
if _, err := os.Stat("./plugins/web_push_config.json"); err == nil {
|
|
||||||
cfgData, err = os.ReadFile("./plugins/web_push_config.json")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
err = json.Unmarshal(cfgData, &pluginConfig)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
token := ""
|
|
||||||
pushURL := ""
|
|
||||||
if pluginConfig != nil {
|
|
||||||
pushURL = pluginConfig.WebPushUrl
|
|
||||||
token = pluginConfig.WebPushToken
|
|
||||||
} else {
|
|
||||||
pushURL = mainConfig.WebPushUrl
|
|
||||||
token = mainConfig.WebPushToken
|
|
||||||
}
|
|
||||||
|
|
||||||
ret := &WebPushHook{
|
|
||||||
url: pushURL,
|
|
||||||
token: token,
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
framework.CreatePlugin("web_push", NewWebPushHook()).Run()
|
|
||||||
}
|
|
@ -32,6 +32,19 @@ type WeChatPushHook struct {
|
|||||||
mainConfig *config.Config
|
mainConfig *config.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *WeChatPushHook) GetName(ctx *context.Context) string {
|
||||||
|
return "WeChatPushHook"
|
||||||
|
}
|
||||||
|
|
||||||
|
// SettingsHtml 插件页面
|
||||||
|
func (w *WeChatPushHook) SettingsHtml(ctx *context.Context, url string, requestData string) string {
|
||||||
|
return fmt.Sprintf(`
|
||||||
|
<div>
|
||||||
|
TG push No Settings Page
|
||||||
|
</div>
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
func (w *WeChatPushHook) ReceiveSaveAfter(ctx *context.Context, email *parsemail.Email, ue []*models.UserEmail) {
|
func (w *WeChatPushHook) ReceiveSaveAfter(ctx *context.Context, email *parsemail.Email, ue []*models.UserEmail) {
|
||||||
if w.appId == "" || w.secret == "" || w.pushUser == "" {
|
if w.appId == "" || w.secret == "" || w.pushUser == "" {
|
||||||
return
|
return
|
||||||
|
@ -54,6 +54,8 @@ func router(mux *http.ServeMux) {
|
|||||||
mux.HandleFunc("/api/user/edit", contextIterceptor(controllers.EditUser))
|
mux.HandleFunc("/api/user/edit", contextIterceptor(controllers.EditUser))
|
||||||
mux.HandleFunc("/api/user/info", contextIterceptor(controllers.Info))
|
mux.HandleFunc("/api/user/info", contextIterceptor(controllers.Info))
|
||||||
mux.HandleFunc("/api/user/list", contextIterceptor(controllers.UserList))
|
mux.HandleFunc("/api/user/list", contextIterceptor(controllers.UserList))
|
||||||
|
mux.HandleFunc("/api/plugin/settings/", contextIterceptor(controllers.SettingsHtml))
|
||||||
|
mux.HandleFunc("/api/plugin/list", contextIterceptor(controllers.GetPluginList))
|
||||||
}
|
}
|
||||||
|
|
||||||
func HttpStart() {
|
func HttpStart() {
|
||||||
|
@ -185,15 +185,12 @@ func (a action) Uidl(session *gopop.Session, msg string) ([]gopop.UidlItem, erro
|
|||||||
|
|
||||||
var res []listItem
|
var res []listItem
|
||||||
|
|
||||||
var err error
|
emailList, _ := list.GetEmailList(session.Ctx.(*context.Context), dto.SearchTag{Type: consts.EmailTypeReceive, Status: -1, GroupId: -1}, "", true, 0, 99999)
|
||||||
var ssql string
|
for _, info := range emailList {
|
||||||
|
res = append(res, listItem{
|
||||||
err = db.Instance.Where("type=0 and status=0").Select("id").Table("email").Find(&res)
|
Id: cast.ToInt64(info.Id),
|
||||||
|
Size: cast.ToInt64(info.Size),
|
||||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
})
|
||||||
log.WithContext(session.Ctx.(*context.Context)).Errorf("SQL:%s Error: %+v", ssql, err)
|
|
||||||
err = nil
|
|
||||||
return []gopop.UidlItem{}, nil
|
|
||||||
}
|
}
|
||||||
ret := []gopop.UidlItem{}
|
ret := []gopop.UidlItem{}
|
||||||
for _, re := range res {
|
for _, re := range res {
|
||||||
|
@ -20,14 +20,14 @@ func HasAuth(ctx *context.Context, email *models.Email) bool {
|
|||||||
if ctx.IsAdmin {
|
if ctx.IsAdmin {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
var ue *models.UserEmail
|
var ue []models.UserEmail
|
||||||
err := db.Instance.Where("email_id = ?", email.Id).Find(&ue)
|
err := db.Instance.Table(&models.UserEmail{}).Where("email_id = ? and user_id = ?", email.Id, ctx.UserID).Find(&ue)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error while checking user: %v", err)
|
log.Errorf("Error while checking user: %v", err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return ue != nil
|
return len(ue) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func DkimGen() string {
|
func DkimGen() string {
|
||||||
|
@ -46,7 +46,7 @@ func (s *Session) Data(r io.Reader) error {
|
|||||||
}
|
}
|
||||||
log.WithContext(ctx).Debugf("开始执行插件ReceiveParseBefore End!")
|
log.WithContext(ctx).Debugf("开始执行插件ReceiveParseBefore End!")
|
||||||
|
|
||||||
email := parsemail.NewEmailFromReader(s.To, bytes.NewReader(emailData))
|
email := parsemail.NewEmailFromReader(s.To, bytes.NewReader(emailData), len(emailData))
|
||||||
|
|
||||||
if s.From != "" {
|
if s.From != "" {
|
||||||
from := parsemail.BuilderUser(s.From)
|
from := parsemail.BuilderUser(s.From)
|
||||||
@ -209,24 +209,26 @@ func saveEmail(ctx *context.Context, size int, email *parsemail.Email, sendUserI
|
|||||||
}
|
}
|
||||||
|
|
||||||
modelEmail := models.Email{
|
modelEmail := models.Email{
|
||||||
Type: cast.ToInt8(emailType),
|
Type: cast.ToInt8(emailType),
|
||||||
Subject: email.Subject,
|
Subject: email.Subject,
|
||||||
ReplyTo: json2string(email.ReplyTo),
|
ReplyTo: json2string(email.ReplyTo),
|
||||||
FromName: email.From.Name,
|
FromName: email.From.Name,
|
||||||
FromAddress: email.From.EmailAddress,
|
FromAddress: email.From.EmailAddress,
|
||||||
To: json2string(email.To),
|
To: json2string(email.To),
|
||||||
Bcc: json2string(email.Bcc),
|
Bcc: json2string(email.Bcc),
|
||||||
Cc: json2string(email.Cc),
|
Cc: json2string(email.Cc),
|
||||||
Text: sql.NullString{String: string(email.Text), Valid: true},
|
Text: sql.NullString{String: string(email.Text), Valid: true},
|
||||||
Html: sql.NullString{String: string(email.HTML), Valid: true},
|
Html: sql.NullString{String: string(email.HTML), Valid: true},
|
||||||
Sender: json2string(email.Sender),
|
Sender: json2string(email.Sender),
|
||||||
Attachments: json2string(email.Attachments),
|
Attachments: json2string(email.Attachments),
|
||||||
SPFCheck: spfV,
|
Size: email.Size,
|
||||||
DKIMCheck: dkimV,
|
SPFCheck: spfV,
|
||||||
SendUserID: sendUserID,
|
DKIMCheck: dkimV,
|
||||||
SendDate: time.Now(),
|
SendUserID: sendUserID,
|
||||||
Status: cast.ToInt8(email.Status),
|
SendDate: time.Now(),
|
||||||
CreateTime: time.Now(),
|
Status: cast.ToInt8(email.Status),
|
||||||
|
CreateTime: time.Now(),
|
||||||
|
CronSendTime: time.Now(),
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := db.Instance.Insert(&modelEmail)
|
_, err := db.Instance.Insert(&modelEmail)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user