New App Store

This commit is contained in:
link 2021-12-29 16:42:20 +08:00
parent 1d62fbd670
commit 1d17d27c96
25 changed files with 689 additions and 227 deletions

2
UI

@ -1 +1 @@
Subproject commit 65d4738a607ec28f22bffb1d489dd284c360d2ce
Subproject commit f3088b635482fd4330c3fce6bbfcd1f70ff8024a

View file

@ -15,6 +15,7 @@ type ServerAppList struct {
Icon string `json:"icon"`
ScreenshotLink Strings `gorm:"type:json" json:"screenshot_link"`
Category string `json:"category"`
CategoryFont string `json:"category_font"`
PortMap string `json:"port_map"`
ImageVersion string `json:"image_version"`
Tip string `json:"tip"`
@ -36,6 +37,8 @@ type ServerAppList struct {
Healthy string `json:"healthy"`
Plugins Strings `json:"plugins"`
Origin string `json:"origin"`
Type int `json:"type"`
Developer string `json:"developer"`
}
type Ports struct {

View file

@ -5,6 +5,7 @@ type ServerCategoryList struct {
//CreatedAt time.Time `json:"created_at"`
//
//UpdatedAt time.Time `json:"updated_at"`
Font string `json:"font"`
Name string `json:"name"`
Count uint `json:"count"`
}

View file

@ -3,33 +3,9 @@ package docker
import "strings"
func GetDir(id, envName string) string {
var path string
if len(id) == 0 {
id = "$AppID"
if strings.Contains(envName, "$AppID") && len(id) > 0 {
return strings.ReplaceAll(envName, "$AppID", id)
}
switch {
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), "media"):
path = "/DATA/Media/"
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/"
case strings.ToLower(envName) == "/tv":
path = "/DATA/Media/TV Shows"
default:
//path = "/media"
}
return path
return envName
}

View file

@ -2,11 +2,12 @@ package sqlite
import (
"fmt"
"time"
"github.com/IceWhaleTech/CasaOS/pkg/utils/file"
model2 "github.com/IceWhaleTech/CasaOS/service/model"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"time"
)
var gdb *gorm.DB
@ -30,7 +31,7 @@ func GetDb(projectPath string) *gorm.DB {
return nil
}
gdb = db
err = db.AutoMigrate(&model2.TaskDBModel{}, &model2.AppNotify{}, &model2.AppListDBModel{})
err = db.AutoMigrate(&model2.TaskDBModel{}, &model2.AppNotify{}, &model2.AppListDBModel{}, &model2.SerialDisk{})
if err != nil {
fmt.Println("检查和创建数据库出错", err)
}

View file

@ -75,6 +75,7 @@ func ExecResultStr(cmdStr string) string {
func ExecLSBLK() []byte {
output, err := exec.Command("lsblk", "-O", "-J", "-b").Output()
if err != nil {
fmt.Println("lsblk", err)
return nil
}
return output

View file

@ -2,18 +2,25 @@ package env_helper
import "strings"
func ReplaceDefaultENV(key string) string {
func ReplaceDefaultENV(key, tz string) string {
temp := ""
switch key {
case "$DefaultPassword":
temp = "casaos"
case "$DefaultUserName":
temp = "admin"
case "$PUID":
temp = "1000"
case "$PGID":
temp = "1000"
case "$TZ":
temp = tz
}
return temp
}
//replace env default setting
func ReplaceStringDefaultENV(str string) string {
return strings.ReplaceAll(strings.ReplaceAll(str, "$DefaultPassword", ReplaceDefaultENV("$DefaultPassword")), "$DefaultUserName", ReplaceDefaultENV("$DefaultUserName"))
return strings.ReplaceAll(strings.ReplaceAll(str, "$DefaultPassword", ReplaceDefaultENV("$DefaultPassword", "")), "$DefaultUserName", ReplaceDefaultENV("$DefaultUserName", ""))
}

View file

@ -22,6 +22,8 @@ import (
func InitFunction() {
go checkSystemApp()
Update2_3()
CheckSerialDiskMount()
}
var syncIsExistence = false
@ -72,9 +74,6 @@ func installSyncthing(appId string) {
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()
@ -171,7 +170,7 @@ func checkSystemApp() {
path := ""
for _, i := range paths {
if i.ContainerPath == "/config" {
path = docker.GetDir(v.CustomId, i.ContainerPath) + "config.xml"
path = docker.GetDir(v.CustomId, i.Path) + "config.xml"
for i := 0; i < 10; i++ {
if file.CheckNotExist(path) {
time.Sleep(1 * time.Second)
@ -189,12 +188,31 @@ func checkSystemApp() {
}
}
if !syncIsExistence {
installSyncthing("44")
installSyncthing("74")
}
}
func CheckSerialDiskMount() {
// 检查挂载点重新挂载
// 检查新硬盘是否有多个分区,如有多个分区需提示
// check mount point
dbList := service.MyService.Disk().GetSerialAll()
list := service.MyService.Disk().LSBLK()
mountPoint := make(map[string]string, len(dbList))
for _, v := range list {
if v.Children != nil {
for _, h := range v.Children {
mountPoint[h.MountPoint] = "1"
}
}
}
//remount
for _, item := range dbList {
if _, ok := mountPoint[item.MountPoint]; !ok {
service.MyService.Disk().MountDisk(item.Path, item.MountPoint)
}
}
}
func Update2_3() {
command.OnlyExec("source " + config.AppInfo.ProjectPath + "/shell/assist.sh")

View file

@ -70,8 +70,7 @@ func InitRouter() *gin.Engine {
//获取网络信息
v1ZiMaGroup.GET("/getnetinfo", v1.NetInfo)
//获取网络信息
v1ZiMaGroup.GET("/getinfo", v1.Info)
//获取系统信息
v1ZiMaGroup.GET("/sysinfo", v1.SysInfo)
}
@ -197,6 +196,7 @@ func InitRouter() *gin.Engine {
v1SysGroup.GET("/port", v1.GetCasaOSPort)
v1SysGroup.PUT("/port", v1.PutCasaOSPort)
v1SysGroup.POST("/kill", v1.PostKillCasaOS)
v1SysGroup.GET("/info", v1.Info)
}
v1FileGroup := v1Group.Group("/file")
v1FileGroup.Use()
@ -211,11 +211,13 @@ func InitRouter() *gin.Engine {
v1FileGroup.POST("/create", v1.PostCreateFile)
v1FileGroup.GET("/download", v1.GetDownloadFile)
v1FileGroup.PUT("/move", v1.PutFileMove)
//v1FileGroup.GET("/download", v1.UserFileDownloadCommonService)
}
v1DiskGroup := v1Group.Group("/disk")
v1DiskGroup.Use()
{
v1DiskGroup.GET("/check", v1.GetDiskCheck)
//获取磁盘列表
v1DiskGroup.GET("/list", v1.GetPlugInDisk)
@ -238,8 +240,8 @@ func InitRouter() *gin.Engine {
v1DiskGroup.POST("/mount", v1.PostMountDisk)
//umount SATA disk
v1DiskGroup.POST("/umount", v1.DeleteUmountDisk)
v1DiskGroup.POST("/umount", v1.PostDiskUmount)
v1DiskGroup.DELETE("/remove/:id", v1.DeleteDisk)
}
v1ShareGroup := v1Group.Group("/share")
v1ShareGroup.Use()

View file

@ -7,8 +7,6 @@ import (
"strconv"
"github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/pkg/docker"
"github.com/IceWhaleTech/CasaOS/pkg/utils/env_helper"
"github.com/IceWhaleTech/CasaOS/pkg/utils/file"
oasis_err2 "github.com/IceWhaleTech/CasaOS/pkg/utils/oasis_err"
port2 "github.com/IceWhaleTech/CasaOS/pkg/utils/port"
@ -33,20 +31,33 @@ func AppList(c *gin.Context) {
//service.MyService.Docker().DockerContainerCommit("test2")
index := c.DefaultQuery("index", "1")
size := c.DefaultQuery("size", "10")
size := c.DefaultQuery("size", "10000")
t := c.DefaultQuery("type", "rank")
categoryId := c.DefaultQuery("category_id", "0")
key := c.DefaultQuery("key", "")
list, count := service.MyService.OAPI().GetServerList(index, size, t, categoryId, key)
recommend, list, community := service.MyService.OAPI().GetServerList(index, size, t, categoryId, key)
for i := 0; i < len(recommend); i++ {
ct, _ := service.MyService.Docker().DockerListByImage(recommend[i].Image, recommend[i].ImageVersion)
if ct != nil {
list[i].State = ct.State
}
}
for i := 0; i < len(list); i++ {
ct, _ := service.MyService.Docker().DockerListByImage(list[i].Image, list[i].ImageVersion)
if ct != nil {
list[i].State = ct.State
}
}
data := make(map[string]interface{}, 2)
data["count"] = count
data["items"] = list
for i := 0; i < len(community); i++ {
ct, _ := service.MyService.Docker().DockerListByImage(community[i].Image, community[i].ImageVersion)
if ct != nil {
list[i].State = ct.State
}
}
data := make(map[string]interface{}, 3)
data["recommend"] = recommend
data["list"] = list
data["community"] = community
c.JSON(http.StatusOK, &model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: data})
}
@ -147,6 +158,13 @@ func AppInfo(c *gin.Context) {
info.PortMap = info.Ports[i].CommendPort
}
}
} else {
for i := 0; i < len(info.Ports); i++ {
if info.Ports[i].Type == 0 {
info.PortMap = info.Ports[i].ContainerPort
break
}
}
}
for i := 0; i < len(info.Devices); i++ {
@ -154,13 +172,10 @@ func AppInfo(c *gin.Context) {
info.Devices[i].Path = info.Devices[i].ContainerPath
}
}
if len(info.Tip) > 0 {
info.Tip = env_helper.ReplaceStringDefaultENV(info.Tip)
}
// if len(info.Tip) > 0 {
// info.Tip = env_helper.ReplaceStringDefaultENV(info.Tip)
// }
for i := 0; i < len(info.Volumes); i++ {
info.Volumes[i].Path = docker.GetDir("", info.Volumes[i].ContainerPath)
}
// portOrder := func(c1, c2 *model.Ports) bool {
// return c1.Type < c2.Type
// }
@ -207,7 +222,7 @@ func CategoryList(c *gin.Context) {
}
rear := append([]model.ServerCategoryList{}, list[0:]...)
list = append(list[:0], model.ServerCategoryList{Count: count, Name: "All"})
list = append(list[:0], model.ServerCategoryList{Count: count, Name: "All", Font: "apps"})
list = append(list, rear...)
c.JSON(http.StatusOK, &model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: list})
}

View file

@ -2,10 +2,12 @@ package v1
import (
"net/http"
"strconv"
"github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/pkg/utils/oasis_err"
"github.com/IceWhaleTech/CasaOS/service"
model2 "github.com/IceWhaleTech/CasaOS/service/model"
"github.com/gin-gonic/gin"
"github.com/shirou/gopsutil/v3/disk"
)
@ -19,44 +21,9 @@ import (
// @Router /disk/list [get]
func GetPlugInDisk(c *gin.Context) {
//ls := service.MyService.Disk().GetPlugInDisk()
//fmt.Println(ls)
//dd, _ := disk.Partitions(true)
//fmt.Println(dd)
//
//dir, err := ioutil.ReadDir("/sys/block")
//if err != nil {
// panic(err)
//}
//
//files := make([]string, 0)
//
////fmt.Println(regexp.MatchString("sd[a-z]*[0-9]", "sda"))
//
//for _, f := range dir {
// if match, _ := regexp.MatchString("sd[a-z]", f.Name()); match {
// files = append(files, f.Name())
// }
//}
//fmt.Println(files)
//filess := make([]string, 0)
//for _, file := range files {
// dirs, _ := ioutil.ReadDir("/sys/block/" + file)
//
// for _, f := range dirs {
// if match, _ := regexp.MatchString("sd[a-z]*[0-9]", f.Name()); match {
// filess = append(filess, f.Name())
// }
// }
//}
//fmt.Println(filess)
//
//for _, s := range filess {
// fmt.Println(disk.Usage("/dev/" + s))
//}
list := service.MyService.Disk().LSBLK()
lst := service.MyService.Disk().LSBLK()
c.JSON(http.StatusOK, model.Result{Success: oasis_err.SUCCESS, Message: oasis_err.GetMsg(oasis_err.SUCCESS), Data: lst})
c.JSON(http.StatusOK, model.Result{Success: oasis_err.SUCCESS, Message: oasis_err.GetMsg(oasis_err.SUCCESS), Data: list})
}
// @Summary get disk list
@ -98,7 +65,7 @@ func GetDiskInfo(c *gin.Context) {
// @Accept multipart/form-data
// @Tags disk
// @Security ApiKeyAuth
// @Param path formData string true "磁盘路径 例如/dev/sda1"
// @Param path formData string true "for example /dev/sda1"
// @Success 200 {string} string "ok"
// @Router /disk/format [post]
func FormatDisk(c *gin.Context) {
@ -108,8 +75,8 @@ func FormatDisk(c *gin.Context) {
if len(path) == 0 || len(t) == 0 {
c.JSON(http.StatusOK, model.Result{Success: oasis_err.INVALID_PARAMS, Message: oasis_err.GetMsg(oasis_err.INVALID_PARAMS)})
return
}
//格式化磁盘
service.MyService.Disk().FormatDisk(path, t)
c.JSON(http.StatusOK, model.Result{Success: oasis_err.SUCCESS, Message: oasis_err.GetMsg(oasis_err.SUCCESS)})
@ -168,23 +135,106 @@ func AddPartition(c *gin.Context) {
c.JSON(http.StatusOK, model.Result{Success: oasis_err.SUCCESS, Message: oasis_err.GetMsg(oasis_err.SUCCESS)})
}
// @Summary add mount point
// @Produce application/json
// @Accept multipart/form-data
// @Tags disk
// @Security ApiKeyAuth
// @Param path formData string true "for example: /dev/sda1"
// @Param serial formData string true "disk id"
// @Success 200 {string} string "ok"
// @Router /disk/mount [post]
func PostMountDisk(c *gin.Context) {
// for example: path=/dev/sda1
path := c.PostForm("path")
//执行挂载目录
service.MyService.Disk().MountDisk(path, "volume")
//添加到数据库
serial := c.PostForm("serial")
mountPath := "/mnt/volume"
var list = service.MyService.Disk().GetSerialAll()
var pathMapList = make(map[string]string, len(list))
for _, v := range list {
pathMapList[v.MountPoint] = "1"
}
for i := 0; i < len(list)+1; i++ {
if _, ok := pathMapList[mountPath+strconv.Itoa(i)]; !ok {
mountPath = mountPath + strconv.Itoa(i)
break
}
}
//mount dir
service.MyService.Disk().MountDisk(path, mountPath)
//save to data
m := model2.SerialDisk{}
m.MountPoint = mountPath
m.Path = path
m.Serial = serial
m.State = 0
service.MyService.Disk().SaveMountPoint(m)
c.JSON(http.StatusOK, model.Result{Success: oasis_err.SUCCESS, Message: oasis_err.GetMsg(oasis_err.SUCCESS)})
}
func DeleteUmountDisk(c *gin.Context) {
// @Summary remove mount point
// @Produce application/json
// @Accept multipart/form-data
// @Tags disk
// @Security ApiKeyAuth
// @Param path formData string true "for example: /dev/sda1"
// @Param mount_point formData string true "for example: /mnt/volume1"
// @Success 200 {string} string "ok"
// @Router /disk/umount [post]
func PostDiskUmount(c *gin.Context) {
// for example: path=/dev/sda1
//
path := c.PostForm("path")
mountPoint := c.PostForm("mount_point")
service.MyService.Disk().UmountPointAndRemoveDir(path)
//删除数据库记录
//delete data
service.MyService.Disk().DeleteMountPoint(path, mountPoint)
c.JSON(http.StatusOK, model.Result{Success: oasis_err.SUCCESS, Message: oasis_err.GetMsg(oasis_err.SUCCESS)})
}
// @Summary confirm delete disk
// @Produce application/json
// @Accept application/json
// @Tags disk
// @Security ApiKeyAuth
// @Param id path string true "id"
// @Success 200 {string} string "ok"
// @Router /disk/remove/{id} [delete]
func DeleteDisk(c *gin.Context) {
id := c.Param("id")
service.MyService.Disk().DeleteMount(id)
c.JSON(http.StatusOK, model.Result{Success: oasis_err.SUCCESS, Message: oasis_err.GetMsg(oasis_err.SUCCESS)})
}
// @Summary check mount point
// @Produce application/json
// @Accept application/json
// @Tags disk
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /disk/init [get]
func GetDiskCheck(c *gin.Context) {
dbList := service.MyService.Disk().GetSerialAll()
list := service.MyService.Disk().LSBLK()
mapList := make(map[string]string)
for _, v := range list {
mapList[v.Serial] = "1"
}
for _, v := range dbList {
if _, ok := mapList[v.Serial]; !ok {
//disk undefind
c.JSON(http.StatusOK, model.Result{Success: oasis_err.ERROR, Message: oasis_err.GetMsg(oasis_err.ERROR), Data: "disk undefind"})
return
}
}
c.JSON(http.StatusOK, model.Result{Success: oasis_err.SUCCESS, Message: oasis_err.GetMsg(oasis_err.SUCCESS)})
}

View file

@ -218,14 +218,24 @@ func InstallApp(c *gin.Context) {
}
}
if m.Origin == "custom" {
for _, device := range m.Devices {
if file.CheckNotExist(device.Path) {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.DEVICE_NOT_EXIST, Message: device.Path + "," + oasis_err2.GetMsg(oasis_err2.DEVICE_NOT_EXIST)})
return
}
for _, device := range m.Devices {
if file.CheckNotExist(device.Path) {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.DEVICE_NOT_EXIST, Message: device.Path + "," + oasis_err2.GetMsg(oasis_err2.DEVICE_NOT_EXIST)})
return
}
} else {
dev := []model.PathMap{}
for _, device := range dev {
if !file.CheckNotExist(device.Path) {
dev = append(dev, device)
}
}
m.Devices = dev
}
//restart := c.PostForm("restart") //always 总是重启, unless-stopped 除非用户手动停止容器,否则总是重新启动, on-failure:仅当容器退出代码非零时重新启动
//if len(restart) > 0 {
//
@ -421,11 +431,11 @@ func InstallApp(c *gin.Context) {
rely := model.MapStrings{}
copier.Copy(&rely, &relyMap)
if m.Origin != "custom" {
for i := 0; i < len(m.Volumes); i++ {
m.Volumes[i].Path = docker.GetDir(id, m.Volumes[i].ContainerPath)
}
}
// if m.Origin != "custom" {
// for i := 0; i < len(m.Volumes); i++ {
// m.Volumes[i].Path = docker.GetDir(id, m.Volumes[i].Path)
// }
// }
portsStr, _ := json2.Marshal(m.Ports)
envsStr, _ := json2.Marshal(m.Envs)

View file

@ -9,6 +9,8 @@ import (
"net/http"
"os"
"path"
"path/filepath"
"time"
"github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/pkg/utils/file"
@ -256,3 +258,64 @@ func PostFileUpload(c *gin.Context) {
io.Copy(out, file)
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
}
func PutFileMove(c *gin.Context) {
from := "/Users/liangjianli/go/CasaOS"
to := "/Users/liangjianli/go/CasaOS/test"
//t := 1 //是否覆盖
//方法体
stopCh := make(chan int)
f, err := os.Stat(from)
if err != nil {
//未拿到文件信息
fmt.Println("stat", err)
}
//未创建新的文件夹
if f.IsDir() {
//from 是文件夹,定义to也是文件夹
if list, err := ioutil.ReadDir(from); err == nil {
for _, v := range list {
time.Sleep(time.Second)
if err = Copy(stopCh, filepath.Join(from, v.Name()), filepath.Join(to, v.Name())); err != nil {
fmt.Printf("copy %s ,err %d", v.Name(), err)
}
}
}
} else {
p := filepath.Dir(to)
if _, err = os.Stat(p); err != nil {
if err = os.MkdirAll(p, 0777); err != nil {
fmt.Println("mkdir", err)
}
}
}
file, err := os.Open(from)
if err != nil {
fmt.Println("open file error ", err)
}
defer file.Close()
out, err := os.Create(to)
if err != nil {
fmt.Println("create to file err", err)
}
defer out.Close()
io.Copy(out, file)
time.Sleep(time.Second * 4)
close(stopCh)
}
func Copy(stop chan int, from, to string) error {
for {
select {
case <-stop:
return nil
default:
fmt.Println(from)
}
}
return nil
}

View file

@ -6,11 +6,14 @@ import (
"net/http"
"os"
"strconv"
"strings"
"time"
"unsafe"
"github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/pkg/config"
"github.com/IceWhaleTech/CasaOS/pkg/utils/oasis_err"
oasis_err2 "github.com/IceWhaleTech/CasaOS/pkg/utils/oasis_err"
port2 "github.com/IceWhaleTech/CasaOS/pkg/utils/port"
"github.com/IceWhaleTech/CasaOS/pkg/utils/version"
"github.com/IceWhaleTech/CasaOS/service"
@ -127,8 +130,9 @@ func GetWidgetConfig(c *gin.Context) {
func PostSetWidgetConfig(c *gin.Context) {
buf := make([]byte, 1024)
n, _ := c.Request.Body.Read(buf)
fmt.Println("错误", strconv.Itoa(n))
service.MyService.System().UpSystemConfig("", string(buf[0:n]))
fmt.Println("错误1", string(buf[0:n]))
c.JSON(http.StatusOK,
model.Result{
Success: oasis_err.SUCCESS,
@ -221,3 +225,44 @@ func GetGuideCheck(c *gin.Context) {
func PostKillCasaOS(c *gin.Context) {
os.Exit(0)
}
// @Summary system info
// @Produce application/json
// @Accept application/json
// @Tags sys
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /sys/info [get]
func Info(c *gin.Context) {
var data = make(map[string]interface{}, 5)
list := service.MyService.Disk().LSBLK()
data["disk"] = list
cpu := service.MyService.ZiMa().GetCpuPercent()
num := service.MyService.ZiMa().GetCpuCoreNum()
cpuData := make(map[string]interface{})
cpuData["percent"] = cpu
cpuData["num"] = num
data["cpu"] = cpuData
data["mem"] = service.MyService.ZiMa().GetMemInfo()
//拼装网络信息
netList := service.MyService.ZiMa().GetNetInfo()
newNet := []model.IOCountersStat{}
nets := service.MyService.ZiMa().GetNet(true)
for _, n := range netList {
for _, netCardName := range nets {
if n.Name == netCardName {
item := *(*model.IOCountersStat)(unsafe.Pointer(&n))
item.State = strings.TrimSpace(service.MyService.ZiMa().GetNetState(n.Name))
item.DateTime = time.Now()
newNet = append(newNet, item)
break
}
}
}
data["net"] = newNet
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: data})
}

View file

@ -1,15 +1,15 @@
package v1
import (
"github.com/IceWhaleTech/CasaOS/model"
oasis_err2 "github.com/IceWhaleTech/CasaOS/pkg/utils/oasis_err"
"github.com/IceWhaleTech/CasaOS/service"
"github.com/gin-gonic/gin"
"github.com/shirou/gopsutil/v3/disk"
"net/http"
"strings"
"time"
"unsafe"
"github.com/IceWhaleTech/CasaOS/model"
oasis_err2 "github.com/IceWhaleTech/CasaOS/pkg/utils/oasis_err"
"github.com/IceWhaleTech/CasaOS/service"
"github.com/gin-gonic/gin"
)
// @Summary 获取cpu信息
@ -83,48 +83,6 @@ func NetInfo(c *gin.Context) {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: newNet})
}
// @Summary 获取信息
// @Produce application/json
// @Accept application/json
// @Tags zima
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /zima/getinfo [get]
func Info(c *gin.Context) {
var data = make(map[string]interface{}, 4)
var diskArr []*disk.UsageStat
diskArr = append(diskArr, service.MyService.ZiMa().GetDiskInfo())
data["disk"] = diskArr
cpu := service.MyService.ZiMa().GetCpuPercent()
num := service.MyService.ZiMa().GetCpuCoreNum()
cpuData := make(map[string]interface{})
cpuData["percent"] = cpu
cpuData["num"] = num
data["cpu"] = cpuData
data["mem"] = service.MyService.ZiMa().GetMemInfo()
//拼装网络信息
netList := service.MyService.ZiMa().GetNetInfo()
newNet := []model.IOCountersStat{}
nets := service.MyService.ZiMa().GetNet(true)
for _, n := range netList {
for _, netCardName := range nets {
if n.Name == netCardName {
item := *(*model.IOCountersStat)(unsafe.Pointer(&n))
item.State = strings.TrimSpace(service.MyService.ZiMa().GetNetState(n.Name))
item.DateTime = time.Now()
newNet = append(newNet, item)
break
}
}
}
data["net"] = newNet
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: data})
}
// @Summary 获取信息系统信息
// @Produce application/json
// @Accept application/json

View file

@ -46,7 +46,7 @@ type appStruct struct {
//获取我的应用列表
func (a *appStruct) GetMyList(index, size int, position bool) *[]model2.MyAppList {
//获取docker应用
cli, err := client2.NewClientWithOpts(client2.FromEnv)
cli, err := client2.NewClientWithOpts(client2.FromEnv, client2.WithTimeout(time.Second*5))
if err != nil {
a.log.Error("初始化client失败", "app.getmylist", "line:36", err)
}
@ -81,13 +81,13 @@ func (a *appStruct) GetMyList(index, size int, position bool) *[]model2.MyAppLis
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
}
// 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,
@ -95,9 +95,9 @@ func (a *appStruct) GetMyList(index, size int, position bool) *[]model2.MyAppLis
CustomId: strings.ReplaceAll(container.Names[0], "/", ""),
Port: m.PortMap,
Index: m.Index,
UpTime: tm,
Image: m.Image,
Slogan: m.Slogan,
//UpTime: tm,
Image: m.Image,
Slogan: m.Slogan,
//Rely: m.Rely,
})
}
@ -120,7 +120,7 @@ func (a *appStruct) GetSystemAppList() *[]model2.MyAppList {
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)
a.log.Error("获取docker容器失败", "app.sys", "line:123", err)
}
//获取本地数据库应用
@ -179,7 +179,7 @@ func (a *appStruct) GetContainerInfo(name string) (types.Container, error) {
filters.Add("name", name)
containers, err := cli.ContainerList(context.Background(), types.ContainerListOptions{All: true, Filters: filters})
if err != nil {
a.log.Error("获取docker容器失败", "app.getmylist", "line:42", err)
a.log.Error("获取docker容器失败", "app.getcontainerinfo", "line:182", err)
}
if len(containers) > 0 {

View file

@ -2,6 +2,7 @@ package service
import (
json2 "encoding/json"
"fmt"
"strconv"
"github.com/IceWhaleTech/CasaOS/model"
@ -12,7 +13,7 @@ import (
)
type CasaService interface {
GetServerList(index, size, tp, categoryId, key string) ([]model.ServerAppList, int64)
GetServerList(index, size, tp, categoryId, key string) (recommend, list, community []model.ServerAppList)
GetServerCategoryList() []model.ServerCategoryList
GetTaskList(size int) []model2.TaskDBModel
GetServerAppInfo(id string) model.ServerAppList
@ -44,20 +45,34 @@ func (o *casaService) GetTaskList(size int) []model2.TaskDBModel {
return list
}
func (o *casaService) GetServerList(index, size, tp, categoryId, key string) ([]model.ServerAppList, int64) {
func (o *casaService) GetServerList(index, size, tp, categoryId, key string) (recommend, list, community []model.ServerAppList) {
keyName := fmt.Sprintf("list_%s_%s_%s_%s", index, size, tp, categoryId)
if result, ok := Cache.Get(keyName); ok {
res, ok := result.(string)
if ok {
json2.Unmarshal([]byte(gjson.Get(res, "data.list").String()), &list)
json2.Unmarshal([]byte(gjson.Get(res, "data.recommend").String()), &recommend)
json2.Unmarshal([]byte(gjson.Get(res, "data.community").String()), &community)
return
}
}
head := make(map[string]string)
head["Authorization"] = GetToken()
listS := httper2.Get(config.ServerInfo.ServerApi+"/v1/app/list?index="+index+"&size="+size+"&type="+tp+"&category_id="+categoryId+"&key="+key, head)
listS := httper2.Get(config.ServerInfo.ServerApi+"/v2/app/newlist?index="+index+"&size="+size+"&rank="+tp+"&category_id="+categoryId+"&key="+key, head)
list := []model.ServerAppList{}
json2.Unmarshal([]byte(gjson.Get(listS, "data.list").String()), &list)
json2.Unmarshal([]byte(gjson.Get(listS, "data.recommend").String()), &recommend)
json2.Unmarshal([]byte(gjson.Get(listS, "data.community").String()), &community)
count := gjson.Get(listS, "data.count").Int()
json2.Unmarshal([]byte(gjson.Get(listS, "data.items").String()), &list)
return list, count
if len(list) > 0 {
Cache.SetDefault(keyName, listS)
}
return
}
func (o *casaService) GetServerCategoryList() []model.ServerCategoryList {
@ -65,7 +80,7 @@ func (o *casaService) GetServerCategoryList() []model.ServerCategoryList {
head := make(map[string]string)
head["Authorization"] = GetToken()
listS := httper2.Get(config.ServerInfo.ServerApi+"/v1/app/category", head)
listS := httper2.Get(config.ServerInfo.ServerApi+"/v2/app/category", head)
list := []model.ServerCategoryList{}
@ -79,7 +94,7 @@ func (o *casaService) GetServerAppInfo(id string) model.ServerAppList {
head["Authorization"] = GetToken()
infoS := httper2.Get(config.ServerInfo.ServerApi+"/v1/app/info/"+id, head)
infoS := httper2.Get(config.ServerInfo.ServerApi+"/v2/app/info/"+id, head)
info := model.ServerAppList{}
json2.Unmarshal([]byte(gjson.Get(infoS, "data").String()), &info)

View file

@ -5,6 +5,7 @@ import (
"fmt"
"strconv"
"strings"
"time"
"github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/pkg/config"
@ -26,7 +27,10 @@ type DiskService interface {
AddPartition(path string) string
GetDiskInfoByPath(path string) *disk.UsageStat
MountDisk(path, volume string)
SerialAll(mountPoint string) *[]model2.SerialDisk
GetSerialAll() []model2.SerialDisk
SaveMountPoint(m model2.SerialDisk)
DeleteMountPoint(path, mountPoint string)
DeleteMount(id string)
}
type diskService struct {
log loger2.OLog
@ -86,9 +90,21 @@ func (d *diskService) GetDiskInfoByPath(path string) *disk.UsageStat {
//get disk details
func (d *diskService) LSBLK() []model.LSBLKModel {
key := "system_lsblk"
var n []model.LSBLKModel
if result, ok := Cache.Get(key); ok {
res, ok := result.([]model.LSBLKModel)
if ok {
return res
}
}
str := command2.ExecLSBLK()
if str == nil {
d.log.Error("lsblk exec error")
d.log.Error("lsblk exec error,lsblk")
return nil
}
var m []model.LSBLKModel
@ -97,8 +113,6 @@ func (d *diskService) LSBLK() []model.LSBLKModel {
d.log.Error("json ummarshal error", err)
}
var n []model.LSBLKModel
var c []model.LSBLKModel
var fsused uint64
@ -136,13 +150,16 @@ func (d *diskService) LSBLK() []model.LSBLKModel {
fsused = 0
}
}
if len(n) > 0 {
Cache.Add(key, n, time.Second*10)
}
return n
}
func (d *diskService) GetDiskInfo(path string) model.LSBLKModel {
str := command2.ExecLSBLKByPath(path)
if str == nil {
d.log.Error("lsblk exec error")
d.log.Error("lsblk exec error,str")
return model.LSBLKModel{}
}
var ml []model.LSBLKModel
@ -197,13 +214,25 @@ func (d *diskService) MountDisk(path, volume string) {
}
func (d *diskService) SaveMountPoint(m model2.SerialDisk) {
d.db.Save(&m)
d.db.Create(&m)
}
func (d *diskService) SerialAll(mountPoint string) *[]model2.SerialDisk {
func (d *diskService) DeleteMount(id string) {
d.db.Delete(&model2.SerialDisk{}).Where("id = ?", id)
}
func (d *diskService) DeleteMountPoint(path, mountPoint string) {
d.db.Delete(&model2.SerialDisk{}).Where("path= ? && mount_point = ?", path, mountPoint)
command2.OnlyExec("source " + config.AppInfo.ProjectPath + "/shell/helper.sh ;do_umount " + path)
}
func (d *diskService) GetSerialAll() []model2.SerialDisk {
var m []model2.SerialDisk
d.db.Find(&m)
return &m
return m
}
func NewDiskService(log loger2.OLog, db *gorm.DB) DiskService {

View file

@ -8,7 +8,6 @@ import (
json2 "encoding/json"
"fmt"
"reflect"
"regexp"
"syscall"
model2 "github.com/IceWhaleTech/CasaOS/service/model"
@ -366,11 +365,11 @@ func (ds *dockerService) DockerContainerCreate(imageName string, containerDbId s
// if net != "host" {
// portMaps[nat.Port(fmt.Sprint(m.Port)+"/tcp")] = []nat.PortBinding{{HostIP: "", HostPort: m.PortMap}}
// }
port := ""
//port := ""
for _, portMap := range m.Ports {
if portMap.CommendPort == m.PortMap && portMap.Protocol == "tcp" || portMap.Protocol == "both" {
port = portMap.ContainerPort
}
// if portMap.CommendPort == m.PortMap && portMap.Protocol == "tcp" || portMap.Protocol == "both" {
// port = portMap.ContainerPort
// }
if portMap.Protocol == "tcp" {
tContainer, _ := strconv.Atoi(portMap.ContainerPort)
@ -413,7 +412,7 @@ func (ds *dockerService) DockerContainerCreate(imageName string, containerDbId s
var envArr []string
for _, e := range m.Envs {
if strings.HasPrefix(e.Value, "$") {
envArr = append(envArr, e.Name+"="+env_helper.ReplaceDefaultENV(e.Value))
envArr = append(envArr, e.Name+"="+env_helper.ReplaceDefaultENV(e.Value, MyService.System().GetTimeZone()))
continue
}
if len(e.Value) > 0 {
@ -443,27 +442,28 @@ func (ds *dockerService) DockerContainerCreate(imageName string, containerDbId s
for _, v := range m.Volumes {
path := v.Path
if len(path) == 0 {
path = docker.GetDir(containerDbId, v.ContainerPath)
path = docker.GetDir(containerDbId, v.Path)
if len(path) == 0 {
continue
}
}
path = strings.ReplaceAll(path, "$AppID", containerDbId)
reg1 := regexp.MustCompile(`([^<>/\\\|:""\*\?]+\.\w+$)`)
result1 := reg1.FindAllStringSubmatch(path, -1)
if len(result1) == 0 {
err = file.IsNotExistMkDir(path)
if err != nil {
ds.log.Error("mkdir error", err)
continue
}
} else {
err = file.IsNotExistCreateFile(path)
if err != nil {
ds.log.Error("mkdir error", err)
continue
}
//reg1 := regexp.MustCompile(`([^<>/\\\|:""\*\?]+\.\w+$)`)
//result1 := reg1.FindAllStringSubmatch(path, -1)
//if len(result1) == 0 {
err = file.IsNotExistMkDir(path)
if err != nil {
ds.log.Error("mkdir error", err)
continue
}
//}
// else {
// err = file.IsNotExistCreateFile(path)
// if err != nil {
// ds.log.Error("mkdir error", err)
// continue
// }
// }
volumes = append(volumes, mount.Mount{
Type: mount.TypeBind,
@ -479,17 +479,17 @@ func (ds *dockerService) DockerContainerCreate(imageName string, containerDbId s
if len(m.Restart) > 0 {
rp.Name = m.Restart
}
healthTest := []string{}
if len(port) > 0 {
healthTest = []string{"CMD-SHELL", "curl -f http://localhost:" + port + m.Index + " || exit 1"}
}
// healthTest := []string{}
// if len(port) > 0 {
// healthTest = []string{"CMD-SHELL", "curl -f http://localhost:" + port + m.Index + " || exit 1"}
// }
health := &container.HealthConfig{
Test: healthTest,
StartPeriod: 0,
Retries: 1000,
}
fmt.Print(health)
// health := &container.HealthConfig{
// Test: healthTest,
// StartPeriod: 0,
// Retries: 1000,
// }
// fmt.Print(health)
config := &container.Config{
Image: imageName,
Labels: map[string]string{"origin": m.Origin, m.Origin: m.Origin},

85
service/file.go Normal file
View file

@ -0,0 +1,85 @@
package service
import (
"context"
"io"
)
// type InteruptReader struct {
// r io.Reader
// interupt chan int
// }
// func NewInteruptReader(r io.Reader) InteruptReader {
// return InteruptReader{
// r,
// make(chan int),
// }
// }
// func (r InteruptReader) Read(p []byte) (n int, err error) {
// if r.r == nil {
// return 0, io.EOF
// }
// select {
// case <-r.interupt:
// return r.r.Read(p)
// default:
// r.r = nil
// return 0, io.EOF
// }
// }
// func (r InteruptReader) Cancel() {
// r.interupt <- 0
// }
type reader struct {
ctx context.Context
r io.Reader
}
// NewReader wraps an io.Reader to handle context cancellation.
//
// Context state is checked BEFORE every Read.
func NewReader(ctx context.Context, r io.Reader) io.Reader {
if r, ok := r.(*reader); ok && ctx == r.ctx {
return r
}
return &reader{ctx: ctx, r: r}
}
func (r *reader) Read(p []byte) (n int, err error) {
select {
case <-r.ctx.Done():
return 0, r.ctx.Err()
default:
return r.r.Read(p)
}
}
type writer struct {
ctx context.Context
w io.Writer
}
type copier struct {
writer
}
func NewWriter(ctx context.Context, w io.Writer) io.Writer {
if w, ok := w.(*copier); ok && ctx == w.ctx {
return w
}
return &copier{writer{ctx: ctx, w: w}}
}
// Write implements io.Writer, but with context awareness.
func (w *writer) Write(p []byte) (n int, err error) {
select {
case <-w.ctx.Done():
return 0, w.ctx.Err()
default:
return w.w.Write(p)
}
}

81
service/file_test.go Normal file
View file

@ -0,0 +1,81 @@
package service
import (
"context"
"fmt"
"io"
"log"
"os"
"testing"
"time"
)
var ctx context.Context
var cancel context.CancelFunc
func TestNewInteruptReader(t *testing.T) {
ctx, cancel = context.WithCancel(context.Background())
go func() {
// 在初始上下文的基础上创建一个有取消功能的上下文
// ctx, cancel := context.WithCancel(ctx)
fmt.Println("开始")
fIn, err := os.Open("/Users/liangjianli/Downloads/demo_data.tar.gz")
if err != nil {
}
defer fIn.Close()
fmt.Println("创建新文件")
fOut, err := os.Create("/Users/liangjianli/Downloads/demo_data1.tar.gz")
if err != nil {
fmt.Println(err)
}
defer fOut.Close()
fmt.Println("准备复制")
// _, err = io.Copy(out, NewReader(ctx, f))
// time.Sleep(time.Second * 2)
//ctx.Done()
// cancel()
// interrupt context after 500ms
// interrupt context with SIGTERM (CTRL+C)
//sigs := make(chan os.Signal, 1)
//signal.Notify(sigs, os.Interrupt)
if err != nil {
log.Fatal(err)
}
// Reader that fails when context is canceled
in := NewReader(ctx, fIn)
// Writer that fails when context is canceled
out := NewWriter(ctx, fOut)
//time.Sleep(2 * time.Second)
//cancel()
n, err := io.Copy(out, in)
log.Println(n, "bytes copied.")
if err != nil {
fmt.Println("Err:", err)
}
fmt.Println("Closing.")
}()
go func() {
//<-sigs
time.Sleep(time.Second)
fmt.Println("退出")
ddd()
}()
time.Sleep(time.Second * 10)
}
func ddd() {
cancel()
}

View file

@ -3,7 +3,7 @@ package model
//SerialAdvanced Technology Attachment (STAT)
type SerialDisk struct {
Id uint `gorm:"column:id;primary_key" json:"id"`
DiskId string `json:"disk_id"`
Serial string `json:"serial"`
Path string `json:"path"`
State int `json:"state"`
MountPoint string `json:"mount_point"`

View file

@ -16,6 +16,7 @@ type SystemService interface {
GetCasaOSLogs(lineNumber int) string
UpdateAssist()
UpSystemPort(port string)
GetTimeZone() string
}
type systemService struct {
log loger.OLog
@ -30,6 +31,11 @@ func (s *systemService) UpdateSystemVersion(version string) {
func (s *systemService) UpdateAssist() {
s.log.Error(command2.ExecResultStrArray("source " + config.AppInfo.ProjectPath + "/shell/assist.sh"))
}
func (s *systemService) GetTimeZone() string {
return command2.ExecResultStr("source " + config.AppInfo.ProjectPath + "/shell/helper.sh ;GetTimeZone")
}
func (s *systemService) GetSystemConfigDebug() []string {
return command2.ExecResultStrArray("source " + config.AppInfo.ProjectPath + "/shell/helper.sh ;GetSysInfo")
}

View file

@ -30,6 +30,11 @@ GetNetCard() {
fi
}
GetTimeZone(){
timedatectl | grep "Time zone" | awk '{print $3}'
}
#查看网卡状态
#param 网卡名称
CatNetCardState() {
@ -194,7 +199,7 @@ do_mount() {
eval $(blkid -o udev ${DEVICE} | grep -i -e "ID_FS_LABEL" -e "ID_FS_TYPE")
LABEL=$2
if grep -q " /media/${LABEL} " /etc/mtab; then
if grep -q " ${LABEL} " /etc/mtab; then
# Already in use, make a unique one
LABEL+="-${DEVBASE}"
fi
@ -205,7 +210,7 @@ do_mount() {
DEV_LABEL="${DEVBASE}"
fi
MOUNT_POINT="/media/${DEV_LABEL}"
MOUNT_POINT="${DEV_LABEL}"
${log} "Mount point: ${MOUNT_POINT}"
@ -233,3 +238,94 @@ do_mount() {
;;
esac
}
# $1=sda1
do_umount() {
log="logger -t usb-mount.sh -s "
DEVBASE=$1
DEVICE="${DEVBASE}"
MOUNT_POINT=$(mount | grep ${DEVICE} | awk '{ print $3 }')
if [[ -z ${MOUNT_POINT} ]]; then
${log} "Warning: ${DEVICE} is not mounted"
else
umount -l ${DEVICE}
${log} "Unmounted ${DEVICE} from ${MOUNT_POINT}"
/bin/rmdir "${MOUNT_POINT}"
sed -i.bak "\@${MOUNT_POINT}@d" /var/log/usb-mount.track
fi
}
# $1=/mnt/volume1/data.img
# $2=100G
PackageDocker() {
image=$1
docker="/mnt/casa_docker"
#判断目录docker存在不存在则创建,存在检查是否为空
if [ ! -d "$docker" ]; then
mkdir ${docker}
fi
if [ "$(ls -A $docker)" = "" ]; then
echo "$docker count is 0"
else
mkdir ${docker}_bak
mv -r ${docker} ${docker}_bak
fi
daemon="/etc/docker/daemon.json"
#1创建img文件在挂载的目录
fallocate -l $2 $image
#2初始化img文件
mkfs -t ext4 $image
#3挂载img文件
sudo mount -o loop $image $docker
#4给移动/var/lib/docker数据到img挂载的目录
systemctl stop docker.socket
systemctl stop docker
cp -r /var/lib/docker/* ${docker}/
#5在/etc/docker写入daemon.json(需要检查)
if [ -d "$daemon" ]; then
mv -r $daemon ${daemon}.bak
fi
echo "{\"data-root\": \"$docker\"}" >$daemon
#删除老数据腾出空间
#rm -fr /var/lib/docker
systemctl start docker.socket
systemctl start docker
}
DockerImgMove() {
image=$1
systemctl stop docker.socket
systemctl stop docker
sudo umount -f $image
}
GetDockerDataRoot() {
docker info | grep "Docker Root Dir:"
}
SetLink() {
ln -s /mnt/casa_sda1/AppData /DATA/AppData
#删除所有软链
find /DATA -type l -delete
}
#压缩文件夹
TarFolder() {
#压缩
tar -zcvf data.tar.gz -C/DATA/ AppDataBak/
#解压
tar zxvf data.tar.gz
#查看某文件夹下的所有包括子文件夹文件
ls /DATA/Media -lR | grep "^-" | wc -l
# ls -lR|grep "^d"| wc -l 查看某个文件夹下文件夹的个数,包括子文件夹下的文件夹个数。
#查看固定文件夹大小
du -sh /DATA
}

View file

@ -1,4 +1,4 @@
package types
const CURRENTVERSION = "0.2.3"
const BODY = "<li>Add detailed CPU and memory statistics.</li><li>Add the multi-language function and add Chinese translation.</li><li>Add the function to modify the search engine.</li><li>Add the function of modifying the WebUI port</li><li>fixed some bugs</li><li>Preprocessing usb automounting</li><li>Update update script</li>"
const CURRENTVERSION = "0.2.4"
const BODY = "<li>New App Store</li><li>Fix minor bugs</li>"