mirror of
https://gitee.com/goploy/goploy.git
synced 2024-11-29 18:57:59 +08:00
U refactor
This commit is contained in:
parent
2dcc8d68d1
commit
05c15c06ab
2
LICENSE
2
LICENSE
@ -356,7 +356,7 @@ remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
for which you have or can give appropriate copyright router.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
|
154
cmd/server/api/agent.go
Normal file
154
cmd/server/api/agent.go
Normal file
@ -0,0 +1,154 @@
|
||||
// Copyright 2022 The Goploy Authors. All rights reserved.
|
||||
// Use of this source code is governed by a GPLv3-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/zhenorzz/goploy/cmd/server/api/middleware"
|
||||
"github.com/zhenorzz/goploy/internal/server"
|
||||
"github.com/zhenorzz/goploy/internal/server/response"
|
||||
"github.com/zhenorzz/goploy/model"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type Agent API
|
||||
|
||||
func (a Agent) Handler() []server.Route {
|
||||
return []server.Route{
|
||||
server.NewWhiteRoute("/agent/report", http.MethodPost, a.Report).Middleware(middleware.CheckSign),
|
||||
server.NewWhiteRoute("/agent/getServerID", http.MethodPost, a.GetServerID).Middleware(middleware.CheckSign),
|
||||
server.NewWhiteRoute("/agent/getCronList", http.MethodPost, a.GetCronList).Middleware(middleware.CheckSign),
|
||||
server.NewWhiteRoute("/agent/getCronLogs", http.MethodPost, a.GetCronLogs).Middleware(middleware.CheckSign),
|
||||
server.NewWhiteRoute("/agent/cronReport", http.MethodPost, a.CronReport).Middleware(middleware.CheckSign),
|
||||
}
|
||||
}
|
||||
|
||||
func (Agent) GetServerID(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
Name string `json:"name"`
|
||||
IP string `json:"ip"`
|
||||
}
|
||||
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
s, err := model.Server{
|
||||
Name: reqData.Name,
|
||||
IP: reqData.IP,
|
||||
}.GetData()
|
||||
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
ID int64 `json:"id"`
|
||||
}{ID: s.ID},
|
||||
}
|
||||
}
|
||||
|
||||
func (Agent) GetCronList(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
ServerID int64 `json:"serverId" validate:"gt=0"`
|
||||
}
|
||||
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
crons, err := model.Cron{ServerID: reqData.ServerID}.GetList()
|
||||
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
List model.Crons `json:"list"`
|
||||
}{List: crons},
|
||||
}
|
||||
}
|
||||
|
||||
func (Agent) GetCronLogs(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
ServerID int64 `json:"serverId" validate:"gt=0"`
|
||||
CronID int64 `json:"cronId" validate:"gt=0"`
|
||||
Page uint64 `json:"page" validate:"gt=0"`
|
||||
Rows uint64 `json:"rows" validate:"gt=0"`
|
||||
}
|
||||
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
crons, err := model.CronLog{ServerID: reqData.ServerID, CronID: reqData.CronID}.GetList(reqData.Page, reqData.Rows)
|
||||
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
List model.CronLogs `json:"list"`
|
||||
}{List: crons},
|
||||
}
|
||||
}
|
||||
|
||||
func (Agent) CronReport(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
ServerId int64 `json:"serverId" validate:"gt=0"`
|
||||
CronId int64 `json:"cronId" validate:"gt=0"`
|
||||
ExecCode int `json:"execCode"`
|
||||
Message string `json:"message" validate:"required"`
|
||||
ReportTime string `json:"reportTime" validate:"required"`
|
||||
}
|
||||
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
err := model.CronLog{
|
||||
ServerID: reqData.ServerId,
|
||||
CronID: reqData.CronId,
|
||||
ExecCode: reqData.ExecCode,
|
||||
Message: reqData.Message,
|
||||
ReportTime: reqData.ReportTime,
|
||||
}.AddRow()
|
||||
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{}
|
||||
}
|
||||
|
||||
func (Agent) Report(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
ServerId int64 `json:"serverId" validate:"gt=0"`
|
||||
Type int `json:"type" validate:"gt=0"`
|
||||
Item string `json:"item" validate:"required"`
|
||||
Value string `json:"value" validate:"required"`
|
||||
ReportTime string `json:"reportTime" validate:"required"`
|
||||
}
|
||||
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
err := model.ServerAgentLog{
|
||||
ServerID: reqData.ServerId,
|
||||
Type: reqData.Type,
|
||||
Item: reqData.Item,
|
||||
Value: reqData.Value,
|
||||
ReportTime: reqData.ReportTime,
|
||||
}.AddRow()
|
||||
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{}
|
||||
}
|
110
cmd/server/api/api.go
Normal file
110
cmd/server/api/api.go
Normal file
@ -0,0 +1,110 @@
|
||||
// Copyright 2022 The Goploy Authors. All rights reserved.
|
||||
// Use of this source code is governed by a GPLv3-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"github.com/go-playground/locales/en"
|
||||
ut "github.com/go-playground/universal-translator"
|
||||
"github.com/gorilla/schema"
|
||||
"gopkg.in/go-playground/validator.v9"
|
||||
enTranslations "gopkg.in/go-playground/validator.v9/translations/en"
|
||||
"reflect"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
type API struct{}
|
||||
|
||||
// Validate use a single instance of Validate, it caches struct info
|
||||
var Validate *validator.Validate
|
||||
|
||||
// Trans Translator
|
||||
var Trans ut.Translator
|
||||
|
||||
func init() {
|
||||
english := en.New()
|
||||
uni := ut.New(english, english)
|
||||
Trans, _ = uni.GetTranslator("english")
|
||||
Validate = validator.New()
|
||||
enTranslations.RegisterDefaultTranslations(Validate, Trans)
|
||||
Validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
|
||||
name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
|
||||
if name == "-" {
|
||||
return ""
|
||||
}
|
||||
return name
|
||||
})
|
||||
Validate.RegisterValidation("password", func(fl validator.FieldLevel) bool {
|
||||
password := fl.Field().String()
|
||||
if len(password) < 8 || len(password) > 16 {
|
||||
return false
|
||||
}
|
||||
var (
|
||||
hasLetter = false
|
||||
hasNumber = false
|
||||
hasSpecial = false
|
||||
)
|
||||
|
||||
for _, char := range password {
|
||||
switch {
|
||||
case unicode.IsLetter(char):
|
||||
hasLetter = true
|
||||
case unicode.IsNumber(char):
|
||||
hasNumber = true
|
||||
case unicode.IsPunct(char) || unicode.IsSymbol(char):
|
||||
hasSpecial = true
|
||||
}
|
||||
}
|
||||
|
||||
if hasLetter && hasNumber {
|
||||
return true
|
||||
} else if hasLetter && hasSpecial {
|
||||
return true
|
||||
} else if hasNumber && hasSpecial {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
_ = Validate.RegisterTranslation("password", Trans, func(ut ut.Translator) error {
|
||||
return ut.Add("password", "{0} policy is min:8, max:16 and at least one alpha and at least one special char!", true) // see universal-translator for details
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T("password", fe.Field())
|
||||
|
||||
return t
|
||||
})
|
||||
}
|
||||
|
||||
var decoder = schema.NewDecoder()
|
||||
|
||||
func decodeJson(data []byte, v interface{}) error {
|
||||
err := json.Unmarshal(data, v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := Validate.Struct(v); err != nil {
|
||||
for _, err := range err.(validator.ValidationErrors) {
|
||||
return errors.New(err.Translate(Trans))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeQuery(data map[string][]string, v interface{}) error {
|
||||
decoder.IgnoreUnknownKeys(true)
|
||||
err := decoder.Decode(v, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := Validate.Struct(v); err != nil {
|
||||
for _, err := range err.(validator.ValidationErrors) {
|
||||
return errors.New(err.Translate(Trans))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
155
cmd/server/api/cron.go
Normal file
155
cmd/server/api/cron.go
Normal file
@ -0,0 +1,155 @@
|
||||
// Copyright 2022 The Goploy Authors. All rights reserved.
|
||||
// Use of this source code is governed by a GPLv3-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/zhenorzz/goploy/cmd/server/api/middleware"
|
||||
"github.com/zhenorzz/goploy/config"
|
||||
"github.com/zhenorzz/goploy/internal/server"
|
||||
"github.com/zhenorzz/goploy/internal/server/response"
|
||||
"github.com/zhenorzz/goploy/model"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Cron struct
|
||||
type Cron API
|
||||
|
||||
func (c Cron) Handler() []server.Route {
|
||||
return []server.Route{
|
||||
server.NewRoute("/cron/getList", http.MethodPost, c.GetList).Permissions(config.ShowCronPage),
|
||||
server.NewRoute("/cron/getLogs", http.MethodPost, c.GetLogs).Permissions(config.ShowCronPage),
|
||||
server.NewRoute("/cron/add", http.MethodPost, c.Add).Permissions(config.AddCron).LogFunc(middleware.AddOPLog),
|
||||
server.NewRoute("/cron/edit", http.MethodPut, c.Edit).Permissions(config.EditCron).LogFunc(middleware.AddOPLog),
|
||||
server.NewRoute("/cron/remove", http.MethodDelete, c.Remove).Permissions(config.DeleteCron).LogFunc(middleware.AddOPLog),
|
||||
}
|
||||
}
|
||||
|
||||
func (Cron) GetList(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
ServerID int64 `json:"serverId" validate:"gt=0"`
|
||||
}
|
||||
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
crons, err := model.Cron{ServerID: reqData.ServerID}.GetList()
|
||||
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
List model.Crons `json:"list"`
|
||||
}{List: crons},
|
||||
}
|
||||
}
|
||||
|
||||
func (Cron) GetLogs(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
ServerID int64 `schema:"serverId" validate:"gt=0"`
|
||||
CronID int64 `schema:"cronId" validate:"gt=0"`
|
||||
Page uint64 `schema:"page" validate:"gt=0"`
|
||||
Rows uint64 `schema:"rows" validate:"gt=0"`
|
||||
}
|
||||
|
||||
var reqData ReqData
|
||||
if err := decodeQuery(gp.URLQuery, &reqData); err != nil {
|
||||
return response.JSON{Code: response.IllegalParam, Message: err.Error()}
|
||||
}
|
||||
|
||||
crons, err := model.CronLog{ServerID: reqData.ServerID, CronID: reqData.CronID}.GetList(reqData.Page, reqData.Rows)
|
||||
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
List model.CronLogs `json:"list"`
|
||||
}{List: crons},
|
||||
}
|
||||
}
|
||||
|
||||
func (Cron) Add(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
ServerID int64 `json:"serverId" validate:"gt=0"`
|
||||
Expression string `json:"expression" validate:"required"`
|
||||
Command string `json:"command" validate:"required"`
|
||||
SingleMode uint8 `json:"singleMode" validate:"gte=0"`
|
||||
LogLevel uint8 `json:"logLevel" validate:"gte=0"`
|
||||
Description string `json:"description" validate:"max=255"`
|
||||
}
|
||||
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
id, err := model.Cron{
|
||||
ServerID: reqData.ServerID,
|
||||
Expression: reqData.Expression,
|
||||
Command: reqData.Command,
|
||||
SingleMode: reqData.SingleMode,
|
||||
LogLevel: reqData.LogLevel,
|
||||
Description: reqData.Description,
|
||||
Creator: gp.UserInfo.Name,
|
||||
}.AddRow()
|
||||
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
|
||||
}
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
ID int64 `json:"id"`
|
||||
}{ID: id},
|
||||
}
|
||||
}
|
||||
|
||||
func (Cron) Edit(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
ID int64 `json:"id" validate:"gt=0"`
|
||||
Expression string `json:"expression" validate:"required"`
|
||||
Command string `json:"command" validate:"required"`
|
||||
SingleMode uint8 `json:"singleMode" validate:"gte=0"`
|
||||
LogLevel uint8 `json:"logLevel" validate:"gte=0"`
|
||||
Description string `json:"description" validate:"max=255"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
err := model.Cron{
|
||||
ID: reqData.ID,
|
||||
Expression: reqData.Expression,
|
||||
Command: reqData.Command,
|
||||
SingleMode: reqData.SingleMode,
|
||||
LogLevel: reqData.LogLevel,
|
||||
Description: reqData.Description,
|
||||
Editor: gp.UserInfo.Name,
|
||||
}.EditRow()
|
||||
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{}
|
||||
}
|
||||
|
||||
func (Cron) Remove(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
ID int64 `json:"id" validate:"gt=0"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
if err := (model.Cron{ID: reqData.ID}).RemoveRow(); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
return response.JSON{}
|
||||
}
|
821
cmd/server/api/deploy.go
Normal file
821
cmd/server/api/deploy.go
Normal file
@ -0,0 +1,821 @@
|
||||
// Copyright 2022 The Goploy Authors. All rights reserved.
|
||||
// Use of this source code is governed by a GPLv3-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"database/sql"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/google/uuid"
|
||||
"github.com/pkg/sftp"
|
||||
"github.com/zhenorzz/goploy/cmd/server/api/middleware"
|
||||
"github.com/zhenorzz/goploy/cmd/server/task"
|
||||
"github.com/zhenorzz/goploy/config"
|
||||
"github.com/zhenorzz/goploy/internal/pkg"
|
||||
"github.com/zhenorzz/goploy/internal/pkg/cmd"
|
||||
"github.com/zhenorzz/goploy/internal/repo"
|
||||
"github.com/zhenorzz/goploy/internal/server"
|
||||
"github.com/zhenorzz/goploy/internal/server/response"
|
||||
"github.com/zhenorzz/goploy/model"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Deploy API
|
||||
|
||||
func (d Deploy) Handler() []server.Route {
|
||||
return []server.Route{
|
||||
server.NewRoute("/deploy/getList", http.MethodGet, d.GetList).Permissions(config.ShowDeployPage),
|
||||
server.NewRoute("/deploy/getPublishTrace", http.MethodGet, d.GetPublishTrace).Permissions(config.DeployDetail),
|
||||
server.NewRoute("/deploy/getPublishTraceDetail", http.MethodGet, d.GetPublishTraceDetail).Permissions(config.DeployDetail),
|
||||
server.NewRoute("/deploy/getPreview", http.MethodGet, d.GetPreview).Permissions(config.DeployDetail),
|
||||
server.NewRoute("/deploy/review", http.MethodPut, d.Review).Permissions(config.DeployReview).LogFunc(middleware.AddOPLog),
|
||||
server.NewRoute("/deploy/resetState", http.MethodPut, d.ResetState).Permissions(config.DeployResetState).LogFunc(middleware.AddOPLog),
|
||||
server.NewRoute("/deploy/publish", http.MethodPost, d.Publish).Permissions(config.DeployProject).Middleware(middleware.HasProjectPermission).LogFunc(middleware.AddOPLog),
|
||||
server.NewRoute("/deploy/rebuild", http.MethodPost, d.Rebuild).Permissions(config.DeployRollback).Middleware(middleware.HasProjectPermission).LogFunc(middleware.AddOPLog),
|
||||
server.NewRoute("/deploy/greyPublish", http.MethodPost, d.GreyPublish).Permissions(config.GreyDeploy).Middleware(middleware.HasProjectPermission).LogFunc(middleware.AddOPLog),
|
||||
server.NewWhiteRoute("/deploy/webhook", http.MethodPost, d.Webhook).Middleware(middleware.FilterEvent),
|
||||
server.NewWhiteRoute("/deploy/callback", http.MethodGet, d.Callback),
|
||||
server.NewRoute("/deploy/fileCompare", http.MethodPost, d.FileCompare).Permissions(config.FileCompare),
|
||||
server.NewRoute("/deploy/fileDiff", http.MethodPost, d.FileDiff).Permissions(config.FileCompare),
|
||||
server.NewRoute("/deploy/manageProcess", http.MethodPost, d.ManageProcess).Permissions(config.ProcessManager).LogFunc(middleware.AddOPLog),
|
||||
}
|
||||
}
|
||||
|
||||
func (Deploy) GetList(gp *server.Goploy) server.Response {
|
||||
var projects model.Projects
|
||||
var err error
|
||||
if _, ok := gp.Namespace.PermissionIDs[config.GetAllDeployList]; ok {
|
||||
projects, err = model.Project{NamespaceID: gp.Namespace.ID}.GetDeployList()
|
||||
} else {
|
||||
projects, err = model.Project{NamespaceID: gp.Namespace.ID, UserID: gp.UserInfo.ID}.GetDeployList()
|
||||
}
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
Project model.Projects `json:"list"`
|
||||
}{Project: projects},
|
||||
}
|
||||
}
|
||||
|
||||
func (Deploy) GetPreview(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
ProjectID int64 `schema:"projectId" validate:"gt=0"`
|
||||
UserID int64 `schema:"userId"`
|
||||
State int `schema:"state"`
|
||||
CommitDate string `schema:"commitDate"`
|
||||
DeployDate string `schema:"deployDate"`
|
||||
Branch string `schema:"branch"`
|
||||
Commit string `schema:"commit"`
|
||||
Filename string `schema:"filename"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeQuery(gp.URLQuery, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
pagination, err := model.PaginationFrom(gp.URLQuery)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
commitDate := strings.Split(reqData.CommitDate, ",")
|
||||
for i, date := range commitDate {
|
||||
tm2, _ := time.Parse("2006-01-02 15:04:05", date)
|
||||
commitDate[i] = strconv.FormatInt(tm2.Unix(), 10)
|
||||
}
|
||||
gitTraceList, pagination, err := model.PublishTrace{
|
||||
ProjectID: reqData.ProjectID,
|
||||
PublisherID: reqData.UserID,
|
||||
State: reqData.State,
|
||||
}.GetPreview(
|
||||
reqData.Branch,
|
||||
reqData.Commit,
|
||||
reqData.Filename,
|
||||
commitDate,
|
||||
strings.Split(reqData.DeployDate, ","),
|
||||
pagination,
|
||||
)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
GitTraceList model.PublishTraces `json:"list"`
|
||||
Pagination model.Pagination `json:"pagination"`
|
||||
}{GitTraceList: gitTraceList, Pagination: pagination},
|
||||
}
|
||||
}
|
||||
|
||||
func (Deploy) GetPublishTrace(gp *server.Goploy) server.Response {
|
||||
lastPublishToken := gp.URLQuery.Get("lastPublishToken")
|
||||
publishTraceList, err := model.PublishTrace{Token: lastPublishToken}.GetListByToken()
|
||||
if err == sql.ErrNoRows {
|
||||
return response.JSON{Code: response.Error, Message: "No deploy record"}
|
||||
} else if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
PublishTraceList model.PublishTraces `json:"list"`
|
||||
}{PublishTraceList: publishTraceList},
|
||||
}
|
||||
}
|
||||
|
||||
func (Deploy) GetPublishTraceDetail(gp *server.Goploy) server.Response {
|
||||
id, err := strconv.ParseInt(gp.URLQuery.Get("id"), 10, 64)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
detail, err := model.PublishTrace{ID: id}.GetDetail()
|
||||
if err == sql.ErrNoRows {
|
||||
return response.JSON{Code: response.Error, Message: "No deploy record"}
|
||||
} else if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
Detail string `json:"detail"`
|
||||
}{Detail: detail},
|
||||
}
|
||||
}
|
||||
|
||||
func (Deploy) ResetState(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
ProjectID int64 `json:"projectId" validate:"gt=0"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
if err := (model.Project{ID: reqData.ProjectID}).ResetState(); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
return response.JSON{}
|
||||
}
|
||||
|
||||
func (Deploy) FileCompare(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
ProjectID int64 `json:"projectId" validate:"gt=0"`
|
||||
FilePath string `json:"filePath" validate:"required"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
project, err := model.Project{ID: reqData.ProjectID}.GetData()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
srcPath := path.Join(config.GetProjectPath(reqData.ProjectID), reqData.FilePath)
|
||||
file, err := os.Open(srcPath)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
defer file.Close()
|
||||
hash := md5.New()
|
||||
_, _ = io.Copy(hash, file)
|
||||
srcMD5 := hex.EncodeToString(hash.Sum(nil))
|
||||
projectServers, err := model.ProjectServer{ProjectID: reqData.ProjectID}.GetBindServerListByProjectID()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
if len(projectServers) == 0 {
|
||||
return response.JSON{Code: response.Error, Message: "project have no server"}
|
||||
}
|
||||
|
||||
type FileCompareData struct {
|
||||
ServerName string `json:"serverName"`
|
||||
ServerIP string `json:"serverIP"`
|
||||
ServerID int64 `json:"serverId"`
|
||||
Status string `json:"status"`
|
||||
IsModified bool `json:"isModified"`
|
||||
}
|
||||
var fileCompareList []FileCompareData
|
||||
ch := make(chan FileCompareData, len(projectServers))
|
||||
|
||||
distPath := path.Join(project.Path, reqData.FilePath)
|
||||
for _, server := range projectServers {
|
||||
go func(server model.ProjectServer) {
|
||||
fileCompare := FileCompareData{server.ServerName, server.ServerIP, server.ServerID, "no change", false}
|
||||
client, err := server.ToSSHConfig().Dial()
|
||||
if err != nil {
|
||||
fileCompare.Status = "client error"
|
||||
ch <- fileCompare
|
||||
return
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
//此时获取了sshClient,下面使用sshClient构建sftpClient
|
||||
sftpClient, err := sftp.NewClient(client)
|
||||
if err != nil {
|
||||
fileCompare.Status = "sftp error"
|
||||
ch <- fileCompare
|
||||
return
|
||||
}
|
||||
defer sftpClient.Close()
|
||||
file, err := sftpClient.Open(distPath)
|
||||
if err != nil {
|
||||
fileCompare.Status = "remote file not exists"
|
||||
ch <- fileCompare
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
hash := md5.New()
|
||||
_, _ = io.Copy(hash, file)
|
||||
distMD5 := hex.EncodeToString(hash.Sum(nil))
|
||||
if srcMD5 != distMD5 {
|
||||
fileCompare.Status = "modified"
|
||||
fileCompare.IsModified = true
|
||||
ch <- fileCompare
|
||||
return
|
||||
}
|
||||
ch <- fileCompare
|
||||
}(server)
|
||||
}
|
||||
|
||||
for i := 0; i < len(projectServers); i++ {
|
||||
fileCompareList = append(fileCompareList, <-ch)
|
||||
}
|
||||
close(ch)
|
||||
return response.JSON{Data: fileCompareList}
|
||||
}
|
||||
|
||||
func (Deploy) FileDiff(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
ProjectID int64 `json:"projectId" validate:"gt=0"`
|
||||
ServerID int64 `json:"serverId" validate:"gt=0"`
|
||||
FilePath string `json:"filePath" validate:"required"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
project, err := model.Project{ID: reqData.ProjectID}.GetData()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
srcText, err := ioutil.ReadFile(path.Join(config.GetProjectPath(reqData.ProjectID), reqData.FilePath))
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
server, err := model.Server{ID: reqData.ServerID}.GetData()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
client, err := server.ToSSHConfig().Dial()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
//此时获取了sshClient,下面使用sshClient构建sftpClient
|
||||
sftpClient, err := sftp.NewClient(client)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
defer sftpClient.Close()
|
||||
|
||||
distFile, err := sftpClient.Open(path.Join(project.Path, reqData.FilePath))
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
defer distFile.Close()
|
||||
distText, err := ioutil.ReadAll(distFile)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{Data: struct {
|
||||
SrcText string `json:"srcText"`
|
||||
DistText string `json:"distText"`
|
||||
}{SrcText: string(srcText), DistText: string(distText)}}
|
||||
}
|
||||
|
||||
func (Deploy) ManageProcess(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
ServerID int64 `json:"serverId" validate:"gt=0"`
|
||||
ProjectProcessID int64 `json:"projectProcessId" validate:"gt=0"`
|
||||
Command string `json:"command" validate:"required"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
projectProcess, err := model.ProjectProcess{ID: reqData.ProjectProcessID}.GetData()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
project, err := (model.Project{ID: projectProcess.ProjectID}).GetData()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
server, err := (model.Server{ID: reqData.ServerID}).GetData()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
script := ""
|
||||
switch reqData.Command {
|
||||
case "status":
|
||||
script = projectProcess.Status
|
||||
case "start":
|
||||
script = projectProcess.Start
|
||||
case "stop":
|
||||
script = projectProcess.Stop
|
||||
case "restart":
|
||||
script = projectProcess.Restart
|
||||
default:
|
||||
return response.JSON{Code: response.Error, Message: "Command error"}
|
||||
}
|
||||
if script == "" {
|
||||
return response.JSON{Code: response.Error, Message: "Command empty"}
|
||||
}
|
||||
|
||||
script = project.ReplaceVars(script)
|
||||
|
||||
client, err := server.ToSSHConfig().Dial()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
session, err := client.NewSession()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
defer session.Close()
|
||||
|
||||
var sshOutbuf, sshErrbuf bytes.Buffer
|
||||
session.Stdout = &sshOutbuf
|
||||
session.Stderr = &sshErrbuf
|
||||
err = session.Run(script)
|
||||
pkg.Log(pkg.TRACE, fmt.Sprintf("%s exec cmd %s, result %t, stdout: %s, stderr: %s", gp.UserInfo.Name, script, err == nil, sshOutbuf.String(), sshErrbuf.String()))
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
ExecRes bool `json:"execRes"`
|
||||
Stdout string `json:"stdout"`
|
||||
Stderr string `json:"stderr"`
|
||||
}{ExecRes: err == nil, Stdout: sshOutbuf.String(), Stderr: sshErrbuf.String()},
|
||||
}
|
||||
}
|
||||
|
||||
func (Deploy) Publish(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
ProjectID int64 `json:"projectId" validate:"gt=0"`
|
||||
Commit string `json:"commit"`
|
||||
Branch string `json:"branch"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
project, err := model.Project{ID: reqData.ProjectID}.GetData()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
if project.DeployState == model.ProjectNotDeploy {
|
||||
err = projectDeploy(gp, project, "", "")
|
||||
} else if project.Review == model.Enable {
|
||||
err = projectReview(gp, project, reqData.Commit, reqData.Branch)
|
||||
} else {
|
||||
err = projectDeploy(gp, project, reqData.Commit, reqData.Branch)
|
||||
}
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{}
|
||||
}
|
||||
|
||||
func (Deploy) Rebuild(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
Token string `json:"token"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
var err error
|
||||
publishTraceList, err := model.PublishTrace{Token: reqData.Token}.GetListByToken()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
projectID := publishTraceList[0].ProjectID
|
||||
project, err := model.Project{ID: projectID}.GetData()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
projectServers, err := model.ProjectServer{ProjectID: projectID}.GetBindServerListByProjectID()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
needToPublish := project.SymlinkPath == ""
|
||||
var commitInfo repo.CommitInfo
|
||||
publishTraceServerCount := 0
|
||||
for _, publishTrace := range publishTraceList {
|
||||
// publish failed
|
||||
if publishTrace.State == 0 {
|
||||
needToPublish = true
|
||||
break
|
||||
}
|
||||
|
||||
if publishTrace.Type == model.Pull {
|
||||
err := json.Unmarshal([]byte(publishTrace.Ext), &commitInfo)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
} else if publishTrace.Type == model.Deploy {
|
||||
for _, projectServer := range projectServers {
|
||||
if strings.Contains(publishTrace.Ext, projectServer.ServerIP) {
|
||||
publishTraceServerCount++
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// project server has changed
|
||||
if publishTraceServerCount != len(projectServers) {
|
||||
needToPublish = true
|
||||
}
|
||||
if needToPublish == false {
|
||||
ch := make(chan bool, len(projectServers))
|
||||
for _, projectServer := range projectServers {
|
||||
go func(projectServer model.ProjectServer) {
|
||||
destDir := path.Join(project.SymlinkPath, project.LastPublishToken)
|
||||
cmdEntity := cmd.New(projectServer.ServerOS)
|
||||
afterDeployCommands := []string{cmdEntity.Symlink(destDir, project.Path), cmdEntity.ChangeDirTime(destDir)}
|
||||
if len(project.AfterDeployScript) != 0 {
|
||||
scriptName := fmt.Sprintf("goploy-after-deploy-p%d-s%d.%s", project.ID, projectServer.ServerID, pkg.GetScriptExt(project.AfterDeployScriptMode))
|
||||
scriptContent := project.ReplaceVars(project.AfterDeployScript)
|
||||
scriptContent = projectServer.ReplaceVars(scriptContent)
|
||||
ioutil.WriteFile(path.Join(config.GetProjectPath(project.ID), scriptName), []byte(project.ReplaceVars(project.AfterDeployScript)), 0755)
|
||||
afterDeployScriptPath := path.Join(project.Path, scriptName)
|
||||
afterDeployCommands = append(afterDeployCommands, cmdEntity.Script(project.AfterDeployScriptMode, afterDeployScriptPath))
|
||||
afterDeployCommands = append(afterDeployCommands, cmdEntity.Remove(afterDeployScriptPath))
|
||||
}
|
||||
client, err := projectServer.ToSSHConfig().Dial()
|
||||
if err != nil {
|
||||
pkg.Log(pkg.ERROR, "projectID:"+strconv.FormatInt(project.ID, 10)+" dial err: "+err.Error())
|
||||
ch <- false
|
||||
return
|
||||
}
|
||||
defer client.Close()
|
||||
session, err := client.NewSession()
|
||||
if err != nil {
|
||||
pkg.Log(pkg.ERROR, "projectID:"+strconv.FormatInt(project.ID, 10)+" new session err: "+err.Error())
|
||||
ch <- false
|
||||
return
|
||||
}
|
||||
|
||||
// check if the path is existed or not
|
||||
if output, err := session.CombinedOutput("cd " + destDir); err != nil {
|
||||
pkg.Log(pkg.ERROR, "projectID:"+strconv.FormatInt(project.ID, 10)+" check symlink path err: "+err.Error()+", detail: "+string(output))
|
||||
ch <- false
|
||||
return
|
||||
}
|
||||
session, err = client.NewSession()
|
||||
if err != nil {
|
||||
pkg.Log(pkg.ERROR, "projectID:"+strconv.FormatInt(project.ID, 10)+" new session err: "+err.Error())
|
||||
ch <- false
|
||||
return
|
||||
}
|
||||
|
||||
// redirect to project path
|
||||
if output, err := session.CombinedOutput(strings.Join(afterDeployCommands, "&&")); err != nil {
|
||||
pkg.Log(pkg.ERROR, "projectID:"+strconv.FormatInt(project.ID, 10)+" symlink err: "+err.Error()+", detail: "+string(output))
|
||||
ch <- false
|
||||
return
|
||||
}
|
||||
ch <- true
|
||||
}(projectServer)
|
||||
}
|
||||
|
||||
for i := 0; i < len(projectServers); i++ {
|
||||
if <-ch == false {
|
||||
needToPublish = true
|
||||
break
|
||||
}
|
||||
}
|
||||
close(ch)
|
||||
if needToPublish == false {
|
||||
model.PublishTrace{
|
||||
Token: reqData.Token,
|
||||
UpdateTime: time.Now().Format("20060102150405"),
|
||||
}.EditUpdateTimeByToken()
|
||||
project.PublisherID = gp.UserInfo.ID
|
||||
project.PublisherName = gp.UserInfo.Name
|
||||
project.LastPublishToken = reqData.Token
|
||||
project.Publish()
|
||||
return response.JSON{Data: "symlink"}
|
||||
}
|
||||
}
|
||||
|
||||
if needToPublish == true {
|
||||
repoEntity, _ := repo.GetRepo(project.RepoType)
|
||||
if !repoEntity.CanRollback() {
|
||||
return response.JSON{Code: response.Error, Message: fmt.Sprintf("plesae enable symlink to rollback the %s repo", project.RepoType)}
|
||||
}
|
||||
|
||||
project.PublisherID = gp.UserInfo.ID
|
||||
project.PublisherName = gp.UserInfo.Name
|
||||
project.DeployState = model.ProjectDeploying
|
||||
project.LastPublishToken = uuid.New().String()
|
||||
err = project.Publish()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
task.AddDeployTask(task.Gsync{
|
||||
UserInfo: gp.UserInfo,
|
||||
Project: project,
|
||||
ProjectServers: projectServers,
|
||||
CommitID: commitInfo.Commit,
|
||||
Branch: commitInfo.Branch,
|
||||
})
|
||||
}
|
||||
return response.JSON{Data: "publish"}
|
||||
}
|
||||
|
||||
func (Deploy) GreyPublish(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
ProjectID int64 `json:"projectId" validate:"gt=0"`
|
||||
Commit string `json:"commit"`
|
||||
Branch string `json:"branch"`
|
||||
ServerIDs []int64 `json:"serverIds"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
project, err := model.Project{ID: reqData.ProjectID}.GetData()
|
||||
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
bindProjectServers, err := model.ProjectServer{ProjectID: project.ID}.GetBindServerListByProjectID()
|
||||
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
projectServers := model.ProjectServers{}
|
||||
|
||||
for _, projectServer := range bindProjectServers {
|
||||
for _, serverID := range reqData.ServerIDs {
|
||||
if projectServer.ServerID == serverID {
|
||||
projectServers = append(projectServers, projectServer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
project.PublisherID = gp.UserInfo.ID
|
||||
project.PublisherName = gp.UserInfo.Name
|
||||
project.DeployState = model.ProjectDeploying
|
||||
project.LastPublishToken = uuid.New().String()
|
||||
err = project.Publish()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
task.AddDeployTask(task.Gsync{
|
||||
UserInfo: gp.UserInfo,
|
||||
Project: project,
|
||||
ProjectServers: projectServers,
|
||||
CommitID: reqData.Commit,
|
||||
Branch: reqData.Branch,
|
||||
})
|
||||
|
||||
return response.JSON{}
|
||||
}
|
||||
|
||||
func (Deploy) Review(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
ProjectReviewID int64 `json:"projectReviewId" validate:"gt=0"`
|
||||
State uint8 `json:"state" validate:"gt=0"`
|
||||
}
|
||||
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
projectReviewModel := model.ProjectReview{
|
||||
ID: reqData.ProjectReviewID,
|
||||
State: reqData.State,
|
||||
Editor: gp.UserInfo.Name,
|
||||
EditorID: gp.UserInfo.ID,
|
||||
}
|
||||
projectReview, err := projectReviewModel.GetData()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
if projectReview.State != model.PENDING {
|
||||
return response.JSON{Code: response.Error, Message: "Project review state is invalid"}
|
||||
}
|
||||
|
||||
if reqData.State == model.APPROVE {
|
||||
project, err := model.Project{ID: projectReview.ProjectID}.GetData()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
if err := projectDeploy(gp, project, projectReview.CommitID, projectReview.Branch); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
}
|
||||
projectReviewModel.EditRow()
|
||||
|
||||
return response.JSON{}
|
||||
}
|
||||
|
||||
func (Deploy) Webhook(gp *server.Goploy) server.Response {
|
||||
projectID, err := strconv.ParseInt(gp.URLQuery.Get("project_id"), 10, 64)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
type ReqData struct {
|
||||
Ref string `json:"ref" validate:"required"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
project, err := model.Project{ID: projectID}.GetData()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
if project.State != model.Disable {
|
||||
return response.JSON{Code: response.Deny, Message: "Project is disabled"}
|
||||
}
|
||||
|
||||
if project.AutoDeploy != model.ProjectWebhookDeploy {
|
||||
return response.JSON{Code: response.Deny, Message: "Webhook auto deploy turn off, go to project setting turn on"}
|
||||
}
|
||||
|
||||
branch := ""
|
||||
if project.RepoType == model.RepoSVN {
|
||||
branch = reqData.Ref
|
||||
} else {
|
||||
branch = strings.Split(reqData.Ref, "/")[2]
|
||||
}
|
||||
|
||||
if project.Branch != branch {
|
||||
return response.JSON{Code: response.Deny, Message: "Receive branch:" + branch + " push event, not equal to current branch"}
|
||||
}
|
||||
|
||||
gp.UserInfo, err = model.User{ID: 1}.GetData()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
projectServers, err := model.ProjectServer{ProjectID: project.ID}.GetBindServerListByProjectID()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
project.PublisherID = gp.UserInfo.ID
|
||||
project.PublisherName = "webhook"
|
||||
project.DeployState = model.ProjectDeploying
|
||||
project.LastPublishToken = uuid.New().String()
|
||||
err = project.Publish()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
task.AddDeployTask(task.Gsync{
|
||||
UserInfo: gp.UserInfo,
|
||||
Project: project,
|
||||
ProjectServers: projectServers,
|
||||
})
|
||||
return response.JSON{Message: "receive push signal"}
|
||||
}
|
||||
|
||||
func (Deploy) Callback(gp *server.Goploy) server.Response {
|
||||
projectReviewID, err := strconv.ParseInt(gp.URLQuery.Get("project_review_id"), 10, 64)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
projectReviewModel := model.ProjectReview{
|
||||
ID: projectReviewID,
|
||||
State: model.APPROVE,
|
||||
Editor: "admin",
|
||||
EditorID: 1,
|
||||
}
|
||||
projectReview, err := projectReviewModel.GetData()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
if projectReview.State != model.PENDING {
|
||||
return response.JSON{Code: response.Error, Message: "Project review state is invalid"}
|
||||
}
|
||||
|
||||
project, err := model.Project{ID: projectReview.ProjectID}.GetData()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
if err := projectDeploy(gp, project, projectReview.CommitID, projectReview.Branch); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
projectReviewModel.EditRow()
|
||||
|
||||
return response.JSON{}
|
||||
}
|
||||
|
||||
func projectDeploy(gp *server.Goploy, project model.Project, commitID string, branch string) error {
|
||||
projectServers, err := model.ProjectServer{ProjectID: project.ID}.GetBindServerListByProjectID()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
project.PublisherID = gp.UserInfo.ID
|
||||
project.PublisherName = gp.UserInfo.Name
|
||||
project.DeployState = model.ProjectDeploying
|
||||
project.LastPublishToken = uuid.New().String()
|
||||
err = project.Publish()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
task.AddDeployTask(task.Gsync{
|
||||
UserInfo: gp.UserInfo,
|
||||
Project: project,
|
||||
ProjectServers: projectServers,
|
||||
CommitID: commitID,
|
||||
Branch: branch,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func projectReview(gp *server.Goploy, project model.Project, commitID string, branch string) error {
|
||||
if len(commitID) == 0 {
|
||||
return errors.New("commit id is required")
|
||||
}
|
||||
projectReviewModel := model.ProjectReview{
|
||||
ProjectID: project.ID,
|
||||
Branch: branch,
|
||||
CommitID: commitID,
|
||||
Creator: gp.UserInfo.Name,
|
||||
CreatorID: gp.UserInfo.ID,
|
||||
}
|
||||
reviewURL := project.ReviewURL
|
||||
if len(reviewURL) > 0 {
|
||||
reviewURL = strings.Replace(reviewURL, "__PROJECT_ID__", strconv.FormatInt(project.ID, 10), 1)
|
||||
reviewURL = strings.Replace(reviewURL, "__PROJECT_NAME__", project.Name, 1)
|
||||
reviewURL = strings.Replace(reviewURL, "__BRANCH__", branch, 1)
|
||||
reviewURL = strings.Replace(reviewURL, "__ENVIRONMENT__", strconv.Itoa(int(project.Environment)), 1)
|
||||
reviewURL = strings.Replace(reviewURL, "__COMMIT_ID__", commitID, 1)
|
||||
reviewURL = strings.Replace(reviewURL, "__PUBLISH_TIME__", strconv.FormatInt(time.Now().Unix(), 10), 1)
|
||||
reviewURL = strings.Replace(reviewURL, "__PUBLISHER_ID__", gp.UserInfo.Name, 1)
|
||||
reviewURL = strings.Replace(reviewURL, "__PUBLISHER_NAME__", strconv.FormatInt(gp.UserInfo.ID, 10), 1)
|
||||
|
||||
projectReviewModel.ReviewURL = reviewURL
|
||||
}
|
||||
id, err := projectReviewModel.AddRow()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(reviewURL) > 0 {
|
||||
callback := "http://"
|
||||
if gp.Request.TLS != nil {
|
||||
callback = "https://"
|
||||
}
|
||||
callback += gp.Request.Host + "/deploy/callback?project_review_id=" + strconv.FormatInt(id, 10)
|
||||
callback = url.QueryEscape(callback)
|
||||
reviewURL = strings.Replace(reviewURL, "__CALLBACK__", callback, 1)
|
||||
|
||||
resp, err := http.Get(reviewURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
317
cmd/server/api/log.go
Normal file
317
cmd/server/api/log.go
Normal file
@ -0,0 +1,317 @@
|
||||
// Copyright 2022 The Goploy Authors. All rights reserved.
|
||||
// Use of this source code is governed by a GPLv3-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/zhenorzz/goploy/config"
|
||||
"github.com/zhenorzz/goploy/internal/server"
|
||||
"github.com/zhenorzz/goploy/internal/server/response"
|
||||
"github.com/zhenorzz/goploy/model"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Log struct
|
||||
type Log API
|
||||
|
||||
func (l Log) Handler() []server.Route {
|
||||
return []server.Route{
|
||||
server.NewRoute("/log/getLoginLogList", http.MethodGet, l.GetLoginLogList).Permissions(config.ShowLoginLogPage),
|
||||
server.NewRoute("/log/getLoginLogTotal", http.MethodGet, l.GetLoginLogTotal).Permissions(config.ShowLoginLogPage),
|
||||
server.NewRoute("/log/getOperationLogList", http.MethodGet, l.GetOperationLogList).Permissions(config.ShowOperationLogPage),
|
||||
server.NewRoute("/log/getOperationLogTotal", http.MethodGet, l.GetOperationLogTotal).Permissions(config.ShowOperationLogPage),
|
||||
server.NewRoute("/log/getSftpLogList", http.MethodGet, l.GetSftpLogList).Permissions(config.ShowSFTPLogPage),
|
||||
server.NewRoute("/log/getSftpLogTotal", http.MethodGet, l.GetSftpLogTotal).Permissions(config.ShowSFTPLogPage),
|
||||
server.NewRoute("/log/getTerminalLogList", http.MethodGet, l.GetTerminalLogList).Permissions(config.ShowTerminalLogPage),
|
||||
server.NewRoute("/log/getTerminalLogTotal", http.MethodGet, l.GetTerminalLogTotal).Permissions(config.ShowTerminalLogPage),
|
||||
server.NewRoute("/log/getTerminalRecord", http.MethodGet, l.GetTerminalRecord).Permissions(config.ShowTerminalRecord),
|
||||
server.NewRoute("/log/getPublishLogList", http.MethodGet, l.GetPublishLogList).Permissions(config.ShowPublishLogPage),
|
||||
server.NewRoute("/log/getPublishLogTotal", http.MethodGet, l.GetPublishLogTotal).Permissions(config.ShowPublishLogPage),
|
||||
}
|
||||
}
|
||||
|
||||
func (Log) GetLoginLogList(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
Account string `schema:"account"`
|
||||
Page uint64 `schema:"page" validate:"gt=0"`
|
||||
Rows uint64 `schema:"rows" validate:"gt=0"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeQuery(gp.URLQuery, &reqData); err != nil {
|
||||
return response.JSON{Code: response.IllegalParam, Message: err.Error()}
|
||||
}
|
||||
|
||||
list, err := model.LoginLog{Account: reqData.Account}.GetList(reqData.Page, reqData.Rows)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
List model.LoginLogs `json:"list"`
|
||||
}{List: list},
|
||||
}
|
||||
}
|
||||
|
||||
func (Log) GetLoginLogTotal(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
Account string `schema:"account"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeQuery(gp.URLQuery, &reqData); err != nil {
|
||||
return response.JSON{Code: response.IllegalParam, Message: err.Error()}
|
||||
}
|
||||
total, err := model.LoginLog{Account: reqData.Account}.GetTotal()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
Total int64 `json:"total"`
|
||||
}{Total: total},
|
||||
}
|
||||
}
|
||||
|
||||
func (Log) GetOperationLogList(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
Username string `schema:"username"`
|
||||
Router string `schema:"router"`
|
||||
API string `schema:"api"`
|
||||
Page uint64 `schema:"page" validate:"gt=0"`
|
||||
Rows uint64 `schema:"rows" validate:"gt=0"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeQuery(gp.URLQuery, &reqData); err != nil {
|
||||
return response.JSON{Code: response.IllegalParam, Message: err.Error()}
|
||||
}
|
||||
|
||||
opLog := model.OperationLog{Username: reqData.Username, Router: reqData.Router, API: reqData.API}
|
||||
|
||||
if gp.UserInfo.SuperManager != model.SuperManager {
|
||||
opLog.NamespaceID = gp.Namespace.ID
|
||||
}
|
||||
|
||||
list, err := opLog.GetList(reqData.Page, reqData.Rows)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
List model.OperationLogs `json:"list"`
|
||||
}{List: list},
|
||||
}
|
||||
}
|
||||
|
||||
func (Log) GetOperationLogTotal(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
Username string `schema:"username"`
|
||||
Router string `schema:"router"`
|
||||
API string `schema:"api"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeQuery(gp.URLQuery, &reqData); err != nil {
|
||||
return response.JSON{Code: response.IllegalParam, Message: err.Error()}
|
||||
}
|
||||
|
||||
opLog := model.OperationLog{Username: reqData.Username, Router: reqData.Router, API: reqData.API}
|
||||
|
||||
if gp.UserInfo.SuperManager != model.SuperManager {
|
||||
opLog.NamespaceID = gp.Namespace.ID
|
||||
}
|
||||
|
||||
total, err := opLog.GetTotal()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
Total int64 `json:"total"`
|
||||
}{Total: total},
|
||||
}
|
||||
}
|
||||
|
||||
func (Log) GetSftpLogList(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
Username string `schema:"username"`
|
||||
ServerName string `schema:"serverName"`
|
||||
Page uint64 `schema:"page" validate:"gt=0"`
|
||||
Rows uint64 `schema:"rows" validate:"gt=0"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeQuery(gp.URLQuery, &reqData); err != nil {
|
||||
return response.JSON{Code: response.IllegalParam, Message: err.Error()}
|
||||
}
|
||||
|
||||
sftpLog := model.SftpLog{Username: reqData.Username, ServerName: reqData.ServerName}
|
||||
|
||||
if gp.UserInfo.SuperManager != model.SuperManager {
|
||||
sftpLog.NamespaceID = gp.Namespace.ID
|
||||
}
|
||||
|
||||
list, err := sftpLog.GetList(reqData.Page, reqData.Rows)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
List model.SftpLogs `json:"list"`
|
||||
}{List: list},
|
||||
}
|
||||
}
|
||||
|
||||
func (Log) GetSftpLogTotal(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
Username string `schema:"username"`
|
||||
ServerName string `schema:"serverName"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeQuery(gp.URLQuery, &reqData); err != nil {
|
||||
return response.JSON{Code: response.IllegalParam, Message: err.Error()}
|
||||
}
|
||||
|
||||
sftpLog := model.SftpLog{Username: reqData.Username, ServerName: reqData.ServerName}
|
||||
|
||||
if gp.UserInfo.SuperManager != model.SuperManager {
|
||||
sftpLog.NamespaceID = gp.Namespace.ID
|
||||
}
|
||||
|
||||
total, err := sftpLog.GetTotal()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
Total int64 `json:"total"`
|
||||
}{Total: total},
|
||||
}
|
||||
}
|
||||
|
||||
func (Log) GetTerminalLogList(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
Username string `schema:"username"`
|
||||
ServerName string `schema:"serverName"`
|
||||
Page uint64 `schema:"page" validate:"gt=0"`
|
||||
Rows uint64 `schema:"rows" validate:"gt=0"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeQuery(gp.URLQuery, &reqData); err != nil {
|
||||
return response.JSON{Code: response.IllegalParam, Message: err.Error()}
|
||||
}
|
||||
|
||||
terminalLog := model.TerminalLog{Username: reqData.Username, ServerName: reqData.ServerName}
|
||||
|
||||
if gp.UserInfo.SuperManager != model.SuperManager {
|
||||
terminalLog.NamespaceID = gp.Namespace.ID
|
||||
}
|
||||
|
||||
list, err := terminalLog.GetList(reqData.Page, reqData.Rows)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
List model.TerminalLogs `json:"list"`
|
||||
}{List: list},
|
||||
}
|
||||
}
|
||||
|
||||
func (Log) GetTerminalLogTotal(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
Username string `schema:"username"`
|
||||
ServerName string `schema:"serverName"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeQuery(gp.URLQuery, &reqData); err != nil {
|
||||
return response.JSON{Code: response.IllegalParam, Message: err.Error()}
|
||||
}
|
||||
|
||||
terminalLog := model.TerminalLog{Username: reqData.Username, ServerName: reqData.ServerName}
|
||||
|
||||
if gp.UserInfo.SuperManager != model.SuperManager {
|
||||
terminalLog.NamespaceID = gp.Namespace.ID
|
||||
}
|
||||
|
||||
total, err := terminalLog.GetTotal()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
Total int64 `json:"total"`
|
||||
}{Total: total},
|
||||
}
|
||||
}
|
||||
|
||||
func (Log) GetTerminalRecord(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
RecordID int64 `schema:"recordId" validate:"gt=0"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeQuery(gp.URLQuery, &reqData); err != nil {
|
||||
return response.JSON{Code: response.IllegalParam, Message: err.Error()}
|
||||
}
|
||||
terminalLog, err := model.TerminalLog{ID: reqData.RecordID}.GetData()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
if gp.UserInfo.SuperManager != model.SuperManager && terminalLog.NamespaceID != gp.Namespace.ID {
|
||||
return response.JSON{Code: response.Error, Message: "You have no access to enter this record"}
|
||||
}
|
||||
return response.File{Filename: config.GetTerminalLogPath(reqData.RecordID)}
|
||||
}
|
||||
|
||||
func (Log) GetPublishLogList(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
Username string `schema:"username"`
|
||||
ProjectName string `schema:"projectName"`
|
||||
Page uint64 `schema:"page" validate:"gt=0"`
|
||||
Rows uint64 `schema:"rows" validate:"gt=0"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeQuery(gp.URLQuery, &reqData); err != nil {
|
||||
return response.JSON{Code: response.IllegalParam, Message: err.Error()}
|
||||
}
|
||||
|
||||
publishLog := model.PublishTrace{PublisherName: reqData.Username, ProjectName: reqData.ProjectName}
|
||||
|
||||
if gp.UserInfo.SuperManager != model.SuperManager {
|
||||
publishLog.NamespaceID = gp.Namespace.ID
|
||||
}
|
||||
|
||||
list, err := publishLog.GetList(reqData.Page, reqData.Rows)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
List model.PublishTraces `json:"list"`
|
||||
}{List: list},
|
||||
}
|
||||
}
|
||||
|
||||
func (Log) GetPublishLogTotal(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
Username string `schema:"username"`
|
||||
ProjectName string `schema:"projectName"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeQuery(gp.URLQuery, &reqData); err != nil {
|
||||
return response.JSON{Code: response.IllegalParam, Message: err.Error()}
|
||||
}
|
||||
|
||||
publishLog := model.PublishTrace{PublisherName: reqData.Username, ProjectName: reqData.ProjectName}
|
||||
|
||||
if gp.UserInfo.SuperManager != model.SuperManager {
|
||||
publishLog.NamespaceID = gp.Namespace.ID
|
||||
}
|
||||
|
||||
total, err := publishLog.GetTotal()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
Total int64 `json:"total"`
|
||||
}{Total: total},
|
||||
}
|
||||
}
|
@ -9,12 +9,12 @@ import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"github.com/zhenorzz/goploy/config"
|
||||
"github.com/zhenorzz/goploy/core"
|
||||
"github.com/zhenorzz/goploy/internal/server"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func CheckSign(gp *core.Goploy) error {
|
||||
func CheckSign(gp *server.Goploy) error {
|
||||
sign := gp.URLQuery.Get("sign")
|
||||
if sign == "" {
|
||||
return errors.New("sign missing")
|
@ -7,14 +7,13 @@ package middleware
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"github.com/zhenorzz/goploy/core"
|
||||
"github.com/zhenorzz/goploy/config"
|
||||
"github.com/zhenorzz/goploy/internal/server"
|
||||
"github.com/zhenorzz/goploy/model"
|
||||
"github.com/zhenorzz/goploy/permission"
|
||||
)
|
||||
|
||||
// HasProjectPermission check the user has publish auth
|
||||
func HasProjectPermission(gp *core.Goploy) error {
|
||||
if _, ok := gp.Namespace.PermissionIDs[permission.GetAllDeployList]; ok {
|
||||
func HasProjectPermission(gp *server.Goploy) error {
|
||||
if _, ok := gp.Namespace.PermissionIDs[config.GetAllDeployList]; ok {
|
||||
return nil
|
||||
}
|
||||
type ReqData struct {
|
||||
@ -32,8 +31,7 @@ func HasProjectPermission(gp *core.Goploy) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// FilterEvent check the webhook event has publish auth
|
||||
func FilterEvent(gp *core.Goploy) error {
|
||||
func FilterEvent(gp *server.Goploy) error {
|
||||
if XGitHubEvent := gp.Request.Header.Get("X-GitHub-Event"); len(XGitHubEvent) != 0 && XGitHubEvent == "push" {
|
||||
return nil
|
||||
} else if XGitLabEvent := gp.Request.Header.Get("X-Gitlab-Event"); len(XGitLabEvent) != 0 && XGitLabEvent == "Push Hook" {
|
@ -6,15 +6,15 @@ package middleware
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/zhenorzz/goploy/core"
|
||||
"github.com/zhenorzz/goploy/internal/pkg"
|
||||
"github.com/zhenorzz/goploy/internal/server"
|
||||
"github.com/zhenorzz/goploy/internal/server/response"
|
||||
"github.com/zhenorzz/goploy/model"
|
||||
"github.com/zhenorzz/goploy/response"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func AddLoginLog(gp *core.Goploy, resp core.Response) {
|
||||
func AddLoginLog(gp *server.Goploy, resp server.Response) {
|
||||
respJson := resp.(response.JSON)
|
||||
account := ""
|
||||
if respJson.Code != response.IllegalParam {
|
||||
@ -39,7 +39,7 @@ func AddLoginLog(gp *core.Goploy, resp core.Response) {
|
||||
}
|
||||
}
|
||||
|
||||
func AddOPLog(gp *core.Goploy, resp core.Response) {
|
||||
func AddOPLog(gp *server.Goploy, resp server.Response) {
|
||||
requestData := ""
|
||||
if gp.URLQuery != nil && len(gp.URLQuery) > 0 && len(gp.Body) > 2 {
|
||||
requestData = "{" + string(gp.Body[1:len(gp.Body)-1]) + ", \"_query\":\"" + gp.URLQuery.Encode() + "\"}"
|
||||
@ -71,7 +71,7 @@ func AddOPLog(gp *core.Goploy, resp core.Response) {
|
||||
}
|
||||
}
|
||||
|
||||
func AddUploadLog(gp *core.Goploy, resp core.Response) {
|
||||
func AddUploadLog(gp *server.Goploy, resp server.Response) {
|
||||
var serverID int64 = 0
|
||||
var path = ""
|
||||
respJson := resp.(response.JSON)
|
||||
@ -97,7 +97,7 @@ func AddUploadLog(gp *core.Goploy, resp core.Response) {
|
||||
}
|
||||
}
|
||||
|
||||
func AddDeleteLog(gp *core.Goploy, resp core.Response) {
|
||||
func AddDeleteLog(gp *server.Goploy, resp server.Response) {
|
||||
var serverID int64 = 0
|
||||
var path = ""
|
||||
respJson := resp.(response.JSON)
|
||||
@ -127,7 +127,7 @@ func AddDeleteLog(gp *core.Goploy, resp core.Response) {
|
||||
}
|
||||
}
|
||||
|
||||
func AddDownloadLog(gp *core.Goploy, resp core.Response) {
|
||||
func AddDownloadLog(gp *server.Goploy, resp server.Response) {
|
||||
msg := ""
|
||||
path := ""
|
||||
var serverID int64 = 0
|
||||
@ -157,7 +157,7 @@ func AddDownloadLog(gp *core.Goploy, resp core.Response) {
|
||||
}
|
||||
}
|
||||
|
||||
func AddPreviewLog(gp *core.Goploy, resp core.Response) {
|
||||
func AddPreviewLog(gp *server.Goploy, resp server.Response) {
|
||||
msg := ""
|
||||
path := ""
|
||||
var serverID int64 = 0
|
172
cmd/server/api/monitor.go
Normal file
172
cmd/server/api/monitor.go
Normal file
@ -0,0 +1,172 @@
|
||||
// Copyright 2022 The Goploy Authors. All rights reserved.
|
||||
// Use of this source code is governed by a GPLv3-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/zhenorzz/goploy/cmd/server/api/middleware"
|
||||
"github.com/zhenorzz/goploy/config"
|
||||
"github.com/zhenorzz/goploy/internal/monitor"
|
||||
"github.com/zhenorzz/goploy/internal/server"
|
||||
"github.com/zhenorzz/goploy/internal/server/response"
|
||||
"github.com/zhenorzz/goploy/model"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Monitor API
|
||||
|
||||
func (m Monitor) Handler() []server.Route {
|
||||
return []server.Route{
|
||||
server.NewRoute("/monitor/getList", http.MethodGet, m.GetList).Permissions(config.ShowMonitorPage),
|
||||
server.NewRoute("/monitor/check", http.MethodPost, m.Check).LogFunc(middleware.AddOPLog),
|
||||
server.NewRoute("/monitor/add", http.MethodPost, m.Add).Permissions(config.AddMonitor).LogFunc(middleware.AddOPLog),
|
||||
server.NewRoute("/monitor/edit", http.MethodPut, m.Edit).Permissions(config.EditMonitor).LogFunc(middleware.AddOPLog),
|
||||
server.NewRoute("/monitor/toggle", http.MethodPut, m.Toggle).Permissions(config.EditMonitor).LogFunc(middleware.AddOPLog),
|
||||
server.NewRoute("/monitor/remove", http.MethodDelete, m.Remove).Permissions(config.DeleteMonitor).LogFunc(middleware.AddOPLog),
|
||||
}
|
||||
}
|
||||
|
||||
func (Monitor) GetList(gp *server.Goploy) server.Response {
|
||||
monitorList, err := model.Monitor{NamespaceID: gp.Namespace.ID}.GetList()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
Monitors model.Monitors `json:"list"`
|
||||
}{Monitors: monitorList},
|
||||
}
|
||||
}
|
||||
|
||||
func (Monitor) Check(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
Type int `json:"type" validate:"oneof=1 2 3 4 5"`
|
||||
Items []string `json:"items" validate:"required"`
|
||||
Timeout time.Duration `json:"timeout"`
|
||||
Process string `json:"process"`
|
||||
Script string `json:"script"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
if err := (monitor.Monitor{
|
||||
Type: reqData.Type,
|
||||
Items: reqData.Items,
|
||||
Timeout: reqData.Timeout,
|
||||
Process: reqData.Process,
|
||||
Script: reqData.Script,
|
||||
}.Check()); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{Message: "Connected"}
|
||||
}
|
||||
|
||||
func (Monitor) Add(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
Type int `json:"type" validate:"oneof=1 2 3 4 5"`
|
||||
Target string `json:"target" validate:"required"`
|
||||
Second int `json:"second" validate:"gt=0"`
|
||||
Times uint16 `json:"times" validate:"gt=0"`
|
||||
SilentCycle int `json:"silentCycle" validate:"required"`
|
||||
NotifyType uint8 `json:"notifyType" validate:"gt=0"`
|
||||
NotifyTarget string `json:"notifyTarget" validate:"required"`
|
||||
Description string `json:"description" validate:"max=255"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
id, err := model.Monitor{
|
||||
NamespaceID: gp.Namespace.ID,
|
||||
Name: reqData.Name,
|
||||
Type: reqData.Type,
|
||||
Target: reqData.Target,
|
||||
Second: reqData.Second,
|
||||
Times: reqData.Times,
|
||||
SilentCycle: reqData.SilentCycle,
|
||||
NotifyType: reqData.NotifyType,
|
||||
NotifyTarget: reqData.NotifyTarget,
|
||||
Description: reqData.Description,
|
||||
}.AddRow()
|
||||
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
ID int64 `json:"id"`
|
||||
}{ID: id},
|
||||
}
|
||||
}
|
||||
|
||||
func (Monitor) Edit(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
ID int64 `json:"id" validate:"gt=0"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Type int `json:"type" validate:"oneof=1 2 3 4 5"`
|
||||
Target string `json:"target" validate:"required"`
|
||||
Second int `json:"second" validate:"gt=0"`
|
||||
Times uint16 `json:"times" validate:"gt=0"`
|
||||
SilentCycle int `json:"silentCycle" validate:"required"`
|
||||
NotifyType uint8 `json:"notifyType" validate:"gt=0"`
|
||||
NotifyTarget string `json:"notifyTarget" validate:"required"`
|
||||
Description string `json:"description" validate:"max=255"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
err := model.Monitor{
|
||||
ID: reqData.ID,
|
||||
Name: reqData.Name,
|
||||
Type: reqData.Type,
|
||||
Target: reqData.Target,
|
||||
Second: reqData.Second,
|
||||
Times: reqData.Times,
|
||||
SilentCycle: reqData.SilentCycle,
|
||||
NotifyType: reqData.NotifyType,
|
||||
NotifyTarget: reqData.NotifyTarget,
|
||||
Description: reqData.Description,
|
||||
}.EditRow()
|
||||
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{}
|
||||
}
|
||||
|
||||
func (Monitor) Toggle(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
ID int64 `json:"id" validate:"gt=0"`
|
||||
State uint8 `json:"state" validate:"oneof=0 1"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
if err := (model.Monitor{ID: reqData.ID, State: reqData.State}).ToggleState(); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{}
|
||||
}
|
||||
|
||||
func (Monitor) Remove(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
ID int64 `json:"id" validate:"gt=0"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
if err := (model.Monitor{ID: reqData.ID}).DeleteRow(); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{}
|
||||
}
|
171
cmd/server/api/namespace.go
Normal file
171
cmd/server/api/namespace.go
Normal file
@ -0,0 +1,171 @@
|
||||
// Copyright 2022 The Goploy Authors. All rights reserved.
|
||||
// Use of this source code is governed by a GPLv3-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/zhenorzz/goploy/cmd/server/api/middleware"
|
||||
"github.com/zhenorzz/goploy/config"
|
||||
"github.com/zhenorzz/goploy/internal/server"
|
||||
"github.com/zhenorzz/goploy/internal/server/response"
|
||||
"github.com/zhenorzz/goploy/model"
|
||||
"net/http"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type Namespace API
|
||||
|
||||
func (n Namespace) Handler() []server.Route {
|
||||
return []server.Route{
|
||||
server.NewRoute("/namespace/getList", http.MethodGet, n.GetList).Permissions(config.ShowNamespacePage),
|
||||
server.NewRoute("/namespace/getOption", http.MethodGet, n.GetOption),
|
||||
server.NewRoute("/namespace/getBindUserList", http.MethodGet, n.GetBindUserList).Permissions(config.ShowNamespacePage),
|
||||
server.NewRoute("/namespace/getUserOption", http.MethodGet, n.GetUserOption),
|
||||
server.NewRoute("/namespace/add", http.MethodPost, n.Add).Permissions(config.AddNamespace).LogFunc(middleware.AddOPLog),
|
||||
server.NewRoute("/namespace/edit", http.MethodPut, n.Edit).Permissions(config.EditNamespace).LogFunc(middleware.AddOPLog),
|
||||
server.NewRoute("/namespace/addUser", http.MethodPost, n.AddUser).Permissions(config.AddNamespaceUser).LogFunc(middleware.AddOPLog),
|
||||
server.NewRoute("/namespace/removeUser", http.MethodDelete, n.RemoveUser).Permissions(config.DeleteNamespaceUser).LogFunc(middleware.AddOPLog),
|
||||
}
|
||||
}
|
||||
|
||||
func (Namespace) GetList(gp *server.Goploy) server.Response {
|
||||
namespaceList, err := model.Namespace{UserID: gp.UserInfo.ID}.GetList()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
Namespaces model.Namespaces `json:"list"`
|
||||
}{Namespaces: namespaceList},
|
||||
}
|
||||
}
|
||||
|
||||
func (Namespace) GetOption(gp *server.Goploy) server.Response {
|
||||
namespaceUsers, err := model.NamespaceUser{UserID: gp.UserInfo.ID}.GetUserNamespaceList()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
NamespaceUsers model.NamespaceUsers `json:"list"`
|
||||
}{NamespaceUsers: namespaceUsers},
|
||||
}
|
||||
}
|
||||
|
||||
func (Namespace) GetUserOption(gp *server.Goploy) server.Response {
|
||||
namespaceUsers, err := model.NamespaceUser{NamespaceID: gp.Namespace.ID}.GetAllUserByNamespaceID()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
NamespaceUsers model.NamespaceUsers `json:"list"`
|
||||
}{NamespaceUsers: namespaceUsers},
|
||||
}
|
||||
}
|
||||
|
||||
func (Namespace) GetBindUserList(gp *server.Goploy) server.Response {
|
||||
id, err := strconv.ParseInt(gp.URLQuery.Get("id"), 10, 64)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
namespaceUsers, err := model.NamespaceUser{NamespaceID: id}.GetBindUserListByNamespaceID()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
NamespaceUsers model.NamespaceUsers `json:"list"`
|
||||
}{NamespaceUsers: namespaceUsers},
|
||||
}
|
||||
}
|
||||
|
||||
func (Namespace) Add(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
id, err := model.Namespace{Name: reqData.Name}.AddRow()
|
||||
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
if err := (model.NamespaceUser{NamespaceID: id}).AddAdminByNamespaceID(); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
ID int64 `json:"id"`
|
||||
}{ID: id},
|
||||
}
|
||||
}
|
||||
|
||||
func (Namespace) Edit(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
ID int64 `json:"id" validate:"gt=0"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
err := model.Namespace{
|
||||
ID: reqData.ID,
|
||||
Name: reqData.Name,
|
||||
}.EditRow()
|
||||
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{}
|
||||
}
|
||||
|
||||
func (Namespace) AddUser(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
NamespaceID int64 `json:"namespaceId" validate:"gt=0"`
|
||||
UserIDs []int64 `json:"userIds" validate:"required"`
|
||||
RoleID int64 `json:"roleId" validate:"required"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
namespaceUsersModel := model.NamespaceUsers{}
|
||||
for _, userID := range reqData.UserIDs {
|
||||
namespaceUserModel := model.NamespaceUser{
|
||||
NamespaceID: reqData.NamespaceID,
|
||||
UserID: userID,
|
||||
RoleID: reqData.RoleID,
|
||||
}
|
||||
namespaceUsersModel = append(namespaceUsersModel, namespaceUserModel)
|
||||
}
|
||||
|
||||
if err := namespaceUsersModel.AddMany(); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
return response.JSON{}
|
||||
}
|
||||
|
||||
func (Namespace) RemoveUser(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
NamespaceUserID int64 `json:"namespaceUserId" validate:"gt=0"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
if err := (model.NamespaceUser{ID: reqData.NamespaceUserID}).DeleteRow(); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{}
|
||||
}
|
894
cmd/server/api/project.go
Normal file
894
cmd/server/api/project.go
Normal file
@ -0,0 +1,894 @@
|
||||
// Copyright 2022 The Goploy Authors. All rights reserved.
|
||||
// Use of this source code is governed by a GPLv3-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/zhenorzz/goploy/cmd/server/api/middleware"
|
||||
"github.com/zhenorzz/goploy/config"
|
||||
"github.com/zhenorzz/goploy/internal/pkg"
|
||||
"github.com/zhenorzz/goploy/internal/repo"
|
||||
"github.com/zhenorzz/goploy/internal/server"
|
||||
"github.com/zhenorzz/goploy/internal/server/response"
|
||||
"github.com/zhenorzz/goploy/model"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Project API
|
||||
|
||||
func (p Project) Handler() []server.Route {
|
||||
return []server.Route{
|
||||
server.NewRoute("/project/getList", http.MethodGet, p.GetList).Permissions(config.ShowProjectPage),
|
||||
server.NewRoute("/project/pingRepos", http.MethodGet, p.PingRepos),
|
||||
server.NewRoute("/project/getRemoteBranchList", http.MethodGet, p.GetRemoteBranchList),
|
||||
server.NewRoute("/project/getBindServerList", http.MethodGet, p.GetBindServerList),
|
||||
server.NewRoute("/project/getBindUserList", http.MethodGet, p.GetBindUserList),
|
||||
server.NewRoute("/project/getProjectFileList", http.MethodGet, p.GetProjectFileList).Permissions(config.FileSync),
|
||||
server.NewRoute("/project/getProjectFileContent", http.MethodGet, p.GetProjectFileContent).Permissions(config.FileSync),
|
||||
server.NewRoute("/project/getReposFileList", http.MethodGet, p.GetReposFileList).Permissions(config.FileCompare),
|
||||
server.NewRoute("/project/add", http.MethodPost, p.Add).Permissions(config.AddProject).LogFunc(middleware.AddOPLog),
|
||||
server.NewRoute("/project/edit", http.MethodPut, p.Edit).Permissions(config.EditProject).LogFunc(middleware.AddOPLog),
|
||||
server.NewRoute("/project/setAutoDeploy", http.MethodPut, p.SetAutoDeploy).Permissions(config.SwitchProjectWebhook).LogFunc(middleware.AddOPLog),
|
||||
server.NewRoute("/project/remove", http.MethodDelete, p.Remove).Permissions(config.DeleteProject).LogFunc(middleware.AddOPLog),
|
||||
server.NewRoute("/project/uploadFile", http.MethodPost, p.UploadFile).Permissions(config.FileSync).LogFunc(middleware.AddOPLog),
|
||||
server.NewRoute("/project/removeFile", http.MethodDelete, p.RemoveFile).Permissions(config.FileSync).LogFunc(middleware.AddOPLog),
|
||||
server.NewRoute("/project/addFile", http.MethodPost, p.AddFile).Permissions(config.FileSync).LogFunc(middleware.AddOPLog),
|
||||
server.NewRoute("/project/editFile", http.MethodPut, p.EditFile).Permissions(config.FileSync).LogFunc(middleware.AddOPLog),
|
||||
server.NewRoute("/project/addTask", http.MethodPost, p.AddTask).Permissions(config.DeployTask).LogFunc(middleware.AddOPLog),
|
||||
server.NewRoute("/project/removeTask", http.MethodDelete, p.RemoveTask).Permissions(config.DeployTask).LogFunc(middleware.AddOPLog),
|
||||
server.NewRoute("/project/getTaskList", http.MethodGet, p.GetTaskList).Permissions(config.DeployTask),
|
||||
server.NewRoute("/project/getReviewList", http.MethodGet, p.GetReviewList).Permissions(config.DeployReview),
|
||||
server.NewRoute("/project/getProcessList", http.MethodGet, p.GetProcessList).Permissions(config.ProcessManager),
|
||||
server.NewRoute("/project/addProcess", http.MethodPost, p.AddProcess).Permissions(config.ProcessManager).LogFunc(middleware.AddOPLog),
|
||||
server.NewRoute("/project/editProcess", http.MethodPut, p.EditProcess).Permissions(config.ProcessManager).LogFunc(middleware.AddOPLog),
|
||||
server.NewRoute("/project/deleteProcess", http.MethodDelete, p.DeleteProcess).Permissions(config.ProcessManager).LogFunc(middleware.AddOPLog),
|
||||
}
|
||||
}
|
||||
|
||||
func (Project) GetList(gp *server.Goploy) server.Response {
|
||||
var projectList model.Projects
|
||||
var err error
|
||||
if _, ok := gp.Namespace.PermissionIDs[config.GetAllProjectList]; ok {
|
||||
projectList, err = model.Project{NamespaceID: gp.Namespace.ID}.GetList()
|
||||
} else {
|
||||
projectList, err = model.Project{NamespaceID: gp.Namespace.ID, UserID: gp.UserInfo.ID}.GetList()
|
||||
}
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
Projects model.Projects `json:"list"`
|
||||
}{Projects: projectList},
|
||||
}
|
||||
}
|
||||
|
||||
func (Project) PingRepos(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
URL string `schema:"url" validate:"required"`
|
||||
RepoType string `schema:"repoType" validate:"required"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeQuery(gp.URLQuery, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
if strings.Contains(reqData.URL, "git@") {
|
||||
host := strings.Split(reqData.URL, "git@")[1]
|
||||
host = strings.Split(host, ":")[0]
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
knownHostsPath := homeDir + "/.ssh/known_hosts"
|
||||
var cmdOutbuf, cmdErrbuf bytes.Buffer
|
||||
cmd := exec.Command("ssh-keygen", "-F", host, "-f", knownHostsPath)
|
||||
cmd.Stdout = &cmdOutbuf
|
||||
cmd.Stderr = &cmdErrbuf
|
||||
if err := cmd.Run(); err != nil {
|
||||
cmdOutbuf.Reset()
|
||||
cmdErrbuf.Reset()
|
||||
cmd := exec.Command("ssh-keyscan", host)
|
||||
cmd.Stdout = &cmdOutbuf
|
||||
cmd.Stderr = &cmdErrbuf
|
||||
if err := cmd.Run(); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: cmdErrbuf.String()}
|
||||
}
|
||||
f, err := os.OpenFile(knownHostsPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
defer f.Close()
|
||||
if _, err := f.Write(cmdOutbuf.Bytes()); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r, err := repo.GetRepo(reqData.RepoType)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
if err := r.Ping(reqData.URL); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
return response.JSON{}
|
||||
}
|
||||
|
||||
func (Project) GetRemoteBranchList(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
URL string `schema:"url" validate:"required"`
|
||||
RepoType string `schema:"repoType" validate:"required"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeQuery(gp.URLQuery, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
if strings.Contains(reqData.URL, "git@") {
|
||||
host := strings.Split(reqData.URL, "git@")[1]
|
||||
host = strings.Split(host, ":")[0]
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
knownHostsPath := homeDir + "/.ssh/known_hosts"
|
||||
var cmdOutbuf, cmdErrbuf bytes.Buffer
|
||||
cmd := exec.Command("ssh-keygen", "-F", host, "-f", knownHostsPath)
|
||||
cmd.Stdout = &cmdOutbuf
|
||||
cmd.Stderr = &cmdErrbuf
|
||||
if err := cmd.Run(); err != nil {
|
||||
cmdOutbuf.Reset()
|
||||
cmdErrbuf.Reset()
|
||||
cmd := exec.Command("ssh-keyscan", host)
|
||||
cmd.Stdout = &cmdOutbuf
|
||||
cmd.Stderr = &cmdErrbuf
|
||||
if err := cmd.Run(); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: cmdErrbuf.String()}
|
||||
}
|
||||
f, err := os.OpenFile(knownHostsPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
defer f.Close()
|
||||
if _, err := f.Write(cmdOutbuf.Bytes()); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r, err := repo.GetRepo(reqData.RepoType)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
list, err := r.RemoteBranchList(reqData.URL)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
return response.JSON{Data: struct {
|
||||
Branch []string `json:"branch"`
|
||||
}{Branch: list}}
|
||||
}
|
||||
|
||||
func (Project) GetBindServerList(gp *server.Goploy) server.Response {
|
||||
id, err := strconv.ParseInt(gp.URLQuery.Get("id"), 10, 64)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
projectServers, err := model.ProjectServer{ProjectID: id}.GetBindServerListByProjectID()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
ProjectServers model.ProjectServers `json:"list"`
|
||||
}{ProjectServers: projectServers},
|
||||
}
|
||||
}
|
||||
|
||||
func (Project) GetBindUserList(gp *server.Goploy) server.Response {
|
||||
id, err := strconv.ParseInt(gp.URLQuery.Get("id"), 10, 64)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
projectUsers, err := model.ProjectUser{ProjectID: id, NamespaceID: gp.Namespace.ID}.GetBindUserListByProjectID()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
ProjectUsers model.ProjectUsers `json:"list"`
|
||||
}{ProjectUsers: projectUsers},
|
||||
}
|
||||
}
|
||||
|
||||
func (Project) GetProjectFileList(gp *server.Goploy) server.Response {
|
||||
id, err := strconv.ParseInt(gp.URLQuery.Get("id"), 10, 64)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
projectFiles, err := model.ProjectFile{ProjectID: id}.GetListByProjectID()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
ProjectFiles model.ProjectFiles `json:"list"`
|
||||
}{ProjectFiles: projectFiles},
|
||||
}
|
||||
}
|
||||
|
||||
func (Project) GetProjectFileContent(gp *server.Goploy) server.Response {
|
||||
id, err := strconv.ParseInt(gp.URLQuery.Get("id"), 10, 64)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
projectFileData, err := model.ProjectFile{ID: id}.GetData()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
fileBytes, err := ioutil.ReadFile(path.Join(config.GetProjectFilePath(projectFileData.ProjectID), projectFileData.Filename))
|
||||
if err != nil {
|
||||
fmt.Println("read fail", err)
|
||||
}
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
Content string `json:"content"`
|
||||
}{Content: string(fileBytes)},
|
||||
}
|
||||
}
|
||||
|
||||
func (Project) GetReposFileList(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
ID int64 `schema:"id" validate:"gt=0"`
|
||||
Path string `schema:"path" validate:"required"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeQuery(gp.URLQuery, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
files, err := ioutil.ReadDir(path.Join(config.GetProjectPath(reqData.ID), reqData.Path))
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
type fileInfo struct {
|
||||
Name string `json:"name"`
|
||||
Size int64 `json:"size"`
|
||||
Mode string `json:"mode"`
|
||||
ModTime string `json:"modTime"`
|
||||
IsDir bool `json:"isDir"`
|
||||
}
|
||||
var fileList []fileInfo
|
||||
for _, f := range files {
|
||||
fileList = append(fileList, fileInfo{
|
||||
Name: f.Name(),
|
||||
Size: f.Size(),
|
||||
Mode: f.Mode().String(),
|
||||
ModTime: f.ModTime().Format("2006-01-02 15:04:05"),
|
||||
IsDir: f.IsDir(),
|
||||
})
|
||||
}
|
||||
|
||||
return response.JSON{Data: fileList}
|
||||
}
|
||||
|
||||
func (Project) Add(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
RepoType string `json:"repoType" validate:"required"`
|
||||
URL string `json:"url" validate:"required"`
|
||||
Path string `json:"path" validate:"required"`
|
||||
Environment uint8 `json:"environment" validate:"required"`
|
||||
Branch string `json:"branch" validate:"required"`
|
||||
SymlinkPath string `json:"symlinkPath"`
|
||||
SymlinkBackupNumber uint8 `json:"symlinkBackupNumber"`
|
||||
Review uint8 `json:"review"`
|
||||
ReviewURL string `json:"reviewURL"`
|
||||
AfterPullScriptMode string `json:"afterPullScriptMode"`
|
||||
AfterPullScript string `json:"afterPullScript"`
|
||||
AfterDeployScriptMode string `json:"afterDeployScriptMode"`
|
||||
AfterDeployScript string `json:"afterDeployScript"`
|
||||
TransferType string `json:"transferType"`
|
||||
TransferOption string `json:"transferOption"`
|
||||
DeployServerMode string `json:"deployServerMode"`
|
||||
ServerIDs []int64 `json:"serverIds"`
|
||||
UserIDs []int64 `json:"userIds"`
|
||||
NotifyType uint8 `json:"notifyType"`
|
||||
NotifyTarget string `json:"notifyTarget"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
if _, err := pkg.ParseCommandLine(reqData.TransferOption); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: "Invalid transfer option format"}
|
||||
}
|
||||
|
||||
projectID, err := model.Project{
|
||||
NamespaceID: gp.Namespace.ID,
|
||||
Name: reqData.Name,
|
||||
RepoType: reqData.RepoType,
|
||||
URL: reqData.URL,
|
||||
Path: reqData.Path,
|
||||
Environment: reqData.Environment,
|
||||
Branch: reqData.Branch,
|
||||
SymlinkPath: reqData.SymlinkPath,
|
||||
SymlinkBackupNumber: reqData.SymlinkBackupNumber,
|
||||
Review: reqData.Review,
|
||||
ReviewURL: reqData.ReviewURL,
|
||||
AfterPullScriptMode: reqData.AfterPullScriptMode,
|
||||
AfterPullScript: reqData.AfterPullScript,
|
||||
AfterDeployScriptMode: reqData.AfterDeployScriptMode,
|
||||
AfterDeployScript: reqData.AfterDeployScript,
|
||||
TransferType: reqData.TransferType,
|
||||
TransferOption: reqData.TransferOption,
|
||||
DeployServerMode: reqData.DeployServerMode,
|
||||
NotifyType: reqData.NotifyType,
|
||||
NotifyTarget: reqData.NotifyTarget,
|
||||
}.AddRow()
|
||||
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
projectServersModel := model.ProjectServers{}
|
||||
for _, serverID := range reqData.ServerIDs {
|
||||
projectServerModel := model.ProjectServer{
|
||||
ProjectID: projectID,
|
||||
ServerID: serverID,
|
||||
}
|
||||
projectServersModel = append(projectServersModel, projectServerModel)
|
||||
}
|
||||
if err := projectServersModel.AddMany(); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
projectUsersModel := model.ProjectUsers{}
|
||||
for _, userID := range reqData.UserIDs {
|
||||
projectUserModel := model.ProjectUser{
|
||||
ProjectID: projectID,
|
||||
UserID: userID,
|
||||
}
|
||||
projectUsersModel = append(projectUsersModel, projectUserModel)
|
||||
}
|
||||
if err := projectUsersModel.AddMany(); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{}
|
||||
}
|
||||
|
||||
func (Project) Edit(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
ID int64 `json:"id" validate:"gt=0"`
|
||||
Name string `json:"name"`
|
||||
RepoType string `json:"repoType"`
|
||||
URL string `json:"url"`
|
||||
Path string `json:"path"`
|
||||
SymlinkPath string `json:"symlinkPath"`
|
||||
SymlinkBackupNumber uint8 `json:"symlinkBackupNumber"`
|
||||
Review uint8 `json:"review"`
|
||||
ReviewURL string `json:"reviewURL"`
|
||||
Environment uint8 `json:"environment"`
|
||||
Branch string `json:"branch"`
|
||||
ServerIDs []int64 `json:"serverIds"`
|
||||
UserIDs []int64 `json:"userIds"`
|
||||
AfterPullScriptMode string `json:"afterPullScriptMode"`
|
||||
AfterPullScript string `json:"afterPullScript"`
|
||||
AfterDeployScriptMode string `json:"afterDeployScriptMode"`
|
||||
AfterDeployScript string `json:"afterDeployScript"`
|
||||
TransferType string `json:"transferType"`
|
||||
TransferOption string `json:"transferOption"`
|
||||
DeployServerMode string `json:"deployServerMode"`
|
||||
NotifyType uint8 `json:"notifyType"`
|
||||
NotifyTarget string `json:"notifyTarget"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
if _, err := pkg.ParseCommandLine(reqData.TransferOption); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: "Invalid option format"}
|
||||
}
|
||||
|
||||
projectData, err := model.Project{ID: reqData.ID}.GetData()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
err = model.Project{
|
||||
ID: reqData.ID,
|
||||
Name: reqData.Name,
|
||||
RepoType: reqData.RepoType,
|
||||
URL: reqData.URL,
|
||||
Path: reqData.Path,
|
||||
Environment: reqData.Environment,
|
||||
Branch: reqData.Branch,
|
||||
SymlinkPath: reqData.SymlinkPath,
|
||||
SymlinkBackupNumber: reqData.SymlinkBackupNumber,
|
||||
Review: reqData.Review,
|
||||
ReviewURL: reqData.ReviewURL,
|
||||
AfterPullScriptMode: reqData.AfterPullScriptMode,
|
||||
AfterPullScript: reqData.AfterPullScript,
|
||||
AfterDeployScriptMode: reqData.AfterDeployScriptMode,
|
||||
AfterDeployScript: reqData.AfterDeployScript,
|
||||
TransferType: reqData.TransferType,
|
||||
TransferOption: reqData.TransferOption,
|
||||
DeployServerMode: reqData.DeployServerMode,
|
||||
NotifyType: reqData.NotifyType,
|
||||
NotifyTarget: reqData.NotifyTarget,
|
||||
}.EditRow()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
if reqData.URL != projectData.URL {
|
||||
srcPath := config.GetProjectPath(projectData.ID)
|
||||
_, err := os.Stat(srcPath)
|
||||
if err == nil || os.IsNotExist(err) == false {
|
||||
repo := reqData.URL
|
||||
cmd := exec.Command("git", "remote", "set-url", "origin", repo)
|
||||
cmd.Dir = srcPath
|
||||
if err := cmd.Run(); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: "Project change url fail, you can do it manually, reason: " + err.Error()}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := (model.ProjectServer{ProjectID: projectData.ID}).DeleteByProjectID(); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
projectServersModel := model.ProjectServers{}
|
||||
for _, serverID := range reqData.ServerIDs {
|
||||
projectServerModel := model.ProjectServer{
|
||||
ProjectID: projectData.ID,
|
||||
ServerID: serverID,
|
||||
}
|
||||
projectServersModel = append(projectServersModel, projectServerModel)
|
||||
}
|
||||
if err := projectServersModel.AddMany(); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
if err := (model.ProjectUser{ProjectID: projectData.ID}).DeleteByProjectID(); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
projectUsersModel := model.ProjectUsers{}
|
||||
for _, userID := range reqData.UserIDs {
|
||||
projectUserModel := model.ProjectUser{
|
||||
ProjectID: projectData.ID,
|
||||
UserID: userID,
|
||||
}
|
||||
projectUsersModel = append(projectUsersModel, projectUserModel)
|
||||
}
|
||||
if err := projectUsersModel.AddMany(); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
return response.JSON{}
|
||||
}
|
||||
|
||||
func (Project) SetAutoDeploy(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
ID int64 `json:"id" validate:"gt=0"`
|
||||
AutoDeploy uint8 `json:"autoDeploy" validate:"gte=0"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
err := model.Project{
|
||||
ID: reqData.ID,
|
||||
AutoDeploy: reqData.AutoDeploy,
|
||||
}.SetAutoDeploy()
|
||||
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{}
|
||||
}
|
||||
|
||||
func (Project) Remove(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
ID int64 `json:"id" validate:"gt=0"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
projectData, err := model.Project{ID: reqData.ID}.GetData()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
srcPath := config.GetProjectPath(projectData.ID)
|
||||
if err := os.RemoveAll(srcPath); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: "Delete folder fail, Detail: " + err.Error()}
|
||||
}
|
||||
|
||||
if err := (model.Project{ID: reqData.ID}).RemoveRow(); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
return response.JSON{}
|
||||
}
|
||||
|
||||
func (Project) UploadFile(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
ProjectFileID int64 `schema:"projectFileId" validate:"gte=0"`
|
||||
ProjectID int64 `schema:"projectId" validate:"gt=0"`
|
||||
Filename string `schema:"filename" validate:"required"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeQuery(gp.URLQuery, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
file, _, err := gp.Request.FormFile("file")
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
filePath := path.Join(config.GetProjectFilePath(reqData.ProjectID), reqData.Filename)
|
||||
fileDir := path.Dir(filePath)
|
||||
if _, err := os.Stat(fileDir); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
err := os.MkdirAll(fileDir, 0755)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
} else {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
}
|
||||
|
||||
// read all of the contents of our uploaded file into a
|
||||
// byte array
|
||||
fileBytes, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(filePath, fileBytes, 0755); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
if reqData.ProjectFileID == 0 {
|
||||
reqData.ProjectFileID, err = model.ProjectFile{
|
||||
Filename: reqData.Filename,
|
||||
ProjectID: reqData.ProjectID,
|
||||
}.AddRow()
|
||||
} else {
|
||||
err = model.ProjectFile{
|
||||
ID: reqData.ProjectFileID,
|
||||
Filename: reqData.Filename,
|
||||
ProjectID: reqData.ProjectID,
|
||||
}.EditRow()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
ID int64 `json:"id"`
|
||||
}{ID: reqData.ProjectFileID},
|
||||
}
|
||||
}
|
||||
|
||||
func (Project) AddFile(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
ProjectID int64 `json:"projectId" validate:"gt=0"`
|
||||
Content string `json:"content" validate:"required"`
|
||||
Filename string `json:"filename" validate:"required"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
filePath := path.Join(config.GetProjectFilePath(reqData.ProjectID), reqData.Filename)
|
||||
fileDir := path.Dir(filePath)
|
||||
if _, err := os.Stat(fileDir); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
err := os.MkdirAll(fileDir, 0755)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
} else {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
}
|
||||
|
||||
file, err := os.Create(filePath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer file.Close()
|
||||
file.WriteString(reqData.Content)
|
||||
|
||||
id, err := model.ProjectFile{
|
||||
ProjectID: reqData.ProjectID,
|
||||
Filename: reqData.Filename,
|
||||
}.AddRow()
|
||||
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
ID int64 `json:"id"`
|
||||
}{ID: id},
|
||||
}
|
||||
}
|
||||
|
||||
func (Project) EditFile(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
ID int64 `json:"id" validate:"gt=0"`
|
||||
Content string `json:"content" validate:"required"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
projectFileData, err := model.ProjectFile{ID: reqData.ID}.GetData()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
_, err = os.Stat(config.GetProjectFilePath(projectFileData.ProjectID))
|
||||
if err != nil {
|
||||
err := os.MkdirAll(config.GetProjectFilePath(projectFileData.ProjectID), os.ModePerm)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
}
|
||||
|
||||
file, err := os.Create(path.Join(config.GetProjectFilePath(projectFileData.ProjectID), projectFileData.Filename))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer file.Close()
|
||||
file.WriteString(reqData.Content)
|
||||
|
||||
return response.JSON{}
|
||||
}
|
||||
|
||||
func (Project) RemoveFile(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
ProjectFileID int64 `json:"projectFileId" validate:"gt=0"`
|
||||
}
|
||||
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
projectFileData, err := model.ProjectFile{ID: reqData.ProjectFileID}.GetData()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
if err := os.Remove(path.Join(config.GetProjectFilePath(projectFileData.ProjectID), projectFileData.Filename)); err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return response.JSON{Code: response.Error, Message: "Delete file fail, Detail: " + err.Error()}
|
||||
}
|
||||
}
|
||||
|
||||
if err := (model.ProjectFile{ID: reqData.ProjectFileID}).DeleteRow(); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
return response.JSON{}
|
||||
}
|
||||
|
||||
func (Project) GetReviewList(gp *server.Goploy) server.Response {
|
||||
pagination, err := model.PaginationFrom(gp.URLQuery)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
id, err := strconv.ParseInt(gp.URLQuery.Get("id"), 10, 64)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
ProjectReviews, pagination, err := model.ProjectReview{ProjectID: id}.GetListByProjectID(pagination)
|
||||
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
ProjectReviews model.ProjectReviews `json:"list"`
|
||||
Pagination model.Pagination `json:"pagination"`
|
||||
}{ProjectReviews: ProjectReviews, Pagination: pagination},
|
||||
}
|
||||
}
|
||||
|
||||
func (Project) GetTaskList(gp *server.Goploy) server.Response {
|
||||
pagination, err := model.PaginationFrom(gp.URLQuery)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
id, err := strconv.ParseInt(gp.URLQuery.Get("id"), 10, 64)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
projectTaskList, pagination, err := model.ProjectTask{ProjectID: id}.GetListByProjectID(pagination)
|
||||
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
ProjectTasks model.ProjectTasks `json:"list"`
|
||||
Pagination model.Pagination `json:"pagination"`
|
||||
}{ProjectTasks: projectTaskList, Pagination: pagination},
|
||||
}
|
||||
}
|
||||
|
||||
func (Project) AddTask(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
ProjectID int64 `json:"projectId" validate:"gt=0"`
|
||||
Branch string `json:"branch" validate:"required"`
|
||||
Commit string `json:"commit" validate:"required"`
|
||||
Date string `json:"date" validate:"required"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
id, err := model.ProjectTask{
|
||||
ProjectID: reqData.ProjectID,
|
||||
CommitID: reqData.Commit,
|
||||
Branch: reqData.Branch,
|
||||
Date: reqData.Date,
|
||||
Creator: gp.UserInfo.Name,
|
||||
CreatorID: gp.UserInfo.ID,
|
||||
}.AddRow()
|
||||
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
ID int64 `json:"id"`
|
||||
}{ID: id},
|
||||
}
|
||||
}
|
||||
|
||||
func (Project) RemoveTask(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
ID int64 `json:"id" validate:"gt=0"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
if err := (model.ProjectTask{ID: reqData.ID}).RemoveRow(); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
return response.JSON{}
|
||||
}
|
||||
|
||||
func (Project) GetProcessList(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
ProjectID int64 `schema:"projectId" validate:"gt=0"`
|
||||
Page uint64 `schema:"page" validate:"gt=0"`
|
||||
Rows uint64 `schema:"rows" validate:"gt=0"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeQuery(gp.URLQuery, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
list, err := model.ProjectProcess{ProjectID: reqData.ProjectID}.GetListByProjectID(reqData.Page, reqData.Rows)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
List model.ProjectProcesses `json:"list"`
|
||||
}{List: list},
|
||||
}
|
||||
}
|
||||
|
||||
func (Project) AddProcess(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
ProjectID int64 `json:"projectId" validate:"gt=0"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Status string `json:"status"`
|
||||
Start string `json:"start"`
|
||||
Stop string `json:"stop"`
|
||||
Restart string `json:"restart"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
id, err := model.ProjectProcess{
|
||||
ProjectID: reqData.ProjectID,
|
||||
Name: reqData.Name,
|
||||
Status: reqData.Status,
|
||||
Start: reqData.Start,
|
||||
Stop: reqData.Stop,
|
||||
Restart: reqData.Restart,
|
||||
}.AddRow()
|
||||
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
ID int64 `json:"id"`
|
||||
}{ID: id},
|
||||
}
|
||||
}
|
||||
|
||||
func (Project) EditProcess(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
ID int64 `json:"id" validate:"gt=0"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Status string `json:"status"`
|
||||
Start string `json:"start"`
|
||||
Stop string `json:"stop"`
|
||||
Restart string `json:"restart"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
err := model.ProjectProcess{
|
||||
ID: reqData.ID,
|
||||
Name: reqData.Name,
|
||||
Status: reqData.Status,
|
||||
Start: reqData.Start,
|
||||
Stop: reqData.Stop,
|
||||
Restart: reqData.Restart,
|
||||
}.EditRow()
|
||||
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{}
|
||||
}
|
||||
|
||||
func (Project) DeleteProcess(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
ID int64 `json:"id" validate:"gt=0"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
if err := (model.ProjectProcess{ID: reqData.ID}).DeleteRow(); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
return response.JSON{}
|
||||
}
|
114
cmd/server/api/repository.go
Normal file
114
cmd/server/api/repository.go
Normal file
@ -0,0 +1,114 @@
|
||||
// Copyright 2022 The Goploy Authors. All rights reserved.
|
||||
// Use of this source code is governed by a GPLv3-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/zhenorzz/goploy/internal/repo"
|
||||
"github.com/zhenorzz/goploy/internal/server"
|
||||
"github.com/zhenorzz/goploy/internal/server/response"
|
||||
"github.com/zhenorzz/goploy/model"
|
||||
"net/http"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Repository struct
|
||||
type Repository API
|
||||
|
||||
func (r Repository) Handler() []server.Route {
|
||||
return []server.Route{
|
||||
server.NewRoute("/repository/getCommitList", http.MethodGet, r.GetCommitList),
|
||||
server.NewRoute("/repository/getBranchList", http.MethodGet, r.GetBranchList),
|
||||
server.NewRoute("/repository/getTagList", http.MethodGet, r.GetTagList),
|
||||
}
|
||||
}
|
||||
|
||||
// GetCommitList get latest 10 commit list
|
||||
func (Repository) GetCommitList(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
ID int64 `schema:"id" validate:"gt=0"`
|
||||
Branch string `schema:"branch" validate:"required"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeQuery(gp.URLQuery, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
project, err := model.Project{ID: reqData.ID}.GetData()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
r, err := repo.GetRepo(project.RepoType)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
list, err := r.BranchLog(project.ID, reqData.Branch, 10)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
CommitList []repo.CommitInfo `json:"list"`
|
||||
}{CommitList: list},
|
||||
}
|
||||
}
|
||||
|
||||
func (Repository) GetBranchList(gp *server.Goploy) server.Response {
|
||||
id, err := strconv.ParseInt(gp.URLQuery.Get("id"), 10, 64)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
project, err := model.Project{ID: id}.GetData()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
r, err := repo.GetRepo(project.RepoType)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
branchList, err := r.BranchList(project.ID)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
BranchList []string `json:"list"`
|
||||
}{BranchList: branchList},
|
||||
}
|
||||
}
|
||||
|
||||
func (Repository) GetTagList(gp *server.Goploy) server.Response {
|
||||
id, err := strconv.ParseInt(gp.URLQuery.Get("id"), 10, 64)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
project, err := model.Project{ID: id}.GetData()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
r, err := repo.GetRepo(project.RepoType)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
list, err := r.TagLog(project.ID, 10)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
TagList []repo.CommitInfo `json:"list"`
|
||||
}{TagList: list},
|
||||
}
|
||||
}
|
181
cmd/server/api/role.go
Normal file
181
cmd/server/api/role.go
Normal file
@ -0,0 +1,181 @@
|
||||
// Copyright 2022 The Goploy Authors. All rights reserved.
|
||||
// Use of this source code is governed by a GPLv3-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/zhenorzz/goploy/cmd/server/api/middleware"
|
||||
"github.com/zhenorzz/goploy/config"
|
||||
"github.com/zhenorzz/goploy/internal/server"
|
||||
"github.com/zhenorzz/goploy/internal/server/response"
|
||||
"github.com/zhenorzz/goploy/model"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type Role API
|
||||
|
||||
func (r Role) Handler() []server.Route {
|
||||
return []server.Route{
|
||||
server.NewRoute("/role/getList", http.MethodGet, r.GetList).Permissions(config.ShowRolePage),
|
||||
server.NewRoute("/role/getOption", http.MethodGet, r.GetOption),
|
||||
server.NewRoute("/role/getPermissionList", http.MethodGet, r.GetPermissionList).Permissions(config.ShowRolePage),
|
||||
server.NewRoute("/role/getPermissionBindings", http.MethodGet, r.GetPermissionBindings).Permissions(config.ShowRolePage),
|
||||
server.NewRoute("/role/add", http.MethodPost, r.Add).Permissions(config.AddRole).LogFunc(middleware.AddOPLog),
|
||||
server.NewRoute("/role/edit", http.MethodPut, r.Edit).Permissions(config.EditRole).LogFunc(middleware.AddOPLog),
|
||||
server.NewRoute("/role/remove", http.MethodDelete, r.Remove).Permissions(config.DeleteRole).LogFunc(middleware.AddOPLog),
|
||||
server.NewRoute("/role/changePermission", http.MethodPut, r.ChangePermission).Permissions(config.EditPermission).LogFunc(middleware.AddOPLog),
|
||||
}
|
||||
}
|
||||
|
||||
func (Role) GetList(*server.Goploy) server.Response {
|
||||
roleList, err := model.Role{}.GetList()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
List model.Roles `json:"list"`
|
||||
}{List: roleList},
|
||||
}
|
||||
}
|
||||
|
||||
func (Role) GetOption(*server.Goploy) server.Response {
|
||||
list, err := model.Role{}.GetAll()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{Data: struct {
|
||||
List model.Roles `json:"list"`
|
||||
}{list}}
|
||||
}
|
||||
|
||||
func (Role) GetPermissionList(*server.Goploy) server.Response {
|
||||
list, err := model.Permission{}.GetList()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
List model.Permissions `json:"list"`
|
||||
}{List: list},
|
||||
}
|
||||
}
|
||||
|
||||
func (Role) GetPermissionBindings(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
RoleID int64 `json:"roleId" validate:"required"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeQuery(gp.URLQuery, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
list, err := model.RolePermission{RoleID: reqData.RoleID}.GetList()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
List model.RolePermissions `json:"list"`
|
||||
}{List: list},
|
||||
}
|
||||
}
|
||||
|
||||
func (Role) Add(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
Description string `json:"description" validate:"max=255"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
id, err := model.Role{Name: reqData.Name, Description: reqData.Description}.AddRow()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
ID int64 `json:"id"`
|
||||
}{ID: id},
|
||||
}
|
||||
}
|
||||
|
||||
func (Role) Edit(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
ID int64 `json:"id" validate:"gt=0"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Description string `json:"description" validate:"max=255"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
err := model.Role{ID: reqData.ID, Name: reqData.Name, Description: reqData.Description}.EditRow()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
return response.JSON{}
|
||||
}
|
||||
|
||||
func (Role) Remove(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
ID int64 `json:"id" validate:"gt=0"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
namespaceUser, err := (model.NamespaceUser{RoleID: reqData.ID}).GetDataByRoleID()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
if namespaceUser.ID > 0 {
|
||||
return response.JSON{Code: response.Error, Message: "The role has binding user"}
|
||||
}
|
||||
|
||||
if err := (model.Role{ID: reqData.ID}).DeleteRow(); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
return response.JSON{}
|
||||
}
|
||||
|
||||
func (Role) ChangePermission(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
PermissionIDs []int64 `json:"permissionIds" validate:"required"`
|
||||
RoleID int64 `json:"roleId" validate:"required"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
if err := (model.RolePermission{RoleID: reqData.RoleID}).DeleteByRoleID(); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
rolePermissionsModel := model.RolePermissions{}
|
||||
for _, PermissionID := range reqData.PermissionIDs {
|
||||
rolePermissionModel := model.RolePermission{
|
||||
PermissionID: PermissionID,
|
||||
RoleID: reqData.RoleID,
|
||||
}
|
||||
rolePermissionsModel = append(rolePermissionsModel, rolePermissionModel)
|
||||
}
|
||||
|
||||
if err := rolePermissionsModel.AddMany(); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
return response.JSON{}
|
||||
}
|
1053
cmd/server/api/server.go
Normal file
1053
cmd/server/api/server.go
Normal file
File diff suppressed because it is too large
Load Diff
383
cmd/server/api/user.go
Normal file
383
cmd/server/api/user.go
Normal file
@ -0,0 +1,383 @@
|
||||
// Copyright 2022 The Goploy Authors. All rights reserved.
|
||||
// Use of this source code is governed by a GPLv3-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"database/sql"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/go-ldap/ldap/v3"
|
||||
"github.com/zhenorzz/goploy/cmd/server/api/middleware"
|
||||
"github.com/zhenorzz/goploy/internal/server"
|
||||
"github.com/zhenorzz/goploy/internal/server/response"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/zhenorzz/goploy/config"
|
||||
"github.com/zhenorzz/goploy/model"
|
||||
)
|
||||
|
||||
type User API
|
||||
|
||||
func (u User) Handler() []server.Route {
|
||||
return []server.Route{
|
||||
server.NewWhiteRoute("/user/login", http.MethodPost, u.Login).LogFunc(middleware.AddLoginLog),
|
||||
server.NewWhiteRoute("/user/extLogin", http.MethodPost, u.ExtLogin).LogFunc(middleware.AddLoginLog),
|
||||
server.NewRoute("/user/info", http.MethodGet, u.Info),
|
||||
server.NewRoute("/user/changePassword", http.MethodPut, u.ChangePassword),
|
||||
server.NewRoute("/user/getList", http.MethodGet, u.GetList).Permissions(config.ShowMemberPage),
|
||||
server.NewRoute("/user/getOption", http.MethodGet, u.GetOption),
|
||||
server.NewRoute("/user/add", http.MethodPost, u.Add).Permissions(config.AddMember).LogFunc(middleware.AddOPLog),
|
||||
server.NewRoute("/user/edit", http.MethodPut, u.Edit).Permissions(config.EditMember).LogFunc(middleware.AddOPLog),
|
||||
server.NewRoute("/user/remove", http.MethodDelete, u.Remove).Permissions(config.DeleteMember).LogFunc(middleware.AddOPLog),
|
||||
}
|
||||
}
|
||||
|
||||
func (User) Login(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
Account string `json:"account" validate:"min=1,max=25"`
|
||||
Password string `json:"password" validate:"password"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.IllegalParam, Message: err.Error()}
|
||||
}
|
||||
|
||||
userData, err := model.User{Account: reqData.Account}.GetDataByAccount()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
if config.Toml.LDAP.Enabled && userData.ID != 1 {
|
||||
conn, err := ldap.DialURL(config.Toml.LDAP.URL)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Deny, Message: err.Error()}
|
||||
}
|
||||
|
||||
if config.Toml.LDAP.BindDN != "" {
|
||||
if err := conn.Bind(config.Toml.LDAP.BindDN, config.Toml.LDAP.Password); err != nil {
|
||||
return response.JSON{Code: response.Deny, Message: err.Error()}
|
||||
}
|
||||
}
|
||||
|
||||
filter := fmt.Sprintf("(%s=%s)", config.Toml.LDAP.UID, reqData.Account)
|
||||
if config.Toml.LDAP.UserFilter != "" {
|
||||
filter = fmt.Sprintf("(&(%s)%s)", config.Toml.LDAP.UserFilter, filter)
|
||||
}
|
||||
|
||||
searchRequest := ldap.NewSearchRequest(
|
||||
config.Toml.LDAP.BaseDN,
|
||||
ldap.ScopeWholeSubtree,
|
||||
ldap.NeverDerefAliases,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
filter,
|
||||
[]string{config.Toml.LDAP.UID},
|
||||
nil)
|
||||
|
||||
sr, err := conn.Search(searchRequest)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Deny, Message: err.Error()}
|
||||
}
|
||||
if len(sr.Entries) != 1 {
|
||||
return response.JSON{Code: response.Deny, Message: fmt.Sprintf("No %s record in baseDN %s", reqData.Account, config.Toml.LDAP.BaseDN)}
|
||||
}
|
||||
if err := conn.Bind(sr.Entries[0].DN, reqData.Password); err != nil {
|
||||
return response.JSON{Code: response.Deny, Message: err.Error()}
|
||||
}
|
||||
} else {
|
||||
if err := userData.Validate(reqData.Password); err != nil {
|
||||
return response.JSON{Code: response.Deny, Message: err.Error()}
|
||||
}
|
||||
}
|
||||
|
||||
if userData.State == model.Disable {
|
||||
return response.JSON{Code: response.AccountDisabled, Message: "Account is disabled"}
|
||||
}
|
||||
namespaceList, err := model.Namespace{UserID: userData.ID}.GetAllByUserID()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
} else if len(namespaceList) == 0 {
|
||||
return response.JSON{Code: response.Error, Message: "No space assigned, please contact the administrator"}
|
||||
}
|
||||
|
||||
token, err := userData.CreateToken()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
_ = model.User{ID: userData.ID, LastLoginTime: time.Now().Format("20060102150405")}.UpdateLastLoginTime()
|
||||
|
||||
cookie := http.Cookie{
|
||||
Name: config.Toml.Cookie.Name,
|
||||
Value: token,
|
||||
Path: "/",
|
||||
MaxAge: config.Toml.Cookie.Expire,
|
||||
HttpOnly: true,
|
||||
}
|
||||
http.SetCookie(gp.ResponseWriter, &cookie)
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
Token string `json:"token"`
|
||||
NamespaceList model.Namespaces `json:"namespaceList"`
|
||||
}{Token: token, NamespaceList: namespaceList},
|
||||
}
|
||||
}
|
||||
|
||||
func (User) ExtLogin(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
Account string `json:"account" validate:"min=1,max=25"`
|
||||
Time int64 `json:"time"`
|
||||
Token string `json:"token" validate:"len=32"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.IllegalParam, Message: err.Error()}
|
||||
}
|
||||
|
||||
if time.Now().Unix() > reqData.Time+30 {
|
||||
return response.JSON{Code: response.IllegalParam, Message: "request time expired"}
|
||||
}
|
||||
|
||||
h := md5.New()
|
||||
h.Write([]byte(reqData.Account + config.Toml.JWT.Key + strconv.FormatInt(reqData.Time, 10)))
|
||||
signedToken := hex.EncodeToString(h.Sum(nil))
|
||||
|
||||
if signedToken != reqData.Token {
|
||||
return response.JSON{Code: response.IllegalParam, Message: "sign error"}
|
||||
}
|
||||
|
||||
userData, err := model.User{Account: reqData.Account}.GetDataByAccount()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
if userData.State == model.Disable {
|
||||
return response.JSON{Code: response.AccountDisabled, Message: "Account is disabled"}
|
||||
}
|
||||
|
||||
namespaceList, err := model.Namespace{UserID: userData.ID}.GetAllByUserID()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
} else if len(namespaceList) == 0 {
|
||||
return response.JSON{Code: response.Error, Message: "No space assigned, please contact the administrator"}
|
||||
}
|
||||
|
||||
token, err := userData.CreateToken()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
_ = model.User{ID: userData.ID, LastLoginTime: time.Now().Format("20060102150405")}.UpdateLastLoginTime()
|
||||
|
||||
cookie := http.Cookie{
|
||||
Name: config.Toml.Cookie.Name,
|
||||
Value: token,
|
||||
Path: "/",
|
||||
MaxAge: config.Toml.Cookie.Expire,
|
||||
HttpOnly: true,
|
||||
}
|
||||
http.SetCookie(gp.ResponseWriter, &cookie)
|
||||
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
Token string `json:"token"`
|
||||
NamespaceList model.Namespaces `json:"namespaceList"`
|
||||
}{Token: token, NamespaceList: namespaceList},
|
||||
}
|
||||
}
|
||||
|
||||
func (User) Info(gp *server.Goploy) server.Response {
|
||||
type RespData struct {
|
||||
UserInfo struct {
|
||||
ID int64 `json:"id"`
|
||||
Account string `json:"account"`
|
||||
Name string `json:"name"`
|
||||
SuperManager int64 `json:"superManager"`
|
||||
} `json:"userInfo"`
|
||||
Namespace struct {
|
||||
ID int64 `json:"id"`
|
||||
PermissionIDs []int64 `json:"permissionIds"`
|
||||
} `json:"namespace"`
|
||||
}
|
||||
data := RespData{}
|
||||
data.UserInfo.ID = gp.UserInfo.ID
|
||||
data.UserInfo.Name = gp.UserInfo.Name
|
||||
data.UserInfo.Account = gp.UserInfo.Account
|
||||
data.UserInfo.SuperManager = gp.UserInfo.SuperManager
|
||||
|
||||
data.Namespace.ID = gp.Namespace.ID
|
||||
data.Namespace.PermissionIDs = make([]int64, len(gp.Namespace.PermissionIDs))
|
||||
i := 0
|
||||
for k := range gp.Namespace.PermissionIDs {
|
||||
data.Namespace.PermissionIDs[i] = k
|
||||
i++
|
||||
}
|
||||
|
||||
return response.JSON{Data: data}
|
||||
}
|
||||
|
||||
func (User) GetList(*server.Goploy) server.Response {
|
||||
users, err := model.User{}.GetList()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
Users model.Users `json:"list"`
|
||||
}{Users: users},
|
||||
}
|
||||
}
|
||||
|
||||
func (User) GetOption(*server.Goploy) server.Response {
|
||||
users, err := model.User{}.GetAll()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{Data: struct {
|
||||
Users model.Users `json:"list"`
|
||||
}{Users: users}}
|
||||
}
|
||||
|
||||
func (User) Add(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
Account string `json:"account" validate:"min=1,max=25"`
|
||||
Password string `json:"password" validate:"omitempty,password"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Contact string `json:"contact" validate:"omitempty,len=11,numeric"`
|
||||
SuperManager int64 `json:"superManager" validate:"min=0,max=1"`
|
||||
}
|
||||
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
userInfo, err := model.User{Account: reqData.Account}.GetDataByAccount()
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
} else if userInfo != (model.User{}) {
|
||||
return response.JSON{Code: response.Error, Message: "Account is already exist"}
|
||||
}
|
||||
id, err := model.User{
|
||||
Account: reqData.Account,
|
||||
Password: reqData.Password,
|
||||
Name: reqData.Name,
|
||||
Contact: reqData.Contact,
|
||||
SuperManager: reqData.SuperManager,
|
||||
}.AddRow()
|
||||
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
if reqData.SuperManager == model.SuperManager {
|
||||
if err := (model.NamespaceUser{UserID: id}).AddAdminByUserID(); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
if err := (model.ProjectUser{UserID: id}).AddAdminByUserID(); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
}
|
||||
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
ID int64 `json:"id"`
|
||||
}{ID: id},
|
||||
}
|
||||
}
|
||||
|
||||
func (User) Edit(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
ID int64 `json:"id" validate:"gt=0"`
|
||||
Password string `json:"password" validate:"omitempty,password"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Contact string `json:"contact" validate:"omitempty,len=11,numeric"`
|
||||
SuperManager int64 `json:"superManager" validate:"min=0,max=1"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
userInfo, err := model.User{ID: reqData.ID}.GetData()
|
||||
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
err = model.User{
|
||||
ID: reqData.ID,
|
||||
Password: reqData.Password,
|
||||
Name: reqData.Name,
|
||||
Contact: reqData.Contact,
|
||||
SuperManager: reqData.SuperManager,
|
||||
}.EditRow()
|
||||
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
if userInfo.SuperManager == model.SuperManager && reqData.SuperManager == model.GeneralUser {
|
||||
if err := (model.NamespaceUser{UserID: reqData.ID}).DeleteByUserID(); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
if err := (model.ProjectUser{UserID: reqData.ID}).DeleteByUserID(); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
} else if userInfo.SuperManager == model.GeneralUser && reqData.SuperManager == model.SuperManager {
|
||||
if err := (model.NamespaceUser{UserID: reqData.ID}).AddAdminByUserID(); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
if err := (model.ProjectUser{UserID: reqData.ID}).AddAdminByUserID(); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
}
|
||||
|
||||
return response.JSON{}
|
||||
}
|
||||
|
||||
func (User) Remove(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
ID int64 `json:"id" validate:"gt=0"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
if reqData.ID == 1 {
|
||||
return response.JSON{Code: response.Error, Message: "Can not delete the super manager"}
|
||||
}
|
||||
if err := (model.User{ID: reqData.ID}).RemoveRow(); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{}
|
||||
}
|
||||
|
||||
func (User) ChangePassword(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
OldPassword string `json:"oldPwd" validate:"password"`
|
||||
NewPassword string `json:"newPwd" validate:"password"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
userData, err := model.User{ID: gp.UserInfo.ID}.GetData()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
if err := userData.Validate(reqData.OldPassword); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
if err := (model.User{ID: gp.UserInfo.ID, Password: reqData.NewPassword}).UpdatePassword(); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{}
|
||||
}
|
@ -13,11 +13,13 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/hashicorp/go-version"
|
||||
"github.com/zhenorzz/goploy/cmd/server/api"
|
||||
"github.com/zhenorzz/goploy/cmd/server/task"
|
||||
"github.com/zhenorzz/goploy/cmd/server/ws"
|
||||
"github.com/zhenorzz/goploy/config"
|
||||
"github.com/zhenorzz/goploy/internal/pkg"
|
||||
"github.com/zhenorzz/goploy/internal/server"
|
||||
"github.com/zhenorzz/goploy/model"
|
||||
"github.com/zhenorzz/goploy/route"
|
||||
"github.com/zhenorzz/goploy/task"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
@ -87,13 +89,28 @@ func main() {
|
||||
println("Log: " + config.Toml.Log.Path)
|
||||
println("Listen: " + config.Toml.Web.Port)
|
||||
println("Running...")
|
||||
route.Init()
|
||||
task.Init()
|
||||
go checkUpdate()
|
||||
// server
|
||||
srv := http.Server{
|
||||
Addr: ":" + config.Toml.Web.Port,
|
||||
srv := server.Server{
|
||||
Server: http.Server{
|
||||
Addr: ":" + config.Toml.Web.Port,
|
||||
},
|
||||
Router: server.NewRouter(),
|
||||
}
|
||||
|
||||
srv.Router.Register(api.User{})
|
||||
srv.Router.Register(api.Namespace{})
|
||||
srv.Router.Register(api.Role{})
|
||||
srv.Router.Register(api.Project{})
|
||||
srv.Router.Register(api.Repository{})
|
||||
srv.Router.Register(api.Monitor{})
|
||||
srv.Router.Register(api.Deploy{})
|
||||
srv.Router.Register(api.Server{})
|
||||
srv.Router.Register(api.Log{})
|
||||
srv.Router.Register(api.Cron{})
|
||||
srv.Router.Register(api.Agent{})
|
||||
srv.Router.Register(ws.GetHub())
|
||||
go func() {
|
||||
c := make(chan os.Signal, 1)
|
||||
|
||||
@ -116,9 +133,7 @@ func main() {
|
||||
}
|
||||
println("Task shutdown gracefully")
|
||||
}()
|
||||
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
log.Fatal("ListenAndServe: ", err.Error())
|
||||
}
|
||||
srv.Spin()
|
||||
_ = os.Remove(config.GetAssetDir())
|
||||
println("shutdown success")
|
||||
}
|
626
cmd/server/task/deploy.go
Normal file
626
cmd/server/task/deploy.go
Normal file
@ -0,0 +1,626 @@
|
||||
// Copyright 2022 The Goploy Authors. All rights reserved.
|
||||
// Use of this source code is governed by a GPLv3-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package task
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"container/list"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/zhenorzz/goploy/cmd/server/ws"
|
||||
"github.com/zhenorzz/goploy/config"
|
||||
"github.com/zhenorzz/goploy/internal/pkg"
|
||||
"github.com/zhenorzz/goploy/internal/pkg/cmd"
|
||||
"github.com/zhenorzz/goploy/internal/repo"
|
||||
"github.com/zhenorzz/goploy/internal/transmitter"
|
||||
"github.com/zhenorzz/goploy/model"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
var deployList = list.New()
|
||||
var deployTick = time.Tick(time.Millisecond)
|
||||
|
||||
type Gsync struct {
|
||||
UserInfo model.User
|
||||
Project model.Project
|
||||
ProjectServers model.ProjectServers
|
||||
CommitInfo repo.CommitInfo
|
||||
CommitID string
|
||||
Branch string
|
||||
}
|
||||
|
||||
type syncMessage struct {
|
||||
serverName string
|
||||
projectID int64
|
||||
detail string
|
||||
state int
|
||||
}
|
||||
|
||||
var projectLogFormat = "projectID: %d %s"
|
||||
|
||||
func startDeployTask() {
|
||||
atomic.AddInt32(&counter, 1)
|
||||
var deployingNumber int32
|
||||
var wg sync.WaitGroup
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-deployTick:
|
||||
if atomic.LoadInt32(&deployingNumber) < config.Toml.APP.DeployLimit {
|
||||
atomic.AddInt32(&deployingNumber, 1)
|
||||
if deployElem := deployList.Front(); deployElem != nil {
|
||||
wg.Add(1)
|
||||
go func(gsync Gsync) {
|
||||
gsync.Exec()
|
||||
atomic.AddInt32(&deployingNumber, -1)
|
||||
wg.Done()
|
||||
}(deployList.Remove(deployElem).(Gsync))
|
||||
} else {
|
||||
atomic.AddInt32(&deployingNumber, -1)
|
||||
}
|
||||
}
|
||||
case <-stop:
|
||||
wg.Wait()
|
||||
atomic.AddInt32(&counter, -1)
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func AddDeployTask(gsync Gsync) {
|
||||
ws.GetHub().Data <- &ws.Data{
|
||||
Type: ws.TypeProject,
|
||||
Message: ws.ProjectMessage{
|
||||
ProjectID: gsync.Project.ID,
|
||||
ProjectName: gsync.Project.Name,
|
||||
State: ws.TaskWaiting,
|
||||
Message: "Task waiting",
|
||||
Ext: struct {
|
||||
LastPublishToken string `json:"lastPublishToken"`
|
||||
}{gsync.Project.LastPublishToken},
|
||||
},
|
||||
}
|
||||
model.PublishTrace{
|
||||
Token: gsync.Project.LastPublishToken,
|
||||
ProjectID: gsync.Project.ID,
|
||||
ProjectName: gsync.Project.Name,
|
||||
PublisherID: gsync.UserInfo.ID,
|
||||
PublisherName: gsync.UserInfo.Name,
|
||||
Type: model.QUEUE,
|
||||
State: model.Success,
|
||||
}.AddRow()
|
||||
deployList.PushBack(gsync)
|
||||
}
|
||||
|
||||
func (gsync Gsync) Exec() {
|
||||
pkg.Logf(pkg.TRACE, "projectID: %d deploy start", gsync.Project.ID)
|
||||
publishTraceModel := model.PublishTrace{
|
||||
Token: gsync.Project.LastPublishToken,
|
||||
ProjectID: gsync.Project.ID,
|
||||
ProjectName: gsync.Project.Name,
|
||||
PublisherID: gsync.UserInfo.ID,
|
||||
PublisherName: gsync.UserInfo.Name,
|
||||
Type: model.Pull,
|
||||
}
|
||||
var err error
|
||||
|
||||
ws.GetHub().Data <- &ws.Data{
|
||||
Type: ws.TypeProject,
|
||||
Message: ws.ProjectMessage{ProjectID: gsync.Project.ID, ProjectName: gsync.Project.Name, State: ws.RepoFollow, Message: "Repo follow"},
|
||||
}
|
||||
r, _ := repo.GetRepo(gsync.Project.RepoType)
|
||||
if len(gsync.CommitID) == 0 {
|
||||
err = r.Follow(gsync.Project, "origin/"+gsync.Project.Branch)
|
||||
} else {
|
||||
err = r.Follow(gsync.Project, gsync.CommitID)
|
||||
}
|
||||
if err != nil {
|
||||
pkg.Logf(pkg.ERROR, projectLogFormat, gsync.Project.ID, err)
|
||||
ws.GetHub().Data <- &ws.Data{
|
||||
Type: ws.TypeProject,
|
||||
Message: ws.ProjectMessage{ProjectID: gsync.Project.ID, ProjectName: gsync.Project.Name, State: ws.DeployFail, Message: err.Error()},
|
||||
}
|
||||
_ = gsync.Project.DeployFail()
|
||||
publishTraceModel.Detail = err.Error()
|
||||
publishTraceModel.State = model.Fail
|
||||
if _, err := publishTraceModel.AddRow(); err != nil {
|
||||
pkg.Logf(pkg.ERROR, projectLogFormat, gsync.Project.ID, err)
|
||||
}
|
||||
gsync.notify(model.ProjectFail, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
commitList, _ := r.CommitLog(gsync.Project.ID, 1)
|
||||
gsync.CommitInfo = commitList[0]
|
||||
if gsync.Branch != "" {
|
||||
gsync.CommitInfo.Branch = gsync.Branch
|
||||
} else {
|
||||
gsync.CommitInfo.Branch = "origin/" + gsync.Project.Branch
|
||||
}
|
||||
|
||||
ext, _ := json.Marshal(gsync.CommitInfo)
|
||||
publishTraceModel.Ext = string(ext)
|
||||
publishTraceModel.State = model.Success
|
||||
if _, err := publishTraceModel.AddRow(); err != nil {
|
||||
pkg.Logf(pkg.ERROR, projectLogFormat, gsync.Project.ID, err)
|
||||
return
|
||||
}
|
||||
|
||||
if totalFileNumber, err := (model.ProjectFile{ProjectID: gsync.Project.ID}).GetTotalByProjectID(); err != nil {
|
||||
pkg.Logf(pkg.ERROR, projectLogFormat, gsync.Project.ID, err)
|
||||
return
|
||||
} else if totalFileNumber > 0 {
|
||||
if err := pkg.CopyDir(config.GetProjectFilePath(gsync.Project.ID), config.GetProjectPath(gsync.Project.ID)); err != nil {
|
||||
pkg.Logf(pkg.ERROR, projectLogFormat, gsync.Project.ID, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if gsync.Project.AfterPullScript != "" {
|
||||
ws.GetHub().Data <- &ws.Data{
|
||||
Type: ws.TypeProject,
|
||||
Message: ws.ProjectMessage{ProjectID: gsync.Project.ID, ProjectName: gsync.Project.Name, State: ws.AfterPullScript, Message: "Run pull script"},
|
||||
}
|
||||
|
||||
publishTraceModel.Type = model.AfterPull
|
||||
ext, _ = json.Marshal(struct {
|
||||
Script string `json:"script"`
|
||||
}{gsync.Project.AfterPullScript})
|
||||
publishTraceModel.Ext = string(ext)
|
||||
pkg.Logf(pkg.TRACE, projectLogFormat, gsync.Project.ID, gsync.Project.AfterPullScript)
|
||||
if outputString, err := gsync.runAfterPullScript(); err != nil {
|
||||
pkg.Logf(pkg.ERROR, projectLogFormat, gsync.Project.ID, fmt.Sprintf("err: %s, output: %s", err, outputString))
|
||||
ws.GetHub().Data <- &ws.Data{
|
||||
Type: ws.TypeProject,
|
||||
Message: ws.ProjectMessage{ProjectID: gsync.Project.ID, ProjectName: gsync.Project.Name, State: ws.DeployFail, Message: err.Error()},
|
||||
}
|
||||
_ = gsync.Project.DeployFail()
|
||||
publishTraceModel.Detail = fmt.Sprintf("err: %s\noutput: %s", err, outputString)
|
||||
publishTraceModel.State = model.Fail
|
||||
if _, err := publishTraceModel.AddRow(); err != nil {
|
||||
pkg.Logf(pkg.ERROR, projectLogFormat, gsync.Project.ID, err)
|
||||
}
|
||||
gsync.notify(model.ProjectFail, err.Error())
|
||||
return
|
||||
} else {
|
||||
publishTraceModel.Detail = outputString
|
||||
publishTraceModel.State = model.Success
|
||||
if _, err := publishTraceModel.AddRow(); err != nil {
|
||||
pkg.Logf(pkg.ERROR, projectLogFormat, gsync.Project.ID, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ws.GetHub().Data <- &ws.Data{
|
||||
Type: ws.TypeProject,
|
||||
Message: ws.ProjectMessage{ProjectID: gsync.Project.ID, ProjectName: gsync.Project.Name, State: ws.Rsync, Message: "Sync"},
|
||||
}
|
||||
ch := make(chan syncMessage, len(gsync.ProjectServers))
|
||||
|
||||
gsync.remoteSync(ch)
|
||||
message := ""
|
||||
for i := 0; i < len(gsync.ProjectServers); i++ {
|
||||
syncMessage := <-ch
|
||||
if syncMessage.state == model.ProjectFail {
|
||||
message += syncMessage.serverName + " error message: " + syncMessage.detail
|
||||
}
|
||||
}
|
||||
close(ch)
|
||||
if message == "" {
|
||||
_ = gsync.Project.DeploySuccess()
|
||||
pkg.Logf(pkg.TRACE, projectLogFormat, gsync.Project.ID, "deploy success")
|
||||
ws.GetHub().Data <- &ws.Data{
|
||||
Type: ws.TypeProject,
|
||||
Message: ws.ProjectMessage{ProjectID: gsync.Project.ID, ProjectName: gsync.Project.Name, State: ws.DeploySuccess, Message: "Success", Ext: gsync.CommitInfo},
|
||||
}
|
||||
gsync.notify(model.ProjectSuccess, message)
|
||||
} else {
|
||||
_ = gsync.Project.DeployFail()
|
||||
pkg.Logf(pkg.TRACE, projectLogFormat, gsync.Project.ID, "deploy fail")
|
||||
ws.GetHub().Data <- &ws.Data{
|
||||
Type: ws.TypeProject,
|
||||
Message: ws.ProjectMessage{ProjectID: gsync.Project.ID, ProjectName: gsync.Project.Name, State: ws.DeployFail, Message: message},
|
||||
}
|
||||
gsync.notify(model.ProjectFail, message)
|
||||
}
|
||||
|
||||
if gsync.Project.SymlinkPath != "" {
|
||||
gsync.removeExpiredBackup()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (gsync Gsync) runAfterPullScript() (string, error) {
|
||||
project := gsync.Project
|
||||
commitInfo := gsync.CommitInfo
|
||||
srcPath := config.GetProjectPath(project.ID)
|
||||
scriptName := "goploy-after-pull." + pkg.GetScriptExt(project.AfterPullScriptMode)
|
||||
scriptFullName := path.Join(srcPath, scriptName)
|
||||
scriptMode := "bash"
|
||||
if len(project.AfterPullScriptMode) != 0 {
|
||||
scriptMode = project.AfterPullScriptMode
|
||||
}
|
||||
scriptText := project.ReplaceVars(commitInfo.ReplaceVars(project.AfterPullScript))
|
||||
_ = ioutil.WriteFile(scriptFullName, []byte(scriptText), 0755)
|
||||
var commandOptions []string
|
||||
if project.AfterPullScriptMode == "cmd" {
|
||||
commandOptions = append(commandOptions, "/C")
|
||||
scriptFullName, _ = filepath.Abs(scriptFullName)
|
||||
}
|
||||
commandOptions = append(commandOptions, scriptFullName)
|
||||
|
||||
handler := exec.Command(scriptMode, commandOptions...)
|
||||
handler.Dir = srcPath
|
||||
|
||||
if output, err := handler.CombinedOutput(); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
_ = os.Remove(scriptFullName)
|
||||
return string(output), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (gsync Gsync) remoteSync(msgChIn chan<- syncMessage) {
|
||||
var serverSync = func(projectServer model.ProjectServer) {
|
||||
project := gsync.Project
|
||||
publishTraceModel := model.PublishTrace{
|
||||
Token: project.LastPublishToken,
|
||||
ProjectID: project.ID,
|
||||
ProjectName: project.Name,
|
||||
PublisherID: gsync.UserInfo.ID,
|
||||
PublisherName: gsync.UserInfo.Name,
|
||||
Type: model.Deploy,
|
||||
}
|
||||
// write after deploy script for rsync
|
||||
scriptName := fmt.Sprintf("goploy-after-deploy-p%d-s%d.%s", project.ID, projectServer.ServerID, pkg.GetScriptExt(project.AfterDeployScriptMode))
|
||||
if len(project.AfterDeployScript) != 0 {
|
||||
scriptContent := project.ReplaceVars(project.AfterDeployScript)
|
||||
scriptContent = projectServer.ReplaceVars(scriptContent)
|
||||
_ = ioutil.WriteFile(path.Join(config.GetProjectPath(project.ID), scriptName), []byte(scriptContent), 0755)
|
||||
}
|
||||
|
||||
transmitterEntity := transmitter.New(project, projectServer)
|
||||
logCmd := transmitterEntity.String()
|
||||
pkg.Log(pkg.TRACE, "projectID: "+strconv.FormatInt(project.ID, 10)+" "+logCmd)
|
||||
ext, _ := json.Marshal(struct {
|
||||
ServerID int64 `json:"serverId"`
|
||||
ServerName string `json:"serverName"`
|
||||
Command string `json:"command"`
|
||||
}{projectServer.ServerID, projectServer.ServerName, logCmd})
|
||||
publishTraceModel.Ext = string(ext)
|
||||
|
||||
if transmitterOutput, err := transmitterEntity.Exec(); err != nil {
|
||||
pkg.Log(pkg.ERROR, fmt.Sprintf("projectID: %d transmit exec err: %s, output: %s", project.ID, err, transmitterOutput))
|
||||
publishTraceModel.Detail = fmt.Sprintf("err: %s\noutput: %s", err, transmitterOutput)
|
||||
publishTraceModel.State = model.Fail
|
||||
publishTraceModel.AddRow()
|
||||
msgChIn <- syncMessage{
|
||||
serverName: projectServer.ServerName,
|
||||
projectID: project.ID,
|
||||
detail: err.Error(),
|
||||
state: model.ProjectFail,
|
||||
}
|
||||
return
|
||||
} else {
|
||||
publishTraceModel.Detail = transmitterOutput
|
||||
publishTraceModel.State = model.Success
|
||||
publishTraceModel.AddRow()
|
||||
}
|
||||
|
||||
var afterDeployCommands []string
|
||||
cmdEntity := cmd.New(projectServer.ServerOS)
|
||||
if len(project.SymlinkPath) != 0 {
|
||||
destDir := path.Join(project.SymlinkPath, project.LastPublishToken)
|
||||
afterDeployCommands = append(afterDeployCommands, cmdEntity.Symlink(destDir, project.Path))
|
||||
}
|
||||
|
||||
if len(project.AfterDeployScript) != 0 {
|
||||
afterDeployScriptPath := path.Join(project.Path, scriptName)
|
||||
afterDeployCommands = append(afterDeployCommands, cmdEntity.Script(project.AfterDeployScriptMode, afterDeployScriptPath))
|
||||
afterDeployCommands = append(afterDeployCommands, cmdEntity.Remove(afterDeployScriptPath))
|
||||
}
|
||||
|
||||
// no symlink and deploy script
|
||||
if len(afterDeployCommands) == 0 {
|
||||
msgChIn <- syncMessage{
|
||||
serverName: projectServer.ServerName,
|
||||
projectID: project.ID,
|
||||
state: model.ProjectSuccess,
|
||||
}
|
||||
return
|
||||
}
|
||||
completeAfterDeployCmd := strings.Join(afterDeployCommands, "&&")
|
||||
publishTraceModel.Type = model.AfterDeploy
|
||||
ext, _ = json.Marshal(struct {
|
||||
ServerID int64 `json:"serverId"`
|
||||
ServerName string `json:"serverName"`
|
||||
Script string `json:"script"`
|
||||
}{projectServer.ServerID, projectServer.ServerName, completeAfterDeployCmd})
|
||||
publishTraceModel.Ext = string(ext)
|
||||
|
||||
client, err := projectServer.ToSSHConfig().Dial()
|
||||
if err != nil {
|
||||
pkg.Log(pkg.ERROR, err.Error())
|
||||
publishTraceModel.Detail = err.Error()
|
||||
publishTraceModel.State = model.Fail
|
||||
publishTraceModel.AddRow()
|
||||
msgChIn <- syncMessage{
|
||||
serverName: projectServer.ServerName,
|
||||
projectID: project.ID,
|
||||
detail: err.Error(),
|
||||
state: model.ProjectFail,
|
||||
}
|
||||
return
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
session, sessionErr := client.NewSession()
|
||||
if sessionErr != nil {
|
||||
pkg.Log(pkg.ERROR, sessionErr.Error())
|
||||
publishTraceModel.Detail = sessionErr.Error()
|
||||
publishTraceModel.State = model.Fail
|
||||
publishTraceModel.AddRow()
|
||||
msgChIn <- syncMessage{
|
||||
serverName: projectServer.ServerName,
|
||||
projectID: project.ID,
|
||||
detail: sessionErr.Error(),
|
||||
state: model.ProjectFail,
|
||||
}
|
||||
return
|
||||
}
|
||||
defer session.Close()
|
||||
pkg.Log(pkg.TRACE, fmt.Sprintf("projectID: %d ssh exec: %s", project.ID, completeAfterDeployCmd))
|
||||
if output, err := session.CombinedOutput(completeAfterDeployCmd); err != nil {
|
||||
pkg.Log(pkg.ERROR, fmt.Sprintf("projectID: %d ssh exec err: %s, output: %s", project.ID, err, output))
|
||||
publishTraceModel.Detail = fmt.Sprintf("err: %s\noutput: %s", err, output)
|
||||
publishTraceModel.State = model.Fail
|
||||
if _, err := publishTraceModel.AddRow(); err != nil {
|
||||
pkg.Log(pkg.ERROR, "projectID: "+strconv.FormatInt(project.ID, 10)+" "+err.Error())
|
||||
}
|
||||
msgChIn <- syncMessage{
|
||||
serverName: projectServer.ServerName,
|
||||
projectID: project.ID,
|
||||
detail: fmt.Sprintf("%s\noutput: %s", err.Error(), output),
|
||||
state: model.ProjectFail,
|
||||
}
|
||||
return
|
||||
} else {
|
||||
publishTraceModel.Detail = string(output)
|
||||
publishTraceModel.State = model.Success
|
||||
if _, err := publishTraceModel.AddRow(); err != nil {
|
||||
pkg.Log(pkg.ERROR, "projectID: "+strconv.FormatInt(project.ID, 10)+" "+err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
msgChIn <- syncMessage{
|
||||
serverName: projectServer.ServerName,
|
||||
projectID: project.ID,
|
||||
state: model.ProjectSuccess,
|
||||
}
|
||||
return
|
||||
}
|
||||
for _, projectServer := range gsync.ProjectServers {
|
||||
if gsync.Project.DeployServerMode == "serial" {
|
||||
serverSync(projectServer)
|
||||
} else {
|
||||
go serverSync(projectServer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// commit id
|
||||
// commit message
|
||||
// server ip & name
|
||||
// deploy user name
|
||||
// deploy time
|
||||
func (gsync Gsync) notify(deployState int, detail string) {
|
||||
if gsync.Project.NotifyType == 0 {
|
||||
return
|
||||
}
|
||||
serverList := ""
|
||||
for _, projectServer := range gsync.ProjectServers {
|
||||
if projectServer.ServerName != projectServer.ServerIP {
|
||||
serverList += projectServer.ServerName + "(" + projectServer.ServerIP + ")"
|
||||
} else {
|
||||
serverList += projectServer.ServerIP
|
||||
}
|
||||
serverList += ", "
|
||||
}
|
||||
serverList = strings.TrimRight(serverList, ", ")
|
||||
project := gsync.Project
|
||||
commitInfo := gsync.CommitInfo
|
||||
var err error
|
||||
var resp *http.Response
|
||||
if project.NotifyType == model.NotifyWeiXin {
|
||||
type markdown struct {
|
||||
Content string `json:"content"`
|
||||
}
|
||||
type message struct {
|
||||
Msgtype string `json:"msgtype"`
|
||||
Markdown markdown `json:"markdown"`
|
||||
}
|
||||
content := "Deploy: <font color=\"warning\">" + project.Name + "</font>\n"
|
||||
content += "Publisher: <font color=\"comment\">" + project.PublisherName + "</font>\n"
|
||||
content += "Author: <font color=\"comment\">" + commitInfo.Author + "</font>\n"
|
||||
if commitInfo.Tag != "" {
|
||||
content += "Tag: <font color=\"comment\">" + commitInfo.Tag + "</font>\n"
|
||||
}
|
||||
content += "Branch: <font color=\"comment\">" + commitInfo.Branch + "</font>\n"
|
||||
content += "CommitSHA: <font color=\"comment\">" + commitInfo.Commit + "</font>\n"
|
||||
content += "CommitMessage: <font color=\"comment\">" + commitInfo.Message + "</font>\n"
|
||||
content += "ServerList: <font color=\"comment\">" + serverList + "</font>\n"
|
||||
if deployState == model.ProjectFail {
|
||||
content += "State: <font color=\"red\">fail</font> \n"
|
||||
content += "> Detail: <font color=\"comment\">" + detail + "</font>"
|
||||
} else {
|
||||
content += "State: <font color=\"green\">success</font>"
|
||||
}
|
||||
|
||||
msg := message{
|
||||
Msgtype: "markdown",
|
||||
Markdown: markdown{
|
||||
Content: content,
|
||||
},
|
||||
}
|
||||
b, _ := json.Marshal(msg)
|
||||
resp, err = http.Post(project.NotifyTarget, "application/json", bytes.NewBuffer(b))
|
||||
} else if project.NotifyType == model.NotifyDingTalk {
|
||||
type markdown struct {
|
||||
Title string `json:"title"`
|
||||
Text string `json:"text"`
|
||||
}
|
||||
type message struct {
|
||||
Msgtype string `json:"msgtype"`
|
||||
Markdown markdown `json:"markdown"`
|
||||
}
|
||||
text := "#### Deploy:" + project.Name + " \n "
|
||||
text += "#### Publisher:" + project.PublisherName + " \n "
|
||||
text += "#### Author:" + commitInfo.Author + " \n "
|
||||
if commitInfo.Tag != "" {
|
||||
text += "#### Tag:" + commitInfo.Tag + " \n "
|
||||
}
|
||||
text += "#### Branch:" + commitInfo.Branch + " \n "
|
||||
text += "#### CommitSHA:" + commitInfo.Commit + " \n "
|
||||
text += "#### CommitMessage:" + commitInfo.Message + " \n "
|
||||
text += "#### ServerList:" + serverList + " \n "
|
||||
if deployState == model.ProjectFail {
|
||||
text += "#### State: <font color=\"red\">fail</font> \n "
|
||||
text += "> Detail: <font color=\"comment\">" + detail + "</font>"
|
||||
} else {
|
||||
text += "#### State: <font color=\"green\">success</font>"
|
||||
}
|
||||
|
||||
msg := message{
|
||||
Msgtype: "markdown",
|
||||
Markdown: markdown{
|
||||
Title: project.Name,
|
||||
Text: text,
|
||||
},
|
||||
}
|
||||
b, _ := json.Marshal(msg)
|
||||
resp, err = http.Post(project.NotifyTarget, "application/json", bytes.NewBuffer(b))
|
||||
} else if project.NotifyType == model.NotifyFeiShu {
|
||||
type content struct {
|
||||
Text string `json:"text"`
|
||||
}
|
||||
type message struct {
|
||||
MsgType string `json:"msg_type"`
|
||||
Content content `json:"content"`
|
||||
}
|
||||
text := ""
|
||||
text += "Publisher: " + project.PublisherName + "\n"
|
||||
text += "Author: " + commitInfo.Author + "\n"
|
||||
if commitInfo.Tag != "" {
|
||||
text += "Tag: " + commitInfo.Tag + "\n"
|
||||
}
|
||||
text += "Branch: " + commitInfo.Branch + "\n"
|
||||
text += "CommitSHA: " + commitInfo.Commit + "\n"
|
||||
text += "CommitMessage: " + commitInfo.Message + "\n"
|
||||
text += "ServerList: " + serverList + "\n"
|
||||
if deployState == model.ProjectFail {
|
||||
text += "State: fail\n "
|
||||
text += "Detail: " + detail
|
||||
} else {
|
||||
text += "State: success"
|
||||
}
|
||||
|
||||
msg := message{
|
||||
MsgType: "text",
|
||||
Content: content{
|
||||
Text: text,
|
||||
},
|
||||
}
|
||||
b, _ := json.Marshal(msg)
|
||||
resp, err = http.Post(project.NotifyTarget, "application/json", bytes.NewBuffer(b))
|
||||
} else if project.NotifyType == model.NotifyCustom {
|
||||
type message struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Data struct {
|
||||
ProjectID int64 `json:"projectId"`
|
||||
ProjectName string `json:"projectName"`
|
||||
Publisher string `json:"publisher"`
|
||||
Author string `json:"author"`
|
||||
Branch string `json:"branch"`
|
||||
Tag string `json:"tag"`
|
||||
CommitSHA string `json:"commitSHA"`
|
||||
CommitMessage string `json:"commitMessage"`
|
||||
ServerList string `json:"serverList"`
|
||||
} `json:"data"`
|
||||
}
|
||||
code := 0
|
||||
if deployState == model.ProjectFail {
|
||||
code = 1
|
||||
}
|
||||
msg := message{
|
||||
Code: code,
|
||||
Message: detail,
|
||||
}
|
||||
msg.Data.ProjectID = project.ID
|
||||
msg.Data.ProjectName = project.Name
|
||||
msg.Data.Publisher = project.PublisherName
|
||||
msg.Data.Author = commitInfo.Author
|
||||
msg.Data.Branch = commitInfo.Branch
|
||||
msg.Data.Tag = commitInfo.Tag
|
||||
msg.Data.CommitSHA = commitInfo.Commit
|
||||
msg.Data.CommitMessage = commitInfo.Message
|
||||
msg.Data.ServerList = serverList
|
||||
b, _ := json.Marshal(msg)
|
||||
resp, err = http.Post(project.NotifyTarget, "application/json", bytes.NewBuffer(b))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
pkg.Log(pkg.ERROR, fmt.Sprintf("projectID: %d notify exec err: %s", project.ID, err))
|
||||
} else {
|
||||
defer resp.Body.Close()
|
||||
responseData, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
pkg.Log(pkg.ERROR, fmt.Sprintf("projectID: %d notify read body err: %s", project.ID, err))
|
||||
} else {
|
||||
pkg.Log(pkg.TRACE, fmt.Sprintf("projectID: %d notify success: %s", project.ID, string(responseData)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//keep the latest 10 project
|
||||
func (gsync Gsync) removeExpiredBackup() {
|
||||
var wg sync.WaitGroup
|
||||
for _, projectServer := range gsync.ProjectServers {
|
||||
wg.Add(1)
|
||||
go func(projectServer model.ProjectServer) {
|
||||
defer wg.Done()
|
||||
client, err := projectServer.ToSSHConfig().Dial()
|
||||
if err != nil {
|
||||
pkg.Log(pkg.ERROR, err.Error())
|
||||
return
|
||||
}
|
||||
defer client.Close()
|
||||
session, err := client.NewSession()
|
||||
if err != nil {
|
||||
pkg.Log(pkg.ERROR, err.Error())
|
||||
return
|
||||
}
|
||||
defer session.Close()
|
||||
var sshOutbuf, sshErrbuf bytes.Buffer
|
||||
session.Stdout = &sshOutbuf
|
||||
session.Stderr = &sshErrbuf
|
||||
if err = session.Run("cd " + gsync.Project.SymlinkPath + ";ls -t | awk 'NR>" + strconv.Itoa(int(gsync.Project.SymlinkBackupNumber)) + "' | xargs rm -rf"); err != nil {
|
||||
pkg.Log(pkg.ERROR, err.Error())
|
||||
}
|
||||
}(projectServer)
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
108
cmd/server/task/monitor.go
Normal file
108
cmd/server/task/monitor.go
Normal file
@ -0,0 +1,108 @@
|
||||
// Copyright 2022 The Goploy Authors. All rights reserved.
|
||||
// Use of this source code is governed by a GPLv3-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package task
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"github.com/zhenorzz/goploy/cmd/server/ws"
|
||||
"github.com/zhenorzz/goploy/internal/monitor"
|
||||
"github.com/zhenorzz/goploy/internal/pkg"
|
||||
"github.com/zhenorzz/goploy/model"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
var monitorTick = time.Tick(time.Minute)
|
||||
|
||||
func startMonitorTask() {
|
||||
atomic.AddInt32(&counter, 1)
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-monitorTick:
|
||||
monitorTask()
|
||||
case <-stop:
|
||||
atomic.AddInt32(&counter, -1)
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
type MonitorCache struct {
|
||||
errorTimes int
|
||||
time int64
|
||||
silentCycle int
|
||||
}
|
||||
|
||||
var monitorCaches = map[int64]MonitorCache{}
|
||||
|
||||
func monitorTask() {
|
||||
monitors, err := model.Monitor{State: model.Enable}.GetAllByState()
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
pkg.Log(pkg.ERROR, "get m list error, detail:"+err.Error())
|
||||
}
|
||||
monitorIDs := map[int64]struct{}{}
|
||||
for _, m := range monitors {
|
||||
monitorIDs[m.ID] = struct{}{}
|
||||
monitorCache, ok := monitorCaches[m.ID]
|
||||
if !ok {
|
||||
monitorCaches[m.ID] = MonitorCache{}
|
||||
monitorCache = monitorCaches[m.ID]
|
||||
}
|
||||
|
||||
if monitorCache.silentCycle > 0 {
|
||||
monitorCache.silentCycle++
|
||||
if monitorCache.silentCycle >= m.SilentCycle {
|
||||
monitorCache.silentCycle = 0
|
||||
}
|
||||
monitorCaches[m.ID] = monitorCache
|
||||
continue
|
||||
}
|
||||
|
||||
now := time.Now().Unix()
|
||||
println(m.Name, "detect", time.Now().String())
|
||||
if int(now-monitorCache.time) >= m.Second {
|
||||
println(m.Name, "in", time.Now().String())
|
||||
monitorCache.time = now
|
||||
ms, err := monitor.NewMonitorFromTarget(m.Type, m.Target)
|
||||
if err != nil {
|
||||
_ = m.TurnOff(err.Error())
|
||||
pkg.Log(pkg.ERROR, "m "+m.Name+" encounter error, "+err.Error())
|
||||
ws.GetHub().Data <- &ws.Data{
|
||||
Type: ws.TypeMonitor,
|
||||
Message: ws.MonitorMessage{MonitorID: m.ID, State: ws.MonitorTurnOff, ErrorContent: err.Error()},
|
||||
}
|
||||
} else if err := ms.Check(); err != nil {
|
||||
monitorErrorContent := err.Error()
|
||||
monitorCache.errorTimes++
|
||||
pkg.Log(pkg.ERROR, "m "+m.Name+" encounter error, "+monitorErrorContent)
|
||||
if m.Times <= uint16(monitorCache.errorTimes) {
|
||||
if body, err := m.Notify(monitorErrorContent); err != nil {
|
||||
pkg.Log(pkg.ERROR, "m "+m.Name+" notify error, "+err.Error())
|
||||
} else {
|
||||
monitorCache.errorTimes = 0
|
||||
monitorCache.silentCycle = 1
|
||||
pkg.Log(pkg.TRACE, "m "+m.Name+" notify return "+body)
|
||||
_ = m.TurnOff(monitorErrorContent)
|
||||
ws.GetHub().Data <- &ws.Data{
|
||||
Type: ws.TypeMonitor,
|
||||
Message: ws.MonitorMessage{MonitorID: m.ID, State: ws.MonitorTurnOff, ErrorContent: monitorErrorContent},
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
monitorCache.errorTimes = 0
|
||||
}
|
||||
monitorCaches[m.ID] = monitorCache
|
||||
}
|
||||
}
|
||||
|
||||
for cacheID := range monitorCaches {
|
||||
if _, ok := monitorIDs[cacheID]; !ok {
|
||||
delete(monitorCaches, cacheID)
|
||||
}
|
||||
}
|
||||
}
|
83
cmd/server/task/project.go
Normal file
83
cmd/server/task/project.go
Normal file
@ -0,0 +1,83 @@
|
||||
// Copyright 2022 The Goploy Authors. All rights reserved.
|
||||
// Use of this source code is governed by a GPLv3-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package task
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"github.com/google/uuid"
|
||||
"github.com/zhenorzz/goploy/internal/pkg"
|
||||
"github.com/zhenorzz/goploy/model"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
var projectTick = time.Tick(time.Minute)
|
||||
|
||||
func startProjectTask() {
|
||||
atomic.AddInt32(&counter, 1)
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-projectTick:
|
||||
projectTask()
|
||||
case <-stop:
|
||||
atomic.AddInt32(&counter, -1)
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func projectTask() {
|
||||
date := time.Now().Format("2006-01-02 15:04:05")
|
||||
projectTasks, err := model.ProjectTask{}.GetNotRunListLTDate(date)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
pkg.Log(pkg.ERROR, "get project task list error, detail:"+err.Error())
|
||||
}
|
||||
for _, projectTask := range projectTasks {
|
||||
project, err := model.Project{ID: projectTask.ProjectID}.GetData()
|
||||
|
||||
if err != nil {
|
||||
pkg.Log(pkg.ERROR, "publish task has no project, detail:"+err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
if err := projectTask.SetRun(); err != nil {
|
||||
pkg.Log(pkg.ERROR, "publish task set run fail, detail:"+err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
projectServers, err := model.ProjectServer{ProjectID: projectTask.ProjectID}.GetBindServerListByProjectID()
|
||||
|
||||
if err != nil {
|
||||
pkg.Log(pkg.ERROR, "publish task has no server, detail:"+err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
userInfo, err := model.User{ID: 1}.GetData()
|
||||
if err != nil {
|
||||
pkg.Log(pkg.ERROR, "publish task has no user, detail:"+err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
project.PublisherID = userInfo.ID
|
||||
project.PublisherName = userInfo.Name
|
||||
project.DeployState = model.ProjectDeploying
|
||||
project.LastPublishToken = uuid.New().String()
|
||||
err = project.Publish()
|
||||
if err != nil {
|
||||
pkg.Log(pkg.ERROR, "publish task change state error, detail:"+err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
AddDeployTask(Gsync{
|
||||
UserInfo: userInfo,
|
||||
Project: project,
|
||||
ProjectServers: projectServers,
|
||||
CommitID: projectTask.CommitID,
|
||||
Branch: projectTask.Branch,
|
||||
})
|
||||
}
|
||||
}
|
115
cmd/server/task/serverMonitor.go
Normal file
115
cmd/server/task/serverMonitor.go
Normal file
@ -0,0 +1,115 @@
|
||||
// Copyright 2022 The Goploy Authors. All rights reserved.
|
||||
// Use of this source code is governed by a GPLv3-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package task
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/zhenorzz/goploy/internal/pkg"
|
||||
"github.com/zhenorzz/goploy/model"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
var serverMonitorTick = time.Tick(time.Minute)
|
||||
|
||||
func startServerMonitorTask() {
|
||||
atomic.AddInt32(&counter, 1)
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-serverMonitorTick:
|
||||
serverMonitorTask()
|
||||
case <-stop:
|
||||
atomic.AddInt32(&counter, -1)
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
type ServerMonitorCache struct {
|
||||
lastCycle int
|
||||
silentCycle int
|
||||
}
|
||||
|
||||
var serverMonitorCaches = map[int64]ServerMonitorCache{}
|
||||
|
||||
var loop = 0
|
||||
|
||||
func serverMonitorTask() {
|
||||
loop++
|
||||
var serverCaches = map[int64]model.Server{}
|
||||
serverMonitorTasks, err := model.ServerMonitor{}.GetAllModBy(loop, time.Now().Format("15:04"))
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
pkg.Log(pkg.ERROR, "get server monitor list error, detail:"+err.Error())
|
||||
}
|
||||
for _, serverMonitor := range serverMonitorTasks {
|
||||
monitorCache, ok := serverMonitorCaches[serverMonitor.ID]
|
||||
if !ok {
|
||||
serverMonitorCaches[serverMonitor.ID] = ServerMonitorCache{}
|
||||
monitorCache = serverMonitorCaches[serverMonitor.ID]
|
||||
}
|
||||
|
||||
if monitorCache.silentCycle > 0 {
|
||||
monitorCache.silentCycle++
|
||||
if monitorCache.silentCycle >= serverMonitor.SilentCycle {
|
||||
monitorCache.silentCycle = 0
|
||||
}
|
||||
serverMonitorCaches[serverMonitor.ID] = monitorCache
|
||||
continue
|
||||
}
|
||||
|
||||
cycleValue, err := model.ServerAgentLog{
|
||||
ServerID: serverMonitor.ServerID,
|
||||
Item: serverMonitor.Item,
|
||||
}.GetCycleValue(serverMonitor.GroupCycle, serverMonitor.Formula)
|
||||
if err != nil {
|
||||
pkg.Log(pkg.ERROR, "get cycle value failed, detail:"+err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
compareRes := false
|
||||
switch serverMonitor.Operator {
|
||||
case ">=":
|
||||
compareRes = strings.Compare(cycleValue, serverMonitor.Value) >= 0
|
||||
case ">":
|
||||
compareRes = strings.Compare(cycleValue, serverMonitor.Value) > 0
|
||||
case "<=":
|
||||
compareRes = strings.Compare(cycleValue, serverMonitor.Value) <= 0
|
||||
case "<":
|
||||
compareRes = strings.Compare(cycleValue, serverMonitor.Value) < 0
|
||||
case "!=":
|
||||
compareRes = strings.Compare(cycleValue, serverMonitor.Value) != 0
|
||||
}
|
||||
if compareRes {
|
||||
monitorCache.lastCycle++
|
||||
} else {
|
||||
monitorCache.lastCycle = 0
|
||||
}
|
||||
|
||||
if monitorCache.lastCycle >= serverMonitor.LastCycle {
|
||||
monitorCache.silentCycle = 1
|
||||
monitorCache.lastCycle = 0
|
||||
|
||||
if _, ok := serverCaches[serverMonitor.ServerID]; !ok {
|
||||
server, err := model.Server{ID: serverMonitor.ServerID}.GetData()
|
||||
if err != nil {
|
||||
pkg.Log(pkg.ERROR, fmt.Sprintf("monitor task %d has no server, detail: %s", serverMonitor.ID, err.Error()))
|
||||
continue
|
||||
}
|
||||
serverCaches[serverMonitor.ServerID] = server
|
||||
}
|
||||
body, err := serverMonitor.Notify(serverCaches[serverMonitor.ServerID], cycleValue)
|
||||
if err != nil {
|
||||
pkg.Log(pkg.ERROR, fmt.Sprintf("monitor task %d notify error, %s", serverMonitor.ID, err.Error()))
|
||||
} else {
|
||||
pkg.Log(pkg.TRACE, fmt.Sprintf("monitor task %d notify return %s", serverMonitor.ID, body))
|
||||
}
|
||||
}
|
||||
serverMonitorCaches[serverMonitor.ID] = monitorCache
|
||||
}
|
||||
}
|
37
cmd/server/task/task.go
Normal file
37
cmd/server/task/task.go
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright 2022 The Goploy Authors. All rights reserved.
|
||||
// Use of this source code is governed by a GPLv3-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package task
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
var counter int32
|
||||
|
||||
var stop = make(chan struct{})
|
||||
|
||||
func Init() {
|
||||
startMonitorTask()
|
||||
startProjectTask()
|
||||
startServerMonitorTask()
|
||||
startDeployTask()
|
||||
}
|
||||
|
||||
func Shutdown(ctx context.Context) error {
|
||||
close(stop)
|
||||
ticker := time.NewTicker(10 * time.Millisecond)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-ticker.C:
|
||||
if atomic.LoadInt32(&counter) == 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
19
cmd/server/ws/MonitorMessage.go
Normal file
19
cmd/server/ws/MonitorMessage.go
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright 2022 The Goploy Authors. All rights reserved.
|
||||
// Use of this source code is governed by a GPLv3-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ws
|
||||
|
||||
type MonitorMessage struct {
|
||||
MonitorID int64 `json:"monitorId"`
|
||||
State uint8 `json:"state"`
|
||||
ErrorContent string `json:"errorContent"`
|
||||
}
|
||||
|
||||
const (
|
||||
MonitorTurnOff = 0
|
||||
)
|
||||
|
||||
func (projectMessage MonitorMessage) canSendTo(*Client) error {
|
||||
return nil
|
||||
}
|
26
cmd/server/ws/ProjectMessage.go
Normal file
26
cmd/server/ws/ProjectMessage.go
Normal file
@ -0,0 +1,26 @@
|
||||
// Copyright 2022 The Goploy Authors. All rights reserved.
|
||||
// Use of this source code is governed by a GPLv3-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ws
|
||||
|
||||
type ProjectMessage struct {
|
||||
ProjectID int64 `json:"projectId"`
|
||||
ProjectName string `json:"projectName"`
|
||||
State uint8 `json:"state"`
|
||||
Message string `json:"message"`
|
||||
Ext interface{} `json:"ext"`
|
||||
}
|
||||
|
||||
const (
|
||||
DeployFail = 0
|
||||
TaskWaiting = 1
|
||||
RepoFollow = 2
|
||||
AfterPullScript = 3
|
||||
Rsync = 4
|
||||
DeploySuccess = 5
|
||||
)
|
||||
|
||||
func (projectMessage ProjectMessage) canSendTo(*Client) error {
|
||||
return nil
|
||||
}
|
148
cmd/server/ws/sftp.go
Normal file
148
cmd/server/ws/sftp.go
Normal file
@ -0,0 +1,148 @@
|
||||
// Copyright 2022 The Goploy Authors. All rights reserved.
|
||||
// Use of this source code is governed by a GPLv3-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ws
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/pkg/sftp"
|
||||
"github.com/zhenorzz/goploy/internal/pkg"
|
||||
"github.com/zhenorzz/goploy/internal/server"
|
||||
"github.com/zhenorzz/goploy/internal/server/response"
|
||||
"github.com/zhenorzz/goploy/model"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// sftp the server file information in websocket
|
||||
func (hub *Hub) sftp(gp *server.Goploy) server.Response {
|
||||
upgrader := websocket.Upgrader{
|
||||
CheckOrigin: func(r *http.Request) bool {
|
||||
if strings.Contains(r.Header.Get("origin"), strings.Split(r.Host, ":")[0]) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
}
|
||||
|
||||
c, err := upgrader.Upgrade(gp.ResponseWriter, gp.Request, nil)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
defer c.Close()
|
||||
c.SetReadLimit(maxMessageSize)
|
||||
c.SetReadDeadline(time.Now().Add(pongWait))
|
||||
c.SetPongHandler(func(string) error { c.SetReadDeadline(time.Now().Add(pongWait)); return nil })
|
||||
|
||||
serverID, err := strconv.ParseInt(gp.URLQuery.Get("serverId"), 10, 64)
|
||||
if err != nil {
|
||||
c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, err.Error()))
|
||||
return response.Empty{}
|
||||
}
|
||||
srv, err := (model.Server{ID: serverID}).GetData()
|
||||
if err != nil {
|
||||
c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, err.Error()))
|
||||
return response.Empty{}
|
||||
}
|
||||
client, err := srv.ToSSHConfig().Dial()
|
||||
if err != nil {
|
||||
c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, err.Error()))
|
||||
return response.Empty{}
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
sftpClient, err := sftp.NewClient(client)
|
||||
if err != nil {
|
||||
c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, err.Error()))
|
||||
return response.Empty{}
|
||||
}
|
||||
defer sftpClient.Close()
|
||||
|
||||
ticker := time.NewTicker(pingPeriod)
|
||||
defer ticker.Stop()
|
||||
stop := make(chan bool, 1)
|
||||
defer func() {
|
||||
stop <- true
|
||||
}()
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
if err := c.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
|
||||
c.Close()
|
||||
return
|
||||
}
|
||||
case <-stop:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
type fileInfo struct {
|
||||
Name string `json:"name"`
|
||||
Size int64 `json:"size"`
|
||||
Mode string `json:"mode"`
|
||||
ModTime string `json:"modTime"`
|
||||
IsDir bool `json:"isDir"`
|
||||
}
|
||||
|
||||
for {
|
||||
messageType, message, err := c.ReadMessage()
|
||||
if err != nil {
|
||||
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) {
|
||||
pkg.Log(pkg.ERROR, err.Error())
|
||||
}
|
||||
break
|
||||
}
|
||||
if messageType == websocket.TextMessage {
|
||||
var fileList []fileInfo
|
||||
code := response.Pass
|
||||
msg := ""
|
||||
fileInfos, err := sftpClient.ReadDir(string(message))
|
||||
if err != nil {
|
||||
code = response.Error
|
||||
msg = err.Error()
|
||||
} else {
|
||||
for _, f := range fileInfos {
|
||||
if f.Mode()&os.ModeSymlink != 0 {
|
||||
continue
|
||||
}
|
||||
fileList = append(fileList, fileInfo{
|
||||
Name: f.Name(),
|
||||
Size: f.Size(),
|
||||
Mode: f.Mode().String(),
|
||||
ModTime: f.ModTime().Format("2006-01-02 15:04:05"),
|
||||
IsDir: f.IsDir(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
b, _ := json.Marshal(response.JSON{Code: code, Message: msg, Data: fileList})
|
||||
if err := c.WriteMessage(websocket.TextMessage, b); err != nil {
|
||||
pkg.Log(pkg.ERROR, err.Error())
|
||||
break
|
||||
}
|
||||
|
||||
// sftp log
|
||||
if err := (model.SftpLog{
|
||||
NamespaceID: gp.Namespace.ID,
|
||||
UserID: gp.UserInfo.ID,
|
||||
ServerID: serverID,
|
||||
RemoteAddr: gp.Request.RemoteAddr,
|
||||
UserAgent: gp.Request.UserAgent(),
|
||||
Type: model.SftpLogTypeRead,
|
||||
Path: string(message),
|
||||
Reason: msg,
|
||||
}.AddRow()); err != nil {
|
||||
pkg.Log(pkg.ERROR, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return response.Empty{}
|
||||
}
|
189
cmd/server/ws/ws.go
Normal file
189
cmd/server/ws/ws.go
Normal file
@ -0,0 +1,189 @@
|
||||
// Copyright 2022 The Goploy Authors. All rights reserved.
|
||||
// Use of this source code is governed by a GPLv3-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ws
|
||||
|
||||
import (
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/zhenorzz/goploy/internal/pkg"
|
||||
"github.com/zhenorzz/goploy/internal/server"
|
||||
"github.com/zhenorzz/goploy/internal/server/response"
|
||||
"github.com/zhenorzz/goploy/model"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// Time allowed to read the next pong message from the peer.
|
||||
pongWait = 60 * time.Second
|
||||
|
||||
// Send pings to peer with this period. Must be less than pongWait.
|
||||
pingPeriod = (pongWait * 9) / 10
|
||||
|
||||
// Maximum message size allowed from peer.
|
||||
maxMessageSize = 10240
|
||||
)
|
||||
|
||||
const (
|
||||
TypeProject = 1
|
||||
TypeMonitor = 3
|
||||
)
|
||||
|
||||
// Client stores a client information
|
||||
type Client struct {
|
||||
Conn *websocket.Conn
|
||||
UserInfo model.User
|
||||
}
|
||||
|
||||
// Data is message struct
|
||||
type Data struct {
|
||||
Type int
|
||||
UserIDs []int64
|
||||
Message Message
|
||||
}
|
||||
|
||||
type Message interface {
|
||||
canSendTo(client *Client) error
|
||||
}
|
||||
|
||||
// Hub is a client struct
|
||||
type Hub struct {
|
||||
// Registered clients.
|
||||
clients map[*Client]bool
|
||||
|
||||
// Inbound messages from the clients.
|
||||
Data chan *Data
|
||||
|
||||
// Register requests from the clients.
|
||||
Register chan *Client
|
||||
|
||||
// Unregister requests from clients.
|
||||
Unregister chan *Client
|
||||
// ping pong ticker
|
||||
ticker chan *Client
|
||||
}
|
||||
|
||||
func init() {
|
||||
go hub.run()
|
||||
}
|
||||
|
||||
func (hub *Hub) Handler() []server.Route {
|
||||
return []server.Route{
|
||||
server.NewRoute("/ws/connect", http.MethodGet, hub.connect),
|
||||
server.NewRoute("/ws/xterm", http.MethodGet, hub.xterm),
|
||||
server.NewRoute("/ws/sftp", http.MethodGet, hub.sftp),
|
||||
}
|
||||
}
|
||||
|
||||
var hub = &Hub{
|
||||
Data: make(chan *Data),
|
||||
clients: make(map[*Client]bool),
|
||||
Register: make(chan *Client),
|
||||
Unregister: make(chan *Client),
|
||||
ticker: make(chan *Client),
|
||||
}
|
||||
|
||||
func GetHub() *Hub {
|
||||
return hub
|
||||
}
|
||||
|
||||
func (hub *Hub) connect(gp *server.Goploy) server.Response {
|
||||
upgrader := websocket.Upgrader{
|
||||
CheckOrigin: func(r *http.Request) bool {
|
||||
if strings.Contains(r.Header.Get("origin"), strings.Split(r.Host, ":")[0]) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
}
|
||||
c, err := upgrader.Upgrade(gp.ResponseWriter, gp.Request, nil)
|
||||
if err != nil {
|
||||
pkg.Log(pkg.ERROR, err.Error())
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
c.SetReadLimit(maxMessageSize)
|
||||
c.SetReadDeadline(time.Now().Add(pongWait))
|
||||
c.SetPongHandler(func(string) error { c.SetReadDeadline(time.Now().Add(pongWait)); return nil })
|
||||
client := &Client{
|
||||
Conn: c,
|
||||
UserInfo: gp.UserInfo,
|
||||
}
|
||||
hub.Register <- client
|
||||
|
||||
ticker := time.NewTicker(pingPeriod)
|
||||
stop := make(chan bool, 1)
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
hub.ticker <- client
|
||||
case <-stop:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
// you must read message to trigger pong handler
|
||||
for {
|
||||
_, _, err = c.ReadMessage()
|
||||
if err != nil {
|
||||
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) {
|
||||
pkg.Log(pkg.ERROR, err.Error())
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
defer func() {
|
||||
hub.Unregister <- client
|
||||
c.Close()
|
||||
ticker.Stop()
|
||||
stop <- true
|
||||
}()
|
||||
|
||||
return response.Empty{}
|
||||
}
|
||||
|
||||
// Run goroutine run the sync hub
|
||||
func (hub *Hub) run() {
|
||||
for {
|
||||
select {
|
||||
case client := <-hub.Register:
|
||||
hub.clients[client] = true
|
||||
case client := <-hub.Unregister:
|
||||
if _, ok := hub.clients[client]; ok {
|
||||
delete(hub.clients, client)
|
||||
client.Conn.Close()
|
||||
}
|
||||
case data := <-hub.Data:
|
||||
for client := range hub.clients {
|
||||
if data.Message.canSendTo(client) != nil {
|
||||
continue
|
||||
}
|
||||
// check userIDs list
|
||||
for _, userID := range data.UserIDs {
|
||||
if client.UserInfo.ID != userID {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if err := client.Conn.WriteJSON(
|
||||
struct {
|
||||
Type int `json:"type"`
|
||||
Message interface{} `json:"message"`
|
||||
}{
|
||||
Type: data.Type,
|
||||
Message: data.Message,
|
||||
}); websocket.IsCloseError(err) {
|
||||
hub.Unregister <- client
|
||||
}
|
||||
}
|
||||
case client := <-hub.ticker:
|
||||
if _, ok := hub.clients[client]; ok {
|
||||
if err := client.Conn.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
|
||||
hub.Unregister <- client
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
191
cmd/server/ws/xterm.go
Normal file
191
cmd/server/ws/xterm.go
Normal file
@ -0,0 +1,191 @@
|
||||
// Copyright 2022 The Goploy Authors. All rights reserved.
|
||||
// Use of this source code is governed by a GPLv3-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ws
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/zhenorzz/goploy/config"
|
||||
"github.com/zhenorzz/goploy/internal/pkg"
|
||||
"github.com/zhenorzz/goploy/internal/server"
|
||||
"github.com/zhenorzz/goploy/internal/server/response"
|
||||
"github.com/zhenorzz/goploy/model"
|
||||
"golang.org/x/crypto/ssh"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// write data to WebSocket
|
||||
// the data comes from ssh server.
|
||||
type xtermBufferWriter struct {
|
||||
buffer bytes.Buffer
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// implement Write interface to write bytes from ssh server into bytes.Buffer.
|
||||
func (w *xtermBufferWriter) Write(p []byte) (int, error) {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
return w.buffer.Write(p)
|
||||
}
|
||||
|
||||
func (hub *Hub) xterm(gp *server.Goploy) server.Response {
|
||||
upgrader := websocket.Upgrader{
|
||||
CheckOrigin: func(r *http.Request) bool {
|
||||
if strings.Contains(r.Header.Get("origin"), strings.Split(r.Host, ":")[0]) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
}
|
||||
c, err := upgrader.Upgrade(gp.ResponseWriter, gp.Request, nil)
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
defer c.Close()
|
||||
c.SetReadLimit(maxMessageSize)
|
||||
c.SetReadDeadline(time.Now().Add(pongWait))
|
||||
c.SetPongHandler(func(string) error { c.SetReadDeadline(time.Now().Add(pongWait)); return nil })
|
||||
|
||||
rows, err := strconv.Atoi(gp.URLQuery.Get("rows"))
|
||||
if err != nil {
|
||||
_ = c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, err.Error()))
|
||||
return response.Empty{}
|
||||
}
|
||||
cols, err := strconv.Atoi(gp.URLQuery.Get("cols"))
|
||||
if err != nil {
|
||||
_ = c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, err.Error()))
|
||||
return response.Empty{}
|
||||
}
|
||||
serverID, err := strconv.ParseInt(gp.URLQuery.Get("serverId"), 10, 64)
|
||||
if err != nil {
|
||||
_ = c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, err.Error()))
|
||||
return response.Empty{}
|
||||
}
|
||||
|
||||
srv, err := (model.Server{ID: serverID}).GetData()
|
||||
if err != nil {
|
||||
_ = c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, err.Error()))
|
||||
return response.Empty{}
|
||||
}
|
||||
client, err := srv.ToSSHConfig().Dial()
|
||||
if err != nil {
|
||||
_ = c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, err.Error()))
|
||||
return response.Empty{}
|
||||
}
|
||||
defer client.Close()
|
||||
// create session
|
||||
session, err := client.NewSession()
|
||||
if err != nil {
|
||||
_ = c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, err.Error()))
|
||||
return response.Empty{}
|
||||
}
|
||||
defer session.Close()
|
||||
sessionStdin, err := session.StdinPipe()
|
||||
if err != nil {
|
||||
_ = c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, err.Error()))
|
||||
return response.Empty{}
|
||||
}
|
||||
comboWriter := new(xtermBufferWriter)
|
||||
//ssh.stdout and stderr will write output into comboWriter
|
||||
session.Stdout = comboWriter
|
||||
session.Stderr = comboWriter
|
||||
// Request pseudo terminal
|
||||
if err := session.RequestPty("xterm", rows, cols, ssh.TerminalModes{}); err != nil {
|
||||
_ = c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, err.Error()))
|
||||
return response.Empty{}
|
||||
}
|
||||
// Start remote shell
|
||||
if err := session.Shell(); err != nil {
|
||||
_ = c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, err.Error()))
|
||||
return response.Empty{}
|
||||
}
|
||||
|
||||
// terminal log
|
||||
tlID, err := model.TerminalLog{
|
||||
NamespaceID: gp.Namespace.ID,
|
||||
UserID: gp.UserInfo.ID,
|
||||
ServerID: serverID,
|
||||
RemoteAddr: gp.Request.RemoteAddr,
|
||||
UserAgent: gp.Request.UserAgent(),
|
||||
StartTime: time.Now().Format("20060102150405"),
|
||||
}.AddRow()
|
||||
if err != nil {
|
||||
_ = c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, err.Error()))
|
||||
return response.Empty{}
|
||||
}
|
||||
|
||||
var recorder *pkg.Recorder
|
||||
recorder, err = pkg.NewRecorder(config.GetTerminalLogPath(tlID), "xterm", rows, cols)
|
||||
if err != nil {
|
||||
pkg.Log(pkg.ERROR, err.Error())
|
||||
} else {
|
||||
defer recorder.Close()
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(pingPeriod)
|
||||
defer ticker.Stop()
|
||||
flushMessageTick := time.NewTicker(time.Millisecond * time.Duration(50))
|
||||
defer flushMessageTick.Stop()
|
||||
stop := make(chan bool, 1)
|
||||
defer func() {
|
||||
stop <- true
|
||||
}()
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
if err := c.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
|
||||
c.Close()
|
||||
return
|
||||
}
|
||||
case <-flushMessageTick.C:
|
||||
if comboWriter.buffer.Len() != 0 {
|
||||
err := c.WriteMessage(websocket.BinaryMessage, comboWriter.buffer.Bytes())
|
||||
if err != nil {
|
||||
c.Close()
|
||||
return
|
||||
}
|
||||
if recorder != nil {
|
||||
if err := recorder.WriteData(comboWriter.buffer.String()); err != nil {
|
||||
pkg.Log(pkg.ERROR, err.Error())
|
||||
}
|
||||
}
|
||||
comboWriter.buffer.Reset()
|
||||
}
|
||||
case <-stop:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
messageType, p, err := c.ReadMessage()
|
||||
if err != nil {
|
||||
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) {
|
||||
pkg.Log(pkg.ERROR, err.Error())
|
||||
}
|
||||
break
|
||||
}
|
||||
if messageType != websocket.PongMessage {
|
||||
if _, err := sessionStdin.Write(p); err != nil {
|
||||
pkg.Log(pkg.ERROR, err.Error())
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := (model.TerminalLog{
|
||||
ID: tlID,
|
||||
EndTime: time.Now().Format("20060102150405"),
|
||||
}.EditRow()); err != nil {
|
||||
pkg.Log(pkg.ERROR, err.Error())
|
||||
}
|
||||
|
||||
return response.Empty{}
|
||||
}
|
@ -4,65 +4,4 @@
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
AssetDir string
|
||||
)
|
||||
|
||||
func GetAssetDir() string {
|
||||
if AssetDir != "" {
|
||||
return AssetDir
|
||||
}
|
||||
file, err := exec.LookPath(os.Args[0])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
app, err := filepath.Abs(file)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
i := strings.LastIndex(app, "/")
|
||||
if i < 0 {
|
||||
i = strings.LastIndex(app, "\\")
|
||||
}
|
||||
if i < 0 {
|
||||
panic(err)
|
||||
}
|
||||
AssetDir = app[0 : i+1]
|
||||
return AssetDir
|
||||
}
|
||||
|
||||
func GetConfigFile() string {
|
||||
return path.Join(GetAssetDir(), "goploy.toml")
|
||||
}
|
||||
|
||||
func GetPidFile() string {
|
||||
return path.Join(GetAssetDir(), "goploy.pid")
|
||||
}
|
||||
|
||||
func GetRepositoryPath() string {
|
||||
if Toml.APP.RepositoryPath != "" {
|
||||
return path.Join(Toml.APP.RepositoryPath, "repository")
|
||||
}
|
||||
return path.Join(GetAssetDir(), "repository")
|
||||
}
|
||||
|
||||
func GetProjectFilePath(projectID int64) string {
|
||||
return path.Join(GetRepositoryPath(), "project-file", "project_"+strconv.FormatInt(projectID, 10))
|
||||
}
|
||||
|
||||
func GetProjectPath(projectID int64) string {
|
||||
return path.Join(GetRepositoryPath(), "project_"+strconv.FormatInt(projectID, 10))
|
||||
}
|
||||
|
||||
func GetTerminalLogPath(tlID int64) string {
|
||||
return path.Join(GetRepositoryPath(), "terminal-log", strconv.FormatInt(tlID, 10)+".cast")
|
||||
}
|
||||
const NamespaceHeaderName = "G-N-ID"
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a GPLv3-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package permission
|
||||
package config
|
||||
|
||||
const (
|
||||
Log = 1
|
134
internal/monitor/monitor.go
Normal file
134
internal/monitor/monitor.go
Normal file
@ -0,0 +1,134 @@
|
||||
// Copyright 2022 The Goploy Authors. All rights reserved.
|
||||
// Use of this source code is governed by a GPLv3-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package monitor
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/zhenorzz/goploy/model"
|
||||
"net"
|
||||
"net/http"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Monitor struct {
|
||||
Type int
|
||||
Items []string
|
||||
Timeout time.Duration
|
||||
Process string
|
||||
Script string
|
||||
}
|
||||
|
||||
func NewMonitorFromTarget(t int, target string) (Monitor, error) {
|
||||
var m Monitor
|
||||
if err := json.Unmarshal([]byte(target), &m); err != nil {
|
||||
return m, err
|
||||
}
|
||||
m.Type = t
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (m Monitor) Check() error {
|
||||
var err error
|
||||
switch m.Type {
|
||||
case 1:
|
||||
err = m.CheckSite()
|
||||
case 2:
|
||||
err = m.CheckPort()
|
||||
case 3:
|
||||
err = m.CheckHostAlive()
|
||||
case 4:
|
||||
m.Script = fmt.Sprintf("ps -ef|grep -v grep|grep %s", m.Process)
|
||||
fallthrough
|
||||
case 5:
|
||||
err = m.CheckScript()
|
||||
default:
|
||||
err = errors.New("type error")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (m Monitor) CheckSite() error {
|
||||
for _, url := range m.Items {
|
||||
client := http.Client{
|
||||
Timeout: m.Timeout * time.Second,
|
||||
}
|
||||
if resp, err := client.Get(url); err != nil {
|
||||
return err
|
||||
} else if 200 < resp.StatusCode || resp.StatusCode >= 400 {
|
||||
return errors.New("Unexpected response status code: " + strconv.Itoa(resp.StatusCode))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m Monitor) CheckPort() error {
|
||||
for _, address := range m.Items {
|
||||
conn, err := net.DialTimeout("tcp", address, m.Timeout*time.Second)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = conn.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m Monitor) CheckHostAlive() error {
|
||||
var stdout, stderr bytes.Buffer
|
||||
for _, addr := range m.Items {
|
||||
var arg []string
|
||||
if runtime.GOOS == "windows" {
|
||||
arg = append(arg, "-n", "1", "-w", strconv.Itoa(int(m.Timeout*1000)), addr)
|
||||
} else {
|
||||
arg = append(arg, "-c", "1", "-W", strconv.Itoa(int(m.Timeout)), addr)
|
||||
}
|
||||
|
||||
stdout.Reset()
|
||||
stderr.Reset()
|
||||
cmd := exec.Command("ping", arg...)
|
||||
cmd.Stdout = &stdout
|
||||
cmd.Stderr = &stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
return errors.New(err.Error() + ", detail: " + stderr.String())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m Monitor) CheckScript() error {
|
||||
for _, serverIDStr := range m.Items {
|
||||
serverID, err := strconv.ParseInt(serverIDStr, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
server, err := (model.Server{ID: serverID}).GetData()
|
||||
if err != nil {
|
||||
return err
|
||||
} else if server.State == model.Disable {
|
||||
continue
|
||||
}
|
||||
client, err := server.ToSSHConfig().SetTimeout(m.Timeout * time.Second).Dial()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
session, err := client.NewSession()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var stdout, stderr bytes.Buffer
|
||||
session.Stdout = &stdout
|
||||
session.Stderr = &stderr
|
||||
if err := session.Run(m.Script); err != nil {
|
||||
return errors.New(err.Error() + ", stdout: " + stdout.String() + ", stderr: " + stderr.String())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
13
internal/server/response/empty.go
Normal file
13
internal/server/response/empty.go
Normal file
@ -0,0 +1,13 @@
|
||||
// Copyright 2022 The Goploy Authors. All rights reserved.
|
||||
// Use of this source code is governed by a GPLv3-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package response
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type Empty struct{}
|
||||
|
||||
func (Empty) Write(http.ResponseWriter, *http.Request) error { return nil }
|
38
internal/server/response/file.go
Normal file
38
internal/server/response/file.go
Normal file
@ -0,0 +1,38 @@
|
||||
// Copyright 2022 The Goploy Authors. All rights reserved.
|
||||
// Use of this source code is governed by a GPLv3-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package response
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type File struct {
|
||||
Filename string
|
||||
}
|
||||
|
||||
func (f File) Write(w http.ResponseWriter, _ *http.Request) error {
|
||||
file, err := os.Open(f.Filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fileStat, err := file.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Disposition", "attachment; filename="+fileStat.Name())
|
||||
w.Header().Set("Content-Type", "application/x-asciicast")
|
||||
w.Header().Set("Content-Length", strconv.FormatInt(fileStat.Size(), 10))
|
||||
|
||||
_, err = io.Copy(w, file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
32
internal/server/response/json.go
Normal file
32
internal/server/response/json.go
Normal file
@ -0,0 +1,32 @@
|
||||
// Copyright 2022 The Goploy Authors. All rights reserved.
|
||||
// Use of this source code is governed by a GPLv3-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package response
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type JSON struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Data interface{} `json:"data"`
|
||||
}
|
||||
|
||||
const (
|
||||
Pass = 0
|
||||
Deny = 1
|
||||
Error = 2
|
||||
AccountDisabled = 10000
|
||||
IllegalRequest = 10001
|
||||
NamespaceInvalid = 10002
|
||||
IllegalParam = 10003
|
||||
LoginExpired = 10086
|
||||
)
|
||||
|
||||
//JSON response
|
||||
func (j JSON) Write(w http.ResponseWriter, _ *http.Request) error {
|
||||
return json.NewEncoder(w).Encode(j)
|
||||
}
|
19
internal/server/response/redirect.go
Normal file
19
internal/server/response/redirect.go
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright 2022 The Goploy Authors. All rights reserved.
|
||||
// Use of this source code is governed by a GPLv3-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package response
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type Redirect struct {
|
||||
URL string
|
||||
Code int
|
||||
}
|
||||
|
||||
func (rdr Redirect) Write(w http.ResponseWriter, r *http.Request) error {
|
||||
http.Redirect(w, r, rdr.URL, rdr.Code)
|
||||
return nil
|
||||
}
|
52
internal/server/response/sftpFile.go
Normal file
52
internal/server/response/sftpFile.go
Normal file
@ -0,0 +1,52 @@
|
||||
// Copyright 2022 The Goploy Authors. All rights reserved.
|
||||
// Use of this source code is governed by a GPLv3-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package response
|
||||
|
||||
import (
|
||||
"github.com/pkg/sftp"
|
||||
"golang.org/x/crypto/ssh"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type SftpFile struct {
|
||||
Client *ssh.Client
|
||||
Filename string
|
||||
Disposition string // attachment | inline
|
||||
}
|
||||
|
||||
func (sf SftpFile) Write(w http.ResponseWriter, _ *http.Request) error {
|
||||
defer sf.Client.Close()
|
||||
|
||||
sftpClient, err := sftp.NewClient(sf.Client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer sftpClient.Close()
|
||||
|
||||
srcFile, err := sftpClient.Open(sf.Filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer srcFile.Close()
|
||||
|
||||
fileStat, err := srcFile.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if sf.Disposition == "attachment" {
|
||||
w.Header().Set("Content-Disposition", "attachment;filename="+fileStat.Name())
|
||||
w.Header().Set("Content-Type", "application/octet-stream")
|
||||
} else {
|
||||
w.Header().Set("Content-Disposition", "inline;filename="+fileStat.Name())
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Length", strconv.FormatInt(fileStat.Size(), 10))
|
||||
_, err = io.Copy(w, srcFile)
|
||||
|
||||
return err
|
||||
}
|
72
internal/server/route.go
Normal file
72
internal/server/route.go
Normal file
@ -0,0 +1,72 @@
|
||||
// Copyright 2022 The Goploy Authors. All rights reserved.
|
||||
// Use of this source code is governed by a GPLv3-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package server
|
||||
|
||||
import "errors"
|
||||
|
||||
type Route struct {
|
||||
pattern string
|
||||
method string // Method specifies the HTTP method (GET, POST, PUT, etc.).
|
||||
permissionIDs []int64 // permission list
|
||||
white bool // no need to login
|
||||
middlewares []func(gp *Goploy) error // Middlewares run before callback, trigger error will end the request
|
||||
callback func(gp *Goploy) Response // API function
|
||||
logFunc func(gp *Goploy, resp Response)
|
||||
}
|
||||
|
||||
type RouteHandler interface {
|
||||
Handler() []Route
|
||||
}
|
||||
|
||||
func NewRoute(pattern, method string, callback func(gp *Goploy) Response) Route {
|
||||
return newRoute(pattern, method, callback)
|
||||
}
|
||||
|
||||
func NewWhiteRoute(pattern, method string, callback func(gp *Goploy) Response) Route {
|
||||
route := newRoute(pattern, method, callback)
|
||||
route.white = true
|
||||
return route
|
||||
}
|
||||
|
||||
func newRoute(pattern, method string, callback func(gp *Goploy) Response) Route {
|
||||
return Route{
|
||||
pattern: pattern,
|
||||
method: method,
|
||||
callback: callback,
|
||||
}
|
||||
}
|
||||
|
||||
func (r Route) Permissions(permissionIDs ...int64) Route {
|
||||
for _, permissionID := range permissionIDs {
|
||||
r.permissionIDs = append(r.permissionIDs, permissionID)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// Middleware global Middleware handle function
|
||||
func (r Route) Middleware(middleware func(gp *Goploy) error) Route {
|
||||
r.middlewares = append(r.middlewares, middleware)
|
||||
return r
|
||||
}
|
||||
|
||||
// LogFunc callback finished
|
||||
func (r Route) LogFunc(f func(gp *Goploy, resp Response)) Route {
|
||||
r.logFunc = f
|
||||
return r
|
||||
}
|
||||
|
||||
func (r Route) hasPermission(permissionIDs map[int64]struct{}) error {
|
||||
if len(r.permissionIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, permissionID := range r.permissionIDs {
|
||||
if _, ok := permissionIDs[permissionID]; ok {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return errors.New("no permission")
|
||||
}
|
@ -2,148 +2,62 @@
|
||||
// Use of this source code is governed by a GPLv3-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package core
|
||||
package server
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/golang-jwt/jwt"
|
||||
"github.com/vearutop/statigz"
|
||||
"github.com/zhenorzz/goploy/config"
|
||||
"github.com/zhenorzz/goploy/internal/pkg"
|
||||
"github.com/zhenorzz/goploy/internal/server/response"
|
||||
"github.com/zhenorzz/goploy/model"
|
||||
"github.com/zhenorzz/goploy/response"
|
||||
"github.com/zhenorzz/goploy/web"
|
||||
"io/fs"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"mime"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Goploy struct {
|
||||
UserInfo model.User
|
||||
Namespace struct {
|
||||
ID int64
|
||||
PermissionIDs map[int64]struct{}
|
||||
}
|
||||
Request *http.Request
|
||||
ResponseWriter http.ResponseWriter
|
||||
URLQuery url.Values
|
||||
Body []byte
|
||||
}
|
||||
|
||||
type RouteApi interface {
|
||||
Routes() []Route
|
||||
}
|
||||
|
||||
type Response interface {
|
||||
Write(http.ResponseWriter, *http.Request) error
|
||||
}
|
||||
|
||||
type Route struct {
|
||||
pattern string
|
||||
method string // Method specifies the HTTP method (GET, POST, PUT, etc.).
|
||||
permissionIDs []int64 // permission list
|
||||
white bool // no need to login
|
||||
middlewares []func(gp *Goploy) error // Middlewares run before callback, trigger error will end the request
|
||||
callback func(gp *Goploy) Response // Controller function
|
||||
logFunc func(gp *Goploy, resp Response)
|
||||
}
|
||||
|
||||
// Router is Route slice and global middlewares
|
||||
type Router struct {
|
||||
routes map[string]Route
|
||||
middlewares *[]func(gp *Goploy) error // Middlewares run before all Route
|
||||
}
|
||||
|
||||
func NewRouter() Router {
|
||||
return Router{
|
||||
func NewRouter() *Router {
|
||||
router := Router{
|
||||
routes: map[string]Route{},
|
||||
middlewares: new([]func(gp *Goploy) error),
|
||||
}
|
||||
|
||||
return &router
|
||||
}
|
||||
|
||||
func NewRoute(pattern, method string, callback func(gp *Goploy) Response) Route {
|
||||
return newRoute(pattern, method, callback)
|
||||
}
|
||||
|
||||
func NewWhiteRoute(pattern, method string, callback func(gp *Goploy) Response) Route {
|
||||
route := newRoute(pattern, method, callback)
|
||||
route.white = true
|
||||
return route
|
||||
}
|
||||
|
||||
func newRoute(pattern, method string, callback func(gp *Goploy) Response) Route {
|
||||
return Route{
|
||||
pattern: pattern,
|
||||
method: method,
|
||||
callback: callback,
|
||||
}
|
||||
}
|
||||
|
||||
// Start a router
|
||||
func (rt Router) Start() {
|
||||
if config.Toml.Env == "production" {
|
||||
subFS, err := fs.Sub(web.Dist, "dist")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
http.Handle("/assets/", statigz.FileServer(subFS.(fs.ReadDirFS)))
|
||||
http.Handle("/favicon.ico", statigz.FileServer(subFS.(fs.ReadDirFS)))
|
||||
}
|
||||
http.Handle("/", rt)
|
||||
}
|
||||
|
||||
// Middleware global Middleware handle function
|
||||
func (rt Router) Middleware(middleware func(gp *Goploy) error) {
|
||||
func (rt *Router) Middleware(middleware func(gp *Goploy) error) {
|
||||
*rt.middlewares = append(*rt.middlewares, middleware)
|
||||
}
|
||||
|
||||
// Add pattern path
|
||||
// callback where path should be handled
|
||||
func (rt Router) Add(ra RouteApi) Router {
|
||||
for _, r := range ra.Routes() {
|
||||
func (rt *Router) Register(ra RouteHandler) {
|
||||
for _, r := range ra.Handler() {
|
||||
rt.routes[r.pattern] = r
|
||||
}
|
||||
return rt
|
||||
}
|
||||
|
||||
func (r Route) Permissions(permissionIDs ...int64) Route {
|
||||
for _, permissionID := range permissionIDs {
|
||||
r.permissionIDs = append(r.permissionIDs, permissionID)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// Middleware global Middleware handle function
|
||||
func (r Route) Middleware(middleware func(gp *Goploy) error) Route {
|
||||
r.middlewares = append(r.middlewares, middleware)
|
||||
return r
|
||||
}
|
||||
|
||||
// LogFunc callback finished
|
||||
func (r Route) LogFunc(f func(gp *Goploy, resp Response)) Route {
|
||||
r.logFunc = f
|
||||
return r
|
||||
}
|
||||
|
||||
func (rt Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
func (rt *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
// If in production env, serve file in go server,
|
||||
// else serve file in npm
|
||||
if config.Toml.Env == "production" {
|
||||
if "/" == r.URL.Path {
|
||||
r, err := web.Dist.Open("dist/index.html")
|
||||
index, err := web.Dist.Open("dist/index.html")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer r.Close()
|
||||
contents, err := ioutil.ReadAll(r)
|
||||
defer index.Close()
|
||||
contents, err := ioutil.ReadAll(index)
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
fmt.Fprint(w, string(contents))
|
||||
return
|
||||
@ -157,7 +71,7 @@ func (rt Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
func (rt Router) doRequest(w http.ResponseWriter, r *http.Request) (*Goploy, Response) {
|
||||
func (rt *Router) doRequest(w http.ResponseWriter, r *http.Request) (*Goploy, Response) {
|
||||
gp := new(Goploy)
|
||||
route, ok := rt.routes[r.URL.Path]
|
||||
if !ok {
|
||||
@ -190,9 +104,9 @@ func (rt Router) doRequest(w http.ResponseWriter, r *http.Request) (*Goploy, Res
|
||||
return gp, response.JSON{Code: response.LoginExpired, Message: "Login expired"}
|
||||
}
|
||||
|
||||
namespaceIDRaw := r.Header.Get(NamespaceHeaderName)
|
||||
namespaceIDRaw := r.Header.Get(config.NamespaceHeaderName)
|
||||
if namespaceIDRaw == "" {
|
||||
namespaceIDRaw = r.URL.Query().Get(NamespaceHeaderName)
|
||||
namespaceIDRaw = r.URL.Query().Get(config.NamespaceHeaderName)
|
||||
}
|
||||
|
||||
namespaceID, err := strconv.ParseInt(namespaceIDRaw, 10, 64)
|
||||
@ -276,20 +190,6 @@ func (rt Router) doRequest(w http.ResponseWriter, r *http.Request) (*Goploy, Res
|
||||
return gp, resp
|
||||
}
|
||||
|
||||
func (r Route) hasPermission(permissionIDs map[int64]struct{}) error {
|
||||
if len(r.permissionIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, permissionID := range r.permissionIDs {
|
||||
if _, ok := permissionIDs[permissionID]; ok {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return errors.New("no permission")
|
||||
}
|
||||
|
||||
func hasContentType(r *http.Request, mimetype string) bool {
|
||||
contentType := r.Header.Get("Content-type")
|
||||
if contentType == "" {
|
52
internal/server/server.go
Normal file
52
internal/server/server.go
Normal file
@ -0,0 +1,52 @@
|
||||
// Copyright 2022 The Goploy Authors. All rights reserved.
|
||||
// Use of this source code is governed by a GPLv3-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"github.com/vearutop/statigz"
|
||||
"github.com/zhenorzz/goploy/config"
|
||||
"github.com/zhenorzz/goploy/model"
|
||||
"github.com/zhenorzz/goploy/web"
|
||||
"io/fs"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
type Goploy struct {
|
||||
UserInfo model.User
|
||||
Namespace struct {
|
||||
ID int64
|
||||
PermissionIDs map[int64]struct{}
|
||||
}
|
||||
Request *http.Request
|
||||
ResponseWriter http.ResponseWriter
|
||||
URLQuery url.Values
|
||||
Body []byte
|
||||
}
|
||||
|
||||
type Response interface {
|
||||
Write(http.ResponseWriter, *http.Request) error
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
http.Server
|
||||
*Router
|
||||
}
|
||||
|
||||
func (srv *Server) Spin() {
|
||||
if config.Toml.Env == "production" {
|
||||
subFS, err := fs.Sub(web.Dist, "dist")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
http.Handle("/assets/", statigz.FileServer(subFS.(fs.ReadDirFS)))
|
||||
http.Handle("/favicon.ico", statigz.FileServer(subFS.(fs.ReadDirFS)))
|
||||
}
|
||||
http.Handle("/", srv.Router)
|
||||
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
log.Fatal("ListenAndServe: ", err.Error())
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user