Compare commits

...

3 Commits

Author SHA1 Message Date
Tiger Wang
a96f6189ac wip 2022-10-11 19:12:12 -04:00
Tiger Wang
e164a5d4c6 wip 2022-10-11 16:55:20 -04:00
Tiger Wang
42ebd5f325 wip 2022-10-11 00:19:02 -04:00
8 changed files with 271 additions and 238 deletions

View File

@@ -1,64 +0,0 @@
/*
* @Author: LinkLeong link@icewhale.com
* @Date: 2021-10-08 10:29:08
* @LastEditors: LinkLeong
* @LastEditTime: 2022-07-22 11:06:07
* @FilePath: /CasaOS/middleware/gin.go
* @Description:
* @Website: https://www.casaos.io
* Copyright (c) 2022 by icewhale, All Rights Reserved.
*/
package middleware
import (
"fmt"
"net/http"
"runtime/debug"
"strings"
"github.com/IceWhaleTech/CasaOS/pkg/utils/loger"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
func Cors() gin.HandlerFunc {
return func(c *gin.Context) {
method := c.Request.Method
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE,UPDATE")
// 允许跨域设置可以返回其他子段,可以自定义字段
c.Header("Access-Control-Allow-Headers", "Authorization, Content-Length, X-CSRF-Token, Token,session,Language,Content-Type,Access-Control-Allow-Origin,Access-Control-Allow-Headers,Access-Control-Allow-Methods,Connection,Host,Origin,Referer,User-Agent,X-Requested-With")
// 允许浏览器(客户端)可以解析的头部 (重要)
c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers")
// c.Writer.Header().Set("Access-Control-Allow-Headers", "Accept, Authorization, Content-Type, Content-Length, X-CSRF-Token, Token, session, Origin, Host, Connection, Accept-Encoding, Accept-Language, X-Requested-With")
// 设置缓存时间
c.Header("Access-Control-Max-Age", "172800")
c.Header("Access-Control-Allow-Credentials", "true")
c.Set("Content-Type", "application/json")
//}
// 允许类型校验
if method == "OPTIONS" {
c.JSON(http.StatusOK, "ok!")
}
defer func() {
if err := recover(); err != nil {
fmt.Println(err)
debug.PrintStack()
}
}()
c.Next()
}
}
func WriteLog() gin.HandlerFunc {
return func(c *gin.Context) {
if !strings.Contains(c.Request.URL.String(), "password") {
loger.Info("request:", zap.Any("path", c.Request.URL.String()), zap.Any("param", c.Params), zap.Any("query", c.Request.URL.Query()), zap.Any("method", c.Request.Method))
c.Next()
}
}
}

View File

@@ -17,6 +17,8 @@ type DockerStatsModel struct {
Previous interface{} `json:"previous"`
}
type DeckerDaemonModel struct {
Graph string `json:"graph"`
// reference - https://docs.docker.com/engine/reference/commandline/dockerd/#daemon-configuration-file
type DockerDaemonConfigurationModel struct {
// e.g. `/var/lib/docker`
Root string `json:"data-root,omitempty"`
}

View File

@@ -15,7 +15,6 @@ import (
)
func NewSshClient(user, password string, port string) (*ssh.Client, error) {
// connet to ssh
// addr = fmt.Sprintf("%s:%d", host, port)
@@ -23,10 +22,10 @@ func NewSshClient(user, password string, port string) (*ssh.Client, error) {
Timeout: time.Second * 5,
User: user,
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
//HostKeyCallback: ,
//HostKeyCallback: hostKeyCallBackFunc(h.Host),
// HostKeyCallback: ,
// HostKeyCallback: hostKeyCallBackFunc(h.Host),
}
//if h.Type == "password" {
// if h.Type == "password" {
config.Auth = []ssh.AuthMethod{ssh.Password(password)}
//} else {
// config.Auth = []ssh.AuthMethod{publicKeyAuthFunc(h.Key)}
@@ -90,11 +89,11 @@ func (w *wsBufferWriter) Write(p []byte) (int, error) {
defer w.mu.Unlock()
return w.buffer.Write(p)
}
func (s *SshConn) Close() {
if s.Session != nil {
s.Session.Close()
}
}
const (
@@ -102,16 +101,15 @@ const (
wsMsgResize = "resize"
)
//ReceiveWsMsg receive websocket msg do some handling then write into ssh.session.stdin
// ReceiveWsMsg receive websocket msg do some handling then write into ssh.session.stdin
func ReceiveWsMsgUser(wsConn *websocket.Conn, logBuff *bytes.Buffer) string {
//tells other go routine quit
// tells other go routine quit
username := ""
for {
//read websocket msg
// read websocket msg
_, wsData, err := wsConn.ReadMessage()
if err != nil {
return ""
}
@@ -125,8 +123,8 @@ func ReceiveWsMsgUser(wsConn *websocket.Conn, logBuff *bytes.Buffer) string {
//}
switch msgObj.Type {
case wsMsgCmd:
//handle xterm.js stdin
//decodeBytes, err := base64.StdEncoding.DecodeString(msgObj.Cmd)
// handle xterm.js stdin
// decodeBytes, err := base64.StdEncoding.DecodeString(msgObj.Cmd)
decodeBytes := []byte(msgObj.Cmd)
if msgObj.Cmd == "\u007f" {
if len(username) == 0 {
@@ -144,7 +142,7 @@ func ReceiveWsMsgUser(wsConn *websocket.Conn, logBuff *bytes.Buffer) string {
if err := wsConn.WriteMessage(websocket.TextMessage, decodeBytes); err != nil {
logrus.WithError(err).Error("ws cmd bytes write to ssh.stdin pipe failed")
}
//write input cmd to log buffer
// write input cmd to log buffer
if _, err := logBuff.Write(decodeBytes); err != nil {
logrus.WithError(err).Error("write received cmd into log buffer failed")
}
@@ -154,11 +152,11 @@ func ReceiveWsMsgUser(wsConn *websocket.Conn, logBuff *bytes.Buffer) string {
}
func ReceiveWsMsgPassword(wsConn *websocket.Conn, logBuff *bytes.Buffer) string {
//tells other go routine quit
// tells other go routine quit
password := ""
for {
//read websocket msg
// read websocket msg
_, wsData, err := wsConn.ReadMessage()
if err != nil {
logrus.WithError(err).Error("reading webSocket message failed")
@@ -175,8 +173,8 @@ func ReceiveWsMsgPassword(wsConn *websocket.Conn, logBuff *bytes.Buffer) string
//}
switch msgObj.Type {
case wsMsgCmd:
//handle xterm.js stdin
//decodeBytes, err := base64.StdEncoding.DecodeString(msgObj.Cmd)
// handle xterm.js stdin
// decodeBytes, err := base64.StdEncoding.DecodeString(msgObj.Cmd)
if msgObj.Cmd == "\r" {
return password
}
@@ -194,16 +192,16 @@ func ReceiveWsMsgPassword(wsConn *websocket.Conn, logBuff *bytes.Buffer) string
}
}
//ReceiveWsMsg receive websocket msg do some handling then write into ssh.session.stdin
// ReceiveWsMsg receive websocket msg do some handling then write into ssh.session.stdin
func (ssConn *SshConn) ReceiveWsMsg(wsConn *websocket.Conn, logBuff *bytes.Buffer, exitCh chan bool) {
//tells other go routine quit
// tells other go routine quit
defer setQuit(exitCh)
for {
select {
case <-exitCh:
return
default:
//read websocket msg
// read websocket msg
_, wsData, err := wsConn.ReadMessage()
if err != nil {
logrus.WithError(err).Error("reading webSocket message failed")
@@ -227,15 +225,15 @@ func (ssConn *SshConn) ReceiveWsMsg(wsConn *websocket.Conn, logBuff *bytes.Buffe
switch msgObj.Type {
case wsMsgResize:
//handle xterm.js size change
// handle xterm.js size change
if msgObj.Cols > 0 && msgObj.Rows > 0 {
if err := ssConn.Session.WindowChange(msgObj.Rows, msgObj.Cols); err != nil {
logrus.WithError(err).Error("ssh pty change windows size failed")
}
}
case wsMsgCmd:
//handle xterm.js stdin
//decodeBytes, err := base64.StdEncoding.DecodeString(msgObj.Cmd)
// handle xterm.js stdin
// decodeBytes, err := base64.StdEncoding.DecodeString(msgObj.Cmd)
decodeBytes := []byte(msgObj.Cmd)
if err != nil {
logrus.WithError(err).Error("websock cmd string base64 decoding failed")
@@ -243,7 +241,7 @@ func (ssConn *SshConn) ReceiveWsMsg(wsConn *websocket.Conn, logBuff *bytes.Buffe
if _, err := ssConn.StdinPipe.Write(decodeBytes); err != nil {
logrus.WithError(err).Error("ws cmd bytes write to ssh.stdin pipe failed")
}
//write input cmd to log buffer
// write input cmd to log buffer
if _, err := logBuff.Write(decodeBytes); err != nil {
logrus.WithError(err).Error("write received cmd into log buffer failed")
}
@@ -253,17 +251,17 @@ func (ssConn *SshConn) ReceiveWsMsg(wsConn *websocket.Conn, logBuff *bytes.Buffe
}
func (ssConn *SshConn) SendComboOutput(wsConn *websocket.Conn, exitCh chan bool) {
//tells other go routine quit
//defer setQuit(exitCh)
// tells other go routine quit
// defer setQuit(exitCh)
//every 120ms write combine output bytes into websocket response
// every 120ms write combine output bytes into websocket response
tick := time.NewTicker(time.Millisecond * time.Duration(120))
//for range time.Tick(120 * time.Millisecond){}
// for range time.Tick(120 * time.Millisecond){}
defer tick.Stop()
for {
select {
case <-tick.C:
//write combine output bytes into websocket response
// write combine output bytes into websocket response
if err := flushComboOutput(ssConn.ComboOutput, wsConn); err != nil {
logrus.WithError(err).Error("ssh sending combo output to webSocket failed")
return
@@ -273,6 +271,7 @@ func (ssConn *SshConn) SendComboOutput(wsConn *websocket.Conn, exitCh chan bool)
}
}
}
func flushComboOutput(w *wsBufferWriter, wsConn *websocket.Conn) error {
if w.buffer.Len() != 0 {
err := wsConn.WriteMessage(websocket.TextMessage, w.buffer.Bytes())
@@ -284,16 +283,16 @@ func flushComboOutput(w *wsBufferWriter, wsConn *websocket.Conn) error {
return nil
}
//ReceiveWsMsg receive websocket msg do some handling then write into ssh.session.stdin
// ReceiveWsMsg receive websocket msg do some handling then write into ssh.session.stdin
func (ssConn *SshConn) Login(wsConn *websocket.Conn, logBuff *bytes.Buffer, exitCh chan bool) {
//tells other go routine quit
// tells other go routine quit
defer setQuit(exitCh)
for {
select {
case <-exitCh:
return
default:
//read websocket msg
// read websocket msg
_, wsData, err := wsConn.ReadMessage()
if err != nil {
logrus.WithError(err).Error("reading webSocket message failed")
@@ -317,15 +316,15 @@ func (ssConn *SshConn) Login(wsConn *websocket.Conn, logBuff *bytes.Buffer, exit
switch msgObj.Type {
case wsMsgResize:
//handle xterm.js size change
// handle xterm.js size change
if msgObj.Cols > 0 && msgObj.Rows > 0 {
if err := ssConn.Session.WindowChange(msgObj.Rows, msgObj.Cols); err != nil {
logrus.WithError(err).Error("ssh pty change windows size failed")
}
}
case wsMsgCmd:
//handle xterm.js stdin
//decodeBytes, err := base64.StdEncoding.DecodeString(msgObj.Cmd)
// handle xterm.js stdin
// decodeBytes, err := base64.StdEncoding.DecodeString(msgObj.Cmd)
decodeBytes := []byte(msgObj.Cmd)
if err != nil {
logrus.WithError(err).Error("websock cmd string base64 decoding failed")
@@ -333,7 +332,7 @@ func (ssConn *SshConn) Login(wsConn *websocket.Conn, logBuff *bytes.Buffer, exit
if _, err := ssConn.StdinPipe.Write(decodeBytes); err != nil {
logrus.WithError(err).Error("ws cmd bytes write to ssh.stdin pipe failed")
}
//write input cmd to log buffer
// write input cmd to log buffer
if _, err := logBuff.Write(decodeBytes); err != nil {
logrus.WithError(err).Error("write received cmd into log buffer failed")
}
@@ -341,6 +340,7 @@ func (ssConn *SshConn) Login(wsConn *websocket.Conn, logBuff *bytes.Buffer, exit
}
}
}
func (ssConn *SshConn) SessionWait(quitChan chan bool) {
if err := ssConn.Session.Wait(); err != nil {
logrus.WithError(err).Error("ssh session wait failed")
@@ -395,7 +395,7 @@ func WsReaderCopy(reader *websocket.Conn, writer io.Writer) {
if err = json2.Unmarshal(p, &msgObj); err != nil {
writer.Write(p)
} else if msgObj.Type == wsMsgResize {
//writer.Write([]byte("stty rows " + strconv.Itoa(msgObj.Rows) + " && stty cols " + strconv.Itoa(msgObj.Cols) + " \r"))
// writer.Write([]byte("stty rows " + strconv.Itoa(msgObj.Rows) + " && stty cols " + strconv.Itoa(msgObj.Cols) + " \r"))
}
}
}

View File

@@ -35,8 +35,8 @@ func ExecResultStrArray(cmdStr string) []string {
fmt.Println(err)
return nil
}
//str, err := ioutil.ReadAll(stdout)
var networklist = []string{}
// str, err := ioutil.ReadAll(stdout)
networklist := []string{}
outputBuf := bufio.NewReader(stdout)
for {
output, _, err := outputBuf.ReadLine()
@@ -54,6 +54,8 @@ func ExecResultStrArray(cmdStr string) []string {
func ExecResultStr(cmdStr string) string {
cmd := exec.Command("/bin/bash", "-c", cmdStr)
println(cmd.String())
stdout, err := cmd.StdoutPipe()
if err != nil {
fmt.Println(err)
@@ -73,7 +75,7 @@ func ExecResultStr(cmdStr string) string {
return string(str)
}
//执行 lsblk 命令
// 执行 lsblk 命令
func ExecLSBLK() []byte {
output, err := exec.Command("lsblk", "-O", "-J", "-b").Output()
if err != nil {
@@ -83,7 +85,7 @@ func ExecLSBLK() []byte {
return output
}
//执行 lsblk 命令
// 执行 lsblk 命令
func ExecLSBLKByPath(path string) []byte {
output, err := exec.Command("lsblk", path, "-O", "-J", "-b").Output()
if err != nil {
@@ -93,7 +95,7 @@ func ExecLSBLKByPath(path string) []byte {
return output
}
//exec smart
// exec smart
func ExecSmartCTLByPath(path string) []byte {
timeout := 3
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
@@ -107,6 +109,5 @@ func ExecSmartCTLByPath(path string) []byte {
}
func ExecEnabledSMART(path string) {
exec.Command("smartctl", "-s on", path).Output()
}

View File

@@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"io"
"io/fs"
"io/ioutil"
"log"
"mime/multipart"
@@ -60,7 +61,7 @@ func MkDir(src string) error {
if err != nil {
return err
}
os.Chmod(src, 0777)
os.Chmod(src, 0o777)
return nil
}
@@ -103,7 +104,7 @@ func MustOpen(fileName, filePath string) (*os.File, error) {
return nil, fmt.Errorf("file.IsNotExistMkDir src: %s, err: %v", src, err)
}
f, err := Open(src+fileName, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0644)
f, err := Open(src+fileName, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0o644)
if err != nil {
return nil, fmt.Errorf("Fail to OpenFile :%v", err)
}
@@ -113,7 +114,7 @@ func MustOpen(fileName, filePath string) (*os.File, error) {
// 判断所给路径文件/文件夹是否存在
func Exists(path string) bool {
_, err := os.Stat(path) //os.Stat获取文件信息
_, err := os.Stat(path) // os.Stat获取文件信息
if err != nil {
if os.IsExist(err) {
return true
@@ -147,7 +148,7 @@ func CreateFile(path string) error {
}
func CreateFileAndWriteContent(path string, content string) error {
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0666)
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0o666)
if err != nil {
return err
}
@@ -163,7 +164,7 @@ func CreateFileAndWriteContent(path string, content string) error {
// IsNotExistMkDir create a directory if it does not exist
func IsNotExistCreateFile(src string) error {
if notExist := CheckNotExist(src); notExist == true {
if notExist := CheckNotExist(src); notExist {
if err := CreateFile(src); err != nil {
return err
}
@@ -267,7 +268,7 @@ func CopySingleFile(src, dst, style string) error {
return os.Chmod(dst, srcinfo.Mode())
}
//Check for duplicate file names
// Check for duplicate file names
func GetNoDuplicateFileName(fullPath string) string {
path, fileName := filepath.Split(fullPath)
fileSuffix := path2.Ext(fileName)
@@ -293,7 +294,7 @@ func CopyDir(src string, dst string, style string) error {
}
return nil
}
//dstPath := dst
// dstPath := dst
lastPath := src[strings.LastIndex(src, "/")+1:]
dst += "/" + lastPath
// for i := 0; Exists(dst); i++ {
@@ -314,7 +315,7 @@ func CopyDir(src string, dst string, style string) error {
}
for _, fd := range fds {
srcfp := path.Join(src, fd.Name())
dstfp := dst //path.Join(dst, fd.Name())
dstfp := dst // path.Join(dst, fd.Name())
if fd.IsDir() {
if err = CopyDir(srcfp, dstfp, style); err != nil {
@@ -336,10 +337,17 @@ func WriteToPath(data []byte, path, name string) error {
} else {
fullPath += "/" + name
}
IsNotExistCreateFile(fullPath)
return WriteToFullPath(data, fullPath, 0o666)
}
func WriteToFullPath(data []byte, fullPath string, perm fs.FileMode) error {
if err := IsNotExistCreateFile(fullPath); err != nil {
return err
}
file, err := os.OpenFile(fullPath,
os.O_WRONLY|os.O_TRUNC|os.O_CREATE,
0666,
perm,
)
if err != nil {
return err
@@ -350,26 +358,31 @@ func WriteToPath(data []byte, path, name string) error {
return err
}
//最终拼接
// 最终拼接
func SpliceFiles(dir, path string, length int, startPoint int) error {
fullPath := path
IsNotExistCreateFile(fullPath)
if err := IsNotExistCreateFile(fullPath); err != nil {
return err
}
file, _ := os.OpenFile(fullPath,
os.O_WRONLY|os.O_TRUNC|os.O_CREATE,
0666,
0o666,
)
defer file.Close()
bufferedWriter := bufio.NewWriter(file)
// todo: here should have a goroutine to remove each partial file after it is read, to save disk space
for i := 0; i < length+startPoint; i++ {
data, err := ioutil.ReadFile(dir + "/" + strconv.Itoa(i+startPoint))
if err != nil {
return err
}
_, err = bufferedWriter.Write(data)
if err != nil {
if _, err := bufferedWriter.Write(data); err != nil { // recommend to use https://github.com/iceber/iouring-go for faster write
return err
}
}
@@ -380,7 +393,6 @@ func SpliceFiles(dir, path string, length int, startPoint int) error {
}
func GetCompressionAlgorithm(t string) (string, archiver.Writer, error) {
switch t {
case "zip", "":
return ".zip", archiver.NewZip(), nil
@@ -400,8 +412,8 @@ func GetCompressionAlgorithm(t string) (string, archiver.Writer, error) {
return "", nil, errors.New("format not implemented")
}
}
func AddFile(ar archiver.Writer, path, commonPath string) error {
func AddFile(ar archiver.Writer, path, commonPath string) error {
info, err := os.Stat(path)
if err != nil {
return err
@@ -447,6 +459,7 @@ func AddFile(ar archiver.Writer, path, commonPath string) error {
return nil
}
func CommonPrefix(sep byte, paths ...string) string {
// Handle special cases.
switch len(paths) {
@@ -513,7 +526,7 @@ func GetFileOrDirSize(path string) (int64, error) {
return fileInfo.Size(), nil
}
//getFileSize get file size by path(B)
// getFileSize get file size by path(B)
func DirSizeB(path string) (int64, error) {
var size int64
err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error {

View File

@@ -1,8 +1,10 @@
package route
import (
"os"
"github.com/IceWhaleTech/CasaOS-Common/middleware"
"github.com/IceWhaleTech/CasaOS-Common/utils/jwt"
"github.com/IceWhaleTech/CasaOS/middleware"
"github.com/IceWhaleTech/CasaOS/pkg/config"
v1 "github.com/IceWhaleTech/CasaOS/route/v1"
@@ -11,13 +13,22 @@ import (
)
func InitRouter() *gin.Engine {
ginMode := gin.ReleaseMode
if config.ServerInfo.RunMode != "" {
ginMode = config.ServerInfo.RunMode
}
if os.Getenv(gin.EnvGinMode) != "" {
ginMode = os.Getenv(gin.EnvGinMode)
}
gin.SetMode(ginMode)
r := gin.Default()
r := gin.New()
r.Use(gin.Recovery())
r.Use(middleware.Cors())
r.Use(middleware.WriteLog())
r.Use(gzip.Gzip(gzip.DefaultCompression))
gin.SetMode(config.ServerInfo.RunMode)
if ginMode != gin.ReleaseMode {
r.Use(middleware.WriteLog())
}
// r.StaticFS("/ui", http.FS(web.Static))
// r.GET("/", WebUIHome)
@@ -35,7 +46,7 @@ func InitRouter() *gin.Engine {
// r.GET("/v1/users/image", v1.GetUserImage)
// r.GET("/v1/users/status", v1.GetUserStatus) //init/check
//r.GET("/v1/guide/check", v1.GetGuideCheck) // /v1/sys/guide_check
// r.GET("/v1/guide/check", v1.GetGuideCheck) // /v1/sys/guide_check
r.GET("/v1/sys/debug", v1.GetSystemConfigDebug) // //debug
r.GET("/v1/sys/socket-port", v1.GetSystemSocketPort) //sys/socket_port
@@ -73,7 +84,7 @@ func InitRouter() *gin.Engine {
v1AppsGroup := v1Group.Group("/apps")
v1AppsGroup.Use()
{
v1AppsGroup.GET("", v1.AppList) //list
v1AppsGroup.GET("", v1.AppList) // list
v1AppsGroup.GET("/:id", v1.AppInfo)
}
v1ContainerGroup := v1Group.Group("/container")
@@ -84,24 +95,24 @@ func InitRouter() *gin.Engine {
v1ContainerGroup.GET("/usage", v1.AppUsageList)
v1ContainerGroup.GET("/:id", v1.ContainerUpdateInfo) ///update/:id/info
v1ContainerGroup.GET("/:id/logs", v1.ContainerLog) // /app/logs/:id
v1ContainerGroup.GET("/networks", v1.GetDockerNetworks) //app/install/config
v1ContainerGroup.GET("/networks", v1.GetDockerNetworks) // app/install/config
v1ContainerGroup.GET("/:id/state", v1.GetContainerState) //app/state/:id ?state=install_progress
v1ContainerGroup.GET("/:id/state", v1.GetContainerState) // app/state/:id ?state=install_progress
// there are problems, temporarily do not deal with
v1ContainerGroup.GET("/:id/terminal", v1.DockerTerminal) //app/terminal/:id
v1ContainerGroup.POST("", v1.InstallApp) //app/install
//v1ContainerGroup.GET("/:id", v1.ContainerInfo) // /app/info/:id
v1ContainerGroup.GET("/:id/terminal", v1.DockerTerminal) // app/terminal/:id
v1ContainerGroup.POST("", v1.InstallApp) // app/install
// v1ContainerGroup.GET("/:id", v1.ContainerInfo) // /app/info/:id
v1ContainerGroup.PUT("/:id", v1.UpdateSetting) ///update/:id/setting
v1ContainerGroup.PUT("/:id/state", v1.ChangAppState) // /app/state/:id
v1ContainerGroup.DELETE("/:id", v1.UnInstallApp) //app/uninstall/:id
//Not used
v1ContainerGroup.DELETE("/:id", v1.UnInstallApp) // app/uninstall/:id
// Not used
v1ContainerGroup.PUT("/:id/latest", v1.PutAppUpdate)
//Not used
// Not used
v1ContainerGroup.POST("/share", v1.ShareAppFile)
v1ContainerGroup.GET("/info", v1.GetcontainerInfo)
v1ContainerGroup.PUT("/info", v1.PutcontainerInfo)
v1ContainerGroup.GET("/info", v1.GetDockerDaemonConfiguration)
v1ContainerGroup.PUT("/info", v1.PutDockerDaemonConfiguration)
}
v1AppCategoriesGroup := v1Group.Group("/app-categories")
@@ -113,19 +124,19 @@ func InitRouter() *gin.Engine {
v1SysGroup := v1Group.Group("/sys")
v1SysGroup.Use()
{
v1SysGroup.GET("/version", v1.GetSystemCheckVersion) //version/check
v1SysGroup.GET("/version", v1.GetSystemCheckVersion) // version/check
v1SysGroup.POST("/update", v1.SystemUpdate)
v1SysGroup.GET("/hardware", v1.GetSystemHardwareInfo) //hardware/info
v1SysGroup.GET("/hardware", v1.GetSystemHardwareInfo) // hardware/info
v1SysGroup.GET("/wsssh", v1.WsSsh)
v1SysGroup.POST("/ssh-login", v1.PostSshLogin)
//v1SysGroup.GET("/config", v1.GetSystemConfig) //delete
//v1SysGroup.POST("/config", v1.PostSetSystemConfig)
v1SysGroup.GET("/logs", v1.GetCasaOSErrorLogs) //error/logs
//v1SysGroup.GET("/widget/config", v1.GetWidgetConfig)//delete
//v1SysGroup.POST("/widget/config", v1.PostSetWidgetConfig)//delete
// v1SysGroup.GET("/config", v1.GetSystemConfig) //delete
// v1SysGroup.POST("/config", v1.PostSetSystemConfig)
v1SysGroup.GET("/logs", v1.GetCasaOSErrorLogs) // error/logs
// v1SysGroup.GET("/widget/config", v1.GetWidgetConfig)//delete
// v1SysGroup.POST("/widget/config", v1.PostSetWidgetConfig)//delete
v1SysGroup.POST("/stop", v1.PostKillCasaOS)
@@ -141,31 +152,31 @@ func InitRouter() *gin.Engine {
v1SysGroup.GET("/server-info", nil)
v1SysGroup.PUT("/server-info", nil)
v1SysGroup.GET("/apps-state", v1.GetSystemAppsStatus)
//v1SysGroup.GET("/port", v1.GetCasaOSPort)
//v1SysGroup.PUT("/port", v1.PutCasaOSPort)
// v1SysGroup.GET("/port", v1.GetCasaOSPort)
// v1SysGroup.PUT("/port", v1.PutCasaOSPort)
v1SysGroup.GET("/proxy", v1.GetSystemProxy)
}
v1PortGroup := v1Group.Group("/port")
v1PortGroup.Use()
{
v1PortGroup.GET("/", v1.GetPort) //app/port
v1PortGroup.GET("/state/:port", v1.PortCheck) //app/check/:port
v1PortGroup.GET("/", v1.GetPort) // app/port
v1PortGroup.GET("/state/:port", v1.PortCheck) // app/check/:port
}
v1FileGroup := v1Group.Group("/file")
v1FileGroup.Use()
{
v1FileGroup.GET("", v1.GetDownloadSingleFile) //download/:path
v1FileGroup.GET("", v1.GetDownloadSingleFile) // download/:path
v1FileGroup.POST("", v1.PostCreateFile)
v1FileGroup.PUT("", v1.PutFileContent)
v1FileGroup.PUT("/name", v1.RenamePath)
//file/rename
v1FileGroup.GET("/content", v1.GetFilerContent) //file/read
// file/rename
v1FileGroup.GET("/content", v1.GetFilerContent) // file/read
//File uploads need to be handled separately, and will not be modified here
// File uploads need to be handled separately, and will not be modified here
v1FileGroup.POST("/upload", v1.PostFileUpload)
v1FileGroup.GET("/upload", v1.GetFileUpload)
//v1FileGroup.GET("/download", v1.UserFileDownloadCommonService)
// v1FileGroup.GET("/download", v1.UserFileDownloadCommonService)
}
v1FolderGroup := v1Group.Group("/folder")
v1FolderGroup.Use()
@@ -178,9 +189,9 @@ func InitRouter() *gin.Engine {
v1BatchGroup.Use()
{
v1BatchGroup.DELETE("", v1.DeleteFile) //file/delete
v1BatchGroup.DELETE("", v1.DeleteFile) // file/delete
v1BatchGroup.DELETE("/:id/task", v1.DeleteOperateFileOrDir)
v1BatchGroup.POST("/task", v1.PostOperateFileOrDir) //file/operate
v1BatchGroup.POST("/task", v1.PostOperateFileOrDir) // file/operate
v1BatchGroup.GET("", v1.GetDownloadFile)
}
v1ImageGroup := v1Group.Group("/image")
@@ -211,7 +222,7 @@ func InitRouter() *gin.Engine {
v1NotifyGroup.Use()
{
v1NotifyGroup.POST("/:path", v1.PostNotifyMessage)
//merge to system
// merge to system
v1NotifyGroup.POST("/system_status", v1.PostSystemStatusNotify)
}
}

View File

@@ -2,8 +2,9 @@ package v1
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"path/filepath"
"strconv"
"github.com/IceWhaleTech/CasaOS/model"
@@ -16,6 +17,11 @@ import (
"github.com/gin-gonic/gin"
)
const (
dockerRootDirFilePath = "/var/lib/casaos/docker_root"
dockerDaemonConfigurationFilePath = "/etc/docker/daemon.json"
)
// @Summary 获取远程列表
// @Produce application/json
// @Accept application/json
@@ -29,8 +35,7 @@ import (
// @Success 200 {string} string "ok"
// @Router /app/list [get]
func AppList(c *gin.Context) {
//service.MyService.Docker().DockerContainerCommit("test2")
// service.MyService.Docker().DockerContainerCommit("test2")
index := c.DefaultQuery("index", "1")
size := c.DefaultQuery("size", "10000")
@@ -139,7 +144,7 @@ func MyAppList(c *gin.Context) {
func AppUsageList(c *gin.Context) {
list := service.MyService.App().GetHardwareUsage()
c.JSON(common_err.SUCCESS, &model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: list})
//c.JSON(common_err.SUCCESS, &model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: nil})
// c.JSON(common_err.SUCCESS, &model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: nil})
}
// @Summary 应用详情
@@ -151,7 +156,6 @@ func AppUsageList(c *gin.Context) {
// @Success 200 {string} string "ok"
// @Router /app/appinfo/{id} [get]
func AppInfo(c *gin.Context) {
id := c.Param("id")
language := c.GetHeader("Language")
info, err := service.MyService.Casa().GetServerAppInfo(id, "", language)
@@ -213,7 +217,7 @@ func AppInfo(c *gin.Context) {
// return c1.Type < c2.Type
// }
//sort
// sort
// if info.NetworkModel != "host" {
// sort.PortsSort(portOrder).Sort(info.Configures.TcpPorts)
// sort.PortsSort(portOrder).Sort(info.Configures.UdpPorts)
@@ -265,53 +269,87 @@ func ShareAppFile(c *gin.Context) {
c.JSON(common_err.SUCCESS, json.RawMessage(content))
}
func GetcontainerInfo(c *gin.Context) {
func GetDockerDaemonConfiguration(c *gin.Context) {
// info, err := service.MyService.Docker().GetDockerInfo()
// if err != nil {
// c.JSON(common_err.SERVICE_ERROR, &model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: err.Error()})
// return
// }
daemon := model.DeckerDaemonModel{}
data := make(map[string]interface{}, 1)
data["docker_root_dir"] = ""
if file.Exists("/etc/docker/daemon.json") {
byteResult := file.ReadFullFile("/etc/docker/daemon.json")
err := json.Unmarshal(byteResult, &daemon)
data := make(map[string]interface{})
if file.Exists(dockerRootDirFilePath) {
buf := file.ReadFullFile(dockerRootDirFilePath)
err := json.Unmarshal(buf, &data)
if err != nil {
c.JSON(common_err.CLIENT_ERROR, &model.Result{Success: common_err.CLIENT_ERROR, Message: common_err.GetMsg(common_err.INVALID_PARAMS), Data: err.Error()})
c.JSON(common_err.CLIENT_ERROR, &model.Result{Success: common_err.CLIENT_ERROR, Message: common_err.GetMsg(common_err.INVALID_PARAMS), Data: err})
return
}
data["docker_root_dir"] = daemon.Graph
}
c.JSON(common_err.SUCCESS, &model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: data})
}
func PutcontainerInfo(c *gin.Context) {
js := make(map[string]interface{})
err := c.BindJSON(&js)
if err != nil {
c.JSON(common_err.CLIENT_ERROR, &model.Result{Success: common_err.CLIENT_ERROR, Message: common_err.GetMsg(common_err.INVALID_PARAMS), Data: err.Error()})
func PutDockerDaemonConfiguration(c *gin.Context) {
request := make(map[string]interface{})
if err := c.BindJSON(&request); err != nil {
c.JSON(http.StatusBadRequest, &model.Result{Success: common_err.CLIENT_ERROR, Message: common_err.GetMsg(common_err.INVALID_PARAMS), Data: err})
return
}
dockerRootDir := js["docker_root_dir"].(string)
daemon := model.DeckerDaemonModel{}
if file.Exists("/etc/docker/daemon.json") {
byteResult := file.ReadFullFile("/etc/docker/daemon.json")
err := json.Unmarshal(byteResult, &daemon)
value, ok := request["docker_root_dir"]
if !ok {
c.JSON(http.StatusBadRequest, &model.Result{Success: common_err.CLIENT_ERROR, Message: common_err.GetMsg(common_err.INVALID_PARAMS), Data: "`docker_root_dir` should not empty"})
return
}
dockerConfig := model.DockerDaemonConfigurationModel{}
if file.Exists(dockerDaemonConfigurationFilePath) {
byteResult := file.ReadFullFile(dockerDaemonConfigurationFilePath)
err := json.Unmarshal(byteResult, &dockerConfig)
if err != nil {
c.JSON(common_err.CLIENT_ERROR, &model.Result{Success: common_err.CLIENT_ERROR, Message: common_err.GetMsg(common_err.INVALID_PARAMS), Data: err.Error()})
c.JSON(http.StatusInternalServerError, &model.Result{Success: common_err.SERVICE_ERROR, Message: "error when trying to deserialize " + dockerDaemonConfigurationFilePath, Data: err})
return
}
}
if !file.Exists(dockerRootDir) {
c.JSON(common_err.CLIENT_ERROR, &model.Result{Success: common_err.CLIENT_ERROR, Message: common_err.GetMsg(common_err.DIR_NOT_EXISTS), Data: common_err.GetMsg(common_err.DIR_NOT_EXISTS)})
return
dockerRootDir := value.(string)
if dockerRootDir == "/" {
dockerConfig.Root = "" // omitempty - empty string will not be serialized
} else {
if !file.Exists(dockerRootDir) {
c.JSON(http.StatusBadRequest, &model.Result{Success: common_err.CLIENT_ERROR, Message: common_err.GetMsg(common_err.DIR_NOT_EXISTS), Data: common_err.GetMsg(common_err.DIR_NOT_EXISTS)})
return
}
dockerConfig.Root = filepath.Join(dockerRootDir, "docker")
if err := file.IsNotExistMkDir(dockerConfig.Root); err != nil {
c.JSON(http.StatusInternalServerError, &model.Result{Success: common_err.SERVICE_ERROR, Message: "error when trying to create " + dockerConfig.Root, Data: err})
return
}
}
daemon.Graph = dockerRootDir
byteMode, _ := json.Marshal(daemon)
file.WriteToPath(byteMode, "/etc/docker", "daemon.json")
fmt.Println(command.ExecResultStr("systemctl daemon-reload"))
fmt.Println(command.ExecResultStr("systemctl restart docker"))
if buf, err := json.Marshal(request); err != nil {
c.JSON(http.StatusBadRequest, &model.Result{Success: common_err.CLIENT_ERROR, Message: "error when trying to serialize docker root json", Data: err})
return
} else {
if err := file.WriteToFullPath(buf, dockerRootDirFilePath, 0o644); err != nil {
c.JSON(http.StatusInternalServerError, &model.Result{Success: common_err.SERVICE_ERROR, Message: "error when trying to write " + dockerRootDirFilePath, Data: err})
return
}
}
c.JSON(common_err.SUCCESS, &model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: js})
if buf, err := json.Marshal(dockerConfig); err != nil {
c.JSON(http.StatusBadRequest, &model.Result{Success: common_err.CLIENT_ERROR, Message: "error when trying to serialize docker config", Data: dockerConfig})
return
} else {
if err := file.WriteToFullPath(buf, dockerDaemonConfigurationFilePath, 0o644); err != nil {
c.JSON(http.StatusInternalServerError, &model.Result{Success: common_err.SERVICE_ERROR, Message: "error when trying to write to " + dockerDaemonConfigurationFilePath, Data: err})
return
}
}
println(command.ExecResultStr("systemctl daemon-reload"))
println(command.ExecResultStr("systemctl restart docker"))
c.JSON(http.StatusOK, &model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: request})
}

View File

@@ -18,9 +18,11 @@ import (
"github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/pkg/utils/common_err"
"github.com/IceWhaleTech/CasaOS/pkg/utils/file"
"github.com/IceWhaleTech/CasaOS/pkg/utils/loger"
"github.com/IceWhaleTech/CasaOS/service"
"github.com/gin-gonic/gin"
uuid "github.com/satori/go.uuid"
"go.uber.org/zap"
)
// @Summary 读取文件
@@ -47,7 +49,7 @@ func GetFilerContent(c *gin.Context) {
})
return
}
//文件读取任务是将文件内容读取到内存中。
// 文件读取任务是将文件内容读取到内存中。
info, err := ioutil.ReadFile(filePath)
if err != nil {
c.JSON(common_err.SERVICE_ERROR, model.Result{
@@ -83,7 +85,6 @@ func GetLocalFile(c *gin.Context) {
return
}
c.File(path)
return
}
// @Summary download
@@ -96,7 +97,6 @@ func GetLocalFile(c *gin.Context) {
// @Success 200 {string} string "ok"
// @Router /file/download [get]
func GetDownloadFile(c *gin.Context) {
t := c.Query("format")
files := c.Query("files")
@@ -135,11 +135,11 @@ func GetDownloadFile(c *gin.Context) {
}
if !info.IsDir() {
//打开文件
// 打开文件
fileTmp, _ := os.Open(filePath)
defer fileTmp.Close()
//获取文件的名称
// 获取文件的名称
fileName := path.Base(filePath)
c.Header("Content-Disposition", "attachment; filename*=utf-8''"+url2.PathEscape(fileName))
c.File(filePath)
@@ -179,7 +179,6 @@ func GetDownloadFile(c *gin.Context) {
log.Printf("Failed to archive %s: %v", fname, err)
}
}
}
func GetDownloadSingleFile(c *gin.Context) {
@@ -202,7 +201,7 @@ func GetDownloadSingleFile(c *gin.Context) {
defer fileTmp.Close()
fileName := path.Base(filePath)
//c.Header("Content-Disposition", "inline")
// c.Header("Content-Disposition", "inline")
c.Header("Content-Disposition", "attachment; filename*=utf-8''"+url2.PathEscape(fileName))
c.File(filePath)
}
@@ -248,7 +247,7 @@ func DirPath(c *gin.Context) {
info[i].Extensions = ex
}
}
//Hide the files or folders in operation
// Hide the files or folders in operation
fileQueue := make(map[string]string)
if len(service.OpStrArr) > 0 {
for _, v := range service.OpStrArr {
@@ -361,7 +360,6 @@ func PostCreateFile(c *gin.Context) {
// @Success 200 {string} string "ok"
// @Router /file/upload [get]
func GetFileUpload(c *gin.Context) {
relative := c.Query("relativePath")
fileName := c.Query("filename")
chunkNumber := c.Query("chunkNumber")
@@ -405,55 +403,92 @@ func PostFileUpload(c *gin.Context) {
hash := file.GetHashByContent([]byte(fileName))
if len(path) == 0 {
c.JSON(common_err.INVALID_PARAMS, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS)})
loger.Error("path should not be empty")
c.JSON(http.StatusBadRequest, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS)})
return
}
tempDir := filepath.Join(path, ".temp", hash+strconv.Itoa(totalChunks)) + "/"
if fileName != relative {
dirPath = strings.TrimSuffix(relative, fileName)
tempDir += dirPath
file.MkDir(path + "/" + dirPath)
if err := file.MkDir(path + "/" + dirPath); err != nil {
loger.Error("error when trying to create `"+path+"/"+dirPath+"`", zap.Error(err))
c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.SERVICE_ERROR, Message: err.Error()})
return
}
}
path += "/" + relative
if !file.CheckNotExist(tempDir + chunkNumber) {
file.RMDir(tempDir + chunkNumber)
if err := file.RMDir(tempDir + chunkNumber); err != nil {
loger.Error("error when trying to remove existing `"+tempDir+chunkNumber+"`", zap.Error(err))
c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.SERVICE_ERROR, Message: err.Error()})
return
}
}
if totalChunks > 1 {
file.IsNotExistMkDir(tempDir)
out, _ := os.OpenFile(tempDir+chunkNumber, os.O_WRONLY|os.O_CREATE, 0644)
defer out.Close()
_, err := io.Copy(out, f)
if err != nil {
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: err.Error()})
if err := file.IsNotExistMkDir(tempDir); err != nil {
loger.Error("error when trying to create `"+tempDir+"`", zap.Error(err))
c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.SERVICE_ERROR, Message: err.Error()})
return
}
out, err := os.OpenFile(tempDir+chunkNumber, os.O_WRONLY|os.O_CREATE, 0o644)
if err != nil {
loger.Error("error when trying to open `"+tempDir+chunkNumber+"` for creation", zap.Error(err))
c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.SERVICE_ERROR, Message: err.Error()})
return
}
defer out.Close()
if _, err := io.Copy(out, f); err != nil { // recommend to use https://github.com/iceber/iouring-go for faster copy
loger.Error("error when trying to write to `"+tempDir+chunkNumber+"`", zap.Error(err))
c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.SERVICE_ERROR, Message: err.Error()})
return
}
fileNum, err := ioutil.ReadDir(tempDir)
if err != nil {
loger.Error("error when trying to read number of files under `"+tempDir+"`", zap.Error(err))
c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.SERVICE_ERROR, Message: err.Error()})
return
}
if totalChunks == len(fileNum) {
if err := file.SpliceFiles(tempDir, path, totalChunks, 1); err != nil {
loger.Error("error when trying to splice files under `"+tempDir+"`", zap.Error(err))
c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.SERVICE_ERROR, Message: err.Error()})
return
}
if err := file.RMDir(tempDir); err != nil {
loger.Error("error when trying to remove `"+tempDir+"`", zap.Error(err))
c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.SERVICE_ERROR, Message: err.Error()})
return
}
}
} else {
out, _ := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0644)
defer out.Close()
_, err := io.Copy(out, f)
out, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0o644)
if err != nil {
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: err.Error()})
loger.Error("error when trying to open `"+path+"` for creation", zap.Error(err))
c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.SERVICE_ERROR, Message: err.Error()})
return
}
c.JSON(http.StatusOK, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS)})
return
}
fileNum, err := ioutil.ReadDir(tempDir)
if err != nil {
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: err.Error()})
return
}
if totalChunks == len(fileNum) {
file.SpliceFiles(tempDir, path, totalChunks, 1)
file.RMDir(tempDir)
}
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS)})
defer out.Close()
if _, err := io.Copy(out, f); err != nil { // recommend to use https://github.com/iceber/iouring-go for faster copy
loger.Error("error when trying to write to `"+path+"`", zap.Error(err))
c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: err.Error()})
return
}
}
c.JSON(http.StatusOK, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS)})
}
// @Summary copy or move file
@@ -465,7 +500,6 @@ func PostFileUpload(c *gin.Context) {
// @Success 200 {string} string "ok"
// @Router /file/operate [post]
func PostOperateFileOrDir(c *gin.Context) {
list := model.FileOperate{}
c.ShouldBind(&list)
@@ -515,7 +549,6 @@ func PostOperateFileOrDir(c *gin.Context) {
// @Success 200 {string} string "ok"
// @Router /file/delete [delete]
func DeleteFile(c *gin.Context) {
paths := []string{}
c.ShouldBind(&paths)
if len(paths) == 0 {
@@ -547,7 +580,6 @@ func DeleteFile(c *gin.Context) {
// @Success 200 {string} string "ok"
// @Router /file/update [put]
func PutFileContent(c *gin.Context) {
fi := model.FileUpdate{}
c.ShouldBind(&fi)
@@ -557,7 +589,7 @@ func PutFileContent(c *gin.Context) {
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.FILE_ALREADY_EXISTS, Message: common_err.GetMsg(common_err.FILE_ALREADY_EXISTS)})
return
}
//err := os.Remove(path)
// err := os.Remove(path)
err := os.RemoveAll(fi.FilePath)
if err != nil {
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.FILE_DELETE_ERROR, Message: common_err.GetMsg(common_err.FILE_DELETE_ERROR), Data: err})