link %!s(int64=3) %!d(string=hai) anos
pai
achega
e1a928cd78

+ 3 - 1
conf/conf.ini.sample

@@ -15,6 +15,9 @@ ProjectPath = /casaOS/server
 HttpPort = 8089
 RunMode = release
 ServerApi = https://api.casaos.zimaboard.com
+Handshake = 
+Token = 
+
 
 [user]
 UserName = admin
@@ -22,7 +25,6 @@ PWD = zimaboard
 Email = user@gmail.com
 Description = description
 Initialized = false
-Token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImVyZXJlIiwicGFzc3dvcmQiOiJhZHNmZGYiLCJleHAiOjE2MjQwMDU0ODEsImlzcyI6Imdpbi1ibG9nIn0.JNsCccZuFCwlSMLJg62iOIB2xymk_k7xGa11xhZ07bc
 
 [zerotier]
 UserName = user

BIN=BIN
github.com/IceWhaleTech/CasaOS-linux-amd64


+ 5 - 2
go.mod

@@ -10,6 +10,7 @@ require (
 	github.com/bits-and-blooms/bitset v1.2.1 // indirect
 	github.com/containerd/containerd v1.5.7
 	github.com/containerd/continuity v0.2.0 // indirect
+	github.com/docker/distribution v2.8.0+incompatible // indirect
 	github.com/docker/docker v20.10.7+incompatible
 	github.com/docker/go-connections v0.4.0
 	github.com/forease/gotld v0.0.0-20190808124948-c50ff635576b
@@ -33,8 +34,10 @@ require (
 	github.com/leodido/go-urn v1.2.1 // indirect
 	github.com/mailru/easyjson v0.7.7 // indirect
 	github.com/mattn/go-isatty v0.0.14 // indirect
+	github.com/mattn/go-sqlite3 v1.14.11 // indirect
 	github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect
 	github.com/morikuni/aec v1.0.0 // indirect
+	github.com/opencontainers/image-spec v1.0.2 // indirect
 	github.com/opencontainers/selinux v1.8.5 // indirect
 	github.com/patrickmn/go-cache v2.1.0+incompatible
 	github.com/pkg/errors v0.9.1
@@ -66,6 +69,6 @@ require (
 	gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
 	gopkg.in/ini.v1 v1.62.0 // indirect
 	gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
-	gorm.io/driver/sqlite v1.1.5
-	gorm.io/gorm v1.21.15
+	gorm.io/driver/sqlite v1.2.6
+	gorm.io/gorm v1.22.5
 )

+ 14 - 0
go.sum

@@ -261,6 +261,8 @@ github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TT
 github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
 github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
 github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
+github.com/docker/distribution v2.8.0+incompatible h1:l9EaZDICImO1ngI+uTifW+ZYvvz7fKISBAKpg+MbWbY=
+github.com/docker/distribution v2.8.0+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
 github.com/docker/docker v20.10.7+incompatible h1:Z6O9Nhsjv+ayUEeI1IojKbYcsGdgYSNqxe1s2MYzUhQ=
 github.com/docker/docker v20.10.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
@@ -492,6 +494,8 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
 github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
 github.com/jinzhu/now v1.1.2 h1:eVKgfIdy9b6zbWBMgFpfDPoAMifwSZagU9HmEU6zgiI=
 github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
+github.com/jinzhu/now v1.1.4 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas=
+github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
 github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
 github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
 github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
@@ -553,6 +557,9 @@ github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp
 github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
 github.com/mattn/go-sqlite3 v1.14.8 h1:gDp86IdQsN/xWjIEmr9MF6o9mpksUgh0fu+9ByFxzIU=
 github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
+github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
+github.com/mattn/go-sqlite3 v1.14.11 h1:gt+cp9c0XGqe9S/wAHTL3n/7MqY+siPWgWJgqdsFrzQ=
+github.com/mattn/go-sqlite3 v1.14.11/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
 github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
 github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
@@ -608,6 +615,8 @@ github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3I
 github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
 github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
 github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
+github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
+github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
 github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
 github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
 github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
@@ -1223,8 +1232,13 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v
 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gorm.io/driver/sqlite v1.1.5 h1:JU8G59VyKu1x1RMQgjefQnkZjDe9wHc1kARDZPu5dZs=
 gorm.io/driver/sqlite v1.1.5/go.mod h1:NpaYMcVKEh6vLJ47VP6T7Weieu4H1Drs3dGD/K6GrGc=
+gorm.io/driver/sqlite v1.2.6 h1:SStaH/b+280M7C8vXeZLz/zo9cLQmIGwwj3cSj7p6l4=
+gorm.io/driver/sqlite v1.2.6/go.mod h1:gyoX0vHiiwi0g49tv+x2E7l8ksauLK0U/gShcdUsjWY=
 gorm.io/gorm v1.21.15 h1:gAyaDoPw0lCyrSFWhBlahbUA1U4P5RViC1uIqoB+1Rk=
 gorm.io/gorm v1.21.15/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
+gorm.io/gorm v1.22.3/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
+gorm.io/gorm v1.22.5 h1:lYREBgc02Be/5lSCTuysZZDb6ffL2qrat6fg9CFbvXU=
+gorm.io/gorm v1.22.5/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
 gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
 gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
 gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=

+ 4 - 2
main.go

@@ -58,8 +58,9 @@ func main() {
 	r := route.InitRouter()
 	//service.SyncTask(sqliteDB)
 	cron2 := cron.New() //创建一个cron实例
-	//执行定时任务(每5秒执行一次)
-	err := cron2.AddFunc("0 0 0 1/1 * *", func() {
+	//every day execution
+	err := cron2.AddFunc("0 0/1 * * * *", func() {
+		//service.PushIpInfo(*&config.ServerInfo.Token)
 		//service.UpdataDDNSList(mysqldb)
 		//service.SyncTask(sqliteDB)
 	})
@@ -77,6 +78,7 @@ func main() {
 		WriteTimeout:   60 * time.Second,
 		MaxHeaderBytes: 1 << 20,
 	}
+
 	s.ListenAndServe()
 
 }

+ 4 - 0
model/app.go

@@ -39,6 +39,10 @@ type ServerAppList struct {
 	Origin         string    `json:"origin"`
 	Type           int       `json:"type"`
 	Developer      string    `json:"developer"`
+	HostName       string    `json:"host_name"`
+	Privileged     bool      `json:"privileged"`
+	CapAdd         Strings   `json:"cap_add"`
+	Cmd            Strings   `json:"cmd"`
 }
 
 type Ports struct {

+ 6 - 0
model/heart.go

@@ -0,0 +1,6 @@
+package model
+
+type CasaOSHeart struct {
+	UuId string `json:"uuid"`
+	Type string `json:"type"`
+}

+ 12 - 8
model/manifest.go

@@ -114,12 +114,16 @@ type CustomizationPostData struct {
 	Volumes      PathArray `json:"volumes"`
 	Devices      PathArray `json:"devices"`
 	//Port         string    `json:"port,omitempty"`
-	PortMap     string `json:"port_map"`
-	CpuShares   int64  `json:"cpu_shares"`
-	Memory      int64  `json:"memory"`
-	Restart     string `json:"restart"`
-	EnableUPNP  bool   `json:"enable_upnp"`
-	Label       string `json:"label"`
-	Description string `json:"description"`
-	Position    bool   `json:"position"`
+	PortMap     string   `json:"port_map"`
+	CpuShares   int64    `json:"cpu_shares"`
+	Memory      int64    `json:"memory"`
+	Restart     string   `json:"restart"`
+	EnableUPNP  bool     `json:"enable_upnp"`
+	Label       string   `json:"label"`
+	Description string   `json:"description"`
+	Position    bool     `json:"position"`
+	HostName    string   `json:"host_name"`
+	Privileged  bool     `json:"privileged"`
+	CapAdd      []string `json:"cap_add"`
+	Cmd         []string `json:"cmd"`
 }

+ 6 - 0
model/notify.go

@@ -0,0 +1,6 @@
+package model
+
+type NotifyMssage struct {
+	Type string `json:"type"`
+	Data string `json:"data"`
+}

+ 19 - 0
model/person.go

@@ -0,0 +1,19 @@
+package model
+
+import "time"
+
+type PersionModel struct {
+	Token     string    `json:"token"`
+	Ips       []string  `json:"ips"`
+	CreatedAt time.Time `gorm:"<-:create;autoCreateTime" json:"created_at"`
+	UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
+}
+
+//记录链接状态
+type ConnectState struct {
+	From      string    `json:"from"`
+	To        string    `json:"to"`
+	Type      string    `json:"type"` //current state 1:ready 2:ok
+	CreatedAt time.Time `json:"created_at"`
+	UUId      string    `json:"uuid"` //对接标识
+}

+ 2 - 0
model/sys_common.go

@@ -24,6 +24,8 @@ type ServerModel struct {
 	RunMode     string
 	ServerApi   string
 	LockAccount bool
+	Handshake   string
+	Token       string
 }
 
 //服务配置

+ 66 - 0
pkg/utils/file/file.go

@@ -2,6 +2,7 @@ package file
 
 import (
 	"fmt"
+	"io"
 	"io/ioutil"
 	"mime/multipart"
 	"os"
@@ -159,3 +160,68 @@ func ReadFullFile(path string) []byte {
 	}
 	return content
 }
+
+// File copies a single file from src to dst
+func CopyFile(src, dst string) error {
+	var err error
+	var srcfd *os.File
+	var dstfd *os.File
+	var srcinfo os.FileInfo
+
+	if srcfd, err = os.Open(src); err != nil {
+		return err
+	}
+	defer srcfd.Close()
+
+	if dstfd, err = os.Create(dst); err != nil {
+		return err
+	}
+	defer dstfd.Close()
+
+	if _, err = io.Copy(dstfd, srcfd); err != nil {
+		return err
+	}
+	if srcinfo, err = os.Stat(src); err != nil {
+		return err
+	}
+	return os.Chmod(dst, srcinfo.Mode())
+}
+
+// Dir copies a whole directory recursively
+func CopyDir(src string, dst string) error {
+	var err error
+	var fds []os.FileInfo
+	var srcinfo os.FileInfo
+
+	if srcinfo, err = os.Stat(src); err != nil {
+		return err
+	}
+	if !srcinfo.IsDir() {
+		if err = CopyFile(src, dst); err != nil {
+			fmt.Println(err)
+		}
+		return nil
+	}
+	if err = os.MkdirAll(dst, srcinfo.Mode()); err != nil {
+		return err
+	}
+
+	if fds, err = ioutil.ReadDir(src); err != nil {
+		return err
+	}
+	for _, fd := range fds {
+		srcfp := path.Join(src, fd.Name())
+		dstfp := path.Join(dst, fd.Name())
+
+		if fd.IsDir() {
+			if err = CopyDir(srcfp, dstfp); err != nil {
+				fmt.Println(err)
+			}
+		} else {
+			if err = CopyFile(srcfp, dstfp); err != nil {
+				fmt.Println(err)
+			}
+		}
+	}
+	return nil
+}

+ 2 - 0
pkg/utils/oasis_err/e.go

@@ -36,6 +36,7 @@ const (
 	//file
 	FILE_DOES_NOT_EXIST = 60001
 	FILE_READ_ERROR     = 60002
+	FILE_DELETE_ERROR   = 60003
 
 	//shortcuts
 	SHORTCUTS_URL_ERROR = 70001
@@ -78,6 +79,7 @@ var MsgFlags = map[int]string{
 	FILE_DOES_NOT_EXIST: "File does not exist",
 
 	FILE_READ_ERROR:     "File read error",
+	FILE_DELETE_ERROR:   "Delete error",
 	SHORTCUTS_URL_ERROR: "URL error",
 }
 

+ 10 - 0
route/init.go

@@ -25,6 +25,8 @@ func InitFunction() {
 	Update2_3()
 	CheckSerialDiskMount()
 
+	CheckToken2_9()
+
 }
 
 var syncIsExistence = false
@@ -234,3 +236,11 @@ func CheckSerialDiskMount() {
 func Update2_3() {
 	command.OnlyExec("source " + config.AppInfo.ProjectPath + "/shell/assist.sh")
 }
+func CheckToken2_9() {
+	if len(config.ServerInfo.Token) == 0 {
+		token := uuid.NewV4().String
+		config.ServerInfo.Token = token()
+		config.Cfg.Section("server").Key("Token").SetValue(token())
+		config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath)
+	}
+}

+ 8 - 2
route/route.go

@@ -208,12 +208,13 @@ func InitRouter() *gin.Engine {
 			v1FileGroup.GET("/read", v1.GetFilerContent)
 			v1FileGroup.POST("/upload", v1.PostFileUpload)
 			v1FileGroup.GET("/dirpath", v1.DirPath)
-			//创建目录
+			//create folder
 			v1FileGroup.POST("/mkdir", v1.MkdirAll)
 			v1FileGroup.POST("/create", v1.PostCreateFile)
 
 			v1FileGroup.GET("/download", v1.GetDownloadFile)
-			v1FileGroup.PUT("/move", v1.PutFileMove)
+			v1FileGroup.POST("/operate", v1.PostOperateFileOrDir)
+			v1FileGroup.DELETE("delete", v1.DeleteFile)
 			//v1FileGroup.GET("/download", v1.UserFileDownloadCommonService)
 		}
 		v1DiskGroup := v1Group.Group("/disk")
@@ -283,6 +284,11 @@ func InitRouter() *gin.Engine {
 		{
 			v1SearchGroup.GET("/search", v1.GetSearchList)
 		}
+		v1PersonGroup := v1Group.Group("/persion")
+		v1PersonGroup.Use()
+		{
+			v1PersonGroup.GET("/test", v1.PersonTest)
+		}
 		v1Group.GET("/sync/config", v1.GetSyncConfig)
 		v1Group.Any("/syncthing/*url", v1.SyncToSyncthing)
 

+ 36 - 6
route/v1/docker.go

@@ -2,6 +2,7 @@ package v1
 
 import (
 	"bytes"
+	"encoding/json"
 	json2 "encoding/json"
 	"net/http"
 	"reflect"
@@ -254,9 +255,11 @@ func InstallApp(c *gin.Context) {
 		installLog.State = 0
 		installLog.CustomId = id
 		installLog.Message = "installing rely"
+		installLog.Class = types.NOTIFY_APP
 		installLog.Type = types.NOTIFY_TYPE_UNIMPORTANT
 		installLog.CreatedAt = strconv.FormatInt(time.Now().Unix(), 10)
 		installLog.UpdatedAt = strconv.FormatInt(time.Now().Unix(), 10)
+		installLog.Id = uuid.NewV4().String()
 		service.MyService.Notify().AddLog(installLog)
 		if m.Origin != "custom" {
 			for _, plugin := range appInfo.Plugins {
@@ -442,6 +445,8 @@ func InstallApp(c *gin.Context) {
 		envsStr, _ := json2.Marshal(m.Envs)
 		volumesStr, _ := json2.Marshal(m.Volumes)
 		devicesStr, _ := json2.Marshal(m.Devices)
+		cmd, _ := json2.Marshal(m.Cmd)
+		capAdd, _ := json.Marshal(m.CapAdd)
 		//step: 保存数据到数据库
 		md := model2.AppListDBModel{
 			CustomId: id,
@@ -469,9 +474,13 @@ func InstallApp(c *gin.Context) {
 			Memory:     m.Memory,
 			Devices:    string(devicesStr),
 			//Rely:       rely,
-			Origin:    m.Origin,
-			CreatedAt: strconv.FormatInt(time.Now().Unix(), 10),
-			UpdatedAt: strconv.FormatInt(time.Now().Unix(), 10),
+			Origin:     m.Origin,
+			CreatedAt:  strconv.FormatInt(time.Now().Unix(), 10),
+			UpdatedAt:  strconv.FormatInt(time.Now().Unix(), 10),
+			Cmd:        string(cmd),
+			CapAdd:     string(capAdd),
+			HostName:   m.HostName,
+			Privileged: m.Privileged,
 		}
 		//if appInfo.NetworkModel == "host" {
 		//	m.PortMap = m.Port
@@ -715,7 +724,7 @@ func UnInstallApp(c *gin.Context) {
 		}
 
 		//step: 删除install log
-		service.MyService.Notify().DelLog(appId)
+		//service.MyService.Notify().DelLog(appId)
 
 		//	for k, v := range info.Rely {
 		//
@@ -755,6 +764,13 @@ func UnInstallApp(c *gin.Context) {
 		//}
 	}
 	config.CasaOSGlobalVariables.AppChange = true
+	unInstallLog := model2.AppNotify{}
+	unInstallLog.State = 0
+	unInstallLog.CustomId = appId
+	unInstallLog.Message = "uninstalled"
+	unInstallLog.Id = uuid.NewV4().String()
+	service.MyService.Notify().UpdateLog(unInstallLog)
+
 	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
 
 }
@@ -926,7 +942,9 @@ func UpdateSetting(c *gin.Context) {
 	envsStr, _ := json2.Marshal(m.Envs)
 	volumesStr, _ := json2.Marshal(m.Volumes)
 	devicesStr, _ := json2.Marshal(m.Devices)
-	if !reflect.DeepEqual(string(portsStr), appInfo.Ports) || !reflect.DeepEqual(string(envsStr), appInfo.Envs) || !reflect.DeepEqual(string(volumesStr), appInfo.Volumes) || m.PortMap != appInfo.PortMap || m.NetworkModel != appInfo.NetModel {
+	capAddStr, _ := json2.Marshal(m.CapAdd)
+	cmdStr, _ := json.Marshal(m.Cmd)
+	if !reflect.DeepEqual(string(portsStr), appInfo.Ports) || !reflect.DeepEqual(string(envsStr), appInfo.Envs) || !reflect.DeepEqual(string(volumesStr), appInfo.Volumes) || m.PortMap != appInfo.PortMap || m.NetworkModel != appInfo.NetModel || m.HostName != appInfo.HostName || !reflect.DeepEqual(string(cmdStr), appInfo.Cmd) || !reflect.DeepEqual(string(capAddStr), appInfo.CapAdd) || m.Privileged != appInfo.Privileged {
 
 		var newUUid = uuid.NewV4().String()
 		var err error
@@ -1044,6 +1062,10 @@ func UpdateSetting(c *gin.Context) {
 	appInfo.Restart = m.Restart
 	appInfo.Memory = m.Memory
 	appInfo.CpuShares = m.CpuShares
+	appInfo.Cmd = string(cmdStr)
+	appInfo.Privileged = m.Privileged
+	appInfo.CapAdd = string(capAddStr)
+	appInfo.HostName = m.HostName
 	appInfo.UpdatedAt = strconv.FormatInt(time.Now().Unix(), 10)
 	service.MyService.App().UpdateApp(appInfo)
 
@@ -1139,7 +1161,6 @@ func ContainerRelyInfo(c *gin.Context) {
 func ContainerUpdateInfo(c *gin.Context) {
 	appId := c.Param("id")
 	appInfo := service.MyService.App().GetAppDBInfo(appId)
-
 	info, err := service.MyService.Docker().DockerContainerInfo(appId)
 	if err != nil {
 		//todo 需要自定义错误
@@ -1162,6 +1183,10 @@ func ContainerUpdateInfo(c *gin.Context) {
 	var dir model.PathArray
 	json2.Unmarshal([]byte(appInfo.Devices), &dir)
 
+	var cmd []string
+	json2.Unmarshal([]byte(appInfo.Cmd), &cmd)
+	var capAdd []string
+	json2.Unmarshal([]byte(appInfo.CapAdd), &capAdd)
 	//volumesStr, _ := json2.Marshal(m.Volumes)
 	//devicesStr, _ := json2.Marshal(m.Devices)
 	m := model.CustomizationPostData{}
@@ -1183,6 +1208,11 @@ func ContainerUpdateInfo(c *gin.Context) {
 	m.EnableUPNP = appInfo.EnableUPNP
 	m.Position = appInfo.Position
 
+	m.CapAdd = capAdd
+	m.Cmd = cmd
+	m.HostName = appInfo.HostName
+	m.Privileged = appInfo.Privileged
+
 	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: m})
 }
 

+ 72 - 71
route/v1/file.go

@@ -9,8 +9,7 @@ import (
 	"net/http"
 	"os"
 	"path"
-	"path/filepath"
-	"time"
+	"strings"
 
 	"github.com/IceWhaleTech/CasaOS/model"
 	"github.com/IceWhaleTech/CasaOS/pkg/utils/file"
@@ -127,12 +126,12 @@ func GetLocalFile(c *gin.Context) {
 	return
 }
 
-// @Summary 下载文件
+// @Summary download
 // @Produce  application/json
 // @Accept application/json
 // @Tags file
 // @Security ApiKeyAuth
-// @Param path query string true "路径"
+// @Param path query string true "path of file"
 // @Success 200 {string} string "ok"
 // @Router /file/download [get]
 func GetDownloadFile(c *gin.Context) {
@@ -165,7 +164,6 @@ func GetDownloadFile(c *gin.Context) {
 	c.Header("Content-Transfer-Encoding", "binary")
 
 	c.File(filePath)
-	return
 }
 
 // @Summary 获取目录列表
@@ -177,18 +175,18 @@ func GetDownloadFile(c *gin.Context) {
 // @Success 200 {string} string "ok"
 // @Router /file/dirpath [get]
 func DirPath(c *gin.Context) {
-	path := c.DefaultQuery("path", "/")
+	path := c.DefaultQuery("path", "")
 	info := service.MyService.ZiMa().GetDirPath(path)
 	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: info})
 }
 
-// @Summary 重命名目录或文件
+// @Summary rename file or dir
 // @Produce  application/json
 // @Accept application/json
 // @Tags file
 // @Security ApiKeyAuth
-// @Param oldpath formData string true "旧的路径"
-// @Param newpath formData string true "新路径"
+// @Param oldpath formData string true "path of old"
+// @Param newpath formData string true "path of new"
 // @Success 200 {string} string "ok"
 // @Router /file/rename [put]
 func RenamePath(c *gin.Context) {
@@ -202,12 +200,12 @@ func RenamePath(c *gin.Context) {
 	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
 }
 
-// @Summary 创建文件夹
+// @Summary create folder
 // @Produce  application/json
 // @Accept  multipart/form-data
 // @Tags file
 // @Security ApiKeyAuth
-// @Param path formData string false "路径"
+// @Param path formData string true "path of folder"
 // @Success 200 {string} string "ok"
 // @Router /file/mkdir [post]
 func MkdirAll(c *gin.Context) {
@@ -240,82 +238,85 @@ func PostCreateFile(c *gin.Context) {
 	c.JSON(http.StatusOK, model.Result{Success: code, Message: oasis_err2.GetMsg(code)})
 }
 
-// @Summary 上传文件
+// @Summary upload file
 // @Produce  application/json
 // @Accept  multipart/form-data
 // @Tags file
 // @Security ApiKeyAuth
-// @Param path formData string false "路径"
+// @Param path formData string false "file path"
+// @Param file formData file true "file"
 // @Success 200 {string} string "ok"
-// @Router /file/mkdir [post]
+// @Router /file/upload [post]
 func PostFileUpload(c *gin.Context) {
-	file, _, _ := c.Request.FormFile("file")
-	//file.Read()
+	f, _, _ := c.Request.FormFile("file")
 	path := c.Query("path")
-	//上传文件
+	if len(path) == 0 {
+		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
+		return
+	}
+	if !file.CheckNotExist(path) {
+		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.FILE_ALREADY_EXISTS, Message: oasis_err2.GetMsg(oasis_err2.FILE_ALREADY_EXISTS)})
+		return
+	}
 	out, _ := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0644)
 	defer out.Close()
-	io.Copy(out, file)
+	_, err := io.Copy(out, f)
+	if err != nil {
+		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.ERROR, Message: oasis_err2.GetMsg(oasis_err2.ERROR), Data: err.Error()})
+		return
+	}
 	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
 }
 
-func PutFileMove(c *gin.Context) {
-	from := "/Users/liangjianli/go/CasaOS"
-	to := "/Users/liangjianli/go/CasaOS/test"
-	//t := 1 //是否覆盖
-
-	//方法体
-	stopCh := make(chan int)
-	f, err := os.Stat(from)
-	if err != nil {
-		//未拿到文件信息
-		fmt.Println("stat", err)
+// @Summary copy or move file
+// @Produce  application/json
+// @Accept  multipart/form-data
+// @Tags file
+// @Security ApiKeyAuth
+// @Param from formData string true "from path"
+// @Param to formData string true "to path"
+// @Param t formData string true "action" Enums(move,copy)
+// @Success 200 {string} string "ok"
+// @Router /file/operate [post]
+func PostOperateFileOrDir(c *gin.Context) {
+	from := c.PostForm("from")
+	to := c.PostForm("to")
+	t := c.PostForm("type")
+	if len(from) == 0 || len(t) == 0 || len(to) == 0 {
+		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
+		return
 	}
-	//未创建新的文件夹
-	if f.IsDir() {
-		//from 是文件夹,定义to也是文件夹
-		if list, err := ioutil.ReadDir(from); err == nil {
-			for _, v := range list {
-				time.Sleep(time.Second)
-				if err = Copy(stopCh, filepath.Join(from, v.Name()), filepath.Join(to, v.Name())); err != nil {
-					fmt.Printf("copy %s ,err %d", v.Name(), err)
-				}
-			}
+	if t == "move" {
+		lastPath := from[strings.LastIndex(from, "/")+1:]
+		if !file.CheckNotExist(to + "/" + lastPath) {
+			c.JSON(http.StatusOK, model.Result{Success: oasis_err2.FILE_OR_DIR_EXISTS, Message: oasis_err2.GetMsg(oasis_err2.FILE_ALREADY_EXISTS)})
+			return
 		}
-	} else {
-		p := filepath.Dir(to)
-		if _, err = os.Stat(p); err != nil {
-			if err = os.MkdirAll(p, 0777); err != nil {
-				fmt.Println("mkdir", err)
-			}
+		err := os.Rename(from, to+"/"+lastPath)
+		if err != nil {
+			c.JSON(http.StatusOK, model.Result{Success: oasis_err2.ERROR, Message: oasis_err2.GetMsg(oasis_err2.ERROR), Data: err.Error()})
+			return
 		}
+	} else if t == "copy" {
+		err := file.CopyDir(from, to)
+		if err != nil {
+			c.JSON(http.StatusOK, model.Result{Success: oasis_err2.ERROR, Message: oasis_err2.GetMsg(oasis_err2.ERROR), Data: err.Error()})
+			return
+		}
+	} else {
+		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
+		return
 	}
-
-	file, err := os.Open(from)
-
-	if err != nil {
-		fmt.Println("open file error ", err)
-	}
-	defer file.Close()
-	out, err := os.Create(to)
-	if err != nil {
-		fmt.Println("create to file err", err)
-	}
-	defer out.Close()
-	io.Copy(out, file)
-	time.Sleep(time.Second * 4)
-	close(stopCh)
+	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
 }
-func Copy(stop chan int, from, to string) error {
-
-	for {
-		select {
-		case <-stop:
-			return nil
-		default:
-			fmt.Println(from)
-
-		}
+func DeleteFile(c *gin.Context) {
+	path := c.Query("path")
+	//err := os.Remove(path)
+	err := os.RemoveAll(path)
+	if err != nil {
+		fmt.Println(err)
+		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.FILE_DELETE_ERROR, Message: oasis_err2.GetMsg(oasis_err2.FILE_DELETE_ERROR), Data: err})
+		return
 	}
-	return nil
+	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
 }

+ 16 - 24
route/v1/notify.go

@@ -1,15 +1,13 @@
 package v1
 
 import (
-	json2 "encoding/json"
-	"github.com/IceWhaleTech/CasaOS/model"
-	"github.com/IceWhaleTech/CasaOS/pkg/utils/oasis_err"
+	"fmt"
+	"net/http"
+
 	"github.com/IceWhaleTech/CasaOS/service"
 	"github.com/IceWhaleTech/CasaOS/types"
 	"github.com/gin-gonic/gin"
 	"github.com/gorilla/websocket"
-	"net/http"
-	"time"
 )
 
 var upGrader = websocket.Upgrader{
@@ -33,24 +31,17 @@ func NotifyWS(c *gin.Context) {
 		return
 	}
 	defer ws.Close()
+	service.WebSocketConns = append(service.WebSocketConns, ws)
+
+	if !service.SocketRun {
+		service.SocketRun = true
+		service.SendMeg()
+	}
 	for {
 		mt, message, err := ws.ReadMessage()
-		if err != nil {
-			break
-		}
-		if string(message) != "notify" {
-			return
-		}
-		for {
-			list := service.MyService.Notify().GetList()
-			json, _ := json2.Marshal(list)
-			err = ws.WriteMessage(mt, json)
-			if err != nil {
-				break
-			}
-			time.Sleep(time.Second * 2)
-		}
+		fmt.Println(mt, message, err)
 	}
+
 }
 
 // @Summary 标记notify已读
@@ -62,9 +53,10 @@ func NotifyWS(c *gin.Context) {
 // @Router /notify/read/{id} [put]
 func PutNotifyRead(c *gin.Context) {
 	id := c.Param("id")
-	if len(id) == 0 {
-		c.JSON(http.StatusOK, model.Result{Success: oasis_err.INVALID_PARAMS, Message: oasis_err.GetMsg(oasis_err.INVALID_PARAMS)})
-		return
-	}
+	// if len(id) == 0 {
+	// 	c.JSON(http.StatusOK, model.Result{Success: oasis_err.INVALID_PARAMS, Message: oasis_err.GetMsg(oasis_err.INVALID_PARAMS)})
+	// 	return
+	// }
+	fmt.Println(id)
 	service.MyService.Notify().MarkRead(id, types.NOTIFY_READ)
 }

+ 25 - 0
route/v1/persion.go

@@ -0,0 +1,25 @@
+package v1
+
+import (
+	"time"
+
+	"github.com/IceWhaleTech/CasaOS/model"
+	"github.com/IceWhaleTech/CasaOS/pkg/config"
+	"github.com/IceWhaleTech/CasaOS/service"
+	"github.com/gin-gonic/gin"
+	uuid "github.com/satori/go.uuid"
+)
+
+func PersonTest(c *gin.Context) {
+
+	//service.MyService.Person().GetPersionInfo("fb2333a1-72b2-4cb4-9e31-61ccaffa55b9")
+
+	m := model.ConnectState{}
+	m.CreatedAt = time.Now()
+	m.From = config.ServerInfo.Token
+	m.To = "fb2333a1-72b2-4cb4-9e31-61ccaffa55b9"
+	m.Type = ""
+	m.UUId = uuid.NewV4().String()
+
+	service.MyService.Person().Handshake(m)
+}

+ 20 - 0
service/casa.go

@@ -1,6 +1,7 @@
 package service
 
 import (
+	"encoding/json"
 	json2 "encoding/json"
 	"fmt"
 	"strconv"
@@ -18,6 +19,7 @@ type CasaService interface {
 	GetTaskList(size int) []model2.TaskDBModel
 	GetServerAppInfo(id, t string, language string) model.ServerAppList
 	ShareAppFile(body []byte) string
+	PushHeart(id, t string, language string)
 }
 
 type casaService struct {
@@ -123,6 +125,24 @@ func GetToken() string {
 	return auth
 }
 
+func (o *casaService) PushHeart(id, t string, language string) {
+
+	m := model.CasaOSHeart{}
+	m.UuId = id
+	m.Type = t
+	b, _ := json.Marshal(m)
+
+	head := make(map[string]string)
+
+	head["Authorization"] = GetToken()
+
+	infoS := httper2.Post(config.ServerInfo.ServerApi+"/v1/analyse/heart", b, "application/json", head)
+
+	info := model.ServerAppList{}
+	json2.Unmarshal([]byte(gjson.Get(infoS, "data").String()), &info)
+
+}
+
 func NewOasisService() CasaService {
 	return &casaService{}
 }

+ 8 - 3
service/docker.go

@@ -433,7 +433,7 @@ func (ds *dockerService) DockerContainerCreate(imageName string, containerDbId s
 	}
 	for _, p := range m.Devices {
 		if len(p.Path) > 0 {
-			res.Devices = append(res.Devices, container.DeviceMapping{PathOnHost: p.Path, PathInContainer: p.ContainerPath})
+			res.Devices = append(res.Devices, container.DeviceMapping{PathOnHost: p.Path, PathInContainer: p.ContainerPath, CgroupPermissions: "rwm"})
 		}
 	}
 	hostConfingBind := []string{}
@@ -490,13 +490,18 @@ func (ds *dockerService) DockerContainerCreate(imageName string, containerDbId s
 	// 	Retries:     1000,
 	// }
 	// fmt.Print(health)
+	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},
 		Env:    envArr,
 		//	Healthcheck: health,
+		Hostname: m.HostName,
+		Cmd:      m.Cmd,
 	}
-	hostConfig := &container.HostConfig{Resources: res, Mounts: volumes, RestartPolicy: rp, NetworkMode: container.NetworkMode(net)}
+	hostConfig := &container.HostConfig{Resources: res, Mounts: volumes, RestartPolicy: rp, NetworkMode: container.NetworkMode(net), Privileged: m.Privileged, CapAdd: m.CapAdd}
 	//if net != "host" {
 	config.ExposedPorts = ports
 	hostConfig.PortBindings = portMaps
@@ -754,7 +759,7 @@ func (ds *dockerService) DockerContainerUpdate(m model.CustomizationPostData, id
 		res.CPUShares = m.CpuShares
 	}
 	for _, p := range m.Devices {
-		res.Devices = append(res.Devices, container.DeviceMapping{PathOnHost: p.Path, PathInContainer: p.ContainerPath})
+		res.Devices = append(res.Devices, container.DeviceMapping{PathOnHost: p.Path, PathInContainer: p.ContainerPath, CgroupPermissions: "rwm"})
 	}
 	_, err = cli.ContainerUpdate(context.Background(), id, container.UpdateConfig{RestartPolicy: rp, Resources: res})
 	if err != nil {

+ 5 - 1
service/model/o_container.go

@@ -40,7 +40,11 @@ type AppListDBModel struct {
 	Memory    int64  `json:"memory"`
 	Restart   string `json:"restart"`
 	//Rely      model.MapStrings `gorm:"type:json" json:"rely"` //[{"mysql":"id"},{"mysql":"id"}]
-	Origin string `json:"origin"`
+	Origin     string `json:"origin"`
+	HostName   string `json:"host_name"`
+	Privileged bool   `json:"privileged"`
+	CapAdd     string `json:"cap_add"`
+	Cmd        string `gorm:"type:json" json:"cmd"`
 }
 
 func (p *AppListDBModel) TableName() string {

+ 2 - 1
service/model/o_notify.go

@@ -6,9 +6,10 @@ type AppNotify struct {
 	CreatedAt string `json:"created_at"`
 	UpdatedAt string `json:"updated_at"`
 	Id        string `json:"id"`
-	Type      int    `json:"type"` // 1:显示即为已读 2:info 3:warning 4:error 5:success
+	Type      int    `json:"type"`
 	Icon      string `json:"icon"`
 	Name      string `json:"name"`
+	Class     int    `json:"class"`
 	CustomId  string `gorm:"column:custom_id;primary_key" json:"custom_id"`
 }
 

+ 62 - 5
service/notify.go

@@ -1,8 +1,12 @@
 package service
 
 import (
+	json2 "encoding/json"
+	"time"
+
 	"github.com/IceWhaleTech/CasaOS/service/model"
 	"github.com/IceWhaleTech/CasaOS/types"
+	"github.com/gorilla/websocket"
 	"gorm.io/gorm"
 )
 
@@ -10,8 +14,9 @@ type NotifyServer interface {
 	GetLog(id string) model.AppNotify
 	AddLog(log model.AppNotify)
 	UpdateLog(log model.AppNotify)
+	UpdateLogByCustomId(log model.AppNotify)
 	DelLog(id string)
-	GetList() (list []model.AppNotify)
+	GetList(c int) (list []model.AppNotify)
 	MarkRead(id string, state int)
 }
 
@@ -19,8 +24,8 @@ type notifyServer struct {
 	db *gorm.DB
 }
 
-func (i notifyServer) GetList() (list []model.AppNotify) {
-	i.db.Where("state=? or state=?", types.NOTIFY_DYNAMICE, types.NOTIFY_UNREAD).Find(&list)
+func (i notifyServer) GetList(c int) (list []model.AppNotify) {
+	i.db.Where("class = ?", c).Where(i.db.Where("state = ?", types.NOTIFY_DYNAMICE).Or("state = ?", types.NOTIFY_UNREAD)).Find(&list)
 	return
 }
 
@@ -31,20 +36,72 @@ func (i *notifyServer) AddLog(log model.AppNotify) {
 func (i *notifyServer) UpdateLog(log model.AppNotify) {
 	i.db.Save(&log)
 }
-
+func (i *notifyServer) UpdateLogByCustomId(log model.AppNotify) {
+	if len(log.CustomId) == 0 {
+		return
+	}
+	i.db.Model(&model.AppNotify{}).Select("*").Where("custom_id = ? ", log.CustomId).Updates(log)
+}
 func (i *notifyServer) GetLog(id string) model.AppNotify {
 	var log model.AppNotify
 	i.db.Where("custom_id = ? ", id).First(&log)
 	return log
 }
 func (i *notifyServer) MarkRead(id string, state int) {
-	i.db.Update("state=", state).Where("custom_id = ? ", id)
+	if id == "0" {
+		i.db.Model(&model.AppNotify{}).Where("1 = ?", 1).Update("state", state)
+		return
+	}
+	i.db.Model(&model.AppNotify{}).Where("id = ? ", id).Update("state", state)
 }
 func (i *notifyServer) DelLog(id string) {
 	var log model.AppNotify
 	i.db.Where("custom_id = ?", id).Delete(&log)
 }
 
+func SendMeg() {
+	// for {
+	// 	mt, message, err := ws.ReadMessage()
+	// 	if err != nil {
+	// 		break
+	// 	}
+	// 	notify := model.NotifyMssage{}
+	// 	json2.Unmarshal(message, &notify)
+	// 	if notify.Type == "read" {
+	// 		service.MyService.Notify().MarkRead(notify.Data, types.NOTIFY_READ)
+	// 	}
+	// 	if notify.Type == "app" {
+	//		go func(ws *websocket.Conn) {
+
+	for {
+		list := MyService.Notify().GetList(types.NOTIFY_APP)
+		json, _ := json2.Marshal(list)
+
+		if len(list) > 0 {
+			var temp []*websocket.Conn
+			for _, v := range WebSocketConns {
+
+				err := v.WriteMessage(1, json)
+				if err == nil {
+					temp = append(temp, v)
+				}
+			}
+			WebSocketConns = temp
+			for _, v := range list {
+				MyService.Notify().MarkRead(v.Id, types.NOTIFY_READ)
+			}
+		}
+
+		if len(WebSocketConns) == 0 {
+			SocketRun = false
+		}
+		time.Sleep(time.Second * 2)
+	}
+	// 	}(ws)
+	// }
+	//	}
+}
+
 func NewNotifyService(db *gorm.DB) NotifyServer {
 	return &notifyServer{db: db}
 }

+ 117 - 0
service/person.go

@@ -0,0 +1,117 @@
+package service
+
+import (
+	"encoding/json"
+	"fmt"
+	"log"
+	"net"
+	"reflect"
+	"time"
+
+	"github.com/IceWhaleTech/CasaOS/model"
+	"github.com/IceWhaleTech/CasaOS/pkg/config"
+	httper2 "github.com/IceWhaleTech/CasaOS/pkg/utils/httper"
+)
+
+type PersonService interface {
+	GetPersionInfo(token string) (m model.PersionModel, err error)
+	Handshake(m model.ConnectState)
+}
+
+type personService struct {
+}
+
+var IpInfo model.PersionModel
+
+func PushIpInfo(token string) {
+
+	m := model.PersionModel{}
+	m.Ips = GetDeviceAllIP()
+	m.Token = token
+	b, _ := json.Marshal(m)
+
+	if reflect.DeepEqual(IpInfo, m) {
+		return
+	}
+	head := make(map[string]string)
+	infoS := httper2.Post(config.ServerInfo.Handshake+"/v1/update", b, "application/json", head)
+	fmt.Println(infoS)
+}
+func (p *personService) GetPersionInfo(token string) (m model.PersionModel, err error) {
+	infoS := httper2.Get(config.ServerInfo.Handshake+"/v1/ips/"+token, nil)
+	err = json.Unmarshal([]byte(infoS), &m)
+	return
+}
+
+//尝试连接
+func (p *personService) Handshake(m model.ConnectState) {
+	//1先进行udp打通成功
+
+	srcAddr := &net.UDPAddr{
+		IP: net.IPv4zero, Port: 9901} //注意端口必须固定
+	dstAddr := &net.UDPAddr{
+		IP: net.ParseIP(config.ServerInfo.Handshake), Port: 9527}
+	//DialTCP在网络协议net上连接本地地址laddr和远端地址raddr。net必须是"udp"、"udp4"、"udp6";如果laddr不是nil,将使用它作为本地地址,否则自动选择一个本地地址。
+	//(conn)UDPConn代表一个UDP网络连接,实现了Conn和PacketConn接口
+	conn, err := net.DialUDP("udp", srcAddr, dstAddr)
+	if err != nil {
+		fmt.Println(err)
+	}
+	b, _ := json.Marshal(m)
+	if _, err = conn.Write(b); err != nil {
+		fmt.Println(err)
+	}
+	data := make([]byte, 1024)
+	//ReadFromUDP从c读取一个UDP数据包,将有效负载拷贝到b,返回拷贝字节数和数据包来源地址。
+	//ReadFromUDP方***在超过一个固定的时间点之后超时,并返回一个错误。
+	n, _, err := conn.ReadFromUDP(data)
+	if err != nil {
+		fmt.Printf("error during read: %s", err)
+	}
+	conn.Close()
+	toPersion := model.PersionModel{}
+	err = json.Unmarshal(data[:n], &toPersion)
+	if err != nil {
+		fmt.Println(err)
+	}
+	// bidirectionHole(srcAddr, &anotherPeer)
+
+	//2udp打洞成功向服务器汇报打洞结果
+	//3转udp打洞
+
+}
+
+func bidirectionHole(srcAddr *net.UDPAddr, anotherAddr *net.UDPAddr) {
+
+	conn, err := net.DialUDP("udp", srcAddr, anotherAddr)
+	if err != nil {
+
+		fmt.Println("send handshake:", err)
+	}
+	go func() {
+		for {
+			time.Sleep(10 * time.Second)
+			if _, err = conn.Write([]byte("from []")); err != nil {
+
+				log.Println("send msg fail", err)
+			}
+		}
+	}()
+
+	for {
+
+		data := make([]byte, 1024)
+		n, _, err := conn.ReadFromUDP(data)
+		if err != nil {
+
+			log.Printf("error during read:%s\n", err)
+		} else {
+
+			log.Printf("收到数据:%s\n", data[:n])
+		}
+	}
+}
+
+func NewPersonService() PersonService {
+	return &personService{}
+}

+ 11 - 0
service/service.go

@@ -2,6 +2,7 @@ package service
 
 import (
 	loger2 "github.com/IceWhaleTech/CasaOS/pkg/utils/loger"
+	"github.com/gorilla/websocket"
 	"github.com/patrickmn/go-cache"
 	"gorm.io/gorm"
 )
@@ -10,6 +11,10 @@ var Cache *cache.Cache
 
 var MyService Repository
 
+var WebSocketConns []*websocket.Conn
+
+var SocketRun bool
+
 type Repository interface {
 	App() AppService
 	DDNS() DDNSService
@@ -27,6 +32,7 @@ type Repository interface {
 	System() SystemService
 	Shortcuts() ShortcutsService
 	Search() SearchService
+	Person() PersonService
 }
 
 func NewService(db *gorm.DB, log loger2.OLog) Repository {
@@ -48,6 +54,7 @@ func NewService(db *gorm.DB, log loger2.OLog) Repository {
 		system:         NewSystemService(log),
 		shortcuts:      NewShortcutsService(db),
 		search:         NewSearchService(),
+		person:         NewPersonService(),
 	}
 }
 
@@ -68,6 +75,7 @@ type store struct {
 	system         SystemService
 	shortcuts      ShortcutsService
 	search         SearchService
+	person         PersonService
 }
 
 func (c *store) Rely() RelyService {
@@ -76,6 +84,9 @@ func (c *store) Rely() RelyService {
 func (c *store) Shortcuts() ShortcutsService {
 	return c.shortcuts
 }
+func (c *store) Person() PersonService {
+	return c.person
+}
 func (c *store) System() SystemService {
 	return c.system
 }

+ 16 - 0
service/system.go

@@ -2,6 +2,7 @@ package service
 
 import (
 	"io/ioutil"
+	"net"
 	"os"
 
 	"github.com/IceWhaleTech/CasaOS/pkg/config"
@@ -71,6 +72,21 @@ func (s *systemService) GetCasaOSLogs(lineNumber int) string {
 	return string(content)
 }
 
+func GetDeviceAllIP() []string {
+	var address []string
+	addrs, err := net.InterfaceAddrs()
+	if err != nil {
+		return address
+	}
+	for _, a := range addrs {
+		if ipNet, ok := a.(*net.IPNet); ok && !ipNet.IP.IsLoopback() {
+			if ipNet.IP.To16() != nil {
+				address = append(address, ipNet.IP.String())
+			}
+		}
+	}
+	return address
+}
 func NewSystemService(log loger.OLog) SystemService {
 	return &systemService{log: log}
 }

+ 0 - 4
service/user.go

@@ -28,10 +28,6 @@ func (c *user) SetUser(username, pwd, token, email, desc string) error {
 		config.Cfg.Section("user").Key("PWD").SetValue(pwd)
 		config.UserInfo.PWD = pwd
 	}
-	if len(token) > 0 {
-		config.Cfg.Section("user").Key("Token").SetValue(token)
-		config.UserInfo.Token = token
-	}
 	if len(email) > 0 {
 		config.Cfg.Section("user").Key("Email").SetValue(email)
 		config.UserInfo.Email = email

+ 1 - 1
service/zima_info.go

@@ -85,7 +85,7 @@ func (c *zima) GetDirPath(path string) []model.Path {
 	ls, _ := ioutil.ReadDir(path)
 	dirs := []model.Path{}
 
-	if strings.Count(path, "/") > 0 {
+	if len(path) > 0 {
 		for _, l := range ls {
 			dirs = append(dirs, model.Path{Name: l.Name(), Path: path + "/" + l.Name(), IsDir: l.IsDir(), Date: l.ModTime()})
 		}

+ 1 - 1
shell/assist.sh

@@ -11,7 +11,7 @@ version_0_2_3() {
 
 # add in v0.2.5
 
-readonly CASA_DEPANDS="curl smartmontools parted fdisk partprobe"
+readonly CASA_DEPANDS="curl smartmontools parted fdisk partprobe ntfs-3g"
 
 version_0_2_5() {
   install_depends "$CASA_DEPANDS"

+ 1 - 1
shell/helper.sh

@@ -184,7 +184,7 @@ do_mount() {
   DEVBASE=$1
   DEVICE="${DEVBASE}"
   # See if this drive is already mounted, and if so where
-  MOUNT_POINT=$(mount | grep ${DEVICE} | awk '{ print $3 }')
+  MOUNT_POINT=$(lsblk -o name,mountpoint | grep ${DEVICE} | awk '{print $2}')
 
   if [ -n "${MOUNT_POINT}" ]; then
     ${log} "Warning: ${DEVICE} is already mounted at ${MOUNT_POINT}"

+ 1 - 1
shell/usb-mount.sh

@@ -12,7 +12,7 @@ DEVBASE=$2
 DEVICE="/dev/${DEVBASE}"
 
 # See if this drive is already mounted, and if so where
-MOUNT_POINT=$(mount | grep ${DEVICE} | awk '{ print $3 }')
+MOUNT_POINT=$(lsblk -o name,mountpoint | grep ${DEVICE} | awk '{print $2}')
 
 do_mount() {
 

+ 4 - 0
types/notify.go

@@ -11,3 +11,7 @@ const (
 	NOTIFY_TYPE_ERROR
 	NOTIFY_TYPE_INSTALL_LOG
 )
+
+const (
+	NOTIFY_APP = iota
+)

+ 2 - 2
types/system.go

@@ -1,5 +1,5 @@
 package types
 
-const CURRENTVERSION = "0.2.8"
+const CURRENTVERSION = "0.2.9"
 
-const BODY = "<li>Compatible with more types of disks</li><li>Add usb display</li><li>Change translation</li>"
+const BODY = "<li>Custom installation of new parameters</li><li>Fixed issues</li>"