添加功能
This commit is contained in:
parent
0f75278f65
commit
44c7487099
|
@ -7,10 +7,12 @@
|
||||||
- [x] 环境变量的增删改查
|
- [x] 环境变量的增删改查
|
||||||
- [x] 配置文件的编辑
|
- [x] 配置文件的编辑
|
||||||
- [x] 脚本文件的增删改查
|
- [x] 脚本文件的增删改查
|
||||||
- [ ] 定时任务的视图筛选功能
|
- [x] 应用授权登录的实现
|
||||||
- [ ] 订阅功能的实现
|
- [x] 订阅功能的实现
|
||||||
|
- [x] 日志功能的实现
|
||||||
|
- [x] 依赖功能的实现
|
||||||
- [ ] 推送功能的实现
|
- [ ] 推送功能的实现
|
||||||
- [ ] 应用授权登录的实现
|
- [ ] 定时任务的视图筛选功能
|
||||||
- [ ] 热更新的实现
|
- [ ] 热更新的实现
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,12 @@ import (
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/huoxue1/qinglong-go/api/config"
|
"github.com/huoxue1/qinglong-go/api/config"
|
||||||
"github.com/huoxue1/qinglong-go/api/cron"
|
"github.com/huoxue1/qinglong-go/api/cron"
|
||||||
|
"github.com/huoxue1/qinglong-go/api/dependencies"
|
||||||
"github.com/huoxue1/qinglong-go/api/env"
|
"github.com/huoxue1/qinglong-go/api/env"
|
||||||
|
"github.com/huoxue1/qinglong-go/api/logs"
|
||||||
|
"github.com/huoxue1/qinglong-go/api/open"
|
||||||
"github.com/huoxue1/qinglong-go/api/scripts"
|
"github.com/huoxue1/qinglong-go/api/scripts"
|
||||||
|
"github.com/huoxue1/qinglong-go/api/subscription"
|
||||||
"github.com/huoxue1/qinglong-go/api/system"
|
"github.com/huoxue1/qinglong-go/api/system"
|
||||||
"github.com/huoxue1/qinglong-go/api/user"
|
"github.com/huoxue1/qinglong-go/api/user"
|
||||||
)
|
)
|
||||||
|
@ -17,4 +21,8 @@ func Api(group *gin.RouterGroup) {
|
||||||
env.Api(group.Group("/envs"))
|
env.Api(group.Group("/envs"))
|
||||||
config.Api(group.Group("/configs"))
|
config.Api(group.Group("/configs"))
|
||||||
scripts.Api(group.Group("/scripts"))
|
scripts.Api(group.Group("/scripts"))
|
||||||
|
open.Api(group.Group("/apps"))
|
||||||
|
subscription.Api(group.Group("/subscriptions"))
|
||||||
|
logs.APi(group.Group("/logs"))
|
||||||
|
dependencies.Api(group.Group("/dependencies"))
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ func get() gin.HandlerFunc {
|
||||||
}
|
}
|
||||||
ctx.JSON(200, res.Ok(gin.H{
|
ctx.JSON(200, res.Ok(gin.H{
|
||||||
"data": crons,
|
"data": crons,
|
||||||
"total": len(crons),
|
"total": models.Count(ctx.Query("searchValue")),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
package dependencies
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/huoxue1/qinglong-go/models"
|
||||||
|
"github.com/huoxue1/qinglong-go/service/dependencies"
|
||||||
|
"github.com/huoxue1/qinglong-go/utils/res"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Api(group *gin.RouterGroup) {
|
||||||
|
group.POST("", post())
|
||||||
|
group.GET("", get())
|
||||||
|
group.GET("/:id", getDep())
|
||||||
|
}
|
||||||
|
|
||||||
|
func get() gin.HandlerFunc {
|
||||||
|
return func(ctx *gin.Context) {
|
||||||
|
dependences, err := models.QueryDependences(ctx.Query("searchValue"))
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(502, res.Err(502, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.JSON(200, res.Ok(dependences))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func post() gin.HandlerFunc {
|
||||||
|
return func(ctx *gin.Context) {
|
||||||
|
var deps []*models.Dependences
|
||||||
|
err := ctx.ShouldBindJSON(&deps)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(502, res.Err(502, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, dep := range deps {
|
||||||
|
dependencies.AddDep(dep)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.JSON(200, res.Ok(deps))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDep() gin.HandlerFunc {
|
||||||
|
return func(ctx *gin.Context) {
|
||||||
|
id, _ := strconv.Atoi(ctx.Param("id"))
|
||||||
|
dep, _ := models.GetDependences(id)
|
||||||
|
ctx.JSON(200, res.Ok(dep))
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
package logs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/huoxue1/qinglong-go/service/scripts"
|
||||||
|
"github.com/huoxue1/qinglong-go/utils/res"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
)
|
||||||
|
|
||||||
|
func APi(group *gin.RouterGroup) {
|
||||||
|
group.GET("", get())
|
||||||
|
group.GET("/:name", getFile())
|
||||||
|
}
|
||||||
|
|
||||||
|
func get() gin.HandlerFunc {
|
||||||
|
|
||||||
|
return func(ctx *gin.Context) {
|
||||||
|
files := scripts.GetFiles(path.Join("data", "log"), "")
|
||||||
|
ctx.JSON(200, res.Ok(files))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFile() gin.HandlerFunc {
|
||||||
|
return func(ctx *gin.Context) {
|
||||||
|
fileName := ctx.Param("name")
|
||||||
|
path := ctx.Query("path")
|
||||||
|
data, _ := os.ReadFile(fmt.Sprintf("data/log/%s/%s", path, fileName))
|
||||||
|
ctx.JSON(200, res.Ok(string(data)))
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,113 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/huoxue1/qinglong-go/models"
|
||||||
|
"github.com/huoxue1/qinglong-go/utils"
|
||||||
|
"github.com/huoxue1/qinglong-go/utils/res"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
unExcludedPath = []string{
|
||||||
|
"/api/system",
|
||||||
|
"/api/user/login",
|
||||||
|
"/api/user/init",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func OpenJwt() gin.HandlerFunc {
|
||||||
|
return func(ctx *gin.Context) {
|
||||||
|
if strings.HasPrefix(ctx.Request.URL.Path, "/open/auth/token") {
|
||||||
|
ctx.Next()
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
tokenHeader := ctx.GetHeader("Authorization")
|
||||||
|
if tokenHeader == "" {
|
||||||
|
ctx.JSON(401, res.Err(401, errors.New("no authorization token was found")))
|
||||||
|
ctx.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
authToken := strings.Split(tokenHeader, " ")[1]
|
||||||
|
claims, _ := utils.ParseToken(authToken)
|
||||||
|
if claims.ExpiresAt < time.Now().Unix() {
|
||||||
|
ctx.JSON(401, res.Err(401, errors.New("the authorization token is expired")))
|
||||||
|
ctx.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
userId, _ := strconv.Atoi(claims.UserID)
|
||||||
|
app, err := models.GetApp(userId)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(401, res.Err(401, errors.New("the authorization token is invalid")))
|
||||||
|
ctx.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, scope := range app.Scopes {
|
||||||
|
if strings.HasPrefix(ctx.Request.URL.Path, "/open/"+scope) {
|
||||||
|
ctx.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.Abort()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Jwt() gin.HandlerFunc {
|
||||||
|
return func(ctx *gin.Context) {
|
||||||
|
for _, s := range unExcludedPath {
|
||||||
|
if strings.HasPrefix(ctx.Request.URL.Path, s) {
|
||||||
|
ctx.Next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := os.ReadFile(path.Join("data", "config", "auth.json"))
|
||||||
|
if err != nil {
|
||||||
|
ctx.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
auth := new(models.AuthFile)
|
||||||
|
_ = json.Unmarshal(data, auth)
|
||||||
|
tokenHeader := ctx.GetHeader("Authorization")
|
||||||
|
if tokenHeader == "" {
|
||||||
|
ctx.JSON(401, res.Err(401, errors.New("no authorization token was found")))
|
||||||
|
ctx.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
authToken := strings.Split(tokenHeader, " ")[1]
|
||||||
|
|
||||||
|
mobile := utils.IsMobile(ctx.GetHeader("User-Agent"))
|
||||||
|
claims, _ := utils.ParseToken(authToken)
|
||||||
|
if claims.ExpiresAt < time.Now().Unix() {
|
||||||
|
ctx.JSON(401, res.Err(401, errors.New("the authorization token is expired")))
|
||||||
|
ctx.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if mobile {
|
||||||
|
if authToken != auth.Tokens.Mobile && authToken != auth.Token {
|
||||||
|
ctx.JSON(401, res.Err(401, errors.New("the authorization token is error")))
|
||||||
|
ctx.Abort()
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
ctx.Next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if authToken != auth.Tokens.Desktop && authToken != auth.Token {
|
||||||
|
ctx.JSON(401, res.Err(401, errors.New("the authorization token is error")))
|
||||||
|
ctx.Abort()
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
ctx.Next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/huoxue1/qinglong-go/api/config"
|
||||||
|
"github.com/huoxue1/qinglong-go/api/cron"
|
||||||
|
"github.com/huoxue1/qinglong-go/api/dependencies"
|
||||||
|
"github.com/huoxue1/qinglong-go/api/env"
|
||||||
|
"github.com/huoxue1/qinglong-go/api/logs"
|
||||||
|
"github.com/huoxue1/qinglong-go/api/open"
|
||||||
|
"github.com/huoxue1/qinglong-go/api/scripts"
|
||||||
|
"github.com/huoxue1/qinglong-go/api/subscription"
|
||||||
|
"github.com/huoxue1/qinglong-go/api/system"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Open(group *gin.RouterGroup) {
|
||||||
|
group.GET("/auth/token", open.Auth())
|
||||||
|
system.Api(group.Group("/system"))
|
||||||
|
cron.Api(group.Group("/crons"))
|
||||||
|
env.Api(group.Group("/envs"))
|
||||||
|
config.Api(group.Group("/configs"))
|
||||||
|
scripts.Api(group.Group("/scripts"))
|
||||||
|
open.Api(group.Group("/apps"))
|
||||||
|
subscription.Api(group.Group("/subscriptions"))
|
||||||
|
logs.APi(group.Group("/logs"))
|
||||||
|
dependencies.Api(group.Group("/dependencies"))
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package open
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/huoxue1/qinglong-go/models"
|
||||||
|
"github.com/huoxue1/qinglong-go/utils"
|
||||||
|
"github.com/huoxue1/qinglong-go/utils/res"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Auth() gin.HandlerFunc {
|
||||||
|
return func(ctx *gin.Context) {
|
||||||
|
clientId := ctx.Query("client_id")
|
||||||
|
clientSecret := ctx.Query("client_secret")
|
||||||
|
app, err := models.GetAppById(clientId)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(401, res.Err(401, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if app.ClientSecret != clientSecret {
|
||||||
|
ctx.JSON(401, res.Err(401, errors.New("the auth fail")))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
token, err := utils.GenerateToken(strconv.Itoa(app.Id), 720)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(503, res.Err(503, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
app.Tokens = append(app.Tokens, token)
|
||||||
|
models.UpdateApp(app)
|
||||||
|
ctx.JSON(200, res.Ok(map[string]any{
|
||||||
|
"token": token,
|
||||||
|
"token_type": "Bearer",
|
||||||
|
"expiration": time.Now().Add(time.Hour * 720).Unix(),
|
||||||
|
}))
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
package open
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/huoxue1/qinglong-go/models"
|
||||||
|
"github.com/huoxue1/qinglong-go/service/open"
|
||||||
|
"github.com/huoxue1/qinglong-go/utils/res"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Api(group *gin.RouterGroup) {
|
||||||
|
group.GET("", get())
|
||||||
|
group.POST("", post())
|
||||||
|
group.PUT("", put())
|
||||||
|
group.DELETE("", del())
|
||||||
|
group.PUT("/:id/reset-secret", reset())
|
||||||
|
}
|
||||||
|
|
||||||
|
func get() gin.HandlerFunc {
|
||||||
|
return func(ctx *gin.Context) {
|
||||||
|
apps, err := models.QueryApp()
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(503, res.Err(503, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.JSON(200, res.Ok(apps))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func del() gin.HandlerFunc {
|
||||||
|
return func(ctx *gin.Context) {
|
||||||
|
var ids []int
|
||||||
|
err := ctx.ShouldBindJSON(&ids)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(503, res.Err(502, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = open.DeleteApp(ids)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(503, res.Err(503, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.JSON(200, res.Ok(true))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func put() gin.HandlerFunc {
|
||||||
|
return func(ctx *gin.Context) {
|
||||||
|
m := new(models.Apps)
|
||||||
|
err := ctx.ShouldBindJSON(m)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(502, res.Err(502, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = open.UpdateApp(m)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(503, res.Err(503, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.JSON(200, res.Ok(m))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func post() gin.HandlerFunc {
|
||||||
|
return func(ctx *gin.Context) {
|
||||||
|
m := new(models.Apps)
|
||||||
|
err := ctx.ShouldBindJSON(m)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(502, res.Err(502, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
id, err := open.AddApp(m)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
app, _ := models.GetApp(id)
|
||||||
|
ctx.JSON(200, res.Ok(app))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func reset() gin.HandlerFunc {
|
||||||
|
return func(ctx *gin.Context) {
|
||||||
|
id, _ := strconv.Atoi(ctx.Param("id"))
|
||||||
|
app, err := models.GetApp(id)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(502, res.Err(502, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = open.ResetApp(app)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(503, res.Err(503, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.JSON(200, res.Ok(app))
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,7 +19,7 @@ func Api(group *gin.RouterGroup) {
|
||||||
|
|
||||||
func get() gin.HandlerFunc {
|
func get() gin.HandlerFunc {
|
||||||
return func(ctx *gin.Context) {
|
return func(ctx *gin.Context) {
|
||||||
files := scripts.GetFiles()
|
files := scripts.GetFiles(path2.Join("data", "scripts"), "")
|
||||||
ctx.JSON(200, res.Ok(files))
|
ctx.JSON(200, res.Ok(files))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,165 @@
|
||||||
|
package subscription
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/huoxue1/qinglong-go/models"
|
||||||
|
"github.com/huoxue1/qinglong-go/service/subscription"
|
||||||
|
"github.com/huoxue1/qinglong-go/utils/res"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Api(group *gin.RouterGroup) {
|
||||||
|
group.GET("", get())
|
||||||
|
group.POST("", post())
|
||||||
|
group.PUT("", put())
|
||||||
|
group.PUT("/disable", disable())
|
||||||
|
group.PUT("/enable", enable())
|
||||||
|
group.DELETE("", del())
|
||||||
|
group.PUT("/run", run())
|
||||||
|
group.GET("/:id/log", log1())
|
||||||
|
group.PUT("/stop", stop())
|
||||||
|
}
|
||||||
|
|
||||||
|
func run() gin.HandlerFunc {
|
||||||
|
return func(ctx *gin.Context) {
|
||||||
|
var ids []int
|
||||||
|
err := ctx.ShouldBindJSON(&ids)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(502, res.Err(502, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = subscription.RunSubscription(ids)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(503, res.Err(503, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.JSON(200, res.Ok(true))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func stop() gin.HandlerFunc {
|
||||||
|
return func(ctx *gin.Context) {
|
||||||
|
var ids []int
|
||||||
|
err := ctx.ShouldBindJSON(&ids)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(502, res.Err(502, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = subscription.StopSubscription(ids)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(503, res.Err(503, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.JSON(200, res.Ok(true))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func get() gin.HandlerFunc {
|
||||||
|
return func(ctx *gin.Context) {
|
||||||
|
|
||||||
|
subs, err := models.QuerySubscription(ctx.Query("searchValue"))
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(503, res.Err(503, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.JSON(200, res.Ok(subs))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func post() gin.HandlerFunc {
|
||||||
|
|
||||||
|
return func(ctx *gin.Context) {
|
||||||
|
sub := new(models.Subscriptions)
|
||||||
|
err := ctx.ShouldBindJSON(sub)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(502, res.Err(502, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
id, err := subscription.AddSubscription(sub)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(503, res.Err(503, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sub.Id = id
|
||||||
|
ctx.JSON(200, res.Ok(sub))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func enable() gin.HandlerFunc {
|
||||||
|
return func(ctx *gin.Context) {
|
||||||
|
var ids []int
|
||||||
|
err := ctx.ShouldBindJSON(&ids)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(503, res.Err(502, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = subscription.EnableSubscription(ids)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(503, res.Err(503, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.JSON(200, res.Ok(true))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func disable() gin.HandlerFunc {
|
||||||
|
return func(ctx *gin.Context) {
|
||||||
|
var ids []int
|
||||||
|
err := ctx.ShouldBindJSON(&ids)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(502, res.Err(502, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = subscription.DisableSubscription(ids)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(503, res.Err(503, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.JSON(200, res.Ok(true))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func put() gin.HandlerFunc {
|
||||||
|
return func(ctx *gin.Context) {
|
||||||
|
s := new(models.Subscriptions)
|
||||||
|
err := ctx.ShouldBindJSON(s)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(503, res.Err(502, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = subscription.UpdateSubscription(s)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(503, res.Err(503, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.JSON(200, res.Ok(s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func del() gin.HandlerFunc {
|
||||||
|
return func(ctx *gin.Context) {
|
||||||
|
var ids []int
|
||||||
|
err := ctx.ShouldBindJSON(&ids)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(503, res.Err(502, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = subscription.DeleteSubscription(ids)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(503, res.Err(503, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.JSON(200, res.Ok(true))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func log1() gin.HandlerFunc {
|
||||||
|
return func(ctx *gin.Context) {
|
||||||
|
id, _ := strconv.Atoi(ctx.Param("id"))
|
||||||
|
s, _ := models.GetSubscription(id)
|
||||||
|
data, _ := os.ReadFile(s.LogPath)
|
||||||
|
ctx.JSON(200, res.Ok(string(data)))
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,15 +9,15 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func Api(group *gin.RouterGroup) {
|
func Api(group *gin.RouterGroup) {
|
||||||
group.GET("/", get())
|
group.GET("", get())
|
||||||
}
|
}
|
||||||
|
|
||||||
func get() gin.HandlerFunc {
|
func get() gin.HandlerFunc {
|
||||||
return func(ctx *gin.Context) {
|
return func(ctx *gin.Context) {
|
||||||
_, err := os.Stat(path.Join("data", "config", "auth.json"))
|
_, err := os.Stat(path.Join("data", "config", "auth.json"))
|
||||||
exist := os.IsExist(err)
|
exist := os.IsNotExist(err)
|
||||||
ctx.JSON(200, res.Ok(system.System{
|
ctx.JSON(200, res.Ok(system.System{
|
||||||
IsInitialized: exist,
|
IsInitialized: !exist,
|
||||||
Version: "2.0.14",
|
Version: "2.0.14",
|
||||||
LastCommitTime: "",
|
LastCommitTime: "",
|
||||||
LastCommitId: "",
|
LastCommitId: "",
|
||||||
|
|
|
@ -0,0 +1,163 @@
|
||||||
|
## Version: v2.8.0
|
||||||
|
## Date: 2021-06-20
|
||||||
|
## Update Content: 可持续发展纲要\n1. session管理破坏性修改\n2. 配置管理可编辑config下文件\n3. 自定义脚本改为查看脚本\n4. 移除互助相关
|
||||||
|
|
||||||
|
## 上面版本号中,如果第2位数字有变化,那么代表增加了新的参数,如果只有第3位数字有变化,仅代表更新了注释,没有增加新的参数,可更新可不更新
|
||||||
|
|
||||||
|
## 在运行 ql repo 命令时,是否自动删除失效的脚本与定时任务
|
||||||
|
AutoDelCron="true"
|
||||||
|
|
||||||
|
## 在运行 ql repo 命令时,是否自动增加新的本地定时任务
|
||||||
|
AutoAddCron="true"
|
||||||
|
|
||||||
|
## 拉取脚本时默认的定时规则,当匹配不到定时规则时使用,例如: 0 9 * * *
|
||||||
|
DefaultCronRule=""
|
||||||
|
|
||||||
|
## ql repo命令拉取脚本时需要拉取的文件后缀,直接写文件后缀名即可
|
||||||
|
RepoFileExtensions="js py"
|
||||||
|
|
||||||
|
## 代理地址,支持HTTP/SOCK5,例如 http://127.0.0.1:7890
|
||||||
|
ProxyUrl=""
|
||||||
|
|
||||||
|
## 资源告警阙值,默认CPU 80%、内存80%、磁盘90%
|
||||||
|
CpuWarn=80
|
||||||
|
MemoryWarn=80
|
||||||
|
DiskWarn=90
|
||||||
|
|
||||||
|
## 设置定时任务执行的超时时间,默认1h,后缀"s"代表秒(默认值), "m"代表分, "h"代表小时, "d"代表天
|
||||||
|
CommandTimeoutTime="1h"
|
||||||
|
|
||||||
|
## 设置批量执行任务时的并发数,默认同时执行5个任务
|
||||||
|
MaxConcurrentNum="5"
|
||||||
|
|
||||||
|
## 在运行 task 命令时,随机延迟启动任务的最大延迟时间
|
||||||
|
## 默认给javascript任务加随机延迟,如 RandomDelay="300" ,表示任务将在 1-300 秒内随机延迟一个秒数,然后再运行,取消延迟赋值为空
|
||||||
|
RandomDelay="300"
|
||||||
|
|
||||||
|
## 需要随机延迟运行任务的文件后缀,直接写后缀名即可,多个后缀用空格分开,例如: js py ts
|
||||||
|
## 默认仅给javascript任务加随机延迟,其它任务按定时规则准点运行。全部任务随机延迟赋值为空
|
||||||
|
RandomDelayFileExtensions="js"
|
||||||
|
|
||||||
|
## 每小时的第几分钟准点运行任务,当在这些时间运行任务时将忽略 RandomDelay 配置,不会被随机延迟
|
||||||
|
## 默认是第0分钟和第30分钟,例如21:00或21:30分的任务将会准点运行。不需要准点运行赋值为空
|
||||||
|
RandomDelayIgnoredMinutes="0 30"
|
||||||
|
|
||||||
|
## 如果你自己会写shell脚本,并且希望在每次运行 ql update 命令时,额外运行你的 shell 脚本,请赋值为 "true",默认为true
|
||||||
|
EnableExtraShell="true"
|
||||||
|
|
||||||
|
## 是否自动启动bot,默认不启动,设置为true时自动启动,目前需要自行克隆bot仓库所需代码,存到ql/repo目录下,文件夹命名为dockerbot
|
||||||
|
AutoStartBot=""
|
||||||
|
|
||||||
|
## 是否使用第三方bot,默认不使用,使用时填入仓库地址,存到ql/repo目录下,文件夹命名为diybot
|
||||||
|
BotRepoUrl=""
|
||||||
|
|
||||||
|
## 安装python依赖时指定pip源
|
||||||
|
PipMirror="https://pypi.doubanio.com/simple/"
|
||||||
|
|
||||||
|
## 安装node依赖时指定npm源
|
||||||
|
NpmMirror="https://registry.npmmirror.com"
|
||||||
|
|
||||||
|
## 通知环境变量
|
||||||
|
## 1. Server酱
|
||||||
|
## https://sct.ftqq.com
|
||||||
|
## 下方填写 SCHKEY 值或 SendKey 值
|
||||||
|
export PUSH_KEY=""
|
||||||
|
|
||||||
|
## 2. BARK
|
||||||
|
## 下方填写app提供的设备码,例如:https://api.day.app/123 那么此处的设备码就是123
|
||||||
|
export BARK_PUSH=""
|
||||||
|
## 下方填写推送图标设置,自定义推送图标(需iOS15或以上)
|
||||||
|
export BARK_ICON="https://qn.whyour.cn/logo.png"
|
||||||
|
## 下方填写推送声音设置,例如choo,具体值请在bark-推送铃声-查看所有铃声
|
||||||
|
export BARK_SOUND=""
|
||||||
|
## 下方填写推送消息分组,默认为"QingLong"
|
||||||
|
export BARK_GROUP="QingLong"
|
||||||
|
|
||||||
|
## 3. Telegram
|
||||||
|
## 下方填写自己申请@BotFather的Token,如10xxx4:AAFcqxxxxgER5uw
|
||||||
|
export TG_BOT_TOKEN=""
|
||||||
|
## 下方填写 @getuseridbot 中获取到的纯数字ID
|
||||||
|
export TG_USER_ID=""
|
||||||
|
## Telegram 代理IP(选填)
|
||||||
|
## 下方填写代理IP地址,代理类型为 http,比如您代理是 http://127.0.0.1:1080,则填写 "127.0.0.1"
|
||||||
|
## 如需使用,请自行解除下一行的注释
|
||||||
|
export TG_PROXY_HOST=""
|
||||||
|
## Telegram 代理端口(选填)
|
||||||
|
## 下方填写代理端口号,代理类型为 http,比如您代理是 http://127.0.0.1:1080,则填写 "1080"
|
||||||
|
## 如需使用,请自行解除下一行的注释
|
||||||
|
export TG_PROXY_PORT=""
|
||||||
|
## Telegram 代理的认证参数(选填)
|
||||||
|
export TG_PROXY_AUTH=""
|
||||||
|
## Telegram api自建反向代理地址(选填)
|
||||||
|
## 教程:https://www.hostloc.com/thread-805441-1-1.html
|
||||||
|
## 如反向代理地址 http://aaa.bbb.ccc 则填写 aaa.bbb.ccc
|
||||||
|
## 如需使用,请赋值代理地址链接,并自行解除下一行的注释
|
||||||
|
export TG_API_HOST=""
|
||||||
|
|
||||||
|
## 4. 钉钉
|
||||||
|
## 官方文档:https://developers.dingtalk.com/document/app/custom-robot-access
|
||||||
|
## 下方填写token后面的内容,只需 https://oapi.dingtalk.com/robot/send?access_token=XXX 等于=符号后面的XXX即可
|
||||||
|
export DD_BOT_TOKEN=""
|
||||||
|
export DD_BOT_SECRET=""
|
||||||
|
|
||||||
|
## 5. 企业微信机器人
|
||||||
|
## 官方说明文档:https://work.weixin.qq.com/api/doc/90000/90136/91770
|
||||||
|
## 下方填写密钥,企业微信推送 webhook 后面的 key
|
||||||
|
export QYWX_KEY=""
|
||||||
|
|
||||||
|
## 6. 企业微信应用
|
||||||
|
## 参考文档:http://note.youdao.com/s/HMiudGkb
|
||||||
|
## 下方填写素材库图片id(corpid,corpsecret,touser,agentid),素材库图片填0为图文消息, 填1为纯文本消息
|
||||||
|
export QYWX_AM=""
|
||||||
|
|
||||||
|
## 7. iGot聚合
|
||||||
|
## 参考文档:https://wahao.github.io/Bark-MP-helper
|
||||||
|
## 下方填写iGot的推送key,支持多方式推送,确保消息可达
|
||||||
|
export IGOT_PUSH_KEY=""
|
||||||
|
|
||||||
|
## 8. Push Plus
|
||||||
|
## 官方网站:http://www.pushplus.plus
|
||||||
|
## 下方填写您的Token,微信扫码登录后一对一推送或一对多推送下面的token,只填 PUSH_PLUS_TOKEN 默认为一对一推送
|
||||||
|
export PUSH_PLUS_TOKEN=""
|
||||||
|
## 一对一多推送(选填)
|
||||||
|
## 下方填写您的一对多推送的 "群组编码" ,(一对多推送下面->您的群组(如无则新建)->群组编码)
|
||||||
|
## 1. 需订阅者扫描二维码 2、如果您是创建群组所属人,也需点击“查看二维码”扫描绑定,否则不能接受群组消息推送
|
||||||
|
export PUSH_PLUS_USER=""
|
||||||
|
|
||||||
|
## 9. go-cqhttp
|
||||||
|
## gobot_url 推送到个人QQ: http://127.0.0.1/send_private_msg 群:http://127.0.0.1/send_group_msg
|
||||||
|
## gobot_token 填写在go-cqhttp文件设置的访问密钥
|
||||||
|
## gobot_qq 如果GOBOT_URL设置 /send_private_msg 则需要填入 user_id=个人QQ 相反如果是 /send_group_msg 则需要填入 group_id=QQ群
|
||||||
|
## go-cqhttp相关API https://docs.go-cqhttp.org/api
|
||||||
|
export GOBOT_URL=""
|
||||||
|
export GOBOT_TOKEN=""
|
||||||
|
export GOBOT_QQ=""
|
||||||
|
|
||||||
|
## 10. gotify
|
||||||
|
## gotify_url 填写gotify地址,如https://push.example.de:8080
|
||||||
|
## gotify_token 填写gotify的消息应用token
|
||||||
|
## gotify_priority 填写推送消息优先级,默认为0
|
||||||
|
export GOTIFY_URL=""
|
||||||
|
export GOTIFY_TOKEN=""
|
||||||
|
export GOTIFY_PRIORITY=0
|
||||||
|
|
||||||
|
## 11. PushDeer
|
||||||
|
## deer_key 填写PushDeer的key
|
||||||
|
export DEER_KEY=""
|
||||||
|
|
||||||
|
## 12. Chat
|
||||||
|
## chat_url 填写synology chat地址,http://IP:PORT/webapi/***token=
|
||||||
|
## chat_token 填写后面的token
|
||||||
|
export CHAT_URL=""
|
||||||
|
export CHAT_TOKEN=""
|
||||||
|
|
||||||
|
## 13. aibotk
|
||||||
|
## 官方说明文档:http://wechat.aibotk.com/oapi/oapi?from=ql
|
||||||
|
## aibotk_key (必填)填写智能微秘书个人中心的apikey
|
||||||
|
export AIBOTK_KEY=""
|
||||||
|
## aibotk_type (必填)填写发送的目标 room 或 contact, 填其他的不生效
|
||||||
|
export AIBOTK_TYPE=""
|
||||||
|
## aibotk_name (必填)填写群名或用户昵称,和上面的type类型要对应
|
||||||
|
export AIBOTK_NAME=""
|
||||||
|
|
||||||
|
## 其他需要的变量,脚本中需要的变量使用 export 变量名= 声明即可
|
|
@ -1,6 +1,7 @@
|
||||||
package user
|
package user
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
_ "embed"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/huoxue1/qinglong-go/models"
|
"github.com/huoxue1/qinglong-go/models"
|
||||||
|
@ -12,6 +13,9 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//go:embed config_sample.sh
|
||||||
|
var sample []byte
|
||||||
|
|
||||||
func Api(group *gin.RouterGroup) {
|
func Api(group *gin.RouterGroup) {
|
||||||
group.GET("/", get())
|
group.GET("/", get())
|
||||||
group.PUT("/init", appInit())
|
group.PUT("/init", appInit())
|
||||||
|
@ -37,6 +41,12 @@ func appInit() gin.HandlerFunc {
|
||||||
ctx.JSON(400, res.Err(400, err))
|
ctx.JSON(400, res.Err(400, err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
_ = os.MkdirAll(path.Join("data", "config"), 0666)
|
||||||
|
_ = os.MkdirAll(path.Join("data", "log"), 0666)
|
||||||
|
_ = os.MkdirAll(path.Join("data", "repo"), 0666)
|
||||||
|
_ = os.MkdirAll(path.Join("data", "scripts"), 0666)
|
||||||
|
_ = os.WriteFile(path.Join("data", "config", "config.sh"), sample, 0666)
|
||||||
|
_ = os.WriteFile(path.Join("data", "config", "config_sample.sh"), sample, 0666)
|
||||||
type Req struct {
|
type Req struct {
|
||||||
UserName string `json:"username"`
|
UserName string `json:"username"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
|
@ -78,7 +88,7 @@ func login() gin.HandlerFunc {
|
||||||
auth := new(models.AuthFile)
|
auth := new(models.AuthFile)
|
||||||
_ = json.Unmarshal(data, auth)
|
_ = json.Unmarshal(data, auth)
|
||||||
if auth.Username == r.UserName && auth.Password == r.Password {
|
if auth.Username == r.UserName && auth.Password == r.Password {
|
||||||
token, err := utils.GenerateToken(r.UserName)
|
token, err := utils.GenerateToken(r.UserName, 48)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.JSON(503, res.Err(503, err))
|
ctx.JSON(503, res.Err(503, err))
|
||||||
return
|
return
|
||||||
|
|
|
@ -4,12 +4,13 @@ import (
|
||||||
"github.com/gin-contrib/static"
|
"github.com/gin-contrib/static"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/huoxue1/qinglong-go/api"
|
"github.com/huoxue1/qinglong-go/api"
|
||||||
"github.com/huoxue1/qinglong-go/utils"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Router() *gin.Engine {
|
func Router() *gin.Engine {
|
||||||
engine := gin.New()
|
engine := gin.New()
|
||||||
|
engine.Use(gin.Logger())
|
||||||
|
engine.Use(gin.Recovery())
|
||||||
engine.Use(static.Serve("/", static.LocalFile("static/dist/", false)))
|
engine.Use(static.Serve("/", static.LocalFile("static/dist/", false)))
|
||||||
engine.NoRoute(func(ctx *gin.Context) {
|
engine.NoRoute(func(ctx *gin.Context) {
|
||||||
if ctx.Request.Method == http.MethodGet {
|
if ctx.Request.Method == http.MethodGet {
|
||||||
|
@ -18,7 +19,8 @@ func Router() *gin.Engine {
|
||||||
}
|
}
|
||||||
ctx.Next()
|
ctx.Next()
|
||||||
})
|
})
|
||||||
api.Api(engine.Group("/api", utils.Jwt()))
|
api.Api(engine.Group("/api", api.Jwt()))
|
||||||
|
api.Open(engine.Group("/open", api.OpenJwt()))
|
||||||
|
|
||||||
return engine
|
return engine
|
||||||
}
|
}
|
||||||
|
|
4
go.mod
4
go.mod
|
@ -3,10 +3,12 @@ module github.com/huoxue1/qinglong-go
|
||||||
go 1.18
|
go 1.18
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/Lyrics-you/sail-logrus-formatter v1.3.1
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||||
github.com/gin-contrib/static v0.0.1
|
github.com/gin-contrib/static v0.0.1
|
||||||
github.com/gin-gonic/gin v1.8.1
|
github.com/gin-gonic/gin v1.8.1
|
||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.0
|
||||||
|
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible
|
||||||
github.com/robfig/cron/v3 v3.0.1
|
github.com/robfig/cron/v3 v3.0.1
|
||||||
github.com/sirupsen/logrus v1.9.0
|
github.com/sirupsen/logrus v1.9.0
|
||||||
modernc.org/sqlite v1.19.4
|
modernc.org/sqlite v1.19.4
|
||||||
|
@ -25,12 +27,14 @@ require (
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||||
github.com/leodido/go-urn v1.2.1 // indirect
|
github.com/leodido/go-urn v1.2.1 // indirect
|
||||||
|
github.com/lestrrat-go/strftime v1.0.6 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/onsi/ginkgo v1.16.5 // indirect
|
github.com/onsi/ginkgo v1.16.5 // indirect
|
||||||
github.com/onsi/gomega v1.13.0 // indirect
|
github.com/onsi/gomega v1.13.0 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.0.5 // indirect
|
github.com/pelletier/go-toml/v2 v2.0.5 // indirect
|
||||||
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
|
||||||
github.com/stretchr/testify v1.8.1 // indirect
|
github.com/stretchr/testify v1.8.1 // indirect
|
||||||
github.com/syndtr/goleveldb v1.0.0 // indirect
|
github.com/syndtr/goleveldb v1.0.0 // indirect
|
||||||
|
|
11
go.sum
11
go.sum
|
@ -5,6 +5,8 @@ gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0p
|
||||||
gitee.com/travelliu/dm v1.8.11192/go.mod h1:DHTzyhCrM843x9VdKVbZ+GKXGRbKM2sJ4LxihRxShkE=
|
gitee.com/travelliu/dm v1.8.11192/go.mod h1:DHTzyhCrM843x9VdKVbZ+GKXGRbKM2sJ4LxihRxShkE=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
||||||
|
github.com/Lyrics-you/sail-logrus-formatter v1.3.1 h1:y/9QraPbDwfccHa4QFZ9g2zNiPoSoQnE5MYizWLiYwY=
|
||||||
|
github.com/Lyrics-you/sail-logrus-formatter v1.3.1/go.mod h1:e9FX8+3MxwQGGkK+8ne3kRpu0gaBc9QTE7jtP+zP070=
|
||||||
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||||
|
@ -221,6 +223,7 @@ github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dv
|
||||||
github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||||
|
github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
|
||||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
|
@ -249,6 +252,12 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||||
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
||||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||||
|
github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8=
|
||||||
|
github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is=
|
||||||
|
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible h1:Y6sqxHMyB1D2YSzWkLibYKgg+SwmyFU9dF2hn6MdTj4=
|
||||||
|
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible/go.mod h1:ZQnN8lSECaebrkQytbHj4xNgtg8CR7RYXnPok8e0EHA=
|
||||||
|
github.com/lestrrat-go/strftime v1.0.6 h1:CFGsDEt1pOpFNU+TJB0nhz9jl+K0hZSLE205AhTIGQQ=
|
||||||
|
github.com/lestrrat-go/strftime v1.0.6/go.mod h1:f7jQKgV5nnJpYgdEasS+/y7EsTb8ykN2z68n3TtcTaw=
|
||||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
@ -334,6 +343,8 @@ github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi
|
||||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
|
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
|
31
main.go
31
main.go
|
@ -1,9 +1,40 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
nested "github.com/Lyrics-you/sail-logrus-formatter/sailor"
|
||||||
"github.com/huoxue1/qinglong-go/controller"
|
"github.com/huoxue1/qinglong-go/controller"
|
||||||
|
rotates "github.com/lestrrat-go/file-rotatelogs"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
w, err := rotates.New(path.Join("data", "logs", "%Y-%m-%d.log"), rotates.WithRotationTime(time.Hour*24))
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("rotates init err: %v", err)
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
log.SetOutput(io.MultiWriter(w, os.Stdout))
|
||||||
|
log.SetFormatter(&nested.Formatter{
|
||||||
|
FieldsOrder: nil,
|
||||||
|
TimeStampFormat: "2006-01-02 15:04:05",
|
||||||
|
CharStampFormat: "",
|
||||||
|
HideKeys: false,
|
||||||
|
Position: true,
|
||||||
|
Colors: true,
|
||||||
|
FieldsColors: true,
|
||||||
|
FieldsSpace: true,
|
||||||
|
ShowFullLevel: false,
|
||||||
|
LowerCaseLevel: true,
|
||||||
|
TrimMessages: true,
|
||||||
|
CallerFirst: false,
|
||||||
|
CustomCallerFormatter: nil,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
engine := controller.Router()
|
engine := controller.Router()
|
||||||
_ = engine.Run(":8080")
|
_ = engine.Run(":8080")
|
||||||
|
|
|
@ -1,16 +1,53 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Apps struct {
|
type Apps struct {
|
||||||
Id int `xorm:"pk autoincr INTEGER"`
|
Id int `xorm:"pk autoincr INTEGER" json:"id,omitempty"`
|
||||||
Name string `xorm:"VARCHAR(255) unique"`
|
Name string `xorm:"TEXT unique" json:"name,omitempty"`
|
||||||
Scopes string `xorm:"JSON"`
|
Scopes []string `xorm:"JSON" json:"scopes,omitempty"`
|
||||||
ClientId string `xorm:"VARCHAR(255)"`
|
ClientId string `xorm:"TEXT" json:"client_id,omitempty"`
|
||||||
ClientSecret string `xorm:"VARCHAR(255)"`
|
ClientSecret string `xorm:"TEXT" json:"client_secret,omitempty"`
|
||||||
Tokens string `xorm:"JSON"`
|
Tokens []string `xorm:"JSON" json:"tokens,omitempty"`
|
||||||
Createdat time.Time `xorm:"not null DATETIME created"`
|
Createdat string `xorm:"not null DATETIME created" json:"createdat,omitempty"`
|
||||||
Updatedat time.Time `xorm:"not null DATETIME updated"`
|
Updatedat string `xorm:"not null DATETIME updated" json:"updatedat,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func QueryApp() ([]*Apps, error) {
|
||||||
|
apps := make([]*Apps, 0)
|
||||||
|
session := engine.Table(new(Apps))
|
||||||
|
err := session.Find(&apps)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return apps, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddApp(app *Apps) (int, error) {
|
||||||
|
_, err := engine.Table(app).Insert(app)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
_, _ = engine.Where("name=?", app.Name).Get(app)
|
||||||
|
return app.Id, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetApp(id int) (*Apps, error) {
|
||||||
|
app := new(Apps)
|
||||||
|
_, err := engine.ID(id).Get(app)
|
||||||
|
return app, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAppById(clientId string) (*Apps, error) {
|
||||||
|
app := new(Apps)
|
||||||
|
_, err := engine.Table(app).Where("client_id=?", clientId).Get(app)
|
||||||
|
return app, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateApp(app *Apps) error {
|
||||||
|
_, err := engine.Table(app).ID(app.Id).AllCols().Update(app)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteApp(id int) error {
|
||||||
|
_, err := engine.Table(new(Apps)).Delete(&Apps{Id: id})
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"xorm.io/builder"
|
"xorm.io/builder"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -56,6 +57,16 @@ func FindAllEnableCron() []*Crontabs {
|
||||||
return crontabs
|
return crontabs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetCronByCommand(command string) (*Crontabs, error) {
|
||||||
|
cron := new(Crontabs)
|
||||||
|
count, _ := engine.Where("command=?", command).Count(cron)
|
||||||
|
if count < 1 {
|
||||||
|
return nil, errors.New("not found")
|
||||||
|
}
|
||||||
|
_, err := engine.Where("command=?", command).Get(cron)
|
||||||
|
return cron, err
|
||||||
|
}
|
||||||
|
|
||||||
func GetCron(id int) (*Crontabs, error) {
|
func GetCron(id int) (*Crontabs, error) {
|
||||||
cron := new(Crontabs)
|
cron := new(Crontabs)
|
||||||
_, err := engine.ID(id).Get(cron)
|
_, err := engine.ID(id).Get(cron)
|
||||||
|
@ -80,3 +91,12 @@ func DeleteCron(id int) error {
|
||||||
_, err := engine.Table(new(Crontabs)).Delete(&Crontabs{Id: id})
|
_, err := engine.Table(new(Crontabs)).Delete(&Crontabs{Id: id})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Count(searchValue string) int64 {
|
||||||
|
count, _ := engine.Table(new(Crontabs)).
|
||||||
|
Where(
|
||||||
|
builder.Like{"name", "%" + searchValue + "%"}.
|
||||||
|
Or(builder.Like{"command", "%" + searchValue + "%"})).
|
||||||
|
Count()
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
|
@ -2,16 +2,61 @@ package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
"xorm.io/builder"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
NODE = 0
|
||||||
|
PYTHON = 1
|
||||||
|
LINUX = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
type Dependences struct {
|
type Dependences struct {
|
||||||
Id int `xorm:"pk autoincr INTEGER"`
|
Id int `xorm:"pk autoincr INTEGER" json:"id,omitempty"`
|
||||||
Name string `xorm:"VARCHAR(255)"`
|
Name string `xorm:"TEXT" json:"name,omitempty"`
|
||||||
Type string `xorm:"NUMBER"`
|
Type int `xorm:"INTEGER" json:"type,omitempty"`
|
||||||
Timestamp string `xorm:"VARCHAR(255)"`
|
Timestamp string `xorm:"TEXT" json:"timestamp,omitempty"`
|
||||||
Status string `xorm:"NUMBER"`
|
Status int `xorm:"INTEGER" json:"status,omitempty"`
|
||||||
Log string `xorm:"JSON"`
|
Log []string `xorm:"JSON" json:"log,omitempty"`
|
||||||
Remark string `xorm:"VARCHAR(255)"`
|
Remark string `xorm:"TEXT" json:"remark,omitempty"`
|
||||||
Createdat time.Time `xorm:"not null DATETIME created"`
|
Createdat time.Time `xorm:"not null DATETIME created" json:"createdAt"`
|
||||||
Updatedat time.Time `xorm:"not null DATETIME updated"`
|
Updatedat time.Time `xorm:"not null DATETIME updated" json:"updatedAt"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func QueryDependences(searchValue string) ([]*Dependences, error) {
|
||||||
|
dep := make([]*Dependences, 0)
|
||||||
|
session := engine.Table(new(Dependences)).
|
||||||
|
Where(
|
||||||
|
builder.Like{"name", "%" + searchValue + "%"})
|
||||||
|
err := session.Find(&dep)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return dep, err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddDependences(dep *Dependences) (int, error) {
|
||||||
|
_, err := engine.Table(dep).Insert(dep)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
_, _ = engine.Where("name=?", dep.Name).Get(dep)
|
||||||
|
return dep.Id, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetDependences(id int) (*Dependences, error) {
|
||||||
|
env := new(Dependences)
|
||||||
|
_, err := engine.ID(id).Get(env)
|
||||||
|
return env, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateDependences(dep *Dependences) error {
|
||||||
|
_, err := engine.Table(dep).ID(dep.Id).AllCols().Update(dep)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteDependences(id int) error {
|
||||||
|
_, err := engine.Table(new(Dependences)).Delete(&Dependences{Id: id})
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,31 +1,80 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
"fmt"
|
||||||
|
"xorm.io/builder"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Subscriptions struct {
|
type Subscriptions struct {
|
||||||
Id int `xorm:"pk autoincr INTEGER"`
|
Id int `xorm:"pk autoincr INTEGER" json:"id,omitempty"`
|
||||||
Name string `xorm:"VARCHAR(255)"`
|
Name string `xorm:"TEXT" json:"name,omitempty"`
|
||||||
Url string `xorm:"VARCHAR(255)"`
|
Url string `xorm:"TEXT" json:"url,omitempty"`
|
||||||
Schedule string `xorm:"VARCHAR(255)"`
|
Schedule string `xorm:"TEXT" json:"schedule,omitempty"`
|
||||||
IntervalSchedule string `xorm:"JSON"`
|
IntervalSchedule map[string]any `xorm:"JSON" json:"interval_schedule,omitempty"`
|
||||||
Type string `xorm:"VARCHAR(255)"`
|
Type string `xorm:"TEXT" json:"type,omitempty"`
|
||||||
Whitelist string `xorm:"VARCHAR(255)"`
|
Whitelist string `xorm:"TEXT" json:"whitelist,omitempty"`
|
||||||
Blacklist string `xorm:"VARCHAR(255)"`
|
Blacklist string `xorm:"TEXT" json:"blacklist,omitempty"`
|
||||||
Status string `xorm:"NUMBER"`
|
Status int `xorm:"INTEGER default(1)" json:"status,omitempty"`
|
||||||
Dependences string `xorm:"VARCHAR(255)"`
|
Dependences string `xorm:"TEXT" json:"dependences,omitempty"`
|
||||||
Extensions string `xorm:"VARCHAR(255)"`
|
Extensions string `xorm:"TEXT" json:"extensions,omitempty"`
|
||||||
SubBefore string `xorm:"VARCHAR(255)"`
|
SubBefore string `xorm:"TEXT" json:"sub_before,omitempty"`
|
||||||
SubAfter string `xorm:"VARCHAR(255)"`
|
SubAfter string `xorm:"TEXT" json:"sub_after,omitempty"`
|
||||||
Branch string `xorm:"VARCHAR(255)"`
|
Branch string `xorm:"TEXT" json:"branch,omitempty"`
|
||||||
PullType string `xorm:"VARCHAR(255)"`
|
PullType string `xorm:"TEXT" json:"pull_type,omitempty"`
|
||||||
PullOption string `xorm:"JSON"`
|
PullOption string `xorm:"JSON" json:"pull_option,omitempty"`
|
||||||
Pid string `xorm:"NUMBER"`
|
Pid int `xorm:"INTEGER" json:"pid,omitempty"`
|
||||||
IsDisabled string `xorm:"NUMBER"`
|
IsDisabled int `xorm:"INTEGER" json:"is_disabled,omitempty"`
|
||||||
LogPath string `xorm:"VARCHAR(255)"`
|
LogPath string `xorm:"TEXT" json:"log_path,omitempty"`
|
||||||
ScheduleType string `xorm:"VARCHAR(255)"`
|
ScheduleType string `xorm:"TEXT" json:"schedule_type,omitempty"`
|
||||||
Alias string `xorm:"VARCHAR(255) unique"`
|
Alias string `xorm:"TEXT unique" json:"alias,omitempty"`
|
||||||
Createdat time.Time `xorm:"not null DATETIME created"`
|
Createdat string `xorm:"not null DATETIME created" json:"createdat,omitempty"`
|
||||||
Updatedat time.Time `xorm:"not null DATETIME updated"`
|
Updatedat string `xorm:"not null DATETIME updated" json:"updatedat,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func QuerySubscription(searchValue string) ([]*Subscriptions, error) {
|
||||||
|
subscription := make([]*Subscriptions, 0)
|
||||||
|
session := engine.Table(new(Subscriptions)).
|
||||||
|
Where(
|
||||||
|
builder.Like{"name", "%" + searchValue + "%"}.
|
||||||
|
Or(builder.Like{"url", "%" + searchValue + "%"}))
|
||||||
|
err := session.Find(&subscription)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return subscription, err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddSubscription(subscription *Subscriptions) (int, error) {
|
||||||
|
_, err := engine.Table(subscription).Insert(subscription)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
_, _ = engine.Where("name=?", subscription.Name).Get(subscription)
|
||||||
|
return subscription.Id, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetSubscription(id int) (*Subscriptions, error) {
|
||||||
|
env := new(Subscriptions)
|
||||||
|
_, err := engine.ID(id).Get(env)
|
||||||
|
return env, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateSubscription(subscription *Subscriptions) error {
|
||||||
|
_, err := engine.Table(subscription).ID(subscription.Id).AllCols().Update(subscription)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteSubscription(id int) error {
|
||||||
|
_, err := engine.Table(new(Subscriptions)).Delete(&Subscriptions{Id: id})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Subscriptions) GetCron() string {
|
||||||
|
if s.ScheduleType == "interval" {
|
||||||
|
t := s.IntervalSchedule["type"].(string)
|
||||||
|
return fmt.Sprintf("@every %v%s", s.IntervalSchedule["value"], string(t[0]))
|
||||||
|
} else {
|
||||||
|
return s.Schedule
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
log2 "github.com/huoxue1/qinglong-go/utils/log"
|
log2 "github.com/huoxue1/qinglong-go/utils/log"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
_ "modernc.org/sqlite"
|
_ "modernc.org/sqlite"
|
||||||
|
"os"
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -12,7 +13,8 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
en, err := xorm.NewEngine("sqlite", "./data/db/database.sqlite")
|
_ = os.MkdirAll("data/db", 0666)
|
||||||
|
en, err := xorm.NewEngine("sqlite", "data/db/database.sqlite")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorln("[sql] " + err.Error())
|
log.Errorln("[sql] " + err.Error())
|
||||||
return
|
return
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"regexp"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetKey(key string) string {
|
||||||
|
file, err := os.ReadFile(path.Join("data", "config", "config.sh"))
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
compile := regexp.MustCompile(key + `="(.*?)"`)
|
||||||
|
if !compile.Match(file) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
datas := compile.FindAllStringSubmatch(string(file), 1)
|
||||||
|
return datas[0][1]
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestGetKey(t *testing.T) {
|
||||||
|
println(GetKey("AutoDelCron"))
|
||||||
|
}
|
|
@ -39,7 +39,9 @@ func UpdateCron(c1 *models.Crontabs) error {
|
||||||
crontabs.Updatedat = time.Now().Format(time.RFC3339)
|
crontabs.Updatedat = time.Now().Format(time.RFC3339)
|
||||||
|
|
||||||
c, _ := manager.Load(c1.Id)
|
c, _ := manager.Load(c1.Id)
|
||||||
c.(*cron.Cron).Stop()
|
if c != nil {
|
||||||
|
c.(*cron.Cron).Stop()
|
||||||
|
}
|
||||||
AddTask(c1)
|
AddTask(c1)
|
||||||
|
|
||||||
return models.UpdateCron(crontabs)
|
return models.UpdateCron(crontabs)
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
package cron
|
package cron
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/huoxue1/qinglong-go/models"
|
"github.com/huoxue1/qinglong-go/models"
|
||||||
"github.com/huoxue1/qinglong-go/service/env"
|
"github.com/huoxue1/qinglong-go/service/env"
|
||||||
|
"github.com/huoxue1/qinglong-go/utils"
|
||||||
"github.com/robfig/cron/v3"
|
"github.com/robfig/cron/v3"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"io"
|
"io"
|
||||||
|
@ -48,8 +50,8 @@ func stopCron(crontabs *models.Crontabs) {
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
cmd := value.(*exec.Cmd)
|
cancel := value.(func())
|
||||||
_ = cmd.Process.Kill()
|
cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
func runCron(crontabs *models.Crontabs) {
|
func runCron(crontabs *models.Crontabs) {
|
||||||
|
@ -72,7 +74,7 @@ func runCron(crontabs *models.Crontabs) {
|
||||||
}
|
}
|
||||||
values := strings.Split(value, "&")
|
values := strings.Split(value, "&")
|
||||||
for _, index := range indexs {
|
for _, index := range indexs {
|
||||||
logFile, _ := os.OpenFile("data/log/"+time.Now().Format("2006-01-02")+"/"+crontabs.Name+".log", os.O_RDWR, 0666)
|
logFile, _ := os.OpenFile("data/log/"+time.Now().Format("2006-01-02")+"/"+crontabs.Name+"_"+uuid.New().String()+".log", os.O_RDWR|os.O_CREATE, 0666)
|
||||||
e2 := exec.Command(strings.Split(ta.cmd, " ")[0], strings.Split(ta.cmd, " ")[1:]...)
|
e2 := exec.Command(strings.Split(ta.cmd, " ")[0], strings.Split(ta.cmd, " ")[1:]...)
|
||||||
e2.Env = []string{e + "=" + values[index]}
|
e2.Env = []string{e + "=" + values[index]}
|
||||||
stdoutPipe, _ := e2.StdoutPipe()
|
stdoutPipe, _ := e2.StdoutPipe()
|
||||||
|
@ -87,7 +89,7 @@ func runCron(crontabs *models.Crontabs) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorln(err.Error())
|
log.Errorln(err.Error())
|
||||||
}
|
}
|
||||||
go syncLog(stdoutPipe, logFile)
|
go io.Copy(logFile, stdoutPipe)
|
||||||
e2.Wait()
|
e2.Wait()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -101,29 +103,28 @@ func runCron(crontabs *models.Crontabs) {
|
||||||
crontabs.LogPath = "data/log/" + time.Now().Format("2006-01-02") + "/" + crontabs.Name + "_" + uuid.New().String() + ".log"
|
crontabs.LogPath = "data/log/" + time.Now().Format("2006-01-02") + "/" + crontabs.Name + "_" + uuid.New().String() + ".log"
|
||||||
crontabs.Status = 0
|
crontabs.Status = 0
|
||||||
models.UpdateCron(crontabs)
|
models.UpdateCron(crontabs)
|
||||||
|
cancelChan := make(chan int, 1)
|
||||||
|
ctx := context.WithValue(context.Background(), "cancel", cancelChan)
|
||||||
|
execManager.Store(crontabs.Id, func() {
|
||||||
|
cancelChan <- 1
|
||||||
|
})
|
||||||
|
now := time.Now()
|
||||||
_ = os.Mkdir("data/log/"+time.Now().Format("2006-01-02"), 0666)
|
_ = os.Mkdir("data/log/"+time.Now().Format("2006-01-02"), 0666)
|
||||||
logFile := &myWriter{crontabs.LogPath}
|
file, _ := os.OpenFile(crontabs.LogPath, os.O_RDWR|os.O_CREATE, 0666)
|
||||||
|
go utils.RunTask(ctx, ta.cmd, envFromDb,
|
||||||
e2 := exec.Command(strings.Split(ta.cmd, " ")[0], strings.Split(ta.cmd, " ")[1:]...)
|
func(ctx context.Context) {
|
||||||
execManager.Store(crontabs.Id, e2)
|
writer := ctx.Value("log").(io.Writer)
|
||||||
stdoutPipe, _ := e2.StdoutPipe()
|
writer.Write([]byte(fmt.Sprintf("##开始执行.. %s\n\n", now.Format("2006-01-02 15:04:05"))))
|
||||||
for s, s2 := range envFromDb {
|
}, func(ctx context.Context) {
|
||||||
e2.Env = append(e2.Env, s+"="+s2)
|
writer := ctx.Value("log").(io.Writer)
|
||||||
}
|
writer.Write([]byte(fmt.Sprintf("\n##执行结束.. %s,耗时%.1f秒\n\n", time.Now().Format("2006-01-02 15:04:05"), time.Now().Sub(now).Seconds())))
|
||||||
startTime := time.Now()
|
crontabs.Status = 1
|
||||||
logFile.Write([]byte(fmt.Sprintf("##开始执行.. %s\n\n", startTime.Format("2006-01-02 15:04:05"))))
|
crontabs.LastExecutionTime = now.Unix()
|
||||||
go func() {
|
crontabs.LastRunningTime = int64(time.Now().Sub(now).Seconds())
|
||||||
err := e2.Start()
|
models.UpdateCron(crontabs)
|
||||||
if err != nil {
|
execManager.LoadAndDelete(crontabs.Id)
|
||||||
log.Errorln(err.Error())
|
file.Close()
|
||||||
}
|
}, file)
|
||||||
go syncLog(stdoutPipe, logFile)
|
|
||||||
e2.Wait()
|
|
||||||
logFile.Write([]byte(fmt.Sprintf("##执行结束.. %s,耗时%.1f秒\n\n", time.Now().Format("2006-01-02 15:04:05"), time.Now().Sub(startTime).Seconds())))
|
|
||||||
crontabs.Status = 1
|
|
||||||
models.UpdateCron(crontabs)
|
|
||||||
execManager.LoadAndDelete(crontabs.Id)
|
|
||||||
}()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,11 +146,11 @@ func handCommand(command string) *task {
|
||||||
commands := strings.Split(command, " ")
|
commands := strings.Split(command, " ")
|
||||||
if commands[0] == "task" {
|
if commands[0] == "task" {
|
||||||
if strings.HasSuffix(commands[1], ".py") {
|
if strings.HasSuffix(commands[1], ".py") {
|
||||||
ta.cmd = "python data/scripts/" + commands[1]
|
ta.cmd = "python " + commands[1]
|
||||||
} else if strings.HasSuffix(commands[1], ".js") {
|
} else if strings.HasSuffix(commands[1], ".js") {
|
||||||
ta.cmd = "node data/scripts/" + commands[1]
|
ta.cmd = "node " + commands[1]
|
||||||
} else if strings.HasSuffix(commands[1], ".sh") {
|
} else if strings.HasSuffix(commands[1], ".sh") {
|
||||||
ta.cmd = "bash data/scripts/" + commands[1]
|
ta.cmd = "bash " + commands[1]
|
||||||
} else if strings.HasSuffix(commands[1], ".ts") {
|
} else if strings.HasSuffix(commands[1], ".ts") {
|
||||||
ta.cmd = "ts-node-transpile-only data/scripts/" + commands[1]
|
ta.cmd = "ts-node-transpile-only data/scripts/" + commands[1]
|
||||||
}
|
}
|
||||||
|
@ -189,31 +190,31 @@ func handCommand(command string) *task {
|
||||||
return ta
|
return ta
|
||||||
}
|
}
|
||||||
|
|
||||||
type myWriter struct {
|
//type myWriter struct {
|
||||||
fileName string
|
// fileName string
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
func (m *myWriter) Write(p []byte) (n int, err error) {
|
//func (m *myWriter) Write(p []byte) (n int, err error) {
|
||||||
file, _ := os.OpenFile(m.fileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
// file, _ := os.OpenFile(m.fileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
||||||
n, err = file.Write(p)
|
// n, err = file.Write(p)
|
||||||
file.Close()
|
// file.Close()
|
||||||
return n, err
|
// return n, err
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
//通过管道同步获取日志的函数
|
////通过管道同步获取日志的函数
|
||||||
func syncLog(reader io.ReadCloser, writer io.Writer) {
|
//func syncLog(reader io.ReadCloser, writer io.Writer) {
|
||||||
buf := make([]byte, 1)
|
// buf := make([]byte, 1)
|
||||||
for {
|
// for {
|
||||||
strNum, err := reader.Read(buf)
|
// strNum, err := reader.Read(buf)
|
||||||
if strNum > 0 {
|
// if strNum > 0 {
|
||||||
outputByte := buf[:strNum]
|
// outputByte := buf[:strNum]
|
||||||
writer.Write(outputByte)
|
// writer.Write(outputByte)
|
||||||
}
|
// }
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
//读到结尾
|
// //读到结尾
|
||||||
if err == io.EOF || strings.Contains(err.Error(), "file already closed") {
|
// if err == io.EOF || strings.Contains(err.Error(), "file already closed") {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
package dependencies
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/huoxue1/qinglong-go/models"
|
||||||
|
"github.com/huoxue1/qinglong-go/utils"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func AddDep(dep *models.Dependences) {
|
||||||
|
if dep.Type == models.NODE {
|
||||||
|
addNodeDep(dep)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func addNodeDep(dep *models.Dependences) {
|
||||||
|
log := ""
|
||||||
|
buffer := bytes.NewBufferString(log)
|
||||||
|
ctx := context.WithValue(context.Background(), "cancel", make(chan int, 1))
|
||||||
|
now := time.Now()
|
||||||
|
utils.RunTask(ctx, fmt.Sprintf("yarn add %s", dep.Name), map[string]string{}, func(ctx context.Context) {
|
||||||
|
writer := ctx.Value("log").(io.Writer)
|
||||||
|
writer.Write([]byte(fmt.Sprintf("##开始执行.. %s\n\n", now.Format("2006-01-02 15:04:05"))))
|
||||||
|
}, func(ctx context.Context) {
|
||||||
|
writer := ctx.Value("log").(io.Writer)
|
||||||
|
writer.Write([]byte(fmt.Sprintf("\n##执行结束.. %s,耗时%.1f秒\n\n", time.Now().Format("2006-01-02 15:04:05"), time.Now().Sub(now).Seconds())))
|
||||||
|
dep.Status = 1
|
||||||
|
var logs []string
|
||||||
|
for _, i2 := range strings.Split(buffer.String(), "\n") {
|
||||||
|
logs = append(logs, i2+"\n\n")
|
||||||
|
}
|
||||||
|
dep.Log = logs
|
||||||
|
models.AddDependences(dep)
|
||||||
|
}, buffer)
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
package open
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/huoxue1/qinglong-go/models"
|
||||||
|
"github.com/huoxue1/qinglong-go/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func AddApp(apps *models.Apps) (int, error) {
|
||||||
|
apps.ClientId = utils.RandomString(6)
|
||||||
|
apps.ClientSecret = utils.RandomString(12)
|
||||||
|
apps.Tokens = []string{}
|
||||||
|
id, err := models.AddApp(apps)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return id, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateApp(apps *models.Apps) error {
|
||||||
|
app, err := models.GetApp(apps.Id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
app.Name = apps.Name
|
||||||
|
app.Scopes = apps.Scopes
|
||||||
|
err = models.UpdateApp(app)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ResetApp(apps *models.Apps) error {
|
||||||
|
apps.ClientSecret = utils.RandomString(12)
|
||||||
|
apps.Tokens = []string{}
|
||||||
|
err := models.UpdateApp(apps)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteApp(ids []int) error {
|
||||||
|
for _, id := range ids {
|
||||||
|
err := models.DeleteApp(id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -1,6 +1,12 @@
|
||||||
package scripts
|
package scripts
|
||||||
|
|
||||||
import "os"
|
import (
|
||||||
|
"bytes"
|
||||||
|
"github.com/huoxue1/qinglong-go/utils"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
type File struct {
|
type File struct {
|
||||||
Key string `json:"key"`
|
Key string `json:"key"`
|
||||||
|
@ -11,48 +17,38 @@ type File struct {
|
||||||
Children []*File `json:"children"`
|
Children []*File `json:"children"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetFiles() []*File {
|
var (
|
||||||
var files []*File
|
excludedFiles = []string{
|
||||||
dir, err := os.ReadDir("data/scripts/")
|
"node_modules",
|
||||||
|
"__pycache__",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetFiles(base, p string) []*File {
|
||||||
|
var files Files
|
||||||
|
dir, err := os.ReadDir(path.Join(base, p))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []*File{}
|
return []*File{}
|
||||||
}
|
}
|
||||||
for _, entry := range dir {
|
for _, entry := range dir {
|
||||||
|
if utils.In(entry.Name(), excludedFiles) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if entry.IsDir() {
|
if entry.IsDir() {
|
||||||
f := &File{
|
f := &File{
|
||||||
Key: entry.Name(),
|
Key: path.Join(p, entry.Name()),
|
||||||
Parent: "",
|
Parent: p,
|
||||||
Title: entry.Name(),
|
Title: entry.Name(),
|
||||||
Type: "directory",
|
Type: "directory",
|
||||||
IsLeaf: true,
|
IsLeaf: true,
|
||||||
Children: []*File{},
|
Children: GetFiles(base, path.Join(p, entry.Name())),
|
||||||
}
|
|
||||||
twoDir, err := os.ReadDir("data/scripts/" + entry.Name())
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, dirEntry := range twoDir {
|
|
||||||
f.Children = append(f.Children, &File{
|
|
||||||
Key: entry.Name() + "/" + dirEntry.Name(),
|
|
||||||
Parent: entry.Name(),
|
|
||||||
Title: dirEntry.Name(),
|
|
||||||
Type: func() string {
|
|
||||||
if dirEntry.IsDir() {
|
|
||||||
return "directory"
|
|
||||||
} else {
|
|
||||||
return "file"
|
|
||||||
}
|
|
||||||
}(),
|
|
||||||
IsLeaf: true,
|
|
||||||
Children: []*File{},
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
files = append(files, f)
|
files = append(files, f)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
files = append(files, &File{
|
files = append(files, &File{
|
||||||
Key: entry.Name(),
|
Key: path.Join(p, entry.Name()),
|
||||||
Parent: "",
|
Parent: p,
|
||||||
Title: entry.Name(),
|
Title: entry.Name(),
|
||||||
Type: "file",
|
Type: "file",
|
||||||
IsLeaf: true,
|
IsLeaf: true,
|
||||||
|
@ -60,5 +56,26 @@ func GetFiles() []*File {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
sort.Sort(files)
|
||||||
return files
|
return files
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Files []*File
|
||||||
|
|
||||||
|
func (a Files) Len() int { // 重写 Len() 方法
|
||||||
|
return len(a)
|
||||||
|
}
|
||||||
|
func (a Files) Swap(i, j int) { // 重写 Swap() 方法
|
||||||
|
a[i], a[j] = a[j], a[i]
|
||||||
|
}
|
||||||
|
func (a Files) Less(i, j int) bool { // 重写 Less() 方法, 从大到小排序
|
||||||
|
if a[i].Type != a[j].Type {
|
||||||
|
if a[i].Type == "file" {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return bytes.Compare([]byte(a[i].Title), []byte(a[j].Title)) > 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,163 @@
|
||||||
|
package subscription
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/huoxue1/qinglong-go/models"
|
||||||
|
"github.com/huoxue1/qinglong-go/service/config"
|
||||||
|
"github.com/huoxue1/qinglong-go/service/cron"
|
||||||
|
"github.com/huoxue1/qinglong-go/utils"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
manager sync.Map
|
||||||
|
)
|
||||||
|
|
||||||
|
func stopSubscription(sub *models.Subscriptions) {
|
||||||
|
defer func() {
|
||||||
|
_ = recover()
|
||||||
|
}()
|
||||||
|
sub.Status = 1
|
||||||
|
_ = models.UpdateSubscription(sub)
|
||||||
|
value, ok := manager.Load(sub.Id)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cancel := value.(func())
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
func downloadFiles(subscriptions *models.Subscriptions) {
|
||||||
|
if subscriptions.Type == "public-repo" {
|
||||||
|
os.RemoveAll(path.Join("data", "scripts", subscriptions.Alias))
|
||||||
|
os.RemoveAll(path.Join("data", "repo", subscriptions.Alias))
|
||||||
|
err := downloadPublicRepo(subscriptions)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
addScripts(subscriptions)
|
||||||
|
file, _ := os.OpenFile(subscriptions.LogPath, os.O_APPEND|os.O_RDWR, 0666)
|
||||||
|
file.WriteString(fmt.Sprintf("\n##执行结束.. %s,耗时0秒\n\n", time.Now().Format("2006-01-02 15:04:05")))
|
||||||
|
_ = file.Close()
|
||||||
|
subscriptions.Status = 1
|
||||||
|
models.UpdateSubscription(subscriptions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func downloadPublicRepo(subscriptions *models.Subscriptions) error {
|
||||||
|
subscriptions.LogPath = "data/log/" + time.Now().Format("2006-01-02") + "/" + subscriptions.Alias + "_" + uuid.New().String() + ".log"
|
||||||
|
_ = os.MkdirAll(filepath.Dir(subscriptions.LogPath), 0666)
|
||||||
|
cmd := fmt.Sprintf("clone -b %s --single-branch %s %s", subscriptions.Branch, subscriptions.Url, path.Join("data", "repo", subscriptions.Alias))
|
||||||
|
command := exec.Command("git", strings.Split(cmd, " ")...)
|
||||||
|
pipe, err := command.StdoutPipe()
|
||||||
|
stderrPipe, _ := command.StderrPipe()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
subscriptions.Status = 0
|
||||||
|
err = models.UpdateSubscription(subscriptions)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
file, _ := os.OpenFile(subscriptions.LogPath, os.O_CREATE|os.O_RDWR, 0666)
|
||||||
|
file.Write([]byte(fmt.Sprintf("##开始执行.. %s\n\n", time.Now().Format("2006-01-02 15:04:05"))))
|
||||||
|
err = command.Start()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
manager.Store(subscriptions.Id, func() {
|
||||||
|
command.Process.Kill()
|
||||||
|
})
|
||||||
|
defer manager.LoadAndDelete(subscriptions.Id)
|
||||||
|
go io.Copy(io.MultiWriter(file, os.Stdout), pipe)
|
||||||
|
go io.Copy(file, stderrPipe)
|
||||||
|
command.Wait()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func addScripts(subscriptions *models.Subscriptions) {
|
||||||
|
file, _ := os.OpenFile(subscriptions.LogPath, os.O_CREATE|os.O_RDWR, 0666)
|
||||||
|
defer file.Close()
|
||||||
|
var extensions []string
|
||||||
|
if subscriptions.Extensions != "" {
|
||||||
|
extensions = strings.Split(subscriptions.Extensions, " ")
|
||||||
|
} else {
|
||||||
|
extensions = strings.Split(config.GetKey("RepoFileExtensions"), " ")
|
||||||
|
}
|
||||||
|
dir, err := os.ReadDir(path.Join("data", "repo", subscriptions.Alias))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, entry := range dir {
|
||||||
|
// 判断文件后缀
|
||||||
|
if !utils.In(strings.TrimPrefix(filepath.Ext(entry.Name()), "."), extensions) {
|
||||||
|
if !entry.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 判断黑名单
|
||||||
|
if utils.In(entry.Name(), strings.Split(subscriptions.Blacklist, "|")) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
compile := regexp.MustCompile(`(` + subscriptions.Whitelist + `)`)
|
||||||
|
if compile.MatchString(entry.Name()) {
|
||||||
|
name, c, _ := getSubCron(path.Join("data", "repo", subscriptions.Alias, entry.Name()))
|
||||||
|
if c != "" {
|
||||||
|
command, err := models.GetCronByCommand(fmt.Sprintf("task %s", path.Join(subscriptions.Alias, entry.Name())))
|
||||||
|
if err != nil {
|
||||||
|
file.WriteString("已添加新的定时任务 " + name + "\n")
|
||||||
|
_, _ = cron.AddCron(&models.Crontabs{
|
||||||
|
Name: name,
|
||||||
|
Command: fmt.Sprintf("task %s", path.Join(subscriptions.Alias, entry.Name())),
|
||||||
|
Schedule: c,
|
||||||
|
Timestamp: time.Now().Format("Mon Jan 02 2006 15:04:05 MST"),
|
||||||
|
Status: 1,
|
||||||
|
Labels: []string{},
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
command.Name = name
|
||||||
|
command.Schedule = c
|
||||||
|
_ = cron.UpdateCron(command)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
utils.Copy(path.Join("data", "repo", subscriptions.Alias, entry.Name()), path.Join("data", "scripts", subscriptions.Alias, entry.Name()))
|
||||||
|
} else {
|
||||||
|
depen := regexp.MustCompile(`(` + subscriptions.Dependences + `)`)
|
||||||
|
if depen.MatchString(entry.Name()) {
|
||||||
|
utils.Copy(path.Join("data", "repo", subscriptions.Alias, entry.Name()), path.Join("data", "scripts", subscriptions.Alias, entry.Name()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSubCron(filePath string) (name string, cron string, err error) {
|
||||||
|
data, err := os.ReadFile(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
cronReg := regexp.MustCompile(`([0-9\-*/,]{1,} ){4,5}([0-9\-*/,]){1,}`)
|
||||||
|
nameEnv := regexp.MustCompile(`new\sEnv\(['|"](.*?)['|"]\)`)
|
||||||
|
if cronReg.Match(data) {
|
||||||
|
cron = string(cronReg.FindAll(data, 1)[0])
|
||||||
|
cron = strings.TrimPrefix(cron, "//")
|
||||||
|
if nameEnv.Match(data) {
|
||||||
|
name = string(nameEnv.FindAllSubmatch(data, 1)[0][1])
|
||||||
|
} else {
|
||||||
|
name = path.Base(filePath)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return "", "", errors.New("not found cron")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
package subscription
|
||||||
|
|
||||||
|
import "github.com/huoxue1/qinglong-go/models"
|
||||||
|
|
||||||
|
var (
|
||||||
|
DISABLESTATUS = 1
|
||||||
|
ENABLESTATUS = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
func AddSubscription(subscriptions *models.Subscriptions) (int, error) {
|
||||||
|
subscriptions.Status = 1
|
||||||
|
return models.AddSubscription(subscriptions)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateSubscription(subscriptions *models.Subscriptions) error {
|
||||||
|
return models.UpdateSubscription(subscriptions)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteSubscription(ids []int) error {
|
||||||
|
for _, id := range ids {
|
||||||
|
err := models.DeleteSubscription(id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func DisableSubscription(ids []int) error {
|
||||||
|
for _, id := range ids {
|
||||||
|
sub, err := models.GetSubscription(id)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
sub.IsDisabled = 1
|
||||||
|
err = models.UpdateSubscription(sub)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func EnableSubscription(ids []int) error {
|
||||||
|
for _, id := range ids {
|
||||||
|
sub, err := models.GetSubscription(id)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
sub.IsDisabled = 0
|
||||||
|
err = models.UpdateSubscription(sub)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func RunSubscription(ids []int) error {
|
||||||
|
for _, id := range ids {
|
||||||
|
sub, err := models.GetSubscription(id)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
sub.IsDisabled = 0
|
||||||
|
go downloadFiles(sub)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func StopSubscription(ids []int) error {
|
||||||
|
for _, id := range ids {
|
||||||
|
sub, err := models.GetSubscription(id)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
stopSubscription(sub)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
func In[T comparable](data T, array []T) bool {
|
||||||
|
for _, t := range array {
|
||||||
|
if t == data {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Copy(src, dest string) {
|
||||||
|
srcInfo, err := os.Stat(src)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, err = os.Stat(dest)
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
if srcInfo.IsDir() {
|
||||||
|
_ = os.MkdirAll(dest, 0666)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if srcInfo.IsDir() {
|
||||||
|
log.Infoln("复制文件")
|
||||||
|
dir, err := os.ReadDir(src)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, entry := range dir {
|
||||||
|
if entry.IsDir() {
|
||||||
|
Copy(path.Join(src, entry.Name()), path.Join(dest, entry.Name()))
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
file, _ := os.Open(path.Join(src, entry.Name()))
|
||||||
|
newFile, _ := os.OpenFile(path.Join(dest, entry.Name()), os.O_RDWR|os.O_CREATE, 0666)
|
||||||
|
_, err := io.Copy(newFile, file)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_ = os.MkdirAll(filepath.Dir(dest), 0666)
|
||||||
|
file, _ := os.Open(src)
|
||||||
|
newFile, _ := os.OpenFile(dest, os.O_RDWR|os.O_CREATE, 0666)
|
||||||
|
_, err := io.Copy(newFile, file)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
86
utils/jwt.go
86
utils/jwt.go
|
@ -3,14 +3,8 @@ package utils
|
||||||
// jwt身份验证demo
|
// jwt身份验证demo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"github.com/dgrijalva/jwt-go"
|
||||||
"errors"
|
"math/rand"
|
||||||
jwt "github.com/dgrijalva/jwt-go"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/huoxue1/qinglong-go/models"
|
|
||||||
"github.com/huoxue1/qinglong-go/utils/res"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -24,9 +18,9 @@ type Claims struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateToken 生成token的函数
|
// GenerateToken 生成token的函数
|
||||||
func GenerateToken(userid string) (string, error) {
|
func GenerateToken(userid string, hour int) (string, error) {
|
||||||
nowTime := time.Now()
|
nowTime := time.Now()
|
||||||
expireTime := nowTime.Add(48 * time.Hour)
|
expireTime := nowTime.Add(time.Duration(hour) * time.Hour)
|
||||||
|
|
||||||
claims := Claims{
|
claims := Claims{
|
||||||
userid, // 自行添加的信息
|
userid, // 自行添加的信息
|
||||||
|
@ -59,68 +53,6 @@ func ParseToken(token string) (*Claims, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
unExcludedPath = []string{
|
|
||||||
"/api/system",
|
|
||||||
"/api/user/login",
|
|
||||||
"/api/user/init",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func Jwt() gin.HandlerFunc {
|
|
||||||
return func(ctx *gin.Context) {
|
|
||||||
for _, s := range unExcludedPath {
|
|
||||||
if strings.HasPrefix(ctx.Request.URL.Path, s) {
|
|
||||||
ctx.Next()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := os.ReadFile(path.Join("data", "config", "auth.json"))
|
|
||||||
if err != nil {
|
|
||||||
ctx.Abort()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
auth := new(models.AuthFile)
|
|
||||||
_ = json.Unmarshal(data, auth)
|
|
||||||
tokenHeader := ctx.GetHeader("Authorization")
|
|
||||||
if tokenHeader == "" {
|
|
||||||
ctx.JSON(401, res.Err(401, errors.New("no authorization token was found")))
|
|
||||||
ctx.Abort()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
authToken := strings.Split(tokenHeader, " ")[1]
|
|
||||||
|
|
||||||
mobile := IsMobile(ctx.GetHeader("User-Agent"))
|
|
||||||
claims, _ := ParseToken(authToken)
|
|
||||||
if claims.ExpiresAt < time.Now().Unix() {
|
|
||||||
ctx.JSON(401, res.Err(401, errors.New("the authorization token is expired")))
|
|
||||||
ctx.Abort()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if mobile {
|
|
||||||
if authToken != auth.Tokens.Mobile && authToken != auth.Token {
|
|
||||||
ctx.JSON(401, res.Err(401, errors.New("the authorization token is error")))
|
|
||||||
ctx.Abort()
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
ctx.Next()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if authToken != auth.Tokens.Desktop && authToken != auth.Token {
|
|
||||||
ctx.JSON(401, res.Err(401, errors.New("the authorization token is error")))
|
|
||||||
ctx.Abort()
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
ctx.Next()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsMobile(userAgent string) bool {
|
func IsMobile(userAgent string) bool {
|
||||||
if len(userAgent) == 0 {
|
if len(userAgent) == 0 {
|
||||||
return false
|
return false
|
||||||
|
@ -139,3 +71,13 @@ func IsMobile(userAgent string) bool {
|
||||||
|
|
||||||
return isMobile
|
return isMobile
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func RandomString(n int) string {
|
||||||
|
var letter = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-?")
|
||||||
|
rand.Seed(time.Now().UnixMilli())
|
||||||
|
b := make([]rune, n)
|
||||||
|
for i := range b {
|
||||||
|
b[i] = letter[rand.Intn(len(letter))]
|
||||||
|
}
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Context struct {
|
||||||
|
process *os.Process
|
||||||
|
}
|
||||||
|
|
||||||
|
func RunTask(ctx context.Context, command string, env map[string]string, onStart func(ctx context.Context), onEnd func(ctx context.Context), logFile io.Writer) {
|
||||||
|
cmd := exec.Command(strings.Split(command, " ")[0], strings.Split(command, " ")[1:]...)
|
||||||
|
for s, s2 := range env {
|
||||||
|
cmd.Env = append(cmd.Env, s+"="+s2)
|
||||||
|
}
|
||||||
|
stdoutPipe, _ := cmd.StdoutPipe()
|
||||||
|
stderrPipe, _ := cmd.StderrPipe()
|
||||||
|
cmd.Dir = "./data/scripts/"
|
||||||
|
onStart(context.WithValue(ctx, "log", logFile))
|
||||||
|
ch := make(chan int, 1)
|
||||||
|
go func() {
|
||||||
|
err := cmd.Start()
|
||||||
|
if err != nil {
|
||||||
|
ch <- 1
|
||||||
|
return
|
||||||
|
}
|
||||||
|
go io.Copy(logFile, stderrPipe)
|
||||||
|
go io.Copy(logFile, stdoutPipe)
|
||||||
|
err = cmd.Wait()
|
||||||
|
if err != nil {
|
||||||
|
ch <- 1
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ch <- 1
|
||||||
|
}()
|
||||||
|
cancel := ctx.Value("cancel").(chan int)
|
||||||
|
select {
|
||||||
|
case <-ch:
|
||||||
|
{
|
||||||
|
onEnd(context.WithValue(ctx, "log", logFile))
|
||||||
|
}
|
||||||
|
case <-cancel:
|
||||||
|
{
|
||||||
|
_ = cmd.Process.Kill()
|
||||||
|
onEnd(context.WithValue(context.Background(), "log", logFile))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue