Bladeren bron

Fixing bugs

Resolve application installation path errors
link 3 jaren geleden
bovenliggende
commit
f020c1162d
15 gewijzigde bestanden met toevoegingen van 327 en 24 verwijderingen
  1. 1 1
      UI
  2. 1 0
      main.go
  3. 2 0
      model/sys_common.go
  4. 8 0
      model/system_app/sync.go
  5. 5 1
      pkg/docker/volumes.go
  6. 13 0
      pkg/utils/file/file.go
  7. 188 0
      route/init.go
  8. 1 16
      route/route.go
  9. 4 0
      route/v1/docker.go
  10. 22 0
      route/v1/sync.go
  11. 71 1
      service/app.go
  12. 8 4
      service/docker.go
  13. 2 0
      service/model/o_container.go
  14. 1 0
      service/service.go
  15. 0 1
      service/system.go

+ 1 - 1
UI

@@ -1 +1 @@
-Subproject commit 11d304e96ddcd9fedea690ab3c8f7750b0ea8a41
+Subproject commit 741aadb0110cf4def85c83d8123da9bbd66c34d1

+ 1 - 0
main.go

@@ -32,6 +32,7 @@ func init() {
 	//gredis.GetRedisConn(config.RedisInfo),
 	service.MyService = service.NewService(sqliteDB, loger2.NewOLoger())
 	service.Cache = cache.Init()
+	route.InitFunction()
 }
 
 // @title casaOS API

+ 2 - 0
model/sys_common.go

@@ -64,4 +64,6 @@ type SystemConfig struct {
 	ConfigStr  string `json:"config_str"`
 	WidgetList string `json:"widget_list"`
 	ConfigPath string `json:"config_path"`
+	SyncPort   string `json:"sync_port"`
+	SyncKey    string `json:"sync_key"`
 }

+ 8 - 0
model/system_app/sync.go

@@ -0,0 +1,8 @@
+package system_app
+
+import "encoding/xml"
+
+type SyncConfig struct {
+	XMLName xml.Name `xml:"configuration"`
+	Key     string   `xml:"gui>apikey"`
+}

+ 5 - 1
pkg/docker/volumes.go

@@ -10,16 +10,20 @@ func GetDir(id, envName string) string {
 	}
 
 	switch {
-	case strings.Contains(strings.ToLower(envName), "config"):
+	case strings.Contains(strings.ToLower(envName), "config") || strings.Contains(strings.ToLower(envName), "photoprism/storage") || strings.Contains(strings.ToLower(envName), "config"):
 		path = "/DATA/AppData/" + id + "/"
 	case strings.Contains(strings.ToLower(envName), "movie"):
 		path = "/DATA/Media/Movies/"
 	case strings.Contains(strings.ToLower(envName), "music"):
 		path = "/DATA/Media/Music/"
+	case strings.Contains(strings.ToLower(envName), "photoprism/originals"):
+		path = "/DATA/Gallery"
 	case strings.Contains(strings.ToLower(envName), "download"):
 		path = "/DATA/Downloads/"
 	case strings.Contains(strings.ToLower(envName), "photo") || strings.Contains(strings.ToLower(envName), "pictures"):
 		path = "/DATA/Downloads/"
+	case strings.ToLower(envName) == "/srv":
+		path = "/DATA/"
 	default:
 		//path = "/media"
 	}

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

@@ -146,3 +146,16 @@ func IsNotExistCreateFile(src string) error {
 
 	return nil
 }
+
+func ReadFullFile(path string) []byte {
+	file, err := os.Open(path)
+	if err != nil {
+		return []byte("")
+	}
+	defer file.Close()
+	content, err := ioutil.ReadAll(file)
+	if err != nil {
+		return []byte("")
+	}
+	return content
+}

+ 188 - 0
route/init.go

@@ -0,0 +1,188 @@
+package route
+
+import (
+	"encoding/json"
+	"encoding/xml"
+	"strconv"
+	"time"
+
+	"github.com/IceWhaleTech/CasaOS/model"
+	"github.com/IceWhaleTech/CasaOS/model/system_app"
+	"github.com/IceWhaleTech/CasaOS/pkg/config"
+	"github.com/IceWhaleTech/CasaOS/pkg/docker"
+	"github.com/IceWhaleTech/CasaOS/pkg/utils/env_helper"
+	"github.com/IceWhaleTech/CasaOS/pkg/utils/file"
+	"github.com/IceWhaleTech/CasaOS/pkg/utils/port"
+	"github.com/IceWhaleTech/CasaOS/service"
+	model2 "github.com/IceWhaleTech/CasaOS/service/model"
+	uuid "github.com/satori/go.uuid"
+)
+
+func InitFunction() {
+	go checkSystemApp()
+}
+
+var syncIsExistence = false
+
+func installSyncthing(appId string) {
+
+	var appInfo model.ServerAppList
+	m := model.CustomizationPostData{}
+	var dockerImage string
+	var dockerImageVersion string
+
+	appInfo = service.MyService.OAPI().GetServerAppInfo(appId)
+
+	dockerImage = appInfo.Image
+
+	if len(appInfo.ImageVersion) == 0 {
+		dockerImageVersion = "latest"
+	}
+
+	if appInfo.NetworkModel != "host" {
+		for i := 0; i < len(appInfo.Ports); i++ {
+			if p, _ := strconv.Atoi(appInfo.Ports[i].ContainerPort); port.IsPortAvailable(p, appInfo.Ports[i].Protocol) {
+				appInfo.Ports[i].CommendPort = strconv.Itoa(p)
+			} else {
+				if appInfo.Ports[i].Protocol == "tcp" {
+					if p, err := port.GetAvailablePort("tcp"); err == nil {
+						appInfo.Ports[i].CommendPort = strconv.Itoa(p)
+					}
+				} else if appInfo.Ports[i].Protocol == "upd" {
+					if p, err := port.GetAvailablePort("udp"); err == nil {
+						appInfo.Ports[i].CommendPort = strconv.Itoa(p)
+					}
+				}
+			}
+
+			if appInfo.Ports[i].Type == 0 {
+				appInfo.PortMap = appInfo.Ports[i].CommendPort
+			}
+		}
+	}
+
+	for i := 0; i < len(appInfo.Devices); i++ {
+		if !file.CheckNotExist(appInfo.Devices[i].ContainerPath) {
+			appInfo.Devices[i].Path = appInfo.Devices[i].ContainerPath
+		}
+	}
+	if len(appInfo.Tip) > 0 {
+		appInfo.Tip = env_helper.ReplaceStringDefaultENV(appInfo.Tip)
+	}
+
+	for i := 0; i < len(appInfo.Volumes); i++ {
+		appInfo.Volumes[i].Path = docker.GetDir("", appInfo.Volumes[i].ContainerPath)
+	}
+	appInfo.MaxMemory = service.MyService.ZiMa().GetMemInfo().Total >> 20
+
+	id := uuid.NewV4().String()
+
+	installLog := model2.AppNotify{}
+
+	// step:下载镜像
+	err := service.MyService.Docker().DockerPullImage(dockerImage+":"+dockerImageVersion, installLog)
+	if err != nil {
+		//pull image error
+		return
+	}
+
+	for !service.MyService.Docker().IsExistImage(dockerImage + ":" + dockerImageVersion) {
+		time.Sleep(time.Second)
+	}
+
+	m.CpuShares = 50
+	m.Envs = appInfo.Envs
+	m.Memory = int64(appInfo.MaxMemory)
+	m.Origin = "system"
+	m.PortMap = appInfo.PortMap
+	m.Ports = appInfo.Ports
+	m.Restart = ""
+	m.Volumes = appInfo.Volumes
+
+	containerId, err := service.MyService.Docker().DockerContainerCreate(dockerImage+":"+dockerImageVersion, id, m, appInfo.NetworkModel)
+
+	if err != nil {
+		// create container error
+		return
+	}
+
+	//step:start container
+	err = service.MyService.Docker().DockerContainerStart(id)
+	if err != nil {
+		//start container error
+		return
+	}
+
+	portsStr, _ := json.Marshal(appInfo.Ports)
+	envsStr, _ := json.Marshal(appInfo.Envs)
+	volumesStr, _ := json.Marshal(appInfo.Volumes)
+	devicesStr, _ := json.Marshal(appInfo.Devices)
+	//step: 保存数据到数据库
+	md := model2.AppListDBModel{
+		CustomId: id,
+		Title:    appInfo.Title,
+		//ScreenshotLink: appInfo.ScreenshotLink,
+		Slogan:      appInfo.Tagline,
+		Description: appInfo.Description,
+		//Tags:           appInfo.Tags,
+		Icon:        appInfo.Icon,
+		Version:     dockerImageVersion,
+		ContainerId: containerId,
+		Image:       dockerImage,
+		Index:       appInfo.Index,
+		PortMap:     appInfo.PortMap,
+		Label:       appInfo.Title,
+		EnableUPNP:  false,
+		Ports:       string(portsStr),
+		Envs:        string(envsStr),
+		Volumes:     string(volumesStr),
+		Position:    true,
+		NetModel:    appInfo.NetworkModel,
+		Restart:     m.Restart,
+		CpuShares:   50,
+		Memory:      int64(appInfo.MaxMemory),
+		Devices:     string(devicesStr),
+		Origin:      m.Origin,
+		CreatedAt:   strconv.FormatInt(time.Now().Unix(), 10),
+		UpdatedAt:   strconv.FormatInt(time.Now().Unix(), 10),
+	}
+	service.MyService.App().SaveContainer(md)
+
+	checkSystemApp()
+}
+
+// check if the system application is installed
+func checkSystemApp() {
+	list := service.MyService.App().GetSystemAppList()
+	for _, v := range *list {
+		if v.Image == "linuxserver/syncthing" {
+			syncIsExistence = true
+			if config.SystemConfigInfo.SyncPort != v.Port {
+				config.SystemConfigInfo.SyncPort = v.Port
+			}
+			var paths []model.PathMap
+			json.Unmarshal([]byte(v.Volumes), &paths)
+			path := ""
+			for _, i := range paths {
+				if i.ContainerPath == "/config" {
+					path = docker.GetDir(v.CustomId, i.ContainerPath) + "config.xml"
+					for i := 0; i < 10; i++ {
+						if file.CheckNotExist(path) {
+							time.Sleep(1 * time.Second)
+						} else {
+							break
+						}
+					}
+					break
+				}
+			}
+			content := file.ReadFullFile(path)
+			syncConfig := &system_app.SyncConfig{}
+			xml.Unmarshal(content, &syncConfig)
+			config.SystemConfigInfo.SyncKey = syncConfig.Key
+		}
+	}
+	if !syncIsExistence {
+		installSyncthing("44")
+	}
+}

+ 1 - 16
route/route.go

@@ -1,10 +1,7 @@
 package route
 
 import (
-	"fmt"
 	"net/http"
-	"net/http/httputil"
-	"net/url"
 
 	"github.com/IceWhaleTech/CasaOS/middleware"
 	"github.com/IceWhaleTech/CasaOS/pkg/config"
@@ -41,19 +38,6 @@ func InitRouter() *gin.Engine {
 	//get user info
 	r.GET("/v1/user/info", v1.UserInfo)
 
-	r.GET("/syncthing/*url", func(c *gin.Context) {
-		ur := c.Param("url")
-		fmt.Println(ur)
-		target := "http://localhost:8384" //最终要访问的服务
-		remote, err := url.Parse(target)
-		if err != nil {
-			fmt.Println(err)
-		}
-		proxy := httputil.NewSingleHostReverseProxy(remote)
-		c.Request.URL.Path = "/" + ur //请求API
-		proxy.ServeHTTP(c.Writer, c.Request)
-	})
-
 	v1Group := r.Group("/v1")
 
 	v1Group.Use(jwt2.JWT(swagHandler))
@@ -282,6 +266,7 @@ func InitRouter() *gin.Engine {
 		{
 			v1SearchGroup.GET("/search", v1.GetSearchList)
 		}
+		v1Group.GET("/sync/*url", v1.SyncToSyncthing)
 	}
 	return r
 }

+ 4 - 0
route/v1/docker.go

@@ -1135,6 +1135,10 @@ func ContainerUpdateInfo(c *gin.Context) {
 	var vol model.PathArray
 	json2.Unmarshal([]byte(appInfo.Volumes), &vol)
 
+	for i := 0; i < len(vol); i++ {
+		vol[i].Path = strings.ReplaceAll(vol[i].Path, "$AppID", appId)
+	}
+
 	var dir model.PathArray
 	json2.Unmarshal([]byte(appInfo.Devices), &dir)
 

+ 22 - 0
route/v1/sync.go

@@ -0,0 +1,22 @@
+package v1
+
+import (
+	"net/http/httputil"
+	"net/url"
+
+	"github.com/IceWhaleTech/CasaOS/pkg/config"
+	"github.com/gin-gonic/gin"
+)
+
+func SyncToSyncthing(c *gin.Context) {
+	u := c.Param("url")
+	target := "http://127.0.0.1:" + config.SystemConfigInfo.SyncPort
+	remote, err := url.Parse(target)
+	if err != nil {
+		return
+	}
+	proxy := httputil.NewSingleHostReverseProxy(remote)
+	c.Request.Header.Add("X-API-Key", config.SystemConfigInfo.SyncKey)
+	c.Request.URL.Path = u
+	proxy.ServeHTTP(c.Writer, c.Request)
+}

+ 71 - 1
service/app.go

@@ -27,6 +27,7 @@ type AppService interface {
 	UpdateApp(m model2.AppListDBModel)
 	GetSimpleContainerInfo(name string) (types.Container, error)
 	DelAppConfigDir(id, path string)
+	GetSystemAppList() *[]model2.MyAppList
 }
 
 type appStruct struct {
@@ -52,7 +53,7 @@ func (a *appStruct) GetMyList(index, size int, position bool) *[]model2.MyAppLis
 	//获取本地数据库应用
 
 	var lm []model2.AppListDBModel
-	a.db.Table(model2.CONTAINERTABLENAME).Select("title,icon,port_map,`index`,container_id,position,label,slogan").Find(&lm)
+	a.db.Table(model2.CONTAINERTABLENAME).Select("title,icon,port_map,`index`,container_id,position,label,slogan,image").Find(&lm)
 
 	list := []model2.MyAppList{}
 	lMap := make(map[string]interface{})
@@ -65,6 +66,67 @@ func (a *appStruct) GetMyList(index, size int, position bool) *[]model2.MyAppLis
 			lMap[dbModel.ContainerId] = dbModel
 		}
 	}
+	for _, container := range containers {
+
+		if lMap[container.ID] != nil && container.Labels["origin"] != "system" {
+			var m model2.AppListDBModel
+			m = lMap[container.ID].(model2.AppListDBModel)
+			if len(m.Label) == 0 {
+				m.Label = m.Title
+			}
+
+			info, err := cli.ContainerInspect(context.Background(), container.ID)
+			var tm string
+			if err != nil {
+				tm = time.Now().String()
+			} else {
+				tm = info.State.StartedAt
+			}
+			list = append(list, model2.MyAppList{
+				Name:     m.Label,
+				Icon:     m.Icon,
+				State:    container.State,
+				CustomId: strings.ReplaceAll(container.Names[0], "/", ""),
+				Port:     m.PortMap,
+				Index:    m.Index,
+				UpTime:   tm,
+				Image:    m.Image,
+				Slogan:   m.Slogan,
+				//Rely:     m.Rely,
+			})
+		}
+
+	}
+
+	return &list
+
+}
+
+//system application list
+func (a *appStruct) GetSystemAppList() *[]model2.MyAppList {
+	//获取docker应用
+	cli, err := client2.NewClientWithOpts(client2.FromEnv)
+	if err != nil {
+		a.log.Error("初始化client失败", "app.getmylist", "line:36", err)
+	}
+	defer cli.Close()
+	fts := filters.NewArgs()
+	fts.Add("label", "origin=system")
+	containers, err := cli.ContainerList(context.Background(), types.ContainerListOptions{All: true, Filters: fts})
+	if err != nil {
+		a.log.Error("获取docker容器失败", "app.getmylist", "line:42", err)
+	}
+
+	//获取本地数据库应用
+
+	var lm []model2.AppListDBModel
+	a.db.Table(model2.CONTAINERTABLENAME).Select("title,icon,port_map,`index`,container_id,position,label,slogan,image,volumes").Find(&lm)
+
+	list := []model2.MyAppList{}
+	lMap := make(map[string]interface{})
+	for _, dbModel := range lm {
+		lMap[dbModel.ContainerId] = dbModel
+	}
 	for _, container := range containers {
 
 		if lMap[container.ID] != nil {
@@ -89,7 +151,9 @@ func (a *appStruct) GetMyList(index, size int, position bool) *[]model2.MyAppLis
 				Port:     m.PortMap,
 				Index:    m.Index,
 				UpTime:   tm,
+				Image:    m.Image,
 				Slogan:   m.Slogan,
+				Volumes:  m.Volumes,
 				//Rely:     m.Rely,
 			})
 		}
@@ -168,6 +232,12 @@ func (a *appStruct) RemoveContainerById(id string) {
 	a.db.Table(model2.CONTAINERTABLENAME).Where("custom_id = ?", id).Delete(&model2.AppListDBModel{})
 }
 
+// init install
+func Init() {
+
+}
+
 func NewAppService(db *gorm.DB, logger loger2.OLog) AppService {
+	Init()
 	return &appStruct{db: db, log: logger}
 }

+ 8 - 4
service/docker.go

@@ -7,6 +7,7 @@ import (
 	"encoding/binary"
 	json2 "encoding/json"
 	"fmt"
+	"reflect"
 	"regexp"
 	"syscall"
 
@@ -332,10 +333,13 @@ func (ds *dockerService) DockerPullImage(imageName string, m model2.AppNotify) e
 			}
 			break
 		}
-		m.Type = types2.NOTIFY_TYPE_INSTALL_LOG
-		m.State = 0
-		m.Message = string(buf[:n])
-		MyService.Notify().UpdateLog(m)
+		if !reflect.DeepEqual(m, model2.AppNotify{}) {
+			m.Type = types2.NOTIFY_TYPE_INSTALL_LOG
+			m.State = 0
+			m.Message = string(buf[:n])
+			MyService.Notify().UpdateLog(m)
+		}
+
 	}
 	return err
 }

+ 2 - 0
service/model/o_container.go

@@ -61,4 +61,6 @@ type MyAppList struct {
 	UpTime   string           `json:"up_time"`
 	Slogan   string           `json:"slogan"`
 	Rely     model.MapStrings `json:"rely"` //[{"mysql":"id"},{"mysql":"id"}]
+	Image    string           `json:"image"`
+	Volumes  string           `json:"volumes"`
 }

+ 1 - 0
service/service.go

@@ -30,6 +30,7 @@ type Repository interface {
 }
 
 func NewService(db *gorm.DB, log loger2.OLog) Repository {
+
 	return &store{
 		app:    NewAppService(db, log),
 		ddns:   NewDDNSService(db, log),

+ 0 - 1
service/system.go

@@ -39,7 +39,6 @@ func (s *systemService) UpSystemConfig(str string, widget string) {
 	}
 	config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath)
 }
-
 func (s *systemService) GetCasaOSLogs(lineNumber int) string {
 	file, err := os.Open(s.log.Path())
 	if err != nil {