Browse Source

Add CasaConnect function

link 3 years ago
parent
commit
dd0645ee0f
61 changed files with 1163 additions and 1277 deletions
  1. 1 1
      UI
  2. 5 8
      conf/conf.ini.sample
  3. 1 0
      go.mod
  4. 1 0
      go.sum
  5. 33 5
      main.go
  6. 13 15
      model/net.go
  7. 7 0
      model/person.go
  8. 8 13
      model/sys_common.go
  9. 9 0
      model/user.go
  10. 4 4
      model/zima.go
  11. 0 4
      pkg/config/init.go
  12. 19 2
      pkg/config/update.go
  13. 1 1
      pkg/sqlite/db.go
  14. 13 0
      pkg/utils/file/file.go
  15. 3 1
      pkg/utils/httper/httper.go
  16. 0 1
      pkg/utils/ini_helper.go
  17. 16 5
      pkg/utils/oasis_err/e.go
  18. 0 47
      pkg/zerotier/zerotier_api.go
  19. 24 9
      route/init.go
  20. 27 57
      route/route.go
  21. 1 1
      route/v1/app.go
  22. 49 4
      route/v1/file.go
  23. 293 81
      route/v1/persion.go
  24. 55 1
      route/v1/system.go
  25. 48 11
      route/v1/user.go
  26. 0 475
      route/v1/zerotier.go
  27. 1 1
      route/v1/zima_info.go
  28. 45 2
      service/casa.go
  29. 23 15
      service/download.go
  30. 8 5
      service/friend.go
  31. 18 15
      service/model/o_download.go
  32. 7 5
      service/model/o_friend.go
  33. 21 0
      service/notify.go
  34. 55 15
      service/person.go
  35. 0 6
      service/service.go
  36. 16 0
      service/system.go
  37. 126 53
      service/udpconn.go
  38. 0 353
      service/zerotier.go
  39. 69 2
      service/zima_info.go
  40. 5 10
      shell/assist.sh
  41. 18 1
      shell/helper.sh
  42. 5 2
      shell/usb-mount.sh
  43. 2 0
      types/notify.go
  44. 1 0
      types/person.go
  45. 1 0
      types/person_download.go
  46. 2 2
      types/system.go
  47. BIN
      web/img/1-small.1b74d2ba.png
  48. 55 0
      web/img/folder-publicshare.0219e0d4.svg
  49. BIN
      web/img/folder.c8ff81f3.png
  50. BIN
      web/img/xfile.402f9e59.png
  51. 1 1
      web/index.html
  52. 9 9
      web/js/0.js
  53. 0 8
      web/js/1.js
  54. 9 10
      web/js/2.js
  55. 0 8
      web/js/3.js
  56. 4 4
      web/js/4.js
  57. 4 4
      web/js/5.js
  58. 10 0
      web/js/6.js
  59. 10 0
      web/js/7.js
  60. 0 0
      web/js/app.js
  61. 7 0
      web/js/chunk-vendors.js

+ 1 - 1
UI

@@ -1 +1 @@
-Subproject commit 247c099bf14a2d9eb94bf7798e04d00dbc8f7efd
+Subproject commit 74fa1f8920aa23f40b04b87cc04ebef5c36b0890

+ 5 - 8
conf/conf.ini.sample

@@ -14,11 +14,12 @@ RootPath = /casaOS
 
 [server]
 HttpPort = 8089
+UDPPort = 
 RunMode = release
 ServerApi = https://api.casaos.zimaboard.com
-Handshake = 
+Handshake = socket.casaos.io
 Token = 
-NickName = 
+USBAutoMount = true
 
 
 [user]
@@ -28,11 +29,7 @@ Email = user@gmail.com
 Description = description
 Initialized = false
 Avatar = 
-
-[zerotier]
-UserName = user
-PWD = pwd
-Token = yBKYyavr2RdFAIVN7iTpzlsB1o6CqTgm
+NickName = 
 
 [redis]
 Host = 127.0.0.1:6379
@@ -48,4 +45,4 @@ Analyse =
 
 [file]
 ShareDir =
-DownloadDir = /DATA
+DownloadDir =

+ 1 - 0
go.mod

@@ -50,6 +50,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/spf13/afero v1.2.2
 	github.com/swaggo/gin-swagger v1.3.0
 	github.com/swaggo/swag v1.7.3
 	github.com/tidwall/gjson v1.10.2

+ 1 - 0
go.sum

@@ -794,6 +794,7 @@ github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:Udh
 github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
 github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
 github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
 github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
 github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
 github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=

+ 33 - 5
main.go

@@ -4,9 +4,9 @@ import (
 	"flag"
 	"fmt"
 	"net/http"
+	"runtime"
 	"time"
 
-	"github.com/IceWhaleTech/CasaOS/model"
 	"github.com/IceWhaleTech/CasaOS/pkg/cache"
 	"github.com/IceWhaleTech/CasaOS/pkg/config"
 	"github.com/IceWhaleTech/CasaOS/pkg/sqlite"
@@ -29,6 +29,24 @@ func init() {
 	config.InitSetup(*configFlag)
 	config.UpdateSetup()
 	loger2.LogSetup()
+	sysType := runtime.GOOS
+	if sysType == "windows" {
+		config.AppInfo.ProjectPath = "C:\\CasaOS\\service"
+		config.Cfg.Section("app").Key("ProjectPath").SetValue("C:\\CasaOS\\service")
+
+		config.AppInfo.RootPath = "C:\\CasaOS"
+		config.Cfg.Section("app").Key("RootPath").SetValue("C:\\CasaOS")
+		config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath)
+	}
+	if sysType == "darwin" {
+		config.AppInfo.ProjectPath = "./CasaOS/service"
+		config.Cfg.Section("app").Key("ProjectPath").SetValue("./CasaOS/service")
+
+		config.AppInfo.RootPath = "./CasaOS"
+		config.Cfg.Section("app").Key("RootPath").SetValue("./CasaOS")
+		config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath)
+	}
+
 	sqliteDB = sqlite.GetDb(config.AppInfo.ProjectPath)
 	//gredis.GetRedisConn(config.RedisInfo),
 	service.MyService = service.NewService(sqliteDB, loger2.NewOLoger())
@@ -36,10 +54,10 @@ func init() {
 
 	go service.UDPService()
 
-	service.Summary = make(map[string]model.FileSummaryModel)
+	fmt.Println("token", service.GetToken())
 	service.UDPAddressMap = make(map[string]string)
 	//go service.SocketConnect()
-
+	service.CancelList = make(map[string]string)
 	route.InitFunction()
 
 	go service.SendIPToServer()
@@ -69,20 +87,30 @@ func main() {
 	//gredis.Setup()
 	r := route.InitRouter()
 	//service.SyncTask(sqliteDB)
-	cron2 := cron.New() //创建一个cron实例
+	cron2 := cron.New()
 	//every day execution
 	err := cron2.AddFunc("0 0/5 * * * *", func() {
 		//service.PushIpInfo(*&config.ServerInfo.Token)
 		//service.UpdataDDNSList(mysqldb)
 		//service.SyncTask(sqliteDB)
+
 		service.SendIPToServer()
+
 		service.LoopFriend()
+
 	})
 	if err != nil {
 		fmt.Println(err)
 	}
+	// err = cron2.AddFunc("0/1 * * * * *", func() {
+
+	// 	//service.SendIPToServer()
+	// 	//service.LoopNet()
 
-	//启动/关闭
+	// })
+	// if err != nil {
+	// 	fmt.Println(err)
+	// }
 	cron2.Start()
 	defer cron2.Stop()
 	s := &http.Server{

+ 13 - 15
model/net.go

@@ -1,19 +1,17 @@
 package model
 
-import "time"
-
 type IOCountersStat struct {
-	Name        string    `json:"name"`        // interface name
-	BytesSent   uint64    `json:"bytesSent"`   // number of bytes sent
-	BytesRecv   uint64    `json:"bytesRecv"`   // number of bytes received
-	PacketsSent uint64    `json:"packetsSent"` // number of packets sent
-	PacketsRecv uint64    `json:"packetsRecv"` // number of packets received
-	Errin       uint64    `json:"errin"`       // total number of errors while receiving
-	Errout      uint64    `json:"errout"`      // total number of errors while sending
-	Dropin      uint64    `json:"dropin"`      // total number of incoming packets which were dropped
-	Dropout     uint64    `json:"dropout"`     // total number of outgoing packets which were dropped (always 0 on OSX and BSD)
-	Fifoin      uint64    `json:"fifoin"`      // total number of FIFO buffers errors while receiving
-	Fifoout     uint64    `json:"fifoout"`     // total number of FIFO buffers errors while sending
-	State       string    `json:"state"`
-	DateTime    time.Time `json:"date_time"`
+	Name        string `json:"name"`        // interface name
+	BytesSent   uint64 `json:"bytesSent"`   // number of bytes sent
+	BytesRecv   uint64 `json:"bytesRecv"`   // number of bytes received
+	PacketsSent uint64 `json:"packetsSent"` // number of packets sent
+	PacketsRecv uint64 `json:"packetsRecv"` // number of packets received
+	Errin       uint64 `json:"errin"`       // total number of errors while receiving
+	Errout      uint64 `json:"errout"`      // total number of errors while sending
+	Dropin      uint64 `json:"dropin"`      // total number of incoming packets which were dropped
+	Dropout     uint64 `json:"dropout"`     // total number of outgoing packets which were dropped (always 0 on OSX and BSD)
+	Fifoin      uint64 `json:"fifoin"`      // total number of FIFO buffers errors while receiving
+	Fifoout     uint64 `json:"fifoout"`     // total number of FIFO buffers errors while sending
+	State       string `json:"state"`
+	Time        int64  `json:"time"`
 }

+ 7 - 0
model/person.go

@@ -45,3 +45,10 @@ type FileSummaryModel struct {
 	Size      int64  `json:"size"`
 	Message   string `json:"message"`
 }
+
+type FriendsModel struct {
+	Id       uint   `gorm:"column:id;primary_key" json:"id"`
+	NickName string `json:"nick_name"`
+	Desc     string `json:"desc"`
+	ShareId  string `json:"share_id"`
+}

+ 8 - 13
model/sys_common.go

@@ -22,12 +22,14 @@ type UserModel struct {
 
 //服务配置
 type ServerModel struct {
-	HttpPort    string
-	RunMode     string
-	ServerApi   string
-	LockAccount bool
-	Handshake   string
-	Token       string
+	HttpPort     string
+	RunMode      string
+	ServerApi    string
+	LockAccount  bool
+	Handshake    string
+	Token        string
+	UDPPort      string
+	USBAutoMount string
 }
 
 //服务配置
@@ -50,13 +52,6 @@ type Result struct {
 	Data    interface{} `json:"data" example:"返回结果"`
 }
 
-//zeritier相关
-type ZeroTierModel struct {
-	UserName string
-	PWD      string
-	Token    string
-}
-
 //redis配置文件
 type RedisModel struct {
 	Host        string

+ 9 - 0
model/user.go

@@ -0,0 +1,9 @@
+package model
+
+type UserInfo struct {
+	NickName string `json:"nick_name"`
+	Desc     string `json:"desc"`
+	ShareId  string `json:"share_id"`
+	Avatar   string `json:"avatar"`
+	Version  int    `json:"version,omitempty"`
+}

+ 4 - 4
model/zima.go

@@ -3,11 +3,11 @@ package model
 import "time"
 
 type Path struct {
-	Name  string    `json:"name"`
-	Path  string    `json:"path"`
-	IsDir bool      `json:"is_dir"`
+	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"`
+	Size  int64     `json:"size"` //File Size
 	Type  string    `json:"type,omitempty"`
 	Label string    `json:"label,omitempty"`
 }

+ 0 - 4
pkg/config/init.go

@@ -25,9 +25,6 @@ var AppInfo = &model.APPModel{}
 //redis相关配置
 var RedisInfo = &model.RedisModel{}
 
-//zerotier相关
-var ZeroTierInfo = &model.ZeroTierModel{}
-
 //server相关
 var ServerInfo = &model.ServerModel{}
 
@@ -56,7 +53,6 @@ func InitSetup(config string) {
 
 	mapTo("user", UserInfo)
 	mapTo("app", AppInfo)
-	mapTo("zerotier", ZeroTierInfo)
 	mapTo("redis", RedisInfo)
 	mapTo("server", ServerInfo)
 	mapTo("system", SystemConfigInfo)

+ 19 - 2
pkg/config/update.go

@@ -1,13 +1,30 @@
 package config
 
-import "github.com/IceWhaleTech/CasaOS/pkg/utils/file"
+import (
+	"runtime"
+
+	"github.com/IceWhaleTech/CasaOS/pkg/utils/file"
+)
 
 //检查目录是否存在
 func mkdirDATAAll() {
-	dirArray := [7]string{"/DATA/AppData", "/DATA/Documents", "/DATA/Downloads", "/DATA/Gallery", "/DATA/Media/Movies", "/DATA/Media/TV Shows", "/DATA/Media/Music"}
+	sysType := runtime.GOOS
+	var dirArray []string
+	if sysType == "linux" {
+		dirArray = []string{"/DATA/AppData", "/DATA/Documents", "/DATA/Downloads", "/DATA/Gallery", "/DATA/Media/Movies", "/DATA/Media/TV Shows", "/DATA/Media/Music"}
+	}
+
+	if sysType == "windows" {
+		dirArray = []string{"C:\\CasaOS\\DATA\\AppData", "C:\\CasaOS\\DATA\\Documents", "C:\\CasaOS\\DATA\\Downloads", "C:\\CasaOS\\DATA\\Gallery", "C:\\CasaOS\\DATA\\Media/Movies", "C:\\CasaOS\\DATA\\Media\\TV Shows", "C:\\CasaOS\\DATA\\Media\\Music"}
+	}
+	if sysType == "darwin" {
+		dirArray = []string{"./CasaOS/DATA/AppData", "./CasaOS/DATA/Documents", "./CasaOS/DATA/Downloads", "./CasaOS/DATA/Gallery", "./CasaOS/DATA/Media/Movies", "./CasaOS/DATA/Media/TV Shows", "./CasaOS/DATA/Media/Music"}
+	}
+
 	for _, v := range dirArray {
 		file.IsNotExistMkDir(v)
 	}
+
 }
 
 func UpdateSetup() {

+ 1 - 1
pkg/sqlite/db.go

@@ -31,7 +31,7 @@ func GetDb(projectPath string) *gorm.DB {
 		return nil
 	}
 	gdb = db
-	err = db.AutoMigrate(&model2.TaskDBModel{}, &model2.AppNotify{}, &model2.AppListDBModel{}, &model2.SerialDisk{}, model2.PersionDownloadDBModel{}, model2.FriendModel{})
+	err = db.AutoMigrate(&model2.TaskDBModel{}, &model2.AppNotify{}, &model2.AppListDBModel{}, &model2.SerialDisk{}, model2.PersonDownloadDBModel{}, model2.FriendModel{})
 	if err != nil {
 		fmt.Println("检查和创建数据库出错", err)
 	}

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

@@ -8,6 +8,8 @@ import (
 	"mime/multipart"
 	"os"
 	"path"
+	path2 "path"
+	"path/filepath"
 	"strconv"
 	"strings"
 )
@@ -226,6 +228,17 @@ func CopyFile(src, dst string) error {
 	return os.Chmod(dst, srcinfo.Mode())
 }
 
+//Check for duplicate file names
+func GetNoDuplicateFileName(fullPath string) string {
+	path, fileName := filepath.Split(fullPath)
+	fileSuffix := path2.Ext(fileName)
+	filenameOnly := strings.TrimSuffix(fileName, fileSuffix)
+	for i := 0; Exists(fullPath); i++ {
+		fullPath = path2.Join(path, filenameOnly+"("+strconv.Itoa(i+1)+")"+fileSuffix)
+	}
+	return fullPath
+}
+
 // Dir copies a whole directory recursively
 func CopyDir(src string, dst string) error {
 	var err error

+ 3 - 1
pkg/utils/httper/httper.go

@@ -3,6 +3,7 @@ package httper
 import (
 	"bytes"
 	"encoding/json"
+	"fmt"
 	"io"
 	"io/ioutil"
 	"net/http"
@@ -67,7 +68,8 @@ func Post(url string, data []byte, contentType string, head map[string]string) (
 	client := &http.Client{Timeout: 5 * time.Second}
 	resp, error := client.Do(req)
 	if error != nil {
-		panic(error)
+		fmt.Println(error)
+		return
 	}
 	defer resp.Body.Close()
 

+ 0 - 1
pkg/utils/ini_helper.go

@@ -1 +0,0 @@
-package utils

+ 16 - 5
pkg/utils/oasis_err/e.go

@@ -37,13 +37,18 @@ const (
 	FILE_DOES_NOT_EXIST = 60001
 	FILE_READ_ERROR     = 60002
 	FILE_DELETE_ERROR   = 60003
+	DIR_NOT_EXISTS      = 60004
 
 	//shortcuts
 	SHORTCUTS_URL_ERROR = 70001
 
-	//persion
-	PERSION_REMOTE_ERROR   = 80001
-	PERSION_DOWN_NOT_EXIST = 80002
+	//person
+	PERSON_REMOTE_ERROR   = 80001
+	PERSON_DOWN_NOT_EXIST = 80002
+	PERSON_EXIST_DOWNLOAD = 80003
+	PERSON_NOT_EXIST_USER = 80004
+	PERSON_EXIST_FRIEND   = 80005
+	PERSON_MYSELF         = 80006
 )
 
 var MsgFlags = map[int]string{
@@ -82,12 +87,18 @@ var MsgFlags = map[int]string{
 	//
 	FILE_DOES_NOT_EXIST: "File does not exist",
 
+	DIR_NOT_EXISTS: "Directory does not exist",
+
 	FILE_READ_ERROR:     "File read error",
 	FILE_DELETE_ERROR:   "Delete error",
 	SHORTCUTS_URL_ERROR: "URL error",
 
-	PERSION_REMOTE_ERROR:   "Remote connection error",
-	PERSION_DOWN_NOT_EXIST: "Download record does not exist",
+	PERSON_REMOTE_ERROR:   "Remote connection error",
+	PERSON_DOWN_NOT_EXIST: "Download record does not exist",
+	PERSON_EXIST_DOWNLOAD: "The same download task exists",
+	PERSON_EXIST_FRIEND:   "Friend already exist",
+	PERSON_NOT_EXIST_USER: "User does not exist",
+	PERSON_MYSELF:         "You can not add yourself",
 }
 
 //获取错误信息

+ 0 - 47
pkg/zerotier/zerotier_api.go

@@ -1,47 +0,0 @@
-package zerotier
-
-import (
-	httper2 "github.com/IceWhaleTech/CasaOS/pkg/utils/httper"
-	"github.com/tidwall/gjson"
-	"net/http"
-)
-
-func PostData(url, token string, data string) interface{} {
-
-	body, code := httper2.ZeroTierPostJson(url, data, GetHead(token))
-
-	if code != http.StatusOK {
-		return ""
-	}
-	result := gjson.Parse(body)
-	return result.Value()
-}
-
-func GetData(url, token string) interface{} {
-
-	body, code := httper2.ZeroTierGet(url, GetHead(token))
-
-	if code != http.StatusOK {
-		return ""
-	}
-	result := gjson.Parse(body)
-	return result.Value()
-}
-
-func DeleteMember(url, token string) interface{} {
-
-	body, code := httper2.ZeroTierDelete(url, GetHead(token))
-
-	if code != http.StatusOK {
-		return ""
-	}
-	result := gjson.Parse(body)
-	return result.Value()
-}
-
-func GetHead(token string) map[string]string {
-	var head = make(map[string]string)
-	head["Authorization"] = "Bearer " + token
-	head["Content-Type"] = "application/json"
-	return head
-}

+ 24 - 9
route/init.go

@@ -4,6 +4,7 @@ import (
 	"encoding/json"
 	"encoding/xml"
 	"fmt"
+	"runtime"
 	"strconv"
 	"time"
 
@@ -231,10 +232,10 @@ func CheckSerialDiskMount() {
 	}
 	service.MyService.Disk().RemoveLSBLKCache()
 	command.OnlyExec("source " + config.AppInfo.ProjectPath + "/shell/helper.sh ;AutoRemoveUnuseDir")
-
 }
 func Update2_3() {
 	command.OnlyExec("source " + config.AppInfo.ProjectPath + "/shell/assist.sh")
+
 }
 func CheckToken2_11() {
 	if len(config.ServerInfo.Token) == 0 {
@@ -253,19 +254,33 @@ func CheckToken2_11() {
 	// 	config.AppInfo.RootPath = "/casaOS"
 	// 	config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath)
 	// }
-	if len(config.FileSettingInfo.ShareDir) == 0 {
-		config.Cfg.Section("file").Key("ShareDir").SetValue("/DATA")
-		config.FileSettingInfo.ShareDir[0] = "/DATA"
-
+	sysType := runtime.GOOS
+	if len(config.FileSettingInfo.DownloadDir) == 0 {
+		downloadPath := "/DATA/Downloads"
+		if sysType == "windows" {
+			downloadPath = "C:\\CasaOS\\DATA\\Downloads"
+		}
+		if sysType == "darwin" {
+			downloadPath = "~/CasaOS/DATA/Downloads"
+		}
+		config.Cfg.Section("file").Key("DownloadDir").SetValue(downloadPath)
+		config.FileSettingInfo.DownloadDir = downloadPath
+		file.IsNotExistMkDir(config.FileSettingInfo.DownloadDir)
 		config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath)
 	}
 
-	if len(config.FileSettingInfo.DownloadDir) == 0 {
-		config.Cfg.Section("file").Key("DownloadDir").SetValue("/DATA/share")
-		config.FileSettingInfo.DownloadDir = "/DATA/share"
-		file.IsNotExistMkDir(config.FileSettingInfo.DownloadDir)
+	if len(config.UserInfo.Description) == 0 {
+		config.Cfg.Section("user").Key("Description").SetValue("nothing")
+		config.UserInfo.Description = "nothing"
 		config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath)
 	}
+	if len(config.ServerInfo.Handshake) == 0 {
+		config.Cfg.Section("server").Key("Handshake").SetValue("socket.casaos.io")
+		config.ServerInfo.Handshake = "socket.casaos.io"
+		config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath)
+	}
+
+	service.MyService.System().ExecUSBAutoMountShell(config.ServerInfo.USBAutoMount)
 
 	// str := []string{}
 	// str = append(str, "ddd")

+ 27 - 57
route/route.go

@@ -18,6 +18,7 @@ var OnlineDemo bool = false
 func InitRouter() *gin.Engine {
 
 	r := gin.Default()
+
 	r.Use(middleware.Cors())
 	r.Use(gzip.Gzip(gzip.DefaultCompression))
 	gin.SetMode(config.ServerInfo.RunMode)
@@ -52,13 +53,16 @@ func InitRouter() *gin.Engine {
 			//chang head
 			v1UserGroup.POST("/head", v1.PostUserHead)
 			//chang user name
-			v1UserGroup.PUT("/changusername", v1.PutUserName)
+			v1UserGroup.PUT("/username", v1.PutUserName)
 			//chang pwd
-			v1UserGroup.PUT("/changuserpwd", v1.PutUserPwd)
+			v1UserGroup.PUT("/password", v1.PutUserPwd)
 			//edit user info
 			v1UserGroup.POST("/info", v1.PostUserChangeInfo)
 			v1UserGroup.PUT("/nick", v1.PutUserChangeNick)
 			v1UserGroup.PUT("/desc", v1.PutUserChangeDesc)
+			v1UserGroup.POST("/person/info", v1.PostUserPersonInfo)
+
+			v1UserGroup.GET("/shareid", v1.GetUserShareID)
 
 		}
 
@@ -78,51 +82,6 @@ func InitRouter() *gin.Engine {
 			//获取系统信息
 			v1ZiMaGroup.GET("/sysinfo", v1.SysInfo)
 		}
-
-		v1ZeroTierGroup := v1Group.Group("/zerotier")
-		v1ZeroTierGroup.Use()
-		{
-			//获取zerotier token
-			v1ZeroTierGroup.POST("/login", v1.ZeroTierGetToken)
-			//注册zerotier
-			v1ZeroTierGroup.POST("/register", v1.ZeroTierRegister)
-			//是否需要登录
-			v1ZeroTierGroup.GET("/islogin", v1.ZeroTierIsNeedLogin)
-			//获取网络列表
-			v1ZeroTierGroup.GET("/list", v1.ZeroTierGetNetworkList)
-			//加入网络
-			v1ZeroTierGroup.POST("/join/:id", v1.ZeroTierJoinNetwork)
-			//离开网络
-			v1ZeroTierGroup.POST("/leave/:id", v1.ZeroTierLeaveNetwork)
-			//详情
-			v1ZeroTierGroup.GET("/info/:id", v1.ZeroTierGetNetworkGetInfo)
-			////网络状态
-			//v1ZeroTierGroup.GET("/status", v1.ZeroTierGetNetworkGetStatus)
-			//修改网络类型
-			//v1ZeroTierGroup.PUT("/type/:id", v1.ZeroTierEditType)
-			//修改网络类型
-			//v1ZeroTierGroup.PUT("/name/:id", v1.ZeroTierEditName)
-			//修改v6 assign
-			//v1ZeroTierGroup.PUT("/v6assign/:id", v1.ZeroTierEditV6Assign)
-			//修改 broadcast
-			//v1ZeroTierGroup.PUT("/broadcast/:id", v1.ZeroTierEditBroadcast)
-			//create new network
-			v1ZeroTierGroup.POST("/create", v1.ZeroTierCreateNetwork)
-			//获取用户列表
-			v1ZeroTierGroup.GET("/member/:id", v1.ZeroTierMemberList)
-			//修改用户信息
-			//v1ZeroTierGroup.PUT("/members/:id/auth/:mId", v1.ZeroTierMemberAuth)
-			//修改网络用户name
-			//v1ZeroTierGroup.PUT("/members/:id/name/:mId", v1.ZeroTierMemberName)
-			v1ZeroTierGroup.DELETE("/members/:id/del/:mId", v1.ZeroTierMemberDelete)
-			v1ZeroTierGroup.DELETE("/network/:id/del", v1.ZeroTierDeleteNetwork)
-			//修改网络用户bridge功能
-			//v1ZeroTierGroup.PUT("/members/:id/bridge/:mId", v1.ZeroTierMemberBridge)
-			v1ZeroTierGroup.PUT("/edit/:id", v1.ZeroTierEdit)
-			v1ZeroTierGroup.GET("/joined/list", v1.ZeroTierJoinedList)
-			v1ZeroTierGroup.PUT("/member/:id/edit/:mId", v1.ZeroTierMemberEdit)
-
-		}
 		v1DDNSGroup := v1Group.Group("/ddns")
 		v1DDNSGroup.Use()
 		{
@@ -201,6 +160,9 @@ func InitRouter() *gin.Engine {
 			v1SysGroup.PUT("/port", v1.PutCasaOSPort)
 			v1SysGroup.POST("/kill", v1.PostKillCasaOS)
 			v1SysGroup.GET("/info", v1.Info)
+			v1SysGroup.PUT("/usb/off", v1.PutSystemOffUSBAutoMount)
+			v1SysGroup.GET("/usb/on", v1.PutSystemOnUSBAutoMount)
+			v1SysGroup.GET("/usb", v1.GetSystemUSBAutoMount)
 		}
 		v1FileGroup := v1Group.Group("/file")
 		v1FileGroup.Use()
@@ -216,6 +178,7 @@ func InitRouter() *gin.Engine {
 			v1FileGroup.POST("/create", v1.PostCreateFile)
 
 			v1FileGroup.GET("/download", v1.GetDownloadFile)
+			v1FileGroup.GET("/new/download", v1.GetFileDownloadNew)
 			v1FileGroup.POST("/operate", v1.PostOperateFileOrDir)
 			v1FileGroup.DELETE("/delete", v1.DeleteFile)
 			v1FileGroup.PUT("/update", v1.PutFileContent)
@@ -289,19 +252,26 @@ func InitRouter() *gin.Engine {
 		{
 			v1SearchGroup.GET("/search", v1.GetSearchList)
 		}
-		v1PersonGroup := v1Group.Group("/persion")
+		v1PersonGroup := v1Group.Group("/person")
 		v1PersonGroup.Use()
 		{
 			v1PersonGroup.GET("/test", v1.PersonTest)
-			v1PersonGroup.GET("/users", v1.GetPersionFriend)
-			v1PersonGroup.POST("/user", v1.PostAddPersionFriend)
-			v1PersonGroup.GET("/directory", v1.GetPersionDirectory)
-			v1PersonGroup.GET("/file", v1.GetPersionFile)
-			v1PersonGroup.GET("/refile/:uuid", v1.GetPersionReFile)
-			v1PersonGroup.PUT("/nick/:token", v1.PutPersionNick)
-			v1PersonGroup.GET("/list", v1.GetPersionDownloadList)
-			v1PersonGroup.DELETE("/file/:uuid", v1.DeletePersionDownloadFile)
-			// v1PersonGroup.PUT("/state/:id", v1.PutPersionCancelDownload) //修改下载状态(开始暂停删除)
+			v1PersonGroup.GET("/users", v1.GetPersonFriend)
+			v1PersonGroup.POST("/user/:shareids", v1.PostAddPersonFriend)
+			v1PersonGroup.DELETE("/user/:shareid", v1.DeletePersonFriend)
+			v1PersonGroup.GET("/directory", v1.GetPersonDirectory)
+			v1PersonGroup.GET("/file", v1.GetPersonFile)
+			v1PersonGroup.GET("/refile/:uuid", v1.GetPersonReFile)
+			v1PersonGroup.PUT("/remarks/:shareid", v1.PutPersonRemarks)
+			v1PersonGroup.GET("/list", v1.GetPersonDownloadList)
+			v1PersonGroup.DELETE("/file/:uuid", v1.DeletePersonDownloadFile)
+
+			v1PersonGroup.POST("/share", v1.PostPersonShare)
+			v1PersonGroup.GET("/share", v1.GetPersonShare)
+			v1PersonGroup.POST("/down/dir", v1.PostPersonDownDir)
+			v1PersonGroup.GET("/down/dir", v1.GetPersonDownDir)
+			v1PersonGroup.PUT("/block/:shareid", v1.PutPersonBlock)
+			v1PersonGroup.GET("/public", v1.GetPersonPublic)
 
 		}
 		v1AnalyseGroup := v1Group.Group("/analyse")

+ 1 - 1
route/v1/app.go

@@ -248,7 +248,7 @@ func ShareAppFile(c *gin.Context) {
 // @Tags app
 // @Security ApiKeyAuth
 // @Success 200 {string} string "ok"
-// @Router /app/share [post]
+// @Router /app/shares [post]
 func AppListResourceUsage() {
 
 }

+ 49 - 4
route/v1/file.go

@@ -7,6 +7,7 @@ import (
 	"io"
 	"io/ioutil"
 	"net/http"
+	url2 "net/url"
 	"os"
 	"path"
 	"strconv"
@@ -17,6 +18,7 @@ import (
 	oasis_err2 "github.com/IceWhaleTech/CasaOS/pkg/utils/oasis_err"
 	"github.com/IceWhaleTech/CasaOS/service"
 	"github.com/gin-gonic/gin"
+	"github.com/spf13/afero"
 )
 
 func downloadReadFile(c *gin.Context) {
@@ -157,16 +159,59 @@ func GetDownloadFile(c *gin.Context) {
 	//获取文件的名称
 	fileName := path.Base(filePath)
 	c.Header("Content-Type", "application/octet-stream")
-	c.Header("Content-Disposition", "attachment; filename="+fileName)
+	c.Header("Content-Disposition", "attachment; filename*=utf-8''"+url2.PathEscape(fileName))
 	c.Header("Content-Transfer-Encoding", "binary")
 	c.Header("Cache-Control", "no-cache")
-	c.Header("Content-Type", "application/octet-stream")
-	c.Header("Content-Disposition", "attachment; filename="+fileName)
-	c.Header("Content-Transfer-Encoding", "binary")
 
 	c.File(filePath)
 }
 
+// @Summary download
+// @Produce  application/json
+// @Accept application/json
+// @Tags file
+// @Security ApiKeyAuth
+// @Param path query string true "path of file"
+// @Success 200 {string} string "ok"
+// @Router /file/new/download [get]
+func GetFileDownloadNew(c *gin.Context) {
+	filePath := c.Query("path")
+	if len(filePath) == 0 {
+		c.JSON(http.StatusOK, model.Result{
+			Success: oasis_err2.INVALID_PARAMS,
+			Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS),
+		})
+		return
+	}
+	if !file.Exists(filePath) {
+		c.JSON(http.StatusOK, model.Result{
+			Success: oasis_err2.FILE_DOES_NOT_EXIST,
+			Message: oasis_err2.GetMsg(oasis_err2.FILE_DOES_NOT_EXIST),
+		})
+		return
+	}
+	//打开文件
+	fileStat, _ := os.Stat(filePath)
+	var AppFs = afero.NewOsFs()
+	fileT, _ := AppFs.Open(filePath)
+	//fileTmp, _ := os.Open(filePath)
+	//defer fileTmp.Close()
+	//获取文件的名称
+	//fileName := path.Base(filePath)
+
+	//c.Header("Content-Disposition", "attachment; filename*=utf-8''"+url2.PathEscape(fileName))
+	//在线
+	//c.Header("Content-Disposition", "inline")
+	// extraHeaders := map[string]string{
+	// 	"Content-Disposition": `attachment; filename="` + url2.PathEscape(fileName) + `"`,
+	// }
+
+	//c.Header("Cache-Control", "private")
+	//c.Header("Content-Type", "application/octet-stream")
+
+	http.ServeContent(c.Writer, c.Request, fileStat.Name(), fileStat.ModTime(), fileT)
+}
+
 // @Summary 获取目录列表
 // @Produce  application/json
 // @Accept application/json

+ 293 - 81
route/v1/persion.go

@@ -7,9 +7,12 @@ import (
 	"net/http"
 	"reflect"
 	"strconv"
+	"strings"
+	"time"
 
 	"github.com/IceWhaleTech/CasaOS/model"
 	"github.com/IceWhaleTech/CasaOS/pkg/config"
+	"github.com/IceWhaleTech/CasaOS/pkg/utils/file"
 	oasis_err2 "github.com/IceWhaleTech/CasaOS/pkg/utils/oasis_err"
 	"github.com/IceWhaleTech/CasaOS/service"
 	model2 "github.com/IceWhaleTech/CasaOS/service/model"
@@ -19,8 +22,10 @@ import (
 )
 
 func PersonTest(c *gin.Context) {
-
 	token := c.Query("token")
+	_, err := uuid.FromString(token)
+	fmt.Println(err)
+
 	//service.MyService.Person().GetPersionInfo("fb2333a1-72b2-4cb4-9e31-61ccaffa55b9")
 
 	msg := model.MessageModel{}
@@ -35,45 +40,48 @@ func PersonTest(c *gin.Context) {
 		fmt.Println(err)
 	}
 	fmt.Println(dd)
+	user := service.MyService.Casa().GetUserInfoByShareId(token)
+	if reflect.DeepEqual(user, model.UserInfo{}) {
+		fmt.Println("空数据")
+	}
+	fmt.Println(user)
 	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
 }
 
-// @Summary retry download file
+// @Summary Retry the file that failed to download
 // @Produce  application/json
 // @Accept application/json
-// @Tags persion
+// @Tags person
 // @Param  uui path string true "download uuid"
-// @Param  path query string true "file path"
 // @Security ApiKeyAuth
 // @Success 200 {string} string "ok"
-// @Router /persion/refile/{uuid} [get]
-func GetPersionReFile(c *gin.Context) {
+// @Router /person/refile/{uuid} [get]
+func GetPersonReFile(c *gin.Context) {
 
-	path := c.Query("path")
-	uuid := c.Param("uuid")
-
-	if len(path) == 0 && len(uuid) == 0 {
+	uid := c.Param("uuid")
+	_, err := uuid.FromString(uid)
+	if err != nil {
 		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
 		return
 	}
 
-	task := service.MyService.Download().GetDownloadById(uuid)
-	if reflect.DeepEqual(task, model2.PersionDownloadDBModel{}) {
-		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.PERSION_REMOTE_ERROR, Message: oasis_err2.GetMsg(oasis_err2.PERSION_REMOTE_ERROR)})
+	task := service.MyService.Download().GetDownloadById(uid)
+	if reflect.DeepEqual(task, model2.PersonDownloadDBModel{}) {
+		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.PERSON_REMOTE_ERROR, Message: oasis_err2.GetMsg(oasis_err2.PERSON_REMOTE_ERROR)})
 		return
 	}
 	token := task.From
 	if _, ok := service.UDPAddressMap[token]; !ok {
-		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.PERSION_REMOTE_ERROR, Message: oasis_err2.GetMsg(oasis_err2.PERSION_REMOTE_ERROR)})
+		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.PERSON_REMOTE_ERROR, Message: oasis_err2.GetMsg(oasis_err2.PERSON_REMOTE_ERROR)})
 		return
 	}
 
 	m := model.MessageModel{}
-	m.Data = path
+	m.Data = task.Path
 	m.From = config.ServerInfo.Token
 	m.To = token
 	m.Type = types.PERSONDOWNLOAD
-	m.UUId = uuid
+	m.UUId = uid
 	go service.Dial(m, false)
 
 	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
@@ -82,35 +90,57 @@ func GetPersionReFile(c *gin.Context) {
 // @Summary download file
 // @Produce  application/json
 // @Accept application/json
-// @Tags persion
-// @Param  token query string true "opponent token"
+// @Tags person
+// @Param  share_id query string true "opponent share_id"
 // @Param  path query string true "file path"
+// @Param  file_name query string true "file name"
+// @Param  local_path query string true "local_path"
 // @Security ApiKeyAuth
 // @Success 200 {string} string "ok"
-// @Router /persion/file [get]
-func GetPersionFile(c *gin.Context) {
+// @Router /person/file [get]
+func GetPersonFile(c *gin.Context) {
 
 	path := c.Query("path")
-	token := c.Query("token")
-	if len(path) == 0 && len(token) == 0 {
+	localPath := c.Query("local_path")
+	token := c.Query("share_id")
+	fileName := c.Query("file_name")
+	_, err := uuid.FromString(token)
+	if len(path) == 0 || err != nil || len(localPath) == 0 || len(fileName) == 0 {
 		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
 		return
 	}
+	if file.CheckNotExist(localPath) {
+		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.DIR_NOT_EXISTS, Message: oasis_err2.GetMsg(oasis_err2.DIR_NOT_EXISTS)})
+		return
+	}
+	if _, ok := service.UDPAddressMap[token]; !ok {
+		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.PERSON_REMOTE_ERROR, Message: oasis_err2.GetMsg(oasis_err2.PERSON_REMOTE_ERROR)})
+		return
+	}
+
 	if _, ok := service.UDPAddressMap[token]; !ok {
-		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.PERSION_REMOTE_ERROR, Message: oasis_err2.GetMsg(oasis_err2.PERSION_REMOTE_ERROR)})
+		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.PERSON_REMOTE_ERROR, Message: oasis_err2.GetMsg(oasis_err2.PERSON_REMOTE_ERROR)})
 		return
 	}
+
 	// task id
 	uuid := uuid.NewV4().String()
 
-	task := model2.PersionDownloadDBModel{}
+	task := model2.PersonDownloadDBModel{}
 	task.UUID = uuid
-	task.Name = ""
+	task.Name = fileName
 	task.Length = 0
 	task.From = token
+	task.Path = path
 	task.Size = 0
 	task.State = types.DOWNLOADAWAIT
+	task.Created = time.Now().Unix()
 	task.Type = 0
+	task.LocalPath = localPath
+	if service.MyService.Download().GetDownloadListByPath(task) > 0 {
+		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.PERSON_EXIST_DOWNLOAD, Message: oasis_err2.GetMsg(oasis_err2.PERSON_EXIST_DOWNLOAD)})
+		return
+	}
 	service.MyService.Download().AddDownloadTask(task)
 
 	m := model.MessageModel{}
@@ -127,79 +157,92 @@ func GetPersionFile(c *gin.Context) {
 // @Summary delete download file records
 // @Produce  application/json
 // @Accept application/json
-// @Tags persion
+// @Tags person
 // @Param  uuid path string true "download uuid"
 // @Security ApiKeyAuth
 // @Success 200 {string} string "ok"
-// @Router /persion/file/{uuid} [delete]
-func DeletePersionDownloadFile(c *gin.Context) {
+// @Router /person/file/{uuid} [delete]
+func DeletePersonDownloadFile(c *gin.Context) {
 
 	id := c.Param("uuid")
-	if len(id) == 0 {
+	_, err := uuid.FromString(id)
+	if err != nil {
 		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
 		return
 	}
 
+	task := service.MyService.Download().GetDownloadById(id)
+	if task.State == types.DOWNLOADING {
+		m := model.MessageModel{}
+		m.Data = ""
+		m.From = config.ServerInfo.Token
+		m.To = task.From
+		m.Type = types.PERSONCANCEL
+		m.UUId = task.UUID
+		service.CancelList[task.UUID] = task.UUID
+		service.Dial(m, false)
+	}
 	service.MyService.Download().DelDownload(id)
 
 	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
 }
 
-// @Summary get file download list
+// @Summary Get file download list
 // @Produce  application/json
 // @Accept application/json
-// @Tags persion
-// @Param  state query int true "wait:1,downloading:1,pause:2,finish:3,error:4" Enums(0,1,2,4)
+// @Tags person
+// @Param  state query int false "wait:0,downloading:1,pause:2,finish:3,error:4,finished:5" Enums(0,1,2,3,4,5)
 // @Security ApiKeyAuth
-// @Success 200 {string} string "ok"
-// @Router /persion/list [get]
-func GetPersionDownloadList(c *gin.Context) {
+// @Success 200 {object} []model2.PersonDownloadDBModel
+// @Router /person/list [get]
+func GetPersonDownloadList(c *gin.Context) {
 	state := c.DefaultQuery("state", "")
 	list := service.MyService.Download().GetDownloadListByState(state)
 	//if it is  downloading, it need to add 'already'
-	if state == strconv.Itoa(types.DOWNLOADING) {
-		for i := 0; i < len(list); i++ {
+	for i := 0; i < len(list); i++ {
+		if list[i].State == types.DOWNLOADING {
 			tempDir := config.AppInfo.RootPath + "/temp" + "/" + list[i].UUID
 			files, err := ioutil.ReadDir(tempDir)
 			if err == nil {
 				list[i].Already = len(files)
 			}
 		}
+		list[i].Duration = time.Now().Unix() - list[i].Created
 	}
 	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: list})
 }
 
-// @Summary edit friend's nick
+// @Summary edit friend's remarks
 // @Produce  application/json
 // @Accept application/json
-// @Tags persion
-// @Param token path string true "token"
-// @Param nick formData string true "nick name"
+// @Tags person
+// @Param remarks formData string true "remarks name"
 // @Security ApiKeyAuth
 // @Success 200 {string} string "ok"
-// @Router /persion/nick/{token} [put]
-func PutPersionNick(c *gin.Context) {
-	token := c.Param("token")
-	nick := c.PostForm("nick")
-	if len(token) == 0 || len(nick) == 0 {
+// @Router /person/remarks/{shareid} [put]
+func PutPersonRemarks(c *gin.Context) {
+	token := c.Param("shareid")
+	_, err := uuid.FromString(token)
+	mark := c.PostForm("remarks")
+	if err != nil || len(mark) == 0 {
 		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
 		return
 	}
 	friend := model2.FriendModel{}
 	friend.Token = token
-	friend.NickName = nick
-	service.MyService.Friend().EditFriendNick(friend)
+	friend.Mark = mark
+	service.MyService.Friend().EditFriendMark(friend)
 	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
 }
 
-// @Summary get friend list
+// @Summary get my friend list
 // @Produce  application/json
 // @Accept application/json
-// @Tags persion
+// @Tags person
 // @Security ApiKeyAuth
-// @Success 200 {string} string "ok"
-// @Router /persion/users [get]
-func GetPersionFriend(c *gin.Context) {
+// @Success 200 {object}  []model2.FriendModel
+// @Router /person/users [get]
+func GetPersonFriend(c *gin.Context) {
 	list := service.MyService.Friend().GetFriendList()
 	for i := 0; i < len(list); i++ {
 		if v, ok := service.UDPAddressMap[list[i].Token]; ok && len(v) > 0 {
@@ -212,51 +255,79 @@ func GetPersionFriend(c *gin.Context) {
 // @Summary add friend
 // @Produce  application/json
 // @Accept application/json
-// @Tags persion
-// @Param  token formData int true "Opponent token"
+// @Tags person
 // @Security ApiKeyAuth
 // @Success 200 {string} string "ok"
-// @Router /persion/user [post]
-func PostAddPersionFriend(c *gin.Context) {
-	token := c.PostForm("token")
-	if len(token) == 0 {
-		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
-		return
-	}
+// @Router /person/user/{shareids} [post]
+func PostAddPersonFriend(c *gin.Context) {
+	token := c.Param("shareids")
+	tokenList := strings.Split(token, ",")
 
-	msg := model.MessageModel{}
-	msg.Type = types.PERSONCONNECTION
-	msg.Data = token
-	msg.From = config.ServerInfo.Token
-	msg.To = token
-	msg.UUId = uuid.NewV4().String()
+	for _, v := range tokenList {
+		_, err := uuid.FromString(v)
+		if err != nil {
+			c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
+			return
+		}
 
-	go service.Dial(msg, true)
+		if v == config.ServerInfo.Token {
+			c.JSON(http.StatusOK, model.Result{Success: oasis_err2.PERSON_MYSELF, Message: oasis_err2.GetMsg(oasis_err2.PERSON_MYSELF)})
+			return
+		}
+
+		udb := service.MyService.Friend().GetFriendById(model2.FriendModel{Token: v})
+		if !reflect.DeepEqual(udb, model2.FriendModel{Token: v}) {
+			c.JSON(http.StatusOK, model.Result{Success: oasis_err2.PERSON_EXIST_FRIEND, Message: oasis_err2.GetMsg(oasis_err2.PERSON_EXIST_FRIEND)})
+			return
+		}
+
+		user := service.MyService.Casa().GetUserInfoByShareId(v)
+		if reflect.DeepEqual(user, model.UserInfo{}) {
+			c.JSON(http.StatusOK, model.Result{Success: oasis_err2.PERSON_NOT_EXIST_USER, Message: oasis_err2.GetMsg(oasis_err2.PERSON_NOT_EXIST_USER)})
+			return
+		}
+
+		message := model.MessageModel{}
+		message.Type = types.PERSONCONNECTION
+		message.Data = v
+		message.From = config.ServerInfo.Token
+		message.To = v
+		message.UUId = uuid.NewV4().String()
+
+		go service.Dial(message, true)
+
+		friend := model2.FriendModel{}
+		friend.Token = v
+		friend.Avatar = user.Avatar
+		friend.Block = false
+		friend.NickName = user.NickName
+		friend.Profile = user.Desc
+		friend.Version = user.Version
+		service.MyService.Friend().AddFriend(friend)
+	}
 
-	friend := model2.FriendModel{}
-	friend.Token = token
-	service.MyService.Friend().AddFriend(friend)
 	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
 }
 
-// @Summary get directory list
+// @Summary Get a list of directories
 // @Produce  application/json
 // @Accept application/json
-// @Tags persion
-// @Param  token query string true "Opponent token"
+// @Tags person
+// @Param  share_id query string true "Opponent share_id"
 // @Param  path query string true "dir path"
 // @Security ApiKeyAuth
-// @Success 200 {string} string "ok"
-// @Router /persion/directory [get]
-func GetPersionDirectory(c *gin.Context) {
+// @Success 200 {object}  []model.Path
+// @Router /person/directory [get]
+func GetPersonDirectory(c *gin.Context) {
 	path := c.Query("path")
-	token := c.Query("token")
-	if len(path) == 0 && len(token) == 0 {
+	token := c.Query("share_id")
+	_, err := uuid.FromString(token)
+	if len(path) == 0 || err != nil {
 		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
 		return
 	}
 	if _, ok := service.UDPAddressMap[token]; !ok {
-		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.PERSION_REMOTE_ERROR, Message: oasis_err2.GetMsg(oasis_err2.PERSION_REMOTE_ERROR)})
+		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.PERSON_REMOTE_ERROR, Message: oasis_err2.GetMsg(oasis_err2.PERSON_REMOTE_ERROR)})
 		return
 	}
 	uuid := uuid.NewV4().String()
@@ -282,3 +353,144 @@ func GetPersionDirectory(c *gin.Context) {
 	}
 	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: dataModel})
 }
+
+// @Summary Modify the download storage directory
+// @Produce  application/json
+// @Accept  multipart/form-data
+// @Tags person
+// @Security ApiKeyAuth
+// @Param path formData string true "path"
+// @Success 200 {string} string "ok"
+// @Router /person/down/dir [post]
+func PostPersonDownDir(c *gin.Context) {
+
+	downPath := c.PostForm("path")
+
+	if len(downPath) == 0 {
+		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
+		return
+	}
+	if file.CheckNotExist(downPath) {
+		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.DIR_NOT_EXISTS, Message: oasis_err2.GetMsg(oasis_err2.DIR_NOT_EXISTS)})
+		return
+	}
+	config.Cfg.Section("file").Key("DownloadDir").SetValue(downPath)
+	config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath)
+	config.FileSettingInfo.DownloadDir = downPath
+	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
+}
+
+// @Summary Get the download storage directory
+// @Produce  application/json
+// @Accept application/json
+// @Tags person
+// @Security ApiKeyAuth
+// @Success 200 {string} string "ok"
+// @Router /person/down/dir [get]
+func GetPersonDownDir(c *gin.Context) {
+	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: config.FileSettingInfo.DownloadDir})
+}
+
+// @Summary Modify the shared directory
+// @Produce  application/json
+// @Accept  multipart/form-data
+// @Tags person
+// @Security ApiKeyAuth
+// @Param share formData string true "share"
+// @Success 200 {string} string "ok"
+// @Router /person/share [post]
+func PostPersonShare(c *gin.Context) {
+
+	share := c.PostForm("share")
+
+	if len(share) == 0 {
+		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
+		return
+	}
+
+	var list []string
+	json.Unmarshal([]byte(share), &list)
+
+	if len(list) == 0 {
+		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
+		return
+	}
+	for _, v := range list {
+		if !file.Exists(v) {
+			c.JSON(http.StatusOK, model.Result{Success: oasis_err2.FILE_ALREADY_EXISTS, Message: oasis_err2.GetMsg(oasis_err2.FILE_ALREADY_EXISTS)})
+			return
+		}
+	}
+
+	config.Cfg.Section("file").Key("ShareDir").SetValue(strings.Join(list, "|"))
+	config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath)
+	config.FileSettingInfo.ShareDir = list
+	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
+}
+
+// @Summary Get the shared directory
+// @Produce  application/json
+// @Accept application/json
+// @Tags person
+// @Security ApiKeyAuth
+// @Success 200 {string} string "ok"
+// @Router /person/share [get]
+func GetPersonShare(c *gin.Context) {
+	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: config.FileSettingInfo.ShareDir})
+}
+
+// @Summary Modify disabled status
+// @Produce  application/json
+// @Accept application/json
+// @Tags person
+// @Param block formData bool false "Disable or not,Default:false "
+// @Security ApiKeyAuth
+// @Success 200 {string} string "ok"
+// @Router /person/block/{shareid} [put]
+func PutPersonBlock(c *gin.Context) {
+	token := c.Param("shareid")
+	_, err := uuid.FromString(token)
+	block, _ := strconv.ParseBool(c.PostForm("block"))
+	if err != nil {
+		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
+		return
+	}
+	friend := model2.FriendModel{}
+	friend.Token = token
+	friend.Block = block
+	service.MyService.Friend().EditFriendBlock(friend)
+	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
+}
+
+// @Summary Delete my friend
+// @Produce  application/json
+// @Accept application/json
+// @Tags person
+// @Security ApiKeyAuth
+// @Success 200 {string} string "ok"
+// @Router /person/user/{shareid} [delete]
+func DeletePersonFriend(c *gin.Context) {
+	token := c.Param("shareid")
+	_, err := uuid.FromString(token)
+	if err != nil {
+		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
+		return
+	}
+	friend := model2.FriendModel{}
+	friend.Token = token
+
+	service.MyService.Friend().DeleteFriend(friend)
+	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
+}
+
+// @Summary Get public person
+// @Produce  application/json
+// @Accept application/json
+// @Tags person
+// @Security ApiKeyAuth
+// @Success 200 {string} string "ok"
+// @Router /person/public [delete]
+func GetPersonPublic(c *gin.Context) {
+	list := service.MyService.Casa().GetPersonPublic()
+	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: list})
+}

+ 55 - 1
route/v1/system.go

@@ -251,6 +251,60 @@ func PostKillCasaOS(c *gin.Context) {
 	os.Exit(0)
 }
 
+// @Summary Turn off usb auto-mount
+// @Produce  application/json
+// @Accept application/json
+// @Tags sys
+// @Security ApiKeyAuth
+// @Success 200 {string} string "ok"
+// @Router /sys/usg/off [put]
+func PutSystemOffUSBAutoMount(c *gin.Context) {
+	service.MyService.System().UpdateUSBAutoMount("False")
+	service.MyService.System().ExecUSBAutoMountShell("False")
+	c.JSON(http.StatusOK,
+		model.Result{
+			Success: oasis_err.SUCCESS,
+			Message: oasis_err.GetMsg(oasis_err.SUCCESS),
+		})
+}
+
+// @Summary Turn off usb auto-mount
+// @Produce  application/json
+// @Accept application/json
+// @Tags sys
+// @Security ApiKeyAuth
+// @Success 200 {string} string "ok"
+// @Router /sys/usb [get]
+func GetSystemUSBAutoMount(c *gin.Context) {
+	state := "True"
+	if config.ServerInfo.USBAutoMount == "False" {
+		state = "False"
+	}
+	c.JSON(http.StatusOK,
+		model.Result{
+			Success: oasis_err.SUCCESS,
+			Message: oasis_err.GetMsg(oasis_err.SUCCESS),
+			Data:    state,
+		})
+}
+
+// @Summary Turn off usb auto-mount
+// @Produce  application/json
+// @Accept application/json
+// @Tags sys
+// @Security ApiKeyAuth
+// @Success 200 {string} string "ok"
+// @Router /sys/usb/on [put]
+func PutSystemOnUSBAutoMount(c *gin.Context) {
+	service.MyService.System().UpdateUSBAutoMount("True")
+	service.MyService.System().ExecUSBAutoMountShell("True")
+	c.JSON(http.StatusOK,
+		model.Result{
+			Success: oasis_err.SUCCESS,
+			Message: oasis_err.GetMsg(oasis_err.SUCCESS),
+		})
+}
+
 // @Summary system info
 // @Produce  application/json
 // @Accept application/json
@@ -374,7 +428,7 @@ func Info(c *gin.Context) {
 			if n.Name == netCardName {
 				item := *(*model.IOCountersStat)(unsafe.Pointer(&n))
 				item.State = strings.TrimSpace(service.MyService.ZiMa().GetNetState(n.Name))
-				item.DateTime = time.Now()
+				item.Time = time.Now().Unix()
 				newNet = append(newNet, item)
 				break
 			}

+ 48 - 11
route/v1/user.go

@@ -121,7 +121,7 @@ func PostUserHead(c *gin.Context) {
 // @Param oldname  formData string true "Old user name"
 // @Security ApiKeyAuth
 // @Success 200 {string} string "ok"
-// @Router /user/changusername [put]
+// @Router /user/username [put]
 func PutUserName(c *gin.Context) {
 	if config.ServerInfo.LockAccount {
 		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.ACCOUNT_LOCK, Message: oasis_err2.GetMsg(oasis_err2.ACCOUNT_LOCK)})
@@ -142,14 +142,14 @@ func PutUserName(c *gin.Context) {
 // @Accept multipart/form-data
 // @Tags user
 // @Param pwd formData string true "Password"
-// @Param oldpwd  formData string true "Old password"
+// @Param old_pwd  formData string true "Old password"
 // @Security ApiKeyAuth
 // @Success 200 {string} string "ok"
-// @Router /user/changuserpwd [put]
+// @Router /user/password [put]
 func PutUserPwd(c *gin.Context) {
-	oldpwd := c.PostForm("oldpwd")
+	oldPwd := c.PostForm("old_pwd")
 	pwd := c.PostForm("pwd")
-	if config.UserInfo.PWD != oldpwd {
+	if config.UserInfo.PWD != oldPwd {
 		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.PWD_INVALID_OLD, Message: oasis_err2.GetMsg(oasis_err2.PWD_INVALID_OLD)})
 		return
 	}
@@ -199,7 +199,7 @@ func PostUserChangeInfo(c *gin.Context) {
 	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: data})
 }
 
-// @Summary edit user info
+// @Summary edit user nick
 // @Produce  application/json
 // @Accept multipart/form-data
 // @Tags user
@@ -211,17 +211,18 @@ func PutUserChangeNick(c *gin.Context) {
 
 	nickName := c.PostForm("nick_name")
 
-	if len(nickName) > 0 {
-		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.PWD_INVALID, Message: oasis_err2.GetMsg(oasis_err2.PWD_INVALID)})
+	if len(nickName) == 0 {
+		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
 		return
 	}
 	user_service.SetUser("", "", "", "", "", nickName)
 	data := make(map[string]string, 1)
 	data["nick_name"] = config.UserInfo.NickName
+	go service.MyService.Casa().PushUserInfo()
 	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: data})
 }
 
-// @Summary edit user info
+// @Summary edit user description
 // @Produce  application/json
 // @Accept multipart/form-data
 // @Tags user
@@ -232,13 +233,38 @@ func PutUserChangeNick(c *gin.Context) {
 func PutUserChangeDesc(c *gin.Context) {
 	desc := c.PostForm("description")
 
-	if len(desc) > 0 {
-		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.PWD_INVALID, Message: oasis_err2.GetMsg(oasis_err2.PWD_INVALID)})
+	if len(desc) == 0 {
+		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
 		return
 	}
 	user_service.SetUser("", "", "", "", desc, "")
 	data := make(map[string]string, 1)
 	data["description"] = config.UserInfo.Description
+	go service.MyService.Casa().PushUserInfo()
+	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: data})
+}
+
+// @Summary Modify user person information (Initialization use)
+// @Produce  application/json
+// @Accept multipart/form-data
+// @Tags user
+// @Param nick_name formData string false "user nick name"
+// @Param description formData string false "Description"
+// @Security ApiKeyAuth
+// @Success 200 {string} string "ok"
+// @Router /user/person/info [post]
+func PostUserPersonInfo(c *gin.Context) {
+	desc := c.PostForm("description")
+	nickName := c.PostForm("nick_name")
+	if len(desc) == 0 || len(nickName) == 0 {
+		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
+		return
+	}
+	user_service.SetUser("", "", "", "", desc, nickName)
+	data := make(map[string]string, 2)
+	data["description"] = config.UserInfo.Description
+	data["nick_name"] = config.UserInfo.NickName
+	go service.MyService.Casa().PushUserInfo()
 	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: data})
 }
 
@@ -262,3 +288,14 @@ func GetUserInfo(c *gin.Context) {
 			Data:    u,
 		})
 }
+
+// @Summary Get my shareId
+// @Produce  application/json
+// @Accept application/json
+// @Tags user
+// @Security ApiKeyAuth
+// @Success 200 {string} string "ok"
+// @Router /user/shareid [get]
+func GetUserShareID(c *gin.Context) {
+	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: config.ServerInfo.Token})
+}

+ 0 - 475
route/v1/zerotier.go

@@ -1,475 +0,0 @@
-package v1
-
-import (
-	json2 "encoding/json"
-	"net/http"
-
-	"github.com/IceWhaleTech/CasaOS/model"
-	"github.com/IceWhaleTech/CasaOS/pkg/config"
-	oasis_err2 "github.com/IceWhaleTech/CasaOS/pkg/utils/oasis_err"
-	"github.com/IceWhaleTech/CasaOS/service"
-	"github.com/gin-gonic/gin"
-)
-
-// @Summary 登录zerotier获取token
-// @Produce  application/json
-// @Accept multipart/form-data
-// @Tags zerotier
-// @Param username formData string true "User name"
-// @Param pwd  formData string true "password"
-// @Security ApiKeyAuth
-// @Success 200 {string} string "ok"
-// @Router /zerotier/login [post]
-func ZeroTierGetToken(c *gin.Context) {
-	username := c.PostForm("username")
-	pwd := c.PostForm("pwd")
-	if len(username) == 0 || len(pwd) == 0 {
-		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
-		return
-	}
-	errInfo := service.MyService.ZeroTier().GetToken(username, pwd)
-
-	if len(errInfo) == 0 {
-		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.ERROR, Message: oasis_err2.GetMsg(oasis_err2.GET_TOKEN_ERROR)})
-	} else {
-		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
-	}
-}
-
-// @Summary 注册zerotier
-// @Produce  application/json
-// @Accept multipart/form-data
-// @Tags zerotier
-// @Param firstName formData string true "first name"
-// @Param pwd  formData string true "password"
-// @Param email  formData string true "email"
-// @Param lastName  formData string true "last name"
-// @Security ApiKeyAuth
-// @Success 200 {string} string "ok"
-// @Router /zerotier/register [post]
-func ZeroTierRegister(c *gin.Context) {
-	firstName := c.PostForm("firstName")
-	pwd := c.PostForm("pwd")
-	email := c.PostForm("email")
-	lastName := c.PostForm("lastName")
-	if len(firstName) == 0 || len(pwd) == 0 || len(email) == 0 || len(lastName) == 0 {
-		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
-		return
-	}
-	errInfo := service.MyService.ZeroTier().ZeroTierRegister(email, lastName, firstName, pwd)
-	if len(errInfo) == 0 {
-		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
-	} else {
-		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.ERROR, Message: errInfo})
-	}
-}
-
-// @Summary 是否需要登录zerotier
-// @Produce  application/json
-// @Accept application/json
-// @Tags zerotier
-// @Security ApiKeyAuth
-// @Success 200 {string} string "false:需要登录,true:不需要登录"
-// @Router /zerotier/islogin [get]
-func ZeroTierIsNeedLogin(c *gin.Context) {
-	if len(config.ZeroTierInfo.Token) == 0 {
-		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: false})
-	} else {
-		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: true})
-	}
-}
-
-// @Summary 获取zerotier网络列表
-// @Produce  application/json
-// @Accept application/json
-// @Tags zerotier
-// @Security ApiKeyAuth
-// @Success 200 {string} string "ok"
-// @Router /zerotier/list [get]
-func ZeroTierGetNetworkList(c *gin.Context) {
-	jsonList, joined := service.MyService.ZeroTier().ZeroTierNetworkList(config.ZeroTierInfo.Token)
-	rdata := make(map[string]interface{})
-	rdata["network_list"] = jsonList
-	rdata["joined"] = joined
-	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: rdata})
-}
-
-// @Summary 获取zerotier网络详情
-// @Produce  application/json
-// @Accept application/json
-// @Tags zerotier
-// @Security ApiKeyAuth
-// @Param id path string true "network id"
-// @Success 200 {string} string "ok"
-// @Router /zerotier/info/{id} [get]
-func ZeroTierGetNetworkGetInfo(c *gin.Context) {
-	id := c.Param("id")
-	if len(id) == 0 {
-		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
-		return
-	}
-	info, joined := service.MyService.ZeroTier().ZeroTierGetInfo(config.ZeroTierInfo.Token, id)
-	rdata := make(map[string]interface{})
-	rdata["info"] = info
-	rdata["joined"] = joined
-	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: rdata})
-}
-
-//// @Summary 获取zerotier网络状态
-//// @Produce  application/json
-//// @Accept application/json
-//// @Tags zerotier
-//// @Security ApiKeyAuth
-//// @Success 200 {string} string "ok"
-//// @Router /zerotier/status [get]
-//func ZeroTierGetNetworkGetStatus(c *gin.Context) {
-//	status := service.MyService.ZeroTier().ZeroTierGetStatus(config.ZeroTierInfo.Token)
-//	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: status})
-//}
-
-//// @Summary 修改网络类型
-//// @Produce  application/json
-//// @Accept application/json
-//// @Tags zerotier
-//// @Security ApiKeyAuth
-//// @Param id path string true "network id"
-//// @Param type formData string true "Private true/false"
-//// @Success 200 {string} string "ok"
-//// @Router /zerotier/type/{id} [put]
-//func ZeroTierEditType(c *gin.Context) {
-//	id := c.Param("id")
-//	t := c.PostForm("type")
-//	if len(id) == 0 || len(t) == 0 {
-//		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
-//		return
-//	}
-//	postData := `{"config":{"private":` + t + `}}`
-//	info := service.MyService.ZeroTier().EditNetwork(config.ZeroTierInfo.Token, postData, id)
-//	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: info})
-//}
-
-//// @Summary 修改名称
-//// @Produce  application/json
-//// @Accept application/json
-//// @Tags zerotier
-//// @Security ApiKeyAuth
-//// @Param id path string true "network id"
-//// @Param name formData string true "需要过滤特殊字符串"
-//// @Success 200 {string} string "ok"
-//// @Router /zerotier/name/{id} [put]
-//func ZeroTierEditName(c *gin.Context) {
-//	id := c.Param("id")
-//	name := c.PostForm("name")
-//	if len(id) == 0 || len(name) == 0 {
-//		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
-//		return
-//	}
-//	postData := `{"config":{"name":"` + name + `"}}`
-//	info := service.MyService.ZeroTier().EditNetwork(config.ZeroTierInfo.Token, postData, id)
-//	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: info})
-//}
-
-//// @Summary V6Assign (注意三个属性需要一起传过来,不传的会被zerotier设置成false)
-//// @Produce  application/json
-//// @Accept application/json
-//// @Tags zerotier
-//// @Security ApiKeyAuth
-//// @Param id path string true "network id"
-//// @Param v6plan formData string false "true/false"
-//// @Param rfc formData string false "true/false"
-//// @Param auto formData string false "true/false"
-//// @Success 200 {string} string "ok"
-//// @Router /zerotier/v6assign/{id} [put]
-//func ZeroTierEditV6Assign(c *gin.Context) {
-//	id := c.Param("id")
-//	v6plan := c.PostForm("v6plan")
-//	rfc := c.PostForm("rfc")
-//	auto := c.PostForm("auto")
-//	if len(id) == 0 {
-//		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
-//		return
-//	}
-//	var spicing string
-//	if len(v6plan) > 0 {
-//		spicing = `"6plane":` + v6plan
-//	}
-//	if len(rfc) > 0 {
-//		if len(spicing) > 0 {
-//			spicing += ","
-//		}
-//		spicing += `"rfc4193":` + rfc
-//	}
-//
-//	if len(auto) > 0 {
-//		if len(spicing) > 0 {
-//			spicing += ","
-//		}
-//		spicing += `"zt":` + auto
-//	}
-//	postData := `{"config":{"v6AssignMode":{` + spicing + `}}}`
-//	info := service.MyService.ZeroTier().EditNetwork(config.ZeroTierInfo.Token, postData, id)
-//	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: info})
-//}
-
-//// @Summary Broadcast
-//// @Produce  application/json
-//// @Accept application/json
-//// @Tags zerotier
-//// @Security ApiKeyAuth
-//// @Param id path string true "network id"
-//// @Param broadcast formData string true "true/false"
-//// @Success 200 {string} string "ok"
-//// @Router /zerotier/broadcast/{id} [put]
-//func ZeroTierEditBroadcast(c *gin.Context) {
-//	id := c.Param("id")
-//	broadcast := c.PostForm("broadcast")
-//	if len(id) == 0 || len(broadcast) == 0 {
-//		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
-//		return
-//	}
-//	postData := `{"config":{"enableBroadcast":` + broadcast + `}}`
-//	info := service.MyService.ZeroTier().EditNetwork(config.ZeroTierInfo.Token, postData, id)
-//	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: info})
-//}
-
-// @Summary 网络列表
-// @Produce  application/json
-// @Accept application/json
-// @Tags zerotier
-// @Security ApiKeyAuth
-// @Param id path string true "network id"
-// @Success 200 {string} string "ok"
-// @Router /zerotier/member/{id} [get]
-func ZeroTierMemberList(c *gin.Context) {
-	id := c.Param("id")
-	if len(id) == 0 {
-		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
-		return
-	}
-	info := service.MyService.ZeroTier().MemberList(config.ZeroTierInfo.Token, id)
-	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: info})
-}
-
-// @Summary create new network
-// @Produce  application/json
-// @Accept application/json
-// @Tags zerotier
-// @Security ApiKeyAuth
-// @Success 200 {string} string "ok"
-// @Router /zerotier/create [post]
-func ZeroTierCreateNetwork(c *gin.Context) {
-	info := service.MyService.ZeroTier().CreateNetwork(config.ZeroTierInfo.Token)
-	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: info})
-}
-
-//// @Summary 通过/拒绝客户端
-//// @Produce  application/json
-//// @Accept application/json
-//// @Tags zerotier
-//// @Security ApiKeyAuth
-//// @Param id path string true "network id"
-//// @Param mId path string true "member_id"
-//// @Param auth formData string true "true/false"
-//// @Success 200 {string} string "ok"
-//// @Router /zerotier/member/{id}/auth/{mId} [put]
-//func ZeroTierMemberAuth(c *gin.Context) {
-//	id := c.Param("id")
-//	mId := c.Param("mId")
-//	auth := c.PostForm("auth")
-//	if len(id) == 0 || len(mId) == 0 || len(auth) == 0 {
-//		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
-//		return
-//	}
-//	postData := `{"config":{"authorized":` + auth + `}}`
-//	info := service.MyService.ZeroTier().EditNetworkMember(config.ZeroTierInfo.Token, postData, id, mId)
-//	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: info})
-//}
-
-//// @Summary 修改名字
-//// @Produce  application/json
-//// @Accept application/json
-//// @Tags zerotier
-//// @Security ApiKeyAuth
-//// @Param id path string true "network id"
-//// @Param mId path string true "member_id"
-//// @Param name formData string true "name"
-//// @Success 200 {string} string "ok"
-//// @Router /zerotier/member/{id}/name/{mId} [put]
-//func ZeroTierMemberName(c *gin.Context) {
-//	id := c.Param("id")
-//	mId := c.Param("mId")
-//	name := c.PostForm("name")
-//	if len(id) == 0 || len(mId) == 0 || len(name) == 0 {
-//		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
-//		return
-//	}
-//	postData := `{"name":"` + name + `"}`
-//	info := service.MyService.ZeroTier().EditNetworkMember(config.ZeroTierInfo.Token, postData, id, mId)
-//	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: info})
-//}
-
-//// @Summary 修改桥接
-//// @Produce  application/json
-//// @Accept application/json
-//// @Tags zerotier
-//// @Security ApiKeyAuth
-//// @Param id path string true "network id"
-//// @Param mId path string true "member_id"
-//// @Param bridge formData string true "true/false"
-//// @Success 200 {string} string "ok"
-//// @Router /zerotier/member/{id}/bridge/{mId} [put]
-//func ZeroTierMemberBridge(c *gin.Context) {
-//	id := c.Param("id")
-//	mId := c.Param("mId")
-//	bridge := c.PostForm("bridge")
-//	if len(id) == 0 || len(mId) == 0 || len(bridge) == 0 {
-//		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
-//		return
-//	}
-//	postData := `{"config":{"activeBridge":` + bridge + `}}`
-//	info := service.MyService.ZeroTier().EditNetworkMember(config.ZeroTierInfo.Token, postData, id, mId)
-//	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: info})
-//}
-
-// @Summary 修改网络
-// @Produce  application/json
-// @Accept application/json
-// @Tags zerotier
-// @Security ApiKeyAuth
-// @Param id path string true "network id"
-// @Param json formData string true "json数据"
-// @Success 200 {string} string "ok"
-// @Router /zerotier/edit/{id} [put]
-func ZeroTierEdit(c *gin.Context) {
-	id := c.Param("id")
-	json := c.PostForm("json")
-	if len(id) == 0 || len(json) == 0 {
-		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
-		return
-	}
-	info := service.MyService.ZeroTier().EditNetwork(config.ZeroTierInfo.Token, json, id)
-	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: info})
-}
-
-// @Summary 获取已加入的网络
-// @Produce  application/json
-// @Accept application/json
-// @Tags zerotier
-// @Security ApiKeyAuth
-// @Success 200 {string} string "ok"
-// @Router /zerotier/joined/list [get]
-func ZeroTierJoinedList(c *gin.Context) {
-	info := service.MyService.ZeroTier().GetJoinNetworks()
-	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: json2.RawMessage(info)})
-}
-
-// @Summary 修改网络用户信息
-// @Produce  application/json
-// @Accept application/json
-// @Tags zerotier
-// @Security ApiKeyAuth
-// @Param id path string true "network id"
-// @Param mId path string true "mId"
-// @Param json formData string true "json数据"
-// @Success 200 {string} string "ok"
-// @Router /zerotier/member/{id}/edit/{mId} [put]
-func ZeroTierMemberEdit(c *gin.Context) {
-	id := c.Param("id")
-	mId := c.Param("mId")
-	json := c.PostForm("json")
-	if len(id) == 0 || len(json) == 0 || len(mId) == 0 {
-		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
-		return
-	}
-	info := service.MyService.ZeroTier().EditNetworkMember(config.ZeroTierInfo.Token, json, id, mId)
-	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: info})
-}
-
-// @Summary 删除网络中的用户
-// @Produce  application/json
-// @Accept application/json
-// @Tags zerotier
-// @Security ApiKeyAuth
-// @Param id path string true "network id"
-// @Param mId path string true "member_id"
-// @Success 200 {string} string "ok"
-// @Router /zerotier/member/{id}/del/{mId} [delete]
-func ZeroTierMemberDelete(c *gin.Context) {
-	id := c.Param("id")
-	mId := c.Param("mId")
-	if len(id) == 0 || len(mId) == 0 {
-		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
-		return
-	}
-	info := service.MyService.ZeroTier().DeleteMember(config.ZeroTierInfo.Token, id, mId)
-	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: info})
-}
-
-// @Summary 删除网络
-// @Produce  application/json
-// @Accept application/json
-// @Tags zerotier
-// @Security ApiKeyAuth
-// @Param id path string true "network id"
-// @Success 200 {string} string "ok"
-// @Router /zerotier/network/{id}/del [delete]
-func ZeroTierDeleteNetwork(c *gin.Context) {
-	id := c.Param("id")
-	if len(id) == 0 {
-		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
-		return
-	}
-	info := service.MyService.ZeroTier().DeleteNetwork(config.ZeroTierInfo.Token, id)
-	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: info})
-}
-
-// @Summary 加入网络
-// @Produce  application/json
-// @Accept multipart/form-data
-// @Tags zerotier
-// @Param id path string true "network id"
-// @Security ApiKeyAuth
-// @Success 200 {string} string "ok"
-// @Router /zerotier/join/{id} [post]
-func ZeroTierJoinNetwork(c *gin.Context) {
-	networkId := c.Param("id")
-	if len(networkId) != 16 {
-		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
-		return
-	}
-	for _, v := range networkId {
-		if !service.MyService.ZeroTier().NetworkIdFilter(v) {
-			c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
-			return
-		}
-	}
-	service.MyService.ZeroTier().ZeroTierJoinNetwork(networkId)
-	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
-}
-
-// @Summary 获取zerotier网络列表
-// @Produce  application/json
-// @Accept multipart/form-data
-// @Tags zerotier
-// @Param id path string true "network id"
-// @Security ApiKeyAuth
-// @Success 200 {string} string "ok"
-// @Router /zerotier/leave/{id} [post]
-func ZeroTierLeaveNetwork(c *gin.Context) {
-	networkId := c.Param("id")
-
-	if len(networkId) != 16 {
-		c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
-		return
-	}
-
-	for _, v := range networkId {
-		if !service.MyService.ZeroTier().NetworkIdFilter(v) {
-			c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
-			return
-		}
-	}
-	service.MyService.ZeroTier().ZeroTierLeaveNetwork(networkId)
-
-	c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
-}

+ 1 - 1
route/v1/zima_info.go

@@ -73,7 +73,7 @@ func NetInfo(c *gin.Context) {
 			if n.Name == netCardName {
 				item := *(*model.IOCountersStat)(unsafe.Pointer(&n))
 				item.State = strings.TrimSpace(service.MyService.ZiMa().GetNetState(n.Name))
-				item.DateTime = time.Now()
+				item.Time = time.Now().Unix()
 				newNet = append(newNet, item)
 				break
 			}

+ 45 - 2
service/casa.go

@@ -22,6 +22,9 @@ type CasaService interface {
 	PushHeart(id, t string, language string)
 	PushAppAnalyse(uuid, t string, name, language string)
 	PushConnectionStatus(uuid, err string, from, to, event string)
+	PushUserInfo()
+	GetUserInfoByShareId(shareId string) model.UserInfo
+	GetPersonPublic() (list []model.FriendsModel)
 }
 
 type casaService struct {
@@ -118,7 +121,6 @@ func GetToken() string {
 	}
 	go func() {
 		str := httper2.Get(config.ServerInfo.ServerApi+"/token", nil)
-
 		t <- gjson.Get(str, "data").String()
 	}()
 	auth = <-t
@@ -178,13 +180,54 @@ func (o *casaService) PushConnectionStatus(uuid, err string, from, to, event str
 
 	head["Authorization"] = GetToken()
 
-	infoS := httper2.Post(config.ServerInfo.ServerApi+"/v1/analyse/app", b, "application/json", head)
+	infoS := httper2.Post(config.ServerInfo.ServerApi+"/v1/analyse/connect", b, "application/json", head)
+
+	info := model.ServerAppList{}
+	json2.Unmarshal([]byte(gjson.Get(infoS, "data").String()), &info)
+
+}
+func (o *casaService) PushUserInfo() {
+	m := model.UserInfo{}
+	m.Desc = config.UserInfo.Description
+	m.Avatar = config.UserInfo.Avatar
+	m.NickName = config.UserInfo.NickName
+	m.ShareId = config.ServerInfo.Token
+	b, _ := json.Marshal(m)
+
+	head := make(map[string]string)
+
+	head["Authorization"] = GetToken()
+
+	infoS := httper2.Post(config.ServerInfo.ServerApi+"/v1/user/info", b, "application/json", head)
 
 	info := model.ServerAppList{}
 	json2.Unmarshal([]byte(gjson.Get(infoS, "data").String()), &info)
 
 }
 
+func (o *casaService) GetUserInfoByShareId(shareId string) model.UserInfo {
+
+	head := make(map[string]string)
+
+	head["Authorization"] = GetToken()
+
+	infoS := httper2.Get(config.ServerInfo.ServerApi+"/v1/user/info/"+shareId, head)
+
+	info := model.UserInfo{}
+	json2.Unmarshal([]byte(gjson.Get(infoS, "data").String()), &info)
+	return info
+}
+func (o *casaService) GetPersonPublic() (list []model.FriendsModel) {
+	head := make(map[string]string)
+
+	head["Authorization"] = GetToken()
+
+	listS := httper2.Get(config.ServerInfo.ServerApi+"/v1/person/public", head)
+
+	json2.Unmarshal([]byte(gjson.Get(listS, "data").String()), &list)
+
+	return list
+}
 func NewCasaService() CasaService {
 	return &casaService{}
 }

+ 23 - 15
service/download.go

@@ -6,43 +6,51 @@ import (
 )
 
 type DownloadService interface {
-	AddDownloadTask(m model2.PersionDownloadDBModel)   //添加下载任务
-	EditDownloadState(m model2.PersionDownloadDBModel) //只修改状态
-	SaveDownload(m model2.PersionDownloadDBModel)
+	AddDownloadTask(m model2.PersonDownloadDBModel)   //添加下载任务
+	EditDownloadState(m model2.PersonDownloadDBModel) //只修改状态
+	SaveDownload(m model2.PersonDownloadDBModel)
 	DelDownload(uuid string)
-	GetDownloadById(uuid string) model2.PersionDownloadDBModel
-	GetDownloadListByState(state string) []model2.PersionDownloadDBModel
-	SetDownloadError(m model2.PersionDownloadDBModel)
+	GetDownloadById(uuid string) model2.PersonDownloadDBModel
+	GetDownloadListByState(state string) []model2.PersonDownloadDBModel
+	SetDownloadError(m model2.PersonDownloadDBModel)
+	GetDownloadListByPath(m model2.PersonDownloadDBModel) int
 }
 type downloadService struct {
 	db *gorm.DB
 }
 
-func (d *downloadService) AddDownloadTask(m model2.PersionDownloadDBModel) {
+func (d *downloadService) GetDownloadListByPath(m model2.PersonDownloadDBModel) int {
+	var list []model2.PersonDownloadDBModel
+	d.db.Select("path").Where("path = ? AND `from` = ? AND state = 0", m.Path, m.From).Find(&list)
+	return len(list)
+}
+
+func (d *downloadService) AddDownloadTask(m model2.PersonDownloadDBModel) {
+
 	d.db.Create(&m)
 }
-func (d *downloadService) EditDownloadState(m model2.PersionDownloadDBModel) {
+func (d *downloadService) EditDownloadState(m model2.PersonDownloadDBModel) {
+
 	d.db.Model(&m).Where("uuid = ?", m.UUID).Update("state", m.State)
 }
 
 //failed during download
-func (d *downloadService) SetDownloadError(m model2.PersionDownloadDBModel) {
+func (d *downloadService) SetDownloadError(m model2.PersonDownloadDBModel) {
 	d.db.Model(&m).Updates(m)
 }
 
 func (d *downloadService) DelDownload(uuid string) {
-	var m model2.PersionDownloadDBModel
+	var m model2.PersonDownloadDBModel
 	d.db.Where("uuid = ?", uuid).Delete(&m)
 }
-func (d *downloadService) GetDownloadById(uuid string) model2.PersionDownloadDBModel {
-	var m model2.PersionDownloadDBModel
+func (d *downloadService) GetDownloadById(uuid string) model2.PersonDownloadDBModel {
+	var m model2.PersonDownloadDBModel
 	d.db.Model(m).Where("uuid = ?", uuid).First(&m)
 	return m
 }
-func (d *downloadService) GetDownloadListByState(state string) (list []model2.PersionDownloadDBModel) {
+func (d *downloadService) GetDownloadListByState(state string) (list []model2.PersonDownloadDBModel) {
 	if len(state) == 0 {
 		d.db.Find(&list)
-
 	} else {
 		d.db.Where("state = ?", state).Find(&list)
 	}
@@ -50,7 +58,7 @@ func (d *downloadService) GetDownloadListByState(state string) (list []model2.Pe
 	return
 }
 
-func (d *downloadService) SaveDownload(m model2.PersionDownloadDBModel) {
+func (d *downloadService) SaveDownload(m model2.PersonDownloadDBModel) {
 	d.db.Save(&m)
 }
 func NewDownloadService(db *gorm.DB) DownloadService {

+ 8 - 5
service/friend.go

@@ -10,7 +10,8 @@ import (
 type FriendService interface {
 	AddFriend(m model2.FriendModel)
 	DeleteFriend(m model2.FriendModel)
-	EditFriendNick(m model2.FriendModel)
+	EditFriendMark(m model2.FriendModel)
+	EditFriendBlock(m model2.FriendModel)
 	GetFriendById(m model2.FriendModel) model2.FriendModel
 	GetFriendList() (list []model2.FriendModel)
 	UpdateAddFriendType(m model2.FriendModel)
@@ -27,17 +28,19 @@ func (p *friendService) AddFriend(m model2.FriendModel) {
 func (p *friendService) DeleteFriend(m model2.FriendModel) {
 	p.db.Where("token = ?", m.Token).Delete(&m)
 }
-func (p *friendService) EditFriendNick(m model2.FriendModel) {
-	p.db.Model(&m).Where("token = ?", m.Token).Update("nick_name", m.NickName)
+func (p *friendService) EditFriendMark(m model2.FriendModel) {
+	p.db.Model(&m).Where("token = ?", m.Token).Update("mark", m.Mark)
+}
+func (p *friendService) EditFriendBlock(m model2.FriendModel) {
+	p.db.Model(&m).Where("token = ?", m.Token).Update("block", m.Block)
 }
-
 func (p *friendService) GetFriendById(m model2.FriendModel) model2.FriendModel {
 	p.db.Model(m).Where("token = ?", m.Token).First(&m)
 	return m
 }
 
 func (p *friendService) GetFriendList() (list []model2.FriendModel) {
-	p.db.Select("nick_name", "avatar", "name", "profile", "token", "state").Find(&list)
+	p.db.Select("nick_name", "avatar", "profile", "token", "state", "mark", "block", "version").Find(&list)
 	return list
 }
 

+ 18 - 15
service/model/o_download.go

@@ -1,21 +1,24 @@
 package model
 
-type PersionDownloadDBModel struct {
+type PersonDownloadDBModel struct {
 	UUID      string `gorm:"column:uuid;primary_key" json:"uuid"`
-	State     int    `json:"state"` //
-	Type      int    `json:"type"`  //defult 1
-	Name      string `json:"name"`  //file name
-	Size      int64  `json:"size"`  //file size
-	BlockSize int    `json:"block_size"`
-	Length    int    `json:"length"` //slice length
-	Hash      string `json:"hash"`
-	Error     string `json:"error"`
-	From      string `json:"from"`
-	Already   int    `json:"already" gorm:"-"`
-	CreatedAt int64  `gorm:"autoCreateTime" json:"created_at"`
-	UpdatedAt int64  `gorm:"autoCreateTime;autoUpdateTime" json:"updated_at"`
+	State     int    `json:"state"`             //
+	Type      int    `json:"type"`              //defult 1
+	Name      string `json:"name"`              //file name
+	Size      int64  `json:"size"`              //file size
+	BlockSize int    `json:"block_size"`        //Size of each file block
+	Length    int    `json:"length"`            //slice length
+	Hash      string `json:"hash"`              //File hash value
+	Error     string `json:"error"`             //
+	From      string `json:"from"`              //Error message
+	Path      string `json:"path"`              //Full path to the file
+	Already   int    `json:"already" gorm:"-"`  //Folder blocks that have been downloaded
+	LocalPath string `json:"local_path"`        //The address where the file is saved after download
+	Duration  int64  `json:"duration" gorm:"-"` //Length of time
+	Created   int64  `gorm:"autoCreateTime" json:"created"`
+	Updated   int64  `gorm:"autoCreateTime;autoUpdateTime" json:"updated"`
 }
 
-func (p *PersionDownloadDBModel) TableName() string {
-	return "o_persion_download"
+func (p *PersonDownloadDBModel) TableName() string {
+	return "o_person_download"
 }

+ 7 - 5
service/model/o_friend.go

@@ -1,15 +1,17 @@
 package model
 
 type FriendModel struct {
-	State     int    `json:"state"` //备用
+	State     int    `json:"state"` //Reserved
 	CreatedAt int64  `gorm:"autoCreateTime" json:"created_at"`
 	UpdatedAt int64  `gorm:"autoCreateTime;autoUpdateTime" json:"updated_at"`
-	NickName  string `json:"nick_name"` //custom name
-	Avatar    string `json:"avatar"`    //头像
-	Name      string `json:"name"`
+	NickName  string `json:"nick_name"`
+	Mark      string `json:"mark"`   //Remarks
+	Block     bool   `json:"block"`  //Disable or not
+	Avatar    string `json:"avatar"` //User avatar
 	Token     string `gorm:"column:token;primary_key" json:"token"`
-	Profile   string `json:"profile"`
+	Profile   string `json:"profile"` //Description
 	OnLine    bool   `json:"on_line" gorm:"-"`
+	Version   int    `json:"version"`
 }
 
 func (p *FriendModel) TableName() string {

+ 21 - 0
service/notify.go

@@ -18,6 +18,7 @@ type NotifyServer interface {
 	DelLog(id string)
 	GetList(c int) (list []model.AppNotify)
 	MarkRead(id string, state int)
+	SendText(m model.AppNotify)
 }
 
 type notifyServer struct {
@@ -102,6 +103,26 @@ func SendMeg() {
 	//	}
 }
 
+func (i notifyServer) SendText(m model.AppNotify) {
+	list := []model.AppNotify{}
+	list = append(list, m)
+	json, _ := json2.Marshal(list)
+	var temp []*websocket.Conn
+	for _, v := range WebSocketConns {
+
+		err := v.WriteMessage(1, json)
+		if err == nil {
+			temp = append(temp, v)
+		}
+	}
+	WebSocketConns = temp
+
+	if len(WebSocketConns) == 0 {
+		SocketRun = false
+	}
+
+}
+
 func NewNotifyService(db *gorm.DB) NotifyServer {
 	return &notifyServer{db: db}
 }

+ 55 - 15
service/person.go

@@ -17,6 +17,7 @@ import (
 	"github.com/IceWhaleTech/CasaOS/pkg/quic_helper"
 	"github.com/IceWhaleTech/CasaOS/pkg/utils/file"
 	httper2 "github.com/IceWhaleTech/CasaOS/pkg/utils/httper"
+	port2 "github.com/IceWhaleTech/CasaOS/pkg/utils/port"
 	model2 "github.com/IceWhaleTech/CasaOS/service/model"
 	"github.com/IceWhaleTech/CasaOS/types"
 	"github.com/lucas-clemente/quic-go"
@@ -32,6 +33,7 @@ type personService struct {
 }
 
 var IpInfo model.PersionModel
+var CancelList map[string]string
 
 func PushIpInfo(token string) {
 
@@ -63,9 +65,16 @@ var StreamList map[string]quic.Stream
 var ServiceMessage chan model.MessageModel
 
 func UDPService() {
+	port := 0
+	if len(config.ServerInfo.UDPPort) > 0 {
+		port, _ = strconv.Atoi(config.ServerInfo.UDPPort)
+		if port != 0 && !port2.IsPortAvailable(port, "udp") {
+			port = 0
+		}
+	}
 
 	srcAddr := &net.UDPAddr{
-		IP: net.IPv4zero, Port: 9904}
+		IP: net.IPv4zero, Port: port}
 	var err error
 	UDPConn, err = net.ListenUDP("udp", srcAddr)
 	if err != nil {
@@ -146,6 +155,7 @@ func ProcessingContent(stream quic.Stream) {
 		prefixByte := make([]byte, 6)
 		_, err := io.ReadFull(stream, prefixByte)
 		if err != nil {
+			fmt.Println(err)
 			return
 		}
 		prefixLength, err := strconv.Atoi(string(prefixByte))
@@ -166,14 +176,22 @@ func ProcessingContent(stream quic.Stream) {
 			//nothing
 			continue
 		} else if m.Type == types.PERSONDIRECTORY {
+			friend := model2.FriendModel{}
+			friend.Token = m.From
 			var list []model.Path
-			if m.Data.(string) == "" || m.Data.(string) == "/" {
-				for _, v := range config.FileSettingInfo.ShareDir {
-					tempList := MyService.ZiMa().GetDirPath(v)
-					list = append(list, tempList...)
+			rFriend := MyService.Friend().GetFriendById(friend)
+			if !reflect.DeepEqual(rFriend, model2.FriendModel{Token: m.From}) && !rFriend.Block {
+				if m.Data.(string) == "" || m.Data.(string) == "/" {
+					for _, v := range config.FileSettingInfo.ShareDir {
+						//tempList := MyService.ZiMa().GetDirPath(v)
+						temp := MyService.ZiMa().GetDirPathOne(v)
+						list = append(list, temp)
+					}
+				} else {
+					list = MyService.ZiMa().GetDirPath(m.Data.(string))
 				}
 			} else {
-				list = MyService.ZiMa().GetDirPath(m.Data.(string))
+				list = []model.Path{}
 			}
 			m.To = m.From
 			m.Data = list
@@ -185,6 +203,7 @@ func ProcessingContent(stream quic.Stream) {
 			SendFileData(stream, m.Data.(string), m.From, m.UUId)
 			break
 		} else if m.Type == types.PERSONADDFRIEND {
+			fmt.Println("有用户来请求加好友", m)
 			friend := model2.FriendModel{}
 			dataModelByte, _ := json.Marshal(m.Data)
 			err := json.Unmarshal(dataModelByte, &friend)
@@ -196,7 +215,7 @@ func ProcessingContent(stream quic.Stream) {
 			mi := model2.FriendModel{}
 			mi.Avatar = config.UserInfo.Avatar
 			mi.Profile = config.UserInfo.Description
-			mi.Name = config.UserInfo.NickName
+			mi.NickName = config.UserInfo.NickName
 			m.To = m.From
 			m.Data = mi
 			m.Type = types.PERSONADDFRIEND
@@ -210,19 +229,34 @@ func ProcessingContent(stream quic.Stream) {
 			} else {
 				delete(UDPAddressMap, m.From)
 			}
-			mi := model2.FriendModel{}
-			mi.Avatar = config.UserInfo.Avatar
-			mi.Profile = config.UserInfo.Description
-			mi.Name = config.UserInfo.NickName
-			mi.Token = config.ServerInfo.Token
+			// mi := model2.FriendModel{}
+			// mi.Avatar = config.UserInfo.Avatar
+			// mi.Profile = config.UserInfo.Description
+			// mi.NickName = config.UserInfo.NickName
+			// mi.Token = config.ServerInfo.Token
+
+			user := MyService.Casa().GetUserInfoByShareId(m.From)
+
+			friend := model2.FriendModel{}
+			friend.Token = m.From
+			friend.Avatar = user.Avatar
+			friend.Block = false
+			friend.NickName = user.NickName
+			friend.Profile = user.Avatar
+			friend.Version = user.Version
+			MyService.Friend().AddFriend(friend)
+
 			msg := model.MessageModel{}
-			msg.Type = types.PERSONADDFRIEND
-			msg.Data = mi
+			msg.Type = types.PERSONHELLO
+			msg.Data = ""
 			msg.To = m.From
 			msg.From = config.ServerInfo.Token
 			msg.UUId = m.UUId
 			Dial(msg, false)
 
+			break
+		} else if m.Type == types.PERSONCANCEL {
+			CancelList[m.UUId] = "cancel"
 			break
 		} else {
 			//不应有不做返回的数据
@@ -289,6 +323,9 @@ func SendFileData(stream quic.Stream, filePath, to, uuid string) error {
 
 	bufferedReader := bufio.NewReader(f)
 	buf := make([]byte, blockSize)
+
+	defer stream.Close()
+
 	for i := 0; i < length; i++ {
 
 		tran := model.TranFileModel{}
@@ -313,8 +350,11 @@ func SendFileData(stream quic.Stream, filePath, to, uuid string) error {
 		prefixLength := file.PrefixLength(len(b))
 		dataLength := file.DataLength(len(buf[:n]))
 		data := append(append(append(prefixLength, b...), dataLength...), buf[:n]...)
+		if _, ok := CancelList[uuid]; ok {
+			delete(CancelList, uuid)
+			return nil
+		}
 		stream.Write(data)
 	}
-	defer stream.Close()
 	return nil
 }

+ 0 - 6
service/service.go

@@ -21,7 +21,6 @@ type Repository interface {
 	User() UserService
 	Docker() DockerService
 	//Redis() RedisService
-	ZeroTier() ZeroTierService
 	ZiMa() ZiMaService
 	Casa() CasaService
 	Disk() DiskService
@@ -45,7 +44,6 @@ func NewService(db *gorm.DB, log loger2.OLog) Repository {
 		user:   NewUserService(),
 		docker: NewDockerService(log),
 		//redis:      NewRedisService(rp),
-		zerotier:       NewZeroTierService(),
 		zima:           NewZiMaService(),
 		casa:           NewCasaService(),
 		disk:           NewDiskService(log, db),
@@ -68,7 +66,6 @@ type store struct {
 	ddns           DDNSService
 	user           UserService
 	docker         DockerService
-	zerotier       ZeroTierService
 	zima           ZiMaService
 	casa           CasaService
 	disk           DiskService
@@ -123,9 +120,6 @@ func (c *store) Docker() DockerService {
 	return c.docker
 }
 
-func (c *store) ZeroTier() ZeroTierService {
-	return c.zerotier
-}
 func (c *store) ZiMa() ZiMaService {
 	return c.zima
 }

+ 16 - 0
service/system.go

@@ -18,6 +18,8 @@ type SystemService interface {
 	UpdateAssist()
 	UpSystemPort(port string)
 	GetTimeZone() string
+	UpdateUSBAutoMount(state string)
+	ExecUSBAutoMountShell(state string)
 }
 type systemService struct {
 	log loger.OLog
@@ -37,6 +39,15 @@ func (s *systemService) GetTimeZone() string {
 	return command2.ExecResultStr("source " + config.AppInfo.ProjectPath + "/shell/helper.sh ;GetTimeZone")
 }
 
+func (s *systemService) ExecUSBAutoMountShell(state string) {
+	if state == "False" {
+		command2.OnlyExec("source " + config.AppInfo.ProjectPath + "/shell/helper.sh ;USB_Remove_File")
+	} else {
+		command2.OnlyExec("source " + config.AppInfo.ProjectPath + "/shell/helper.sh ;USB_Move_File")
+	}
+
+}
+
 func (s *systemService) GetSystemConfigDebug() []string {
 	return command2.ExecResultStrArray("source " + config.AppInfo.ProjectPath + "/shell/helper.sh ;GetSysInfo")
 }
@@ -51,6 +62,11 @@ func (s *systemService) UpSystemConfig(str string, widget string) {
 	}
 	config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath)
 }
+func (s *systemService) UpdateUSBAutoMount(state string) {
+	config.ServerInfo.USBAutoMount = state
+	config.Cfg.Section("system").Key("USBAutoMount").SetValue(state)
+	config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath)
+}
 func (s *systemService) UpSystemPort(port string) {
 	if len(port) > 0 && port != config.ServerInfo.HttpPort {
 		config.Cfg.Section("server").Key("HttpPort").SetValue(port)

+ 126 - 53
service/udpconn.go

@@ -10,6 +10,7 @@ import (
 	"io/ioutil"
 	"net"
 	"os"
+	path2 "path"
 	"strconv"
 	"time"
 
@@ -32,29 +33,52 @@ func Dial(msg model.MessageModel, server bool) (m model.MessageModel, err error)
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 	defer cancel()
 	Message = make(chan model.MessageModel)
-
+	_, port, err := net.SplitHostPort(UDPConn.LocalAddr().String())
+	if config.ServerInfo.UDPPort != port {
+		config.ServerInfo.UDPPort = port
+		config.Cfg.Section("server").Key("UDPPort").SetValue(port)
+		config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath)
+	}
+	p, err := strconv.Atoi(port)
 	srcAddr := &net.UDPAddr{
-		IP: net.IPv4zero, Port: 9904} //注意端口必须固定
+		IP: net.IPv4zero, Port: p} //注意端口必须固定
 	addr := UDPAddressMap[msg.To]
-	ticker := msg.To
+	ticket := msg.To
 	if server {
 		addr = config.ServerInfo.Handshake + ":9527"
-		ticker = "bench"
+		ticket = "bench"
 	}
 	dstAddr, err := net.ResolveUDPAddr("udp", addr)
 
 	//DialTCP在网络协议net上连接本地地址laddr和远端地址raddr。net必须是"udp"、"udp4"、"udp6";如果laddr不是nil,将使用它作为本地地址,否则自动选择一个本地地址。
 	//(conn)UDPConn代表一个UDP网络连接,实现了Conn和PacketConn接口
 
-	session, err := quic.DialContext(ctx, UDPConn, dstAddr, srcAddr.String(), quic_helper.GetClientTlsConfig(ticker), quic_helper.GetQUICConfig())
+	session, err := quic.DialContext(ctx, UDPConn, dstAddr, srcAddr.String(), quic_helper.GetClientTlsConfig(ticket), quic_helper.GetQUICConfig())
 	if err != nil {
-		go MyService.Casa().PushConnectionStatus(m.UUId, err.Error(), m.From, m.To, m.Type)
+		if msg.Type == types.PERSONDOWNLOAD {
+			task := MyService.Download().GetDownloadById(msg.UUId)
+			task.Error = err.Error()
+			task.State = types.DOWNLOADERROR
+			MyService.Download().SetDownloadError(task)
+		}
+		if config.SystemConfigInfo.Analyse != "False" {
+			go MyService.Casa().PushConnectionStatus(msg.UUId, err.Error(), msg.From, msg.To, msg.Type)
+		}
+
 		return m, err
 	}
 
 	stream, err := session.OpenStreamSync(ctx)
 	if err != nil {
-		go MyService.Casa().PushConnectionStatus(m.UUId, err.Error(), m.From, m.To, m.Type)
+		if msg.Type == types.PERSONDOWNLOAD {
+			task := MyService.Download().GetDownloadById(msg.UUId)
+			task.Error = err.Error()
+			task.State = types.DOWNLOADERROR
+			MyService.Download().SetDownloadError(task)
+		}
+		if config.SystemConfigInfo.Analyse != "False" {
+			go MyService.Casa().PushConnectionStatus(msg.UUId, err.Error(), msg.From, msg.To, msg.Type)
+		}
 		session.CloseWithError(1, err.Error())
 		return m, err
 	}
@@ -66,7 +90,9 @@ func Dial(msg model.MessageModel, server bool) (m model.MessageModel, err error)
 	go ReadContent(stream)
 	result := <-Message
 	stream.Close()
-	go MyService.Casa().PushConnectionStatus(m.UUId, "OK", m.From, m.To, m.Type)
+	if config.SystemConfigInfo.Analyse != "False" {
+		go MyService.Casa().PushConnectionStatus(msg.UUId, "OK", msg.From, msg.To, msg.Type)
+	}
 	return result, nil
 }
 
@@ -88,8 +114,6 @@ func SendData(stream quic.Stream, m model.MessageModel) {
 	stream.Write(data)
 }
 
-var Summary map[string]model.FileSummaryModel
-
 //读取数据
 func ReadContent(stream quic.Stream) {
 	for {
@@ -97,6 +121,12 @@ func ReadContent(stream quic.Stream) {
 		_, err := io.ReadFull(stream, prefixByte)
 		if err != nil {
 			fmt.Println(err)
+			time.Sleep(time.Second * 1)
+			for k, v := range CancelList {
+				tempPath := config.AppInfo.RootPath + "/temp" + "/" + v
+				fmt.Println(file.RMDir(tempPath))
+				delete(CancelList, k)
+			}
 			break
 		}
 		prefixLength, err := strconv.Atoi(string(prefixByte))
@@ -112,12 +142,14 @@ func ReadContent(stream quic.Stream) {
 		}
 		m := model.MessageModel{}
 		err = json.Unmarshal(messageByte, &m)
+		fmt.Println("客户端", m)
 		if err != nil {
 			fmt.Println(err)
 			break
 		}
 
 		if m.Type == types.PERSONDOWNLOAD {
+
 			dataModelByte, _ := json.Marshal(m.Data)
 			dataModel := model.TranFileModel{}
 			err := json.Unmarshal(dataModelByte, &dataModel)
@@ -149,29 +181,32 @@ func ReadContent(stream quic.Stream) {
 				fmt.Println("hash不匹配", hash, dataModel.Hash)
 				continue
 			}
-
 			tempPath := config.AppInfo.RootPath + "/temp" + "/" + m.UUId
 			file.IsNotExistMkDir(tempPath)
 			filepath := tempPath + "/" + strconv.Itoa(dataModel.Index)
-			tempFile, err := os.Stat(filepath)
+			_, err = os.Stat(filepath)
 
-			if os.IsNotExist(err) || tempFile.Size() == 0 {
+			if os.IsNotExist(err) {
 				err = ioutil.WriteFile(filepath, dataByte, 0644)
-				task := model2.PersionDownloadDBModel{}
+				task := model2.PersonDownloadDBModel{}
 				task.UUID = m.UUId
-				task.Error = err.Error()
-				task.State = types.DOWNLOADERROR
-				MyService.Download().SetDownloadError(task)
+				if err != nil {
+					task.Error = err.Error()
+					task.State = types.DOWNLOADERROR
+					MyService.Download().SetDownloadError(task)
+				}
 
 			} else {
 				if file.GetHashByPath(filepath) != dataModel.Hash {
 					os.Remove(filepath)
 					err = ioutil.WriteFile(filepath, dataByte, 0644)
-					task := model2.PersionDownloadDBModel{}
+					task := model2.PersonDownloadDBModel{}
 					task.UUID = m.UUId
-					task.Error = err.Error()
-					task.State = types.DOWNLOADERROR
-					MyService.Download().SetDownloadError(task)
+					if err != nil {
+						task.Error = err.Error()
+						task.State = types.DOWNLOADERROR
+						MyService.Download().SetDownloadError(task)
+					}
 				}
 			}
 
@@ -181,21 +216,21 @@ func ReadContent(stream quic.Stream) {
 				continue
 			}
 			if len(files) >= dataModel.Length {
-				summary := Summary[m.UUId]
-				file.SpliceFiles(tempPath, config.FileSettingInfo.DownloadDir+"/"+summary.Name, dataModel.Length, 0)
-				if file.GetHashByPath(config.FileSettingInfo.DownloadDir+"/"+summary.Name) == summary.Hash {
+				summary := MyService.Download().GetDownloadById(m.UUId)
+				summary.State = types.DOWNLOADFINISH
+				MyService.Download().EditDownloadState(summary)
+				fullPath := file.GetNoDuplicateFileName(path2.Join(summary.LocalPath, summary.Name))
+				file.SpliceFiles(tempPath, fullPath, dataModel.Length, 0)
+				if file.GetHashByPath(fullPath) == summary.Hash {
 					file.RMDir(tempPath)
-					task := model2.PersionDownloadDBModel{}
-					task.UUID = m.UUId
-					task.State = types.DOWNLOADFINISH
-					MyService.Download().EditDownloadState(task)
-					delete(Summary, m.UUId)
+					summary.State = types.DOWNLOADFINISHED
+					MyService.Download().EditDownloadState(summary)
 				} else {
 					os.Remove(config.FileSettingInfo.DownloadDir + "/" + summary.Name)
-					task := model2.PersionDownloadDBModel{}
-					task.UUID = m.UUId
-					task.State = types.DOWNLOADERROR
-					MyService.Download().EditDownloadState(task)
+
+					summary.State = types.DOWNLOADERROR
+					summary.Error = "hash mismatch"
+					MyService.Download().SetDownloadError(summary)
 				}
 
 				break
@@ -205,42 +240,55 @@ func ReadContent(stream quic.Stream) {
 			dataModel := model.FileSummaryModel{}
 			dataModelByte, _ := json.Marshal(m.Data)
 			err := json.Unmarshal(dataModelByte, &dataModel)
-			fmt.Println(err)
+			if err != nil {
+				fmt.Println(err)
+			}
+
+			task := MyService.Download().GetDownloadById(m.UUId)
+			fullPath := path2.Join(task.LocalPath, task.Name)
+			task.State = types.DOWNLOADING
+			if len(dataModel.Message) > 0 {
+				task.State = types.DOWNLOADERROR
+				task.Error = dataModel.Message
+			}
+			if file.Exists(fullPath) && file.GetHashByPath(fullPath) == dataModel.Hash {
+				task.State = types.DOWNLOADFINISHED
+				go func(from, uuid string) {
+					m := model.MessageModel{}
+					m.Data = ""
+					m.From = config.ServerInfo.Token
+					m.To = from
+					m.Type = types.PERSONCANCEL
+					m.UUId = uuid
+					CancelList[uuid] = uuid
+					Dial(m, false)
+				}(task.From, task.UUID)
 
-			task := model2.PersionDownloadDBModel{}
+			}
 			task.UUID = m.UUId
 			task.Name = dataModel.Name
 			task.Length = dataModel.Length
 			task.Size = dataModel.Size
-			task.State = types.DOWNLOADING
 			task.BlockSize = dataModel.BlockSize
 			task.Hash = dataModel.Hash
 			task.Type = 0
 			task.From = m.From
-			if len(dataModel.Message) > 0 {
-				task.State = types.DOWNLOADERROR
-				task.Error = dataModel.Message
-			}
-
 			MyService.Download().SaveDownload(task)
 
-			Summary[m.UUId] = dataModel
-
 		} else if m.Type == types.PERSONCONNECTION {
-
 			if len(m.Data.(string)) > 0 {
 				UDPAddressMap[m.From] = m.Data.(string)
 			} else {
 				delete(UDPAddressMap, m.From)
 			}
-			mi := model2.FriendModel{}
-			mi.Avatar = config.UserInfo.Avatar
-			mi.Profile = config.UserInfo.Description
-			mi.Name = config.UserInfo.NickName
-			mi.Token = config.ServerInfo.Token
+			// mi := model2.FriendModel{}
+			// mi.Avatar = config.UserInfo.Avatar
+			// mi.Profile = config.UserInfo.Description
+			// mi.NickName = config.UserInfo.NickName
+			// mi.Token = config.ServerInfo.Token
 			msg := model.MessageModel{}
-			msg.Type = types.PERSONADDFRIEND
-			msg.Data = mi
+			msg.Type = types.PERSONHELLO
+			msg.Data = ""
 			msg.To = m.From
 			msg.From = config.ServerInfo.Token
 			msg.UUId = m.UUId
@@ -248,10 +296,21 @@ func ReadContent(stream quic.Stream) {
 			Message <- m
 			break
 		} else if m.Type == "get_ip" {
+			notify := model2.AppNotify{}
+			notify.CustomId = m.From
 			if len(m.Data.(string)) == 0 {
+				if _, ok := UDPAddressMap[m.From]; ok {
+					notify.Type = types.NOTIFY_TYPE_PERSION_FIRNED_LEAVE
+					go MyService.Notify().SendText(notify)
+				}
 				delete(UDPAddressMap, m.From)
+				Message <- m
 				break
 			}
+			if _, ok := UDPAddressMap[m.From]; !ok {
+				notify.Type = types.NOTIFY_TYPE_PERSION_FIRNED_LIVE
+				go MyService.Notify().SendText(notify)
+			}
 			UDPAddressMap[m.From] = m.Data.(string)
 			Message <- m
 			break
@@ -264,7 +323,7 @@ func ReadContent(stream quic.Stream) {
 
 func SendIPToServer() {
 	msg := model.MessageModel{}
-	msg.Type = "hello"
+	msg.Type = types.PERSONHELLO
 	msg.Data = ""
 	msg.From = config.ServerInfo.Token
 	msg.To = config.ServerInfo.Token
@@ -282,9 +341,10 @@ func LoopFriend() {
 		msg.From = config.ServerInfo.Token
 		msg.To = list[i].Token
 		msg.UUId = uuid.NewV4().String()
+
 		Dial(msg, true)
 
-		msg.Type = "hello"
+		msg.Type = types.PERSONHELLO
 		msg.Data = ""
 		msg.From = config.ServerInfo.Token
 		msg.To = list[i].Token
@@ -292,6 +352,19 @@ func LoopFriend() {
 		if _, ok := UDPAddressMap[list[i].Token]; ok {
 			go Dial(msg, false)
 		}
+		go func(shareId string) {
+			user := MyService.Casa().GetUserInfoByShareId(shareId)
+			m := model2.FriendModel{}
+			m.Token = shareId
+			friend := MyService.Friend().GetFriendById(m)
+			if friend.Version != user.Version {
+				friend.Avatar = user.Avatar
+				friend.NickName = user.NickName
+				friend.Profile = user.Desc
+				friend.Version = user.Version
+				MyService.Friend().UpdateOrCreate(friend)
+			}
+		}(list[i].Token)
 
 	}
 }

+ 0 - 353
service/zerotier.go

@@ -1,353 +0,0 @@
-package service
-
-import (
-	"bytes"
-	"errors"
-	"fmt"
-	"io/ioutil"
-	"math/rand"
-	"net/http"
-	"strconv"
-	"strings"
-	"time"
-	"unicode"
-
-	"github.com/IceWhaleTech/CasaOS/pkg/config"
-	command2 "github.com/IceWhaleTech/CasaOS/pkg/utils/command"
-	httper2 "github.com/IceWhaleTech/CasaOS/pkg/utils/httper"
-	"github.com/IceWhaleTech/CasaOS/pkg/zerotier"
-	"github.com/PuerkitoBio/goquery"
-	"github.com/tidwall/gjson"
-)
-
-type ZeroTierService interface {
-	GetToken(username, pwd string) string
-	ZeroTierRegister(email, lastName, firstName, password string) string
-	ZeroTierNetworkList(token string) (interface{}, []string)
-	ZeroTierJoinNetwork(networkId string)
-	ZeroTierLeaveNetwork(networkId string)
-	ZeroTierGetInfo(token, id string) (interface{}, []string)
-	ZeroTierGetStatus(token string) interface{}
-	EditNetwork(token string, data string, id string) interface{}
-	CreateNetwork(token string) interface{}
-	MemberList(token string, id string) interface{}
-	EditNetworkMember(token string, data string, id, mId string) interface{}
-	DeleteMember(token string, id, mId string) interface{}
-	DeleteNetwork(token, id string) interface{}
-	GetJoinNetworks() string
-	NetworkIdFilter(letter rune) bool
-}
-type zerotierStruct struct {
-}
-
-var client http.Client
-
-func (c *zerotierStruct) ZeroTierJoinNetwork(networkId string) {
-	command2.OnlyExec(`zerotier-cli join ` + networkId)
-}
-func (c *zerotierStruct) ZeroTierLeaveNetwork(networkId string) {
-	command2.OnlyExec(`zerotier-cli leave ` + networkId)
-}
-
-//登录并获取token
-func (c *zerotierStruct) GetToken(username, pwd string) string {
-	if len(config.ZeroTierInfo.Token) > 0 {
-		return config.ZeroTierInfo.Token
-	} else {
-		return LoginGetToken(username, pwd)
-	}
-}
-
-func (c *zerotierStruct) ZeroTierRegister(email, lastName, firstName, password string) string {
-
-	url := "https://accounts.zerotier.com/auth/realms/zerotier/protocol/openid-connect/registrations?client_id=zt-central&redirect_uri=https%3A%2F%2Fmy.zerotier.com%2Fapi%2F_auth%2Foidc%2Fcallback&response_type=code&scope=openid+profile+email+offline_access&state=state"
-
-	action, cookies, _ := ZeroTierGet(url, nil, 4)
-	var buff bytes.Buffer
-	buff.WriteString("email=")
-	buff.WriteString(email)
-	buff.WriteString("&password=")
-	buff.WriteString(password)
-	buff.WriteString("&password-confirm=")
-	buff.WriteString(password)
-	buff.WriteString("&user.attributes.marketingOptIn=true")
-	buff.WriteString("&firstName")
-	buff.WriteString(firstName)
-	buff.WriteString("&lastName")
-	buff.WriteString(lastName)
-
-	action, errInfo, _ := ZeroTierPost(buff, action, cookies, false)
-	if len(errInfo) > 0 {
-		return errInfo
-	}
-	action, _, _ = ZeroTierGet(action, cookies, 5)
-	return ""
-}
-
-//固定请求head
-func GetHead() map[string]string {
-	var head = make(map[string]string, 4)
-	head["Accept"] = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"
-	head["Accept-Language"] = "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3"
-	head["Connection"] = "keep-alive"
-	head["User-Agent"] = "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36"
-	return head
-}
-
-//登录并获取token,会出现账号密码错误,和邮箱未验证情况,目前未出现其他情况
-func LoginGetToken(username, pwd string) string {
-	//拿到登录的action
-	var loginUrl = "https://accounts.zerotier.com/auth/realms/zerotier/protocol/openid-connect/auth?client_id=zt-central&redirect_uri=https%3A%2F%2Fmy.zerotier.com%2Fapi%2F_auth%2Foidc%2Fcallback&response_type=code&scope=openid+profile+email+offline_access&state=states"
-	action, cookies, _ := ZeroTierGet(loginUrl, nil, 1)
-	if len(action) == 0 {
-		//没有拿到action,页面结构变了
-		return ""
-	}
-	//登录
-	var str bytes.Buffer
-	str.WriteString("username=")
-	str.WriteString(username)
-	str.WriteString("&password=")
-	str.WriteString(pwd)
-	str.WriteString("&credentialId=&login=Log+In")
-	url, logingErrInfo, _ := ZeroTierPost(str, action, cookies, true)
-
-	action, cookies, isLoginOk := ZeroTierGet(url, cookies, 2)
-
-	if isLoginOk {
-		//登录成功,可以继续调用api
-		randomTokenUrl := "https://my.zerotier.com/api/randomToken"
-		json, _, _ := ZeroTierGet(randomTokenUrl, cookies, 3)
-		//获取一个随机token
-		token := gjson.Get(json, "token")
-
-		userInfoUrl := "https://my.zerotier.com/api/status"
-		json, _, _ = ZeroTierGet(userInfoUrl, cookies, 3)
-		//拿到用户id
-		userId := gjson.Get(json, "user.id")
-
-		//设置新token
-		addTokenUrl := "https://my.zerotier.com/api/user/" + userId.String() + "/token"
-		data := make(map[string]string)
-		rand.Seed(time.Now().UnixNano())
-		data["tokenName"] = "oasis-token-" + strconv.Itoa(rand.Intn(1000))
-		data["token"] = token.String()
-		head := make(map[string]string)
-		head["Content-Type"] = "application/json"
-		_, statusCode := httper2.ZeroTierPost(addTokenUrl, data, head, cookies)
-		if statusCode == http.StatusOK {
-			config.Cfg.Section("zerotier").Key("Token").SetValue(token.String())
-			config.Cfg.SaveTo("conf/conf.ini")
-			config.ZeroTierInfo.Token = token.String()
-		}
-	} else {
-		//登录错误信息
-		if len(logingErrInfo) > 0 {
-			return logingErrInfo
-		} else {
-			//验证邮箱
-			action, _, _ = ZeroTierGet(url, cookies, 5)
-			return "You need to verify your email address to activate your account."
-		}
-	}
-	return ""
-}
-
-// t 1:获取action,2:登录成功后拿session(可能需要验证有了或登录失败) 3:随机生成token 4:注册页面拿action  5:注册成功后拿验证邮箱的地址
-func ZeroTierGet(url string, cookies []*http.Cookie, t uint8) (action string, c []*http.Cookie, isExistSession bool) {
-	isExistSession = false
-	action = ""
-	c = []*http.Cookie{}
-	request, _ := http.NewRequest(http.MethodGet, url, nil)
-	for k, v := range GetHead() {
-		request.Header.Add(k, v)
-	}
-	for _, cookie := range cookies {
-		request.AddCookie(cookie)
-	}
-	resp, err := client.Do(request)
-	if err != nil {
-		return
-	}
-	defer resp.Body.Close()
-	c = resp.Cookies()
-	if t == 1 {
-		doc, err := goquery.NewDocumentFromReader(resp.Body)
-		if err != nil {
-			return
-		}
-		action, _ = doc.Find("#kc-form-login").Attr("action")
-		return
-	} else if t == 2 {
-		for _, cookie := range resp.Cookies() {
-			if cookie.Name == "pgx-session" {
-				isExistSession = true
-				break
-			}
-		}
-		//判断是否登录成功,如果需要验证邮箱,则返回验证邮箱的地址。
-		if resp.StatusCode == http.StatusFound && len(resp.Header.Get("Location")) > 0 {
-			action = resp.Header.Get("Location")
-		}
-		return
-	} else if t == 3 {
-		//返回获取到的字符串
-		byteArr, _ := ioutil.ReadAll(resp.Body)
-		action = string(byteArr)
-	} else if t == 4 {
-		doc, err := goquery.NewDocumentFromReader(resp.Body)
-		if err != nil {
-			return
-		}
-		action, _ = doc.Find("#kc-register-form").Attr("action")
-		return
-
-	} else if t == 5 {
-		doc, _ := goquery.NewDocumentFromReader(resp.Body)
-		fmt.Println(doc.Html())
-		action, _ = doc.Find("#kc-info-wrapper a").Attr("href")
-		return
-	}
-
-	return
-}
-
-//模拟提交表单
-func ZeroTierPost(str bytes.Buffer, action string, cookies []*http.Cookie, isLogin bool) (url, errInfo string, err error) {
-	req, err := http.NewRequest(http.MethodPost, action, strings.NewReader(str.String()))
-	if err != nil {
-		return "", "", errors.New("newrequest error")
-	}
-	for k, v := range GetHead() {
-		req.Header.Set(k, v)
-	}
-	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
-	for _, cookie := range cookies {
-		req.AddCookie(cookie)
-	}
-	res, err := client.Do(req)
-	defer res.Body.Close()
-	if err != nil {
-		return "", "", errors.New("request error")
-	}
-	if !isLogin {
-		//注册成功
-		if res.StatusCode == http.StatusFound && len(res.Header.Get("Location")) > 0 {
-			return res.Header.Get("Location"), "", nil
-		} else {
-			register, _ := goquery.NewDocumentFromReader(res.Body)
-			firstErr := strings.TrimSpace(register.Find("#input-error-firstname").Text())
-			lastErr := strings.TrimSpace(register.Find("#input-error-lastname").Text())
-			emailErr := strings.TrimSpace(register.Find("#input-error-email").Text())
-			pwdErr := strings.TrimSpace(register.Find("#input-error-password").Text())
-			var errD strings.Builder
-			if len(firstErr) > 0 {
-				errD.WriteString(firstErr + ",")
-			}
-			if len(lastErr) > 0 {
-				errD.WriteString(lastErr + ",")
-			}
-			if len(emailErr) > 0 {
-				errD.WriteString(emailErr + ",")
-			}
-			if len(pwdErr) > 0 {
-				errD.WriteString(pwdErr + ",")
-			}
-			return "", errD.String(), nil
-		}
-
-	} else {
-		if res.StatusCode == http.StatusFound && len(res.Header.Get("Location")) > 0 {
-			return res.Header.Get("Location"), "", nil
-		}
-		doc, err := goquery.NewDocumentFromReader(res.Body)
-		if err != nil {
-			return "", "", errors.New("request error")
-		}
-
-		errDesc := doc.Find("#input-error").Text()
-		if len(errDesc) > 0 {
-			return "", strings.TrimSpace(errDesc), nil
-		}
-
-	}
-
-	return "", "", nil
-}
-
-//获取zerotile网络列表和本地用户已加入的网络
-func (c *zerotierStruct) ZeroTierNetworkList(token string) (interface{}, []string) {
-	url := "https://my.zerotier.com/api/network"
-	return zerotier.GetData(url, token), command2.ExecResultStrArray(`zerotier-cli listnetworks | awk 'NR>1 {print $3} {line=$0}'`)
-}
-
-// get network info
-func (c *zerotierStruct) ZeroTierGetInfo(token, id string) (interface{}, []string) {
-	url := "https://my.zerotier.com/api/network/" + id
-	info := zerotier.GetData(url, token)
-	return info, command2.ExecResultStrArray(`zerotier-cli listnetworks | awk 'NR>1 {print $3} {line=$0}'`)
-}
-
-//get status
-func (c *zerotierStruct) ZeroTierGetStatus(token string) interface{} {
-	url := "https://my.zerotier.com/api/v1/status"
-	info := zerotier.GetData(url, token)
-	return info
-}
-
-func (c *zerotierStruct) EditNetwork(token string, data string, id string) interface{} {
-	url := "https://my.zerotier.com/api/v1/network/" + id
-	info := zerotier.PostData(url, token, data)
-	return info
-}
-
-func (c *zerotierStruct) EditNetworkMember(token string, data string, id, mId string) interface{} {
-	url := "https://my.zerotier.com/api/v1/network/" + id + "/member/" + mId
-	info := zerotier.PostData(url, token, data)
-	return info
-}
-
-func (c *zerotierStruct) MemberList(token string, id string) interface{} {
-	url := "https://my.zerotier.com/api/v1/network/" + id + "/member"
-	info := zerotier.GetData(url, token)
-	return info
-}
-
-func (c *zerotierStruct) DeleteMember(token string, id, mId string) interface{} {
-	url := "https://my.zerotier.com/api/v1/network/" + id + "/member/" + mId
-	info := zerotier.DeleteMember(url, token)
-	return info
-}
-
-func (c *zerotierStruct) DeleteNetwork(token, id string) interface{} {
-	url := "https://my.zerotier.com/api/v1/network/" + id
-	info := zerotier.DeleteMember(url, token)
-	return info
-}
-
-func (c *zerotierStruct) CreateNetwork(token string) interface{} {
-	url := "https://my.zerotier.com/api/v1/network"
-	info := zerotier.PostData(url, token, "{}")
-	return info
-}
-
-func (c *zerotierStruct) GetJoinNetworks() string {
-	json := command2.ExecResultStr("source " + config.AppInfo.ProjectPath + "/shell/helper.sh ;GetLocalJoinNetworks")
-	return json
-}
-
-func (c *zerotierStruct) NetworkIdFilter(letter rune) bool {
-	if unicode.IsNumber(letter) || unicode.IsLetter(letter) {
-		return true
-	} else {
-		return false
-	}
-}
-func NewZeroTierService() ZeroTierService {
-	//初始化client
-	client = http.Client{Timeout: 30 * time.Second, CheckRedirect: func(req *http.Request, via []*http.Request) error {
-		return http.ErrUseLastResponse //禁止重定向
-	},
-	}
-	return &zerotierStruct{}
-}

+ 69 - 2
service/zima_info.go

@@ -4,10 +4,12 @@ import (
 	"fmt"
 	"io/ioutil"
 	"os"
+	"path/filepath"
 	"runtime"
 	"strconv"
 	"strings"
 	"time"
+	"unsafe"
 
 	"github.com/IceWhaleTech/CasaOS/model"
 	"github.com/IceWhaleTech/CasaOS/pkg/config"
@@ -32,12 +34,15 @@ type ZiMaService interface {
 	GetNetState(name string) string
 	GetSysInfo() host.InfoStat
 	GetDirPath(path string) []model.Path
+	GetDirPathOne(path string) (m model.Path)
 	MkdirAll(path string) (int, error)
 	CreateFile(path string) (int, error)
 	RenameFile(oldF, newF string) (int, error)
 	GetCpuInfo() []cpu.InfoStat
 }
 
+var NetArray [][]model.IOCountersStat
+
 type zima struct {
 }
 
@@ -84,10 +89,19 @@ func (c *zima) GetDirPath(path string) []model.Path {
 
 	ls, _ := ioutil.ReadDir(path)
 	dirs := []model.Path{}
-
 	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(), Size: l.Size()})
+			filePath := filepath.Join(path, l.Name())
+			link, err := filepath.EvalSymlinks(filePath)
+			if err != nil {
+				link = filePath
+			}
+			temp := model.Path{Name: l.Name(), Path: filePath, IsDir: l.IsDir(), Date: l.ModTime(), Size: l.Size()}
+			if filePath != link {
+				file, _ := os.Stat(link)
+				temp.IsDir = file.IsDir()
+			}
+			dirs = append(dirs, temp)
 		}
 	} else {
 		dirs = append(dirs, model.Path{Name: "DATA", Path: "/DATA/", IsDir: true, Date: time.Now()})
@@ -95,6 +109,21 @@ func (c *zima) GetDirPath(path string) []model.Path {
 	return dirs
 }
 
+func (c *zima) GetDirPathOne(path string) (m model.Path) {
+
+	f, err := os.Stat(path)
+
+	if err != nil {
+		return
+	}
+	m.IsDir = f.IsDir()
+	m.Name = f.Name()
+	m.Path = path
+	m.Size = f.Size()
+	m.Date = f.ModTime()
+	return
+}
+
 //获取系统信息
 func (c *zima) GetSysInfo() host.InfoStat {
 	info, _ := host.Info()
@@ -174,3 +203,41 @@ func (c *zima) RenameFile(oldF, newF string) (int, error) {
 func NewZiMaService() ZiMaService {
 	return &zima{}
 }
+
+func LoopNet() {
+	netList := MyService.ZiMa().GetNetInfo()
+
+	nets := MyService.ZiMa().GetNet(true)
+	num := 0
+	for i := 0; i < len(netList); i++ {
+
+		for _, netCardName := range nets {
+
+			if netList[i].Name == netCardName {
+				var netArray []model.IOCountersStat
+				if len(NetArray) < (num + 1) {
+					netArray = []model.IOCountersStat{}
+				} else {
+					netArray = NetArray[num]
+				}
+				item := *(*model.IOCountersStat)(unsafe.Pointer(&netList[i]))
+				item.State = strings.TrimSpace(MyService.ZiMa().GetNetState(netList[i].Name))
+				item.Time = time.Now().Unix()
+
+				if len(netArray) >= 60 {
+					netArray = netArray[1:]
+				}
+				netArray = append(netArray, item)
+				if len(NetArray) < (num + 1) {
+					NetArray = append(NetArray, []model.IOCountersStat{})
+				}
+
+				NetArray[num] = netArray
+
+				num++
+				break
+			}
+		}
+
+	}
+}

+ 5 - 10
shell/assist.sh

@@ -1,13 +1,6 @@
 #!/bin/bash
 
-#add in v0.2.3
-version_0_2_3() {
-  ((EUID)) && sudo_cmd="sudo"
-  $sudo_cmd cp -rf /casaOS/server/shell/11-usb-mount.rules /etc/udev/rules.d/
-  $sudo_cmd chmod +x /casaOS/server/shell/usb-mount.sh
-  $sudo_cmd cp -rf /casaOS/server/shell/usb-mount@.service /etc/systemd/system/
 
-}
 
 # add in v0.2.5
 
@@ -16,7 +9,9 @@ readonly CASA_DEPANDS="curl smartmontools parted fdisk ntfs-3g"
 version_0_2_5() {
   install_depends "$CASA_DEPANDS"
 }
-
+version_0_2_11() {
+  sysctl -w net.core.rmem_max=2500000
+}
 
 #Install Depends
 install_depends() {
@@ -35,6 +30,6 @@ install_depends() {
     fi
 }
 
-version_0_2_3
-
 version_0_2_5
+
+version_0_2_11

+ 18 - 1
shell/helper.sh

@@ -245,9 +245,13 @@ do_umount() {
   if [[ -z ${MOUNT_POINT} ]]; then
     ${log} "Warning: ${DEVICE} is not mounted"
   else
+    /bin/kill -9 $(lsof ${MOUNT_POINT})
     umount -l ${DEVICE}
     ${log} "Unmounted ${DEVICE} from ${MOUNT_POINT}"
-    /bin/rmdir "${MOUNT_POINT}"
+    if [ "`ls -A ${MOUNT_POINT}`" = "" ]; then
+      /bin/rm -fr "${MOUNT_POINT}"
+    fi
+    
     sed -i.bak "\@${MOUNT_POINT}@d" /var/log/usb-mount.track
   fi
 
@@ -325,3 +329,16 @@ TarFolder() {
   #查看固定文件夹大小
   du -sh /DATA
 }
+
+USB_Move_File() {
+  ((EUID)) && sudo_cmd="sudo"
+  $sudo_cmd cp -rf /casaOS/server/shell/11-usb-mount.rules /etc/udev/rules.d/
+  $sudo_cmd chmod +x /casaOS/server/shell/usb-mount.sh
+  $sudo_cmd cp -rf /casaOS/server/shell/usb-mount@.service /etc/systemd/system/
+}
+
+USB_Remove_File() {
+  ((EUID)) && sudo_cmd="sudo"
+  $sudo_cmd rm -fr /etc/udev/rules.d/11-usb-mount.rules
+  $sudo_cmd rm -fr /etc/systemd/system/usb-mount@.service
+}

+ 5 - 2
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=$(lsblk -o name,mountpoint | grep ${DEVICE} | awk '{print $2}')
+MOUNT_POINT=$(lsblk -l -p -o name,mountpoint | grep ${DEVICE} | awk '{print $2}')
 
 do_mount() {
 
@@ -112,9 +112,12 @@ do_umount() {
   if [[ -z ${MOUNT_POINT} ]]; then
     ${log} "Warning: ${DEVICE} is not mounted"
   else
+    #/bin/kill -9 $(lsof ${MOUNT_POINT})
     umount -l ${DEVICE}
     ${log} "Unmounted ${DEVICE} from ${MOUNT_POINT}"
-    /bin/rmdir "${MOUNT_POINT}"
+    if [ "`ls -A ${MOUNT_POINT}`" = "" ]; then
+      /bin/rm -fr "${MOUNT_POINT}"
+    fi
     sed -i.bak "\@${MOUNT_POINT}@d" /var/log/usb-mount.track
   fi
 

+ 2 - 0
types/notify.go

@@ -10,6 +10,8 @@ const (
 	NOTIFY_TYPE_NEED_CONFIRM
 	NOTIFY_TYPE_ERROR
 	NOTIFY_TYPE_INSTALL_LOG
+	NOTIFY_TYPE_PERSION_FIRNED_LEAVE
+	NOTIFY_TYPE_PERSION_FIRNED_LIVE
 )
 
 const (

+ 1 - 0
types/persion.go → types/person.go

@@ -6,3 +6,4 @@ const PERSONSUMMARY = "summary"
 const PERSONCONNECTION = "connection"
 const PERSONDIRECTORY = "directory"
 const PERSONHELLO = "hello"
+const PERSONCANCEL = "cancel" // Cancel Download

+ 1 - 0
types/persion_download.go → types/person_download.go

@@ -6,4 +6,5 @@ const (
 	DOWNLOADPAUSE
 	DOWNLOADFINISH
 	DOWNLOADERROR
+	DOWNLOADFINISHED
 )

+ 2 - 2
types/system.go

@@ -1,5 +1,5 @@
 package types
 
-const CURRENTVERSION = "0.2.10"
+const CURRENTVERSION = "0.3.0"
 
-const BODY = "<li>Added CasaOS own file manager</li><li>Fixed the problem of failed to create storage space</li>"
+const BODY = "<li>Add CasaConnect function, now you can share private files peer-to-peer with your friends.</li><li>Add a widget for network traffic monitoring</li><li>Updated the initial directory of Files to the Root directory</li><li>Fix the application ipv6 opening problem</li>"

BIN
web/img/1-small.1b74d2ba.png


+ 55 - 0
web/img/folder-publicshare.0219e0d4.svg

@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="64" height="64" version="1.1" viewBox="0 0 16.933 16.933" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs>
+  <linearGradient id="linearGradient1911" x1="25.085" x2="25.085" y1="24.031" y2="26.412" gradientTransform="translate(-35.822,-21.385)" gradientUnits="userSpaceOnUse">
+   <stop stop-color="#fcbc19" stop-opacity=".99608" offset="0"/>
+   <stop stop-color="#f4b61f" offset="1"/>
+  </linearGradient>
+  <linearGradient id="linearGradient4625" x1=".52918" x2="16.404" y1="5.0665" y2="5.0665" gradientTransform="translate(-17.925)" gradientUnits="userSpaceOnUse">
+   <stop stop-color="#b78815" offset="0"/>
+   <stop stop-color="#e2b24b" stop-opacity="0" offset="1"/>
+  </linearGradient>
+  <linearGradient id="linearGradient1951" x1="100" x2="133.19" y1="17.453" y2="51.606" gradientTransform="matrix(.26458 0 0 .26458 -38.033 -.13539)" gradientUnits="userSpaceOnUse">
+   <stop stop-color="#fce798" offset="0"/>
+   <stop stop-color="#ffc937" offset="1"/>
+  </linearGradient>
+  <linearGradient id="linearGradient11110" x1=".52917" x2="16.404" y1="5.3815" y2="5.3815" gradientTransform="translate(-17.925)" gradientUnits="userSpaceOnUse">
+   <stop stop-color="#fff" offset="0"/>
+   <stop stop-color="#fff" stop-opacity="0" offset="1"/>
+  </linearGradient>
+  <linearGradient id="linearGradient1119" x1="8.4665" x2="8.4665" y1="6.0853" y2="9.4879" gradientUnits="userSpaceOnUse">
+   <stop stop-color="#fddb7a" offset="0"/>
+   <stop stop-color="#ffd970" offset="1"/>
+  </linearGradient>
+  <linearGradient id="linearGradient1284" x1="8.4665" x2="8.4665" y1="10.26" y2="13.229" gradientUnits="userSpaceOnUse">
+   <stop stop-color="#ffd96e" offset="0"/>
+   <stop stop-color="#ffd561" offset="1"/>
+  </linearGradient>
+  <linearGradient id="linearGradient1272" x1="-3.9033" x2="-3.9033" y1="7.7839" y2="15.613" gradientTransform="translate(12.362 -2.1249)" gradientUnits="userSpaceOnUse">
+   <stop stop-color="#c68d00" offset="0"/>
+   <stop stop-color="#a67100" offset="1"/>
+  </linearGradient>
+ </defs>
+ <metadata>
+  <rdf:RDF>
+   <cc:Work rdf:about="">
+    <dc:format>image/svg+xml</dc:format>
+    <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+   </cc:Work>
+  </rdf:RDF>
+ </metadata>
+ <g transform="translate(17.925 4.2e-5)">
+  <path d="m-16.728 2.2489c-0.3653 0-0.66145 0.32575-0.66145 0.72759v2.9636c-0.0026 0.02629-0.0072 0.052-0.0072 0.07906v7.9373c0 0.40184 0.29614 0.72759 0.66144 0.72759h14.552c0.36531 0 0.66144-0.32575 0.66144-0.72759v-9.393c0-0.40184-0.29614-0.72759-0.66144-0.72759v5.16e-4h-8.1993l-0.89864-1.1095s-0.36607-0.478-1.0583-0.478h-1.3229z" color="#000000" color-rendering="auto" dominant-baseline="auto" fill="url(#linearGradient1911)" image-rendering="auto" shape-rendering="auto" solid-color="#000000" stop-color="#000000" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;font-variation-settings:normal;inline-size:0;isolation:auto;mix-blend-mode:normal;shape-margin:0;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal"/>
+  <path d="m-9.5646 3.8364c-0.11312 0.0018-0.30848-0.01542-0.51986 0.09508-0.73647 0.37946-0.77226 0.59366-1.557 0.71415h-4.9608c-0.21986 0-0.41824 0.08855-0.56171 0.23202-0.13937 0.13936-0.22041 0.33333-0.2253 0.54569v0.2775c0.0049-0.21236 0.08593-0.40633 0.2253-0.54569 0.14347-0.14347 0.34185-0.23202 0.56171-0.23202h4.9608c0.78472-0.1205 0.82051-0.33469 1.557-0.71415 0.21138-0.1105 0.40673-0.09328 0.51986-0.09508h7.3828c0.18216 3.65e-4 0.3472 0.08101 0.46663 0.21239 0.1197 0.13167 0.19378 0.31377 0.19378 0.51469v-0.2775c0-0.20092-0.07408-0.38302-0.19378-0.51469-0.11943-0.13137-0.28447-0.21202-0.46663-0.21239h-1.1652zm-7.8247 2.1037c-0.0026 0.02629-0.0067 0.05201-0.0067 0.07906v0.2775c0-0.02705 0.0041-0.05277 0.0067-0.07906z" fill="url(#linearGradient4625)" opacity=".001" stroke-width=".26458"/>
+  <path d="m-17.396 13.674v0.28215c0 0.20092 0.07357 0.3825 0.19327 0.51417s0.28553 0.21342 0.46818 0.21342h14.552c0.18265 0 0.34796-0.08175 0.46766-0.21342s0.19378-0.31325 0.19378-0.51417v-0.28215c0 0.20092-0.07409 0.3825-0.19378 0.51417-0.1197 0.13167-0.28501 0.21342-0.46766 0.21342h-14.552c-0.18265 0-0.34848-0.08176-0.46818-0.21342s-0.19327-0.31325-0.19327-0.51417z" fill="#e4a729" fill-opacity=".99608" stroke-width=".26458"/>
+  <path d="m-10.383 4.0979s-0.35919 0.47868-1.1575 0.79218c-0.03271 0.00587-0.06509 0.011575-0.10077 0.017052h-4.9608c-0.21986 0-0.41824 0.088552-0.56171 0.23202-0.13937 0.13936-0.22042 0.33333-0.2253 0.54569v0.51675c-0.0026 0.02629-0.0067 0.05201-0.0067 0.07906v7.3953c0 0.20092 0.07357 0.3825 0.19327 0.51417s0.28553 0.21342 0.46818 0.21342h14.552c0.18265 0 0.34796-0.08175 0.46766-0.21342s0.19378-0.31325 0.19378-0.51417v-8.851c0-0.20092-0.07408-0.38302-0.19378-0.51469-0.11945-0.13137-0.28449-0.21202-0.46665-0.21239h-7.4821z" fill="url(#linearGradient1951)"/>
+  <path d="m-10.383 4.0979s-0.35919 0.47868-1.1575 0.79218c-0.03271 0.00587-0.06509 0.011575-0.10077 0.017052h-4.9608c-0.21986 0-0.41824 0.088552-0.56171 0.23202-0.13937 0.13936-0.22041 0.33333-0.2253 0.54569v0.38446c0.0049-0.21236 0.08593-0.40633 0.2253-0.54569 0.14347-0.14347 0.34185-0.23202 0.56171-0.23202h4.9608c0.03568-0.00548 0.06806-0.011184 0.10077-0.017052 0.75911-0.26303 1.2883-0.79218 1.2883-0.79218h8.0707c0.18216 3.704e-4 0.34718 0.081016 0.46663 0.21239 0.1197 0.13167 0.19378 0.31377 0.19378 0.51469v-0.38446c0-0.20092-0.07408-0.38302-0.19378-0.51469-0.11945-0.13137-0.28447-0.21202-0.46663-0.21239h-7.4821zm-7.0062 2.1037c-0.0026 0.02629-0.0067 0.05201-0.0067 0.07906v0.38446c0-0.02705 0.0041-0.05277 0.0067-0.07906z" fill="url(#linearGradient11110)" opacity=".3"/>
+ </g>
+ <circle cx="8.4665" cy="9.525" r="3.9687" fill="url(#linearGradient1272)" stroke-linecap="round" stroke-linejoin="round" stroke-width=".1726"/>
+ <g transform="matrix(.77756 0 0 .77756 10.567 2.0159)">
+  <g transform="matrix(1.0741 0 0 1.0741 -11.796 -.7153)">
+   <circle cx="8.4665" cy="7.8051" r="1.7198" fill="url(#linearGradient1119)" stroke-linecap="round" stroke-linejoin="round" stroke-width=".34395" style="paint-order:stroke fill markers"/>
+   <path d="m5.2916 11.064 5.4e-6 -0.14432c0-0.43295 0.43294-0.72158 0.72157-0.72158h4.9067c0.28863 0 0.72158 0.28863 0.72158 0.72158v0.14432c0 1.3096-1.416 2.1647-3.1749 2.1647-1.7589 0-3.1749-0.85514-3.1749-2.1647z" fill="url(#linearGradient1284)" stroke-width=".14049"/>
+  </g>
+ </g>
+</svg>

BIN
web/img/folder.c8ff81f3.png


BIN
web/img/xfile.402f9e59.png


+ 1 - 1
web/index.html

@@ -20,7 +20,7 @@
   <title>
     CasaOS
   </title>
-<link href="/ui/js/0.js" rel="prefetch"><link href="/ui/js/1.js" rel="prefetch"><link href="/ui/js/2.js" rel="prefetch"><link href="/ui/js/3.js" rel="prefetch"><link href="/ui/js/4.js" rel="prefetch"><link href="/ui/js/5.js" rel="prefetch"><link href="/ui/js/app.js" rel="preload" as="script"><link href="/ui/js/chunk-vendors.js" rel="preload" as="script"></head>
+<link href="/ui/js/0.js" rel="prefetch"><link href="/ui/js/1.js" rel="prefetch"><link href="/ui/js/2.js" rel="prefetch"><link href="/ui/js/3.js" rel="prefetch"><link href="/ui/js/4.js" rel="prefetch"><link href="/ui/js/5.js" rel="prefetch"><link href="/ui/js/6.js" rel="prefetch"><link href="/ui/js/7.js" rel="prefetch"><link href="/ui/js/app.js" rel="preload" as="script"><link href="/ui/js/chunk-vendors.js" rel="preload" as="script"></head>
 
 <body>
   <noscript>

File diff suppressed because it is too large
+ 9 - 9
web/js/0.js


File diff suppressed because it is too large
+ 0 - 8
web/js/1.js


File diff suppressed because it is too large
+ 9 - 10
web/js/2.js


File diff suppressed because it is too large
+ 0 - 8
web/js/3.js


File diff suppressed because it is too large
+ 4 - 4
web/js/4.js


File diff suppressed because it is too large
+ 4 - 4
web/js/5.js


File diff suppressed because it is too large
+ 10 - 0
web/js/6.js


File diff suppressed because it is too large
+ 10 - 0
web/js/7.js


File diff suppressed because it is too large
+ 0 - 0
web/js/app.js


File diff suppressed because it is too large
+ 7 - 0
web/js/chunk-vendors.js


Some files were not shown because too many files changed in this diff