diff --git a/CHANGELOG.md b/CHANGELOG.md index d64918d..f78ec4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +## [0.3.5-alpha] - 2022-08-08 + +### Added + +- [File] Condivisione samba montata +- [File] Condivisione dei file tramite samba + +### Changed + +- [Disk] Supporto per il montaggio di dischi dati esistenti + +### Fixed + +- fixed uninstalling imported docker container apps results in wiping ALL your config data from them ([#360](https://github.com/IceWhaleTech/CasaOS/issues/360)) + ## [0.3.4] - 2022-07-29(UTC) ### Added diff --git a/go.mod b/go.mod index 47e365e..550dbc5 100644 --- a/go.mod +++ b/go.mod @@ -27,6 +27,7 @@ require ( github.com/gogo/googleapis v1.4.1 // indirect github.com/golang-jwt/jwt/v4 v4.4.1 github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/mock v1.6.0 github.com/gomodule/redigo v1.8.5 github.com/google/go-github/v36 v36.0.0 github.com/google/uuid v1.3.0 // indirect @@ -56,6 +57,7 @@ require ( github.com/sirupsen/logrus v1.8.1 github.com/smartystreets/assertions v1.2.0 // indirect github.com/smartystreets/goconvey v1.6.4 // indirect + github.com/stretchr/testify v1.7.0 github.com/tidwall/gjson v1.10.2 github.com/tklauser/go-sysconf v0.3.6 // indirect github.com/ugorji/go v1.2.6 // indirect diff --git a/main.go b/main.go index 932f28d..e1ec179 100644 --- a/main.go +++ b/main.go @@ -15,6 +15,7 @@ import ( "github.com/IceWhaleTech/CasaOS/pkg/utils/random" "github.com/IceWhaleTech/CasaOS/route" "github.com/IceWhaleTech/CasaOS/service" + "github.com/IceWhaleTech/CasaOS/types" "github.com/robfig/cron" "gorm.io/gorm" @@ -26,21 +27,31 @@ var configFlag = flag.String("c", "", "config address") var dbFlag = flag.String("db", "", "db path") var resetUser = flag.Bool("ru", false, "reset user") var user = flag.String("user", "", "user name") +var version = flag.Bool("v", false, "show version") func init() { flag.Parse() + if *version { + fmt.Println("v" + types.CURRENTVERSION) + return + } config.InitSetup(*configFlag) config.UpdateSetup() + loger.LogInit() if len(*dbFlag) == 0 { *dbFlag = config.AppInfo.DBPath + "/db" } + sqliteDB = sqlite.GetDb(*dbFlag) //gredis.GetRedisConn(config.RedisInfo), + service.MyService = service.NewService(sqliteDB) + service.Cache = cache.Init() service.GetToken() + service.NewVersionApp = make(map[string]string) route.InitFunction() @@ -62,6 +73,9 @@ func init() { // @BasePath /v1 func main() { service.NotifyMsg = make(chan notify.Message, 10) + if *version { + return + } if *resetUser { if user == nil || len(*user) == 0 { fmt.Println("user is empty") diff --git a/model/connections.go b/model/connections.go index c0ab07b..84719ab 100644 --- a/model/connections.go +++ b/model/connections.go @@ -2,7 +2,7 @@ * @Author: LinkLeong link@icewhale.org * @Date: 2022-07-27 10:30:43 * @LastEditors: LinkLeong - * @LastEditTime: 2022-07-27 11:36:08 + * @LastEditTime: 2022-08-04 20:06:04 * @FilePath: /CasaOS/model/connections.go * @Description: * @Website: https://www.casaos.io @@ -16,6 +16,5 @@ type Connections struct { Password string `json:"password,omitempty"` Host string `json:"host"` Port string `json:"port"` - Directory string `json:"directory"` MountPoint string `json:"mount_point"` } diff --git a/model/disk.go b/model/disk.go index c573e13..75d3bc5 100644 --- a/model/disk.go +++ b/model/disk.go @@ -2,7 +2,7 @@ * @Author: LinkLeong link@icewhale.com * @Date: 2022-07-13 10:43:45 * @LastEditors: LinkLeong - * @LastEditTime: 2022-07-29 14:21:59 + * @LastEditTime: 2022-08-03 14:45:35 * @FilePath: /CasaOS/model/disk.go * @Description: * @Website: https://www.casaos.io @@ -61,16 +61,21 @@ type Drive struct { } type DriveUSB struct { - Name string `json:"name"` - Size uint64 `json:"size"` - Used uint64 `json:"use"` - Model string `json:"model"` - Mount bool `json:"mount"` - Avail uint64 `json:"avail"` + Name string `json:"name"` + Size uint64 `json:"size"` + Model string `json:"model"` + Avail uint64 `json:"avail"` + Children []USBChildren `json:"children"` +} +type USBChildren struct { + Name string `json:"name"` + Size uint64 `json:"size"` + Avail uint64 `json:"avail"` + MountPoint string `json:"mount_point"` } type Storage struct { - MountPoint string `json:"mountpoint"` + MountPoint string `json:"mount_point"` Size string `json:"size"` Avail string `json:"avail"` //可用空间 Type string `json:"type"` diff --git a/model/zima.go b/model/zima.go index 5e8e664..e144b7c 100644 --- a/model/zima.go +++ b/model/zima.go @@ -2,7 +2,7 @@ * @Author: LinkLeong link@icewhale.org * @Date: 2022-05-13 18:15:46 * @LastEditors: LinkLeong - * @LastEditTime: 2022-07-28 13:47:00 + * @LastEditTime: 2022-08-01 18:32:57 * @FilePath: /CasaOS/model/zima.go * @Description: * @Website: https://www.casaos.io @@ -13,13 +13,13 @@ package model import "time" type Path struct { - Name string `json:"name"` //File name or document name - Path string `json:"path"` //Full path to file or folder - IsDir bool `json:"is_dir"` //Is it a folder - Date time.Time `json:"date"` - Size int64 `json:"size"` //File Size - Type string `json:"type,omitempty"` - Label string `json:"label,omitempty"` - Write bool `json:"write"` - Extensions map[string]string `json:"extensions"` + Name string `json:"name"` //File name or document name + Path string `json:"path"` //Full path to file or folder + IsDir bool `json:"is_dir"` //Is it a folder + Date time.Time `json:"date"` + Size int64 `json:"size"` //File Size + Type string `json:"type,omitempty"` + Label string `json:"label,omitempty"` + Write bool `json:"write"` + Extensions map[string]interface{} `json:"extensions"` } diff --git a/pkg/samba/smaba.go b/pkg/samba/smaba.go index 0d35e0b..58a9874 100644 --- a/pkg/samba/smaba.go +++ b/pkg/samba/smaba.go @@ -2,7 +2,7 @@ * @Author: LinkLeong link@icewhale.org * @Date: 2022-07-27 10:35:29 * @LastEditors: LinkLeong - * @LastEditTime: 2022-07-27 10:40:26 + * @LastEditTime: 2022-08-01 13:56:44 * @FilePath: /CasaOS/pkg/samba/smaba.go * @Description: * @Website: https://www.casaos.io @@ -47,3 +47,29 @@ func ConnectSambaService(host, port, username, password, directory string) error } return errors.New("directory not found") } + +//get share name list +func GetSambaSharesList(host, port, username, password string) ([]string, error) { + conn, err := net.Dial("tcp", host+":"+port) + if err != nil { + return nil, err + } + defer conn.Close() + d := &smb2.Dialer{ + Initiator: &smb2.NTLMInitiator{ + User: username, + Password: password, + }, + } + + s, err := d.Dial(conn) + if err != nil { + return nil, err + } + defer s.Logoff() + names, err := s.ListSharenames() + if err != nil { + return nil, err + } + return names, err +} diff --git a/route/init.go b/route/init.go index c3a1591..19e4e20 100644 --- a/route/init.go +++ b/route/init.go @@ -1,11 +1,13 @@ package route import ( + "fmt" "os" "strconv" "strings" "github.com/IceWhaleTech/CasaOS/pkg/config" + "github.com/IceWhaleTech/CasaOS/pkg/samba" "github.com/IceWhaleTech/CasaOS/pkg/utils/command" "github.com/IceWhaleTech/CasaOS/pkg/utils/encryption" "github.com/IceWhaleTech/CasaOS/pkg/utils/file" @@ -17,13 +19,12 @@ import ( func InitFunction() { ShellInit() CheckSerialDiskMount() - CheckToken2_11() ImportApplications() // Soon to be removed ChangeAPIUrl() - MoveUserToDB() + InitNetworkMount() } func CheckSerialDiskMount() { @@ -40,25 +41,25 @@ func CheckSerialDiskMount() { command.ExecEnabledSMART(v.Path) if v.Children != nil { for _, h := range v.Children { - if len(h.MountPoint) == 0 && len(v.Children) == 1 && h.FsType == "ext4" { - if m, ok := mountPoint[h.UUID]; ok { - //mount point check - volume := m - if !file.CheckNotExist(m) { - for i := 0; file.CheckNotExist(volume); i++ { - volume = m + strconv.Itoa(i+1) - } + //if len(h.MountPoint) == 0 && len(v.Children) == 1 && h.FsType == "ext4" { + if m, ok := mountPoint[h.UUID]; ok { + //mount point check + volume := m + if !file.CheckNotExist(m) { + for i := 0; file.CheckNotExist(volume); i++ { + volume = m + strconv.Itoa(i+1) } - service.MyService.Disk().MountDisk(h.Path, volume) - if volume != m { - ms := model2.SerialDisk{} - ms.UUID = v.UUID - ms.MountPoint = volume - service.MyService.Disk().UpdateMountPoint(ms) - } - } + service.MyService.Disk().MountDisk(h.Path, volume) + if volume != m { + ms := model2.SerialDisk{} + ms.UUID = v.UUID + ms.MountPoint = volume + service.MyService.Disk().UpdateMountPoint(ms) + } + } + //} } } } @@ -138,3 +139,32 @@ func MoveUserToDB() { } } + +func InitNetworkMount() { + connections := service.MyService.Connections().GetConnectionsList() + for _, v := range connections { + connection := service.MyService.Connections().GetConnectionByID(fmt.Sprint(v.ID)) + directories, err := samba.GetSambaSharesList(connection.Host, connection.Port, connection.Username, connection.Password) + if err != nil { + service.MyService.Connections().DeleteConnection(fmt.Sprint(connection.ID)) + continue + } + baseHostPath := "/mnt/" + connection.Host + + mountPointList := service.MyService.System().GetDirPath(baseHostPath) + for _, v := range mountPointList { + service.MyService.Connections().UnmountSmaba(v.Path) + } + + os.RemoveAll(baseHostPath) + + file.IsNotExistMkDir(baseHostPath) + for _, v := range directories { + mountPoint := baseHostPath + "/" + v + file.IsNotExistMkDir(mountPoint) + service.MyService.Connections().MountSmaba(connection.Username, connection.Host, v, connection.Port, mountPoint, connection.Password) + } + connection.Directories = strings.Join(directories, ",") + service.MyService.Connections().UpdateConnection(&connection) + } +} diff --git a/route/periodical.go b/route/periodical.go index 0740ab2..c3bab59 100644 --- a/route/periodical.go +++ b/route/periodical.go @@ -2,7 +2,7 @@ * @Author: LinkLeong link@icewhale.com * @Date: 2022-07-01 15:11:36 * @LastEditors: LinkLeong - * @LastEditTime: 2022-07-21 15:25:07 + * @LastEditTime: 2022-08-03 14:49:15 * @FilePath: /CasaOS/route/periodical.go * @Description: * @Website: https://www.casaos.io @@ -133,21 +133,14 @@ func SendUSBBySocket() { temp.Model = v.Model temp.Name = v.Name temp.Size = v.Size - mountTemp := true - if len(v.Children) == 0 { - mountTemp = false - } for _, child := range v.Children { if len(child.MountPoint) > 0 { avail, _ := strconv.ParseUint(child.FSAvail, 10, 64) temp.Avail += avail - used, _ := strconv.ParseUint(child.FSUsed, 10, 64) - temp.Used += used - } else { - mountTemp = false + } } - temp.Mount = mountTemp + usb = append(usb, temp) } } @@ -250,21 +243,12 @@ func SendAllHardwareStatusBySocket() { temp.Model = v.Model temp.Name = v.Name temp.Size = v.Size - mountTemp := true - if len(v.Children) == 0 { - mountTemp = false - } for _, child := range v.Children { if len(child.MountPoint) > 0 { avail, _ := strconv.ParseUint(child.FSAvail, 10, 64) temp.Avail += avail - used, _ := strconv.ParseUint(child.FSUsed, 10, 64) - temp.Used += used - } else { - mountTemp = false } } - temp.Mount = mountTemp usb = append(usb, temp) } } diff --git a/route/route.go b/route/route.go index 823aff3..8629bd9 100644 --- a/route/route.go +++ b/route/route.go @@ -196,6 +196,8 @@ func InitRouter() *gin.Engine { //v1DisksGroup.POST("", v1.PostMountDisk) v1DisksGroup.GET("", v1.GetDiskList) + v1DisksGroup.GET("/usb", v1.GetDisksUSBList) + v1DisksGroup.DELETE("/usb", v1.DeleteDiskUSB) // //format storage // v1DiskGroup.POST("/format", v1.PostDiskFormat) diff --git a/route/v1/disk.go b/route/v1/disk.go index a0b7f47..7d5a9c9 100644 --- a/route/v1/disk.go +++ b/route/v1/disk.go @@ -1,7 +1,6 @@ package v1 import ( - "fmt" "net/http" "reflect" "strconv" @@ -45,19 +44,12 @@ func GetDiskList(c *gin.Context) { temp.Model = v.Model temp.Name = v.Name temp.Size = v.Size - mountTemp := true - if len(v.Children) == 0 { - mountTemp = false - } for _, child := range v.Children { if len(child.MountPoint) > 0 { avail, _ := strconv.ParseUint(child.FSAvail, 10, 64) temp.Avail += avail - } else { - mountTemp = false } } - temp.Mount = mountTemp data = append(data, temp) } } @@ -189,6 +181,60 @@ func GetDiskList(c *gin.Context) { c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: data}) } +// @Summary disk list +// @Produce application/json +// @Accept application/json +// @Tags disk +// @Security ApiKeyAuth +// @Success 200 {string} string "ok" +// @Router /disk/list [get] +func GetDisksUSBList(c *gin.Context) { + list := service.MyService.Disk().LSBLK(false) + data := []model.DriveUSB{} + for _, v := range list { + if v.Tran == "usb" { + temp := model.DriveUSB{} + temp.Model = v.Model + temp.Name = v.Label + if temp.Name == "" { + temp.Name = v.Name + } + temp.Size = v.Size + children := []model.USBChildren{} + for _, child := range v.Children { + + if len(child.MountPoint) > 0 { + tempChildren := model.USBChildren{} + tempChildren.MountPoint = child.MountPoint + tempChildren.Size, _ = strconv.ParseUint(child.FSSize, 10, 64) + tempChildren.Avail, _ = strconv.ParseUint(child.FSAvail, 10, 64) + tempChildren.Name = child.Label + avail, _ := strconv.ParseUint(child.FSAvail, 10, 64) + children = append(children, tempChildren) + temp.Avail += avail + } + } + + temp.Children = children + data = append(data, temp) + } + } + c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: data}) + +} + +func DeleteDiskUSB(c *gin.Context) { + js := make(map[string]string) + c.ShouldBind(&js) + mountPoint := js["mount_point"] + if file.CheckNotExist(mountPoint) { + c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.DIR_NOT_EXISTS, Message: common_err.GetMsg(common_err.DIR_NOT_EXISTS)}) + return + } + service.MyService.Disk().UmountUSB(mountPoint) + c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: mountPoint}) +} + // @Summary get disk list // @Produce application/json // @Accept application/json @@ -274,10 +320,10 @@ func PostDiskAddPartition(c *gin.Context) { js := make(map[string]interface{}) c.ShouldBind(&js) path := js["path"].(string) - name := js["name"].(string) + //name := js["name"].(string) format := js["format"].(bool) - if len(name) == 0 || len(path) == 0 { + if len(path) == 0 { c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS)}) return } @@ -285,11 +331,14 @@ func PostDiskAddPartition(c *gin.Context) { c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.DISK_BUSYING, Message: common_err.GetMsg(common_err.DISK_BUSYING)}) return } - if !file.CheckNotExist("/DATA/" + name) { - // /mnt/name exist - c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.NAME_NOT_AVAILABLE, Message: common_err.GetMsg(common_err.NAME_NOT_AVAILABLE)}) - return - } + + //diskInfo := service.MyService.Disk().GetDiskInfo(path) + + // if !file.CheckNotExist("/DATA/" + name) { + // // /mnt/name exist + // c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.NAME_NOT_AVAILABLE, Message: common_err.GetMsg(common_err.NAME_NOT_AVAILABLE)}) + // return + // } diskMap[path] = "busying" currentDisk := service.MyService.Disk().GetDiskInfo(path) if format { @@ -302,33 +351,43 @@ func PostDiskAddPartition(c *gin.Context) { service.MyService.Disk().AddPartition(path) } - formatBool := true - for formatBool { - currentDisk = service.MyService.Disk().GetDiskInfo(path) - fmt.Println(currentDisk.Children) - if len(currentDisk.Children) > 0 { - formatBool = false - break - } - time.Sleep(time.Second) - } + // formatBool := true + // for formatBool { + // currentDisk = service.MyService.Disk().GetDiskInfo(path) + // if len(currentDisk.Children) > 0 { + // formatBool = false + // break + // } + // time.Sleep(time.Second) + // } currentDisk = service.MyService.Disk().GetDiskInfo(path) // if len(currentDisk.Children) != 1 { // c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.DISK_NEEDS_FORMAT, Message: common_err.GetMsg(common_err.DISK_NEEDS_FORMAT)}) // return // } for i := 0; i < len(currentDisk.Children); i++ { - mountPath := "/DATA/" + name + childrenName := currentDisk.Children[i].Label + if len(childrenName) == 0 { + childrenName = "Storage_" + currentDisk.Children[i].Name + } + mountPath := "/DATA/" + childrenName + if !file.CheckNotExist(mountPath) { + ls := service.MyService.System().GetDirPath(mountPath) + if len(ls) > 0 { + // exist + c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.NAME_NOT_AVAILABLE, Message: common_err.GetMsg(common_err.NAME_NOT_AVAILABLE)}) + return + } + } m := model2.SerialDisk{} - m.MountPoint = mountPath + strconv.Itoa(i) + m.MountPoint = mountPath m.Path = currentDisk.Children[i].Path m.UUID = currentDisk.Children[i].UUID m.State = 0 m.CreatedAt = time.Now().Unix() service.MyService.Disk().SaveMountPoint(m) - //mount dir - service.MyService.Disk().MountDisk(currentDisk.Children[i].Path, mountPath+strconv.Itoa(i)) + service.MyService.Disk().MountDisk(currentDisk.Children[i].Path, mountPath) } service.MyService.Disk().RemoveLSBLKCache() @@ -339,7 +398,7 @@ func PostDiskAddPartition(c *gin.Context) { msg := notify.StorageMessage{} msg.Action = "ADDED" msg.Path = currentDisk.Children[0].Path - msg.Volume = "/DATA/" + name + msg.Volume = "/DATA/" msg.Size = currentDisk.Children[0].Size msg.Type = currentDisk.Children[0].Tran service.MyService.Notify().SendStorageBySocket(msg) diff --git a/route/v1/docker.go b/route/v1/docker.go index 3e15ae1..c04cce8 100644 --- a/route/v1/docker.go +++ b/route/v1/docker.go @@ -209,7 +209,7 @@ func InstallApp(c *gin.Context) { dockerImage = m.Image dockerImageVersion = "latest" } - + m.Image = dockerImage + ":" + dockerImageVersion for _, u := range m.Ports { if u.Protocol == "udp" { @@ -334,11 +334,11 @@ func InstallApp(c *gin.Context) { return } - for !service.MyService.Docker().IsExistImage(dockerImage + ":" + dockerImageVersion) { + for !service.MyService.Docker().IsExistImage(m.Image) { time.Sleep(time.Second) } - _, err = service.MyService.Docker().DockerContainerCreate(dockerImage+":"+dockerImageVersion, m) + _, err = service.MyService.Docker().DockerContainerCreate(m, "") if err != nil { //service.MyService.Redis().Set(id, "{\"id\"\""+id+"\",\"state\":false,\"message\":\""+err.Error()+"\",\"speed\":80}", 100) notify := notify.Application{} @@ -829,7 +829,6 @@ func UpdateSetting(c *gin.Context) { // c.JSON(http.StatusOK, model.Result{Success: common_err.ERROR_APP_NAME_EXIST, Message: common_err.GetMsg(common_err.ERROR_APP_NAME_EXIST)}) // return // } - service.MyService.Docker().DockerContainerStop(id) portMap, _ := strconv.Atoi(m.PortMap) if !port2.IsPortAvailable(portMap, "tcp") { @@ -874,7 +873,7 @@ func UpdateSetting(c *gin.Context) { service.MyService.Docker().DockerContainerUpdateName(id, id) //service.MyService.Docker().DockerContainerRemove(id, true) - containerId, err := service.MyService.Docker().DockerContainerCreate(m.Image, m) + containerId, err := service.MyService.Docker().DockerContainerCreate(m, id) if err != nil { service.MyService.Docker().DockerContainerUpdateName(m.ContainerName, id) service.MyService.Docker().DockerContainerStart(id) diff --git a/route/v1/file.go b/route/v1/file.go index 44a0d0f..0b2dbc7 100644 --- a/route/v1/file.go +++ b/route/v1/file.go @@ -1,6 +1,7 @@ package v1 import ( + "fmt" "io" "io/ioutil" "log" @@ -221,7 +222,7 @@ func DirPath(c *gin.Context) { shares := service.MyService.Shares().GetSharesList() sharesMap := make(map[string]string) for _, v := range shares { - sharesMap[v.Path] = v.Name + sharesMap[v.Path] = fmt.Sprint(v.ID) } if path == "/DATA/AppData" { list := service.MyService.Docker().DockerContainerList() @@ -234,11 +235,6 @@ func DirPath(c *gin.Context) { info[i].Label = v info[i].Type = "application" } - if _, ok := sharesMap[info[i].Path]; ok { - ex := make(map[string]string) - ex["shared"] = "true" - info[i].Extensions = ex - } } } else if path == "/DATA" { disk := make(map[string]string) @@ -265,14 +261,19 @@ func DirPath(c *gin.Context) { if v, ok := disk[info[i].Path]; ok { info[i].Type = v } - if _, ok := sharesMap[info[i].Path]; ok { - ex := make(map[string]string) - ex["shared"] = "true" - info[i].Extensions = ex - } } } + for i := 0; i < len(info); i++ { + if v, ok := sharesMap[info[i].Path]; ok { + ex := make(map[string]interface{}) + shareEx := make(map[string]string) + shareEx["shared"] = "true" + shareEx["id"] = v + ex["share"] = shareEx + info[i].Extensions = ex + } + } //Hide the files or folders in operation fileQueue := make(map[string]string) if len(service.OpStrArr) > 0 { diff --git a/route/v1/samba.go b/route/v1/samba.go index d330684..5bb3dc2 100644 --- a/route/v1/samba.go +++ b/route/v1/samba.go @@ -2,7 +2,7 @@ * @Author: LinkLeong link@icewhale.com * @Date: 2022-07-26 11:08:48 * @LastEditors: LinkLeong - * @LastEditTime: 2022-07-28 11:51:03 + * @LastEditTime: 2022-08-05 12:16:39 * @FilePath: /CasaOS/route/v1/samba.go * @Description: * @Website: https://www.casaos.io @@ -12,6 +12,7 @@ package v1 import ( "fmt" + "os" "path/filepath" "strings" @@ -108,10 +109,9 @@ func GetSambaConnectionsList(c *gin.Context) { connectionList = append(connectionList, model.Connections{ ID: v.ID, Username: v.Username, - MountPoint: v.MountPoint, - Directory: v.Directory, Port: v.Port, Host: v.Host, + MountPoint: v.MountPoint, }) } c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: connectionList}) @@ -119,29 +119,24 @@ func GetSambaConnectionsList(c *gin.Context) { func PostSambaConnectionsCreate(c *gin.Context) { connection := model.Connections{} - err := c.ShouldBindJSON(&connection) - fmt.Println(err) + c.ShouldBindJSON(&connection) if connection.Port == "" { connection.Port = "445" } - if connection.Username == "" || connection.Directory == "" || connection.Host == "" || connection.MountPoint == "" { + if connection.Username == "" || connection.Host == "" { c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS)}) return } // check is exists - connections := service.MyService.Connections().GetConnectionByDirectory(connection.Directory) + connections := service.MyService.Connections().GetConnectionByHost(connection.Host) if len(connections) > 0 { - for _, v := range connections { - if v.Host == connection.Host { - c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.Record_ALREADY_EXIST, Message: common_err.GetMsg(common_err.Record_ALREADY_EXIST), Data: common_err.GetMsg(common_err.Record_ALREADY_EXIST)}) - return - } - } + c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.Record_ALREADY_EXIST, Message: common_err.GetMsg(common_err.Record_ALREADY_EXIST), Data: common_err.GetMsg(common_err.Record_ALREADY_EXIST)}) + return } // check connect is ok - if err := samba.ConnectSambaService(connection.Host, connection.Port, connection.Username, connection.Password, connection.Directory); err != nil { - fmt.Println("check", err) + directories, err := samba.GetSambaSharesList(connection.Host, connection.Port, connection.Username, connection.Password) + 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 } @@ -149,13 +144,21 @@ func PostSambaConnectionsCreate(c *gin.Context) { connectionDBModel := model2.ConnectionsDBModel{} connectionDBModel.Username = connection.Username connectionDBModel.Password = connection.Password - connectionDBModel.Directory = connection.Directory connectionDBModel.Host = connection.Host connectionDBModel.Port = connection.Port - connectionDBModel.MountPoint = connection.MountPoint - file.IsNotExistMkDir(connection.MountPoint) + connectionDBModel.Directories = strings.Join(directories, ",") + baseHostPath := "/mnt/" + connection.Host + connectionDBModel.MountPoint = baseHostPath + connection.MountPoint = baseHostPath + file.IsNotExistMkDir(baseHostPath) + for _, v := range directories { + mountPoint := baseHostPath + "/" + v + file.IsNotExistMkDir(mountPoint) + service.MyService.Connections().MountSmaba(connectionDBModel.Username, connectionDBModel.Host, v, connectionDBModel.Port, mountPoint, connectionDBModel.Password) + } + service.MyService.Connections().CreateConnection(&connectionDBModel) - service.MyService.Connections().MountSmaba(&connectionDBModel) + connection.ID = connectionDBModel.ID c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: connection}) } @@ -167,7 +170,11 @@ func DeleteSambaConnections(c *gin.Context) { c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.Record_NOT_EXIST, Message: common_err.GetMsg(common_err.Record_NOT_EXIST)}) return } - service.MyService.Connections().UnmountSmaba(connection.MountPoint) + mountPointList := service.MyService.System().GetDirPath(connection.MountPoint) + for _, v := range mountPointList { + service.MyService.Connections().UnmountSmaba(v.Path) + } + os.RemoveAll(connection.MountPoint) service.MyService.Connections().DeleteConnection(id) c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: id}) } diff --git a/route/v1/samba_test.go b/route/v1/samba_test.go new file mode 100644 index 0000000..455b973 --- /dev/null +++ b/route/v1/samba_test.go @@ -0,0 +1,73 @@ +/* + * @Author: LinkLeong link@icewhale.org + * @Date: 2022-08-02 15:10:56 + * @LastEditors: LinkLeong + * @LastEditTime: 2022-08-02 16:58:42 + * @FilePath: /CasaOS/route/v1/samba_test.go + * @Description: + * @Website: https://www.casaos.io + * Copyright (c) 2022 by icewhale, All Rights Reserved. + */ +package v1 + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/gin-gonic/gin" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" +) + +func performRequest(r http.Handler, method, path string) *httptest.ResponseRecorder { + req, _ := http.NewRequest(method, path, nil) + w := httptest.NewRecorder() + r.ServeHTTP(w, req) + return w +} + +// func TestHelloWorld(t *testing.T) { +// // Build our expected body +// body := gin.H{ +// "hello": "world", +// } +// // Grab our router +// router := "SetupRouter()" +// // Perform a GET request with that handler. +// w := performRequest(router, "GET", "/") +// // Assert we encoded correctly, +// // the request gives a 200 +// assert.Equal(t, http.StatusOK, w.Code) +// // Convert the JSON response to a map +// var response map[string]string +// err := json.Unmarshal([]byte(w.Body.String()), &response) +// // Grab the value & whether or not it exists +// value, exists := response["hello"] +// // Make some assertions on the correctness of the response. +// assert.Nil(t, err) +// assert.True(t, exists) +// assert.Equal(t, body["hello"], value) +// } + +func TestGetSambaSharesList(t *testing.T) { + gin.SetMode(gin.TestMode) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + executeWithContext := func() *httptest.ResponseRecorder { + response := httptest.NewRecorder() + con, ginEngine := gin.CreateTestContext(response) + + requestUrl := "/v1/samba/shares" + httpRequest, _ := http.NewRequest("GET", requestUrl, nil) + GetSambaSharesList(con) + ginEngine.ServeHTTP(response, httpRequest) + return response + } + + t.Run("Happy", func(t *testing.T) { + res := executeWithContext() + assert.Equal(t, http.StatusOK, res.Code) + }) + +} diff --git a/route/v1/storage.go b/route/v1/storage.go index ff1ab74..d4169cc 100644 --- a/route/v1/storage.go +++ b/route/v1/storage.go @@ -2,7 +2,7 @@ * @Author: LinkLeong link@icewhale.com * @Date: 2022-07-11 16:02:29 * @LastEditors: LinkLeong - * @LastEditTime: 2022-07-29 14:14:17 + * @LastEditTime: 2022-08-04 11:27:25 * @FilePath: /CasaOS/route/v1/storage.go * @Description: * @Website: https://www.casaos.io @@ -13,7 +13,6 @@ package v1 import ( "reflect" "strconv" - "strings" "github.com/IceWhaleTech/CasaOS/model" "github.com/IceWhaleTech/CasaOS/pkg/utils/common_err" @@ -22,14 +21,22 @@ import ( ) func GetStorageList(c *gin.Context) { - + system := c.Query("system") storages := []model.Storages{} disks := service.MyService.Disk().LSBLK(false) diskNumber := 1 children := 1 + findSystem := 0 for _, d := range disks { - children = 1 - if d.Tran == "sata" || d.Tran == "nvme" || d.Tran == "spi" || d.Tran == "sas" || strings.Contains(d.SubSystems, "virtio") || (d.Tran == "ata" && d.Type == "disk") { + if d.Tran != "usb" { + tempSystemDisk := false + children = 1 + tempDisk := model.Storages{ + DiskName: d.Model, + Path: d.Path, + Size: d.Size, + } + storageArr := []model.Storage{} temp := service.MyService.Disk().SmartCTL(d.Path) if reflect.DeepEqual(temp, model.SmartctlA{}) { @@ -37,6 +44,24 @@ func GetStorageList(c *gin.Context) { } for _, v := range d.Children { if v.MountPoint != "" { + if findSystem == 0 { + if v.MountPoint == "/" { + tempDisk.DiskName = "System" + findSystem = 1 + tempSystemDisk = true + } + if len(v.Children) > 0 { + for _, c := range v.Children { + if c.MountPoint == "/" { + tempDisk.DiskName = "System" + findSystem = 1 + tempSystemDisk = true + break + } + } + } + } + stor := model.Storage{} stor.MountPoint = v.MountPoint stor.Size = v.FSSize @@ -53,16 +78,27 @@ func GetStorageList(c *gin.Context) { storageArr = append(storageArr, stor) } } + if len(storageArr) > 0 { - storages = append(storages, model.Storages{ - DiskName: d.Model, - Path: d.Path, - Size: d.Size, - Children: storageArr, - }) - diskNumber += 1 + if tempSystemDisk && len(system) > 0 { + tempStorageArr := []model.Storage{} + for i := 0; i < len(storageArr); i++ { + if storageArr[i].MountPoint != "/boot/efi" && storageArr[i].Type != "swap" { + tempStorageArr = append(tempStorageArr, storageArr[i]) + } + } + tempDisk.Children = tempStorageArr + storages = append(storages, tempDisk) + diskNumber += 1 + } else if !tempSystemDisk { + tempDisk.Children = storageArr + storages = append(storages, tempDisk) + diskNumber += 1 + } + } } } + c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: storages}) } diff --git a/route/v1/system.go b/route/v1/system.go index 42d7d32..c4e7233 100644 --- a/route/v1/system.go +++ b/route/v1/system.go @@ -354,21 +354,13 @@ func GetSystemUtilization(c *gin.Context) { temp.Model = v.Model temp.Name = v.Name temp.Size = v.Size - mountTemp := true - if len(v.Children) == 0 { - mountTemp = false - } + for _, child := range v.Children { if len(child.MountPoint) > 0 { avail, _ := strconv.ParseUint(child.FSAvail, 10, 64) temp.Avail += avail - used, _ := strconv.ParseUint(child.FSUsed, 10, 64) - temp.Used += used - } else { - mountTemp = false } } - temp.Mount = mountTemp usb = append(usb, temp) } } diff --git a/service/connections.go b/service/connections.go index 5136676..f9fe135 100644 --- a/service/connections.go +++ b/service/connections.go @@ -2,7 +2,7 @@ * @Author: LinkLeong link@icewhale.org * @Date: 2022-07-26 18:13:22 * @LastEditors: LinkLeong - * @LastEditTime: 2022-07-27 13:44:12 + * @LastEditTime: 2022-08-04 20:10:31 * @FilePath: /CasaOS/service/connections.go * @Description: * @Website: https://www.casaos.io @@ -20,11 +20,12 @@ import ( type ConnectionsService interface { GetConnectionsList() (connections []model2.ConnectionsDBModel) - GetConnectionByDirectory(directory string) (connections []model2.ConnectionsDBModel) + GetConnectionByHost(host string) (connections []model2.ConnectionsDBModel) GetConnectionByID(id string) (connections model2.ConnectionsDBModel) CreateConnection(connection *model2.ConnectionsDBModel) DeleteConnection(id string) - MountSmaba(connection *model2.ConnectionsDBModel) string + UpdateConnection(connection *model2.ConnectionsDBModel) + MountSmaba(username, host, directory, port, mountPoint, password string) string UnmountSmaba(mountPoint string) string } @@ -32,27 +33,30 @@ type connectionsStruct struct { db *gorm.DB } -func (s *connectionsStruct) GetConnectionByDirectory(directory string) (connections []model2.ConnectionsDBModel) { - s.db.Select("username,host,directory,status,mount_point,id").Where("directory = ?", directory).Find(&connections) +func (s *connectionsStruct) GetConnectionByHost(host string) (connections []model2.ConnectionsDBModel) { + s.db.Select("username,host,status,id").Where("host = ?", host).Find(&connections) return } func (s *connectionsStruct) GetConnectionByID(id string) (connections model2.ConnectionsDBModel) { - s.db.Select("username,password,host,directory,status,mount_point,id").Where("id = ?", id).First(&connections) + s.db.Select("username,password,host,status,id,directories,mount_point,port").Where("id = ?", id).First(&connections) return } func (s *connectionsStruct) GetConnectionsList() (connections []model2.ConnectionsDBModel) { - s.db.Select("username,host,port,directory,status,mount_point,id").Find(&connections) + s.db.Select("username,host,port,status,id,mount_point").Find(&connections) return } func (s *connectionsStruct) CreateConnection(connection *model2.ConnectionsDBModel) { s.db.Create(connection) } +func (s *connectionsStruct) UpdateConnection(connection *model2.ConnectionsDBModel) { + s.db.Save(connection) +} func (s *connectionsStruct) DeleteConnection(id string) { s.db.Where("id= ?", id).Delete(&model.ConnectionsDBModel{}) } -func (s *connectionsStruct) MountSmaba(connection *model2.ConnectionsDBModel) string { - str := command2.ExecResultStr("source " + config.AppInfo.ShellPath + "/helper.sh ;MountCIFS " + connection.Username + " " + connection.Host + " " + connection.Directory + " " + connection.Port + " " + connection.MountPoint + " " + connection.Password) +func (s *connectionsStruct) MountSmaba(username, host, directory, port, mountPoint, password string) string { + str := command2.ExecResultStr("source " + config.AppInfo.ShellPath + "/helper.sh ;MountCIFS " + username + " " + host + " " + directory + " " + port + " " + mountPoint + " " + password) return str } func (s *connectionsStruct) UnmountSmaba(mountPoint string) string { diff --git a/service/disk.go b/service/disk.go index e832c3b..dfe8219 100644 --- a/service/disk.go +++ b/service/disk.go @@ -36,6 +36,7 @@ type DiskService interface { DeleteMount(id string) UpdateMountPoint(m model2.SerialDisk) RemoveLSBLKCache() + UmountUSB(path string) } type diskService struct { db *gorm.DB @@ -45,6 +46,10 @@ func (d *diskService) RemoveLSBLKCache() { key := "system_lsblk" Cache.Delete(key) } +func (d *diskService) UmountUSB(path string) { + r := command2.ExecResultStr("source " + config.AppInfo.ShellPath + "/helper.sh ;UDEVILUmount " + path) + fmt.Println(r) +} func (d *diskService) SmartCTL(path string) model.SmartctlA { key := "system_smart_" + path @@ -243,9 +248,9 @@ func (d *diskService) GetDiskInfo(path string) model.LSBLKModel { } func (d *diskService) MountDisk(path, volume string) { - fmt.Println("source " + config.AppInfo.ShellPath + "/helper.sh ;do_mount " + path + " " + volume) + //fmt.Println("source " + config.AppInfo.ShellPath + "/helper.sh ;do_mount " + path + " " + volume) r := command2.ExecResultStr("source " + config.AppInfo.ShellPath + "/helper.sh ;do_mount " + path + " " + volume) - fmt.Print(r) + fmt.Println(r) } func (d *diskService) SaveMountPoint(m model2.SerialDisk) { diff --git a/service/docker.go b/service/docker.go index 4501345..45bc4a8 100644 --- a/service/docker.go +++ b/service/docker.go @@ -45,7 +45,7 @@ import ( type DockerService interface { DockerPullImage(imageName string, icon, name string) error IsExistImage(imageName string) bool - DockerContainerCreate(imageName string, m model.CustomizationPostData) (containerId string, err error) + DockerContainerCreate(m model.CustomizationPostData, id string) (containerId string, err error) DockerContainerCopyCreate(info *types.ContainerJSON) (containerId string, err error) DockerContainerStart(name string) error DockerContainerStats(name string) (string, error) @@ -376,7 +376,7 @@ func (ds *dockerService) DockerContainerCopyCreate(info *types.ContainerJSON) (c //param mapPort 容器主端口映射到外部的端口 //param tcp 容器其他tcp端口 //param udp 容器其他udp端口 -func (ds *dockerService) DockerContainerCreate(imageName string, m model.CustomizationPostData) (containerId string, err error) { +func (ds *dockerService) DockerContainerCreate(m model.CustomizationPostData, id string) (containerId string, err error) { if len(m.NetworkModel) == 0 { m.NetworkModel = "bridge" } @@ -385,6 +385,7 @@ func (ds *dockerService) DockerContainerCreate(imageName string, m model.Customi if err != nil { return "", err } + defer cli.Close() ports := make(nat.PortSet) portMaps := make(nat.PortMap) @@ -523,15 +524,26 @@ func (ds *dockerService) DockerContainerCreate(imageName string, m model.Customi if len(m.HostName) == 0 { m.HostName = m.Label } - config := &container.Config{ - Image: imageName, - Labels: map[string]string{"origin": m.Origin, m.Origin: m.Origin, "casaos": "casaos"}, - Env: envArr, - // Healthcheck: health, - Hostname: m.HostName, - Cmd: m.Cmd, + + info, err := cli.ContainerInspect(context.Background(), id) + hostConfig := &container.HostConfig{} + config := &container.Config{} + config.Labels = map[string]string{} + if err == nil { + // info.HostConfig = &container.HostConfig{} + // info.Config = &container.Config{} + // info.NetworkSettings = &types.NetworkSettings{} + hostConfig = info.HostConfig + config = info.Config } + config.Cmd = m.Cmd + config.Image = m.Image + config.Env = envArr + config.Hostname = m.HostName + config.ExposedPorts = ports + config.Labels["origin"] = m.Origin + config.Labels["casaos"] = "casaos" config.Labels["web"] = m.PortMap config.Labels["icon"] = m.Icon config.Labels["desc"] = m.Description @@ -541,12 +553,19 @@ func (ds *dockerService) DockerContainerCreate(imageName string, m model.Customi config.Labels["protocol"] = m.Protocol config.Labels["host"] = m.Host config.Labels["name"] = m.Label - hostConfig := &container.HostConfig{Resources: res, Mounts: volumes, RestartPolicy: rp, NetworkMode: container.NetworkMode(m.NetworkModel), Privileged: m.Privileged, CapAdd: m.CapAdd} + //container, err := cli.ContainerCreate(context.Background(), info.Config, info.HostConfig, &network.NetworkingConfig{info.NetworkSettings.Networks}, nil, info.Name) + + hostConfig.Mounts = volumes + hostConfig.Privileged = m.Privileged + hostConfig.CapAdd = m.CapAdd + hostConfig.NetworkMode = container.NetworkMode(m.NetworkModel) + hostConfig.RestartPolicy = rp + hostConfig.Resources = res + //hostConfig := &container.HostConfig{Resources: res, Mounts: volumes, RestartPolicy: rp, NetworkMode: , Privileged: m.Privileged, CapAdd: m.CapAdd} //if net != "host" { - config.ExposedPorts = ports + hostConfig.PortBindings = portMaps //} - containerDb, err := cli.ContainerCreate(context.Background(), config, hostConfig, diff --git a/service/model/o_connections.go b/service/model/o_connections.go index 9c79df3..45b1244 100644 --- a/service/model/o_connections.go +++ b/service/model/o_connections.go @@ -2,7 +2,7 @@ * @Author: LinkLeong link@icewhale.org * @Date: 2022-07-26 17:17:57 * @LastEditors: LinkLeong - * @LastEditTime: 2022-07-27 10:44:46 + * @LastEditTime: 2022-08-01 17:08:08 * @FilePath: /CasaOS/service/model/o_connections.go * @Description: * @Website: https://www.casaos.io @@ -11,16 +11,16 @@ package model type ConnectionsDBModel struct { - ID uint `gorm:"column:id;primary_key" json:"id"` - Updated int64 `gorm:"autoUpdateTime"` - Created int64 `gorm:"autoCreateTime"` - Username string `json:"username"` - Password string `json:"password"` - Host string `json:"host"` - Port string `json:"port"` - Directory string `json:"directory"` - MountPoint string `json:"mount_point"` - Status string `json:"status"` + ID uint `gorm:"column:id;primary_key" json:"id"` + Updated int64 `gorm:"autoUpdateTime"` + Created int64 `gorm:"autoCreateTime"` + Username string `json:"username"` + Password string `json:"password"` + Host string `json:"host"` + Port string `json:"port"` + Status string `json:"status"` + Directories string `json:"directories"` // string array + MountPoint string `json:"mount_point"` //parent directory of mount point } func (p *ConnectionsDBModel) TableName() string { diff --git a/shell/helper.sh b/shell/helper.sh index 3a724ca..2a7545e 100644 --- a/shell/helper.sh +++ b/shell/helper.sh @@ -383,5 +383,7 @@ CheckServiceStatus(){ echo "running" fi } - +UDEVILUmount(){ + $sudo_cmd udevil umount -f $1 +} diff --git a/types/system.go b/types/system.go index 9a3d4e8..92b4c4c 100644 --- a/types/system.go +++ b/types/system.go @@ -2,7 +2,7 @@ * @Author: LinkLeong link@icewhale.com * @Date: 2022-02-17 18:53:22 * @LastEditors: LinkLeong - * @LastEditTime: 2022-07-18 18:47:15 + * @LastEditTime: 2022-08-10 13:50:57 * @FilePath: /CasaOS/types/system.go * @Description: * @Website: https://www.casaos.io @@ -10,6 +10,6 @@ */ package types -const CURRENTVERSION = "0.3.4" +const CURRENTVERSION = "0.3.5" -const BODY = "" +const BODY = " " diff --git a/web/index.html b/web/index.html index bcede30..bb2b479 100644 --- a/web/index.html +++ b/web/index.html @@ -20,7 +20,7 @@ CasaOS - +