mirror of
https://gitee.com/goploy/goploy.git
synced 2024-11-30 03:07:59 +08:00
A add log
This commit is contained in:
parent
6e008443fb
commit
8d052f6b2e
@ -11,7 +11,7 @@ type Agent Controller
|
||||
|
||||
func (a Agent) Routes() []core.Route {
|
||||
return []core.Route{
|
||||
core.NewRoute("/agent/report", http.MethodPost, a.Report).White(),
|
||||
core.NewWhiteRoute("/agent/report", http.MethodPost, a.Report),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,9 +12,9 @@ type Cron Controller
|
||||
|
||||
func (c Cron) Routes() []core.Route {
|
||||
return []core.Route{
|
||||
core.NewRoute("/cron/report", http.MethodPost, c.Report).White(),
|
||||
core.NewRoute("/cron/getList", http.MethodPost, c.GetList).White(),
|
||||
core.NewRoute("/cron/getLogs", http.MethodPost, c.GetLogs).White(),
|
||||
core.NewWhiteRoute("/cron/report", http.MethodPost, c.Report),
|
||||
core.NewWhiteRoute("/cron/getList", http.MethodPost, c.GetList),
|
||||
core.NewWhiteRoute("/cron/getLogs", http.MethodPost, c.GetLogs),
|
||||
core.NewRoute("/cron/add", http.MethodPost, c.Add).Roles(core.RoleAdmin, core.RoleManager),
|
||||
core.NewRoute("/cron/edit", http.MethodPut, c.Edit).Roles(core.RoleAdmin, core.RoleManager),
|
||||
core.NewRoute("/cron/remove", http.MethodDelete, c.Remove).Roles(core.RoleAdmin, core.RoleManager),
|
||||
|
@ -42,8 +42,8 @@ func (d Deploy) Routes() []core.Route {
|
||||
core.NewRoute("/deploy/publish", http.MethodPost, d.Publish).Middleware(middleware.HasPublishAuth),
|
||||
core.NewRoute("/deploy/rebuild", http.MethodPost, d.Rebuild).Middleware(middleware.HasPublishAuth),
|
||||
core.NewRoute("/deploy/greyPublish", http.MethodPost, d.GreyPublish).Middleware(middleware.HasPublishAuth).Roles(core.RoleAdmin, core.RoleManager, core.RoleGroupManager),
|
||||
core.NewRoute("/deploy/webhook", http.MethodPost, d.Webhook).Middleware(middleware.FilterEvent).White(),
|
||||
core.NewRoute("/deploy/callback", http.MethodGet, d.Callback).White(),
|
||||
core.NewWhiteRoute("/deploy/webhook", http.MethodPost, d.Webhook).Middleware(middleware.FilterEvent),
|
||||
core.NewWhiteRoute("/deploy/callback", http.MethodGet, d.Callback),
|
||||
core.NewRoute("/deploy/fileCompare", http.MethodPost, d.FileCompare).Roles(core.RoleAdmin, core.RoleManager, core.RoleGroupManager),
|
||||
core.NewRoute("/deploy/fileDiff", http.MethodPost, d.FileDiff).Roles(core.RoleAdmin, core.RoleManager, core.RoleGroupManager),
|
||||
core.NewRoute("/deploy/manageProcess", http.MethodPost, d.ManageProcess).Roles(core.RoleAdmin, core.RoleManager),
|
||||
|
252
controller/LogController.go
Normal file
252
controller/LogController.go
Normal file
@ -0,0 +1,252 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"github.com/zhenorzz/goploy/core"
|
||||
"github.com/zhenorzz/goploy/model"
|
||||
"github.com/zhenorzz/goploy/response"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Log struct
|
||||
type Log Controller
|
||||
|
||||
func (l Log) Routes() []core.Route {
|
||||
return []core.Route{
|
||||
core.NewRoute("/log/getLoginLogList", http.MethodGet, l.GetLoginLogList).Roles(core.RoleAdmin),
|
||||
core.NewRoute("/log/getLoginLogTotal", http.MethodGet, l.GetLoginLogTotal).Roles(core.RoleAdmin),
|
||||
core.NewRoute("/log/getSftpLogList", http.MethodGet, l.GetSftpLogList).Roles(core.RoleAdmin, core.RoleManager),
|
||||
core.NewRoute("/log/getSftpLogTotal", http.MethodGet, l.GetSftpLogTotal).Roles(core.RoleAdmin, core.RoleManager),
|
||||
core.NewRoute("/log/getTerminalLogList", http.MethodGet, l.GetTerminalLogList).Roles(core.RoleAdmin, core.RoleManager),
|
||||
core.NewRoute("/log/getTerminalLogTotal", http.MethodGet, l.GetTerminalLogTotal).Roles(core.RoleAdmin, core.RoleManager),
|
||||
core.NewRoute("/log/getTerminalRecord", http.MethodGet, l.GetTerminalRecord).Roles(core.RoleAdmin, core.RoleManager),
|
||||
core.NewRoute("/log/getPublishLogList", http.MethodGet, l.GetPublishLogList).Roles(core.RoleAdmin, core.RoleManager),
|
||||
core.NewRoute("/log/getPublishLogTotal", http.MethodGet, l.GetPublishLogTotal).Roles(core.RoleAdmin, core.RoleManager),
|
||||
}
|
||||
}
|
||||
|
||||
func (Log) GetLoginLogList(gp *core.Goploy) core.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 *core.Goploy) core.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) GetSftpLogList(gp *core.Goploy) core.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 *core.Goploy) core.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 *core.Goploy) core.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 *core.Goploy) core.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 *core.Goploy) core.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 terminalLog.NamespaceID != gp.Namespace.ID {
|
||||
return response.JSON{Code: response.Error, Message: "You have no access to enter this record"}
|
||||
}
|
||||
return response.File{Filename: core.GetTerminalLogPath(reqData.RecordID)}
|
||||
}
|
||||
|
||||
func (Log) GetPublishLogList(gp *core.Goploy) core.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 *core.Goploy) core.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},
|
||||
}
|
||||
}
|
@ -3,13 +3,13 @@ package controller
|
||||
import (
|
||||
"github.com/pkg/sftp"
|
||||
"github.com/zhenorzz/goploy/core"
|
||||
"github.com/zhenorzz/goploy/middleware"
|
||||
"github.com/zhenorzz/goploy/model"
|
||||
"github.com/zhenorzz/goploy/response"
|
||||
"github.com/zhenorzz/goploy/utils"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
@ -23,14 +23,13 @@ func (s Server) Routes() []core.Route {
|
||||
core.NewRoute("/server/getTotal", http.MethodGet, s.GetTotal),
|
||||
core.NewRoute("/server/getOption", http.MethodGet, s.GetOption),
|
||||
core.NewRoute("/server/getPublicKey", http.MethodGet, s.GetPublicKey),
|
||||
core.NewRoute("/server/getTerminalRecord", http.MethodGet, s.GetTerminalRecord),
|
||||
core.NewRoute("/server/check", http.MethodPost, s.Check).Roles(core.RoleAdmin, core.RoleManager),
|
||||
core.NewRoute("/server/add", http.MethodPost, s.Add).Roles(core.RoleAdmin, core.RoleManager),
|
||||
core.NewRoute("/server/edit", http.MethodPut, s.Edit).Roles(core.RoleAdmin, core.RoleManager),
|
||||
core.NewRoute("/server/toggle", http.MethodPut, s.Toggle).Roles(core.RoleAdmin, core.RoleManager),
|
||||
core.NewRoute("/server/previewFile", http.MethodGet, s.PreviewFile).Roles(core.RoleAdmin, core.RoleManager),
|
||||
core.NewRoute("/server/downloadFile", http.MethodGet, s.DownloadFile).Roles(core.RoleAdmin, core.RoleManager),
|
||||
core.NewRoute("/server/uploadFile", http.MethodPost, s.UploadFile).Roles(core.RoleAdmin, core.RoleManager),
|
||||
core.NewRoute("/server/previewFile", http.MethodGet, s.PreviewFile).Roles(core.RoleAdmin, core.RoleManager).LogFunc(middleware.AddPreviewLog),
|
||||
core.NewRoute("/server/downloadFile", http.MethodGet, s.DownloadFile).Roles(core.RoleAdmin, core.RoleManager).LogFunc(middleware.AddDownloadLog),
|
||||
core.NewRoute("/server/uploadFile", http.MethodPost, s.UploadFile).Roles(core.RoleAdmin, core.RoleManager).LogFunc(middleware.AddUploadLog),
|
||||
core.NewRoute("/server/report", http.MethodGet, s.Report).Roles(core.RoleAdmin, core.RoleManager),
|
||||
core.NewRoute("/server/getAllMonitor", http.MethodGet, s.GetAllMonitor),
|
||||
core.NewRoute("/server/addMonitor", http.MethodPost, s.AddMonitor).Roles(core.RoleAdmin, core.RoleManager),
|
||||
@ -79,10 +78,6 @@ func (Server) GetOption(gp *core.Goploy) core.Response {
|
||||
}
|
||||
}
|
||||
|
||||
func (Server) GetTerminalRecord(*core.Goploy) core.Response {
|
||||
return response.File{Filename: path.Join(core.GetLogPath(), "demo.cast")}
|
||||
}
|
||||
|
||||
func (Server) GetPublicKey(gp *core.Goploy) core.Response {
|
||||
publicKeyPath := gp.URLQuery.Get("path")
|
||||
|
||||
@ -289,20 +284,20 @@ func (Server) UploadFile(gp *core.Goploy) core.Response {
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeQuery(gp.URLQuery, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
return response.JSON{Code: response.IllegalParam, Message: err.Error()}
|
||||
}
|
||||
|
||||
file, fileHandler, err := gp.Request.FormFile("file")
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.IllegalParam, Message: err.Error()}
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
server, err := (model.Server{ID: reqData.ID}).GetData()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
file, fileHandler, err := gp.Request.FormFile("file")
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
client, err := server.Convert2SSHConfig().Dial()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/go-ldap/ldap/v3"
|
||||
"github.com/zhenorzz/goploy/middleware"
|
||||
"github.com/zhenorzz/goploy/response"
|
||||
"net/http"
|
||||
"time"
|
||||
@ -17,7 +18,7 @@ type User Controller
|
||||
|
||||
func (u User) Routes() []core.Route {
|
||||
return []core.Route{
|
||||
core.NewRoute("/user/login", http.MethodPost, u.Login).White(),
|
||||
core.NewWhiteRoute("/user/login", http.MethodPost, u.Login).LogFunc(middleware.AddLoginLog),
|
||||
core.NewRoute("/user/info", http.MethodGet, u.Info),
|
||||
core.NewRoute("/user/getList", http.MethodGet, u.GetList),
|
||||
core.NewRoute("/user/getTotal", http.MethodGet, u.GetTotal),
|
||||
@ -36,7 +37,7 @@ func (User) Login(gp *core.Goploy) core.Response {
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
return response.JSON{Code: response.IllegalParam, Message: err.Error()}
|
||||
}
|
||||
|
||||
userData, err := model.User{Account: reqData.Account}.GetDataByAccount()
|
||||
@ -77,7 +78,7 @@ func (User) Login(gp *core.Goploy) core.Response {
|
||||
return response.JSON{Code: response.Deny, Message: err.Error()}
|
||||
}
|
||||
if len(sr.Entries) != 1 {
|
||||
return response.JSON{Code: response.Deny, Message: err.Error()}
|
||||
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()}
|
||||
@ -91,9 +92,10 @@ func (User) Login(gp *core.Goploy) core.Response {
|
||||
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"}
|
||||
}
|
||||
|
||||
|
@ -73,3 +73,7 @@ func GetProjectFilePath(projectID int64) string {
|
||||
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")
|
||||
}
|
||||
|
@ -38,18 +38,19 @@ type Response interface {
|
||||
}
|
||||
|
||||
type Route struct {
|
||||
pattern string //
|
||||
pattern string
|
||||
method string // Method specifies the HTTP method (GET, POST, PUT, etc.).
|
||||
roles map[string]struct{} // permission role
|
||||
callback func(gp *Goploy) Response // Controller function
|
||||
middlewares []func(gp *Goploy) error // Middlewares run before all callback
|
||||
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 this Route
|
||||
middlewares []func(gp *Goploy) error // Middlewares run before all Route
|
||||
}
|
||||
|
||||
func NewRouter() Router {
|
||||
@ -67,6 +68,16 @@ func NewRoute(pattern, method string, callback func(gp *Goploy) Response) Route
|
||||
}
|
||||
}
|
||||
|
||||
func NewWhiteRoute(pattern, method string, callback func(gp *Goploy) Response) Route {
|
||||
return Route{
|
||||
pattern: pattern,
|
||||
method: method,
|
||||
white: true,
|
||||
roles: map[string]struct{}{},
|
||||
callback: callback,
|
||||
}
|
||||
}
|
||||
|
||||
// Start a router
|
||||
func (rt Router) Start() {
|
||||
if config.Toml.Env == "production" {
|
||||
@ -94,12 +105,6 @@ func (rt Router) Add(ra RouteApi) Router {
|
||||
return rt
|
||||
}
|
||||
|
||||
// White no need to check login
|
||||
func (r Route) White() Route {
|
||||
r.white = true
|
||||
return r
|
||||
}
|
||||
|
||||
// Roles Add much permission to the Route
|
||||
func (r Route) Roles(roles ...string) Route {
|
||||
for _, role := range roles {
|
||||
@ -114,6 +119,12 @@ func (r Route) Middleware(middleware func(gp *Goploy) error) Route {
|
||||
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) {
|
||||
// If in production env, serve file in go server,
|
||||
// else serve file in npm
|
||||
@ -236,7 +247,13 @@ func (rt Router) doRequest(w http.ResponseWriter, r *http.Request) (*Goploy, Res
|
||||
}
|
||||
}
|
||||
|
||||
return gp, route.callback(gp)
|
||||
resp := route.callback(gp)
|
||||
|
||||
if route.logFunc != nil {
|
||||
route.logFunc(gp, resp)
|
||||
}
|
||||
|
||||
return gp, resp
|
||||
}
|
||||
|
||||
func (r Route) hasRole(namespaceRole string) error {
|
||||
|
@ -1,7 +1,7 @@
|
||||
# Import sql manually https://github.com/zhenorzz/goploy/blob/master/model/sql/goploy.sql
|
||||
FROM alpine
|
||||
LABEL maintainer="zhenorzz@gmail.com"
|
||||
ARG GOPLOY_VER=v1.4.5
|
||||
ARG GOPLOY_VER=v1.4.6
|
||||
ENV GOPLOY_VER=${GOPLOY_VER}
|
||||
|
||||
ENV MYSQL_PORT=3306
|
||||
|
@ -1,5 +1,16 @@
|
||||
# change log
|
||||
|
||||
## 1.4.6
|
||||
|
||||
*2022-02-24*
|
||||
|
||||
### New features
|
||||
- web log
|
||||
- sftp file preview
|
||||
|
||||
### Bug fixed
|
||||
- web cookies undefined
|
||||
|
||||
## 1.4.5
|
||||
|
||||
*2022-01-26*
|
||||
@ -31,6 +42,7 @@
|
||||
### Optimization
|
||||
- code
|
||||
- select db
|
||||
|
||||
### Bug fixes
|
||||
- fix exit deploy script
|
||||
- fix tag refresh
|
||||
|
2
main.go
2
main.go
@ -38,7 +38,7 @@ var (
|
||||
s string
|
||||
)
|
||||
|
||||
const appVersion = "1.4.5"
|
||||
const appVersion = "1.4.6"
|
||||
|
||||
func init() {
|
||||
flag.StringVar(&core.AssetDir, "asset-dir", "", "default: ./")
|
||||
|
34
middleware/LoginMiddleware.go
Normal file
34
middleware/LoginMiddleware.go
Normal file
@ -0,0 +1,34 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/zhenorzz/goploy/core"
|
||||
"github.com/zhenorzz/goploy/model"
|
||||
"github.com/zhenorzz/goploy/response"
|
||||
"time"
|
||||
)
|
||||
|
||||
func AddLoginLog(gp *core.Goploy, resp core.Response) {
|
||||
respJson := resp.(response.JSON)
|
||||
account := ""
|
||||
if respJson.Code != response.IllegalParam {
|
||||
type ReqData struct {
|
||||
Account string `json:"account"`
|
||||
}
|
||||
var reqData ReqData
|
||||
_ = json.Unmarshal(gp.Body, &reqData)
|
||||
account = reqData.Account
|
||||
}
|
||||
|
||||
err := model.LoginLog{
|
||||
Account: account,
|
||||
RemoteAddr: gp.Request.RemoteAddr,
|
||||
UserAgent: gp.Request.UserAgent(),
|
||||
Referer: gp.Request.Referer(),
|
||||
Reason: respJson.Message,
|
||||
LoginTime: time.Now().Format("20060102150405"),
|
||||
}.AddRow()
|
||||
if err != nil {
|
||||
core.Log(core.ERROR, err.Error())
|
||||
}
|
||||
}
|
94
middleware/SftpMiddleware.go
Normal file
94
middleware/SftpMiddleware.go
Normal file
@ -0,0 +1,94 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/zhenorzz/goploy/core"
|
||||
"github.com/zhenorzz/goploy/model"
|
||||
"github.com/zhenorzz/goploy/response"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func AddUploadLog(gp *core.Goploy, resp core.Response) {
|
||||
var serverID int64 = 0
|
||||
var path = ""
|
||||
respJson := resp.(response.JSON)
|
||||
if respJson.Code != response.IllegalParam {
|
||||
serverID, _ = strconv.ParseInt(gp.URLQuery.Get("id"), 10, 64)
|
||||
file, fileHandler, _ := gp.Request.FormFile("file")
|
||||
path = gp.URLQuery.Get("filePath") + "/" + fileHandler.Filename
|
||||
_ = file.Close()
|
||||
}
|
||||
|
||||
err := model.SftpLog{
|
||||
NamespaceID: gp.Namespace.ID,
|
||||
UserID: gp.UserInfo.ID,
|
||||
ServerID: serverID,
|
||||
RemoteAddr: gp.Request.RemoteAddr,
|
||||
UserAgent: gp.Request.UserAgent(),
|
||||
Type: model.SftpLogTypeUpload,
|
||||
Path: path,
|
||||
Reason: respJson.Message,
|
||||
}.AddRow()
|
||||
if err != nil {
|
||||
core.Log(core.ERROR, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func AddDownloadLog(gp *core.Goploy, resp core.Response) {
|
||||
msg := ""
|
||||
path := ""
|
||||
var serverID int64 = 0
|
||||
switch resp.(type) {
|
||||
case response.JSON:
|
||||
respJson := resp.(response.JSON)
|
||||
if respJson.Code != response.IllegalParam {
|
||||
msg = respJson.Message
|
||||
serverID, _ = strconv.ParseInt(gp.URLQuery.Get("id"), 10, 64)
|
||||
}
|
||||
case response.SftpFile:
|
||||
path = resp.(response.SftpFile).Filename
|
||||
}
|
||||
|
||||
err := model.SftpLog{
|
||||
NamespaceID: gp.Namespace.ID,
|
||||
UserID: gp.UserInfo.ID,
|
||||
ServerID: serverID,
|
||||
RemoteAddr: gp.Request.RemoteAddr,
|
||||
UserAgent: gp.Request.UserAgent(),
|
||||
Type: model.SftpLogTypeDownload,
|
||||
Path: path,
|
||||
Reason: msg,
|
||||
}.AddRow()
|
||||
if err != nil {
|
||||
core.Log(core.ERROR, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func AddPreviewLog(gp *core.Goploy, resp core.Response) {
|
||||
msg := ""
|
||||
path := ""
|
||||
var serverID int64 = 0
|
||||
switch resp.(type) {
|
||||
case response.JSON:
|
||||
respJson := resp.(response.JSON)
|
||||
if respJson.Code != response.IllegalParam {
|
||||
msg = respJson.Message
|
||||
serverID, _ = strconv.ParseInt(gp.URLQuery.Get("id"), 10, 64)
|
||||
}
|
||||
case response.SftpFile:
|
||||
path = resp.(response.SftpFile).Filename
|
||||
}
|
||||
|
||||
err := model.SftpLog{
|
||||
NamespaceID: gp.Namespace.ID,
|
||||
UserID: gp.UserInfo.ID,
|
||||
ServerID: serverID,
|
||||
RemoteAddr: gp.Request.RemoteAddr,
|
||||
UserAgent: gp.Request.UserAgent(),
|
||||
Type: model.SftpLogTypePreview,
|
||||
Path: path,
|
||||
Reason: msg,
|
||||
}.AddRow()
|
||||
if err != nil {
|
||||
core.Log(core.ERROR, err.Error())
|
||||
}
|
||||
}
|
90
model/LoginLogModel.go
Normal file
90
model/LoginLogModel.go
Normal file
@ -0,0 +1,90 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
sq "github.com/Masterminds/squirrel"
|
||||
)
|
||||
|
||||
const loginLogTable = "`login_log`"
|
||||
|
||||
type LoginLog struct {
|
||||
ID int64 `json:"id"`
|
||||
Account string `json:"account"`
|
||||
RemoteAddr string `json:"remoteAddr"`
|
||||
UserAgent string `json:"userAgent"`
|
||||
Referer string `json:"referer"`
|
||||
Reason string `json:"reason"`
|
||||
LoginTime string `json:"loginTime"`
|
||||
}
|
||||
|
||||
type LoginLogs []LoginLog
|
||||
|
||||
func (ll LoginLog) GetList(page, limit uint64) (LoginLogs, error) {
|
||||
builder := sq.
|
||||
Select(
|
||||
"id",
|
||||
"account",
|
||||
"remote_addr",
|
||||
"user_agent",
|
||||
"referer",
|
||||
"reason",
|
||||
"login_time",
|
||||
).
|
||||
From(loginLogTable)
|
||||
if len(ll.Account) > 0 {
|
||||
builder = builder.Where(sq.Eq{"account": ll.Account})
|
||||
}
|
||||
|
||||
rows, err := builder.
|
||||
Limit(limit).
|
||||
Offset((page - 1) * limit).
|
||||
OrderBy("id DESC").
|
||||
RunWith(DB).
|
||||
Query()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
loginLogs := LoginLogs{}
|
||||
for rows.Next() {
|
||||
var loginLog LoginLog
|
||||
if err := rows.Scan(
|
||||
&loginLog.ID,
|
||||
&loginLog.Account,
|
||||
&loginLog.RemoteAddr,
|
||||
&loginLog.UserAgent,
|
||||
&loginLog.Referer,
|
||||
&loginLog.Reason,
|
||||
&loginLog.LoginTime,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
loginLogs = append(loginLogs, loginLog)
|
||||
}
|
||||
|
||||
return loginLogs, nil
|
||||
}
|
||||
|
||||
func (ll LoginLog) GetTotal() (int64, error) {
|
||||
var total int64
|
||||
builder := sq.Select("COUNT(*) AS count").From(loginLogTable)
|
||||
|
||||
if len(ll.Account) > 0 {
|
||||
builder = builder.Where(sq.Eq{"account": ll.Account})
|
||||
}
|
||||
err := builder.RunWith(DB).
|
||||
QueryRow().
|
||||
Scan(&total)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return total, nil
|
||||
}
|
||||
|
||||
func (ll LoginLog) AddRow() error {
|
||||
_, err := sq.
|
||||
Insert(loginLogTable).
|
||||
Columns("account", "remote_addr", "user_agent", "referer", "reason", "login_time").
|
||||
Values(ll.Account, ll.RemoteAddr, ll.UserAgent, ll.Referer, ll.Reason, ll.LoginTime).
|
||||
RunWith(DB).
|
||||
Exec()
|
||||
return err
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
sq "github.com/Masterminds/squirrel"
|
||||
)
|
||||
|
||||
@ -10,6 +11,7 @@ const publishTraceTable = "`publish_trace`"
|
||||
type PublishTrace struct {
|
||||
ID int64 `json:"id"`
|
||||
Token string `json:"token"`
|
||||
NamespaceID int64 `json:"namespaceId"`
|
||||
ProjectID int64 `json:"projectId"`
|
||||
ProjectName string `json:"projectName"`
|
||||
Detail string `json:"detail"`
|
||||
@ -41,24 +43,86 @@ const (
|
||||
AfterDeploy = 6
|
||||
)
|
||||
|
||||
// AddRow return LastInsertId
|
||||
func (pt PublishTrace) AddRow() (int64, error) {
|
||||
result, err := sq.
|
||||
Insert(publishTraceTable).
|
||||
Columns("token", "project_id", "project_name", "detail", "state", "publisher_id", "publisher_name", "type", "ext").
|
||||
Values(pt.Token, pt.ProjectID, pt.ProjectName, pt.Detail, pt.State, pt.PublisherID, pt.PublisherName, pt.Type, pt.Ext).
|
||||
RunWith(DB).
|
||||
Exec()
|
||||
func (pt PublishTrace) GetList(page, limit uint64) (PublishTraces, error) {
|
||||
builder := sq.
|
||||
Select(
|
||||
fmt.Sprintf("%s.token", publishTraceTable),
|
||||
fmt.Sprintf("min(%s.project_id)", publishTraceTable),
|
||||
fmt.Sprintf("min(%s.project_name)", publishTraceTable),
|
||||
fmt.Sprintf("min(%s.publisher_id)", publishTraceTable),
|
||||
fmt.Sprintf("min(%s.publisher_name)", publishTraceTable),
|
||||
fmt.Sprintf("min(%s.state)", publishTraceTable),
|
||||
fmt.Sprintf("IFNULL(GROUP_CONCAT(IF(%s.state = 0, %[1]s.detail, NULL)), '') as detail", publishTraceTable),
|
||||
fmt.Sprintf("min(%s.insert_time) as insert_time", publishTraceTable),
|
||||
).
|
||||
From(publishTraceTable).
|
||||
GroupBy("token")
|
||||
|
||||
if pt.NamespaceID > 0 {
|
||||
builder = builder.
|
||||
Join(fmt.Sprintf("%s ON %[1]s.id = %s.project_id", projectTable, publishTraceTable)).
|
||||
Where(sq.Eq{projectTable + ".namespace_id": pt.NamespaceID})
|
||||
}
|
||||
if pt.PublisherName != "" {
|
||||
builder = builder.Where(sq.Eq{publishTraceTable + ".publisher_name": pt.PublisherName})
|
||||
}
|
||||
if pt.ProjectName != "" {
|
||||
builder = builder.Where(sq.Eq{publishTraceTable + ".project_name": pt.ProjectName})
|
||||
}
|
||||
|
||||
rows, err := builder.RunWith(DB).
|
||||
OrderBy("insert_time DESC").
|
||||
Limit(limit).
|
||||
Offset((page - 1) * limit).
|
||||
Query()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
publishTraces := PublishTraces{}
|
||||
for rows.Next() {
|
||||
var publishTrace PublishTrace
|
||||
|
||||
if err := rows.Scan(
|
||||
&publishTrace.Token,
|
||||
&publishTrace.ProjectID,
|
||||
&publishTrace.ProjectName,
|
||||
&publishTrace.PublisherID,
|
||||
&publishTrace.PublisherName,
|
||||
&publishTrace.State,
|
||||
&publishTrace.Detail,
|
||||
&publishTrace.InsertTime); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
publishTraces = append(publishTraces, publishTrace)
|
||||
}
|
||||
|
||||
return publishTraces, nil
|
||||
}
|
||||
|
||||
func (pt PublishTrace) GetTotal() (int64, error) {
|
||||
var total int64
|
||||
builder := sq.Select("COUNT(distinct token) AS count").
|
||||
From(publishTraceTable)
|
||||
if pt.NamespaceID > 0 {
|
||||
builder = builder.
|
||||
Join(fmt.Sprintf("%s ON %[1]s.id = %s.project_id", projectTable, publishTraceTable)).
|
||||
Where(sq.Eq{projectTable + ".namespace_id": pt.NamespaceID})
|
||||
}
|
||||
if pt.PublisherName != "" {
|
||||
builder = builder.Where(sq.Eq{publishTraceTable + ".publisher_name": pt.PublisherName})
|
||||
}
|
||||
if pt.ProjectName != "" {
|
||||
builder = builder.Where(sq.Eq{publishTraceTable + ".project_name": pt.ProjectName})
|
||||
}
|
||||
err := builder.RunWith(DB).
|
||||
QueryRow().
|
||||
Scan(&total)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
id, err := result.LastInsertId()
|
||||
return id, err
|
||||
return total, nil
|
||||
}
|
||||
|
||||
// GetListByToken -
|
||||
func (pt PublishTrace) GetListByToken() (PublishTraces, error) {
|
||||
rows, err := sq.
|
||||
Select(
|
||||
@ -105,7 +169,6 @@ func (pt PublishTrace) GetListByToken() (PublishTraces, error) {
|
||||
return publishTraces, nil
|
||||
}
|
||||
|
||||
// GetPreview -
|
||||
func (pt PublishTrace) GetPreview(
|
||||
branch string,
|
||||
commit string,
|
||||
@ -229,6 +292,23 @@ func (pt PublishTrace) GetDetail() (string, error) {
|
||||
return detail, nil
|
||||
}
|
||||
|
||||
// AddRow return LastInsertId
|
||||
func (pt PublishTrace) AddRow() (int64, error) {
|
||||
result, err := sq.
|
||||
Insert(publishTraceTable).
|
||||
Columns("token", "project_id", "project_name", "detail", "state", "publisher_id", "publisher_name", "type", "ext").
|
||||
Values(pt.Token, pt.ProjectID, pt.ProjectName, pt.Detail, pt.State, pt.PublisherID, pt.PublisherName, pt.Type, pt.Ext).
|
||||
RunWith(DB).
|
||||
Exec()
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
id, err := result.LastInsertId()
|
||||
return id, err
|
||||
}
|
||||
|
||||
// EditUpdateTimeByToken -
|
||||
func (pt PublishTrace) EditUpdateTimeByToken() error {
|
||||
_, err := sq.
|
||||
|
131
model/SftpLogModel.go
Normal file
131
model/SftpLogModel.go
Normal file
@ -0,0 +1,131 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
sq "github.com/Masterminds/squirrel"
|
||||
)
|
||||
|
||||
const sftpLogTable = "`sftp_log`"
|
||||
|
||||
type SftpLog struct {
|
||||
ID int64 `json:"id"`
|
||||
NamespaceID int64 `json:"namespaceID"`
|
||||
UserID int64 `json:"userID"`
|
||||
Username string `json:"username"`
|
||||
ServerID int64 `json:"serverID"`
|
||||
ServerName string `json:"serverName"`
|
||||
RemoteAddr string `json:"remoteAddr"`
|
||||
UserAgent string `json:"userAgent"`
|
||||
Type string `json:"type"`
|
||||
Path string `json:"path"`
|
||||
Reason string `json:"reason"`
|
||||
InsertTime string `json:"insertTime"`
|
||||
UpdateTime string `json:"updateTime"`
|
||||
}
|
||||
|
||||
const (
|
||||
SftpLogTypeDownload = "DOWNLOAD"
|
||||
SftpLogTypeUpload = "UPLOAD"
|
||||
SftpLogTypeRead = "READ"
|
||||
SftpLogTypePreview = "PREVIEW"
|
||||
)
|
||||
|
||||
type SftpLogs []SftpLog
|
||||
|
||||
func (sl SftpLog) GetList(page, limit uint64) (SftpLogs, error) {
|
||||
builder := sq.
|
||||
Select(
|
||||
sftpLogTable+".id",
|
||||
sftpLogTable+".namespace_id",
|
||||
sftpLogTable+".user_id",
|
||||
userTable+".name",
|
||||
sftpLogTable+".server_id",
|
||||
fmt.Sprintf("IFNULL(%s.name, '')", serverTable),
|
||||
sftpLogTable+".remote_addr",
|
||||
sftpLogTable+".user_agent",
|
||||
sftpLogTable+".type",
|
||||
sftpLogTable+".path",
|
||||
sftpLogTable+".reason",
|
||||
).
|
||||
From(sftpLogTable).
|
||||
LeftJoin(fmt.Sprintf("%s ON %[1]s.id = %s.user_id", userTable, sftpLogTable)).
|
||||
LeftJoin(fmt.Sprintf("%s ON %[1]s.id = %s.server_id", serverTable, sftpLogTable))
|
||||
|
||||
if sl.NamespaceID > 0 {
|
||||
builder = builder.Where(sq.Eq{sftpLogTable + ".namespace_id": sl.NamespaceID})
|
||||
}
|
||||
if sl.Username != "" {
|
||||
builder = builder.Where(sq.Eq{userTable + ".name": sl.Username})
|
||||
}
|
||||
if sl.ServerName != "" {
|
||||
builder = builder.Where(sq.Eq{serverTable + ".name": sl.ServerName})
|
||||
}
|
||||
|
||||
rows, err := builder.
|
||||
Limit(limit).
|
||||
Offset((page - 1) * limit).
|
||||
OrderBy("id DESC").
|
||||
RunWith(DB).
|
||||
Query()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sftpLogs := SftpLogs{}
|
||||
for rows.Next() {
|
||||
var sftpLog SftpLog
|
||||
if err := rows.Scan(
|
||||
&sftpLog.ID,
|
||||
&sftpLog.NamespaceID,
|
||||
&sftpLog.UserID,
|
||||
&sftpLog.Username,
|
||||
&sftpLog.ServerID,
|
||||
&sftpLog.ServerName,
|
||||
&sftpLog.RemoteAddr,
|
||||
&sftpLog.UserAgent,
|
||||
&sftpLog.Type,
|
||||
&sftpLog.Path,
|
||||
&sftpLog.Reason,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sftpLogs = append(sftpLogs, sftpLog)
|
||||
}
|
||||
|
||||
return sftpLogs, nil
|
||||
}
|
||||
|
||||
func (sl SftpLog) GetTotal() (int64, error) {
|
||||
var total int64
|
||||
builder := sq.Select("COUNT(*) AS count").From(sftpLogTable)
|
||||
|
||||
if sl.NamespaceID > 0 {
|
||||
builder = builder.Where(sq.Eq{sftpLogTable + ".namespace_id": sl.NamespaceID})
|
||||
}
|
||||
if sl.Username != "" {
|
||||
builder = builder.
|
||||
LeftJoin(fmt.Sprintf("%s ON %[1]s.id = %s.user_id", userTable, sftpLogTable)).
|
||||
Where(sq.Eq{userTable + ".name": sl.Username})
|
||||
}
|
||||
if sl.ServerName != "" {
|
||||
builder = builder.
|
||||
LeftJoin(fmt.Sprintf("%s ON %[1]s.id = %s.server_id", serverTable, sftpLogTable)).
|
||||
Where(sq.Eq{serverTable + ".name": sl.ServerName})
|
||||
}
|
||||
err := builder.RunWith(DB).
|
||||
QueryRow().
|
||||
Scan(&total)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return total, nil
|
||||
}
|
||||
|
||||
func (sl SftpLog) AddRow() error {
|
||||
_, err := sq.
|
||||
Insert(sftpLogTable).
|
||||
Columns("namespace_id", "user_id", "server_id", "remote_addr", "user_agent", "type", "path", "reason").
|
||||
Values(sl.NamespaceID, sl.UserID, sl.ServerID, sl.RemoteAddr, sl.UserAgent, sl.Type, sl.Path, sl.Reason).
|
||||
RunWith(DB).
|
||||
Exec()
|
||||
return err
|
||||
}
|
165
model/TerminalLogModel.go
Normal file
165
model/TerminalLogModel.go
Normal file
@ -0,0 +1,165 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
sq "github.com/Masterminds/squirrel"
|
||||
)
|
||||
|
||||
const terminalLogTable = "`terminal_log`"
|
||||
|
||||
type TerminalLog struct {
|
||||
ID int64 `json:"id"`
|
||||
NamespaceID int64 `json:"namespaceID"`
|
||||
UserID int64 `json:"userID"`
|
||||
Username string `json:"username"`
|
||||
ServerID int64 `json:"serverID"`
|
||||
ServerName string `json:"serverName"`
|
||||
RemoteAddr string `json:"remoteAddr"`
|
||||
UserAgent string `json:"userAgent"`
|
||||
StartTime string `json:"startTime"`
|
||||
EndTime string `json:"endTime"`
|
||||
InsertTime string `json:"insertTime"`
|
||||
UpdateTime string `json:"updateTime"`
|
||||
}
|
||||
|
||||
type TerminalLogs []TerminalLog
|
||||
|
||||
func (tl TerminalLog) GetData() (TerminalLog, error) {
|
||||
var terminalLog TerminalLog
|
||||
err := sq.
|
||||
Select(
|
||||
"id",
|
||||
"namespace_id",
|
||||
"user_id",
|
||||
"server_id",
|
||||
"remote_addr",
|
||||
"user_agent",
|
||||
"start_time",
|
||||
"end_time",
|
||||
).
|
||||
From(terminalLogTable).
|
||||
Where(sq.Eq{"id": tl.ID}).
|
||||
OrderBy("id DESC").
|
||||
RunWith(DB).
|
||||
QueryRow().
|
||||
Scan(&terminalLog.ID,
|
||||
&terminalLog.NamespaceID,
|
||||
&terminalLog.UserID,
|
||||
&terminalLog.ServerID,
|
||||
&terminalLog.RemoteAddr,
|
||||
&terminalLog.UserAgent,
|
||||
&terminalLog.StartTime,
|
||||
&terminalLog.EndTime,
|
||||
)
|
||||
if err != nil {
|
||||
return terminalLog, err
|
||||
}
|
||||
return terminalLog, nil
|
||||
}
|
||||
|
||||
func (tl TerminalLog) GetList(page, limit uint64) (TerminalLogs, error) {
|
||||
builder := sq.
|
||||
Select(
|
||||
terminalLogTable+".id",
|
||||
terminalLogTable+".namespace_id",
|
||||
terminalLogTable+".user_id",
|
||||
userTable+".name",
|
||||
terminalLogTable+".server_id",
|
||||
fmt.Sprintf("IFNULL(%s.name, '')", serverTable),
|
||||
terminalLogTable+".remote_addr",
|
||||
terminalLogTable+".user_agent",
|
||||
terminalLogTable+".start_time",
|
||||
terminalLogTable+".end_time",
|
||||
).
|
||||
From(terminalLogTable).
|
||||
LeftJoin(fmt.Sprintf("%s ON %[1]s.id = %s.user_id", userTable, terminalLogTable)).
|
||||
LeftJoin(fmt.Sprintf("%s ON %[1]s.id = %s.server_id", serverTable, terminalLogTable))
|
||||
|
||||
if tl.NamespaceID > 0 {
|
||||
builder = builder.Where(sq.Eq{terminalLogTable + ".namespace_id": tl.NamespaceID})
|
||||
}
|
||||
if tl.Username != "" {
|
||||
builder = builder.Where(sq.Eq{userTable + ".name": tl.Username})
|
||||
}
|
||||
if tl.ServerName != "" {
|
||||
builder = builder.Where(sq.Eq{serverTable + ".name": tl.ServerName})
|
||||
}
|
||||
|
||||
rows, err := builder.Limit(limit).Offset((page - 1) * limit).OrderBy("id DESC").
|
||||
RunWith(DB).Query()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
terminalLogs := TerminalLogs{}
|
||||
for rows.Next() {
|
||||
var terminalLog TerminalLog
|
||||
if err := rows.Scan(
|
||||
&terminalLog.ID,
|
||||
&terminalLog.NamespaceID,
|
||||
&terminalLog.UserID,
|
||||
&terminalLog.Username,
|
||||
&terminalLog.ServerID,
|
||||
&terminalLog.ServerName,
|
||||
&terminalLog.RemoteAddr,
|
||||
&terminalLog.UserAgent,
|
||||
&terminalLog.StartTime,
|
||||
&terminalLog.EndTime,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
terminalLogs = append(terminalLogs, terminalLog)
|
||||
}
|
||||
|
||||
return terminalLogs, nil
|
||||
}
|
||||
|
||||
func (tl TerminalLog) GetTotal() (int64, error) {
|
||||
var total int64
|
||||
builder := sq.Select("COUNT(*) AS count").From(terminalLogTable)
|
||||
|
||||
if tl.NamespaceID > 0 {
|
||||
builder = builder.Where(sq.Eq{terminalLogTable + ".namespace_id": tl.NamespaceID})
|
||||
}
|
||||
if tl.Username != "" {
|
||||
builder = builder.
|
||||
LeftJoin(fmt.Sprintf("%s ON %[1]s.id = %s.user_id", userTable, terminalLogTable)).
|
||||
Where(sq.Eq{userTable + ".name": tl.Username})
|
||||
}
|
||||
if tl.ServerName != "" {
|
||||
builder = builder.
|
||||
LeftJoin(fmt.Sprintf("%s ON %[1]s.id = %s.server_id", serverTable, terminalLogTable)).
|
||||
Where(sq.Eq{serverTable + ".name": tl.ServerName})
|
||||
}
|
||||
err := builder.RunWith(DB).QueryRow().Scan(&total)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return total, nil
|
||||
}
|
||||
|
||||
func (tl TerminalLog) AddRow() (int64, error) {
|
||||
result, err := sq.
|
||||
Insert(terminalLogTable).
|
||||
Columns("namespace_id", "user_id", "server_id", "remote_addr", "user_agent", "start_time").
|
||||
Values(tl.NamespaceID, tl.UserID, tl.ServerID, tl.RemoteAddr, tl.UserAgent, tl.StartTime).
|
||||
RunWith(DB).
|
||||
Exec()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
id, err := result.LastInsertId()
|
||||
return id, err
|
||||
}
|
||||
|
||||
func (tl TerminalLog) EditRow() error {
|
||||
_, err := sq.
|
||||
Update(terminalLogTable).
|
||||
SetMap(sq.Eq{
|
||||
"end_time": tl.EndTime,
|
||||
}).
|
||||
Where(sq.Eq{"id": tl.ID}).
|
||||
RunWith(DB).
|
||||
Exec()
|
||||
return err
|
||||
}
|
@ -12,11 +12,11 @@ import (
|
||||
|
||||
const userTable = "`user`"
|
||||
|
||||
// SuperManager super manager
|
||||
const SuperManager = 1
|
||||
|
||||
// GeneralUser general user
|
||||
const GeneralUser = 0
|
||||
// user role
|
||||
const (
|
||||
SuperManager = 1
|
||||
GeneralUser = 0
|
||||
)
|
||||
|
||||
// User -
|
||||
type User struct {
|
||||
|
40
model/sql/1.4.6.sql
Normal file
40
model/sql/1.4.6.sql
Normal file
@ -0,0 +1,40 @@
|
||||
CREATE TABLE IF NOT EXISTS `login_log` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`account` varchar(30) NOT NULL DEFAULT '',
|
||||
`remote_addr` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
|
||||
`user_agent` varchar(255) NOT NULL DEFAULT '',
|
||||
`referer` varchar(255) NOT NULL DEFAULT '',
|
||||
`reason` varchar(2555) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
|
||||
`login_time` datetime NOT NULL,
|
||||
`insert_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `sftp_log` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`namespace_id` int(10) unsigned NOT NULL DEFAULT '0',
|
||||
`user_id` int(10) unsigned NOT NULL DEFAULT '0',
|
||||
`server_id` int(10) unsigned NOT NULL DEFAULT '0',
|
||||
`remote_addr` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
|
||||
`user_agent` varchar(255) NOT NULL DEFAULT '',
|
||||
`type` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'READ|PREVIEW|DOWNLOAD|UPLOAD',
|
||||
`path` varchar(255) NOT NULL DEFAULT '',
|
||||
`reason` varchar(2555) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
|
||||
`insert_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `terminal_log` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`namespace_id` int(10) unsigned NOT NULL DEFAULT '0',
|
||||
`user_id` int(10) unsigned NOT NULL DEFAULT '0',
|
||||
`server_id` int(10) unsigned NOT NULL DEFAULT '0',
|
||||
`remote_addr` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
|
||||
`user_agent` varchar(255) NOT NULL DEFAULT '',
|
||||
`start_time` datetime NOT NULL,
|
||||
`end_time` datetime NOT NULL DEFAULT '1970-01-01 00:00:00',
|
||||
`insert_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
@ -2,17 +2,6 @@ CREATE DATABASE IF NOT EXISTS `goploy`;
|
||||
|
||||
use `goploy`;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `log` (
|
||||
`id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`type` tinyint(3) UNSIGNED NOT NULL DEFAULT 1 COMMENT 'log type',
|
||||
`ip` int(10) UNSIGNED NOT NULL DEFAULT 0,
|
||||
`desc` varchar(30) NOT NULL DEFAULT '' COMMENT 'description',
|
||||
`user_id` int(10) UNSIGNED NOT NULL DEFAULT 0 COMMENT '',
|
||||
`create_time` int(10) UNSIGNED NOT NULL DEFAULT 0 COMMENT '',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
INDEX `idx_create_time`(`create_time`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `project` (
|
||||
`id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`namespace_id` int(10) UNSIGNED NOT NULL DEFAULT 0,
|
||||
@ -284,8 +273,49 @@ CREATE TABLE IF NOT EXISTS `system_config` (
|
||||
UNIQUE KEY `udx_key` (`key`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `login_log` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`account` varchar(30) NOT NULL DEFAULT '',
|
||||
`remote_addr` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
|
||||
`user_agent` varchar(255) NOT NULL DEFAULT '',
|
||||
`referer` varchar(255) NOT NULL DEFAULT '',
|
||||
`reason` varchar(2555) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
|
||||
`login_time` datetime NOT NULL,
|
||||
`insert_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `sftp_log` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`namespace_id` int(10) unsigned NOT NULL DEFAULT '0',
|
||||
`user_id` int(10) unsigned NOT NULL DEFAULT '0',
|
||||
`server_id` int(10) unsigned NOT NULL DEFAULT '0',
|
||||
`remote_addr` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
|
||||
`user_agent` varchar(255) NOT NULL DEFAULT '',
|
||||
`type` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'READ|PREVIEW|DOWNLOAD|UPLOAD',
|
||||
`path` varchar(255) NOT NULL DEFAULT '',
|
||||
`reason` varchar(2555) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
|
||||
`insert_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `terminal_log` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`namespace_id` int(10) unsigned NOT NULL DEFAULT '0',
|
||||
`user_id` int(10) unsigned NOT NULL DEFAULT '0',
|
||||
`server_id` int(10) unsigned NOT NULL DEFAULT '0',
|
||||
`remote_addr` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
|
||||
`user_agent` varchar(255) NOT NULL DEFAULT '',
|
||||
`start_time` datetime NOT NULL,
|
||||
`end_time` datetime NOT NULL DEFAULT '1970-01-01 00:00:00',
|
||||
`insert_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
INSERT IGNORE INTO `user`(`id`, `account`, `password`, `name`, `contact`, `state`, `super_manager`) VALUES (1, 'admin', '$2a$10$89ZJ2xeJj35GOw11Qiucr.phaEZP4.kBX6aKTs7oWFp1xcGBBgijm', '超管', '', 1, 1);
|
||||
INSERT IGNORE INTO `namespace`(`id`, `name`) VALUES (1, 'goploy');
|
||||
INSERT IGNORE INTO `namespace_user`(`id`, `namespace_id`, `user_id`, `role`) VALUES (1, 1, 1, 'admin');
|
||||
INSERT IGNORE INTO `system_config` (`id`, `key`, `value`) VALUES (1, 'version', '1.4.5');
|
||||
INSERT IGNORE INTO `system_config` (`id`, `key`, `value`) VALUES (1, 'version', '1.4.6');
|
||||
|
||||
|
@ -19,6 +19,7 @@ const (
|
||||
AccountDisabled = 10000
|
||||
IllegalRequest = 10001
|
||||
NamespaceInvalid = 10002
|
||||
IllegalParam = 10003
|
||||
LoginExpired = 10086
|
||||
)
|
||||
|
||||
|
@ -45,8 +45,5 @@ func (sf SftpFile) Write(w http.ResponseWriter) error {
|
||||
w.Header().Set("Content-Length", strconv.FormatInt(fileStat.Size(), 10))
|
||||
_, err = io.Copy(w, srcFile)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ func Init() {
|
||||
rt.Add(controller.Monitor{})
|
||||
rt.Add(controller.Deploy{})
|
||||
rt.Add(controller.Server{})
|
||||
rt.Add(controller.Log{})
|
||||
rt.Add(controller.Cron{})
|
||||
rt.Add(controller.Agent{})
|
||||
|
||||
|
@ -3,6 +3,7 @@ package utils
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -75,8 +76,14 @@ func (recorder *Recorder) WriteData(data string) (err error) {
|
||||
|
||||
func NewRecorder(recordingPath, term string, h int, w int) (recorder *Recorder, err error) {
|
||||
recorder = &Recorder{}
|
||||
var file *os.File
|
||||
file, err = os.Create(recordingPath)
|
||||
|
||||
if _, err := os.Stat(path.Dir(recordingPath)); err != nil {
|
||||
if err := os.MkdirAll(path.Dir(recordingPath), os.ModePerm); err != nil {
|
||||
return recorder, err
|
||||
}
|
||||
}
|
||||
|
||||
file, err := os.Create(recordingPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
206
web/src/api/log.ts
Normal file
206
web/src/api/log.ts
Normal file
@ -0,0 +1,206 @@
|
||||
import { Request, Pagination, Total } from './types'
|
||||
|
||||
export class LoginLogData {
|
||||
public datagram!: {
|
||||
id: number
|
||||
account: string
|
||||
remoteAddr: string
|
||||
userAgent: string
|
||||
referer: string
|
||||
reason: string
|
||||
loginTime: string
|
||||
}
|
||||
}
|
||||
|
||||
export class LoginLogList extends Request {
|
||||
readonly url = '/log/getLoginLogList'
|
||||
readonly method = 'get'
|
||||
|
||||
public pagination: Pagination
|
||||
|
||||
public param: {
|
||||
account: string
|
||||
}
|
||||
|
||||
public datagram!: {
|
||||
list: LoginLogData['datagram'][]
|
||||
}
|
||||
constructor(param: LoginLogList['param'], pagination: Pagination) {
|
||||
super()
|
||||
this.pagination = pagination
|
||||
this.param = { ...param, ...pagination }
|
||||
}
|
||||
}
|
||||
|
||||
export class LoginLogTotal extends Request {
|
||||
readonly url = '/log/getLoginLogTotal'
|
||||
readonly method = 'get'
|
||||
|
||||
public param: {
|
||||
account: string
|
||||
}
|
||||
|
||||
public datagram!: Total
|
||||
|
||||
constructor(param: LoginLogTotal['param']) {
|
||||
super()
|
||||
this.param = param
|
||||
}
|
||||
}
|
||||
|
||||
export class SftpLogData {
|
||||
public datagram!: {
|
||||
id: number
|
||||
namespaceId: number
|
||||
userId: number
|
||||
username: string
|
||||
serverId: number
|
||||
serverName: string
|
||||
remoteAddr: string
|
||||
userAgent: string
|
||||
type: string
|
||||
path: string
|
||||
reason: string
|
||||
}
|
||||
}
|
||||
|
||||
export class SftpLogList extends Request {
|
||||
readonly url = '/log/getSftpLogList'
|
||||
readonly method = 'get'
|
||||
|
||||
public pagination: Pagination
|
||||
|
||||
public param: {
|
||||
username: string
|
||||
serverName: string
|
||||
}
|
||||
|
||||
public datagram!: {
|
||||
list: SftpLogData['datagram'][]
|
||||
}
|
||||
constructor(param: SftpLogList['param'], pagination: Pagination) {
|
||||
super()
|
||||
this.pagination = pagination
|
||||
this.param = { ...param, ...pagination }
|
||||
}
|
||||
}
|
||||
|
||||
export class SftpLogTotal extends Request {
|
||||
readonly url = '/log/getSftpLogTotal'
|
||||
readonly method = 'get'
|
||||
|
||||
public param: {
|
||||
username: string
|
||||
serverName: string
|
||||
}
|
||||
|
||||
public datagram!: Total
|
||||
|
||||
constructor(param: SftpLogTotal['param']) {
|
||||
super()
|
||||
this.param = param
|
||||
}
|
||||
}
|
||||
|
||||
export class TerminalLogData {
|
||||
public datagram!: {
|
||||
id: number
|
||||
namespaceId: number
|
||||
userId: number
|
||||
username: string
|
||||
serverId: number
|
||||
serverName: string
|
||||
remoteAddr: string
|
||||
userAgent: string
|
||||
startTime: string
|
||||
endTime: string
|
||||
}
|
||||
}
|
||||
|
||||
export class TerminalLogList extends Request {
|
||||
readonly url = '/log/getTerminalLogList'
|
||||
readonly method = 'get'
|
||||
|
||||
public pagination: Pagination
|
||||
|
||||
public param: {
|
||||
username: string
|
||||
serverName: string
|
||||
}
|
||||
|
||||
public datagram!: {
|
||||
list: TerminalLogData['datagram'][]
|
||||
}
|
||||
constructor(param: TerminalLogList['param'], pagination: Pagination) {
|
||||
super()
|
||||
this.pagination = pagination
|
||||
this.param = { ...param, ...pagination }
|
||||
}
|
||||
}
|
||||
|
||||
export class TerminalLogTotal extends Request {
|
||||
readonly url = '/log/getTerminalLogTotal'
|
||||
readonly method = 'get'
|
||||
|
||||
public param: {
|
||||
username: string
|
||||
serverName: string
|
||||
}
|
||||
|
||||
public datagram!: Total
|
||||
|
||||
constructor(param: TerminalLogTotal['param']) {
|
||||
super()
|
||||
this.param = param
|
||||
}
|
||||
}
|
||||
|
||||
export class PublishLogData {
|
||||
public datagram!: {
|
||||
token: string
|
||||
publisherId: number
|
||||
publisherName: string
|
||||
projectId: number
|
||||
projectName: string
|
||||
state: number
|
||||
insertTime: string
|
||||
}
|
||||
}
|
||||
|
||||
export class PublishLogList extends Request {
|
||||
readonly url = '/log/getPublishLogList'
|
||||
readonly method = 'get'
|
||||
|
||||
public pagination: Pagination
|
||||
|
||||
public param: {
|
||||
username: string
|
||||
projectName: string
|
||||
}
|
||||
|
||||
public datagram!: {
|
||||
list: PublishLogData['datagram'][]
|
||||
}
|
||||
constructor(param: PublishLogList['param'], pagination: Pagination) {
|
||||
super()
|
||||
this.pagination = pagination
|
||||
this.param = { ...param, ...pagination }
|
||||
}
|
||||
}
|
||||
|
||||
export class PublishLogTotal extends Request {
|
||||
readonly url = '/log/getPublishLogTotal'
|
||||
readonly method = 'get'
|
||||
|
||||
public param: {
|
||||
username: string
|
||||
projectName: string
|
||||
}
|
||||
|
||||
public datagram!: Total
|
||||
|
||||
constructor(param: PublishLogTotal['param']) {
|
||||
super()
|
||||
this.param = param
|
||||
}
|
||||
}
|
1
web/src/icons/svg/log.svg
Normal file
1
web/src/icons/svg/log.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1645410396659" class="icon" viewBox="0 0 1056 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2389" xmlns:xlink="http://www.w3.org/1999/xlink" width="206.25" height="200"><defs><style type="text/css"></style></defs><path d="M10.955762 512.623801h80.286179a10.95574 10.95574 0 0 1 10.95574 10.976708v12.150911a10.95574 10.95574 0 0 1-10.95574 10.913805H10.955762A10.95574 10.95574 0 0 1 0.000023 535.709485v-12.108976a10.95574 10.95574 0 0 1 10.955739-10.976708z m0-340.309396h80.286179a10.95574 10.95574 0 0 1 10.95574 10.95574v12.182363a10.95574 10.95574 0 0 1-10.95574 10.95574H10.955762A10.95574 10.95574 0 0 1 0.000023 195.452508V183.270145a10.95574 10.95574 0 0 1 10.955739-10.95574z m0 170.207118h80.286179a10.95574 10.95574 0 0 1 10.95574 10.95574v12.150911a10.95574 10.95574 0 0 1-10.95574 10.95574H10.955762A10.95574 10.95574 0 0 1 0.000023 365.628174v-12.140427a10.95574 10.95574 0 0 1 10.955739-10.95574z m0 340.183588h80.286179a10.95574 10.95574 0 0 1 10.95574 10.95574v12.150912a10.95574 10.95574 0 0 1-10.95574 10.95574H10.955762A10.95574 10.95574 0 0 1 0.000023 705.811763v-12.140428a10.95574 10.95574 0 0 1 10.955739-10.95574z m-10.955739 0" p-id="2390"></path><path d="M815.06514 68.019954H755.159783a8.554913 8.554913 0 0 1-8.523461-7.579904A68.082847 68.082847 0 0 0 679.035737 0.00001H108.980804a76.606308 76.606308 0 0 0-76.63776 76.616792v50.826247a8.586365 8.586365 0 0 0 8.586365 8.586364h50.83673a42.65924 42.65924 0 0 1 42.690692 42.648756v25.476027a34.135779 34.135779 0 0 1-34.062391 34.062392H40.971345a8.586365 8.586365 0 0 0-8.628301 8.607332v50.836731a8.586365 8.586365 0 0 0 8.586365 8.586364h50.83673a42.65924 42.65924 0 0 1 42.65924 42.65924v16.889663a42.65924 42.65924 0 0 1-42.65924 42.65924H40.971345a8.586365 8.586365 0 0 0-8.586365 8.586364v50.826247a8.586365 8.586365 0 0 0 8.586365 8.586364h50.83673a42.65924 42.65924 0 0 1 42.65924 42.65924v25.455059a34.135779 34.135779 0 0 1-34.062391 34.072876H40.971345a8.586365 8.586365 0 0 0-8.586365 8.586364v50.952054a8.586365 8.586365 0 0 0 8.586365 8.586365h50.83673a42.65924 42.65924 0 0 1 42.65924 42.65924v25.24538a34.135779 34.135779 0 0 1-34.062391 34.062391H40.971345a8.586365 8.586365 0 0 0-8.628301 8.659752v119.034901a76.606308 76.606308 0 0 0 76.606308 76.606308h697.529423a76.606308 76.606308 0 0 0 76.606308-76.606308V136.039897a68.072363 68.072363 0 0 0-68.019943-68.009459z m-295.867883 84.689442c8.387169-14.174316 33.118835-20.496145 47.639122-12.507366l81.240219 45.500394c14.530771 8.219426 21.209054 32.164794 12.863821 46.33911L404.230133 665.783997a8.586365 8.586365 0 0 1-11.584777 3.145189l-126.751097-70.934485a8.586365 8.586365 0 0 1-3.145189-11.867845zM365.009633 708.894048l-103.067828 38.297912a8.586365 8.586365 0 0 1-11.532358-7.338774l-8.387169-100.56216a8.586365 8.586365 0 0 1 12.748497-8.208942l111.423545 62.264248a8.586365 8.586365 0 0 1-1.184687 15.547716z m450.055507 141.806065c0 37.522099-2.096792 68.009459-68.00946 68.00946H134.456831a68.145751 68.145751 0 0 1-67.29655-58.1126 8.617817 8.617817 0 0 1 8.502493-9.89686h594.671275a76.606308 76.606308 0 0 0 76.606308-76.606308V110.825969a8.607333 8.607333 0 0 1 8.701688-8.596848c33.42287 0.345971 59.307771 4.990366 59.307771 67.988491v680.482501z m0 0" p-id="2391"></path></svg>
|
After Width: | Height: | Size: 3.3 KiB |
@ -132,7 +132,12 @@
|
||||
"crontab": "Crontab",
|
||||
"serverCron": "Cron",
|
||||
"namespace": "Namespace",
|
||||
"member": "Member"
|
||||
"member": "Member",
|
||||
"log": "Log",
|
||||
"loginLog": "Login log",
|
||||
"publishLog": "Publish log",
|
||||
"sftpLog": "SFTP log",
|
||||
"terminalLog": "Terminal log"
|
||||
},
|
||||
"tagsView": {
|
||||
"refresh": "Refresh",
|
||||
|
@ -132,7 +132,12 @@
|
||||
"template": "模板设置",
|
||||
"crontab": "Crontab管理",
|
||||
"namespace": "空间设置",
|
||||
"member": "成员设置"
|
||||
"member": "成员设置",
|
||||
"log": "日志记录",
|
||||
"loginLog": "登录日志",
|
||||
"publishLog": "构建日志",
|
||||
"sftpLog": "SFTP日志",
|
||||
"terminalLog": "终端日志"
|
||||
},
|
||||
"tagsView": {
|
||||
"refresh": "刷新",
|
||||
|
@ -261,6 +261,59 @@ export const asyncRoutes: RouteRecordRaw[] = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/log',
|
||||
component: Layout,
|
||||
redirect: '/log/loginLog',
|
||||
name: 'log',
|
||||
meta: {
|
||||
title: 'log',
|
||||
icon: 'log',
|
||||
roles: ['admin'],
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'loginLog',
|
||||
name: 'LoginLog',
|
||||
component: () => import('@/views/log/loginLog.vue'),
|
||||
meta: {
|
||||
title: 'loginLog',
|
||||
icon: 'log',
|
||||
roles: ['admin'],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'publishLog',
|
||||
name: 'PublishLog',
|
||||
component: () => import('@/views/log/publishLog.vue'),
|
||||
meta: {
|
||||
title: 'publishLog',
|
||||
icon: 'log',
|
||||
roles: ['admin'],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'sftpLog',
|
||||
name: 'SftpLog',
|
||||
component: () => import('@/views/log/sftpLog.vue'),
|
||||
meta: {
|
||||
title: 'sftpLog',
|
||||
icon: 'log',
|
||||
roles: ['admin'],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'terminalLog',
|
||||
name: 'TerminalLog',
|
||||
component: () => import('@/views/log/terminalLog.vue'),
|
||||
meta: {
|
||||
title: 'terminalLog',
|
||||
icon: 'log',
|
||||
roles: ['admin'],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
// 404 page must be placed at the end !!!
|
||||
{
|
||||
path: '/:pathMatch(.*)*',
|
||||
|
@ -60,4 +60,9 @@
|
||||
&__fixed-right{
|
||||
height: 100% !important;
|
||||
}
|
||||
}
|
||||
|
||||
.el-pagination .btn-prev .el-icon,
|
||||
.el-pagination .btn-next .el-icon {
|
||||
width: 100%;
|
||||
}
|
@ -43,7 +43,12 @@ export function getRole() {
|
||||
export function getNamespace(): Namespace {
|
||||
const n =
|
||||
sessionStorage.getItem(NamespaceKey) || localStorage.getItem(NamespaceKey)
|
||||
return n ? JSON.parse(n) : { id: 0, name: '', role: '' }
|
||||
|
||||
try {
|
||||
return n ? JSON.parse(n) : { id: 0, name: '', role: '' }
|
||||
} catch (e) {
|
||||
return { id: 0, name: '', role: '' }
|
||||
}
|
||||
}
|
||||
|
||||
export function getNamespaceId(): number {
|
||||
|
110
web/src/views/log/loginLog.vue
Normal file
110
web/src/views/log/loginLog.vue
Normal file
@ -0,0 +1,110 @@
|
||||
<template>
|
||||
<el-row class="app-container">
|
||||
<el-row class="app-bar" type="flex" justify="space-between">
|
||||
<el-row>
|
||||
<el-input
|
||||
v-model="account"
|
||||
style="width: 200px"
|
||||
placeholder="Filter the account"
|
||||
/>
|
||||
<el-button
|
||||
:loading="tableLoading"
|
||||
type="primary"
|
||||
icon="el-icon-search"
|
||||
@click="searchList"
|
||||
/>
|
||||
</el-row>
|
||||
</el-row>
|
||||
<el-table
|
||||
:key="tableHeight"
|
||||
v-loading="tableLoading"
|
||||
border
|
||||
stripe
|
||||
highlight-current-row
|
||||
:max-height="tableHeight"
|
||||
:data="tableData"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-table-column prop="id" label="ID" width="80" />
|
||||
<el-table-column prop="account" label="Account" width="80" />
|
||||
<el-table-column prop="remoteAddr" label="Remote addr" width="160" />
|
||||
<el-table-column prop="referer" label="Referer" width="200" />
|
||||
<el-table-column
|
||||
prop="userAgent"
|
||||
label="User agent"
|
||||
width="160"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column prop="reason" label="Reason" show-overflow-tooltip />
|
||||
<el-table-column prop="loginTime" label="Login time" width="135" />
|
||||
</el-table>
|
||||
<el-row type="flex" justify="end" style="margin-top: 10px; width: 100%">
|
||||
<el-pagination
|
||||
style=""
|
||||
:total="pagination.total"
|
||||
:page-size="pagination.rows"
|
||||
background
|
||||
layout="prev, pager, next"
|
||||
@current-change="handlePageChange"
|
||||
/>
|
||||
</el-row>
|
||||
</el-row>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { LoginLogList, LoginLogTotal } from '@/api/log'
|
||||
import tableHeight from '@/mixin/tableHeight'
|
||||
import { defineComponent } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'LoginLog',
|
||||
mixins: [tableHeight],
|
||||
data() {
|
||||
return {
|
||||
tableLoading: false,
|
||||
account: '',
|
||||
tableData: [] as LoginLogList['datagram']['list'],
|
||||
pagination: {
|
||||
page: 1,
|
||||
rows: 19,
|
||||
total: 0,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
this.getList()
|
||||
this.getTotal()
|
||||
},
|
||||
|
||||
methods: {
|
||||
searchList() {
|
||||
this.pagination.page = 1
|
||||
this.getList()
|
||||
this.getTotal()
|
||||
},
|
||||
getList() {
|
||||
this.tableLoading = true
|
||||
this.tableData = []
|
||||
new LoginLogList({ account: this.account }, this.pagination)
|
||||
.request()
|
||||
.then((response) => {
|
||||
this.tableData = response.data.list
|
||||
})
|
||||
.finally(() => {
|
||||
this.tableLoading = false
|
||||
})
|
||||
},
|
||||
getTotal() {
|
||||
new LoginLogTotal({ account: this.account })
|
||||
.request()
|
||||
.then((response) => {
|
||||
this.pagination.total = response.data.total
|
||||
})
|
||||
},
|
||||
handlePageChange(val = 1) {
|
||||
this.pagination.page = val
|
||||
this.getList()
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
371
web/src/views/log/publishLog.vue
Normal file
371
web/src/views/log/publishLog.vue
Normal file
@ -0,0 +1,371 @@
|
||||
<template>
|
||||
<el-row class="app-container">
|
||||
<el-row class="app-bar" type="flex" justify="space-between">
|
||||
<el-row>
|
||||
<el-input
|
||||
v-model="searchParam.username"
|
||||
style="width: 200px"
|
||||
placeholder="Filter the username"
|
||||
/>
|
||||
<el-input
|
||||
v-model="searchParam.projectName"
|
||||
style="width: 200px"
|
||||
placeholder="Filter the project name"
|
||||
/>
|
||||
<el-button
|
||||
:loading="tableLoading"
|
||||
type="primary"
|
||||
icon="el-icon-search"
|
||||
@click="searchList"
|
||||
/>
|
||||
</el-row>
|
||||
</el-row>
|
||||
<el-table
|
||||
:key="tableHeight"
|
||||
v-loading="tableLoading"
|
||||
border
|
||||
stripe
|
||||
highlight-current-row
|
||||
:max-height="tableHeight"
|
||||
:data="tableData"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-table-column prop="token" label="Token" width="300" />
|
||||
<el-table-column prop="publisherName" label="Username" width="80" />
|
||||
<el-table-column prop="projectName" label="Project Name" width="160" />
|
||||
<el-table-column prop="state" label="State" align="center" width="80">
|
||||
<template #default="scope">
|
||||
<span v-if="scope.row.state === 1" style="color: #67c23a">
|
||||
{{ $t('success') }}
|
||||
</span>
|
||||
<span v-else style="color: #f56c6c">{{ $t('fail') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="detail" label="Reason" show-overflow-tooltip />
|
||||
<el-table-column prop="insertTime" label="insertTime" width="135" />
|
||||
<el-table-column
|
||||
prop="operation"
|
||||
:label="$t('op')"
|
||||
width="100"
|
||||
align="center"
|
||||
:fixed="$store.state.app.device === 'mobile' ? false : 'right'"
|
||||
>
|
||||
<template #default="scope">
|
||||
<el-button type="text" @click="handleDetail(scope.row)">
|
||||
{{ $t('detail') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-row type="flex" justify="end" style="margin-top: 10px; width: 100%">
|
||||
<el-pagination
|
||||
style=""
|
||||
:total="pagination.total"
|
||||
:page-size="pagination.rows"
|
||||
background
|
||||
layout="prev, pager, next"
|
||||
@current-change="handlePageChange"
|
||||
/>
|
||||
</el-row>
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
:title="$t('detail')"
|
||||
:fullscreen="$store.state.app.device === 'mobile'"
|
||||
>
|
||||
<el-row v-loading="traceLoading" class="project-detail">
|
||||
<div
|
||||
v-for="(item, index) in publishLocalTraceList"
|
||||
:key="index"
|
||||
style="width: 100%"
|
||||
>
|
||||
<template v-if="item.type === 2">
|
||||
<el-row style="margin: 5px 0">
|
||||
<i v-if="item.state === 1" class="el-icon-check icon-success" />
|
||||
<i v-else class="el-icon-close icon-fail" />
|
||||
-------------GIT-------------
|
||||
</el-row>
|
||||
<el-row>Time: {{ item.updateTime }}</el-row>
|
||||
<template v-if="item.state !== 0">
|
||||
<el-row>Branch: {{ item['branch'] }}</el-row>
|
||||
<el-row>Commit:{{ item['commit'] }}</el-row>
|
||||
<el-row>Message: {{ item['message'] }}</el-row>
|
||||
<el-row>Author: {{ item['author'] }}</el-row>
|
||||
<el-row>
|
||||
Datetime:
|
||||
{{ item['timestamp'] ? parseTime(item['timestamp']) : '' }}
|
||||
</el-row>
|
||||
<el-row>
|
||||
<span style="white-space: pre-line">{{ item['diff'] }}</span>
|
||||
</el-row>
|
||||
</template>
|
||||
<el-row v-else style="margin: 5px 0">
|
||||
<span style="white-space: pre-line; padding: 5px 0">
|
||||
{{ item.detail }}
|
||||
</span>
|
||||
</el-row>
|
||||
</template>
|
||||
<div v-if="item.type === 3">
|
||||
<hr />
|
||||
<el-row align="middle">
|
||||
<i v-if="item.state === 1" class="el-icon-check icon-success" />
|
||||
<i v-else class="el-icon-close icon-fail" />
|
||||
--------After pull--------
|
||||
</el-row>
|
||||
<el-row>Time: {{ item.updateTime }}</el-row>
|
||||
<el-row style="width: 100%">
|
||||
<div>Script:</div>
|
||||
<pre style="white-space: pre-line">{{ item.script }}</pre>
|
||||
</el-row>
|
||||
<div v-loading="traceDetail[item.id] === ''" style="margin: 5px 0">
|
||||
<span style="padding: 5px 0">[goploy ~]#</span>
|
||||
<el-button
|
||||
v-if="item.state === 1 && !(item.id in traceDetail)"
|
||||
type="text"
|
||||
@click="getPublishTraceDetail(item)"
|
||||
>
|
||||
{{ $t('deployPage.showDetail') }}
|
||||
</el-button>
|
||||
<div v-else style="white-space: pre-line; padding: 5px 0">
|
||||
{{ traceDetail[item.id] }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-tabs v-model="activeRomoteTracePane">
|
||||
<el-tab-pane
|
||||
v-for="(item, serverName) in publishRemoteTraceList"
|
||||
:key="serverName"
|
||||
:label="serverName"
|
||||
:name="serverName"
|
||||
>
|
||||
<div v-for="(trace, key) in item" :key="key">
|
||||
<template v-if="trace.type === 4">
|
||||
<el-row style="margin: 5px 0">
|
||||
<i
|
||||
v-if="trace.state === 1"
|
||||
class="el-icon-check icon-success"
|
||||
/>
|
||||
<i v-else class="el-icon-close icon-fail" />
|
||||
---------Before deploy---------
|
||||
</el-row>
|
||||
<el-row style="margin: 5px 0">
|
||||
Time: {{ trace.updateTime }}
|
||||
</el-row>
|
||||
<el-row>
|
||||
Script:
|
||||
<pre style="white-space: pre-line">{{ trace.script }}</pre>
|
||||
</el-row>
|
||||
<div v-loading="traceDetail[trace.id] === ''">
|
||||
<span style="padding: 5px 0">[goploy ~]#</span>
|
||||
<el-button
|
||||
v-if="trace.state === 1 && !(trace.id in traceDetail)"
|
||||
type="text"
|
||||
@click="getPublishTraceDetail(trace)"
|
||||
>
|
||||
{{ $t('deployPage.showDetail') }}
|
||||
</el-button>
|
||||
<div v-else style="white-space: pre-line; padding: 5px 0">
|
||||
{{ traceDetail[trace.id] }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="trace.type === 5">
|
||||
<el-row style="margin: 5px 0">
|
||||
<i
|
||||
v-if="trace.state === 1"
|
||||
class="el-icon-check icon-success"
|
||||
/>
|
||||
<i v-else class="el-icon-close icon-fail" />
|
||||
-----------Rsync------------
|
||||
</el-row>
|
||||
<el-row style="margin: 5px 0">
|
||||
Time: {{ trace.updateTime }}
|
||||
</el-row>
|
||||
<el-row>Command: {{ trace.command }}</el-row>
|
||||
<div v-loading="traceDetail[trace.id] === ''">
|
||||
<span style="padding: 5px 0">[goploy ~]#</span>
|
||||
<el-button
|
||||
v-if="trace.state === 1 && !(trace.id in traceDetail)"
|
||||
type="text"
|
||||
@click="getPublishTraceDetail(trace)"
|
||||
>
|
||||
{{ $t('deployPage.showDetail') }}
|
||||
</el-button>
|
||||
<div v-else style="white-space: pre-line; padding: 5px 0">
|
||||
{{ traceDetail[trace.id] }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="trace.type === 6">
|
||||
<el-row style="margin: 5px 0">
|
||||
<i
|
||||
v-if="trace.state === 1"
|
||||
class="el-icon-check icon-success"
|
||||
/>
|
||||
<i v-else class="el-icon-close icon-fail" />
|
||||
--------After deploy--------
|
||||
</el-row>
|
||||
<el-row style="margin: 5px 0">
|
||||
Time: {{ trace.updateTime }}
|
||||
</el-row>
|
||||
<el-row>Script: {{ trace.script }}</el-row>
|
||||
<div
|
||||
v-loading="traceDetail[trace.id] === ''"
|
||||
style="margin: 5px 0"
|
||||
>
|
||||
<span>[goploy ~]#</span>
|
||||
<el-button
|
||||
v-if="trace.state === 1 && !(trace.id in traceDetail)"
|
||||
type="text"
|
||||
@click="getPublishTraceDetail(trace)"
|
||||
>
|
||||
{{ $t('deployPage.showDetail') }}
|
||||
</el-button>
|
||||
<div v-else style="white-space: pre-line; padding: 5px 0">
|
||||
{{ traceDetail[trace.id] }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-row>
|
||||
</el-dialog>
|
||||
</el-row>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { PublishLogData, PublishLogList, PublishLogTotal } from '@/api/log'
|
||||
import { DeployTrace, DeployTraceDetail, PublishTraceData } from '@/api/deploy'
|
||||
import { parseTime } from '@/utils'
|
||||
import tableHeight from '@/mixin/tableHeight'
|
||||
import { defineComponent } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'PublishLog',
|
||||
mixins: [tableHeight],
|
||||
data() {
|
||||
return {
|
||||
dialogVisible: false,
|
||||
tableLoading: false,
|
||||
searchParam: {
|
||||
username: '',
|
||||
projectName: '',
|
||||
},
|
||||
tableData: [] as PublishLogList['datagram']['list'],
|
||||
pagination: {
|
||||
page: 1,
|
||||
rows: 17,
|
||||
total: 0,
|
||||
},
|
||||
traceLoading: false,
|
||||
traceDetail: {} as Record<number, string>,
|
||||
activeRomoteTracePane: '',
|
||||
publishLocalTraceList: [] as DeployTrace['datagram']['list'],
|
||||
publishRemoteTraceList: {} as Record<
|
||||
string,
|
||||
DeployTrace['datagram']['list']
|
||||
>,
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
this.getList()
|
||||
this.getTotal()
|
||||
},
|
||||
|
||||
methods: {
|
||||
parseTime,
|
||||
searchList() {
|
||||
this.pagination.page = 1
|
||||
this.getList()
|
||||
this.getTotal()
|
||||
},
|
||||
getList() {
|
||||
this.tableLoading = true
|
||||
this.tableData = []
|
||||
new PublishLogList(this.searchParam, this.pagination)
|
||||
.request()
|
||||
.then((response) => {
|
||||
this.tableData = response.data.list
|
||||
})
|
||||
.finally(() => {
|
||||
this.tableLoading = false
|
||||
})
|
||||
},
|
||||
getTotal() {
|
||||
new PublishLogTotal(this.searchParam).request().then((response) => {
|
||||
this.pagination.total = response.data.total
|
||||
})
|
||||
},
|
||||
handlePageChange(val = 1) {
|
||||
this.pagination.page = val
|
||||
this.getList()
|
||||
},
|
||||
handleDetail(data: PublishLogData['datagram']) {
|
||||
this.dialogVisible = true
|
||||
this.traceLoading = true
|
||||
new DeployTrace({ lastPublishToken: data.token })
|
||||
.request()
|
||||
.then((response) => {
|
||||
const publishTraceList = response.data.list.map((element) => {
|
||||
if (element.ext !== '') {
|
||||
Object.assign(element, JSON.parse(element.ext))
|
||||
}
|
||||
return element
|
||||
})
|
||||
|
||||
this.publishLocalTraceList = publishTraceList.filter(
|
||||
(element) => element.type < 4
|
||||
)
|
||||
this.publishRemoteTraceList = {}
|
||||
for (const trace of publishTraceList) {
|
||||
if (trace.detail !== '') {
|
||||
this.traceDetail[trace.id] = trace.detail
|
||||
}
|
||||
if (trace.type < 4) continue
|
||||
if (!this.publishRemoteTraceList[trace.serverName]) {
|
||||
this.publishRemoteTraceList[trace.serverName] = []
|
||||
}
|
||||
this.publishRemoteTraceList[trace.serverName].push(trace)
|
||||
}
|
||||
this.activeRomoteTracePane = Object.keys(
|
||||
this.publishRemoteTraceList
|
||||
)[0]
|
||||
})
|
||||
.finally(() => {
|
||||
this.traceLoading = false
|
||||
})
|
||||
},
|
||||
|
||||
getPublishTraceDetail(data: PublishTraceData['datagram']) {
|
||||
this.traceDetail[data.id] = ''
|
||||
new DeployTraceDetail({ id: data.id }).request().then((response) => {
|
||||
this.traceDetail[data.id] =
|
||||
response.data.detail === ''
|
||||
? this.$t('deployPage.noDetail')
|
||||
: response.data.detail
|
||||
})
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
@import '@/styles/mixin.scss';
|
||||
.icon-success {
|
||||
color: #67c23a;
|
||||
font-size: 14px;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.icon-fail {
|
||||
color: #f56c6c;
|
||||
font-size: 14px;
|
||||
font-weight: 900;
|
||||
}
|
||||
.project-detail {
|
||||
padding-left: 5px;
|
||||
height: 470px;
|
||||
overflow-y: auto;
|
||||
@include scrollBar();
|
||||
}
|
||||
</style>
|
117
web/src/views/log/sftpLog.vue
Normal file
117
web/src/views/log/sftpLog.vue
Normal file
@ -0,0 +1,117 @@
|
||||
<template>
|
||||
<el-row class="app-container">
|
||||
<el-row class="app-bar" type="flex" justify="space-between">
|
||||
<el-row>
|
||||
<el-input
|
||||
v-model="searchParam.username"
|
||||
style="width: 200px"
|
||||
placeholder="Filter the username"
|
||||
/>
|
||||
<el-input
|
||||
v-model="searchParam.serverName"
|
||||
style="width: 200px"
|
||||
placeholder="Filter the server name"
|
||||
/>
|
||||
<el-button
|
||||
:loading="tableLoading"
|
||||
type="primary"
|
||||
icon="el-icon-search"
|
||||
@click="searchList"
|
||||
/>
|
||||
</el-row>
|
||||
</el-row>
|
||||
<el-table
|
||||
:key="tableHeight"
|
||||
v-loading="tableLoading"
|
||||
border
|
||||
stripe
|
||||
highlight-current-row
|
||||
:max-height="tableHeight"
|
||||
:data="tableData"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-table-column prop="id" label="ID" width="80" />
|
||||
<el-table-column prop="username" label="Username" width="80" />
|
||||
<el-table-column prop="serverName" label="Server Name" width="160" />
|
||||
<el-table-column prop="remoteAddr" label="Remote addr" width="160" />
|
||||
<el-table-column
|
||||
prop="userAgent"
|
||||
label="User agent"
|
||||
width="160"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column prop="type" label="Type" width="100" />
|
||||
<el-table-column prop="path" label="Path" show-overflow-tooltip />
|
||||
<el-table-column prop="reason" label="Reason" show-overflow-tooltip />
|
||||
</el-table>
|
||||
<el-row type="flex" justify="end" style="margin-top: 10px; width: 100%">
|
||||
<el-pagination
|
||||
style=""
|
||||
:total="pagination.total"
|
||||
:page-size="pagination.rows"
|
||||
background
|
||||
layout="prev, pager, next"
|
||||
@current-change="handlePageChange"
|
||||
/>
|
||||
</el-row>
|
||||
</el-row>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { SftpLogList, SftpLogTotal } from '@/api/log'
|
||||
import tableHeight from '@/mixin/tableHeight'
|
||||
import { defineComponent } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'SftpLog',
|
||||
mixins: [tableHeight],
|
||||
data() {
|
||||
return {
|
||||
tableLoading: false,
|
||||
searchParam: {
|
||||
username: '',
|
||||
serverName: '',
|
||||
},
|
||||
tableData: [] as SftpLogList['datagram']['list'],
|
||||
pagination: {
|
||||
page: 1,
|
||||
rows: 19,
|
||||
total: 0,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
this.getList()
|
||||
this.getTotal()
|
||||
},
|
||||
|
||||
methods: {
|
||||
searchList() {
|
||||
this.pagination.page = 1
|
||||
this.getList()
|
||||
this.getTotal()
|
||||
},
|
||||
getList() {
|
||||
this.tableLoading = true
|
||||
this.tableData = []
|
||||
new SftpLogList(this.searchParam, this.pagination)
|
||||
.request()
|
||||
.then((response) => {
|
||||
this.tableData = response.data.list
|
||||
})
|
||||
.finally(() => {
|
||||
this.tableLoading = false
|
||||
})
|
||||
},
|
||||
getTotal() {
|
||||
new SftpLogTotal(this.searchParam).request().then((response) => {
|
||||
this.pagination.total = response.data.total
|
||||
})
|
||||
},
|
||||
handlePageChange(val = 1) {
|
||||
this.pagination.page = val
|
||||
this.getList()
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
177
web/src/views/log/terminalLog.vue
Normal file
177
web/src/views/log/terminalLog.vue
Normal file
@ -0,0 +1,177 @@
|
||||
<template>
|
||||
<el-row class="app-container">
|
||||
<el-row v-if="recordViewer" class="terminal">
|
||||
<div class="close" @click="closeRecordViewer">×</div>
|
||||
<div ref="record" style="width: 100%; height: 100%"></div>
|
||||
</el-row>
|
||||
<el-row v-show="!recordViewer">
|
||||
<el-row class="app-bar" type="flex" justify="space-between">
|
||||
<el-row>
|
||||
<el-input
|
||||
v-model="searchParam.username"
|
||||
style="width: 200px"
|
||||
placeholder="Filter the username"
|
||||
/>
|
||||
<el-input
|
||||
v-model="searchParam.serverName"
|
||||
style="width: 200px"
|
||||
placeholder="Filter the server name"
|
||||
/>
|
||||
<el-button
|
||||
:loading="tableLoading"
|
||||
type="primary"
|
||||
icon="el-icon-search"
|
||||
@click="searchList"
|
||||
/>
|
||||
</el-row>
|
||||
</el-row>
|
||||
<el-table
|
||||
:key="tableHeight"
|
||||
v-loading="tableLoading"
|
||||
border
|
||||
stripe
|
||||
highlight-current-row
|
||||
:max-height="tableHeight"
|
||||
:data="tableData"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-table-column prop="id" label="ID" width="80" />
|
||||
<el-table-column prop="username" label="Username" width="80" />
|
||||
<el-table-column prop="serverName" label="Server Name" width="160" />
|
||||
<el-table-column prop="remoteAddr" label="Remote addr" width="160" />
|
||||
<el-table-column
|
||||
prop="userAgent"
|
||||
label="User agent"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column prop="startTime" label="Start time" width="135" />
|
||||
<el-table-column prop="endTime" label="End time" width="135" />
|
||||
<el-table-column
|
||||
prop="operation"
|
||||
:label="$t('op')"
|
||||
width="100"
|
||||
align="center"
|
||||
:fixed="$store.state.app.device === 'mobile' ? false : 'right'"
|
||||
>
|
||||
<template #default="scope">
|
||||
<el-button type="text" @click="handleRecord(scope.row)">
|
||||
Record
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-row type="flex" justify="end" style="margin-top: 10px; width: 100%">
|
||||
<el-pagination
|
||||
style=""
|
||||
:total="pagination.total"
|
||||
:page-size="pagination.rows"
|
||||
background
|
||||
layout="prev, pager, next"
|
||||
@current-change="handlePageChange"
|
||||
/>
|
||||
</el-row>
|
||||
</el-row>
|
||||
</el-row>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import 'asciinema-player/dist/bundle/asciinema-player.css'
|
||||
import * as AsciinemaPlayer from 'asciinema-player'
|
||||
import { TerminalLogData, TerminalLogList, TerminalLogTotal } from '@/api/log'
|
||||
import { NamespaceKey, getNamespaceId } from '@/utils/namespace'
|
||||
import tableHeight from '@/mixin/tableHeight'
|
||||
import { defineComponent } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'TerminalLog',
|
||||
mixins: [tableHeight],
|
||||
data() {
|
||||
return {
|
||||
recordViewer: false,
|
||||
tableLoading: false,
|
||||
searchParam: {
|
||||
username: '',
|
||||
serverName: '',
|
||||
},
|
||||
tableData: [] as TerminalLogList['datagram']['list'],
|
||||
pagination: {
|
||||
page: 1,
|
||||
rows: 19,
|
||||
total: 0,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
this.getList()
|
||||
this.getTotal()
|
||||
},
|
||||
|
||||
methods: {
|
||||
searchList() {
|
||||
this.pagination.page = 1
|
||||
this.getList()
|
||||
this.getTotal()
|
||||
},
|
||||
getList() {
|
||||
this.tableLoading = true
|
||||
this.tableData = []
|
||||
new TerminalLogList(this.searchParam, this.pagination)
|
||||
.request()
|
||||
.then((response) => {
|
||||
this.tableData = response.data.list
|
||||
})
|
||||
.finally(() => {
|
||||
this.tableLoading = false
|
||||
})
|
||||
},
|
||||
getTotal() {
|
||||
new TerminalLogTotal(this.searchParam).request().then((response) => {
|
||||
this.pagination.total = response.data.total
|
||||
})
|
||||
},
|
||||
handlePageChange(val = 1) {
|
||||
this.pagination.page = val
|
||||
this.getList()
|
||||
},
|
||||
handleRecord(data: TerminalLogData['datagram']) {
|
||||
this.recordViewer = true
|
||||
const castUrl = `${location.origin}${
|
||||
import.meta.env.VITE_APP_BASE_API
|
||||
}/log/getTerminalRecord?${NamespaceKey}=${getNamespaceId()}&recordId=${
|
||||
data.id
|
||||
}`
|
||||
this.$nextTick(() => {
|
||||
AsciinemaPlayer.create(castUrl, this.$refs['record'], {
|
||||
fit: false,
|
||||
fontSize: '14px',
|
||||
})
|
||||
})
|
||||
},
|
||||
closeRecordViewer() {
|
||||
this.recordViewer = false
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.terminal {
|
||||
height: calc(100vh - 124px);
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
.close {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
right: 30px;
|
||||
color: #fff;
|
||||
z-index: 1000;
|
||||
font-size: 18px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
.asciinema-player {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
@ -63,7 +63,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-row type="flex" justify="end" style="margin-top: 10px">
|
||||
<el-row type="flex" justify="end" style="margin-top: 10px; width: 100%">
|
||||
<el-pagination
|
||||
hide-on-single-page
|
||||
:total="pagination.total"
|
||||
@ -158,7 +158,7 @@ export default defineComponent({
|
||||
tempFormData: {},
|
||||
pagination: {
|
||||
page: 1,
|
||||
rows: 16,
|
||||
rows: 18,
|
||||
total: 0,
|
||||
},
|
||||
formProps: {
|
||||
|
@ -103,7 +103,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-row type="flex" justify="end" style="margin-top: 10px">
|
||||
<el-row type="flex" justify="end" style="margin-top: 10px; width: 100%">
|
||||
<el-pagination
|
||||
hide-on-single-page
|
||||
:total="pagination.total"
|
||||
|
@ -61,7 +61,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-row type="flex" justify="end" style="margin-top: 10px">
|
||||
<el-row type="flex" justify="end" style="margin-top: 10px; width: 100%">
|
||||
<el-pagination
|
||||
hide-on-single-page
|
||||
:total="pagination.total"
|
||||
|
@ -151,7 +151,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-row type="flex" justify="end" style="margin-top: 10px">
|
||||
<el-row type="flex" justify="end" style="margin-top: 10px; width: 100%">
|
||||
<el-pagination
|
||||
hide-on-single-page
|
||||
:total="pagination.total"
|
||||
|
@ -96,7 +96,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-row type="flex" justify="end" style="margin-top: 10px">
|
||||
<el-row type="flex" justify="end" style="margin-top: 10px; width: 100%">
|
||||
<el-pagination
|
||||
hide-on-single-page
|
||||
:total="pagination.total"
|
||||
|
@ -101,7 +101,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-row type="flex" justify="end" style="margin-top: 10px">
|
||||
<el-row type="flex" justify="end" style="margin-top: 10px; width: 100%">
|
||||
<el-pagination
|
||||
hide-on-single-page
|
||||
:total="pagination.total"
|
||||
|
@ -400,7 +400,7 @@ export default defineComponent({
|
||||
this.fileListLoading = true
|
||||
this.selectedFile = {} as file
|
||||
this.dir = path.normalize(target)
|
||||
this.ws?.send(target)
|
||||
this.ws?.send(this.dir)
|
||||
},
|
||||
|
||||
dirOpen(dir: string) {
|
||||
|
14
ws/Sftp.go
14
ws/Sftp.go
@ -123,6 +123,20 @@ func (hub *Hub) Sftp(gp *core.Goploy) core.Response {
|
||||
core.Log(core.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 {
|
||||
core.Log(core.ERROR, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
40
ws/Xterm.go
40
ws/Xterm.go
@ -6,6 +6,7 @@ import (
|
||||
"github.com/zhenorzz/goploy/core"
|
||||
"github.com/zhenorzz/goploy/model"
|
||||
"github.com/zhenorzz/goploy/response"
|
||||
"github.com/zhenorzz/goploy/utils"
|
||||
"golang.org/x/crypto/ssh"
|
||||
"net/http"
|
||||
"strconv"
|
||||
@ -101,11 +102,31 @@ func (hub *Hub) Xterm(gp *core.Goploy) core.Response {
|
||||
return response.Empty{}
|
||||
}
|
||||
|
||||
//recorder, _ := utils.NewRecorder(path.Join(core.GetLogPath(), "demo.cast"), "xterm", rows, cols)
|
||||
//defer recorder.Close()
|
||||
// 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 *utils.Recorder
|
||||
recorder, err = utils.NewRecorder(core.GetTerminalLogPath(tlID), "xterm", rows, cols)
|
||||
if err != nil {
|
||||
core.Log(core.ERROR, err.Error())
|
||||
} else {
|
||||
defer recorder.Close()
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(pingPeriod)
|
||||
defer ticker.Stop()
|
||||
flushMessageTick := time.NewTicker(time.Millisecond * time.Duration(120))
|
||||
flushMessageTick := time.NewTicker(time.Millisecond * time.Duration(50))
|
||||
defer flushMessageTick.Stop()
|
||||
stop := make(chan bool, 1)
|
||||
defer func() {
|
||||
@ -126,7 +147,11 @@ func (hub *Hub) Xterm(gp *core.Goploy) core.Response {
|
||||
c.Close()
|
||||
return
|
||||
}
|
||||
//recorder.WriteData(comboWriter.buffer.String())
|
||||
if recorder != nil {
|
||||
if err := recorder.WriteData(comboWriter.buffer.String()); err != nil {
|
||||
core.Log(core.ERROR, err.Error())
|
||||
}
|
||||
}
|
||||
comboWriter.buffer.Reset()
|
||||
}
|
||||
case <-stop:
|
||||
@ -151,5 +176,12 @@ func (hub *Hub) Xterm(gp *core.Goploy) core.Response {
|
||||
}
|
||||
}
|
||||
|
||||
if err := (model.TerminalLog{
|
||||
ID: tlID,
|
||||
EndTime: time.Now().Format("20060102150405"),
|
||||
}.EditRow()); err != nil {
|
||||
core.Log(core.ERROR, err.Error())
|
||||
}
|
||||
|
||||
return response.Empty{}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user