æ·feat: commplete init process
This commit is contained in:
+12
-1
@@ -15,7 +15,18 @@
|
||||
*.out
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
vendor/
|
||||
|
||||
# Go workspace file
|
||||
go.work
|
||||
|
||||
|
||||
# web sources
|
||||
/web/node_modules
|
||||
/web/.vite
|
||||
/web/.vscode
|
||||
|
||||
# Project ignore
|
||||
/test
|
||||
/conf
|
||||
/runtime
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
FROM golang:latest
|
||||
|
||||
ENV GOPROXY https://goproxy.cn,direct
|
||||
WORKDIR $GOPATH/src/github.com/EDDYCJY/go-gin-example
|
||||
COPY . $GOPATH/src/github.com/EDDYCJY/go-gin-example
|
||||
RUN go build .
|
||||
|
||||
EXPOSE 8000
|
||||
ENTRYPOINT ["./go-gin-example"]
|
||||
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) The go-gin-example Authors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -0,0 +1,23 @@
|
||||
.PHONY: build clean tool lint help
|
||||
|
||||
all: build
|
||||
|
||||
build:
|
||||
@go build -v .
|
||||
|
||||
tool:
|
||||
go vet ./...; true
|
||||
gofmt -w .
|
||||
|
||||
lint:
|
||||
golint ./...
|
||||
|
||||
clean:
|
||||
rm -rf go-gin-example
|
||||
go clean -i .
|
||||
|
||||
help:
|
||||
@echo "make: compile packages and dependencies"
|
||||
@echo "make tool: run specified go tool"
|
||||
@echo "make lint: golint ./..."
|
||||
@echo "make clean: remove object files and cached files"
|
||||
@@ -1 +1,89 @@
|
||||
# notice-push
|
||||
# Go Gin Example [](https://goreportcard.com/report/github.com/EDDYCJY/go-gin-example) [](https://godoc.org/github.com/EDDYCJY/go-gin-example) [](https://raw.githubusercontent.com/EDDYCJY/go-gin-example/master/LICENSE)
|
||||
|
||||
An example of gin contains many useful features
|
||||
|
||||
[简体中文](https://message-nest/blob/master/README_ZH.md)
|
||||
|
||||
## Installation
|
||||
```
|
||||
$ go get github.com/EDDYCJY/go-gin-example
|
||||
```
|
||||
|
||||
## How to run
|
||||
|
||||
### Required
|
||||
|
||||
- Mysql
|
||||
- Redis
|
||||
|
||||
### Ready
|
||||
|
||||
Create a **blog database** and import [SQL](https://message-nest/blob/master/docs/sql/blog.sql)
|
||||
|
||||
### Conf
|
||||
|
||||
You should modify `conf/app.ini`
|
||||
|
||||
```
|
||||
[database]
|
||||
Type = mysql
|
||||
User = root
|
||||
Password =
|
||||
Host = 127.0.0.1:3306
|
||||
Name = blog
|
||||
TablePrefix = blog_
|
||||
|
||||
[redis]
|
||||
Host = 127.0.0.1:6379
|
||||
Password =
|
||||
MaxIdle = 30
|
||||
MaxActive = 30
|
||||
IdleTimeout = 200
|
||||
...
|
||||
```
|
||||
|
||||
### Run
|
||||
```
|
||||
$ cd $GOPATH/src/go-gin-example
|
||||
|
||||
$ go run main.go
|
||||
```
|
||||
|
||||
Project information and existing API
|
||||
|
||||
```
|
||||
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
|
||||
- using env: export GIN_MODE=release
|
||||
- using code: gin.SetMode(gin.ReleaseMode)
|
||||
|
||||
[GIN-debug] GET /auth --> message-nest/routers/api.GetAuth (3 handlers)
|
||||
[GIN-debug] GET /swagger/*any --> message-nest/vendor/github.com/swaggo/gin-swagger.WrapHandler.func1 (3 handlers)
|
||||
[GIN-debug] GET /api/v1/tags --> message-nest/routers/api/v1.GetTags (4 handlers)
|
||||
[GIN-debug] POST /api/v1/tags --> message-nest/routers/api/v1.AddTag (4 handlers)
|
||||
[GIN-debug] PUT /api/v1/tags/:id --> message-nest/routers/api/v1.EditTag (4 handlers)
|
||||
[GIN-debug] DELETE /api/v1/tags/:id --> message-nest/routers/api/v1.DeleteTag (4 handlers)
|
||||
[GIN-debug] GET /api/v1/articles --> message-nest/routers/api/v1.GetArticles (4 handlers)
|
||||
[GIN-debug] GET /api/v1/articles/:id --> message-nest/routers/api/v1.GetArticle (4 handlers)
|
||||
[GIN-debug] POST /api/v1/articles --> message-nest/routers/api/v1.AddArticle (4 handlers)
|
||||
[GIN-debug] PUT /api/v1/articles/:id --> message-nest/routers/api/v1.EditArticle (4 handlers)
|
||||
[GIN-debug] DELETE /api/v1/articles/:id --> message-nest/routers/api/v1.DeleteArticle (4 handlers)
|
||||
|
||||
Listening port is 8000
|
||||
Actual pid is 4393
|
||||
```
|
||||
Swagger doc
|
||||
|
||||

|
||||
|
||||
## Features
|
||||
|
||||
- RESTful API
|
||||
- Gorm
|
||||
- Swagger
|
||||
- logging
|
||||
- Jwt-go
|
||||
- Gin
|
||||
- Graceful restart or stop (fvbock/endless)
|
||||
- App configurable
|
||||
- Cron
|
||||
- Redis
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
# Go Gin Example [](https://goreportcard.com/report/github.com/EDDYCJY/go-gin-example) [](https://godoc.org/github.com/EDDYCJY/go-gin-example) [](https://raw.githubusercontent.com/EDDYCJY/go-gin-example/master/LICENSE)
|
||||
|
||||
`gin` 的一个例子,包含许多有用特性
|
||||
|
||||
## 目录
|
||||
|
||||
本项目提供 [Gin实践](https://segmentfault.com/a/1190000013297625) 的连载示例代码
|
||||
|
||||
1. [Gin实践 连载一 Golang介绍与环境安装](https://book.eddycjy.com/golang/gin/install.html)
|
||||
2. [Gin实践 连载二 搭建Blog API's(一)](https://book.eddycjy.com/golang/gin/api-01.html)
|
||||
3. [Gin实践 连载三 搭建Blog API's(二)](https://book.eddycjy.com/golang/gin/api-02.html)
|
||||
4. [Gin实践 连载四 搭建Blog API's(三)](https://book.eddycjy.com/golang/gin/api-03.html)
|
||||
5. [Gin实践 连载五 使用JWT进行身份校验](https://book.eddycjy.com/golang/gin/jwt.html)
|
||||
6. [Gin实践 连载六 编写一个简单的文件日志](https://book.eddycjy.com/golang/gin/log.html)
|
||||
7. [Gin实践 连载七 Golang优雅重启HTTP服务](https://book.eddycjy.com/golang/gin/reload-http.html)
|
||||
8. [Gin实践 连载八 为它加上Swagger](https://book.eddycjy.com/golang/gin/swagger.html)
|
||||
9. [Gin实践 连载九 将Golang应用部署到Docker](https://book.eddycjy.com/golang/gin/golang-docker.html)
|
||||
10. [Gin实践 连载十 定制 GORM Callbacks](https://book.eddycjy.com/golang/gin/gorm-callback.html)
|
||||
11. [Gin实践 连载十一 Cron定时任务](https://book.eddycjy.com/golang/gin/cron.html)
|
||||
12. [Gin实践 连载十二 优化配置结构及实现图片上传](https://book.eddycjy.com/golang/gin/config-upload.html)
|
||||
13. [Gin实践 连载十三 优化你的应用结构和实现Redis缓存](https://book.eddycjy.com/golang/gin/application-redis.html)
|
||||
14. [Gin实践 连载十四 实现导出、导入 Excel](https://book.eddycjy.com/golang/gin/excel.html)
|
||||
15. [Gin实践 连载十五 生成二维码、合并海报](https://book.eddycjy.com/golang/gin/image.html)
|
||||
16. [Gin实践 连载十六 在图片上绘制文字](https://book.eddycjy.com/golang/gin/font.html)
|
||||
17. [Gin实践 连载十七 用 Nginx 部署 Go 应用](https://book.eddycjy.com/golang/gin/nginx.html)
|
||||
18. [Gin实践 番外 Golang交叉编译](https://book.eddycjy.com/golang/gin/cgo.html)
|
||||
19. [Gin实践 番外 请入门 Makefile](https://book.eddycjy.com/golang/gin/makefile.html)
|
||||
|
||||
## 安装
|
||||
```
|
||||
$ go get github.com/EDDYCJY/go-gin-example
|
||||
```
|
||||
|
||||
## 如何运行
|
||||
|
||||
### 必须
|
||||
|
||||
- Mysql
|
||||
- Redis
|
||||
|
||||
### 准备
|
||||
|
||||
创建一个 `blog` 数据库,并且导入建表的 [SQL](https://message-nest/blob/master/docs/sql/blog.sql)
|
||||
|
||||
### 配置
|
||||
|
||||
你应该修改 `conf/app.ini` 配置文件
|
||||
|
||||
```
|
||||
[database]
|
||||
Type = mysql
|
||||
User = root
|
||||
Password = rootroot
|
||||
Host = 127.0.0.1:3306
|
||||
Name = blog
|
||||
TablePrefix = blog_
|
||||
|
||||
[redis]
|
||||
Host = 127.0.0.1:6379
|
||||
Password =
|
||||
MaxIdle = 30
|
||||
MaxActive = 30
|
||||
IdleTimeout = 200
|
||||
...
|
||||
```
|
||||
|
||||
|
||||
### 运行
|
||||
```
|
||||
$ cd $GOPATH/src/go-gin-example
|
||||
|
||||
$ go run main.go
|
||||
```
|
||||
|
||||
项目的运行信息和已存在的 API's
|
||||
|
||||
```
|
||||
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
|
||||
- using env: export GIN_MODE=release
|
||||
- using code: gin.SetMode(gin.ReleaseMode)
|
||||
|
||||
[GIN-debug] GET /auth --> message-nest/routers/api.GetAuth (3 handlers)
|
||||
[GIN-debug] GET /swagger/*any --> message-nest/vendor/github.com/swaggo/gin-swagger.WrapHandler.func1 (3 handlers)
|
||||
[GIN-debug] GET /api/v1/tags --> message-nest/routers/api/v1.GetTags (4 handlers)
|
||||
[GIN-debug] POST /api/v1/tags --> message-nest/routers/api/v1.AddTag (4 handlers)
|
||||
[GIN-debug] PUT /api/v1/tags/:id --> message-nest/routers/api/v1.EditTag (4 handlers)
|
||||
[GIN-debug] DELETE /api/v1/tags/:id --> message-nest/routers/api/v1.DeleteTag (4 handlers)
|
||||
[GIN-debug] GET /api/v1/articles --> message-nest/routers/api/v1.GetArticles (4 handlers)
|
||||
[GIN-debug] GET /api/v1/articles/:id --> message-nest/routers/api/v1.GetArticle (4 handlers)
|
||||
[GIN-debug] POST /api/v1/articles --> message-nest/routers/api/v1.AddArticle (4 handlers)
|
||||
[GIN-debug] PUT /api/v1/articles/:id --> message-nest/routers/api/v1.EditArticle (4 handlers)
|
||||
[GIN-debug] DELETE /api/v1/articles/:id --> message-nest/routers/api/v1.DeleteArticle (4 handlers)
|
||||
|
||||
Listening port is 8000
|
||||
Actual pid is 4393
|
||||
```
|
||||
Swagger 文档
|
||||
|
||||

|
||||
|
||||
## 特性
|
||||
|
||||
- RESTful API
|
||||
- Gorm
|
||||
- Swagger
|
||||
- logging
|
||||
- Jwt-go
|
||||
- Gin
|
||||
- Graceful restart or stop (fvbock/endless)
|
||||
- App configurable
|
||||
- Cron
|
||||
- Redis
|
||||
|
||||
## 联系我
|
||||
|
||||

|
||||
@@ -0,0 +1,56 @@
|
||||
module message-nest
|
||||
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/astaxie/beego v1.9.3-0.20171218111859-f16688817aa4
|
||||
github.com/dgrijalva/jwt-go v3.1.0+incompatible
|
||||
github.com/gin-gonic/gin v1.9.1
|
||||
github.com/go-ini/ini v1.32.1-0.20180214101753-32e4be5f41bb
|
||||
github.com/go-playground/locales v0.14.1
|
||||
github.com/go-playground/universal-translator v0.18.1
|
||||
github.com/go-playground/validator/v10 v10.16.0
|
||||
github.com/google/uuid v1.5.0
|
||||
github.com/jinzhu/gorm v0.0.0-20180213101209-6e1387b44c64
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/unknwon/com v1.0.1
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/bytedance/sonic v1.10.2 // indirect
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
|
||||
github.com/chenzhuoyu/iasm v0.9.1 // indirect
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20190920000552-128d9f4ae1cd // indirect
|
||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-sql-driver/mysql v1.4.1-0.20190510102335-877a9775f068 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/jinzhu/inflection v0.0.0-20170102125226-1c35d901db3d // indirect
|
||||
github.com/jinzhu/now v1.0.1 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
|
||||
github.com/kr/pretty v0.1.0 // indirect
|
||||
github.com/leodido/go-urn v1.2.4 // indirect
|
||||
github.com/lib/pq v1.2.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.11.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
|
||||
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
golang.org/x/arch v0.6.0 // indirect
|
||||
golang.org/x/crypto v0.17.0 // indirect
|
||||
golang.org/x/net v0.19.0 // indirect
|
||||
golang.org/x/sys v0.15.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
google.golang.org/appengine v1.6.3 // indirect
|
||||
google.golang.org/protobuf v1.32.0 // indirect
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
|
||||
gopkg.in/ini.v1 v1.47.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
@@ -0,0 +1,161 @@
|
||||
github.com/astaxie/beego v1.9.3-0.20171218111859-f16688817aa4 h1:dNIynF6ICiq1NghlpIBxljb2JbyC61/JqWB5A9cfUfo=
|
||||
github.com/astaxie/beego v1.9.3-0.20171218111859-f16688817aa4/go.mod h1:0R4++1tUqERR0WYFWdfkcrsyoVBCG4DgpDGokT3yb+U=
|
||||
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
|
||||
github.com/bytedance/sonic v1.10.2 h1:GQebETVBxYB7JGWJtLBi07OVzWwt+8dWA00gEVW2ZFE=
|
||||
github.com/bytedance/sonic v1.10.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA=
|
||||
github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
|
||||
github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0=
|
||||
github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20190920000552-128d9f4ae1cd h1:tXCgEGHPT4XELDS7nB0OBb9968yCOd+MnyNf+6m1u40=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20190920000552-128d9f4ae1cd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||
github.com/dgrijalva/jwt-go v3.1.0+incompatible h1:FFziAwDQQ2dz1XClWMkwvukur3evtZx7x/wMHKM1i20=
|
||||
github.com/dgrijalva/jwt-go v3.1.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
|
||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
|
||||
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
|
||||
github.com/go-ini/ini v1.32.1-0.20180214101753-32e4be5f41bb h1:v+YnQ81wH+hTjaP5nFSpqASFbe9UETYm1vG65qp7Zc0=
|
||||
github.com/go-ini/ini v1.32.1-0.20180214101753-32e4be5f41bb/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.16.0 h1:x+plE831WK4vaKHO/jpgUGsvLKIqRRkz6M78GuJAfGE=
|
||||
github.com/go-playground/validator/v10 v10.16.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||
github.com/go-sql-driver/mysql v1.4.1-0.20190510102335-877a9775f068 h1:q2kwd9Bcgl2QpSi/Wjcx9jzwyICt3EWTP5to43QhwaA=
|
||||
github.com/go-sql-driver/mysql v1.4.1-0.20190510102335-877a9775f068/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
|
||||
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/jinzhu/gorm v0.0.0-20180213101209-6e1387b44c64 h1:8I4kQ5M5OjZKNsgRUs20soTdIoo1GbiGApV31kJ9e6Y=
|
||||
github.com/jinzhu/gorm v0.0.0-20180213101209-6e1387b44c64/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo=
|
||||
github.com/jinzhu/inflection v0.0.0-20170102125226-1c35d901db3d h1:jRQLvyVGL+iVtDElaEIDdKwpPqUIZJfzkNLV34htpEc=
|
||||
github.com/jinzhu/inflection v0.0.0-20170102125226-1c35d901db3d/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M=
|
||||
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
|
||||
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
|
||||
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
||||
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
|
||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-sqlite3 v1.11.0 h1:LDdKkqtYlom37fkvqs8rMPFKAMe8+SgjbwZ6ex1/A/Q=
|
||||
github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
|
||||
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304 h1:Jpy1PXuP99tXNrhbq2BaPz9B+jNAvH1JPQQpG/9GCXY=
|
||||
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 h1:WN9BUFbdyOsSH/XohnWpXOlq9NBD5sGAB2FciQMUEe8=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/unknwon/com v1.0.1 h1:3d1LTxD+Lnf3soQiD4Cp/0BRB+Rsa/+RTvz8GMMzIXs=
|
||||
github.com/unknwon/com v1.0.1/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/arch v0.6.0 h1:S0JTfE48HbRj80+4tbvZDYsJ3tGv6BUU3XxyZ7CirAc=
|
||||
golang.org/x/arch v0.6.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
|
||||
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
|
||||
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
google.golang.org/appengine v1.6.3 h1:hvZejVcIxAKHR8Pq2gXaDggf6CWT1QEqO+JEBeOKCG8=
|
||||
google.golang.org/appengine v1.6.3/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
|
||||
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
|
||||
gopkg.in/ini.v1 v1.47.0 h1:XAVsOWcIxjm6JVEbCbSZgSBZIF0BrCzXs4orAQr6uc8=
|
||||
gopkg.in/ini.v1 v1.47.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
@@ -0,0 +1,55 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"message-nest/pkg/table"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"message-nest/models"
|
||||
"message-nest/pkg/logging"
|
||||
"message-nest/pkg/setting"
|
||||
"message-nest/pkg/util"
|
||||
"message-nest/routers"
|
||||
)
|
||||
|
||||
func init() {
|
||||
setting.Setup()
|
||||
models.Setup()
|
||||
logging.Setup()
|
||||
util.Setup()
|
||||
table.Setup()
|
||||
}
|
||||
|
||||
// @title Golang Gin API
|
||||
// @version 1.0
|
||||
// @description An example of gin
|
||||
// @termsOfService https://github.com/EDDYCJY/go-gin-example
|
||||
// @license.name MIT
|
||||
// @license.url https://message-nest/blob/master/LICENSE
|
||||
func main() {
|
||||
gin.SetMode(setting.ServerSetting.RunMode)
|
||||
|
||||
routersInit := routers.InitRouter()
|
||||
readTimeout := setting.ServerSetting.ReadTimeout
|
||||
writeTimeout := setting.ServerSetting.WriteTimeout
|
||||
endPoint := fmt.Sprintf(":%d", setting.ServerSetting.HttpPort)
|
||||
maxHeaderBytes := 1 << 20
|
||||
|
||||
server := &http.Server{
|
||||
Addr: endPoint,
|
||||
Handler: routersInit,
|
||||
ReadTimeout: readTimeout,
|
||||
WriteTimeout: writeTimeout,
|
||||
MaxHeaderBytes: maxHeaderBytes,
|
||||
}
|
||||
|
||||
log.Printf("[info] start http server listening http://0.0.0.0%s", endPoint)
|
||||
|
||||
err := server.ListenAndServe()
|
||||
if err != nil {
|
||||
log.Printf("Server err: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Cors 直接放行所有跨域请求并放行所有 OPTIONS 方法
|
||||
func Cors() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
method := c.Request.Method
|
||||
origin := c.Request.Header.Get("Origin")
|
||||
c.Header("Access-Control-Allow-Origin", origin)
|
||||
c.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token,X-Token,X-User-Id,M-Token")
|
||||
c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS,DELETE,PUT")
|
||||
c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type, New-Token, New-Expires-At")
|
||||
c.Header("Access-Control-Allow-Credentials", "true")
|
||||
|
||||
// 放行所有OPTIONS方法
|
||||
if method == "OPTIONS" {
|
||||
c.AbortWithStatus(http.StatusNoContent)
|
||||
}
|
||||
// 处理请求
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"message-nest/pkg/e"
|
||||
"message-nest/pkg/util"
|
||||
)
|
||||
|
||||
var ExcludedRoutes = []string{
|
||||
"/api/v1/message/send",
|
||||
}
|
||||
|
||||
// JWT is jwt middleware
|
||||
func JWT() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
path := c.FullPath()
|
||||
for _, route := range ExcludedRoutes {
|
||||
if path == route {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var code int
|
||||
var data interface{}
|
||||
code = e.SUCCESS
|
||||
token := c.Request.Header.Get("M-Token")
|
||||
|
||||
if token == "" {
|
||||
code = e.ERROR_AUTH_NO_TOKEN
|
||||
} else {
|
||||
claims, err := util.ParseToken(token)
|
||||
if err != nil {
|
||||
switch err.(*jwt.ValidationError).Errors {
|
||||
case jwt.ValidationErrorExpired:
|
||||
code = e.ERROR_AUTH_CHECK_TOKEN_TIMEOUT
|
||||
default:
|
||||
code = e.ERROR_AUTH_CHECK_TOKEN_FAIL
|
||||
}
|
||||
} else {
|
||||
c.Set("currentUserName", claims.Username)
|
||||
}
|
||||
}
|
||||
|
||||
if code != e.SUCCESS {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{
|
||||
"code": code,
|
||||
"msg": e.GetMsg(code),
|
||||
"data": data,
|
||||
})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package models
|
||||
|
||||
import "github.com/jinzhu/gorm"
|
||||
|
||||
type Auth struct {
|
||||
ID int `gorm:"primary_key" json:"id"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
// CheckAuth checks if authentication information exists
|
||||
func CheckAuth(username, password string) (bool, error) {
|
||||
var auth Auth
|
||||
err := db.Select("id").Where(Auth{Username: username, Password: password}).First(&auth).Error
|
||||
if err != nil && err != gorm.ErrRecordNotFound {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if auth.ID > 0 {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// EditUser 编辑用户信息
|
||||
func EditUser(username string, data interface{}) error {
|
||||
if err := db.Model(&Auth{}).Where("username = ? ", username).Updates(data).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/google/uuid"
|
||||
"github.com/jinzhu/gorm"
|
||||
_ "github.com/jinzhu/gorm/dialects/mysql"
|
||||
"log"
|
||||
"message-nest/pkg/setting"
|
||||
"message-nest/pkg/util"
|
||||
"time"
|
||||
)
|
||||
|
||||
var db *gorm.DB
|
||||
|
||||
//type Model struct {
|
||||
// ID int `gorm:"primary_key" json:"id"`
|
||||
// CreatedOn util.Time `json:"created_on"`
|
||||
// ModifiedOn util.Time `json:"modified_on"`
|
||||
//}
|
||||
|
||||
type UUIDModel struct {
|
||||
ID uuid.UUID `gorm:"primary_key" json:"id"`
|
||||
|
||||
CreatedBy string `json:"created_by"`
|
||||
ModifiedBy string `json:"modified_by"`
|
||||
CreatedOn util.Time `json:"created_on"`
|
||||
ModifiedOn util.Time `json:"modified_on"`
|
||||
}
|
||||
|
||||
// Setup initializes the database instance
|
||||
func Setup() {
|
||||
var err error
|
||||
db, err = gorm.Open(setting.DatabaseSetting.Type, fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
|
||||
setting.DatabaseSetting.User,
|
||||
setting.DatabaseSetting.Password,
|
||||
setting.DatabaseSetting.Host,
|
||||
setting.DatabaseSetting.Name))
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("models.Setup err: %v", err)
|
||||
}
|
||||
gorm.DefaultTableNameHandler = func(db *gorm.DB, defaultTableName string) string {
|
||||
return setting.DatabaseSetting.TablePrefix + defaultTableName
|
||||
}
|
||||
|
||||
db.LogMode(true)
|
||||
db.SingularTable(true)
|
||||
db.Callback().Create().Replace("gorm:update_time_stamp", updateTimeStampForCreateCallback)
|
||||
db.Callback().Update().Replace("gorm:update_time_stamp", updateTimeStampForUpdateCallback)
|
||||
db.Callback().Delete().Replace("gorm:delete", deleteCallback)
|
||||
db.DB().SetMaxIdleConns(10)
|
||||
db.DB().SetMaxOpenConns(100)
|
||||
}
|
||||
|
||||
// CloseDB closes database connection (unnecessary)
|
||||
func CloseDB() {
|
||||
defer db.Close()
|
||||
}
|
||||
|
||||
// updateTimeStampForCreateCallback will set `CreatedOn`, `ModifiedOn` when creating
|
||||
func updateTimeStampForCreateCallback(scope *gorm.Scope) {
|
||||
if !scope.HasError() {
|
||||
nowTime := time.Now()
|
||||
fmt.Printf("fyyyyyynowTime %s", nowTime)
|
||||
|
||||
if createTimeField, ok := scope.FieldByName("CreatedOn"); ok {
|
||||
if createTimeField.IsBlank {
|
||||
createTimeField.Set(nowTime)
|
||||
}
|
||||
}
|
||||
|
||||
if modifyTimeField, ok := scope.FieldByName("ModifiedOn"); ok {
|
||||
if modifyTimeField.IsBlank {
|
||||
modifyTimeField.Set(nowTime)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// updateTimeStampForUpdateCallback will set `ModifiedOn` when updating
|
||||
func updateTimeStampForUpdateCallback(scope *gorm.Scope) {
|
||||
if _, ok := scope.Get("gorm:update_column"); !ok {
|
||||
scope.SetColumn("ModifiedOn", time.Now())
|
||||
}
|
||||
}
|
||||
|
||||
// deleteCallback will set `DeletedOn` where deleting
|
||||
func deleteCallback(scope *gorm.Scope) {
|
||||
if !scope.HasError() {
|
||||
var extraOption string
|
||||
if str, ok := scope.Get("gorm:delete_option"); ok {
|
||||
extraOption = fmt.Sprint(str)
|
||||
}
|
||||
|
||||
deletedOnField, hasDeletedOnField := scope.FieldByName("DeletedOn")
|
||||
|
||||
if !scope.Search.Unscoped && hasDeletedOnField {
|
||||
scope.Raw(fmt.Sprintf(
|
||||
"UPDATE %v SET %v=%v%v%v",
|
||||
scope.QuotedTableName(),
|
||||
scope.Quote(deletedOnField.DBName),
|
||||
scope.AddToVars(time.Now().Unix()),
|
||||
addExtraSpaceIfExist(scope.CombinedConditionSql()),
|
||||
addExtraSpaceIfExist(extraOption),
|
||||
)).Exec()
|
||||
} else {
|
||||
scope.Raw(fmt.Sprintf(
|
||||
"DELETE FROM %v%v%v",
|
||||
scope.QuotedTableName(),
|
||||
addExtraSpaceIfExist(scope.CombinedConditionSql()),
|
||||
addExtraSpaceIfExist(extraOption),
|
||||
)).Exec()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// addExtraSpaceIfExist adds a separator
|
||||
func addExtraSpaceIfExist(str string) string {
|
||||
if str != "" {
|
||||
return " " + str
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package models
|
||||
|
||||
type SendTasksIns struct {
|
||||
UUIDModel
|
||||
|
||||
TaskID string `json:"task_id"`
|
||||
WayID string `json:"way_id"`
|
||||
WayType string `json:"way_type"`
|
||||
ContentType string `json:"content_type"`
|
||||
Config string `json:"config"`
|
||||
Extra string `json:"extra"`
|
||||
}
|
||||
|
||||
// InsEmailConfig 实例里面的邮箱config
|
||||
type InsEmailConfig struct {
|
||||
ToAccount string `json:"to_account" validate:"required,email" label:"收件邮箱"`
|
||||
Title string `json:"title" validate:"required,max=150" label:"邮箱标题"`
|
||||
}
|
||||
|
||||
// ManyAddTaskIns 批量添加实例
|
||||
func ManyAddTaskIns(taskIns []SendTasksIns) error {
|
||||
tx := db.Begin()
|
||||
for _, ins := range taskIns {
|
||||
// 存在就跳过这条ins记录
|
||||
err := db.Where("id = ?", ins.ID).Find(&SendTasksIns{}).Error
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
if err := tx.Create(&ins).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := tx.Commit().Error; err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddTaskInsOne 添加一条实例
|
||||
func AddTaskInsOne(ins SendTasksIns) error {
|
||||
if err := db.Create(&ins).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteMsgTaskIns 删除一条实例
|
||||
func DeleteMsgTaskIns(id string) error {
|
||||
if err := db.Where("id = ?", id).Delete(&SendTasksIns{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/google/uuid"
|
||||
"github.com/jinzhu/gorm"
|
||||
"message-nest/pkg/table"
|
||||
)
|
||||
|
||||
type SendTasks struct {
|
||||
UUIDModel
|
||||
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// AddSendTaskWithID 添加实例的时候添加任务
|
||||
func AddSendTaskWithID(name string, id string, createdBy string) error {
|
||||
err := db.Where("id = ?", id).Find(&SendTasks{}).Error
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
uuidObj, _ := uuid.Parse(id)
|
||||
task := SendTasks{
|
||||
UUIDModel: UUIDModel{
|
||||
ID: uuidObj,
|
||||
CreatedBy: createdBy,
|
||||
ModifiedBy: createdBy,
|
||||
},
|
||||
Name: name,
|
||||
}
|
||||
if err := db.Create(&task).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddSendTask 添加任务
|
||||
func AddSendTask(name string, createdBy string) error {
|
||||
newUUID := uuid.New()
|
||||
task := SendTasks{
|
||||
UUIDModel: UUIDModel{
|
||||
ID: newUUID,
|
||||
CreatedBy: createdBy,
|
||||
ModifiedBy: createdBy,
|
||||
},
|
||||
Name: name,
|
||||
}
|
||||
if err := db.Create(&task).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetSendTasks 获取所有任务
|
||||
func GetSendTasks(pageNum int, pageSize int, name string, maps interface{}) ([]SendTasks, error) {
|
||||
var (
|
||||
tasks []SendTasks
|
||||
err error
|
||||
)
|
||||
query := db.Where(maps)
|
||||
if name != "" {
|
||||
query = query.Where("name like ?", fmt.Sprintf("%%%s%%", name))
|
||||
}
|
||||
query = query.Order("created_on DESC")
|
||||
if pageSize > 0 || pageNum > 0 {
|
||||
query = query.Offset(pageNum).Limit(pageSize)
|
||||
}
|
||||
err = query.Find(&tasks).Error
|
||||
if err != nil && err != gorm.ErrRecordNotFound {
|
||||
return nil, err
|
||||
}
|
||||
return tasks, nil
|
||||
}
|
||||
|
||||
// GetSendTasksTotal 获取所有任务总数
|
||||
func GetSendTasksTotal(name string, maps interface{}) (int, error) {
|
||||
var (
|
||||
err error
|
||||
total int
|
||||
)
|
||||
query := db.Model(&SendTasks{}).Where(maps)
|
||||
if name != "" {
|
||||
query = query.Where("name like ?", fmt.Sprintf("%%%s%%", name))
|
||||
}
|
||||
|
||||
err = query.Count(&total).Error
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return total, nil
|
||||
}
|
||||
|
||||
type SendTasksInsRes struct {
|
||||
SendTasksIns
|
||||
|
||||
WayName string `json:"way_name"`
|
||||
}
|
||||
|
||||
type TaskIns struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
InsData []SendTasksInsRes `json:"ins_data"`
|
||||
}
|
||||
|
||||
// GetSendTasksTotal 获取所有任务下所有的实例
|
||||
func GetTasksIns(id string) (TaskIns, error) {
|
||||
insTable := table.InsTableName
|
||||
waysTable := table.WayTableName
|
||||
var (
|
||||
task SendTasks
|
||||
taskIns []SendTasksInsRes
|
||||
taskResult TaskIns
|
||||
)
|
||||
err := db.Where("id = ?", id).First(&task).Error
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return taskResult, err
|
||||
}
|
||||
|
||||
db.
|
||||
Table(insTable).
|
||||
Select(fmt.Sprintf("%s.*, %s.name as way_name", insTable, waysTable)).
|
||||
Joins(fmt.Sprintf("JOIN %s ON %s.way_id = %s.id", waysTable, insTable, waysTable)).
|
||||
Where(fmt.Sprintf("%s.task_id = ?", insTable), id).
|
||||
Scan(&taskIns)
|
||||
|
||||
taskResult.ID = task.ID
|
||||
taskResult.Name = task.Name
|
||||
taskResult.InsData = taskIns
|
||||
return taskResult, nil
|
||||
}
|
||||
|
||||
// FindTaskByWayId 通过way_id找到关联的任务
|
||||
func FindTaskByWayId(wayId string) []SendTasks {
|
||||
insTable := table.InsTableName
|
||||
taskTable := table.TasksTableName
|
||||
var (
|
||||
tasks []SendTasks
|
||||
)
|
||||
|
||||
db.
|
||||
Table(taskTable).
|
||||
Select(fmt.Sprintf("%s.*", taskTable)).
|
||||
Joins(fmt.Sprintf("JOIN %s ON %s.task_id = %s.id", insTable, insTable, taskTable)).
|
||||
Where(fmt.Sprintf("%s.way_id = ?", insTable), wayId).
|
||||
Scan(&tasks)
|
||||
|
||||
return tasks
|
||||
}
|
||||
|
||||
// 删除任务并删除所有关联的实例
|
||||
func DeleteMsgTask(id string) error {
|
||||
tx := db.Begin()
|
||||
if err := db.Where("id = ?", id).Delete(&SendTasks{}).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
if err := db.Where("task_id = ?", id).Delete(&SendTasksIns{}).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
tx.Commit()
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"message-nest/pkg/table"
|
||||
"message-nest/pkg/util"
|
||||
)
|
||||
|
||||
type SendTasksLogs struct {
|
||||
ID int `gorm:"primary_key" json:"id"`
|
||||
TaskID string `json:"task_id"`
|
||||
Log string `json:"log"`
|
||||
Status int `json:"status"`
|
||||
|
||||
CreatedOn util.Time `json:"created_on"`
|
||||
ModifiedOn util.Time `json:"modified_on"`
|
||||
}
|
||||
|
||||
// Add 添加日志记录
|
||||
func (log *SendTasksLogs) Add() error {
|
||||
if err := db.Create(&log).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 日志列表的结果
|
||||
type LogsResult struct {
|
||||
ID int `json:"id"`
|
||||
TaskID string `json:"task_id"`
|
||||
Log string `json:"log"`
|
||||
CreatedOn util.Time `json:"created_on"`
|
||||
ModifiedOn util.Time `json:"modified_on"`
|
||||
TaskName string `json:"task_name"`
|
||||
Status int `json:"status"`
|
||||
}
|
||||
|
||||
// GetSendLogs 获取所有日志记录
|
||||
func GetSendLogs(pageNum int, pageSize int, name string, taskId string, maps interface{}) ([]LogsResult, error) {
|
||||
var logs []LogsResult
|
||||
logt := table.LogsTableName
|
||||
taskt := table.TasksTableName
|
||||
|
||||
query := db.
|
||||
Table(logt).
|
||||
Select(fmt.Sprintf("%s.*, %s.name as task_name", logt, taskt)).
|
||||
Joins(fmt.Sprintf("JOIN %s ON %s.task_id = %s.id", taskt, logt, taskt))
|
||||
|
||||
if name != "" {
|
||||
query = query.Where(fmt.Sprintf("%s.name like ?", taskt), fmt.Sprintf("%%%s%%", name))
|
||||
}
|
||||
if taskId != "" {
|
||||
query = query.Where(fmt.Sprintf("%s.id = ?", taskt), taskId)
|
||||
}
|
||||
query = query.Order("created_on DESC")
|
||||
if pageSize > 0 || pageNum > 0 {
|
||||
query = query.Offset(pageNum).Limit(pageSize)
|
||||
}
|
||||
query.Scan(&logs)
|
||||
|
||||
return logs, nil
|
||||
}
|
||||
|
||||
// GetSendLogsTotal 获取所有日志总数
|
||||
func GetSendLogsTotal(name string, taskId string, maps interface{}) (int, error) {
|
||||
var total int
|
||||
logt := table.LogsTableName
|
||||
taskt := table.TasksTableName
|
||||
query := db.
|
||||
Table(logt).
|
||||
Joins(fmt.Sprintf("JOIN %s ON %s.task_id = %s.id", taskt, logt, taskt))
|
||||
if name != "" {
|
||||
query = query.Where(fmt.Sprintf("%s.name like ?", taskt), fmt.Sprintf("%%%s%%", name))
|
||||
}
|
||||
if taskId != "" {
|
||||
query = query.Where(fmt.Sprintf("%s.id = ?", taskt), taskId)
|
||||
}
|
||||
query.Count(&total)
|
||||
return total, nil
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/google/uuid"
|
||||
"github.com/jinzhu/gorm"
|
||||
)
|
||||
|
||||
type SendWays struct {
|
||||
UUIDModel
|
||||
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Auth string `gorm:"not null" json:"auth"`
|
||||
}
|
||||
|
||||
func AddSendWay(name string, auth string, wayType string, createdBy string, modifiedBy string) error {
|
||||
newUUID := uuid.New()
|
||||
way := SendWays{
|
||||
UUIDModel: UUIDModel{
|
||||
ID: newUUID,
|
||||
CreatedBy: createdBy,
|
||||
ModifiedBy: modifiedBy,
|
||||
},
|
||||
Name: name,
|
||||
Type: wayType,
|
||||
Auth: auth,
|
||||
}
|
||||
if err := db.Create(&way).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetSendWays(pageNum int, pageSize int, name string, type_ string, maps interface{}) ([]SendWays, error) {
|
||||
var (
|
||||
ways []SendWays
|
||||
err error
|
||||
)
|
||||
query := db.Where(maps)
|
||||
if name != "" {
|
||||
query = query.Where("name like ?", fmt.Sprintf("%%%s%%", name))
|
||||
}
|
||||
if type_ != "" {
|
||||
query = query.Where("type = ?", type_)
|
||||
}
|
||||
|
||||
query = query.Order("created_on DESC")
|
||||
if pageSize > 0 || pageNum > 0 {
|
||||
query = query.Offset(pageNum).Limit(pageSize)
|
||||
}
|
||||
|
||||
err = query.Find(&ways).Error
|
||||
if err != nil && err != gorm.ErrRecordNotFound {
|
||||
return nil, err
|
||||
}
|
||||
return ways, nil
|
||||
}
|
||||
|
||||
func GetSendWaysTotal(name string, type_ string, maps interface{}) (int, error) {
|
||||
var (
|
||||
err error
|
||||
total int
|
||||
)
|
||||
query := db.Model(&SendWays{}).Where(maps)
|
||||
if name != "" {
|
||||
query = query.Where("name like ?", fmt.Sprintf("%%%s%%", name))
|
||||
}
|
||||
if type_ != "" {
|
||||
query = query.Where("type = ?", type_)
|
||||
}
|
||||
|
||||
err = query.Count(&total).Error
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return total, nil
|
||||
}
|
||||
|
||||
func GetWayByID(id string) (SendWays, error) {
|
||||
var way SendWays
|
||||
err := db.Where("id = ? ", id).Find(&way).Error
|
||||
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return way, err
|
||||
}
|
||||
return way, nil
|
||||
}
|
||||
|
||||
func DeleteMsgWay(id string) error {
|
||||
if err := db.Where("id = ?", id).Delete(&SendWays{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func EditSendWay(id string, data interface{}) error {
|
||||
if err := db.Model(&SendWays{}).Where("id = ? ", id).Updates(data).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/astaxie/beego/validation"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-playground/validator/v10"
|
||||
"message-nest/pkg/e"
|
||||
"message-nest/pkg/util"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// BindAndValid binds and validates data
|
||||
func BindAndValid(c *gin.Context, form interface{}) (int, int) {
|
||||
err := c.Bind(form)
|
||||
if err != nil {
|
||||
return http.StatusBadRequest, e.INVALID_PARAMS
|
||||
}
|
||||
|
||||
valid := validation.Validation{}
|
||||
check, err := valid.Valid(form)
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, e.ERROR
|
||||
}
|
||||
if !check {
|
||||
MarkErrors(valid.Errors)
|
||||
return http.StatusBadRequest, e.INVALID_PARAMS
|
||||
}
|
||||
|
||||
return http.StatusOK, e.SUCCESS
|
||||
}
|
||||
|
||||
func CommonPlaygroundValid(obj interface{}) (int, string) {
|
||||
if err := util.CustomerValidate.Struct(obj); err != nil {
|
||||
errs := err.(validator.ValidationErrors)
|
||||
errMsg := BuildValidationErrors(errs)
|
||||
return http.StatusBadRequest, errMsg
|
||||
}
|
||||
return http.StatusOK, ""
|
||||
}
|
||||
|
||||
func BindJsonAndPlayValid(c *gin.Context, req interface{}) (int, string) {
|
||||
err := c.ShouldBindJSON(req)
|
||||
if err != nil {
|
||||
return http.StatusBadRequest, err.Error()
|
||||
} else {
|
||||
return CommonPlaygroundValid(req)
|
||||
}
|
||||
}
|
||||
|
||||
func BuildValidationErrors(errors []validator.FieldError) string {
|
||||
var errorMsgBuilder strings.Builder
|
||||
for i, err := range errors {
|
||||
if i > 0 {
|
||||
errorMsgBuilder.WriteString("; ")
|
||||
}
|
||||
message := err.Translate(util.Trans)
|
||||
errorMsgBuilder.WriteString(fmt.Sprintf("%s", message))
|
||||
}
|
||||
return errorMsgBuilder.String()
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/astaxie/beego/validation"
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"message-nest/pkg/logging"
|
||||
)
|
||||
|
||||
// MarkErrors logs error logs
|
||||
func MarkErrors(errors []*validation.Error) {
|
||||
for _, err := range errors {
|
||||
logging.Logger.Error(err.Key, err.Message)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 从请求中获取当前用户
|
||||
func GetCurrentUserName(c *gin.Context) string {
|
||||
userName, ok := c.Get("currentUserName")
|
||||
if !ok {
|
||||
return ""
|
||||
} else {
|
||||
return fmt.Sprintf("%s", userName)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
|
||||
"message-nest/pkg/e"
|
||||
)
|
||||
|
||||
type Gin struct {
|
||||
C *gin.Context
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data interface{} `json:"data"`
|
||||
}
|
||||
|
||||
// Response setting gin.JSON
|
||||
func (g *Gin) Response(httpCode, errCode int, data interface{}) {
|
||||
g.C.JSON(httpCode, Response{
|
||||
Code: errCode,
|
||||
Msg: e.GetMsg(errCode),
|
||||
Data: data,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
func (g *Gin) CResponse(errCode int, Msg string, data interface{}) {
|
||||
g.C.JSON(http.StatusOK, Response{
|
||||
Code: errCode,
|
||||
Msg: Msg,
|
||||
Data: data,
|
||||
})
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package e
|
||||
|
||||
const (
|
||||
SUCCESS = 200
|
||||
ERROR = 500
|
||||
INVALID_PARAMS = 400
|
||||
|
||||
ERROR_EXIST_TAG = 10001
|
||||
ERROR_EXIST_TAG_FAIL = 10002
|
||||
ERROR_NOT_EXIST_TAG = 10003
|
||||
ERROR_GET_TAGS_FAIL = 10004
|
||||
ERROR_COUNT_TAG_FAIL = 10005
|
||||
ERROR_ADD_TAG_FAIL = 10006
|
||||
ERROR_EDIT_TAG_FAIL = 10007
|
||||
ERROR_DELETE_TAG_FAIL = 10008
|
||||
ERROR_EXPORT_TAG_FAIL = 10009
|
||||
ERROR_IMPORT_TAG_FAIL = 10010
|
||||
|
||||
ERROR_NOT_EXIST_ARTICLE = 10011
|
||||
ERROR_CHECK_EXIST_ARTICLE_FAIL = 10012
|
||||
ERROR_ADD_ARTICLE_FAIL = 10013
|
||||
ERROR_DELETE_ARTICLE_FAIL = 10014
|
||||
ERROR_EDIT_ARTICLE_FAIL = 10015
|
||||
ERROR_COUNT_ARTICLE_FAIL = 10016
|
||||
ERROR_GET_ARTICLES_FAIL = 10017
|
||||
ERROR_GET_ARTICLE_FAIL = 10018
|
||||
ERROR_GEN_ARTICLE_POSTER_FAIL = 10019
|
||||
|
||||
ERROR_AUTH_CHECK_TOKEN_FAIL = 20001
|
||||
ERROR_AUTH_CHECK_TOKEN_TIMEOUT = 20002
|
||||
ERROR_AUTH_TOKEN = 20003
|
||||
ERROR_AUTH = 20004
|
||||
ERROR_AUTH_NO_TOKEN = 20005
|
||||
|
||||
ERROR_UPLOAD_SAVE_IMAGE_FAIL = 30001
|
||||
ERROR_UPLOAD_CHECK_IMAGE_FAIL = 30002
|
||||
ERROR_UPLOAD_CHECK_IMAGE_FORMAT = 30003
|
||||
)
|
||||
@@ -0,0 +1,44 @@
|
||||
package e
|
||||
|
||||
var MsgFlags = map[int]string{
|
||||
SUCCESS: "ok",
|
||||
ERROR: "fail",
|
||||
INVALID_PARAMS: "请求参数错误",
|
||||
ERROR_EXIST_TAG: "已存在该标签名称",
|
||||
ERROR_EXIST_TAG_FAIL: "获取已存在标签失败",
|
||||
ERROR_NOT_EXIST_TAG: "该标签不存在",
|
||||
ERROR_GET_TAGS_FAIL: "获取所有标签失败",
|
||||
ERROR_COUNT_TAG_FAIL: "统计标签失败",
|
||||
ERROR_ADD_TAG_FAIL: "新增标签失败",
|
||||
ERROR_EDIT_TAG_FAIL: "修改标签失败",
|
||||
ERROR_DELETE_TAG_FAIL: "删除标签失败",
|
||||
ERROR_EXPORT_TAG_FAIL: "导出标签失败",
|
||||
ERROR_IMPORT_TAG_FAIL: "导入标签失败",
|
||||
ERROR_NOT_EXIST_ARTICLE: "该文章不存在",
|
||||
ERROR_ADD_ARTICLE_FAIL: "新增文章失败",
|
||||
ERROR_DELETE_ARTICLE_FAIL: "删除文章失败",
|
||||
ERROR_CHECK_EXIST_ARTICLE_FAIL: "检查文章是否存在失败",
|
||||
ERROR_EDIT_ARTICLE_FAIL: "修改文章失败",
|
||||
ERROR_COUNT_ARTICLE_FAIL: "统计文章失败",
|
||||
ERROR_GET_ARTICLES_FAIL: "获取多个文章失败",
|
||||
ERROR_GET_ARTICLE_FAIL: "获取单个文章失败",
|
||||
ERROR_GEN_ARTICLE_POSTER_FAIL: "生成文章海报失败",
|
||||
ERROR_AUTH_CHECK_TOKEN_FAIL: "Token鉴权失败",
|
||||
ERROR_AUTH_CHECK_TOKEN_TIMEOUT: "Token已超时",
|
||||
ERROR_AUTH_TOKEN: "Token生成失败",
|
||||
ERROR_AUTH_NO_TOKEN: "Token缺失",
|
||||
ERROR_AUTH: "Token错误",
|
||||
ERROR_UPLOAD_SAVE_IMAGE_FAIL: "保存图片失败",
|
||||
ERROR_UPLOAD_CHECK_IMAGE_FAIL: "检查图片失败",
|
||||
ERROR_UPLOAD_CHECK_IMAGE_FORMAT: "校验图片错误,图片格式或大小有问题",
|
||||
}
|
||||
|
||||
// GetMsg get error information based on Code
|
||||
func GetMsg(code int) string {
|
||||
msg, ok := MsgFlags[code]
|
||||
if ok {
|
||||
return msg
|
||||
}
|
||||
|
||||
return MsgFlags[ERROR]
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"os"
|
||||
)
|
||||
|
||||
var Logger = logrus.New()
|
||||
|
||||
func Setup() {
|
||||
Logger := logrus.New()
|
||||
Logger.SetFormatter(&logrus.JSONFormatter{})
|
||||
Logger.SetOutput(os.Stdout)
|
||||
Logger.SetLevel(logrus.InfoLevel)
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package message
|
||||
|
||||
type DTalkMessage struct {
|
||||
WebhookUrl string
|
||||
}
|
||||
|
||||
func (d *DTalkMessage) send(content string) {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"gopkg.in/gomail.v2"
|
||||
)
|
||||
|
||||
type EmailMessage struct {
|
||||
Server string
|
||||
Port int
|
||||
Account string
|
||||
Passwd string
|
||||
GM *gomail.Dialer
|
||||
}
|
||||
|
||||
func (e *EmailMessage) Init(host string, port int, account string, passwd string) {
|
||||
e.Server = host
|
||||
e.Port = port
|
||||
e.Account = account
|
||||
e.Passwd = passwd
|
||||
e.GM = gomail.NewDialer(host, port, account, passwd)
|
||||
}
|
||||
|
||||
func (e *EmailMessage) SendTextMessage(toEmail string, title string, content string) string {
|
||||
m := gomail.NewMessage()
|
||||
m.SetHeader("From", e.Account)
|
||||
m.SetHeader("To", toEmail)
|
||||
m.SetHeader("Subject", title)
|
||||
m.SetBody("text/html", content)
|
||||
|
||||
if err := e.GM.DialAndSend(m); err != nil {
|
||||
return fmt.Sprintf("邮件发送失败: %s", err)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (e *EmailMessage) SendHtmlMessage(toEmail string, title string, content string) string {
|
||||
return e.SendTextMessage(toEmail, title, content)
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package setting
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/go-ini/ini"
|
||||
)
|
||||
|
||||
type App struct {
|
||||
JwtSecret string
|
||||
PageSize int
|
||||
PrefixUrl string
|
||||
|
||||
RuntimeRootPath string
|
||||
|
||||
ImageSavePath string
|
||||
ImageMaxSize int
|
||||
ImageAllowExts []string
|
||||
|
||||
ExportSavePath string
|
||||
QrCodeSavePath string
|
||||
FontSavePath string
|
||||
|
||||
LogSavePath string
|
||||
LogSaveName string
|
||||
LogFileExt string
|
||||
TimeFormat string
|
||||
|
||||
LogKeepNum int
|
||||
CleanTaskLogID string
|
||||
}
|
||||
|
||||
var AppSetting = &App{}
|
||||
|
||||
type Server struct {
|
||||
RunMode string
|
||||
HttpPort int
|
||||
ReadTimeout time.Duration
|
||||
WriteTimeout time.Duration
|
||||
}
|
||||
|
||||
var ServerSetting = &Server{}
|
||||
|
||||
type Database struct {
|
||||
Type string
|
||||
User string
|
||||
Password string
|
||||
Host string
|
||||
Name string
|
||||
TablePrefix string
|
||||
}
|
||||
|
||||
var DatabaseSetting = &Database{}
|
||||
|
||||
type Redis struct {
|
||||
Host string
|
||||
Password string
|
||||
MaxIdle int
|
||||
MaxActive int
|
||||
IdleTimeout time.Duration
|
||||
}
|
||||
|
||||
var RedisSetting = &Redis{}
|
||||
|
||||
var cfg *ini.File
|
||||
|
||||
// Setup initialize the configuration instance
|
||||
func Setup() {
|
||||
var err error
|
||||
cfg, err = ini.Load("conf/app.ini")
|
||||
if err != nil {
|
||||
log.Fatalf("setting.Setup, fail to parse 'conf/app.ini': %v", err)
|
||||
}
|
||||
|
||||
mapTo("app", AppSetting)
|
||||
mapTo("server", ServerSetting)
|
||||
mapTo("database", DatabaseSetting)
|
||||
mapTo("redis", RedisSetting)
|
||||
|
||||
AppSetting.ImageMaxSize = AppSetting.ImageMaxSize * 1024 * 1024
|
||||
ServerSetting.ReadTimeout = ServerSetting.ReadTimeout * time.Second
|
||||
ServerSetting.WriteTimeout = ServerSetting.WriteTimeout * time.Second
|
||||
RedisSetting.IdleTimeout = RedisSetting.IdleTimeout * time.Second
|
||||
|
||||
// 默认值
|
||||
AppSetting.LogKeepNum = 1000
|
||||
AppSetting.CleanTaskLogID = "00000000-0000-0000-0000-000000000001"
|
||||
}
|
||||
|
||||
// mapTo map section
|
||||
func mapTo(section string, v interface{}) {
|
||||
err := cfg.Section(section).MapTo(v)
|
||||
if err != nil {
|
||||
log.Fatalf("Cfg.MapTo %s err: %v", section, err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package table
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"message-nest/pkg/setting"
|
||||
)
|
||||
|
||||
var InsTableName string
|
||||
var WayTableName string
|
||||
var LogsTableName string
|
||||
var TasksTableName string
|
||||
|
||||
func Setup() {
|
||||
InsTableName = fmt.Sprintf("%ssend_tasks_ins", setting.DatabaseSetting.TablePrefix)
|
||||
WayTableName = fmt.Sprintf("%ssend_ways", setting.DatabaseSetting.TablePrefix)
|
||||
LogsTableName = fmt.Sprintf("%ssend_tasks_logs", setting.DatabaseSetting.TablePrefix)
|
||||
TasksTableName = fmt.Sprintf("%ssend_tasks", setting.DatabaseSetting.TablePrefix)
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
func TimeStampToYmdMHD(stamp int) string {
|
||||
timestamp := int64(stamp)
|
||||
timeObj := time.Unix(timestamp, 0)
|
||||
formattedTime := timeObj.Format("2006-01-02 15:04:05")
|
||||
return formattedTime
|
||||
}
|
||||
|
||||
func GetNowTimeStampToYmdMHD() string {
|
||||
now := time.Now().Unix()
|
||||
timeObj := time.Unix(now, 0)
|
||||
formattedTime := timeObj.Format("2006-01-02 15:04:05")
|
||||
return formattedTime
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
)
|
||||
|
||||
var jwtSecret []byte
|
||||
|
||||
type Claims struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
jwt.StandardClaims
|
||||
}
|
||||
|
||||
// GenerateToken generate tokens used for auth
|
||||
func GenerateToken(username, password string) (string, error) {
|
||||
nowTime := time.Now()
|
||||
expireTime := nowTime.Add(7 * 24 * time.Hour)
|
||||
|
||||
claims := Claims{
|
||||
username,
|
||||
EncodeMD5(password),
|
||||
jwt.StandardClaims{
|
||||
ExpiresAt: expireTime.Unix(),
|
||||
Issuer: "message-nest",
|
||||
},
|
||||
}
|
||||
|
||||
tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
token, err := tokenClaims.SignedString(jwtSecret)
|
||||
|
||||
return token, err
|
||||
}
|
||||
|
||||
// ParseToken parsing token
|
||||
func ParseToken(token string) (*Claims, error) {
|
||||
tokenClaims, err := jwt.ParseWithClaims(token, &Claims{}, func(token *jwt.Token) (interface{}, error) {
|
||||
return jwtSecret, nil
|
||||
})
|
||||
|
||||
if tokenClaims != nil {
|
||||
if claims, ok := tokenClaims.Claims.(*Claims); ok && tokenClaims.Valid {
|
||||
return claims, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
// EncodeMD5 md5 encryption
|
||||
func EncodeMD5(value string) string {
|
||||
m := md5.New()
|
||||
m.Write([]byte(value))
|
||||
|
||||
return hex.EncodeToString(m.Sum(nil))
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/unknwon/com"
|
||||
|
||||
"message-nest/pkg/setting"
|
||||
)
|
||||
|
||||
// GetPage get page parameters
|
||||
func GetPage(c *gin.Context) int {
|
||||
result := 0
|
||||
page := com.StrTo(c.Query("page")).MustInt()
|
||||
if page > 0 {
|
||||
result = (page - 1) * setting.AppSetting.PageSize
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func GetPageSize(c *gin.Context) (int, int) {
|
||||
result := 0
|
||||
page := com.StrTo(c.Query("page")).MustInt()
|
||||
size := com.StrTo(c.Query("size")).MustInt()
|
||||
if page > 0 {
|
||||
result = (page - 1) * size
|
||||
}
|
||||
return result, size
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
const timeFormat = "2006-01-02 15:04:05"
|
||||
const timezone = "Asia/Shanghai"
|
||||
|
||||
// 全局定义
|
||||
type Time time.Time
|
||||
|
||||
func (t Time) MarshalJSON() ([]byte, error) {
|
||||
b := make([]byte, 0, len(timeFormat)+2)
|
||||
b = append(b, '"')
|
||||
b = time.Time(t).AppendFormat(b, timeFormat)
|
||||
b = append(b, '"')
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (t *Time) UnmarshalJSON(data []byte) (err error) {
|
||||
now, err := time.ParseInLocation(`"`+timeFormat+`"`, string(data), time.Local)
|
||||
*t = Time(now)
|
||||
return
|
||||
}
|
||||
|
||||
func (t Time) String() string {
|
||||
return time.Time(t).Format(timeFormat)
|
||||
}
|
||||
|
||||
func (t Time) local() time.Time {
|
||||
loc, _ := time.LoadLocation(timezone)
|
||||
return time.Time(t).In(loc)
|
||||
}
|
||||
|
||||
func (t Time) Value() (driver.Value, error) {
|
||||
var zeroTime time.Time
|
||||
var ti = time.Time(t)
|
||||
if ti.UnixNano() == zeroTime.UnixNano() {
|
||||
return nil, nil
|
||||
}
|
||||
return ti, nil
|
||||
}
|
||||
|
||||
func (t *Time) Scan(v interface{}) error {
|
||||
value, ok := v.(time.Time)
|
||||
if ok {
|
||||
*t = Time(value)
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("can not convert %v to timestamp", v)
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"github.com/go-playground/locales/en"
|
||||
"github.com/go-playground/locales/zh"
|
||||
"github.com/go-playground/universal-translator"
|
||||
"github.com/go-playground/validator/v10"
|
||||
zh_trans "github.com/go-playground/validator/v10/translations/zh"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//var trans ut.Translator
|
||||
|
||||
func TransInit() (trans ut.Translator, validate *validator.Validate) {
|
||||
// 创建翻译器
|
||||
zhTrans := zh.New() // 中文转换器
|
||||
enTrans := en.New() // 因为转换器
|
||||
|
||||
uni := ut.New(zhTrans, zhTrans, enTrans) // 创建一个通用转换器
|
||||
|
||||
curLocales := "zh" // 设置当前语言类型
|
||||
trans, _ = uni.GetTranslator(curLocales) // 获取对应语言的转换器
|
||||
|
||||
validate = validator.New() // 创建验证器
|
||||
_ = zh_trans.RegisterDefaultTranslations(validate, trans)
|
||||
|
||||
//switch curLocales {
|
||||
//case "zh":
|
||||
// // 内置tag注册 中文翻译器
|
||||
// _ = zh_trans.RegisterDefaultTranslations(validate, trans)
|
||||
//case "en":
|
||||
// // 内置tag注册 英文翻译器
|
||||
// _ = en_trans.RegisterDefaultTranslations(validate, trans)
|
||||
//}
|
||||
|
||||
// 注册 RegisterTagNameFunc
|
||||
validate.RegisterTagNameFunc(func(field reflect.StructField) string {
|
||||
name := strings.SplitN(field.Tag.Get("label"), ",", 2)[0]
|
||||
if name == "-" {
|
||||
return ""
|
||||
}
|
||||
|
||||
return name
|
||||
})
|
||||
return trans, validate
|
||||
}
|
||||
|
||||
var Trans, CustomerValidate = TransInit()
|
||||
@@ -0,0 +1,10 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"message-nest/pkg/setting"
|
||||
)
|
||||
|
||||
// Setup Initialize the util
|
||||
func Setup() {
|
||||
jwtSecret = []byte(setting.AppSetting.JwtSecret)
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/astaxie/beego/validation"
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"message-nest/pkg/app"
|
||||
"message-nest/pkg/e"
|
||||
"message-nest/pkg/util"
|
||||
"message-nest/service/auth_service"
|
||||
)
|
||||
|
||||
type auth struct {
|
||||
Username string `valid:"Required; MaxSize(50)"`
|
||||
Password string `valid:"Required; MaxSize(50)"`
|
||||
}
|
||||
|
||||
type ReqAuth struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"passwd"`
|
||||
}
|
||||
|
||||
func GetAuth(c *gin.Context) {
|
||||
appG := app.Gin{C: c}
|
||||
valid := validation.Validation{}
|
||||
|
||||
var user ReqAuth
|
||||
err := c.ShouldBindJSON(&user)
|
||||
if err != nil {
|
||||
app.MarkErrors(valid.Errors)
|
||||
appG.Response(http.StatusBadRequest, e.INVALID_PARAMS, nil)
|
||||
return
|
||||
}
|
||||
username := user.Username
|
||||
password := user.Password
|
||||
|
||||
a := auth{Username: username, Password: password}
|
||||
ok, _ := valid.Valid(&a)
|
||||
|
||||
if !ok {
|
||||
app.MarkErrors(valid.Errors)
|
||||
appG.Response(http.StatusBadRequest, e.INVALID_PARAMS, nil)
|
||||
return
|
||||
}
|
||||
|
||||
authService := auth_service.Auth{Username: username, Password: password}
|
||||
isExist, err := authService.Check()
|
||||
if err != nil {
|
||||
appG.Response(http.StatusInternalServerError, e.ERROR_AUTH_CHECK_TOKEN_FAIL, nil)
|
||||
return
|
||||
}
|
||||
|
||||
if !isExist {
|
||||
appG.Response(http.StatusUnauthorized, e.ERROR_AUTH, nil)
|
||||
return
|
||||
}
|
||||
|
||||
token, err := util.GenerateToken(username, password)
|
||||
if err != nil {
|
||||
appG.Response(http.StatusInternalServerError, e.ERROR_AUTH_TOKEN, nil)
|
||||
return
|
||||
}
|
||||
|
||||
appG.Response(http.StatusOK, e.SUCCESS, map[string]string{
|
||||
"token": token,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
"message-nest/models"
|
||||
"message-nest/pkg/app"
|
||||
"message-nest/pkg/e"
|
||||
"message-nest/service/send_ins_service"
|
||||
"message-nest/service/send_task_service"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type DeleteMsgTaskInsReq struct {
|
||||
ID string `json:"id" validate:"required,len=36" label:"实例id"`
|
||||
}
|
||||
|
||||
// DeleteMsgSendWay 删除消息渠道
|
||||
func DeleteMsgTaskIns(c *gin.Context) {
|
||||
var (
|
||||
appG = app.Gin{C: c}
|
||||
req DeleteMsgTaskInsReq
|
||||
)
|
||||
|
||||
errCode, errMsg := app.BindJsonAndPlayValid(c, &req)
|
||||
if errCode != e.SUCCESS {
|
||||
appG.CResponse(errCode, errMsg, nil)
|
||||
return
|
||||
}
|
||||
|
||||
InsService := send_ins_service.SendTaskInsService{
|
||||
ID: req.ID,
|
||||
}
|
||||
err := InsService.Delete()
|
||||
if err != nil {
|
||||
appG.CResponse(http.StatusBadRequest, "删除实例失败!", nil)
|
||||
return
|
||||
}
|
||||
|
||||
appG.CResponse(http.StatusOK, "删除实例成功!", nil)
|
||||
}
|
||||
|
||||
// GetMsgSendWayIns 获取消息任务实例
|
||||
func GetMsgSendWayIns(c *gin.Context) {
|
||||
appG := app.Gin{C: c}
|
||||
id := c.Query("id")
|
||||
|
||||
if id == "" {
|
||||
appG.CResponse(http.StatusBadRequest, "任务id为空!", nil)
|
||||
return
|
||||
}
|
||||
|
||||
sendTaskService := send_task_service.SendTaskService{
|
||||
ID: id,
|
||||
}
|
||||
|
||||
task, err := sendTaskService.GetTaskWithIns()
|
||||
if err != nil {
|
||||
appG.CResponse(http.StatusBadRequest, "获取到的任务信息为空!", nil)
|
||||
return
|
||||
}
|
||||
|
||||
appG.CResponse(http.StatusOK, "获取任务信息成功", task)
|
||||
}
|
||||
|
||||
type SendTasksInsReq struct {
|
||||
ID string `json:"id" validate:"required,len=36" label:"实例id"`
|
||||
TaskId string `json:"task_id" validate:"required,len=36" label:"ins任务id"`
|
||||
WayID string `json:"way_id" validate:"required,len=36" label:"渠道id"`
|
||||
ContentType string `json:"content_type" validate:"required,max=100" label:"实例内容类型"`
|
||||
Config string `json:"config" validate:"" label:"任务配置"`
|
||||
Extra string `json:"extra" validate:"" label:"任务额外信息"`
|
||||
//WayName string `json:"way_name" validate:"required,max=100" label:"渠道名"`
|
||||
WayType string `json:"way_type" validate:"required,max=100" label:"渠道类型"`
|
||||
}
|
||||
|
||||
type AddManyTasksInsReq struct {
|
||||
TaskId string `json:"id" validate:"required,len=36" label:"任务id"`
|
||||
TaskName string `json:"name" validate:"required,max=100" label:"任务名"`
|
||||
InsData []SendTasksInsReq `json:"ins_data"`
|
||||
}
|
||||
|
||||
// AddManyTasksIns 添加发送任务关联的实例id
|
||||
func AddManyTasksIns(c *gin.Context) {
|
||||
var (
|
||||
appG = app.Gin{C: c}
|
||||
req AddManyTasksInsReq
|
||||
)
|
||||
|
||||
currentUser := app.GetCurrentUserName(c)
|
||||
errCode, errStr := app.BindJsonAndPlayValid(c, &req)
|
||||
if errCode != e.SUCCESS {
|
||||
appG.CResponse(errCode, errStr, nil)
|
||||
return
|
||||
}
|
||||
|
||||
// 校验提交的各个实例信息
|
||||
var taskIns []models.SendTasksIns
|
||||
if len(req.InsData) > 0 {
|
||||
for _, data := range req.InsData {
|
||||
code, dataErrStr := app.CommonPlaygroundValid(data)
|
||||
if code != http.StatusOK {
|
||||
appG.CResponse(code, dataErrStr, nil)
|
||||
return
|
||||
}
|
||||
uuidObj, _ := uuid.Parse(data.ID)
|
||||
taskIns = append(taskIns, models.SendTasksIns{
|
||||
UUIDModel: models.UUIDModel{
|
||||
ID: uuidObj,
|
||||
CreatedBy: currentUser,
|
||||
ModifiedBy: currentUser,
|
||||
},
|
||||
TaskID: data.TaskId,
|
||||
WayID: data.WayID,
|
||||
WayType: data.WayType,
|
||||
ContentType: data.ContentType,
|
||||
Config: data.Config,
|
||||
Extra: data.Extra,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
sendTaskInsService := send_ins_service.SendTaskInsService{}
|
||||
err := sendTaskInsService.ManyAdd(taskIns)
|
||||
if err != "" {
|
||||
appG.CResponse(http.StatusBadRequest, fmt.Sprintf("添加实例失败!错误原因:%s", err), nil)
|
||||
return
|
||||
}
|
||||
|
||||
sendTaskService := send_task_service.SendTaskService{
|
||||
ID: req.TaskId,
|
||||
Name: req.TaskName,
|
||||
CreatedBy: currentUser,
|
||||
}
|
||||
errAdd := sendTaskService.AddWithID()
|
||||
if errAdd != nil {
|
||||
appG.CResponse(http.StatusBadRequest, fmt.Sprintf("添加任务失败!错误原因:%s", err), nil)
|
||||
return
|
||||
}
|
||||
|
||||
appG.CResponse(http.StatusOK, "添加实例成功!", nil)
|
||||
|
||||
}
|
||||
|
||||
// AddTasksIns 添加发送任务关联的实例id
|
||||
func AddTasksIns(c *gin.Context) {
|
||||
var (
|
||||
appG = app.Gin{C: c}
|
||||
req SendTasksInsReq
|
||||
)
|
||||
|
||||
//currentUser := app.GetCurrentUserName(c)
|
||||
errCode, errStr := app.BindJsonAndPlayValid(c, &req)
|
||||
if errCode != e.SUCCESS {
|
||||
appG.CResponse(errCode, errStr, nil)
|
||||
return
|
||||
}
|
||||
|
||||
sendTaskInsService := send_ins_service.SendTaskInsService{}
|
||||
uuidObj, _ := uuid.Parse(req.ID)
|
||||
err := sendTaskInsService.AddOne(models.SendTasksIns{
|
||||
UUIDModel: models.UUIDModel{ID: uuidObj},
|
||||
TaskID: req.TaskId,
|
||||
WayID: req.WayID,
|
||||
WayType: req.WayType,
|
||||
ContentType: req.ContentType,
|
||||
Config: req.Config,
|
||||
Extra: req.Extra,
|
||||
//Cre: req.Extra,
|
||||
})
|
||||
if err != "" {
|
||||
appG.CResponse(http.StatusBadRequest, fmt.Sprintf("添加实例失败!错误原因:%s", err), nil)
|
||||
return
|
||||
}
|
||||
|
||||
appG.CResponse(http.StatusOK, "添加实例成功!", nil)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"message-nest/pkg/app"
|
||||
"message-nest/pkg/e"
|
||||
"message-nest/service/send_message_service"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type SendMessageReq struct {
|
||||
TaskID string `json:"task_id" validate:"required,len=36" label:"任务id"`
|
||||
Text string `json:"text" validate:"required" label:"文本内容"`
|
||||
HTML string `json:"html" label:"html内容"`
|
||||
MarkDown string `json:"markdown" label:"markdown内容"`
|
||||
}
|
||||
|
||||
// DoSendMassage 外部调用发信接口
|
||||
func DoSendMassage(c *gin.Context) {
|
||||
var (
|
||||
appG = app.Gin{C: c}
|
||||
req SendMessageReq
|
||||
)
|
||||
|
||||
errCode, errMsg := app.BindJsonAndPlayValid(c, &req)
|
||||
if errCode != e.SUCCESS {
|
||||
appG.CResponse(errCode, errMsg, nil)
|
||||
return
|
||||
}
|
||||
|
||||
msgService := send_message_service.SendMessageService{
|
||||
TaskID: req.TaskID,
|
||||
Text: req.Text,
|
||||
HTML: req.HTML,
|
||||
MarkDown: req.MarkDown,
|
||||
}
|
||||
err := msgService.Send()
|
||||
if err != "" {
|
||||
appG.CResponse(http.StatusBadRequest, "发送失败!", nil)
|
||||
return
|
||||
}
|
||||
|
||||
appG.CResponse(http.StatusOK, "发送成功!", nil)
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"message-nest/pkg/e"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"message-nest/pkg/app"
|
||||
"message-nest/pkg/util"
|
||||
"message-nest/service/send_task_service"
|
||||
)
|
||||
|
||||
type DeleteMsgSendTaskReq struct {
|
||||
ID string `json:"id" validate:"required,len=36" label:"任务id"`
|
||||
}
|
||||
|
||||
// DeleteMsgSendTask 删除消息任务
|
||||
func DeleteMsgSendTask(c *gin.Context) {
|
||||
var (
|
||||
appG = app.Gin{C: c}
|
||||
req DeleteMsgSendTaskReq
|
||||
)
|
||||
|
||||
errCode, errMsg := app.BindJsonAndPlayValid(c, &req)
|
||||
if errCode != e.SUCCESS {
|
||||
appG.CResponse(errCode, errMsg, nil)
|
||||
return
|
||||
}
|
||||
|
||||
MsgSendTaskService := send_task_service.SendTaskService{
|
||||
ID: req.ID,
|
||||
}
|
||||
err := MsgSendTaskService.Delete()
|
||||
if err != nil {
|
||||
appG.CResponse(http.StatusBadRequest, "删除发信任务失败!", nil)
|
||||
return
|
||||
}
|
||||
|
||||
appG.CResponse(http.StatusOK, "删除发信任务成功!", nil)
|
||||
}
|
||||
|
||||
// GetMsgSendTaskList 获取消息任务列表
|
||||
func GetMsgSendTaskList(c *gin.Context) {
|
||||
appG := app.Gin{C: c}
|
||||
name := c.Query("name")
|
||||
|
||||
offset, limit := util.GetPageSize(c)
|
||||
MsgSendTaskService := send_task_service.SendTaskService{
|
||||
Name: name,
|
||||
PageNum: offset,
|
||||
PageSize: limit,
|
||||
}
|
||||
tasks, err := MsgSendTaskService.GetAll()
|
||||
if err != nil {
|
||||
appG.CResponse(http.StatusInternalServerError, "获取任务任务失败!", nil)
|
||||
return
|
||||
}
|
||||
|
||||
count, err := MsgSendTaskService.Count()
|
||||
if err != nil {
|
||||
appG.CResponse(http.StatusInternalServerError, "获取任务任务总数失败!", nil)
|
||||
return
|
||||
}
|
||||
|
||||
appG.CResponse(http.StatusOK, "获取任务任务成功", map[string]interface{}{
|
||||
"lists": tasks,
|
||||
"total": count,
|
||||
})
|
||||
}
|
||||
|
||||
type AddMsgSendTaskReq struct {
|
||||
Name string `json:"name" validate:"required,max=100,min=1" label:"任务任务名"`
|
||||
}
|
||||
|
||||
// AddMsgSendTask 添加发送任务
|
||||
func AddMsgSendTask(c *gin.Context) {
|
||||
var (
|
||||
appG = app.Gin{C: c}
|
||||
req AddMsgSendTaskReq
|
||||
)
|
||||
|
||||
currentUser := app.GetCurrentUserName(c)
|
||||
errCode, errStr := app.BindJsonAndPlayValid(c, &req)
|
||||
if errCode != e.SUCCESS {
|
||||
appG.CResponse(errCode, errStr, nil)
|
||||
return
|
||||
}
|
||||
|
||||
MsgSendTaskService := send_task_service.SendTaskService{
|
||||
Name: req.Name,
|
||||
CreatedBy: currentUser,
|
||||
ModifiedBy: currentUser,
|
||||
}
|
||||
|
||||
err := MsgSendTaskService.Add()
|
||||
if err != nil {
|
||||
appG.CResponse(http.StatusBadRequest, "添加任务任务失败!", nil)
|
||||
return
|
||||
}
|
||||
|
||||
appG.CResponse(http.StatusOK, "添加任务任务成功!", nil)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"message-nest/pkg/app"
|
||||
"message-nest/pkg/util"
|
||||
"message-nest/service/send_logs_service"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// GetMsgSendWayList 获取消息渠道列表
|
||||
func GetTaskSendLogsList(c *gin.Context) {
|
||||
appG := app.Gin{C: c}
|
||||
name := c.Query("name")
|
||||
taskId := c.Query("taskid")
|
||||
|
||||
offset, limit := util.GetPageSize(c)
|
||||
logsService := send_logs_service.SendTaskLogsService{
|
||||
TaskId: taskId,
|
||||
Name: name,
|
||||
PageNum: offset,
|
||||
PageSize: limit,
|
||||
}
|
||||
ways, err := logsService.GetAll()
|
||||
if err != nil {
|
||||
appG.CResponse(http.StatusInternalServerError, "获取日志失败!", nil)
|
||||
return
|
||||
}
|
||||
|
||||
count, err := logsService.Count()
|
||||
if err != nil {
|
||||
appG.CResponse(http.StatusInternalServerError, "获取日志总数失败!", nil)
|
||||
return
|
||||
}
|
||||
|
||||
appG.CResponse(http.StatusOK, "获取日志成功", map[string]interface{}{
|
||||
"lists": ways,
|
||||
"total": count,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"message-nest/pkg/app"
|
||||
"message-nest/pkg/e"
|
||||
"message-nest/pkg/util"
|
||||
"message-nest/service/send_way_service"
|
||||
)
|
||||
|
||||
type DeleteMsgSendWayReq struct {
|
||||
ID string `json:"id" validate:"required,len=36" label:"渠道id"`
|
||||
}
|
||||
|
||||
// DeleteMsgSendWay 删除消息渠道
|
||||
func DeleteMsgSendWay(c *gin.Context) {
|
||||
var (
|
||||
appG = app.Gin{C: c}
|
||||
req DeleteMsgSendWayReq
|
||||
)
|
||||
|
||||
errCode, errMsg := app.BindJsonAndPlayValid(c, &req)
|
||||
if errCode != e.SUCCESS {
|
||||
appG.CResponse(errCode, errMsg, nil)
|
||||
return
|
||||
}
|
||||
|
||||
sendWayService := send_way_service.SendWay{
|
||||
ID: req.ID,
|
||||
}
|
||||
err := sendWayService.Delete()
|
||||
if err != nil {
|
||||
appG.CResponse(http.StatusBadRequest, fmt.Sprintf("删除发信渠道失败!%s", err), nil)
|
||||
return
|
||||
}
|
||||
|
||||
appG.CResponse(http.StatusOK, "删除发信渠道成功!", nil)
|
||||
}
|
||||
|
||||
// GetMsgSendWay 获取消息渠道
|
||||
func GetMsgSendWay(c *gin.Context) {
|
||||
appG := app.Gin{C: c}
|
||||
id := c.Query("id")
|
||||
|
||||
if id == "" {
|
||||
appG.CResponse(http.StatusBadRequest, "渠道id为空!", nil)
|
||||
return
|
||||
}
|
||||
|
||||
sendWayService := send_way_service.SendWay{
|
||||
ID: id,
|
||||
}
|
||||
|
||||
way, err := sendWayService.GetByID()
|
||||
if err != nil {
|
||||
appG.CResponse(http.StatusBadRequest, "获取到的渠道信息为空!", nil)
|
||||
return
|
||||
}
|
||||
|
||||
appG.CResponse(http.StatusOK, "获取渠道信息成功", way)
|
||||
}
|
||||
|
||||
// GetMsgSendWayList 获取消息渠道列表
|
||||
func GetMsgSendWayList(c *gin.Context) {
|
||||
appG := app.Gin{C: c}
|
||||
name := c.Query("name")
|
||||
type_ := c.Query("type")
|
||||
|
||||
offset, limit := util.GetPageSize(c)
|
||||
sendWayService := send_way_service.SendWay{
|
||||
Name: name,
|
||||
Type: type_,
|
||||
PageNum: offset,
|
||||
PageSize: limit,
|
||||
}
|
||||
ways, err := sendWayService.GetAll()
|
||||
if err != nil {
|
||||
appG.CResponse(http.StatusInternalServerError, "获取渠道信息失败!", nil)
|
||||
return
|
||||
}
|
||||
|
||||
count, err := sendWayService.Count()
|
||||
if err != nil {
|
||||
appG.CResponse(http.StatusInternalServerError, "获取渠道信息总数失败!", nil)
|
||||
return
|
||||
}
|
||||
|
||||
appG.CResponse(http.StatusOK, "获取渠道信息成功", map[string]interface{}{
|
||||
"lists": ways,
|
||||
"total": count,
|
||||
})
|
||||
}
|
||||
|
||||
type AddMsgSendWayReq struct {
|
||||
Name string `json:"name" validate:"required,max=100,min=1" label:"渠道名"`
|
||||
Type string `json:"type" validate:"required,max=100,min=1" label:"渠道类型"`
|
||||
Auth string `json:"auth" validate:"required,max=2048,min=6" label:"渠道认证方式"`
|
||||
}
|
||||
|
||||
// AddMsgSendWay 添加发送渠道
|
||||
func AddMsgSendWay(c *gin.Context) {
|
||||
var (
|
||||
appG = app.Gin{C: c}
|
||||
req AddMsgSendWayReq
|
||||
)
|
||||
currentUser := app.GetCurrentUserName(c)
|
||||
errCode, errStr := app.BindJsonAndPlayValid(c, &req)
|
||||
if errCode != e.SUCCESS {
|
||||
appG.CResponse(errCode, errStr, nil)
|
||||
return
|
||||
}
|
||||
|
||||
sendWayService := send_way_service.SendWay{
|
||||
Name: req.Name,
|
||||
Type: req.Type,
|
||||
Auth: req.Auth,
|
||||
CreatedBy: currentUser,
|
||||
ModifiedBy: currentUser,
|
||||
}
|
||||
|
||||
diffMsg, _ := sendWayService.ValidateDiffWay()
|
||||
if diffMsg != "" {
|
||||
appG.CResponse(http.StatusBadRequest, diffMsg, nil)
|
||||
return
|
||||
}
|
||||
|
||||
err := sendWayService.Add()
|
||||
if err != nil {
|
||||
appG.CResponse(http.StatusBadRequest, "添加渠道失败!", nil)
|
||||
return
|
||||
}
|
||||
|
||||
appG.CResponse(http.StatusOK, "添加渠道成功!", nil)
|
||||
|
||||
}
|
||||
|
||||
type EditSendWayReq struct {
|
||||
ID string `json:"id" validate:"required,len=36" label:"渠道id"`
|
||||
Name string `json:"name" validate:"required,max=100,min=1" label:"渠道名"`
|
||||
Type string `json:"type" validate:"required,max=100,min=1" label:"渠道类型"`
|
||||
Auth string `json:"auth" validate:"required" label:"渠道认证信息"`
|
||||
}
|
||||
|
||||
// EditSendWay 编辑发送渠道
|
||||
func EditSendWay(c *gin.Context) {
|
||||
var (
|
||||
appG = app.Gin{C: c}
|
||||
req = EditSendWayReq{}
|
||||
)
|
||||
|
||||
currentUser := app.GetCurrentUserName(c)
|
||||
errCode, errMsg := app.BindJsonAndPlayValid(c, &req)
|
||||
if errCode != e.SUCCESS {
|
||||
appG.CResponse(errCode, errMsg, nil)
|
||||
return
|
||||
}
|
||||
|
||||
sendWayService := send_way_service.SendWay{
|
||||
ID: req.ID,
|
||||
Name: req.Name,
|
||||
Type: req.Type,
|
||||
Auth: req.Auth,
|
||||
ModifiedBy: currentUser,
|
||||
}
|
||||
|
||||
diffMsg, _ := sendWayService.ValidateDiffWay()
|
||||
if diffMsg != "" {
|
||||
appG.CResponse(http.StatusBadRequest, diffMsg, nil)
|
||||
return
|
||||
}
|
||||
|
||||
err := sendWayService.Edit()
|
||||
if err != nil {
|
||||
appG.CResponse(http.StatusInternalServerError, "编辑渠道信息失败", nil)
|
||||
return
|
||||
}
|
||||
|
||||
appG.CResponse(http.StatusOK, "编辑渠道信息成功", nil)
|
||||
}
|
||||
|
||||
type TestSendWayReq struct {
|
||||
Name string `json:"name" validate:"required,max=100,min=1" label:"渠道名"`
|
||||
Type string `json:"type" validate:"required,max=100,min=1" label:"渠道类型"`
|
||||
Auth string `json:"auth" validate:"required" label:"渠道认证信息"`
|
||||
}
|
||||
|
||||
// TestSendWay 测试发送渠道
|
||||
func TestSendWay(c *gin.Context) {
|
||||
var (
|
||||
appG = app.Gin{C: c}
|
||||
req = TestSendWayReq{}
|
||||
)
|
||||
|
||||
errCode, errMsg := app.BindJsonAndPlayValid(c, &req)
|
||||
if errCode != e.SUCCESS {
|
||||
appG.CResponse(errCode, errMsg, nil)
|
||||
return
|
||||
}
|
||||
|
||||
sendWayService := send_way_service.SendWay{
|
||||
Name: req.Name,
|
||||
Type: req.Type,
|
||||
Auth: req.Auth,
|
||||
}
|
||||
|
||||
diffMsg, msgObj := sendWayService.ValidateDiffWay()
|
||||
if diffMsg != "" {
|
||||
appG.CResponse(http.StatusBadRequest, diffMsg, nil)
|
||||
return
|
||||
}
|
||||
|
||||
errTestMsg := sendWayService.TestSendWay(msgObj)
|
||||
if errTestMsg != "" {
|
||||
appG.CResponse(http.StatusInternalServerError, errTestMsg, nil)
|
||||
return
|
||||
}
|
||||
|
||||
appG.CResponse(http.StatusOK, "测试渠道信息成功", nil)
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"message-nest/pkg/app"
|
||||
"message-nest/pkg/e"
|
||||
"message-nest/service/settings_service"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type EditUserPasswd struct {
|
||||
OldPassword string `json:"old_passwd" validate:"required,max=50" label:"旧密码"`
|
||||
NewPassword string `json:"new_passwd" validate:"required,max=50" label:"新密码"`
|
||||
}
|
||||
|
||||
// EditPasswd 用户重设密码
|
||||
func EditPasswd(c *gin.Context) {
|
||||
var (
|
||||
appG = app.Gin{C: c}
|
||||
req EditUserPasswd
|
||||
)
|
||||
|
||||
currentUser := app.GetCurrentUserName(c)
|
||||
errCode, errStr := app.BindJsonAndPlayValid(c, &req)
|
||||
if errCode != e.SUCCESS {
|
||||
appG.CResponse(errCode, errStr, nil)
|
||||
return
|
||||
}
|
||||
|
||||
sendTaskService := settings_service.UserSettings{
|
||||
UserName: currentUser,
|
||||
OldPassword: req.OldPassword,
|
||||
NewPassword: req.NewPassword,
|
||||
}
|
||||
err := sendTaskService.EditUserPasswd()
|
||||
if err != nil {
|
||||
appG.CResponse(http.StatusBadRequest, fmt.Sprintf("修改密码失败!错误原因:%s", err), nil)
|
||||
return
|
||||
}
|
||||
|
||||
appG.CResponse(http.StatusOK, "修改成功!", nil)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package routers
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"message-nest/middleware"
|
||||
|
||||
"message-nest/routers/api"
|
||||
"message-nest/routers/api/v1"
|
||||
)
|
||||
|
||||
// InitRouter initialize routing information
|
||||
func InitRouter() *gin.Engine {
|
||||
r := gin.New()
|
||||
r.Use(gin.Logger())
|
||||
r.Use(gin.Recovery())
|
||||
r.Use(middleware.Cors())
|
||||
|
||||
r.POST("/auth", api.GetAuth)
|
||||
|
||||
apiv1 := r.Group("/api/v1")
|
||||
apiv1.Use(middleware.JWT())
|
||||
{
|
||||
// sendways
|
||||
apiv1.POST("/sendways/add", v1.AddMsgSendWay)
|
||||
apiv1.POST("/sendways/delete", v1.DeleteMsgSendWay)
|
||||
apiv1.POST("/sendways/edit", v1.EditSendWay)
|
||||
apiv1.POST("/sendways/test", v1.TestSendWay)
|
||||
apiv1.GET("/sendways/list", v1.GetMsgSendWayList)
|
||||
apiv1.GET("/sendways/get", v1.GetMsgSendWay)
|
||||
|
||||
// sendtasks
|
||||
apiv1.GET("/sendtasks/list", v1.GetMsgSendTaskList)
|
||||
apiv1.POST("/sendtasks/add", v1.AddMsgSendTask)
|
||||
apiv1.POST("/sendtasks/delete", v1.DeleteMsgSendTask)
|
||||
|
||||
// sendtasks/ins
|
||||
apiv1.POST("/sendtasks/ins/addmany", v1.AddManyTasksIns)
|
||||
apiv1.POST("/sendtasks/ins/addone", v1.AddTasksIns)
|
||||
apiv1.GET("/sendtasks/ins/gettask", v1.GetMsgSendWayIns)
|
||||
apiv1.POST("/sendtasks/ins/delete", v1.DeleteMsgTaskIns)
|
||||
|
||||
// message/send
|
||||
apiv1.POST("/message/send", v1.DoSendMassage)
|
||||
|
||||
apiv1.GET("/sendlogs/list", v1.GetTaskSendLogsList)
|
||||
|
||||
// settings
|
||||
apiv1.POST("/settings/setpasswd", v1.EditPasswd)
|
||||
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package auth_service
|
||||
|
||||
import "message-nest/models"
|
||||
|
||||
type Auth struct {
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
func (a *Auth) Check() (bool, error) {
|
||||
return models.CheckAuth(a.Username, a.Password)
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package send_ins_service
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"message-nest/models"
|
||||
"message-nest/pkg/app"
|
||||
)
|
||||
|
||||
type SendTaskInsService struct {
|
||||
ID string
|
||||
Name string
|
||||
CreatedBy string
|
||||
ModifiedBy string
|
||||
CreatedOn string
|
||||
|
||||
PageNum int
|
||||
PageSize int
|
||||
}
|
||||
|
||||
// ValidateDiffWay 各种发信渠道具体字段校验
|
||||
func (sw *SendTaskInsService) ValidateDiffIns(ins models.SendTasksIns) (string, interface{}) {
|
||||
var empty interface{}
|
||||
if ins.WayType == "Email" {
|
||||
var emailConfig models.InsEmailConfig
|
||||
err := json.Unmarshal([]byte(ins.Config), &emailConfig)
|
||||
if err != nil {
|
||||
return "邮箱auth反序列化失败!", empty
|
||||
}
|
||||
_, Msg := app.CommonPlaygroundValid(emailConfig)
|
||||
return Msg, emailConfig
|
||||
}
|
||||
return "", empty
|
||||
}
|
||||
|
||||
func (st *SendTaskInsService) ManyAdd(taskIns []models.SendTasksIns) string {
|
||||
|
||||
for _, ins := range taskIns {
|
||||
errStr, _ := st.ValidateDiffIns(ins)
|
||||
if errStr != "" {
|
||||
return errStr
|
||||
}
|
||||
}
|
||||
err := models.ManyAddTaskIns(taskIns)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("%s", err)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (st *SendTaskInsService) AddOne(ins models.SendTasksIns) string {
|
||||
errStr, _ := st.ValidateDiffIns(ins)
|
||||
if errStr != "" {
|
||||
return errStr
|
||||
}
|
||||
err := models.AddTaskInsOne(ins)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("%s", err)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (st *SendTaskInsService) Delete() error {
|
||||
return models.DeleteMsgTaskIns(st.ID)
|
||||
}
|
||||
|
||||
func (st *SendTaskInsService) Count() (int, error) {
|
||||
return models.GetSendTasksTotal(st.Name, st.getMaps())
|
||||
}
|
||||
|
||||
func (st *SendTaskInsService) GetAll() ([]models.SendTasks, error) {
|
||||
tasks, err := models.GetSendTasks(st.PageNum, st.PageSize, st.Name, st.getMaps())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tasks, nil
|
||||
}
|
||||
|
||||
func (st *SendTaskInsService) getMaps() map[string]interface{} {
|
||||
maps := make(map[string]interface{})
|
||||
return maps
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package send_logs_service
|
||||
|
||||
import (
|
||||
"message-nest/models"
|
||||
)
|
||||
|
||||
type SendTaskLogsService struct {
|
||||
ID int
|
||||
TaskId string
|
||||
Name string
|
||||
|
||||
PageNum int
|
||||
PageSize int
|
||||
}
|
||||
|
||||
func (st *SendTaskLogsService) Count() (int, error) {
|
||||
return models.GetSendLogsTotal(st.Name, st.TaskId, st.getMaps())
|
||||
}
|
||||
|
||||
func (st *SendTaskLogsService) GetAll() ([]models.LogsResult, error) {
|
||||
tasks, err := models.GetSendLogs(st.PageNum, st.PageSize, st.Name, st.TaskId, st.getMaps())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tasks, nil
|
||||
}
|
||||
|
||||
func (st *SendTaskLogsService) getMaps() map[string]interface{} {
|
||||
maps := make(map[string]interface{})
|
||||
return maps
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
package send_message_service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"message-nest/models"
|
||||
"message-nest/pkg/logging"
|
||||
"message-nest/pkg/message"
|
||||
"message-nest/service/send_task_service"
|
||||
"message-nest/service/send_way_service"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type SendMessageService struct {
|
||||
TaskID string
|
||||
Text string
|
||||
HTML string
|
||||
MarkDown string
|
||||
}
|
||||
|
||||
func (sm *SendMessageService) Send() string {
|
||||
var logOutput []string
|
||||
status := 1
|
||||
|
||||
sendTaskService := send_task_service.SendTaskService{
|
||||
ID: sm.TaskID,
|
||||
}
|
||||
task, err := sendTaskService.GetTaskWithIns()
|
||||
if err != nil {
|
||||
return fmt.Sprintf("任务不存在!任务id: %s", sm.TaskID)
|
||||
}
|
||||
|
||||
for idx, ins := range task.InsData {
|
||||
way, err := models.GetWayByID(ins.WayID)
|
||||
if err != nil {
|
||||
logOutput = append(logOutput, fmt.Sprintf("渠道信息不存在!渠道id:%s", ins.WayID))
|
||||
}
|
||||
wayService := send_way_service.SendWay{
|
||||
ID: fmt.Sprintf("%s", way.ID),
|
||||
Name: way.Name,
|
||||
Auth: way.Auth,
|
||||
Type: way.Type,
|
||||
}
|
||||
|
||||
logOutput = append(logOutput, fmt.Sprintf(">> 实例 %d", idx+1))
|
||||
logOutput = append(logOutput, fmt.Sprintf("开始发送,实例: %s", ins.WayID))
|
||||
logOutput = append(logOutput, fmt.Sprintf("实例类型: %s + %s", ins.WayType, ins.ContentType))
|
||||
logOutput = append(logOutput, fmt.Sprintf("实例配置: %s", ins.Config))
|
||||
|
||||
errStr, msgObj := wayService.ValidateDiffWay()
|
||||
if errStr != "" {
|
||||
sm.MarkStatus(errStr, &status)
|
||||
logOutput = append(logOutput, fmt.Sprintf("实例渠道认证校验失败: %s", errStr))
|
||||
continue
|
||||
}
|
||||
|
||||
// 邮箱类型的实例
|
||||
emailAuth, ok := msgObj.(send_way_service.WayDetailEmail)
|
||||
if ok {
|
||||
errMsg := sm.SendTaskEmail(emailAuth)
|
||||
sm.MarkStatus(errMsg, &status)
|
||||
logOutput = append(logOutput, sm.TransError(errMsg))
|
||||
continue
|
||||
}
|
||||
|
||||
logOutput = append(logOutput, fmt.Sprintf("未知渠道的发信实例: %s", ins.ID))
|
||||
|
||||
}
|
||||
|
||||
logOutput = sm.FormatSendContent(logOutput)
|
||||
sm.RecordSendLog(logOutput, status)
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// FormatSendContent 格式化输出的发送内容
|
||||
func (sm *SendMessageService) FormatSendContent(logOutput []string) []string {
|
||||
logOutput = append(logOutput, fmt.Sprintf(">> 发送的内容:"))
|
||||
if sm.Text != "" {
|
||||
logOutput = append(logOutput, fmt.Sprintf("Text: %s", sm.Text))
|
||||
}
|
||||
if sm.HTML != "" {
|
||||
logOutput = append(logOutput, fmt.Sprintf("HTML: %s", sm.HTML))
|
||||
}
|
||||
if sm.MarkDown != "" {
|
||||
logOutput = append(logOutput, fmt.Sprintf("MarkDown: %s", sm.MarkDown))
|
||||
}
|
||||
return logOutput
|
||||
}
|
||||
|
||||
// MarkStatus 标记任务状态
|
||||
func (sm *SendMessageService) MarkStatus(errStr string, status *int) {
|
||||
if errStr != "" {
|
||||
*status = 0
|
||||
}
|
||||
}
|
||||
|
||||
// RecordSendLog 记录发送日志
|
||||
func (sm *SendMessageService) RecordSendLog(logOutput []string, status int) {
|
||||
if len(logOutput) <= 0 {
|
||||
return
|
||||
}
|
||||
log := models.SendTasksLogs{
|
||||
Log: strings.Join(logOutput, "\n"),
|
||||
TaskID: sm.TaskID,
|
||||
Status: status,
|
||||
}
|
||||
err := log.Add()
|
||||
if err != nil {
|
||||
logging.Logger.Error(fmt.Sprintf("添加日志失败!原因是:%s", err))
|
||||
}
|
||||
}
|
||||
|
||||
// TransError 转化错误
|
||||
func (sm *SendMessageService) TransError(err string) string {
|
||||
if err == "" {
|
||||
return "发送成功!\n"
|
||||
} else {
|
||||
return fmt.Sprintf("发送失败:%s!", err)
|
||||
}
|
||||
}
|
||||
|
||||
// SendTaskEmail 执行发送邮件
|
||||
func (sm *SendMessageService) SendTaskEmail(auth send_way_service.WayDetailEmail) string {
|
||||
var emailer message.EmailMessage
|
||||
emailer.Init(auth.Server, auth.Port, auth.Account, auth.Passwd)
|
||||
//errMsg := emailer.SendTextMessage("sayheya@qq.com", "test", "This is a test email from message-nest.")
|
||||
//return errMsg
|
||||
return ""
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package send_task_service
|
||||
|
||||
import (
|
||||
"message-nest/models"
|
||||
)
|
||||
|
||||
type SendTaskService struct {
|
||||
ID string
|
||||
Name string
|
||||
CreatedBy string
|
||||
ModifiedBy string
|
||||
CreatedOn string
|
||||
|
||||
PageNum int
|
||||
PageSize int
|
||||
}
|
||||
|
||||
func (st *SendTaskService) Add() error {
|
||||
return models.AddSendTask(st.Name, st.CreatedBy)
|
||||
}
|
||||
|
||||
func (st *SendTaskService) AddWithID() error {
|
||||
return models.AddSendTaskWithID(st.Name, st.ID, st.CreatedBy)
|
||||
}
|
||||
|
||||
func (st *SendTaskService) Delete() error {
|
||||
return models.DeleteMsgTask(st.ID)
|
||||
}
|
||||
|
||||
func (st *SendTaskService) GetTaskWithIns() (models.TaskIns, error) {
|
||||
return models.GetTasksIns(st.ID)
|
||||
}
|
||||
|
||||
func (st *SendTaskService) Count() (int, error) {
|
||||
return models.GetSendTasksTotal(st.Name, st.getMaps())
|
||||
}
|
||||
|
||||
func (st *SendTaskService) GetAll() ([]models.SendTasks, error) {
|
||||
tasks, err := models.GetSendTasks(st.PageNum, st.PageSize, st.Name, st.getMaps())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tasks, nil
|
||||
}
|
||||
|
||||
func (st *SendTaskService) getMaps() map[string]interface{} {
|
||||
maps := make(map[string]interface{})
|
||||
return maps
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package send_way_service
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"message-nest/models"
|
||||
"message-nest/pkg/app"
|
||||
"message-nest/pkg/message"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type SendWay struct {
|
||||
ID string
|
||||
Name string
|
||||
Type string
|
||||
CreatedBy string
|
||||
ModifiedBy string
|
||||
Auth string
|
||||
CreatedOn string
|
||||
|
||||
PageNum int
|
||||
PageSize int
|
||||
}
|
||||
|
||||
// WayDetailEmail 邮箱渠道明细字段
|
||||
type WayDetailEmail struct {
|
||||
Server string `validate:"required,max=50" label:"SMTP服务地址"`
|
||||
Port int `validate:"required,max=65535" label:"SMTP服务端口"`
|
||||
Account string `validate:"required,email" label:"邮箱账号"`
|
||||
Passwd string `validate:"required,max=50" label:"邮箱密码"`
|
||||
}
|
||||
|
||||
// WayDetailDTalk 钉钉渠道明细字段
|
||||
type WayDetailDTalk struct {
|
||||
WebhookUrl string `validate:"required,url" label:"钉钉webhookUrl地址"`
|
||||
}
|
||||
|
||||
func (sw *SendWay) GetByID() (interface{}, error) {
|
||||
return models.GetWayByID(sw.ID)
|
||||
}
|
||||
|
||||
func (sw *SendWay) Add() error {
|
||||
return models.AddSendWay(sw.Name, sw.Auth, sw.Type, sw.CreatedBy, sw.ModifiedBy)
|
||||
}
|
||||
|
||||
func (sw *SendWay) Edit() error {
|
||||
data := make(map[string]interface{})
|
||||
data["modified_by"] = sw.ModifiedBy
|
||||
data["name"] = sw.Name
|
||||
data["auth"] = sw.Auth
|
||||
return models.EditSendWay(sw.ID, data)
|
||||
}
|
||||
|
||||
func (sw *SendWay) Delete() error {
|
||||
tasks := models.FindTaskByWayId(sw.ID)
|
||||
if len(tasks) > 0 {
|
||||
var names []string
|
||||
for _, task := range tasks {
|
||||
names = append(names, task.Name)
|
||||
}
|
||||
return errors.New(fmt.Sprintf("已经存在使用的任务,删除失败!任务名:%s", strings.Join(names, ", ")))
|
||||
}
|
||||
return models.DeleteMsgWay(sw.ID)
|
||||
}
|
||||
|
||||
func (sw *SendWay) Count() (int, error) {
|
||||
return models.GetSendWaysTotal(sw.Name, sw.Type, sw.getMaps())
|
||||
}
|
||||
|
||||
func (sw *SendWay) GetAll() ([]models.SendWays, error) {
|
||||
tags, err := models.GetSendWays(sw.PageNum, sw.PageSize, sw.Name, sw.Type, sw.getMaps())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tags, nil
|
||||
}
|
||||
|
||||
func (sw *SendWay) getMaps() map[string]interface{} {
|
||||
maps := make(map[string]interface{})
|
||||
return maps
|
||||
}
|
||||
|
||||
// ValidateDiffWay 各种发信渠道具体字段校验
|
||||
func (sw *SendWay) ValidateDiffWay() (string, interface{}) {
|
||||
var empty interface{}
|
||||
if sw.Type == "Email" {
|
||||
var email WayDetailEmail
|
||||
err := json.Unmarshal([]byte(sw.Auth), &email)
|
||||
if err != nil {
|
||||
return "邮箱auth反序列化失败!", empty
|
||||
}
|
||||
_, Msg := app.CommonPlaygroundValid(email)
|
||||
return Msg, email
|
||||
} else if sw.Type == "Dtalk" {
|
||||
var dtalk WayDetailDTalk
|
||||
err := json.Unmarshal([]byte(sw.Auth), &dtalk)
|
||||
if err != nil {
|
||||
return "钉钉auth反序列化失败!", empty
|
||||
}
|
||||
_, Msg := app.CommonPlaygroundValid(dtalk)
|
||||
return Msg, dtalk
|
||||
}
|
||||
return fmt.Sprintf("未知的发信渠道校验: %s", sw.Type), empty
|
||||
}
|
||||
|
||||
// TestSendWay 尝试带发信测试连通性
|
||||
func (sw *SendWay) TestSendWay(msgObj interface{}) string {
|
||||
emailAuth, ok := msgObj.(WayDetailEmail)
|
||||
if ok {
|
||||
var emailer message.EmailMessage
|
||||
emailer.Init(emailAuth.Server, 465, emailAuth.Account, emailAuth.Passwd)
|
||||
errMsg := emailer.SendTextMessage(emailAuth.Account, "test", "This is a test email from message-nest.")
|
||||
return errMsg
|
||||
}
|
||||
return fmt.Sprintf("未知的发信渠道校验: %s", sw.Type)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package settings_service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"message-nest/models"
|
||||
)
|
||||
|
||||
type UserSettings struct {
|
||||
UserName string
|
||||
OldPassword string
|
||||
NewPassword string
|
||||
}
|
||||
|
||||
// EditUserPasswd 用户设置密码
|
||||
func (us *UserSettings) EditUserPasswd() error {
|
||||
ok, _ := models.CheckAuth(us.UserName, us.OldPassword)
|
||||
if !ok {
|
||||
return errors.New("旧密码校验失败!")
|
||||
}
|
||||
var user = make(map[string]string)
|
||||
user["password"] = us.NewPassword
|
||||
return models.EditUser(us.UserName, user)
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/* eslint-env node */
|
||||
require('@rushstack/eslint-patch/modern-module-resolution')
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
'extends': [
|
||||
'plugin:vue/vue3-essential',
|
||||
'eslint:recommended',
|
||||
'@vue/eslint-config-prettier/skip-formatting'
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest'
|
||||
}
|
||||
}
|
||||
Vendored
+30
@@ -0,0 +1,30 @@
|
||||
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
|
||||
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
|
||||
|
||||
name: build-api-front-output
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [ "api-front" ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: "18.x"
|
||||
- run: npm install
|
||||
- run: npm run build
|
||||
|
||||
- name: Upload files to server
|
||||
uses: appleboy/scp-action@master
|
||||
with:
|
||||
host: 'nd2.ex.engigu.cn'
|
||||
username: 'root'
|
||||
password: ${{ secrets.ND2PWD }}
|
||||
source: './dist'
|
||||
target: '/root/workspace/ngproxy/data/pages/api-front'
|
||||
@@ -0,0 +1,28 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
|
||||
/cypress/videos/
|
||||
/cypress/screenshots/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/prettierrc",
|
||||
"semi": false,
|
||||
"tabWidth": 2,
|
||||
"singleQuote": true,
|
||||
"printWidth": 100,
|
||||
"trailingComma": "none"
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
# message-nest
|
||||
|
||||
This template should help get you started developing with Vue 3 in Vite.
|
||||
|
||||
## Customize configuration
|
||||
|
||||
See [Vite Configuration Reference](https://vitejs.dev/config/).
|
||||
|
||||
## Project Setup
|
||||
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compile and Hot-Reload for Development
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Compile and Minify for Production
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Lint with [ESLint](https://eslint.org/)
|
||||
|
||||
```sh
|
||||
npm run lint
|
||||
```
|
||||
@@ -0,0 +1,29 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Message Nest</title>
|
||||
</head>
|
||||
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#app {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
}
|
||||
</style>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Generated
+6266
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "message-nest",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore",
|
||||
"format": "prettier --write src/"
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.1.0",
|
||||
"axios": "^1.6.2",
|
||||
"element-plus": "^2.4.2",
|
||||
"fingerprintjs2": "^2.1.4",
|
||||
"lodash": "^4.17.21",
|
||||
"pinia": "^2.1.7",
|
||||
"prismjs": "^1.29.0",
|
||||
"uuid": "^9.0.1",
|
||||
"vue": "^3.3.4",
|
||||
"vue-clipboard3": "^2.0.0",
|
||||
"vue-router": "^4.2.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rushstack/eslint-patch": "^1.3.3",
|
||||
"@vitejs/plugin-vue": "^4.4.0",
|
||||
"@vue/eslint-config-prettier": "^8.0.0",
|
||||
"eslint": "^8.49.0",
|
||||
"eslint-plugin-vue": "^9.17.0",
|
||||
"prettier": "^3.0.3",
|
||||
"vite": "^4.4.11",
|
||||
"vite-plugin-prismjs": "^0.0.8"
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 5.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1 @@
|
||||
<svg t="1702547210136" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1861" width="200" height="200"><path d="M970.091313 224.033616a14.201535 14.201535 0 0 0-14.100687-3.631838c-88.419556-13.429657-142.919111-12.953859-248.242424 23.762747-69.454869 24.217859-109.056 79.873293-135.267556 116.712728-6.692202 9.403475-18.949172 26.625293-23.77309 29.509818-5.70699-1.303273-21.929374-16.036202-31.762101-24.965172-28.626747-25.994343-64.252121-58.353778-106.767516-58.757172-0.312889-0.005172-0.627071-0.005172-0.939959-0.005171-29.62101 0-54.798222 13.434828-72.857859 38.89907-13.222788 18.651798-17.914828 37.388929-18.106182 38.176323a14.31402 14.31402 0 0 0 2.893576 12.515556 14.336 14.336 0 0 0 11.782465 5.095434l0.409858-0.024565c19.192242-1.152 59.141172-3.545212 65.807516 81.857939 4.288646 54.899071 29.742545 108.631919 69.833697 147.419798 45.499475 44.025535 106.237414 67.298263 175.650909 67.298263 98.97503 0 166.222869-24.480323 205.207272-45.015919 43.172202-22.742626 62.621737-45.918384 63.429819-46.898425a14.170505 14.170505 0 0 0 1.529535-15.874586 14.191192 14.191192 0 0 0-14.166626-7.337373c-37.924202 4.443798-92.767677 6.09099-138.666667-11.298909-7.580444-2.872889-13.878303-5.893172-18.959515-8.691071 20.273131-9.964606 51.000889-28.383677 83.621495-59.93503 34.186343-33.065374 37.722505-69.908687 40.838464-102.423273 2.297535-23.919192 4.666182-48.651636 18.500526-73.091879 28.807758-50.883232 66.645333-74.489535 74.560646-79.035475a14.201535 14.201535 0 0 0 12.135434-7.776969 14.193778 14.193778 0 0 0-2.59103-16.484849z" fill="" p-id="1862"></path><path d="M674.834101 716.481939c-33.422222 4.90796-74.989899 16.884364-117.22602-8.102787-34.843152-20.613172-53.056646-20.993293-77.783919-18.49794-0.156444 0.015515-0.312889 0.015515-0.469334 0.034909-0.274101 0.033616-0.548202 0.060768-0.822303 0.094384-4.697212 0.487434-9.65301 1.065374-14.995394 1.62004-6.702545 0.649051-13.414141 1.19596-20.133495 1.634263-75.333818 4.102465-136.860444-5.381172-163.012525-10.404202-49.488162-9.872808-86.57196-23.921778-108.946101-33.970424-59.832889-26.868364-87.080081-56.671677-87.080081-72.989738 0-1.873455 0.384-2.222545 0.787394-2.585858 2.196687-1.974303 10.616242-6.520242 41.302626-6.004364 24.793212 0.418909 56.970343 3.858101 94.308849 7.853253 40.722101 4.348121 86.873212 9.283232 136.348444 11.686788 6.913293 0.333576 13.959758 0.625778 20.93899 0.858505 9.510788 0.307717 17.545051-7.182222 17.868283-16.707233 0.319354-9.535354-7.175758-17.550222-16.705939-17.868282a1386.24 1386.24 0 0 1-20.419233-0.839112c-48.521051-2.358303-94.161455-7.236525-134.434909-11.544565-90.868364-9.712485-139.353212-13.818828-162.333737 6.833131-8.02004 7.21196-12.262141 17.004606-12.262141 28.317737 0 0.672323 0.029737 1.348525 0.065939 2.026021l0.005172 0.100848c-0.045253 0.524929-0.071111 1.039515-0.071111 1.539879 0 41.464242 9.561212 81.661414 28.414707 119.484768 17.493333 35.088808 42.251636 66.663434 73.583192 93.927434 9.169455-6.551273 20.155475-13.604202 32.524929-20.050748 49.737697-25.921939 97.838545-29.339152 139.099798-9.884444 101.590626 47.906909 142.060606 47.837091 206.198949-0.364606 5.70699-4.287354 13.814949-3.140525 18.103596 2.567758 4.289939 5.708283 3.140525 13.813657-2.567757 18.103595-38.425859 28.877576-69.968162 41.279354-105.143596 41.280647-0.484848 0-0.969697-0.002586-1.457132-0.007758-32.760242-0.316768-69.311354-11.381657-126.16404-38.190545-51.905939-24.47903-106.215434 1.008485-139.548444 23.465374 64.611556 47.922424 146.341495 74.101657 232.704 74.101656 117.55701 0 226.204444-48.934788 292.761858-131.362909l0.002586-0.002586c18.79402-14.959192 12.028121-41.362101-23.442101-36.152889z" fill="" p-id="1863"></path></svg>
|
||||
|
After Width: | Height: | Size: 3.7 KiB |
@@ -0,0 +1 @@
|
||||
<svg t="1702547210136" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1861" width="200" height="200"><path d="M970.091313 224.033616a14.201535 14.201535 0 0 0-14.100687-3.631838c-88.419556-13.429657-142.919111-12.953859-248.242424 23.762747-69.454869 24.217859-109.056 79.873293-135.267556 116.712728-6.692202 9.403475-18.949172 26.625293-23.77309 29.509818-5.70699-1.303273-21.929374-16.036202-31.762101-24.965172-28.626747-25.994343-64.252121-58.353778-106.767516-58.757172-0.312889-0.005172-0.627071-0.005172-0.939959-0.005171-29.62101 0-54.798222 13.434828-72.857859 38.89907-13.222788 18.651798-17.914828 37.388929-18.106182 38.176323a14.31402 14.31402 0 0 0 2.893576 12.515556 14.336 14.336 0 0 0 11.782465 5.095434l0.409858-0.024565c19.192242-1.152 59.141172-3.545212 65.807516 81.857939 4.288646 54.899071 29.742545 108.631919 69.833697 147.419798 45.499475 44.025535 106.237414 67.298263 175.650909 67.298263 98.97503 0 166.222869-24.480323 205.207272-45.015919 43.172202-22.742626 62.621737-45.918384 63.429819-46.898425a14.170505 14.170505 0 0 0 1.529535-15.874586 14.191192 14.191192 0 0 0-14.166626-7.337373c-37.924202 4.443798-92.767677 6.09099-138.666667-11.298909-7.580444-2.872889-13.878303-5.893172-18.959515-8.691071 20.273131-9.964606 51.000889-28.383677 83.621495-59.93503 34.186343-33.065374 37.722505-69.908687 40.838464-102.423273 2.297535-23.919192 4.666182-48.651636 18.500526-73.091879 28.807758-50.883232 66.645333-74.489535 74.560646-79.035475a14.201535 14.201535 0 0 0 12.135434-7.776969 14.193778 14.193778 0 0 0-2.59103-16.484849z" fill="" p-id="1862"></path><path d="M674.834101 716.481939c-33.422222 4.90796-74.989899 16.884364-117.22602-8.102787-34.843152-20.613172-53.056646-20.993293-77.783919-18.49794-0.156444 0.015515-0.312889 0.015515-0.469334 0.034909-0.274101 0.033616-0.548202 0.060768-0.822303 0.094384-4.697212 0.487434-9.65301 1.065374-14.995394 1.62004-6.702545 0.649051-13.414141 1.19596-20.133495 1.634263-75.333818 4.102465-136.860444-5.381172-163.012525-10.404202-49.488162-9.872808-86.57196-23.921778-108.946101-33.970424-59.832889-26.868364-87.080081-56.671677-87.080081-72.989738 0-1.873455 0.384-2.222545 0.787394-2.585858 2.196687-1.974303 10.616242-6.520242 41.302626-6.004364 24.793212 0.418909 56.970343 3.858101 94.308849 7.853253 40.722101 4.348121 86.873212 9.283232 136.348444 11.686788 6.913293 0.333576 13.959758 0.625778 20.93899 0.858505 9.510788 0.307717 17.545051-7.182222 17.868283-16.707233 0.319354-9.535354-7.175758-17.550222-16.705939-17.868282a1386.24 1386.24 0 0 1-20.419233-0.839112c-48.521051-2.358303-94.161455-7.236525-134.434909-11.544565-90.868364-9.712485-139.353212-13.818828-162.333737 6.833131-8.02004 7.21196-12.262141 17.004606-12.262141 28.317737 0 0.672323 0.029737 1.348525 0.065939 2.026021l0.005172 0.100848c-0.045253 0.524929-0.071111 1.039515-0.071111 1.539879 0 41.464242 9.561212 81.661414 28.414707 119.484768 17.493333 35.088808 42.251636 66.663434 73.583192 93.927434 9.169455-6.551273 20.155475-13.604202 32.524929-20.050748 49.737697-25.921939 97.838545-29.339152 139.099798-9.884444 101.590626 47.906909 142.060606 47.837091 206.198949-0.364606 5.70699-4.287354 13.814949-3.140525 18.103596 2.567758 4.289939 5.708283 3.140525 13.813657-2.567757 18.103595-38.425859 28.877576-69.968162 41.279354-105.143596 41.280647-0.484848 0-0.969697-0.002586-1.457132-0.007758-32.760242-0.316768-69.311354-11.381657-126.16404-38.190545-51.905939-24.47903-106.215434 1.008485-139.548444 23.465374 64.611556 47.922424 146.341495 74.101657 232.704 74.101656 117.55701 0 226.204444-48.934788 292.761858-131.362909l0.002586-0.002586c18.79402-14.959192 12.028121-41.362101-23.442101-36.152889z" fill="" p-id="1863"></path></svg>
|
||||
|
After Width: | Height: | Size: 3.7 KiB |
@@ -0,0 +1,9 @@
|
||||
<template>
|
||||
<Index></Index>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Index from "./views/home/index.vue"
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@@ -0,0 +1,89 @@
|
||||
// request.js
|
||||
import axios from 'axios';
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { usePageState } from '../store/page_sate';
|
||||
import { CONSTANT } from '../constant';
|
||||
|
||||
const ERR_NETWORK = "ERR_NETWORK";
|
||||
|
||||
const request = axios.create({
|
||||
baseURL: 'http://127.0.0.1:8000',
|
||||
timeout: 50000,
|
||||
withCredentials: true, // 允许发送跨域凭证(例如,用于保持登录状态)
|
||||
});
|
||||
|
||||
|
||||
// 请求拦截器
|
||||
request.interceptors.request.use(
|
||||
(config) => {
|
||||
const pageState = usePageState();
|
||||
// console.log('config', config);
|
||||
if (!CONSTANT.NO_AUTH_URL.includes(config.url)) {
|
||||
config.url = '/api/v1' + config.url;
|
||||
}
|
||||
|
||||
if (pageState.Token && !CONSTANT.NO_AUTH_URL.includes(config.url)) {
|
||||
// 添加 token 参数到 URL 中
|
||||
config.headers = {
|
||||
...config.headers,
|
||||
'm-token': pageState.Token,
|
||||
};
|
||||
}
|
||||
|
||||
return config;
|
||||
},
|
||||
(error) => {
|
||||
// 处理请求错误
|
||||
handleException(error);
|
||||
// return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
// 响应拦截器
|
||||
request.interceptors.response.use(
|
||||
(response) => {
|
||||
// console.log('request.interceptors.response', response);
|
||||
if (response && response.data.code != 200) {
|
||||
ElMessage({ message: response.data.msg, type: 'error' })
|
||||
// Promise.reject();
|
||||
}
|
||||
return response;
|
||||
},
|
||||
(error) => {
|
||||
if (error.response && error.response.status === 401) {
|
||||
// 未授权,可能是登录状态过期,执行登出操作
|
||||
logout();
|
||||
}
|
||||
handleException(error);
|
||||
// return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
// 异常处理
|
||||
const handleException = (error) => {
|
||||
console.log('handleException', error);
|
||||
if (error.code == ERR_NETWORK) {
|
||||
ElMessage({ message: `网络错误!`, type: 'error' })
|
||||
} else if (error.response && error.response.data.code != 200) {
|
||||
ElMessage({ message: error.response.data.msg, type: 'error' })
|
||||
};
|
||||
|
||||
// if (error.response) {
|
||||
// // 服务器返回错误状态码
|
||||
// console.error('Server Error:', error.response.status, error.response.data);
|
||||
// } else if (error.request) {
|
||||
// // 请求发送成功,但没有收到响应
|
||||
// console.error('No response received:', error.request);
|
||||
// } else {
|
||||
// // 其他错误
|
||||
// console.error('Error:', error.message);
|
||||
// }
|
||||
};
|
||||
|
||||
// 登出系统
|
||||
const logout = () => {
|
||||
// 执行登出逻辑,清除本地存储的用户信息等
|
||||
console.log('User logged out');
|
||||
};
|
||||
|
||||
export { request, handleException, logout };
|
||||
@@ -0,0 +1,33 @@
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* #main-container {
|
||||
display: block;
|
||||
margin-left: 0px;
|
||||
padding: 0;
|
||||
} */
|
||||
|
||||
.main-center-body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 0 0 0 0;
|
||||
padding: 0;
|
||||
background-color: #f5f5f5;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
/* flex: 1; */
|
||||
}
|
||||
|
||||
.container {
|
||||
background-color: white;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.1);
|
||||
max-width: 400px;
|
||||
width: 100%;
|
||||
/* margin-top: -10vh; */
|
||||
/* flex: 1; */
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
textarea {
|
||||
width: 99%;
|
||||
padding: 2px;
|
||||
margin-bottom: 10px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
button {
|
||||
width: 46%;
|
||||
padding: 10px;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
margin-top: 3%;
|
||||
margin-left: 1%;
|
||||
margin-right: 1%;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
max-width: 90%;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>
|
||||
|
After Width: | Height: | Size: 276 B |
@@ -0,0 +1 @@
|
||||
@import './base.css';
|
||||
@@ -0,0 +1 @@
|
||||
<svg t="1700568914730" class="titleicon" viewBox="0 0 1280 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4276" width="200" height="200"><path d="M1046.485822 53.112897c4.54104 44.898732-52.062062 38.886651-76.749969 36.200402-24.687907-2.622291-56.794977 18.995617-73.29622 42.532274-9.977496 14.390619-18.164159 37.351651-24.048323 57.498518 104.891624-10.233329 257.368228 37.351651 342.176943 157.657228 15.030202 23.536657 27.246239 48.160605 36.903944 73.616011 38.183109 101.054125 34.089778 207.03304 4.732914 308.790707a544.732902 544.732902 0 0 1-21.234158 61.016225 406.90275 406.90275 0 0 1-65.813098 105.914957 403.12921 403.12921 0 0 1-165.204307 118.514743l-18.547909 6.587706-12.08812 1.918749H215.923245l-7.54708-0.7675-12.791662-3.38979c-104.891624-33.322278-189.956172-133.225154-195.32867-325.29195L0 674.403893l0.575625-13.23937c1.854791-27.629989 8.186663-62.743099 21.553949-101.885583a410.996082 410.996082 0 0 1 127.596823-186.054716 383.557968 383.557968 0 0 1 106.426623-62.551224c86.023923-33.514153 176.397011-35.816652 260.118436-19.827075l4.541039 0.831458 4.349165 0.895416 15.222077 3.453749 11.832287 3.197915 59.033518 18.867701-36.072486 44.770815c-15.733744 19.507284-43.875399 47.201231-83.401632 74.703302l-17.39666 11.512496-17.908326 10.744995-8.31458 4.477082 26.222906 116.404119-36.328318 9.785621-14.070828 3.453749c-25.583323 5.500414-50.974771 7.035414-74.063719-0.447709-18.803742-6.012081-42.660191-24.304157-63.830391-46.369772l-8.890205-9.657705-3.645623-4.349165-1.726874 3.709582a136.231194 136.231194 0 0 0-9.08208 30.380196c-5.756248 33.450195 8.058747 73.424137 24.815823 92.547671l4.604998 4.732914 18.036243 15.541869 6.971455 5.244581c39.334359 27.693947 79.116426 30.380196 130.986613-8.570413 20.146867-15.158119 38.694776-43.939357 54.556436-83.017883 7.16333-17.588534 13.431245-36.456235 19.315409-56.794976l3.261874-11.896246 3.261874-12.407911 9.593746-38.502901 5.756247-20.274783 5.820206-17.588535c11.256662-31.787279 26.222906-62.231433 45.858106-91.076629 51.998104-75.854552 129.707447-128.939947 236.837612-149.790356-23.152907-23.024991-58.074143-49.311855-97.728293-51.614354-90.820796-6.971455-147.935565 45.666231-155.418687 50.143313-7.611039 4.477082-32.810612 18.547909-54.684353 0-21.809783-18.547909-10.872912-83.145799 45.410399-113.270162 56.28331-30.124363 151.261396-36.328318 213.428871 14.070828 35.304986 28.71728 56.027477 68.435389 67.284139 97.47246l6.39583-0.959375 1.535-0.255833c-1.023333-23.728532 1.023333-54.492478 13.942911-96.257252C897.7188 12.819164 943.385031 4.184792 959.886274 0.795002c16.565202-3.325832 81.546842 1.918749 86.599548 52.253937z m-170.960555 215.091787l-6.779581 0.831458-17.652492 2.878124c-88.77413 16.309368-150.110147 57.882268-191.235339 118.003077-15.605827 22.769157-27.502072 47.137272-36.64811 72.848512l-4.796873 14.454577-4.15729 14.582494-13.623119 53.852895-3.645624 13.047494a632.227866 632.227866 0 0 1-22.065616 64.853724c-20.722492 51.102687-46.433731 90.884755-80.139759 116.276202-81.099134 60.888308-157.337436 55.835602-222.319075 9.913538l-10.169371-7.483122-9.785621-8.058747-7.674997-6.77958-11.192704-10.553121c-34.665402-36.136444-58.266018-102.653083-47.776855-163.2216a223.214492 223.214492 0 0 1 28.909155-76.494135c9.785621-16.501243 19.763117-29.228946 27.94978-37.991235l4.668956-4.796873 43.491649-42.212483 20.274783 57.370602c7.227289 20.466658 48.73623 64.59789 62.423308 68.947055l1.854791 0.511667 2.110624 0.319791 3.006041 0.127917-22.385408-99.647043 29.420822-13.111453a354.968605 354.968605 0 0 0 45.474356-24.304157l14.326661-9.593746 14.326661-10.425204-13.814995-1.279166a390.337549 390.337549 0 0 0-127.916614 11.384579l-17.972285 5.308539-17.780409 6.395831a305.784667 305.784667 0 0 0-84.936632 49.88748A332.839031 332.839031 0 0 0 95.937461 584.99018c-10.872912 31.787279-15.989577 60.05685-17.460618 80.779341l-0.38375 8.442497 0.255833 19.827075c2.814166 86.983298 25.32749 150.046189 61.527892 193.921588 22.065616 26.734572 46.689564 43.491649 68.819138 52.445812l9.337913 3.38979 2.942082 0.831458h745.561987l11.000829-3.837499a320.622994 320.622994 0 0 0 108.601206-70.098304l12.791661-13.111453 10.872912-12.279995c22.513324-26.734572 39.014567-53.852895 48.992063-75.598719l3.837499-8.954163 8.31458-21.234158c3.581665-9.785621 6.651664-19.25145 10.361246-32.107071 25.007698-86.791423 28.397488-176.141178-2.814166-258.711352a322.349868 322.349868 0 0 0-28.525405-57.626435c-38.758734-54.876228-94.978086-90.373088-161.942434-109.112872-42.532274-11.960203-87.750797-15.86166-112.56662-13.751036z m104.699749 166.931182a60.696434 60.696434 0 0 1 35.432902 55.963519c0 33.386236-25.775198 60.4406-57.43456 60.504558a57.562476 57.562476 0 0 1-53.149353-37.41561 62.934974 62.934974 0 0 1 12.47187-66.004973 55.387894 55.387894 0 0 1 62.679141-13.111452z" p-id="4277"></path></svg>
|
||||
|
After Width: | Height: | Size: 4.8 KiB |
@@ -0,0 +1,34 @@
|
||||
// 定义一些常量名
|
||||
const CONSTANT = {
|
||||
PAGE: 1,
|
||||
PAGE_SIZE: 6,
|
||||
TOTAL: 0,
|
||||
LOG_TASK_ID: "00000000-0000-0000-0000-000000000001",
|
||||
STORE_TOKEN_NAME: '__message_nest_token__',
|
||||
NO_AUTH_URL: [
|
||||
'/auth',
|
||||
],
|
||||
WAYS_DATA: [
|
||||
{
|
||||
type: 'Email',
|
||||
label: '邮箱',
|
||||
inputs: [
|
||||
{ subLabel: 'smtp服务地址', value: '', col: 'server' },
|
||||
{ subLabel: 'smtp服务端口', value: '', col: 'port' },
|
||||
{ subLabel: '邮箱账号', value: '', col: 'account' },
|
||||
{ subLabel: '邮箱密码', value: '', col: 'passwd' },
|
||||
{ subLabel: '渠道名', value: '', col: 'name' },
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'Dtalk',
|
||||
label: '钉钉',
|
||||
inputs: [
|
||||
{ subLabel: 'webhook地址', value: '', col: 'webhook_url' },
|
||||
{ subLabel: '渠道名', value: '', col: 'name' },
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
export { CONSTANT }
|
||||
@@ -0,0 +1,20 @@
|
||||
import './assets/main.css'
|
||||
|
||||
import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
import ElementPlus from 'element-plus'
|
||||
import 'element-plus/dist/index.css'
|
||||
// import './assets/styles/global.css'
|
||||
import pinia from './store';
|
||||
|
||||
|
||||
const app = createApp(App)
|
||||
app.use(router)
|
||||
app.use(ElementPlus)
|
||||
app.use(pinia);
|
||||
|
||||
|
||||
app.mount('#app')
|
||||
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router'
|
||||
import LoginInex from '../views/home/login.vue'
|
||||
import { CONSTANT } from '../constant'
|
||||
|
||||
const router = createRouter({
|
||||
// history: createWebHistory(import.meta.env.BASE_URL),
|
||||
history: createWebHashHistory(),
|
||||
routes: [
|
||||
{
|
||||
path: '/login',
|
||||
name: 'login',
|
||||
component: LoginInex
|
||||
},
|
||||
{
|
||||
path: '/sendways',
|
||||
name: 'sendWays',
|
||||
component: () => import('../views/tabsTools/sendWays/sendWays.vue')
|
||||
},
|
||||
{
|
||||
path: '/sendtasks',
|
||||
name: 'sendtasks',
|
||||
component: () => import('../views/tabsTools/sendTasks/sendTasks.vue')
|
||||
},
|
||||
{
|
||||
path: '/sendlogs',
|
||||
name: 'sendlogs',
|
||||
component: () => import('../views/tabsTools/sendLogs/sendLogs.vue')
|
||||
},
|
||||
{
|
||||
path: '/settings',
|
||||
name: 'settings',
|
||||
component: () => import('../views/tabsTools/settings/settings.vue')
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
// 登录失效重定向到登录页面
|
||||
router.beforeEach((to, from, next) => {
|
||||
const isAuthenticated = Boolean(localStorage.getItem(CONSTANT.STORE_TOKEN_NAME));
|
||||
if (!isAuthenticated && to.path !== '/login') {
|
||||
next('/login');
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
});
|
||||
|
||||
export default router
|
||||
@@ -0,0 +1,6 @@
|
||||
// store/index.js
|
||||
import { createPinia } from 'pinia';
|
||||
|
||||
const pinia = createPinia();
|
||||
|
||||
export default pinia;
|
||||
@@ -0,0 +1,23 @@
|
||||
|
||||
import { defineStore } from 'pinia';
|
||||
|
||||
export const usePageState = defineStore({
|
||||
id: 'pageState',
|
||||
state: () => ({
|
||||
isLogin: false, // 全局的登录状态
|
||||
Token: '', // 全局的登录状态
|
||||
isShowAddWayDialog: false,
|
||||
ShowDialogData: {}
|
||||
}),
|
||||
actions: {
|
||||
setIsLogin(state) {
|
||||
this.isLogin = state;
|
||||
},
|
||||
setToken(token) {
|
||||
this.Token = token;
|
||||
},
|
||||
setShowAddWayDialog(status) {
|
||||
this.isShowAddWayDialog = status;
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,27 @@
|
||||
import useClipboard from "vue-clipboard3";
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
function copyToClipboard(obj) {
|
||||
const { toClipboard } = useClipboard();
|
||||
try {
|
||||
if (!obj) {
|
||||
ElMessage({
|
||||
message: `复制内容为空`,
|
||||
type: 'error',
|
||||
})
|
||||
} else {
|
||||
toClipboard(obj);
|
||||
ElMessage({
|
||||
message: '复制成功',
|
||||
type: 'success',
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
ElMessage({
|
||||
message: `复制失败,${e}`,
|
||||
type: 'error',
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
export { copyToClipboard };
|
||||
@@ -0,0 +1,28 @@
|
||||
|
||||
|
||||
const gethttpOrigin = () => {
|
||||
return window.location.origin
|
||||
}
|
||||
|
||||
class ApiStrGenerate {
|
||||
|
||||
|
||||
static getCurlString(task_id, options) {
|
||||
let data = { task_id: task_id };
|
||||
data.text = 'Hello World!';
|
||||
if (options.html) {
|
||||
data.html = '<h1> Hello World! </h1>';
|
||||
}
|
||||
if (options.markdown) {
|
||||
data.html = '** Hello World! **';
|
||||
}
|
||||
let dataStr = JSON.stringify(data, null, 4)
|
||||
let example = `curl -X POST --location '${gethttpOrigin()}/api/v1/message/send' \\
|
||||
--header 'Accept: application/json' \\
|
||||
--data '${dataStr}'`;
|
||||
return example;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { ApiStrGenerate };
|
||||
@@ -0,0 +1,23 @@
|
||||
|
||||
<template>
|
||||
<el-popconfirm width="70px" confirm-button-text="确定" cancel-button-text="算了" icon-color="#626AEF" title="确定删除吗?"
|
||||
@confirm="handleDelete()">
|
||||
<template #reference>
|
||||
<el-button link size="small" type="danger">删除</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
methods: {
|
||||
handleDelete() {
|
||||
// 触发自定义事件
|
||||
this.$emit('customHandleDelete');
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
<template>
|
||||
<div class="inex-title-bar" v-if="pageState.isLogin">
|
||||
<el-menu :collapse="isCollapse" breakpoint="768px" mode="horizontal" @select="handleSelect()"
|
||||
:default-active="currActivate()" :ellipsis="false" :menu-width="'auto'">
|
||||
<el-menu-item index="0" :disabled="false">
|
||||
<img style="width: 60px" src="../../../public/titlelogo.svg" alt="Element logo" />
|
||||
</el-menu-item>
|
||||
|
||||
<div class="flex-grow" style="flex-grow: 1" />
|
||||
<div v-for="(item, index) in menuData" :key="index" class="banner-title">
|
||||
<router-link :to="{ path: item.path }">
|
||||
<el-menu-item :index="item.id">{{ item.title }}</el-menu-item>
|
||||
</router-link>
|
||||
</div>
|
||||
<el-button plain class="logout-btn" @click="clickLogout()">登出</el-button>
|
||||
</el-menu>
|
||||
</div>
|
||||
<router-view></router-view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, onMounted, reactive } from 'vue';
|
||||
import { usePageState } from '../../store/page_sate.js';
|
||||
import { CONSTANT } from '../../constant'
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const pageState = usePageState();
|
||||
const router = useRouter();
|
||||
const isCollapse = ref(false);
|
||||
const menuData = reactive([
|
||||
{
|
||||
id: '1',
|
||||
title: '发信日志',
|
||||
path: '/sendlogs',
|
||||
}, {
|
||||
id: '2',
|
||||
title: '发信任务',
|
||||
path: '/sendtasks',
|
||||
}, {
|
||||
id: '3',
|
||||
title: '发信渠道',
|
||||
path: '/sendways',
|
||||
}, {
|
||||
id: '4',
|
||||
title: '设置',
|
||||
path: '/settings',
|
||||
},
|
||||
]);
|
||||
|
||||
const checkIsLogin = () => {
|
||||
pageState.isLogin = Boolean(localStorage.getItem(CONSTANT.STORE_TOKEN_NAME));
|
||||
};
|
||||
|
||||
const clickLogout = () => {
|
||||
localStorage.removeItem(CONSTANT.STORE_TOKEN_NAME);
|
||||
console.log('clickLogout')
|
||||
router.push('/login', { replace: true }).then(() => { router.go() });
|
||||
};
|
||||
|
||||
const loadLocalToken = () => {
|
||||
pageState.setToken(localStorage.getItem(CONSTANT.STORE_TOKEN_NAME));
|
||||
}
|
||||
|
||||
const currActivate = () => {
|
||||
const cur_path = useRoute().path;
|
||||
let result = '1';
|
||||
menuData.forEach(element => {
|
||||
if (element.path == cur_path) {
|
||||
result = element.id;
|
||||
};
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
checkIsLogin();
|
||||
loadLocalToken();
|
||||
});
|
||||
|
||||
const handleSelect = () => { };
|
||||
|
||||
return {
|
||||
isCollapse, handleSelect, menuData, pageState, clickLogout, currActivate
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.el-menu-item ul {
|
||||
height: 15vh;
|
||||
}
|
||||
|
||||
.el-menu-item {
|
||||
width: 150px !important;
|
||||
font-size: 15px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
}
|
||||
|
||||
.el-menu-item li {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.logout-btn {
|
||||
margin: auto 40px auto 40px;
|
||||
float: right;
|
||||
/* background-color: #f7efee;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
border-radius: 5px;
|
||||
transition: background-color 0.3s ease; */
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,123 @@
|
||||
<template>
|
||||
<div class="login-center-container" v-if="!pageState.isLogin">
|
||||
|
||||
<div class="main-center-body">
|
||||
<div class="container">
|
||||
<img class="login-logo" src="../../../public/logo.svg" alt="login logo">
|
||||
<p class="desc">A Message Way Hosted Site</p>
|
||||
|
||||
<div class="login-block">
|
||||
<p class="login-text">账号:</p>
|
||||
<el-input style="width: 80%;" v-model="account" placeholder="请输入账号" />
|
||||
</div>
|
||||
|
||||
<div class="login-block">
|
||||
<p class="login-text">密码:</p>
|
||||
<el-input style="width: 80%;" v-model="passwd" type="password" placeholder="请输入密码" show-password />
|
||||
</div>
|
||||
|
||||
<div class="btn-area">
|
||||
<el-button id="custom-h2d-copy-button" type="success" @click="clickLogin()">登录</el-button>
|
||||
<el-button type="primary" @click="clickRegister()">注册</el-button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { toRefs, reactive, onMounted } from 'vue';
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { request } from '../../api/api'
|
||||
import { CONSTANT } from '../../constant'
|
||||
import { usePageState } from '../../store/page_sate';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const router = useRouter();
|
||||
|
||||
|
||||
const pageState = usePageState();
|
||||
const state = reactive({
|
||||
account: 'admin',
|
||||
passwd: '123456',
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
});
|
||||
|
||||
// 登录
|
||||
const clickLogin = async () => {
|
||||
const rspe = await request.post('/auth', { username: state.account, passwd: state.passwd });
|
||||
const rsp = rspe.data;
|
||||
if (rsp.code != 200) {
|
||||
ElMessage({ message: rsp.msg, type: 'error' });
|
||||
} else {
|
||||
pageState.setToken(rsp.data.token);
|
||||
pageState.setIsLogin(true);
|
||||
localStorage.setItem(CONSTANT.STORE_TOKEN_NAME, rsp.data.token);
|
||||
router.push('/sendlogs', { replace: true })
|
||||
}
|
||||
};
|
||||
|
||||
// 注册
|
||||
const clickRegister = () => {
|
||||
ElMessage({ message: `暂未开放注册!`, type: 'error' })
|
||||
|
||||
};
|
||||
|
||||
return { ...toRefs(state), clickLogin, clickRegister, pageState };
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
@import url('../../../src/assets/center_button_textarea.css');
|
||||
|
||||
.login-logo {
|
||||
height: 200px !important;
|
||||
}
|
||||
|
||||
.login-center-container {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.login-center-container img {
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.desc {
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
font-size: 25px;
|
||||
margin-bottom: 80px;
|
||||
/* color: rgb(64, 87, 45); */
|
||||
}
|
||||
|
||||
.login-text {
|
||||
font-size: 13px;
|
||||
/* width: 20px; */
|
||||
width: 20%;
|
||||
display: inline;
|
||||
/* display: flex; */
|
||||
/* justify-content: right; */
|
||||
/* align-items: center; */
|
||||
}
|
||||
|
||||
.login-block {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.btn-area {
|
||||
margin-top: 30px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
<template>
|
||||
<div class="main-center-body">
|
||||
<div class="container">
|
||||
<div class="search-input-sendways">
|
||||
<el-input v-model="search" size="small" placeholder="根据任务名字搜索相应日志" @change="filterFunc()" />
|
||||
</div>
|
||||
<hr />
|
||||
<div ref="refContainer">
|
||||
<el-table :data="tableData" empty-text="发信日志为空" :row-style="rowStyle()">
|
||||
<el-table-column label="ID" prop="id" width="100px" />
|
||||
<el-table-column label="任务名" prop="task_name" show-overflow-tooltip width="150px" />
|
||||
<el-table-column label="发信日志" prop="log">
|
||||
<template #default="scope">
|
||||
<el-tooltip placement="top">
|
||||
<template #content>
|
||||
<div v-html="TransHtml(scope.row.log)"></div>
|
||||
</template>
|
||||
<span class="log-overflow">{{ scope.row.log }}</span>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" prop="created_on" width="160px" />
|
||||
<el-table-column label="状态" prop="status" width="60px">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.status == 0" type="danger">失败</el-tag>
|
||||
<el-tag v-if="scope.row.status == 1" type="success">成功</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<div class="pagination-block">
|
||||
<el-pagination layout="prev, pager, next" :total="total" :page-size="pageSize" @current-change="handPageChange" />
|
||||
<el-text class="total-tip" size="small">共{{ total }}条</el-text>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script >
|
||||
import { reactive, toRefs, onMounted } from 'vue'
|
||||
import { request } from '../../../api/api'
|
||||
import { copyToClipboard } from '../../../util/clipboard.js';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import { CONSTANT } from '@/constant'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
},
|
||||
setup() {
|
||||
const router = useRoute();
|
||||
const state = reactive({
|
||||
search: '',
|
||||
optionValue: '',
|
||||
tableData: [],
|
||||
total: CONSTANT.TOTAL,
|
||||
pageSize: CONSTANT.PAGE_SIZE,
|
||||
currPage: CONSTANT.PAGE,
|
||||
});
|
||||
|
||||
const handleDelete = async (index, row) => {
|
||||
const rsp = await request.post('/sendways/delete', { id: row.id });
|
||||
if (rsp.status == 200) {
|
||||
state.tableData.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
const TransHtml = (raw) => {
|
||||
if (raw) {
|
||||
return raw.replace(/\n/g, '<br />')
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
const handPageChange = async (pageNum) => {
|
||||
state.currPage = pageNum;
|
||||
await queryListData(pageNum, state.pageSize);
|
||||
|
||||
}
|
||||
|
||||
const rowStyle = () => {
|
||||
return {
|
||||
'font-size': '13px',
|
||||
}
|
||||
}
|
||||
|
||||
const filterFunc = async () => {
|
||||
await queryListData(state.currPage, state.pageSize, state.search, state.optionValue);
|
||||
|
||||
}
|
||||
|
||||
const queryListData = async (page, size, name = '', taskid = '') => {
|
||||
let params = { page: page, size: size, name: name, taskid: taskid };
|
||||
const rsp = await request.get('/sendlogs/list', { params: params });
|
||||
state.tableData = await rsp.data.data.lists;
|
||||
state.total = await rsp.data.data.total;
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
state.search = router.query.name;
|
||||
await queryListData(1, state.pageSize, router.query.name, router.query.taskid);
|
||||
});
|
||||
|
||||
return {
|
||||
...toRefs(state), handleDelete, TransHtml,
|
||||
rowStyle, handPageChange, filterFunc, copyToClipboard
|
||||
};
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
hr {
|
||||
color: #FAFCFF;
|
||||
background-color: #FAFCFF;
|
||||
border-color: #FAFCFF;
|
||||
}
|
||||
|
||||
.container {
|
||||
background-color: white;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.1);
|
||||
max-width: 1000px;
|
||||
width: 100%;
|
||||
margin-top: -10vh;
|
||||
}
|
||||
|
||||
|
||||
.pagination-block {
|
||||
margin-top: 30px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.total-tip {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.search-input-sendways {
|
||||
width: 200px;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
|
||||
.log-overflow {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 1;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,261 @@
|
||||
<template>
|
||||
<el-dialog v-model="isShow" width="58%" :close-on-press-escape="false" :before-close="() => { }" :show-close="false">
|
||||
<template #header="">
|
||||
<el-text class="mx-1">添加发信任务(绑定关联实例)</el-text>
|
||||
<el-tooltip placement="top">
|
||||
<template #content>
|
||||
一个任务可以关联创建多个实例
|
||||
<br />
|
||||
选择不同的渠道,填写的实例信息也不一样
|
||||
<br />
|
||||
一个任务可以绑定一个实例,也可以绑定多个实例,多个实例意味着一个消息可以推送给多个消息渠道
|
||||
</template>
|
||||
<el-icon>
|
||||
<QuestionFilled />
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
|
||||
<div class="add-top">
|
||||
<el-input v-model="currTaskInput.taskName" placeholder="请输入任务名" size="small" class="taskNameInput"></el-input>
|
||||
</div>
|
||||
|
||||
<div class="dashed" />
|
||||
|
||||
<div class="ins-area">
|
||||
|
||||
<div class="ins-add">
|
||||
<el-input v-model="searchWayID" placeholder="请输入要添加的渠道id" size="small" @change="searchID"
|
||||
class="searchInput"></el-input>
|
||||
<el-button @click="searchID()" size="small" type="primary" style="margin-left: 20px;">查询</el-button>
|
||||
|
||||
<div class="store-area" v-if="isShowAddBox">
|
||||
|
||||
<div class="display-label">
|
||||
<el-text class="mx-1" size="small">渠道名:{{ currWayTmp.name }}</el-text><br />
|
||||
<el-text class="mx-1" size="small">渠道类型:{{ currWayTmp.type }}</el-text> <br />
|
||||
<el-text class="mx-1" size="small">渠道创建时间:{{ currWayTmp.created_on }}</el-text><br />
|
||||
</div>
|
||||
|
||||
<el-radio-group v-model="currInsInput.content_type" class="ml-4">
|
||||
<el-radio label="text" size="small">text</el-radio>
|
||||
<el-radio label="html" size="small">html</el-radio>
|
||||
</el-radio-group>
|
||||
|
||||
<div>
|
||||
<el-input v-model="currInsInput.toAccount" placeholder="目的邮箱账号(发给谁)" size="small"
|
||||
style="width: 200px; margin: 10px 40px 5px 0;" class="searchInput"></el-input>
|
||||
<el-input v-model="currInsInput.title" placeholder="邮箱标题" size="small"
|
||||
style="width: 200px; margin: 0px 40px 5px 0;" class="searchInput"></el-input>
|
||||
<el-button @click="clickStore()" size="small" style="width: 200px">暂存</el-button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ins-table">
|
||||
<el-table :data="insTableData" empty-text="发信实例为空" style="width: 100%" max-height="300"
|
||||
:row-style="insRowStyle()">
|
||||
<el-table-column prop="way_name" label="渠道名" />
|
||||
<el-table-column prop="way_type" label="渠道+内容类型">
|
||||
<template #default="scope">
|
||||
{{ scope.row.way_type }}+{{ scope.row.content_type }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="way_type" label="额外信息">
|
||||
<template #default="scope">
|
||||
{{ formatExtraInfo(scope) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column fixed="right" label="操作" width="60px">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" size="small" @click.prevent="insTableData.splice(scope.$index, 1)">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="handleCancer()" size="small">取消</el-button>
|
||||
<el-button type="primary" size="small" @click="handleSubmit()">
|
||||
确定添加
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, onMounted, watch, reactive, toRefs } from 'vue';
|
||||
import { _ } from 'lodash';
|
||||
import { QuestionFilled } from '@element-plus/icons-vue'
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { usePageState } from '../../../store/page_sate.js';
|
||||
import { request } from '../../../api/api'
|
||||
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
QuestionFilled,
|
||||
},
|
||||
props: {
|
||||
componentName: String
|
||||
},
|
||||
setup(props) {
|
||||
const pageState = usePageState();
|
||||
const state = reactive({
|
||||
insTableData: [],
|
||||
isShow: false,
|
||||
isShowAddBox: false,
|
||||
searchWayID: '',
|
||||
currWayTmp: {},
|
||||
currInsInput: {
|
||||
content_type: 'text'
|
||||
},
|
||||
currTaskInput: {
|
||||
taskName: '',
|
||||
taskId: uuidv4(),
|
||||
},
|
||||
});
|
||||
|
||||
watch(pageState.ShowDialogData, (newValue, oldValue) => {
|
||||
if (newValue[props.componentName]) {
|
||||
state.isShow = pageState.ShowDialogData[props.componentName].isShow;
|
||||
resetPageInitData();
|
||||
}
|
||||
});
|
||||
|
||||
const handleCancer = () => {
|
||||
if (pageState.ShowDialogData[props.componentName]) {
|
||||
pageState.ShowDialogData[props.componentName].isShow = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 页面每次弹出,重置数据
|
||||
const resetPageInitData = () => {
|
||||
state.insTableData = [];
|
||||
state.currInsInput = { content_type: 'text' };
|
||||
state.currWayTmp = {};
|
||||
state.searchWayID = '';
|
||||
state.isShowAddBox = false;
|
||||
state.currTaskInput = {
|
||||
taskName: '',
|
||||
taskId: uuidv4(),
|
||||
}
|
||||
}
|
||||
|
||||
const insRowStyle = () => {
|
||||
return {
|
||||
'font-size': '12px'
|
||||
}
|
||||
}
|
||||
|
||||
// 点击暂存实例
|
||||
const clickStore = () => {
|
||||
let insData = {
|
||||
id: uuidv4(),
|
||||
task_id: state.currTaskInput.taskId,
|
||||
way_id: state.currWayTmp.id,
|
||||
way_type: state.currWayTmp.type,
|
||||
way_name: state.currWayTmp.name,
|
||||
content_type: state.currInsInput.content_type,
|
||||
config: JSON.stringify({ to_account: state.currInsInput.toAccount, title: state.currInsInput.title })
|
||||
};
|
||||
state.insTableData.push(insData);
|
||||
}
|
||||
|
||||
const searchID = async () => {
|
||||
const rsp = await request.get('/sendways/get', { params: { id: state.searchWayID } });
|
||||
let data = await rsp.data;
|
||||
state.isShowAddBox = Boolean(data.data);
|
||||
if (data.data) {
|
||||
state.currWayTmp = data.data;
|
||||
}
|
||||
}
|
||||
|
||||
const getFinalData = () => {
|
||||
let postData = { id: state.currTaskInput.taskId, name: state.currTaskInput.taskName }
|
||||
postData.ins_data = state.insTableData
|
||||
return postData
|
||||
}
|
||||
|
||||
|
||||
|
||||
const handleSubmit = async () => {
|
||||
let postData = getFinalData();
|
||||
const rsp = await request.post('/sendtasks/ins/addmany', postData);
|
||||
if (await rsp.data.code == 200) {
|
||||
handleCancer();
|
||||
window.location.reload();
|
||||
}
|
||||
}
|
||||
|
||||
const formatExtraInfo = (scope) => {
|
||||
if (!scope.row.config) {
|
||||
return ""
|
||||
}
|
||||
let config = JSON.parse(scope.row.config)
|
||||
let info = `发送账号:${config.to_account}\n标题:${config.title}`
|
||||
return info
|
||||
}
|
||||
|
||||
return {
|
||||
...toRefs(state), handleCancer, handleSubmit,
|
||||
searchID, formatExtraInfo,
|
||||
clickStore, insRowStyle
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
::v-deep(.el-table .cell) {
|
||||
white-space: pre-line !important;
|
||||
}
|
||||
|
||||
.dashed {
|
||||
border-top: 2px dashed var(--el-border-color);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.ins-area {
|
||||
display: flex;
|
||||
max-width: 900px;
|
||||
|
||||
}
|
||||
|
||||
.add-top {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.ins-add {
|
||||
flex: 45%;
|
||||
}
|
||||
|
||||
.ins-table {
|
||||
flex: 55%;
|
||||
}
|
||||
|
||||
.searchInput {
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
.wayTitleInput {
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
.taskNameInput {
|
||||
/* width: 80%; */
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
.display-label {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,263 @@
|
||||
<template>
|
||||
<el-dialog v-model="isShow" width="58%" :close-on-press-escape="false" :before-close="() => { }" :show-close="false">
|
||||
<template #header="">
|
||||
<el-text class="mx-1">编辑发信任务</el-text>
|
||||
<el-tooltip placement="top">
|
||||
</el-tooltip>
|
||||
</template>
|
||||
|
||||
|
||||
<div class="dashed" />
|
||||
|
||||
<div class="ins-area">
|
||||
|
||||
<div class="ins-add">
|
||||
<el-input v-model="searchWayID" placeholder="请输入要添加的渠道id" size="small" @change="searchID"
|
||||
class="searchInput"></el-input>
|
||||
<el-button @click="searchID()" size="small" type="primary" style="margin-left: 20px;">查询</el-button>
|
||||
|
||||
<div class="store-area" v-if="isShowAddBox">
|
||||
|
||||
<div class="display-label">
|
||||
<el-text class="mx-1" size="small">渠道名:{{ currWayTmp.name }}</el-text><br />
|
||||
<el-text class="mx-1" size="small">渠道类型:{{ currWayTmp.type }}</el-text> <br />
|
||||
<el-text class="mx-1" size="small">渠道创建时间:{{ currWayTmp.created_on }}</el-text><br />
|
||||
</div>
|
||||
|
||||
<el-radio-group v-model="currInsInput.content_type" class="ml-4">
|
||||
<el-radio label="text" size="small">text</el-radio>
|
||||
<el-radio label="html" size="small">html</el-radio>
|
||||
</el-radio-group>
|
||||
<div>
|
||||
<el-input v-model="currInsInput.toAccount" placeholder="目的邮箱账号(发给谁)" size="small"
|
||||
style="width: 200px; margin: 10px 40px 5px 0;" class="searchInput"></el-input>
|
||||
<el-input v-model="currInsInput.title" placeholder="邮箱标题" size="small"
|
||||
style="width: 200px; margin: 0px 40px 5px 0;" class="searchInput"></el-input>
|
||||
</div>
|
||||
<div>
|
||||
<el-button @click="handleAddSubmit()" size="small" style="width: 200px">添加</el-button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ins-table">
|
||||
<el-table :data="insTableData" empty-text="发信实例为空" style="width: 100%" max-height="300"
|
||||
:row-style="insRowStyle()">
|
||||
<el-table-column prop="way_name" label="渠道名" />
|
||||
<el-table-column prop="way_type" label="渠道+内容类型">
|
||||
<template #default="scope">
|
||||
{{ scope.row.way_type }}+{{ scope.row.content_type }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="way_type" label="额外信息">
|
||||
<template #default="scope">
|
||||
{{ formatExtraInfo(scope) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column fixed="right" label="操作" width="60px">
|
||||
<template #default="scope">
|
||||
<tableDeleteButton @customHandleDelete="handleDelete(scope.$index, scope.row)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="handleCancer()" size="small">取消</el-button>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, onMounted, watch, reactive, toRefs } from 'vue';
|
||||
import { _ } from 'lodash';
|
||||
import { QuestionFilled } from '@element-plus/icons-vue'
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { usePageState } from '@/store/page_sate.js';
|
||||
import { request } from '@/api/api'
|
||||
import tableDeleteButton from '@/views/common/tableDeleteButton.vue'
|
||||
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
QuestionFilled,
|
||||
tableDeleteButton,
|
||||
},
|
||||
props: {
|
||||
componentName: String
|
||||
},
|
||||
setup(props) {
|
||||
const pageState = usePageState();
|
||||
const state = reactive({
|
||||
insTableData: [],
|
||||
isShow: false,
|
||||
isShowAddBox: false,
|
||||
searchWayID: '',
|
||||
currWayTmp: {},
|
||||
currInsInput: {},
|
||||
currTaskInput: {
|
||||
taskName: '',
|
||||
taskId: '',
|
||||
},
|
||||
});
|
||||
|
||||
watch(pageState.ShowDialogData, (newValue, oldValue) => {
|
||||
if (newValue[props.componentName]) {
|
||||
state.isShow = pageState.ShowDialogData[props.componentName].isShow;
|
||||
resetPageInitData();
|
||||
state.currTaskInput.taskId = pageState.ShowDialogData[props.componentName].rowData.id;
|
||||
queryListData();
|
||||
}
|
||||
});
|
||||
|
||||
const queryListData = async () => {
|
||||
let params = { id: state.currTaskInput.taskId };
|
||||
const rsp = await request.get('/sendtasks/ins/gettask', { params: params });
|
||||
state.insTableData = await rsp.data.data.ins_data;
|
||||
state.total = await rsp.data.data.total;
|
||||
}
|
||||
|
||||
const handleCancer = () => {
|
||||
if (pageState.ShowDialogData[props.componentName]) {
|
||||
pageState.ShowDialogData[props.componentName].isShow = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 页面每次弹出,重置数据
|
||||
const resetPageInitData = () => {
|
||||
state.insTableData = [];
|
||||
state.currInsInput = {};
|
||||
state.currWayTmp = {};
|
||||
state.searchWayID = '';
|
||||
state.isShowAddBox = false;
|
||||
state.currTaskInput = {
|
||||
taskName: '',
|
||||
// taskId: uuidv4(),
|
||||
}
|
||||
}
|
||||
|
||||
const insRowStyle = () => {
|
||||
return {
|
||||
'font-size': '12px'
|
||||
}
|
||||
}
|
||||
|
||||
const handleDelete = async (index, row) => {
|
||||
const rsp = await request.post('/sendtasks/ins/delete', { id: row.id });
|
||||
if (rsp.status == 200) {
|
||||
state.insTableData.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// // 点击暂存实例
|
||||
// const clickStore = () => {
|
||||
// let insData = {
|
||||
// id: uuidv4(),
|
||||
// task_id: state.currTaskInput.taskId,
|
||||
// way_id: state.currWayTmp.id,
|
||||
// way_type: state.currWayTmp.type,
|
||||
// way_name: state.currWayTmp.name,
|
||||
// content_type: state.currInsInput.content_type
|
||||
// };
|
||||
|
||||
// }
|
||||
|
||||
const formatExtraInfo = (scope) => {
|
||||
if (!scope.row.config) {
|
||||
return ""
|
||||
}
|
||||
let config = JSON.parse(scope.row.config)
|
||||
let info = `发送账号:${config.to_account}\n标题:${config.title}`
|
||||
return info
|
||||
}
|
||||
|
||||
const searchID = async () => {
|
||||
const rsp = await request.get('/sendways/get', { params: { id: state.searchWayID } });
|
||||
let data = await rsp.data;
|
||||
state.isShowAddBox = Boolean(data.data);
|
||||
if (data.data) {
|
||||
state.currWayTmp = data.data;
|
||||
}
|
||||
}
|
||||
|
||||
const getFinalData = () => {
|
||||
let postData = {
|
||||
id: uuidv4(),
|
||||
task_id: state.currTaskInput.taskId,
|
||||
way_id: state.currWayTmp.id,
|
||||
way_type: state.currWayTmp.type,
|
||||
way_name: state.currWayTmp.name,
|
||||
content_type: state.currInsInput.content_type,
|
||||
config: JSON.stringify({ to_account: state.currInsInput.toAccount, title: state.currInsInput.title })
|
||||
}
|
||||
return postData
|
||||
}
|
||||
|
||||
|
||||
const handleAddSubmit = async () => {
|
||||
let postData = getFinalData();
|
||||
const rsp = await request.post('/sendtasks/ins/addone', postData);
|
||||
if (await rsp.data.code == 200) {
|
||||
state.insTableData.push(postData);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...toRefs(state), handleCancer, handleAddSubmit,
|
||||
searchID, handleDelete, insRowStyle, formatExtraInfo
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
::v-deep(.el-table .cell) {
|
||||
white-space: pre-line !important;
|
||||
}
|
||||
|
||||
.dashed {
|
||||
border-top: 2px dashed var(--el-border-color);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.ins-area {
|
||||
display: flex;
|
||||
max-width: 900px;
|
||||
|
||||
}
|
||||
|
||||
/* .add-top {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
*/
|
||||
.ins-add {
|
||||
flex: 45%;
|
||||
}
|
||||
|
||||
.ins-table {
|
||||
flex: 55%;
|
||||
}
|
||||
|
||||
.searchInput {
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
.wayTitleInput {
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
.taskNameInput {
|
||||
/* width: 80%; */
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
.display-label {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,204 @@
|
||||
<template>
|
||||
<div class="main-center-body">
|
||||
<div class="container">
|
||||
|
||||
<div class="search-input-sendways">
|
||||
<el-input v-model="search" size="small" placeholder="搜索" @change="filterFunc()" />
|
||||
</div>
|
||||
|
||||
<div class="search-box">
|
||||
<el-button size="small" type="primary" @click="clickAdd">新增任务</el-button>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<div ref="refContainer">
|
||||
<el-table :data="tableData" empty-text="发信任务为空" :row-style="rowStyle()">
|
||||
<el-table-column label="ID" prop="id" width="320px" />
|
||||
<el-table-column label="任务名" prop="name" />
|
||||
<el-table-column label="创建时间" prop="created_on" />
|
||||
<el-table-column fixed="right" label="操作" width="190px">
|
||||
<template #default="scope">
|
||||
<el-button link size="small" type="primary" @click="handleViewAPI(scope.$index, scope.row)">接口</el-button>
|
||||
<el-button link size="small" type="primary" @click="handleViewLogs(scope.$index, scope.row)">日志</el-button>
|
||||
<el-button link size="small" type="primary" @click="handleEdit(scope.$index, scope.row)">编辑</el-button>
|
||||
<tableDeleteButton @customHandleDelete="handleDelete(scope.$index, scope.row)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<div class="pagination-block">
|
||||
<el-pagination layout="prev, pager, next" :total="total" :page-size="pageSize" @current-change="handPageChange" />
|
||||
<el-text class="total-tip" size="small">共{{ total }}条</el-text>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<addTaskPopUpComponent :componentName="addTaskPopUpComponentName" />
|
||||
<editTaskPopUpComponent :componentName="editTaskPopUpComponentName" />
|
||||
<viewTaskAPIPopUpComponent :componentName="viewTaskAPIPopUpComponentName" />
|
||||
</template>
|
||||
|
||||
<script >
|
||||
import { computed, ref, reactive, toRefs, onMounted } from 'vue'
|
||||
// import { InfoFilled } from '@element-plus/icons-vue'
|
||||
import { usePageState } from '../../../store/page_sate';
|
||||
import { request } from '../../../api/api'
|
||||
import addTaskPopUpComponent from './addTaskPopUp.vue'
|
||||
import editTaskPopUpComponent from './editTaskPopUp.vue'
|
||||
import viewTaskAPIPopUpComponent from './viewTaskAPIPopUp.vue'
|
||||
import tableDeleteButton from '@/views/common/tableDeleteButton.vue'
|
||||
import { useRouter } from 'vue-router';
|
||||
import { CONSTANT } from '@/constant'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
addTaskPopUpComponent,
|
||||
editTaskPopUpComponent,
|
||||
tableDeleteButton,
|
||||
viewTaskAPIPopUpComponent,
|
||||
// InfoFilled,
|
||||
},
|
||||
setup() {
|
||||
const pageState = usePageState();
|
||||
const router = useRouter();
|
||||
|
||||
const state = reactive({
|
||||
addTaskPopUpComponentName: 'addTaskPopUpComponentName',
|
||||
editTaskPopUpComponentName: 'editTaskPopUpComponentName',
|
||||
viewTaskAPIPopUpComponentName: 'viewTaskAPIPopUpComponentName',
|
||||
search: '',
|
||||
optionValue: '',
|
||||
dialogFormVisible: false,
|
||||
// confirmBtnVisible: false,
|
||||
tableData: [],
|
||||
total: CONSTANT.TOTAL,
|
||||
pageSize: CONSTANT.PAGE_SIZE,
|
||||
currPage: CONSTANT.PAGE,
|
||||
displayCols: [
|
||||
{ 'col': 'id', 'label': '任务ID' },
|
||||
{ 'col': 'name', 'label': '任务名' },
|
||||
{ 'col': 'type', 'label': '发信任务' },
|
||||
{ 'col': 'created_on', 'label': '创建时间' },
|
||||
],
|
||||
options: [
|
||||
{ label: '邮箱', value: 'Email' },
|
||||
{ label: '钉钉', value: 'Dtalk' }
|
||||
]
|
||||
});
|
||||
|
||||
const handleEdit = (index, row) => {
|
||||
console.log(index, row)
|
||||
let name = state.editTaskPopUpComponentName;
|
||||
pageState.ShowDialogData[name] = {};
|
||||
pageState.ShowDialogData[name].isShow = true;
|
||||
pageState.ShowDialogData[name].rowData = row;
|
||||
}
|
||||
|
||||
const handleDelete = async (index, row) => {
|
||||
const rsp = await request.post('/sendtasks/delete', { id: row.id });
|
||||
if (rsp.status == 200) {
|
||||
state.tableData.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
const handleViewAPI = (index, row) => {
|
||||
console.log(index, row)
|
||||
let name = state.viewTaskAPIPopUpComponentName;
|
||||
pageState.ShowDialogData[name] = {};
|
||||
pageState.ShowDialogData[name].isShow = true;
|
||||
pageState.ShowDialogData[name].rowData = row;
|
||||
}
|
||||
|
||||
const handPageChange = async (pageNum) => {
|
||||
state.currPage = pageNum;
|
||||
await queryListData(pageNum, state.pageSize);
|
||||
|
||||
}
|
||||
const rowStyle = () => {
|
||||
return {
|
||||
'font-size': '13px',
|
||||
}
|
||||
}
|
||||
|
||||
const handleViewLogs = (index, row) => {
|
||||
router.push('/sendlogs?name=' + row.name, { replace: true });
|
||||
}
|
||||
|
||||
const filterFunc = async () => {
|
||||
await queryListData(state.currPage, state.pageSize, state.search, state.optionValue);
|
||||
|
||||
}
|
||||
const clickAdd = () => {
|
||||
pageState.ShowDialogData[state.addTaskPopUpComponentName] = {};
|
||||
pageState.ShowDialogData[state.addTaskPopUpComponentName].isShow = true;
|
||||
}
|
||||
|
||||
const queryListData = async (page, size, name = '') => {
|
||||
let params = { page: page, size: size, name: name };
|
||||
const rsp = await request.get('/sendtasks/list', { params: params });
|
||||
state.tableData = await rsp.data.data.lists;
|
||||
state.total = await rsp.data.data.total;
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await queryListData(1, state.pageSize);
|
||||
});
|
||||
|
||||
|
||||
return {
|
||||
...toRefs(state), handleEdit, handleDelete, handleViewAPI, handleViewLogs,
|
||||
clickAdd, rowStyle, handPageChange, filterFunc
|
||||
};
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
hr {
|
||||
color: #FAFCFF;
|
||||
background-color: #FAFCFF;
|
||||
border-color: #FAFCFF;
|
||||
}
|
||||
|
||||
.container {
|
||||
background-color: white;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.1);
|
||||
max-width: 1000px;
|
||||
width: 100%;
|
||||
margin-top: -10vh;
|
||||
}
|
||||
|
||||
.pagination-block {
|
||||
margin-top: 30px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.total-tip {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
float: right;
|
||||
}
|
||||
|
||||
:global(.select .el-input__inner) {
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.search-input-sendways {
|
||||
/* margin-left: 40px; */
|
||||
width: 150px;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.op-col {
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,142 @@
|
||||
<template>
|
||||
<el-dialog v-model="isShow" width="58%" :close-on-press-escape="false" :before-close="() => { }" :show-close="false">
|
||||
<template #header="">
|
||||
<el-text class="mx-1">查看接入API</el-text>
|
||||
<el-tooltip placement="top">
|
||||
<template #content>
|
||||
一个任务可能关联多个不同渠道的实例
|
||||
<br />
|
||||
实例的内容类型大体上可以可以分为text、html、markdown
|
||||
<br />
|
||||
发送的消息会优先现在相应的类型消息进行发送,如果没有,将使用传的text消息进行发送
|
||||
<br />
|
||||
*text节点必传
|
||||
</template>
|
||||
<el-icon>
|
||||
<QuestionFilled />
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
|
||||
<el-tabs v-model="activeName" @tab-click="renderApiString">
|
||||
<el-tab-pane label="curl" name="curl">
|
||||
<pre><code class="language-shell line-numbers">{{ currCode }}</code></pre>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="handleCancer()" size="small">取消</el-button>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, onMounted, watch, reactive, toRefs, onUpdated } from 'vue';
|
||||
import { _ } from 'lodash';
|
||||
import { usePageState } from '@/store/page_sate.js';
|
||||
import Prism from "prismjs";
|
||||
import { ApiStrGenerate } from "@/util/viewApi.js";
|
||||
import { QuestionFilled } from '@element-plus/icons-vue'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
QuestionFilled,
|
||||
},
|
||||
props: {
|
||||
componentName: String,
|
||||
},
|
||||
setup(props) {
|
||||
const pageState = usePageState();
|
||||
const state = reactive({
|
||||
isShow: false,
|
||||
currCode: '',
|
||||
activeName: 'curl',
|
||||
language: "python",
|
||||
});
|
||||
|
||||
watch(pageState.ShowDialogData, (newValue, oldValue) => {
|
||||
if (newValue[props.componentName]) {
|
||||
state.isShow = pageState.ShowDialogData[props.componentName].isShow;
|
||||
renderApiString();
|
||||
}
|
||||
});
|
||||
|
||||
const handleCancer = () => {
|
||||
if (pageState.ShowDialogData[props.componentName]) {
|
||||
pageState.ShowDialogData[props.componentName].isShow = false;
|
||||
}
|
||||
}
|
||||
|
||||
const renderApiString = () => {
|
||||
console.log('renderApiString', state.activeName)
|
||||
let task_id = pageState.ShowDialogData[props.componentName].rowData.id;
|
||||
if (state.activeName == 'curl') {
|
||||
state.currCode = ApiStrGenerate.getCurlString(task_id, {});
|
||||
}
|
||||
setTimeout(() => {
|
||||
Prism.highlightAll()
|
||||
}, 100)
|
||||
}
|
||||
|
||||
return {
|
||||
...toRefs(state), handleCancer, renderApiString
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.language-javascript {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
|
||||
/* pre {
|
||||
overflow: hidden !important;
|
||||
code{
|
||||
display: inline-block;
|
||||
padding-bottom: 20px;
|
||||
position: relative;
|
||||
top: 20px;
|
||||
}
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
background: red;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
top: 10px;
|
||||
left: 15px;
|
||||
transform: translate(-50%);
|
||||
}
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
background: sandybrown;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
top: 10px;
|
||||
left: 30px;
|
||||
transform: translate(-50%);
|
||||
}
|
||||
code:first-child{
|
||||
&::after{
|
||||
content: "";
|
||||
position: absolute;
|
||||
background: limegreen;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
top: -24px;
|
||||
left: -7px;
|
||||
transform: translate(-50%);
|
||||
}
|
||||
}
|
||||
} */
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
<template>
|
||||
<el-dialog v-model="isShow" width="500px" :close-on-press-escape="false" :before-close="() => { }" :show-close="false">
|
||||
<template #header="">
|
||||
<el-text class="mx-1">新增发信渠道</el-text>
|
||||
</template>
|
||||
|
||||
<el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick">
|
||||
<el-form label-width="100px" v-for="item in waysLabelData">
|
||||
<el-tab-pane :label="item.label" :name="item.type">
|
||||
<el-form-item :label="one.subLabel" v-for="one in item.inputs">
|
||||
<el-input v-model="one.value" />
|
||||
</el-form-item>
|
||||
</el-tab-pane>
|
||||
</el-form>
|
||||
</el-tabs>
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="testMseeageDialogVisible = true" size="small">测试发信</el-button>
|
||||
<el-button @click="handleCancer()" size="small">取消</el-button>
|
||||
<el-button type="primary" size="small" @click="handleSubmit()">
|
||||
确定添加
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="testMseeageDialogVisible" width="50%" align-center>
|
||||
<span>将发送一条测试信息,将注意查收</span>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button size="small" @click="testMseeageDialogVisible = false">取消</el-button>
|
||||
<el-button size="small" type="primary" @click="(testMseeageDialogVisible = false) || handleTest()">
|
||||
确定发送
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, onMounted, watch, reactive, toRefs } from 'vue';
|
||||
import { usePageState } from '../../../store/page_sate.js';
|
||||
import { request } from '../../../api/api'
|
||||
import { CONSTANT } from '../../../constant'
|
||||
import { _ } from 'lodash';
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
componentName: String
|
||||
},
|
||||
setup(props) {
|
||||
const pageState = usePageState();
|
||||
const state = reactive({
|
||||
isShow: false,
|
||||
testMseeageDialogVisible: false,
|
||||
activeName: "Email",
|
||||
waysLabelData: _.cloneDeep(CONSTANT.WAYS_DATA),
|
||||
|
||||
});
|
||||
|
||||
watch(pageState.ShowDialogData, (newValue, oldValue) => {
|
||||
if (newValue[props.componentName]) {
|
||||
state.isShow = pageState.ShowDialogData[props.componentName].isShow;
|
||||
}
|
||||
});
|
||||
|
||||
const handleCancer = () => {
|
||||
if (pageState.ShowDialogData[props.componentName]) {
|
||||
pageState.ShowDialogData[props.componentName].isShow = false;
|
||||
}
|
||||
}
|
||||
|
||||
const handleClick = () => {
|
||||
}
|
||||
|
||||
const getInputData = (type) => {
|
||||
for (const element of state.waysLabelData) {
|
||||
if (element.type == type) {
|
||||
const data = {};
|
||||
for (const item of element.inputs) {
|
||||
data[item.col] = item.value;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
const getFinalData = () => {
|
||||
const inputData = getInputData(state.activeName);
|
||||
const { name, ...nameObject } = inputData;
|
||||
let postData = { name: name }
|
||||
const { name: _, ...auth } = inputData;
|
||||
if (state.activeName == 'Email') {
|
||||
auth.port = Number(auth.port)
|
||||
};
|
||||
postData.auth = JSON.stringify(auth);
|
||||
postData.type = state.activeName;
|
||||
return postData
|
||||
}
|
||||
|
||||
const handleTest = async () => {
|
||||
let postData = getFinalData();
|
||||
const rsp = await request.post('/sendways/test', postData).data;
|
||||
if (await rsp.data.code == 200) {
|
||||
ElMessage({ message: response.data.msg, type: 'success' })
|
||||
}
|
||||
}
|
||||
|
||||
const handleSubmit = async () => {
|
||||
let postData = getFinalData();
|
||||
const rsp = await request.post('/sendways/add', postData).data;
|
||||
if (rsp.code == 200) {
|
||||
handleCancer();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
...toRefs(state), handleCancer, handleSubmit, handleClick, handleTest
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* :global(.el-dialog) {
|
||||
width: 500px;
|
||||
|
||||
} */
|
||||
/* :global(.el-dialog__title) {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
:global(.el-dialog label) {
|
||||
font-size: 13px;
|
||||
} */
|
||||
</style>
|
||||
@@ -0,0 +1,171 @@
|
||||
<template>
|
||||
<el-dialog v-model="isShow" width="500px" :close-on-press-escape="false" :before-close="() => { }" :show-close="false">
|
||||
<template #header="">
|
||||
<el-text class="mx-1">编辑发信渠道</el-text>
|
||||
</template>
|
||||
|
||||
<el-tabs class="demo-tabs" @tab-click="handleClick">
|
||||
|
||||
<el-form label-width="100px" v-for="item in waysLabelData">
|
||||
<!-- <el-tab-pane :label="item.label" :name="item.type"> -->
|
||||
<el-form-item :label="one.subLabel" v-for="one in item.inputs">
|
||||
<el-input v-model="one.value" />
|
||||
</el-form-item>
|
||||
<!-- </el-tab-pane> -->
|
||||
</el-form>
|
||||
|
||||
</el-tabs>
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="testMseeageDialogVisible = true" size="small">测试发信</el-button>
|
||||
<el-button @click="handleCancer()" size="small">取消</el-button>
|
||||
<el-button type="primary" size="small" @click="handleSubmit()">
|
||||
确定编辑
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="testMseeageDialogVisible" width="50%" align-center>
|
||||
<span>将发送一条测试信息,将注意查收</span>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button size="small" @click="testMseeageDialogVisible = false">取消</el-button>
|
||||
<el-button size="small" type="primary" @click="(testMseeageDialogVisible = false) || handleTest()">
|
||||
确定发送
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, onMounted, watch, reactive, toRefs } from 'vue';
|
||||
import { usePageState } from '../../../store/page_sate.js';
|
||||
import { request } from '../../../api/api'
|
||||
import { CONSTANT } from '../../../constant'
|
||||
import { _ } from 'lodash';
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
componentName: String
|
||||
},
|
||||
setup(props) {
|
||||
const pageState = usePageState();
|
||||
const state = reactive({
|
||||
isShow: false,
|
||||
testMseeageDialogVisible: false,
|
||||
waysLabelData: [],
|
||||
editData: {},
|
||||
|
||||
});
|
||||
|
||||
const dealDisplayData = () => {
|
||||
|
||||
}
|
||||
|
||||
// 监测父页面传过来的数据
|
||||
watch(pageState.ShowDialogData, (newValue, oldValue) => {
|
||||
if (newValue[props.componentName]) {
|
||||
// 弹出编辑框
|
||||
state.isShow = pageState.ShowDialogData[props.componentName].isShow;
|
||||
|
||||
// 展示编辑框
|
||||
if (newValue[props.componentName].rowData) {
|
||||
const row = pageState.ShowDialogData[props.componentName].rowData;
|
||||
let nowData = [];
|
||||
_.cloneDeep(CONSTANT.WAYS_DATA).forEach(element => {
|
||||
if (element.type == row.type) {
|
||||
// 填充输入框的值
|
||||
state.editData = row;
|
||||
element.inputs.forEach(one => {
|
||||
let newRow = Object.assign(row, JSON.parse(row.auth));
|
||||
if (newRow[one.col]) {
|
||||
one.value = newRow[one.col];
|
||||
};
|
||||
});
|
||||
nowData.push(element);
|
||||
};
|
||||
});
|
||||
state.waysLabelData = nowData;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
const handleCancer = () => {
|
||||
if (pageState.ShowDialogData[props.componentName]) {
|
||||
pageState.ShowDialogData[props.componentName].isShow = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const handleClick = () => {
|
||||
}
|
||||
|
||||
const getEditData = (type) => {
|
||||
for (const element of state.waysLabelData) {
|
||||
// if (element.type == type) {
|
||||
const data = {};
|
||||
for (const item of element.inputs) {
|
||||
data[item.col] = item.value;
|
||||
}
|
||||
return data;
|
||||
// }
|
||||
}
|
||||
return {};
|
||||
}
|
||||
const getFinalData = () => {
|
||||
const editData = getEditData(state.waysLabelData);
|
||||
const { name, ...nameObject } = editData;
|
||||
let postData = { name: name }
|
||||
const { name: _, ...auth } = editData;
|
||||
if (state.editData.type == 'Email') {
|
||||
auth.port = Number(auth.port)
|
||||
};
|
||||
postData.auth = JSON.stringify(auth);
|
||||
postData.type = state.editData.type;
|
||||
postData.id = state.editData.id;
|
||||
return postData
|
||||
}
|
||||
|
||||
const handleTest = async () => {
|
||||
let postData = getFinalData();
|
||||
const rsp = await request.post('/sendways/test', postData);
|
||||
if (await rsp.data.code == 200) {
|
||||
ElMessage({ message: response.data.msg, type: 'success' })
|
||||
}
|
||||
}
|
||||
|
||||
const handleSubmit = async () => {
|
||||
let postData = getFinalData();
|
||||
const rsp = await request.post('/sendways/edit', postData);
|
||||
// console.log('edit res',rsp)
|
||||
if (await rsp.data.code == 200) {
|
||||
handleCancer();
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...toRefs(state), handleCancer, handleSubmit, handleClick, handleTest
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* :global(.el-dialog) {
|
||||
width: 500px;
|
||||
|
||||
} */
|
||||
/* :global(.el-dialog__title) {
|
||||
font-size: 14px;
|
||||
} */
|
||||
|
||||
/* :global(.el-dialog label) {
|
||||
font-size: 13px;
|
||||
} */
|
||||
</style>
|
||||
@@ -0,0 +1,218 @@
|
||||
<template>
|
||||
<div class="main-center-body">
|
||||
<div class="container">
|
||||
<el-select v-model="optionValue" class="select" placeholder="渠道筛选" size="small" @change="filterFunc()">
|
||||
<el-option v-for="item in options" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
|
||||
<div class="search-input-sendways">
|
||||
<el-input v-model="search" size="small" placeholder="搜索" @change="filterFunc()" />
|
||||
</div>
|
||||
|
||||
<!-- <td class="line">
|
||||
<div />
|
||||
</td> -->
|
||||
<div class="search-box">
|
||||
<el-button size="small" type="primary" @click="clickAdd">新增渠道</el-button>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
<div ref="refContainer">
|
||||
<el-table :data="tableData" empty-text="发信渠道为空" :row-style="rowStyle()">
|
||||
<el-table-column label="ID" width="320px">
|
||||
<template #default="scope">
|
||||
{{ scope.row.id }}
|
||||
<el-icon>
|
||||
<CopyDocument @click="copyToClipboard(scope.row.id)" />
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="渠道名" prop="name" />
|
||||
<el-table-column label="发信渠道" prop="type" />
|
||||
<el-table-column label="创建时间" prop="created_on" />
|
||||
<el-table-column fixed="right" label="操作" width="100px">
|
||||
<template #default="scope">
|
||||
<!-- <el-button link size="small" type="primary" @click="handleView(scope.$index, scope.row)">查看</el-button> -->
|
||||
<el-button link size="small" type="primary" @click="handleEdit(scope.$index, scope.row)">编辑</el-button>
|
||||
<tableDeleteButton @customHandleDelete="handleDelete(scope.$index, scope.row)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
</el-table>
|
||||
|
||||
</div>
|
||||
<div class="pagination-block">
|
||||
<el-pagination layout="prev, pager, next" :total="total" :page-size="pageSize" @current-change="handPageChange" />
|
||||
<el-text class="total-tip" size="small">共{{ total }}条</el-text>
|
||||
</div>
|
||||
|
||||
<addWayComponent :componentName="addWayComponentName" />
|
||||
<editWayComponent :componentName="editWayComponentName" />
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script >
|
||||
import { reactive, toRefs, onMounted } from 'vue'
|
||||
import addWayComponent from './addWayPopUp.vue'
|
||||
import editWayComponent from './editWayPopUp.vue'
|
||||
import { usePageState } from '../../../store/page_sate.js';
|
||||
import { request } from '../../../api/api'
|
||||
import { CopyDocument } from '@element-plus/icons-vue'
|
||||
import { copyToClipboard } from '../../../util/clipboard.js';
|
||||
import tableDeleteButton from '@/views/common/tableDeleteButton.vue'
|
||||
import { CONSTANT } from '@/constant'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
addWayComponent,
|
||||
editWayComponent,
|
||||
CopyDocument,
|
||||
tableDeleteButton,
|
||||
},
|
||||
setup() {
|
||||
const pageState = usePageState();
|
||||
const state = reactive({
|
||||
addWayComponentName: 'addWayComponent',
|
||||
editWayComponentName: 'editWayComponent',
|
||||
search: '',
|
||||
optionValue: '',
|
||||
dialogFormVisible: false,
|
||||
// confirmBtnVisible: false,
|
||||
tableData: [],
|
||||
total: CONSTANT.TOTAL,
|
||||
pageSize: CONSTANT.PAGE_SIZE,
|
||||
currPage: CONSTANT.PAGE,
|
||||
displayCols: [
|
||||
{ 'col': 'id', 'label': '渠道ID' },
|
||||
{ 'col': 'name', 'label': '渠道名' },
|
||||
{ 'col': 'type', 'label': '发信渠道' },
|
||||
{ 'col': 'created_on', 'label': '创建时间' },
|
||||
],
|
||||
options: [
|
||||
{ label: '邮箱', value: 'Email' },
|
||||
{ label: '钉钉', value: 'Dtalk' }
|
||||
]
|
||||
});
|
||||
|
||||
const handleEdit = (index, row) => {
|
||||
console.log(index, row)
|
||||
let name = state.editWayComponentName;
|
||||
pageState.ShowDialogData[name] = {};
|
||||
pageState.ShowDialogData[name].isShow = true;
|
||||
pageState.ShowDialogData[name].rowData = row;
|
||||
}
|
||||
|
||||
const handleDelete = async (index, row) => {
|
||||
const rsp = await request.post('/sendways/delete', { id: row.id });
|
||||
if (rsp.status == 200 && await rsp.data.code == 200) {
|
||||
state.tableData.splice(index, 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const handPageChange = async (pageNum) => {
|
||||
console.log('pageNum', pageNum)
|
||||
state.currPage = pageNum;
|
||||
await queryListData(pageNum, state.pageSize);
|
||||
|
||||
}
|
||||
const rowStyle = () => {
|
||||
return {
|
||||
'font-size': '13px',
|
||||
}
|
||||
}
|
||||
const filterFunc = async () => {
|
||||
console.log('state.optionValue', state.optionValue);
|
||||
await queryListData(state.currPage, state.pageSize, state.search, state.optionValue);
|
||||
|
||||
}
|
||||
const clickAdd = () => {
|
||||
pageState.ShowDialogData[state.addWayComponentName] = {};
|
||||
pageState.ShowDialogData[state.addWayComponentName].isShow = true;
|
||||
console.log('clickAdd', pageState.ShowDialogData[state.addWayComponentName].isShow)
|
||||
}
|
||||
|
||||
const queryListData = async (page, size, name = '', type = '') => {
|
||||
let params = { page: page, size: size, name: name, type: type };
|
||||
const rsp = await request.get('/sendways/list', { params: params });
|
||||
state.tableData = await rsp.data.data.lists;
|
||||
state.total = await rsp.data.data.total;
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await queryListData(1, state.pageSize);
|
||||
});
|
||||
|
||||
return {
|
||||
...toRefs(state), handleEdit, handleDelete,
|
||||
clickAdd, rowStyle, handPageChange, filterFunc, copyToClipboard
|
||||
};
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
hr {
|
||||
color: #FAFCFF;
|
||||
background-color: #FAFCFF;
|
||||
border-color: #FAFCFF;
|
||||
}
|
||||
|
||||
.container {
|
||||
background-color: white;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.1);
|
||||
max-width: 1000px;
|
||||
width: 100%;
|
||||
margin-top: -10vh;
|
||||
}
|
||||
|
||||
.el-table th,
|
||||
.el-table .cell {
|
||||
font-size: 10px;
|
||||
/* 调整字体大小 */
|
||||
}
|
||||
|
||||
/* .search-input {
|
||||
margin-right: 0px;
|
||||
margin-left: 0px;
|
||||
padding-left: 0px;
|
||||
} */
|
||||
|
||||
.el-dialog__title {
|
||||
font-size: 14px !important;
|
||||
}
|
||||
|
||||
.pagination-block {
|
||||
margin-top: 30px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.total-tip {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
float: right;
|
||||
}
|
||||
|
||||
:global(.select .el-input__inner) {
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.search-input-sendways {
|
||||
margin-left: 40px;
|
||||
width: 150px;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.op-col {
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,92 @@
|
||||
<template>
|
||||
<div class="main-center-body">
|
||||
<div class="container">
|
||||
<div class="setting-main" style="display: flex;">
|
||||
<div style="width: 120px;">
|
||||
<el-menu :default-active="currTab" mode="vertical" width="100px" @select="handleSelect">
|
||||
<el-menu-item index="resetPasswd">
|
||||
重置密码
|
||||
</el-menu-item>
|
||||
<el-menu-item index="logCleanSetting">
|
||||
日志清理
|
||||
</el-menu-item>
|
||||
<el-menu-item index="siteCustomSetting">
|
||||
站点设置
|
||||
</el-menu-item>
|
||||
<el-menu-item index="aboutSetting">
|
||||
站点关于
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</div>
|
||||
<div class="setting-right">
|
||||
<resetPasswd v-if="currTab == 'resetPasswd'" />
|
||||
<setLogClean v-if="currTab == 'logCleanSetting'" />
|
||||
<siteCustom v-if="currTab == 'siteCustomSetting'" />
|
||||
<aboutSetting v-if="currTab == 'aboutSetting'" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script >
|
||||
import { reactive, toRefs, onMounted } from 'vue'
|
||||
import { copyToClipboard } from '@/util/clipboard.js';
|
||||
|
||||
import resetPasswd from './view/resetPasswd.vue'
|
||||
import setLogClean from './view/setLogClean.vue'
|
||||
import siteCustom from './view/siteCustom.vue'
|
||||
import aboutSetting from './view/about.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
resetPasswd,
|
||||
setLogClean,
|
||||
siteCustom,
|
||||
aboutSetting,
|
||||
},
|
||||
setup() {
|
||||
const state = reactive({
|
||||
currTab: 'resetPasswd',
|
||||
});
|
||||
|
||||
const handleSelect = (index) => {
|
||||
state.currTab = index;
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
});
|
||||
|
||||
return {
|
||||
...toRefs(state), copyToClipboard, handleSelect
|
||||
};
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
hr {
|
||||
color: #FAFCFF;
|
||||
background-color: #FAFCFF;
|
||||
border-color: #FAFCFF;
|
||||
}
|
||||
|
||||
.container {
|
||||
background-color: white;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.1);
|
||||
max-width: 800px;
|
||||
width: 100%;
|
||||
margin-top: -10vh;
|
||||
}
|
||||
|
||||
/* .setting-main {
|
||||
|
||||
} */
|
||||
|
||||
.setting-right {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<div class="setting-container">
|
||||
<el-text size="small">当前版本:v1.0.0</el-text>
|
||||
<div class="buttom">
|
||||
<div class="tips">
|
||||
<el-text size="small">版本功能说明</el-text>
|
||||
<el-tooltip placement="top">
|
||||
<template #content>
|
||||
logo请输入svg文本,替换后登录页面、ico、导航栏logo将全部一起更换
|
||||
<br />
|
||||
*将在下一次登录的时候生效
|
||||
</template>
|
||||
<el-icon>
|
||||
<QuestionFilled />
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, reactive, toRefs } from 'vue';
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { request } from '@/api/api'
|
||||
import { QuestionFilled } from '@element-plus/icons-vue'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
QuestionFilled,
|
||||
},
|
||||
props: {
|
||||
},
|
||||
methods: {
|
||||
},
|
||||
setup() {
|
||||
const state = reactive({
|
||||
oldPasswd: '',
|
||||
newPasswd: '',
|
||||
});
|
||||
|
||||
const handleChange = async () => {
|
||||
let postData = { old_passwd: state.oldPasswd, new_passwd: state.newPasswd };
|
||||
const rsp = await request.post('/settings/setpasswd', postData);
|
||||
if (await rsp.data.code == 200) {
|
||||
let msg = await rsp.data.msg;
|
||||
ElMessage({ message: msg, type: 'success' })
|
||||
}
|
||||
}
|
||||
return {
|
||||
...toRefs(state), handleChange
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
:deep(.el-input .el-input__wrapper) {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
:deep(.el-button) {
|
||||
float: right !important;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.setting-container {
|
||||
width: 200px;
|
||||
margin: 50px auto;
|
||||
}
|
||||
|
||||
.buttom {
|
||||
margin-top: 30px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<div class="setting-container">
|
||||
<el-input v-model="oldPasswd" size="small" placeholder="请输入旧密码" type="password" show-password>
|
||||
<template #prepend>旧密码</template>
|
||||
</el-input>
|
||||
<el-input v-model="newPasswd" size="small" placeholder="请输入新密码" type="password" show-password
|
||||
style=" margin-top: 15px;">
|
||||
<template #prepend>新密码</template>
|
||||
</el-input>
|
||||
<el-button @click="handleChange" size="small" type="primary">确定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, reactive, toRefs } from 'vue';
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { request } from '@/api/api'
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
},
|
||||
methods: {
|
||||
},
|
||||
setup() {
|
||||
const state = reactive({
|
||||
oldPasswd: '',
|
||||
newPasswd: '',
|
||||
});
|
||||
|
||||
const handleChange = async () => {
|
||||
let postData = { old_passwd: state.oldPasswd, new_passwd: state.newPasswd };
|
||||
const rsp = await request.post('/settings/setpasswd', postData);
|
||||
if (await rsp.data.code == 200) {
|
||||
let msg = await rsp.data.msg;
|
||||
ElMessage({ message: msg, type: 'success' })
|
||||
}
|
||||
}
|
||||
return {
|
||||
...toRefs(state), handleChange
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* :deep(.el-input .el-input__wrapper) {
|
||||
margin-top: 10px;
|
||||
} */
|
||||
|
||||
:deep(.el-button) {
|
||||
float: right !important;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.setting-container {
|
||||
width: 300px;
|
||||
margin: 50px auto;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<div class="setting-container">
|
||||
<div>
|
||||
<el-input v-model="cron" size="small" placeholder="请输入定时日志清除的Cron表达式">
|
||||
<template size="small" #prepend>cron://</template>
|
||||
</el-input>
|
||||
<el-input v-model.number="keepNum" size="small" placeholder="请输入要保留的最近的日志条数" style="margin-top: 15px;">
|
||||
<template size="small" #prepend>保留数</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="buttom">
|
||||
<div class="tips">
|
||||
<el-text size="small">说明</el-text>
|
||||
<el-tooltip placement="top">
|
||||
<template #content>
|
||||
cron如果不设置,默认是在每天的0点1分进行清理
|
||||
<br />
|
||||
保留数目如果不设置,默认保留最近1000条
|
||||
</template>
|
||||
<el-icon>
|
||||
<QuestionFilled />
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<el-button @click="handleView" size="small">查看日志</el-button>
|
||||
<el-button @click="handleSubmit" size="small" type="primary">确定</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, reactive, toRefs } from 'vue';
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { QuestionFilled } from '@element-plus/icons-vue'
|
||||
import { useRouter } from 'vue-router';
|
||||
import { request } from '@/api/api'
|
||||
import { CONSTANT } from '@/constant'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
QuestionFilled,
|
||||
},
|
||||
props: {
|
||||
},
|
||||
methods: {
|
||||
},
|
||||
setup() {
|
||||
const router = useRouter();
|
||||
const state = reactive({
|
||||
cron: '',
|
||||
keepNum: 1000,
|
||||
});
|
||||
|
||||
const handleSubmit = async () => {
|
||||
let postData = { old_passwd: state.oldPasswd, new_passwd: state.newPasswd };
|
||||
const rsp = await request.post('/settings/setpasswd', postData);
|
||||
if (await rsp.data.code == 200) {
|
||||
let msg = await rsp.data.msg;
|
||||
ElMessage({ message: msg, type: 'success' })
|
||||
}
|
||||
}
|
||||
const handleView = async () => {
|
||||
router.push('/sendlogs?taskid=' + CONSTANT.LOG_TASK_ID, { replace: true });
|
||||
}
|
||||
return {
|
||||
...toRefs(state), handleSubmit, handleView
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* :deep(.el-input .el-input__wrapper) {
|
||||
margin-top: 10px;
|
||||
} */
|
||||
|
||||
:deep(.el-button) {
|
||||
float: right !important;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.buttom {
|
||||
display: flex;
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.tips {
|
||||
width: 300px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.setting-container {
|
||||
width: 300px;
|
||||
margin: 40px auto;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,99 @@
|
||||
<template>
|
||||
<div class="setting-container">
|
||||
<div>
|
||||
<el-input v-model="title" size="small" placeholder="请输入自定义的网站标题">
|
||||
<template size="small" #prepend>站点标题</template>
|
||||
</el-input>
|
||||
<el-input v-model.number="slogan" size="small" placeholder="请输入自定义的网站slogan" style="margin-top: 15px;">
|
||||
<template size="small" #prepend>站点标语</template>
|
||||
</el-input>
|
||||
<el-input v-model.number="logo" size="small" placeholder="请输入自定义的网站logo(svg文本)" style="margin-top: 15px;">
|
||||
<template size="small" #prepend>站点图标</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="buttom">
|
||||
<div class="tips">
|
||||
<el-text size="small">说明</el-text>
|
||||
<el-tooltip placement="top">
|
||||
<template #content>
|
||||
1. logo请输入svg文本,替换后登录页面,ico,导航栏logo将全部一起更换
|
||||
<br />
|
||||
2. slogan将在登录页面展示
|
||||
<br />
|
||||
*将在下一次登录的时候生效
|
||||
</template>
|
||||
<el-icon>
|
||||
<QuestionFilled />
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<el-button @click="handleView" size="small">查看日志</el-button>
|
||||
<el-button @click="handleSubmit" size="small" type="primary">确定</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, reactive, toRefs } from 'vue';
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { QuestionFilled } from '@element-plus/icons-vue'
|
||||
import { useRouter } from 'vue-router';
|
||||
import { request } from '@/api/api'
|
||||
import { CONSTANT } from '@/constant'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
QuestionFilled,
|
||||
},
|
||||
props: {
|
||||
},
|
||||
methods: {
|
||||
},
|
||||
setup() {
|
||||
const router = useRouter();
|
||||
const state = reactive({
|
||||
title: '',
|
||||
slogan: '',
|
||||
logo: '',
|
||||
});
|
||||
|
||||
const handleSubmit = async () => {
|
||||
let postData = { old_passwd: state.oldPasswd, new_passwd: state.newPasswd };
|
||||
const rsp = await request.post('/settings/setpasswd', postData);
|
||||
if (await rsp.data.code == 200) {
|
||||
let msg = await rsp.data.msg;
|
||||
ElMessage({ message: msg, type: 'success' })
|
||||
}
|
||||
}
|
||||
const handleView = async () => {
|
||||
router.push('/sendlogs?taskid=' + CONSTANT.LOG_TASK_ID, { replace: true });
|
||||
}
|
||||
return {
|
||||
...toRefs(state), handleSubmit, handleView
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
:deep(.el-button) {
|
||||
float: right !important;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.buttom {
|
||||
display: flex;
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.tips {
|
||||
width: 300px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.setting-container {
|
||||
width: 300px;
|
||||
margin: 40px auto;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,23 @@
|
||||
import { fileURLToPath, URL } from 'node:url'
|
||||
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import { prismjsPlugin } from 'vite-plugin-prismjs'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
prismjsPlugin({
|
||||
languages: 'all', // 语言
|
||||
plugins: ['line-numbers', 'show-language', 'copy-to-clipboard', 'inline-color'],
|
||||
theme: 'default',// 主题
|
||||
css: true,
|
||||
})
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||
}
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user