Compare commits

...

58 Commits

Author SHA1 Message Date
老竭力
601e7ce10b Update casa.yml 2022-01-04 14:51:09 +08:00
老竭力
4a6fc9a945 Update system.go 2022-01-04 14:47:46 +08:00
老竭力
b377af1d24 Update casa.yml 2021-12-30 19:17:11 +08:00
老竭力
3fc00f8da7 Update casa.yml 2021-12-30 19:13:40 +08:00
老竭力
6e39fe5f8c Update casa.yml 2021-12-30 19:04:37 +08:00
老竭力
20c240a123 Update casa.yml 2021-12-30 19:00:11 +08:00
老竭力
3ea9fc0de0 Update casa.yml 2021-12-30 18:52:37 +08:00
老竭力
b80b08ef07 Update casa.yml 2021-12-30 18:50:45 +08:00
老竭力
505af8d101 Update casa.yml 2021-12-30 18:46:17 +08:00
老竭力
ae35a6d291 Update casa.yml 2021-12-30 18:44:07 +08:00
John Guan
2b95c07a47 Update casa.yml 2021-12-30 18:16:19 +08:00
John Guan
27a011e715 Update casa.yml 2021-12-30 18:01:01 +08:00
link
476831a12f Merge pull request #65 from IceWhaleTech/dev
update action system
2021-12-30 16:59:05 +08:00
link
9675eff69e update action system 2021-12-30 16:58:24 +08:00
link
85a044246e Merge pull request #64 from IceWhaleTech/dev
update action system
2021-12-30 16:53:32 +08:00
link
5c41fbcf3d update action system 2021-12-30 16:52:58 +08:00
link
6baab7a525 Merge pull request #63 from IceWhaleTech/dev
update action system
2021-12-30 16:42:39 +08:00
link
ab3b5a9077 update action system 2021-12-30 16:41:16 +08:00
link
5811c271b2 Merge pull request #62 from IceWhaleTech/dev
update web
2021-12-30 16:20:50 +08:00
link
cf5387346d update web 2021-12-30 16:20:30 +08:00
link
8df63c6c5c Merge pull request #60 from IceWhaleTech/dev
New App Store
2021-12-29 16:42:56 +08:00
link
1d17d27c96 New App Store 2021-12-29 16:42:20 +08:00
John Guan
bdcbae69a5 Update README.md 2021-12-14 11:41:42 +08:00
John Guan
cdbc3437be Update README.md 2021-12-14 10:40:17 +08:00
link
8940b520e0 Merge pull request #53 from IceWhaleTech/dev
update ui
2021-12-11 15:44:55 +08:00
link
1d62fbd670 update ui 2021-12-11 15:44:17 +08:00
link
a0b56d809e Merge pull request #52 from IceWhaleTech/dev
update cpu changes when the application is removed
2021-12-10 18:08:49 +08:00
link
f1f6d33e26 update cpu changes when the application is removed 2021-12-10 18:08:02 +08:00
link
f292edd2ba Merge pull request #51 from IceWhaleTech/dev
update app.go
2021-12-10 17:14:46 +08:00
link
caa9b50b65 update app.go 2021-12-10 17:14:01 +08:00
link
61b824065b Merge pull request #50 from IceWhaleTech/dev
update stats steam
2021-12-10 16:34:29 +08:00
link
9a900b2ca0 update stats steam 2021-12-10 16:33:39 +08:00
link
4c1cbc98a4 Merge pull request #49 from IceWhaleTech/dev
fixed bug
2021-12-10 14:18:01 +08:00
link
8660b95756 fixed bug 2021-12-10 14:17:36 +08:00
link
e7ebdc040f Merge pull request #48 from IceWhaleTech/dev
feat: Multiple updates
2021-12-09 19:03:45 +08:00
link
6c235d3f2a feat: Multiple updates
1.Add the function of modifying the WebUI port.
2.Add the function to modify the search engine.
3.Add the multi-language function and add Chinese translation.
4.Add detailed CPU and memory statistics.
2021-12-09 19:02:41 +08:00
link
997d912f4d Merge branch 'main' into dev 2021-12-06 17:12:53 +08:00
link
2508a4e07d Pull Submit 2021-12-06 17:08:36 +08:00
link
90b997337c Merge pull request #42 from xuchaoa/main
format code and fix wrong spell
2021-12-06 03:07:48 -06:00
xuc2
0bb3c92335 format code and fix wrong spell 2021-12-03 16:48:07 +08:00
link
7fd539c57e Merge pull request #40 from IceWhaleTech/dev
update volume path
2021-12-01 20:49:48 -06:00
link
52bd22ab2b update volume path 2021-12-02 10:49:02 +08:00
John Guan
46e9458e82 Update demo.yml 2021-12-01 21:58:57 +08:00
John Guan
3f5595e794 Update demo.yml 2021-12-01 21:54:02 +08:00
John Guan
d22cc7d3f6 Update demo.yml 2021-12-01 21:49:23 +08:00
John Guan
eb03a3e6c7 Update demo.yml 2021-12-01 21:46:34 +08:00
John Guan
65cc1f4752 Update demo.yml 2021-12-01 21:36:44 +08:00
John Guan
99db197a39 Update demo.yml 2021-12-01 21:31:17 +08:00
John Guan
7e2a5d553c Update demo.yml 2021-12-01 21:14:34 +08:00
John Guan
f02337b83b Update demo.yml 2021-12-01 21:09:18 +08:00
John Guan
f66b67b0c9 Update demo.yml 2021-12-01 21:05:52 +08:00
John Guan
91087b5341 Update demo.yml 2021-12-01 21:01:40 +08:00
John Guan
c67191d8c2 Update demo.yml 2021-12-01 20:45:36 +08:00
John Guan
0fac461783 Update demo.yml 2021-12-01 20:11:32 +08:00
John Guan
ec5bc922e7 Update demo.yml 2021-12-01 20:07:44 +08:00
John Guan
7194792c3f Update demo.yml 2021-12-01 19:51:36 +08:00
John Guan
76c8bade7f Update demo.yml 2021-12-01 19:50:30 +08:00
John Guan
126857fab0 Create demo.yml 2021-12-01 19:43:50 +08:00
68 changed files with 2606 additions and 761 deletions

View File

@@ -63,7 +63,17 @@ jobs:
# git clone $REPO_URL -b $REPO_BRANCH --recursive casa
# ln -sf /workdir/casa $GITHUB_WORKSPACE/casa
# ls
- name: Compile glibc
run: |
wget -q http://ftp.gnu.org/gnu/glibc/glibc-2.30.tar.bz2
sudo mkdir /opt/glibc230
tar xvfj glibc-2.30.tar.bz2
mkdir build
cd build
../glibc-2.30/configure --prefix=/opt/glibc230 --enable-cet
sudo make -j$(($(nproc) + 1))
sudo make install
- name: Set enviroment for github-release
run: |
@@ -76,7 +86,7 @@ jobs:
uses: actions/setup-node@v2
with:
node-version: '14'
- name: Build frontend with nodejs and yarn
run: |
cd UI
@@ -98,7 +108,7 @@ jobs:
v: true
x: false
race: false
ldflags: -s -w
ldflags: -s -w -L /opt/glibc230/lib -extldflags "-static"
buildmode: default
#
# - name: List Files

79
.github/workflows/demo.yml vendored Normal file
View File

@@ -0,0 +1,79 @@
name: Demo Reset
# Controls when the workflow will run
on:
schedule:
- cron: "0 * * * *"
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# OLD_INSTANCE_SNAPSHOT_NAME=$(aws lightsail get-instance-snapshots | grep '"name": "CasaOS-Demo-Snapshot-[0-9]' | sed 's/ //g' | sed 's/"//g' | sed 's/,//g' | sed 's/name://g')
# OLD_INSTANCE_NAME=$(aws lightsail get-instances | grep '"name": "CasaOS-Demo-[0-9]' | sed 's/ //g' | sed 's/"//g' | sed 's/,//g' | sed 's/name://g')
# NEW_INSTANCE_NAME=CasaOS-Demo-$(date +%s)
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "build"
reset:
# The type of runner that the job will run on
runs-on: ubuntu-latest
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v2
- name: Configure AWS credentials from Test account
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-west-2
- name: Get old instance and snapshot name, create new instance name
run: |
echo "OLD_INSTANCE_SNAPSHOT_NAME=$(aws lightsail get-instance-snapshots | grep '"name": "CasaOS-Demo-Snapshot-[0-9]' | sed 's/ //g' | sed 's/"//g' | sed 's/,//g' | sed 's/name://g')" >> $GITHUB_ENV
echo "OLD_INSTANCE_NAME=$(aws lightsail get-instances | grep '"name": "CasaOS-Demo-[0-9]' | sed 's/ //g' | sed 's/"//g' | sed 's/,//g' | sed 's/name://g')" >> $GITHUB_ENV
echo "NEW_INSTANCE_NAME=CasaOS-Demo-$(date +%s)" >> $GITHUB_ENV
- name: Create instances from snapshot
run: |
aws lightsail create-instances-from-snapshot \
--instance-snapshot-name ${{ env.OLD_INSTANCE_SNAPSHOT_NAME }} \
--instance-names ${{ env.NEW_INSTANCE_NAME }} \
--availability-zone us-west-2a \
--bundle-id large_2_0
- name: Wait for new instance running
run: |
TIMEOUT=$(($(date +%s)+600))
while [ $TIMEOUT -gt $(date +%s) ]
do
NEW_INSTANCE_STATE=$(aws lightsail get-instance-state --instance-name ${{ env.NEW_INSTANCE_NAME }} | grep '"name":' | sed 's/ //g' | sed 's/"//g' | sed 's/name://g')
if [ $NEW_INSTANCE_STATE == running ]
then
echo "New instance is running now"
sleep 10s
break
fi
done
- name: Put instance public ports
run: |
aws lightsail put-instance-public-ports \
--port-infos fromPort=0,toPort=65535,protocol=all \
--instance-name ${{ env.NEW_INSTANCE_NAME }}
- name: Attach static ip
run: |
aws lightsail attach-static-ip \
--static-ip-name CasaOS-Demo-IP \
--instance-name ${{ env.NEW_INSTANCE_NAME }}
- name: Delete old instance
run: |
aws lightsail delete-instance \
--instance-name ${{ env.OLD_INSTANCE_NAME }}

View File

@@ -6,7 +6,7 @@
[![Pull Requests](https://img.shields.io/github/issues-pr/IceWhaleTech/CasaOS?color=162453&style=flat-square)](https://github.com/IceWhaleTech/CasaOS/pulls)
[![Issues](https://img.shields.io/github/issues/IceWhaleTech/CasaOS?color=162453&style=flat-square)](https://github.com/IceWhaleTech/CasaOS/issues)
[![GitHub Stars](https://img.shields.io/github/stars/IceWhaleTech/CasaOS?color=162453&logo=github&style=flat-square)](https://github.com/IceWhaleTech/CasaOS/stargazers)
[![Discord](https://img.shields.io/discord/884667213326463016?color=162453&label=Chat&logo=discord&logoColor=fff&style=flat-square)](https://discord.gg/Gx4BCEtHjx)
[![Discord](https://img.shields.io/discord/884667213326463016?color=162453&label=Chat&logo=discord&logoColor=fff&style=flat-square)](https://discord.gg/knqAbbBbeX)
CasaOS is an open-source Home Cloud system based on the Docker ecosystem and designed for home scenarios. It is committed to building the world's most simple, easy-to-use, and elegant Home Cloud system.
@@ -19,7 +19,7 @@ IceWhale team believes that through community-driven collaborative innovation an
> ⚠️ Note:
>
> CasaOS is still in the early development stage and may vary significantly with the final release. Feel free to test run and share your feedback in the [Discord server](https://discord.gg/Gx4BCEtHjx)!
> CasaOS is still in the early development stage and may vary significantly with the final release. Feel free to test run and share your feedback in the [Discord server](https://discord.gg/knqAbbBbeX)!
### Quick Setup CasaOS
@@ -71,9 +71,9 @@ After looking at many systems and software on the market, the team found no serv
So, we set out to build this open source project to develop CasaOS with our own hands, everyone in the community, and you.
> A warm welcome for you to share and discuss your great ideas in the [Discord server](https://discord.gg/Gx4BCEtHjx)!
> A warm welcome for you to share and discuss your great ideas in the [Discord server](https://discord.gg/knqAbbBbeX)!
[![Discord Card](https://discordapp.com/api/guilds/884667213326463016/widget.png?style=banner2)](https://discord.gg/Gx4BCEtHjx)
[![Discord Card](https://discordapp.com/api/guilds/884667213326463016/widget.png?style=banner2)](https://discord.gg/knqAbbBbeX)

2
UI

Submodule UI updated: f7c46d7379...f3088b6354

4
go.mod
View File

@@ -48,8 +48,8 @@ require (
github.com/smartystreets/goconvey v1.6.4 // indirect
github.com/swaggo/gin-swagger v1.3.0
github.com/swaggo/swag v1.7.3
github.com/tidwall/gjson v1.8.0
github.com/tidwall/pretty v1.2.0 // indirect
github.com/tidwall/gjson v1.10.2
github.com/tidwall/sjson v1.2.3
github.com/tklauser/go-sysconf v0.3.6 // indirect
github.com/ugorji/go v1.2.6 // indirect
go.opencensus.io v0.23.0 // indirect

11
go.sum
View File

@@ -741,13 +741,14 @@ github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I=
github.com/tidwall/gjson v1.8.0 h1:Qt+orfosKn0rbNTZqHYDqBrmm3UDA4KRkv70fDzG+PQ=
github.com/tidwall/gjson v1.8.0/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk=
github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE=
github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.1.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tidwall/gjson v1.10.2 h1:APbLGOM0rrEkd8WBw9C24nllro4ajFuJu0Sc9hRz8Bo=
github.com/tidwall/gjson v1.10.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/sjson v1.2.3 h1:5+deguEhHSEjmuICXZ21uSSsXotWMA0orU783+Z7Cp8=
github.com/tidwall/sjson v1.2.3/go.mod h1:5WdjKx3AQMvCJ4RG6/2UYT7dLrGvJUV1x4jdTAyGvZs=
github.com/tklauser/go-sysconf v0.3.4/go.mod h1:Cl2c8ZRWfHD5IrfHo9VN+FX9kCFjIOyVklgXycLB6ek=
github.com/tklauser/go-sysconf v0.3.6 h1:oc1sJWvKkmvIxhDHeKWvZS4f6AW+YcoguSfRF2/Hmo4=
github.com/tklauser/go-sysconf v0.3.6/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI=

View File

@@ -2,8 +2,9 @@ package middleware
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
"github.com/gin-gonic/gin"
)
func Cors() gin.HandlerFunc {
@@ -17,7 +18,7 @@ func Cors() gin.HandlerFunc {
//服务器支持的所有跨域请求的方法
c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE,UPDATE")
//允许跨域设置可以返回其他子段,可以自定义字段
c.Header("Access-Control-Allow-Headers", "Authorization, Content-Length, X-CSRF-Token, Token,session")
c.Header("Access-Control-Allow-Headers", "Authorization, Content-Length, X-CSRF-Token, Token,session,Language")
// 允许浏览器(客户端)可以解析的头部 (重要)
c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers")
//设置缓存时间

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

@@ -24,6 +24,7 @@ type LSBLKModel struct {
Tran string `json:"tran"`
MinIO uint64 `json:"min-io"`
UsedPercent float64 `json:"used_percent"`
Serial string `json:"serial"`
Children []LSBLKModel `json:"children"`
//详情特有
StartSector uint64 `json:"start_sector,omitempty"`

8
model/docker.go Normal file
View File

@@ -0,0 +1,8 @@
package model
type DockerStatsModel struct {
Icon string `json:"icon"`
Title string `json:"title"`
Data interface{} `json:"data"`
Pre interface{} `json:"pre"`
}

View File

@@ -68,3 +68,7 @@ type SystemConfig struct {
SyncPort string `json:"sync_port"`
SyncKey string `json:"sync_key"`
}
type CasaOSGlobalVariables struct {
AppChange bool
}

View File

@@ -1,3 +1 @@
package model

View File

@@ -33,6 +33,8 @@ var ServerInfo = &model.ServerModel{}
var SystemConfigInfo = &model.SystemConfig{}
var CasaOSGlobalVariables = &model.CasaOSGlobalVariables{}
var Cfg *ini.File
//初始化设置,获取系统的部分信息。

View File

@@ -3,31 +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/"
default:
//path = "/media"
}
return path
return envName
}

View File

@@ -6,7 +6,6 @@ import (
"golang.org/x/oauth2"
)
func GetGithubClient() *github.Client {
ctx := context.Background()
ts := oauth2.StaticTokenSource(

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

@@ -7,7 +7,7 @@ import (
"strings"
)
func GetCtrlUrl(host,device string) string {
func GetCtrlUrl(host, device string) string {
request := ctrlUrlRequest(host, device)
response, _ := http.DefaultClient.Do(request)
resultBody, _ := ioutil.ReadAll(response.Body)
@@ -86,4 +86,4 @@ func resolve(resultStr string) string {
}
}
return controlURL
}
}

View File

@@ -22,21 +22,21 @@ func send() (string, error) {
"ST: urn:schemas-upnp-org:service:WANIPConnection:1\r\n" +
"MAN: \"ssdp:discover\"\r\n" + "MX: 3\r\n\r\n"
var conn *net.UDPConn
remotAddr, err := net.ResolveUDPAddr("udp", "239.255.255.250:1900")
remoteAddr, err := net.ResolveUDPAddr("udp", "239.255.255.250:1900")
if err != nil {
return "", errors.New("组播地址格式不正确")
}
locaAddr, err := net.ResolveUDPAddr("udp", ip_helper2.GetLoclIp()+":")
localAddr, err := net.ResolveUDPAddr("udp", ip_helper2.GetLoclIp()+":")
if err != nil {
return "", errors.New("本地ip地址格式不正确")
}
conn, err = net.ListenUDP("udp", locaAddr)
conn, err = net.ListenUDP("udp", localAddr)
defer conn.Close()
if err != nil {
return "", errors.New("监听udp出错")
}
_, err = conn.WriteToUDP([]byte(str), remotAddr)
_, err = conn.WriteToUDP([]byte(str), remoteAddr)
if err != nil {
return "", errors.New("发送msg到组播地址出错")
}

View File

@@ -5,4 +5,4 @@ import "testing"
func TestGateway(t *testing.T) {
Gateway()
}
}

View File

@@ -2,40 +2,37 @@ package upnp
import (
"bytes"
"github.com/pkg/errors"
"net/http"
"strconv"
"strings"
loger2 "github.com/IceWhaleTech/CasaOS/pkg/utils/loger"
"github.com/pkg/errors"
)
//
////添加一个端口映射
func (n *Upnp)AddPortMapping(localPort, remotePort int, protocol string) (err error) {
defer func(err error) {
func (n *Upnp) AddPortMapping(localPort, remotePort int, protocol string) (err error) {
defer func() {
if errTemp := recover(); errTemp != nil {
//log.Println("upnp模块报错了", errTemp)
err = errTemp.(error)
loger2.NewOLoger().Error("upnp模块报错了", errTemp)
}
}(err)
if issuccess := addSend(localPort, remotePort, protocol,n.GatewayHost, n.CtrlUrl,n.LocalHost); issuccess {
}()
if isSuccess := addSend(localPort, remotePort, protocol, n.GatewayHost, n.CtrlUrl, n.LocalHost); isSuccess {
return nil
} else {
return errors.New("添加一个端口映射失败")
}
return
}
func addSend(localPort, remotePort int, protocol, host, ctrUrl,localHost string) bool {
request := addRequest(localPort, remotePort, protocol, host, ctrUrl,localHost)
func addSend(localPort, remotePort int, protocol, host, ctrUrl, localHost string) bool {
request := addRequest(localPort, remotePort, protocol, host, ctrUrl, localHost)
response, _ := http.DefaultClient.Do(request)
defer response.Body.Close()
//resultBody, _ := ioutil.ReadAll(response.Body)
//fmt.Println(string(resultBody))
if response.StatusCode == 200 {
return true
}
return false
return response.StatusCode == 200
}
type Node struct {
@@ -45,7 +42,7 @@ type Node struct {
Child []Node
}
func addRequest(localPort, remotePort int, protocol string, gatewayHost, ctlUrl,localHost string) *http.Request {
func addRequest(localPort, remotePort int, protocol string, gatewayHost, ctlUrl, localHost string) *http.Request {
//请求头
header := http.Header{}
header.Set("Accept", "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2")
@@ -109,27 +106,25 @@ func (n *Node) BuildXML() string {
return buf.String()
}
func (n *Upnp)DelPortMapping(remotePort int, protocol string) bool {
issuccess := delSendSend(remotePort, protocol,n.GatewayHost,n.CtrlUrl)
if issuccess {
func (n *Upnp) DelPortMapping(remotePort int, protocol string) bool {
isSuccess := delSendSend(remotePort, protocol, n.GatewayHost, n.CtrlUrl)
if isSuccess {
//this.MappingPort.delMapping(remotePort, protocol)
//fmt.Println("删除了一个端口映射: remote:", remotePort)
}
return issuccess
return isSuccess
}
func delSendSend(remotePort int, protocol,host,ctlUrl string) bool {
delrequest := delbuildRequest(remotePort, protocol,host,ctlUrl)
func delSendSend(remotePort int, protocol, host, ctlUrl string) bool {
delrequest := delbuildRequest(remotePort, protocol, host, ctlUrl)
response, _ := http.DefaultClient.Do(delrequest)
//resultBody, _ := ioutil.ReadAll(response.Body)
defer response.Body.Close()
if response.StatusCode == 200 {
// log.Println(string(resultBody))
return true
}
return false
return response.StatusCode == 200
}
func delbuildRequest(remotePort int, protocol,host,ctlUrl string) *http.Request {
func delbuildRequest(remotePort int, protocol, host, ctlUrl string) *http.Request {
//请求头
header := http.Header{}
header.Set("Accept", "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2")
@@ -160,4 +155,4 @@ func delbuildRequest(remotePort int, protocol,host,ctlUrl string) *http.Request
request.Header = header
request.Header.Set("Content-Length", strconv.Itoa(len([]byte(bodyStr))))
return request
}
}

View File

@@ -7,10 +7,10 @@ import (
type Upnp struct {
LocalHost string `json:"local_host"`
GatewayName string `json:"gateway_name"` //网关名称
GatewayHost string `json:"gateway_host"` //网关ip和端口
GatewayName string `json:"gateway_name"` //网关名称
GatewayHost string `json:"gateway_host"` //网关ip和端口
DeviceDescUrl string `json:"device_desc_url"` //设备描述url
CtrlUrl string `json:"ctrl_url"` //控制请求url
CtrlUrl string `json:"ctrl_url"` //控制请求url
}
func Testaaa() {
@@ -23,4 +23,3 @@ func Testaaa() {
fmt.Println("gateway ip address: ", upnpMan.Gateway.Host)
}
}

View File

@@ -6,4 +6,4 @@ import (
func TestTestaaa(t *testing.T) {
(Testaaa())
}
}

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

@@ -6,7 +6,7 @@ import (
"os"
"path/filepath"
"runtime"
"github.com/IceWhaleTech/CasaOS/pkg/config"
file2 "github.com/IceWhaleTech/CasaOS/pkg/utils/file"
)

View File

@@ -16,6 +16,7 @@ const (
DIR_ALREADY_EXISTS = 20001
FILE_ALREADY_EXISTS = 20002
FILE_OR_DIR_EXISTS = 20003
PORT_IS_OCCUPIED = 20004
//zerotier
GET_TOKEN_ERROR = 30001
@@ -49,6 +50,7 @@ var MsgFlags = map[int]string{
DIR_ALREADY_EXISTS: "Directory already exists",
FILE_ALREADY_EXISTS: "File already exists",
FILE_OR_DIR_EXISTS: "File or directory already exists",
PORT_IS_OCCUPIED: "Port is occupied",
//zerotier
GET_TOKEN_ERROR: "Get token error,Please log in to zerotier's official website to confirm whether the account is available",

View File

@@ -6,5 +6,5 @@ import (
)
func TestRandomString(t *testing.T) {
fmt.Println(RandomString(6,true))
}
fmt.Println(RandomString(6, true))
}

View File

@@ -10,6 +10,7 @@ import (
"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/command"
"github.com/IceWhaleTech/CasaOS/pkg/utils/env_helper"
"github.com/IceWhaleTech/CasaOS/pkg/utils/file"
"github.com/IceWhaleTech/CasaOS/pkg/utils/port"
@@ -20,6 +21,9 @@ import (
func InitFunction() {
go checkSystemApp()
Update2_3()
CheckSerialDiskMount()
}
var syncIsExistence = false
@@ -70,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()
@@ -169,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)
@@ -187,6 +188,32 @@ 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)
}
@@ -141,6 +140,8 @@ func InitRouter() *gin.Engine {
{
//获取我的已安装的列表
v1AppGroup.GET("/mylist", v1.MyAppList)
//
v1AppGroup.GET("/usage", v1.AppUsageList)
//app详情
v1AppGroup.GET("/appinfo/:id", v1.AppInfo)
//获取未安装的列表
@@ -192,6 +193,10 @@ func InitRouter() *gin.Engine {
v1SysGroup.POST("/config", v1.PostSetSystemConfig)
v1SysGroup.GET("/widget/config", v1.GetWidgetConfig)
v1SysGroup.POST("/widget/config", v1.PostSetWidgetConfig)
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()
@@ -206,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)
@@ -221,7 +228,7 @@ func InitRouter() *gin.Engine {
v1DiskGroup.POST("/format", v1.FormatDisk)
//添加分区
v1DiskGroup.POST("/addpart", v1.AddPartition)
v1DiskGroup.POST("/part", v1.AddPartition)
//获取可以格式化的内容
v1DiskGroup.GET("/type", v1.FormatDiskType)
@@ -229,6 +236,12 @@ func InitRouter() *gin.Engine {
//删除分区
v1DiskGroup.DELETE("/delpart", v1.RemovePartition)
//mount SATA disk
v1DiskGroup.POST("/mount", v1.PostMountDisk)
//umount SATA disk
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})
}
@@ -103,6 +114,18 @@ func MyAppList(c *gin.Context) {
c.JSON(http.StatusOK, &model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: list})
}
// @Summary my app hardware usage list
// @Produce application/json
// @Accept application/json
// @Tags app
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /app/usage [get]
func AppUsageList(c *gin.Context) {
list := service.MyService.App().GetHardwareUsage()
c.JSON(http.StatusOK, &model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: list})
}
// @Summary 应用详情
// @Produce application/json
// @Accept application/json
@@ -135,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++ {
@@ -142,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
// }
@@ -195,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})
}
@@ -212,3 +239,14 @@ func ShareAppFile(c *gin.Context) {
content := service.MyService.OAPI().ShareAppFile(str)
c.JSON(http.StatusOK, json.RawMessage(content))
}
// @Summary Resource Usage
// @Produce application/json
// @Accept application/json
// @Tags app
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /app/share [post]
func AppListResourceUsage() {
}

View File

@@ -64,7 +64,7 @@ func DDNSAddConfig(c *gin.Context) {
})
return
}
var m model2.DDNSUpdataDBModel
var m model2.DDNSUpdateDBModel
c.Bind(&m)
if err := service.MyService.DDNS().SaveConfig(m); err != nil {
c.JSON(http.StatusOK,

View File

@@ -1,13 +1,15 @@
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"
"net/http"
"strconv"
)
// @Summary 获取磁盘列表
@@ -19,47 +21,12 @@ 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 获取磁盘列表
// @Summary get disk list
// @Produce application/json
// @Accept application/json
// @Tags disk
@@ -76,12 +43,12 @@ func GetPlugInDisks(c *gin.Context) {
c.JSON(http.StatusOK, model.Result{Success: oasis_err.SUCCESS, Message: oasis_err.GetMsg(oasis_err.SUCCESS), Data: result})
}
// @Summary 磁盘详情
// @Summary disk detail
// @Produce application/json
// @Accept application/json
// @Tags disk
// @Security ApiKeyAuth
// @Param path query string true "要获取的磁盘详情 例如/dev/sda"
// @Param path query string true "for example /dev/sda"
// @Success 200 {string} string "ok"
// @Router /disk/info [get]
func GetDiskInfo(c *gin.Context) {
@@ -93,12 +60,12 @@ func GetDiskInfo(c *gin.Context) {
c.JSON(http.StatusOK, model.Result{Success: oasis_err.SUCCESS, Message: oasis_err.GetMsg(oasis_err.SUCCESS), Data: m})
}
// @Summary 磁盘详情
// @Summary format disk
// @Produce application/json
// @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,16 +75,10 @@ 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().UmountPointAndRemoveDir(path)
//格式化磁盘
service.MyService.Disk().FormatDisk(path, t)
//重新挂载
c.JSON(http.StatusOK, model.Result{Success: oasis_err.SUCCESS, Message: oasis_err.GetMsg(oasis_err.SUCCESS)})
}
@@ -154,25 +115,126 @@ func RemovePartition(c *gin.Context) {
c.JSON(http.StatusOK, model.Result{Success: oasis_err.SUCCESS, Message: oasis_err.GetMsg(oasis_err.SUCCESS)})
}
// @Summary 添加分区
// @Summary serial number
// @Produce application/json
// @Accept multipart/form-data
// @Tags disk
// @Security ApiKeyAuth
// @Param path formData string true "磁盘路径 例如/dev/sda"
// @Param size formData string true "需要分区容量大小(MB)"
// @Param num formData string true "磁盘符号"
// @Param serial formData string true "serial"
// @Success 200 {string} string "ok"
// @Router /disk/addpart [post]
func AddPartition(c *gin.Context) {
path := c.PostForm("path")
size, _ := strconv.Atoi(c.DefaultPostForm("size", "0"))
num := c.DefaultPostForm("num", "9")
if len(path) == 0 {
serial := c.PostForm("serial")
if len(path) == 0 || len(serial) == 0 {
c.JSON(http.StatusOK, model.Result{Success: oasis_err.INVALID_PARAMS, Message: oasis_err.GetMsg(oasis_err.INVALID_PARAMS)})
return
}
//size*1024*1024/512
service.MyService.Disk().AddPartition(path, num, uint64(size*1024*2))
service.MyService.Disk().AddPartition(path)
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")
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)})
}
// @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) {
//
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

@@ -2,7 +2,6 @@ package v1
import (
"bytes"
"encoding/json"
json2 "encoding/json"
"net/http"
"reflect"
@@ -11,6 +10,7 @@ import (
"time"
"github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/pkg/config"
"github.com/IceWhaleTech/CasaOS/pkg/docker"
upnp2 "github.com/IceWhaleTech/CasaOS/pkg/upnp"
"github.com/IceWhaleTech/CasaOS/pkg/utils/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 {
//
@@ -268,11 +278,11 @@ func InstallApp(c *gin.Context) {
rely.ContainerId = mysqlContainerId
rely.CustomId = mid
rely.ContainerCustomId = id
var msqlConfig model2.MysqlConfigs
var mysqlConfig model2.MysqlConfigs
//结构体转换
copier.Copy(&msqlConfig, &mc)
rely.Config = msqlConfig
copier.Copy(&mysqlConfig, &mc)
rely.Config = mysqlConfig
service.MyService.Rely().Create(rely)
relyMap["mysql"] = mid
@@ -421,9 +431,12 @@ func InstallApp(c *gin.Context) {
rely := model.MapStrings{}
copier.Copy(&rely, &relyMap)
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)
volumesStr, _ := json2.Marshal(m.Volumes)
@@ -463,6 +476,7 @@ func InstallApp(c *gin.Context) {
// m.PortMap = m.Port
//}
service.MyService.App().SaveContainer(md)
config.CasaOSGlobalVariables.AppChange = true
}()
@@ -677,7 +691,7 @@ func UnInstallApp(c *gin.Context) {
}
//step删除容器
err = service.MyService.Docker().DockerContainerRemove(appId)
err = service.MyService.Docker().DockerContainerRemove(appId, false)
if err != nil {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.UNINSTALL_APP_ERROR, Message: oasis_err2.GetMsg(oasis_err2.UNINSTALL_APP_ERROR), Data: err.Error()})
return
@@ -739,7 +753,7 @@ func UnInstallApp(c *gin.Context) {
// }
//}
}
config.CasaOSGlobalVariables.AppChange = true
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
}
@@ -908,16 +922,10 @@ func UpdateSetting(c *gin.Context) {
//如果容器端口均未修改,这不进行处理
portsStr, _ := json2.Marshal(m.Ports)
list := []model.PathMap{}
json.Unmarshal([]byte(appInfo.Volumes), &list)
for i := 0; i < len(list); i++ {
list[i].Path = docker.GetDir(id, list[i].ContainerPath)
}
envsStr, _ := json2.Marshal(m.Envs)
volumesStr, _ := json2.Marshal(m.Volumes)
devicesStr, _ := json2.Marshal(m.Devices)
listStr, _ := json2.Marshal(list)
if !reflect.DeepEqual(string(portsStr), appInfo.Ports) || !reflect.DeepEqual(string(envsStr), appInfo.Envs) || !reflect.DeepEqual(volumesStr, listStr) || m.PortMap != appInfo.PortMap || m.NetworkModel != appInfo.NetModel {
if !reflect.DeepEqual(string(portsStr), appInfo.Ports) || !reflect.DeepEqual(string(envsStr), appInfo.Envs) || !reflect.DeepEqual(string(volumesStr), appInfo.Volumes) || m.PortMap != appInfo.PortMap || m.NetworkModel != appInfo.NetModel {
var newUUid = uuid.NewV4().String()
var err error
@@ -934,7 +942,7 @@ func UpdateSetting(c *gin.Context) {
return
}
err = service.MyService.Docker().DockerContainerRemove(id)
err = service.MyService.Docker().DockerContainerRemove(id, true)
if err != nil {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.ERROR, Message: oasis_err2.GetMsg(oasis_err2.ERROR)})
return

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

@@ -4,12 +4,17 @@ import (
"encoding/json"
"fmt"
"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"
model2 "github.com/IceWhaleTech/CasaOS/service/model"
@@ -80,7 +85,7 @@ func GetCasaOSErrorLogs(c *gin.Context) {
// @Produce application/json
// @Accept multipart/form-data
// @Tags sys
// @Param file formData file true "用户头像"
// @Param config formData string true "config json string"
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /sys/changhead [post]
@@ -102,7 +107,7 @@ func GetSystemConfigDebug(c *gin.Context) {
array := service.MyService.System().GetSystemConfigDebug()
disk := service.MyService.ZiMa().GetDiskInfo()
array = append(array, fmt.Sprintf("disk,totle:%v,used:%v,UsedPercent:%v", disk.Total>>20, disk.Used>>20, disk.UsedPercent))
array = append(array, fmt.Sprintf("disk,total:%v,used:%v,UsedPercent:%v", disk.Total>>20, disk.Used>>20, disk.UsedPercent))
c.JSON(http.StatusOK, model.Result{Success: oasis_err.SUCCESS, Message: oasis_err.GetMsg(oasis_err.SUCCESS), Data: array})
}
@@ -125,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,
@@ -135,6 +141,58 @@ func PostSetWidgetConfig(c *gin.Context) {
})
}
// @Summary get casaos server port
// @Produce application/json
// @Accept application/json
// @Tags sys
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /sys/port [get]
func GetCasaOSPort(c *gin.Context) {
c.JSON(http.StatusOK,
model.Result{
Success: oasis_err.SUCCESS,
Message: oasis_err.GetMsg(oasis_err.SUCCESS),
Data: config.ServerInfo.HttpPort,
})
}
// @Summary edit casaos server port
// @Produce application/json
// @Accept application/json
// @Tags sys
// @Security ApiKeyAuth
// @Param port formData string true "port"
// @Success 200 {string} string "ok"
// @Router /sys/port [put]
func PutCasaOSPort(c *gin.Context) {
port, err := strconv.Atoi(c.PostForm("port"))
if err != nil {
c.JSON(http.StatusOK,
model.Result{
Success: oasis_err.ERROR,
Message: err.Error(),
})
return
}
isAvailable := port2.IsPortAvailable(port, "tcp")
if !isAvailable {
c.JSON(http.StatusOK,
model.Result{
Success: oasis_err.PORT_IS_OCCUPIED,
Message: oasis_err.GetMsg(oasis_err.PORT_IS_OCCUPIED),
})
return
}
service.MyService.System().UpSystemPort(strconv.Itoa(port))
c.JSON(http.StatusOK,
model.Result{
Success: oasis_err.SUCCESS,
Message: oasis_err.GetMsg(oasis_err.SUCCESS),
})
}
// @Summary 检查是否进入引导状态
// @Produce application/json
// @Accept application/json
@@ -156,3 +214,55 @@ func GetGuideCheck(c *gin.Context) {
Data: data,
})
}
// @Summary active killing casaos
// @Produce application/json
// @Accept application/json
// @Tags sys
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /sys/kill [post]
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

@@ -2,9 +2,15 @@ package service
import (
"context"
"encoding/json"
"io"
"io/ioutil"
"runtime"
"strings"
"sync"
"time"
"github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/pkg/config"
"github.com/IceWhaleTech/CasaOS/pkg/utils/command"
loger2 "github.com/IceWhaleTech/CasaOS/pkg/utils/loger"
@@ -27,6 +33,9 @@ type AppService interface {
GetSimpleContainerInfo(name string) (types.Container, error)
DelAppConfigDir(path string)
GetSystemAppList() *[]model2.MyAppList
GetHardwareUsageSteam()
GetHardwareUsage() []model.DockerStatsModel
GetAppStats(id string) string
}
type appStruct struct {
@@ -37,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)
}
@@ -48,7 +57,6 @@ func (a *appStruct) GetMyList(index, size int, position bool) *[]model2.MyAppLis
if err != nil {
a.log.Error("获取docker容器失败", "app.getmylist", "line:42", err)
}
//获取本地数据库应用
var lm []model2.AppListDBModel
@@ -68,19 +76,18 @@ func (a *appStruct) GetMyList(index, size int, position bool) *[]model2.MyAppLis
for _, container := range containers {
if lMap[container.ID] != nil && container.Labels["origin"] != "system" {
var m model2.AppListDBModel
m = lMap[container.ID].(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
}
// 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,
@@ -88,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,
})
}
@@ -113,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)
}
//获取本地数据库应用
@@ -129,8 +136,7 @@ func (a *appStruct) GetSystemAppList() *[]model2.MyAppList {
for _, container := range containers {
if lMap[container.ID] != nil {
var m model2.AppListDBModel
m = lMap[container.ID].(model2.AppListDBModel)
m := lMap[container.ID].(model2.AppListDBModel)
if len(m.Label) == 0 {
m.Label = m.Title
}
@@ -156,7 +162,6 @@ func (a *appStruct) GetSystemAppList() *[]model2.MyAppList {
//Rely: m.Rely,
})
}
}
return &list
@@ -174,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 {
@@ -194,6 +199,10 @@ func (a *appStruct) GetSimpleContainerInfo(name string) (types.Container, error)
filters := filters.NewArgs()
filters.Add("name", name)
containers, err := cli.ContainerList(context.Background(), types.ContainerListOptions{All: true, Filters: filters})
if err != nil {
return types.Container{}, err
}
if len(containers) > 0 {
return containers[0], nil
}
@@ -231,12 +240,117 @@ func (a *appStruct) RemoveContainerById(id string) {
a.db.Table(model2.CONTAINERTABLENAME).Where("custom_id = ?", id).Delete(&model2.AppListDBModel{})
}
// init install
func Init() {
var dataStats sync.Map
var isFinish bool = false
func (a *appStruct) GetAppStats(id string) string {
cli, err := client2.NewClientWithOpts(client2.FromEnv)
if err != nil {
return ""
}
defer cli.Close()
con, err := cli.ContainerStats(context.Background(), id, false)
if err != nil {
return err.Error()
}
defer con.Body.Close()
c, _ := ioutil.ReadAll(con.Body)
return string(c)
}
func (a *appStruct) GetHardwareUsage() []model.DockerStatsModel {
steam := true
for !isFinish {
if steam {
steam = false
go func() {
a.GetHardwareUsageSteam()
}()
}
runtime.Gosched()
}
list := []model.DockerStatsModel{}
dataStats.Range(func(key, value interface{}) bool {
list = append(list, value.(model.DockerStatsModel))
return true
})
return list
}
func (a *appStruct) GetHardwareUsageSteam() {
cli, err := client2.NewClientWithOpts(client2.FromEnv)
if err != nil {
return
}
defer cli.Close()
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
var lm []model2.AppListDBModel
a.db.Table(model2.CONTAINERTABLENAME).Select("label,title,icon,container_id").Where("origin != ?", "system").Find(&lm)
for i := 0; i < 100; i++ {
if config.CasaOSGlobalVariables.AppChange {
lm = []model2.AppListDBModel{}
config.CasaOSGlobalVariables.AppChange = false
a.db.Table(model2.CONTAINERTABLENAME).Select("label,title,icon,container_id").Where("origin != ?", "system").Find(&lm)
dataApps := dataStats
dataStats.Range(func(key, value interface{}) bool {
dataStats.Delete(key)
return true
})
for _, v := range lm {
m, _ := dataApps.Load(v.ContainerId)
if m != nil {
dataStats.Store(v.ContainerId, m)
}
}
}
var wg sync.WaitGroup
for _, v := range lm {
wg.Add(1)
go func(v model2.AppListDBModel, i int) {
defer wg.Done()
stats, err := cli.ContainerStats(ctx, v.ContainerId, true)
if err != nil {
return
}
decode := json.NewDecoder(stats.Body)
var data interface{}
if err := decode.Decode(&data); err == io.EOF {
return
}
m, _ := dataStats.Load(v.ContainerId)
dockerStats := model.DockerStatsModel{}
if m != nil {
dockerStats.Pre = m.(model.DockerStatsModel).Data
}
dockerStats.Data = data
dockerStats.Icon = v.Icon
if len(v.Label) > 0 {
dockerStats.Title = v.Label
} else {
dockerStats.Title = v.Title
}
dataStats.Store(v.ContainerId, dockerStats)
if i == 99 {
stats.Body.Close()
}
}(v, i)
}
wg.Wait()
isFinish = true
time.Sleep(time.Second * 3)
}
isFinish = false
cancel()
}
func NewAppService(db *gorm.DB, logger loger2.OLog) AppService {
Init()
return &appStruct{db: db, log: logger}
}

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

@@ -1,12 +1,13 @@
package service
import (
"os/exec"
ip_helper2 "github.com/IceWhaleTech/CasaOS/pkg/utils/ip_helper"
loger2 "github.com/IceWhaleTech/CasaOS/pkg/utils/loger"
"github.com/IceWhaleTech/CasaOS/service/ddns"
"github.com/IceWhaleTech/CasaOS/service/model"
"gorm.io/gorm"
"os/exec"
)
type ddnsStruct struct {
@@ -20,17 +21,15 @@ type DDNSService interface {
GetConfigList() *[]model.DDNSList
DeleteConfig(id uint) bool
GetType(name string) (uint, string)
SaveConfig(model model.DDNSUpdataDBModel) error
SaveConfig(model model.DDNSUpdateDBModel) error
}
//判断当前添加的是否存在
func (d *ddnsStruct) IsExis(t int, domain string, host string) bool {
var count int64
d.db.Table(model.DDNSLISTTABLENAME).Where("type=? AND domain=? AND host=?", t, domain, host).Count(&count)
if count > 0 {
return true
}
return false
return count > 0
}
//前台获取已配置的ddns列表
@@ -41,7 +40,7 @@ func (d *ddnsStruct) GetConfigList() *[]model.DDNSList {
}
func (d *ddnsStruct) DeleteConfig(id uint) bool {
d.db.Delete(&model.DDNSUpdataDBModel{Id: id})
d.db.Delete(&model.DDNSUpdateDBModel{Id: id})
return true
}
@@ -66,12 +65,12 @@ func (d *ddnsStruct) GetType(name string) (uint, string) {
}
//保存配置到数据库
func (d *ddnsStruct) GetDockerRootDir(model model.DDNSUpdataDBModel) error {
func (d *ddnsStruct) GetDockerRootDir(model model.DDNSUpdateDBModel) error {
return d.db.Create(&model).Error
}
//保存配置到数据库
func (d *ddnsStruct) SaveConfig(model model.DDNSUpdataDBModel) error {
func (d *ddnsStruct) SaveConfig(model model.DDNSUpdateDBModel) error {
return d.db.Create(&model).Error
}
@@ -87,7 +86,7 @@ func chackPing(b chan bool, url string) {
}
//更新列表
func UpdataDDNSList(db *gorm.DB) {
func UpdateDDNSList(db *gorm.DB) {
var s []model.DDNSCoreList
db.Table(model.DDNSLISTTABLENAME).Select("o_ddns_type.name as name,o_ddns_type.api_host as api_host,o_ddns.id,`host`,domain,user_name,`password`,`key`,secret,type").Joins("left join o_ddns_type on o_ddns.type=o_ddns_type.id").Scan(&s)
for _, item := range s {

View File

@@ -31,4 +31,3 @@ func SetOauth(request *http.Request, value string) {
func SetXFilter(request *http.Request, value string) {
request.Header.Set("X-Filter", value)
}

View File

@@ -5,13 +5,16 @@ import (
"fmt"
"strconv"
"strings"
"time"
"github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/pkg/config"
command2 "github.com/IceWhaleTech/CasaOS/pkg/utils/command"
loger2 "github.com/IceWhaleTech/CasaOS/pkg/utils/loger"
model2 "github.com/IceWhaleTech/CasaOS/service/model"
"github.com/shirou/gopsutil/v3/disk"
"github.com/tidwall/gjson"
"gorm.io/gorm"
)
type DiskService interface {
@@ -21,11 +24,17 @@ type DiskService interface {
UmountPointAndRemoveDir(path string) string
GetDiskInfo(path string) model.LSBLKModel
DelPartition(path, num string) string
AddPartition(path, num string, size uint64) string
AddPartition(path string) string
GetDiskInfoByPath(path string) *disk.UsageStat
MountDisk(path, volume string)
GetSerialAll() []model2.SerialDisk
SaveMountPoint(m model2.SerialDisk)
DeleteMountPoint(path, mountPoint string)
DeleteMount(id string)
}
type diskService struct {
log loger2.OLog
db *gorm.DB
}
//通过脚本获取外挂磁盘
@@ -55,28 +64,17 @@ func (d *diskService) DelPartition(path, num string) string {
return ""
}
//添加分区
func (d *diskService) AddPartition(path, num string, size uint64) string {
var maxSector uint64 = 0
chiList := command2.ExecResultStrArray("source " + config.AppInfo.ProjectPath + "/shell/helper.sh ;GetPartitionSectors " + path)
if len(chiList) == 0 {
d.log.Error("chiList length error")
}
for i := 0; i < len(chiList); i++ {
tempArr := strings.Split(chiList[i], ",")
tempSector, _ := strconv.ParseUint(tempArr[2], 10, 64)
if tempSector > maxSector {
maxSector = tempSector
}
}
r := command2.ExecResultStrArray("source ./shell/helper.sh ;AddPartition " + path + " " + num + " " + strconv.FormatUint(maxSector+1, 10) + " " + strconv.FormatUint(size+maxSector+1, 10))
//part
func (d *diskService) AddPartition(path string) string {
r := command2.ExecResultStrArray("source " + config.AppInfo.ProjectPath + "/shell/helper.sh ;AddPartition " + path)
fmt.Println(r)
return ""
}
func (d *diskService) AddAllPartition(path string) {
}
//获取硬盘详情
func (d *diskService) GetDiskInfoByPath(path string) *disk.UsageStat {
diskInfo, err := disk.Usage(path + "1")
@@ -90,11 +88,23 @@ func (d *diskService) GetDiskInfoByPath(path string) *disk.UsageStat {
return diskInfo
}
//获取磁盘信息
//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
@@ -103,15 +113,13 @@ func (d *diskService) LSBLK() []model.LSBLKModel {
d.log.Error("json ummarshal error", err)
}
var n []model.LSBLKModel
var c []model.LSBLKModel
var fsused uint64
var health = true
for _, i := range m {
if i.Children != nil {
if i.Type != "loop" && !i.RO {
fsused = 0
for _, child := range i.Children {
if child.RM {
@@ -134,7 +142,7 @@ func (d *diskService) LSBLK() []model.LSBLKModel {
i.Children = c
if fsused > 0 {
i.UsedPercent, err = strconv.ParseFloat(fmt.Sprintf("%.4f", float64(fsused)/float64(i.Size)), 64)
fmt.Println(err)
d.log.Fatal("diskservice_lsblk_fsused", err)
}
n = append(n, i)
health = true
@@ -142,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,17 +208,33 @@ func (d *diskService) GetDiskInfo(path string) model.LSBLKModel {
return m
}
//func GetDiskInfo(path string) *disk.UsageStat {
// diskInfo, _ := disk.Usage(path)
// diskInfo.UsedPercent, _ = strconv.ParseFloat(fmt.Sprintf("%.1f", diskInfo.UsedPercent), 64)
// diskInfo.InodesUsedPercent, _ = strconv.ParseFloat(fmt.Sprintf("%.1f", diskInfo.InodesUsedPercent), 64)
// return diskInfo
//}
//func (d *diskService) GetPlugInDisk() []string {
// return disk.Partitions(false)
//}
func NewDiskService(log loger2.OLog) DiskService {
return &diskService{log: log}
func (d *diskService) MountDisk(path, volume string) {
r := command2.ExecResultStr("source " + config.AppInfo.ProjectPath + "/shell/helper.sh ;do_mount " + path + " " + volume)
fmt.Print(r)
}
func (d *diskService) SaveMountPoint(m model2.SerialDisk) {
d.db.Create(&m)
}
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
}
func NewDiskService(log loger2.OLog, db *gorm.DB) DiskService {
return &diskService{log: log, db: db}
}

View File

@@ -8,7 +8,6 @@ import (
json2 "encoding/json"
"fmt"
"reflect"
"regexp"
"syscall"
model2 "github.com/IceWhaleTech/CasaOS/service/model"
@@ -53,7 +52,7 @@ type DockerService interface {
DockerListByImage(image, version string) (*types.Container, error)
DockerContainerInfo(name string) (*types.ContainerJSON, error)
DockerImageRemove(name string) error
DockerContainerRemove(name string) error
DockerContainerRemove(name string, update bool) error
DockerContainerStop(id string) error
DockerContainerUpdateName(name, id string) (err error)
DockerContainerUpdate(m model.CustomizationPostData, id string) (err error)
@@ -107,7 +106,7 @@ func (ds *dockerService) GetNetWorkNameByNetWorkID(id string) (string, error) {
defer cli.Close()
filter := filters.NewArgs()
filter.Add("id", id)
d, err := cli.NetworkList(context.Background(), types.NetworkListOptions{filter})
d, err := cli.NetworkList(context.Background(), types.NetworkListOptions{Filters: filter})
if err == nil && len(d) > 0 {
return d[0].Name, nil
}
@@ -158,7 +157,7 @@ func DockerEx() {
importResponse.Close()
println(string(response))
if string(response) != "response" {
fmt.Println("expected response to contain 'response', got %s", string(response))
fmt.Printf("expected response to contain 'response', got %s", string(response))
}
}
@@ -187,7 +186,6 @@ func (ds *dockerService) DockerImageInfo(image string) {
if err != nil {
fmt.Print(err)
}
}
func MsqlExec(container string) error {
@@ -266,6 +264,8 @@ func DockerLogs() {
if err != nil {
log.Fatal(err)
}
defer i.Close()
hdr := make([]byte, 8)
for {
_, err := i.Read(hdr)
@@ -284,7 +284,6 @@ func DockerLogs() {
_, err = i.Read(dat)
fmt.Fprint(w, string(dat))
}
defer i.Close()
}
//正式内容
@@ -352,7 +351,7 @@ func (ds *dockerService) DockerPullImage(imageName string, m model2.AppNotify) e
//param udp 容器其他udp端口
func (ds *dockerService) DockerContainerCreate(imageName string, containerDbId string, m model.CustomizationPostData, net string) (containerId string, err error) {
if len(net) == 0 {
net = "oasis"
net = "bridge"
}
cli, err := client2.NewClientWithOpts(client2.FromEnv)
@@ -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},
@@ -515,7 +515,7 @@ func (ds *dockerService) DockerContainerCreate(imageName string, containerDbId s
}
//删除容器
func (ds *dockerService) DockerContainerRemove(name string) error {
func (ds *dockerService) DockerContainerRemove(name string, update bool) error {
cli, err := client2.NewClientWithOpts(client2.FromEnv)
if err != nil {
return err
@@ -524,9 +524,11 @@ func (ds *dockerService) DockerContainerRemove(name string) error {
err = cli.ContainerRemove(context.Background(), name, types.ContainerRemoveOptions{})
//路径处理
path := docker.GetDir(name, "/config")
if !file.CheckNotExist(path) {
file.RMDir(path)
if !update {
path := docker.GetDir(name, "/config")
if !file.CheckNotExist(path) {
file.RMDir(path)
}
}
if err != nil {
@@ -787,7 +789,7 @@ func (ds *dockerService) DockerNetworkModelList() []types.NetworkResource {
networks, _ := cli.NetworkList(context.Background(), types.NetworkListOptions{})
return networks
}
func NewDcokerService(log loger2.OLog) DockerService {
func NewDockerService(log loger2.OLog) DockerService {
return &dockerService{rootDir: command2.ExecResultStr(`source ./shell/helper.sh ;GetDockerRootDir`), log: log}
}

View File

@@ -7,5 +7,3 @@ type MysqlConfig struct {
DataBasePassword string `json:"data_base_password"`
DataBaseDB string `json:"data_base_db"`
}

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

@@ -26,14 +26,10 @@ type AppListDBModel struct {
PortMap string `json:"port_map"`
Label string `json:"label"`
EnableUPNP bool `json:"enable_upnp"`
//Envs model.EnvArrey `json:"envs" bson:"envs"`
//Ports model.PortArrey `json:"ports" bson:"ports"`
//Volumes model.PathArrey `json:"volumes" bson:"volumes"`
//Devices model.PathArrey `json:"devices" bson:"devices"`
Envs string `json:"envs"`
Ports string `json:"ports"`
Volumes string `json:"volumes"`
Devices string `json:"devices"`
Envs string `json:"envs"`
Ports string `json:"ports"`
Volumes string `json:"volumes"`
Devices string `json:"devices"`
//Envs []model.Env `json:"envs"`
//Ports []model.PortMap `gorm:"type:json" json:"ports"`
//Volumes []model.PathMap `gorm:"type:json" json:"volumes"`

View File

@@ -2,11 +2,11 @@ package model
import "time"
func (p *DDNSUpdataDBModel) TableName() string {
func (p *DDNSUpdateDBModel) TableName() string {
return "o_ddns"
}
type DDNSUpdataDBModel struct {
type DDNSUpdateDBModel struct {
Id uint `gorm:"column:id;primary_key" json:"id"`
Ipv4 string `gorm:"-"`
Ipv6 string `gorm:"-"`
@@ -17,8 +17,8 @@ type DDNSUpdataDBModel struct {
Secret string `json:"secret" form:"secret"`
UserName string `json:"user_name" form:"user_name"`
Password string `json:"password" form:"password"`
CreatedAt time.Time `gorm:"<-:create" json:"created_at"`
UpdatedAt time.Time `gorm:"<-:create;<-:update" json:"updated_at"`
CreatedAt time.Time `gorm:"<-:create" json:"created_at"`
UpdatedAt time.Time `gorm:"<-:create;<-:update" json:"updated_at"`
}
const DDNSLISTTABLENAME = "o_ddns"
@@ -39,9 +39,9 @@ type DDNSList struct {
//定时任务使用
type DDNSCoreList struct {
Id uint `gorm:"column:id;primary_key" json:"id"`
Id uint `gorm:"column:id;primary_key" json:"id"`
Domain string `json:"domain" form:"domain"`
Name string `json:"domain" form:"name"`
Name string `json:"name" form:"name"`
Type uint `json:"type"`
Key string `json:"key"`
Message string `json:"message"`

14
service/model/o_disk.go Normal file
View File

@@ -0,0 +1,14 @@
package model
//SerialAdvanced Technology Attachment (STAT)
type SerialDisk struct {
Id uint `gorm:"column:id;primary_key" json:"id"`
Serial string `json:"serial"`
Path string `json:"path"`
State int `json:"state"`
MountPoint string `json:"mount_point"`
}
func (p *SerialDisk) TableName() string {
return "o_disk"
}

View File

@@ -35,12 +35,12 @@ func NewService(db *gorm.DB, log loger2.OLog) Repository {
app: NewAppService(db, log),
ddns: NewDDNSService(db, log),
user: NewUserService(),
docker: NewDcokerService(log),
docker: NewDockerService(log),
//redis: NewRedisService(rp),
zerotier: NewZeroTierService(),
zima: NewZiMaService(),
oapi: NewOasisService(),
disk: NewDiskService(log),
disk: NewDiskService(log, db),
notify: NewNotifyService(db),
shareDirectory: NewShareDirService(db, log),
task: NewTaskService(db, log),

View File

@@ -14,6 +14,9 @@ type SystemService interface {
UpdateSystemVersion(version string)
GetSystemConfigDebug() []string
GetCasaOSLogs(lineNumber int) string
UpdateAssist()
UpSystemPort(port string)
GetTimeZone() string
}
type systemService struct {
log loger.OLog
@@ -25,6 +28,14 @@ func (s *systemService) UpdateSystemVersion(version string) {
s.log.Error(command2.ExecResultStrArray("source " + config.AppInfo.ProjectPath + "/shell/tools.sh ;update " + version))
//s.log.Error(command2.ExecResultStr(config.AppInfo.ProjectPath + "/shell/tool.sh -r " + version))
}
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")
}
@@ -39,6 +50,13 @@ func (s *systemService) UpSystemConfig(str string, widget string) {
}
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)
config.ServerInfo.HttpPort = port
}
config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath)
}
func (s *systemService) GetCasaOSLogs(lineNumber int) string {
file, err := os.Open(s.log.Path())
if err != nil {

View File

@@ -2,6 +2,8 @@ package service
import (
json2 "encoding/json"
"strconv"
"github.com/IceWhaleTech/CasaOS/pkg/config"
httper2 "github.com/IceWhaleTech/CasaOS/pkg/utils/httper"
loger2 "github.com/IceWhaleTech/CasaOS/pkg/utils/loger"
@@ -9,7 +11,6 @@ import (
"github.com/IceWhaleTech/CasaOS/types"
"github.com/tidwall/gjson"
"gorm.io/gorm"
"strconv"
)
type TaskService interface {

View File

@@ -34,20 +34,20 @@ type ZeroTierService interface {
DeleteNetwork(token, id string) interface{}
GetJoinNetworks() string
}
type zerotierstruct struct {
type zerotierStruct struct {
}
var client http.Client
func (c *zerotierstruct) ZeroTierJoinNetwork(networkId string) {
func (c *zerotierStruct) ZeroTierJoinNetwork(networkId string) {
command2.OnlyExec(`zerotier-cli join ` + networkId)
}
func (c *zerotierstruct) ZeroTierLeaveNetwork(networkId string) {
func (c *zerotierStruct) ZeroTierLeaveNetwork(networkId string) {
command2.OnlyExec(`zerotier-cli leave ` + networkId)
}
//登录并获取token
func (c *zerotierstruct) GetToken(username, pwd string) string {
func (c *zerotierStruct) GetToken(username, pwd string) string {
if len(config.ZeroTierInfo.Token) > 0 {
return config.ZeroTierInfo.Token
} else {
@@ -55,7 +55,7 @@ func (c *zerotierstruct) GetToken(username, pwd string) string {
}
}
func (c *zerotierstruct) ZeroTierRegister(email, lastName, firstName, password string) string {
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"
@@ -210,7 +210,7 @@ func ZeroTierGet(url string, cookies []*http.Cookie, t uint8) (action string, c
}
//模拟提交表单
func ZeroTierPost(str bytes.Buffer, action string, cookes []*http.Cookie, isLogin bool) (url, errInfo string, err error) {
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")
@@ -219,7 +219,7 @@ func ZeroTierPost(str bytes.Buffer, action string, cookes []*http.Cookie, isLogi
req.Header.Set(k, v)
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
for _, cookie := range cookes {
for _, cookie := range cookies {
req.AddCookie(cookie)
}
res, err := client.Do(req)
@@ -273,62 +273,62 @@ func ZeroTierPost(str bytes.Buffer, action string, cookes []*http.Cookie, isLogi
}
//获取zerotile网络列表和本地用户已加入的网络
func (c *zerotierstruct) ZeroTierNetworkList(token string) (interface{}, []string) {
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) {
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{} {
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{} {
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{} {
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{} {
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{} {
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{} {
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{} {
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 {
func (c *zerotierStruct) GetJoinNetworks() string {
json := command2.ExecResultStr("source " + config.AppInfo.ProjectPath + "/shell/helper.sh ;GetLocalJoinNetworks")
return json
}
@@ -339,5 +339,5 @@ func NewZeroTierService() ZeroTierService {
return http.ErrUseLastResponse //禁止重定向
},
}
return &zerotierstruct{}
return &zerotierStruct{}
}

View File

@@ -116,7 +116,6 @@ func (c *zima) GetNetState(name string) string {
//网络信息
func (c *zima) GetNetInfo() []net.IOCountersStat {
//loger.Error("输出个内容试试")
parts, _ := net.IOCounters(true)
//fmt.Println(net.ConntrackStatsWithContext(true))
return parts

12
shell/assist.sh Normal file
View File

@@ -0,0 +1,12 @@
#!/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/
}
version_0_2_3

View File

@@ -30,6 +30,11 @@ GetNetCard() {
fi
}
GetTimeZone(){
timedatectl | grep "Time zone" | awk '{print $3}'
}
#查看网卡状态
#param 网卡名称
CatNetCardState() {
@@ -103,20 +108,22 @@ DelPartition() {
EOF
}
#添加分区
#添加分区只有一个分区
#param 路径 /dev/sdb
#param 磁盘号 最大128
#param 磁盘大小 字节 512*2048=1024kb=1M
#param 要挂载的目录
AddPartition() {
# fdisk $1 <<EOF
# n
# $2
# $3
# $4
# wq
#EOF
parted $1 mkpart primary ext4 s3 s4
DelPartition $1
parted -s $1 mklabel gpt
parted -s $1 mkpart primary ext4 0 100%
mkfs.ext4 $11
partprobe $1
# mount $11 $2
}
#磁盘类型
@@ -151,7 +158,174 @@ GetPartitionSectors() {
fdisk $1 -l | grep "/dev/sda[1-9]" | awk 'BEGIN{OFS=","}{print $1,$2,$3,$4}'
}
#检查没有使用的挂载点删除文件夹
AutoRemoveUnuseDir() {
DIRECTORY="/mnt/"
dir=$(ls -l $DIRECTORY | awk '/^d/ {print $NF}')
for i in $dir; do
path="$DIRECTORY$i"
mountStr=$(mountpoint $path)
notMountpoint="is not a mountpoint"
if [[ $mountStr =~ $notMountpoint ]]; then
if [ "$(ls -A $path)" = "" ]; then
rm -fr $path
else
echo "$path is not empty"
fi
fi
done
}
#重载samba服务
ReloadSamba() {
/etc/init.d/smbd reload
}
# $1=sda1
# $2=volume{1}
do_mount() {
DEVBASE=$1
DEVICE="${DEVBASE}"
# See if this drive is already mounted, and if so where
MOUNT_POINT=$(mount | grep ${DEVICE} | awk '{ print $3 }')
if [ -n "${MOUNT_POINT}" ]; then
${log} "Warning: ${DEVICE} is already mounted at ${MOUNT_POINT}"
exit 1
fi
# Get info for this drive: $ID_FS_LABEL and $ID_FS_TYPE
eval $(blkid -o udev ${DEVICE} | grep -i -e "ID_FS_LABEL" -e "ID_FS_TYPE")
LABEL=$2
if grep -q " ${LABEL} " /etc/mtab; then
# Already in use, make a unique one
LABEL+="-${DEVBASE}"
fi
DEV_LABEL="${LABEL}"
# Use the device name in case the drive doesn't have label
if [ -z ${DEV_LABEL} ]; then
DEV_LABEL="${DEVBASE}"
fi
MOUNT_POINT="${DEV_LABEL}"
${log} "Mount point: ${MOUNT_POINT}"
mkdir -p ${MOUNT_POINT}
case ${ID_FS_TYPE} in
vfat)
mount -t vfat -o rw,relatime,users,gid=100,umask=000,shortname=mixed,utf8=1,flush ${DEVICE} ${MOUNT_POINT}
;;
ext[2-4])
mount -o noatime ${DEVICE} ${MOUNT_POINT} >/dev/null 2>&1
;;
exfat)
mount -t exfat ${DEVICE} ${MOUNT_POINT} >/dev/null 2>&1
;;
ntfs)
ntfs-3g ${DEVICE} ${MOUNT_POINT}
;;
iso9660)
mount -t iso9660 ${DEVICE} ${MOUNT_POINT}
;;
*)
/bin/rmdir "${MOUNT_POINT}"
exit 0
;;
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

@@ -60,7 +60,7 @@ show() {
}
run_external_script() {
show 0 "run_external_script"
assist.sh
}
update() {
@@ -99,13 +99,13 @@ update() {
target_arch="386"
;;
*armv5*)
target_arch="armv5"
target_arch="arm-5"
;;
*armv6*)
target_arch="armv6"
target_arch="arm-6"
;;
*armv7*)
target_arch="armv7"
target_arch="arm-7"
;;
*)
show 1 "Aborted, unsupported or unknown architecture: $unamem"

View File

@@ -1,12 +1,10 @@
#!/bin/sh
# copy to /oasis/util/shell path
# copy to /casaOS/util/shell path
# chmod 755
log="logger -t usb-mount.sh -s "
${log} "变量:$1 $2"
ACTION=$1
DEVBASE=$2
@@ -18,10 +16,10 @@ MOUNT_POINT=$(mount | grep ${DEVICE} | awk '{ print $3 }')
do_mount() {
if [[ -n ${MOUNT_POINT} ]]; then
if [ -n "${MOUNT_POINT}" ]; then
${log} "Warning: ${DEVICE} is already mounted at ${MOUNT_POINT}"
exit 1
fib
fi
# Get info for this drive: $ID_FS_LABEL and $ID_FS_TYPE
eval $(blkid -o udev ${DEVICE} | grep -i -e "ID_FS_LABEL" -e "ID_FS_TYPE")
@@ -33,7 +31,7 @@ do_mount() {
# Figure out a mount point to use
# LABEL=${ID_FS_LABEL}
LABEL=${DEVBASE}
if grep -q " /media/${LABEL} " /etc/mtab; then
if grep -q " /mnt/casa_${LABEL} " /etc/mtab; then
# Already in use, make a unique one
LABEL+="-${DEVBASE}"
fi
@@ -44,7 +42,7 @@ do_mount() {
DEV_LABEL="${DEVBASE}"
fi
MOUNT_POINT="/media/${DEV_LABEL}"
MOUNT_POINT="/mnt/casa_${DEV_LABEL}"
${log} "Mount point: ${MOUNT_POINT}"

View File

@@ -4,5 +4,5 @@ Description=Mount USB Drive on %i
[Service]
Type=oneshot
RemainAfterExit=true
ExecStart=/oasis/util/shell/usb-mount.sh add %i
ExecStop=/oasis/util/shell/usb-mount.sh remove %i
ExecStart=/casaOS/server/shell/usb-mount.sh add %i
ExecStop=/casaOS/server/shell/usb-mount.sh remove %i

View File

@@ -1,4 +1,4 @@
package types
const CURRENTVERSION = "0.2.2"
const BODY = "<li>ui adjustment</li><li>fixed bugs</li>"
const CURRENTVERSION = "0.2.5"
const BODY = "<li>New App Store</li><li>Fix minor bugs</li>"

View File

@@ -0,0 +1,11 @@
<svg width="72" height="72" viewBox="0 0 72 72" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect opacity="0.32" x="0.435625" y="0.435625" width="71.1288" height="71.1288" rx="7.56437" fill="white" stroke="url(#paint0_linear_812_2050)" stroke-width="0.87125"/>
<path d="M36.0606 22L36.0239 50" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M22 36H50" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<defs>
<linearGradient id="paint0_linear_812_2050" x1="77.6757" y1="64.5405" x2="35.9839" y2="53.5747" gradientUnits="userSpaceOnUse">
<stop stop-color="#CBEFFF" stop-opacity="0.16"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 752 B

View File

@@ -15,7 +15,7 @@
<meta name="msapplication-TileColor" content="#da532c">
<meta name="theme-color" content="#ffffff">
<link rel="icon" href="/ui/favicon.svg" type="image/svg+xml">
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/@mdi/font@6.2.95/css/materialdesignicons.min.css">
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/@mdi/font@6.5.95/css/materialdesignicons.min.css">
<title>
CasaOS
</title>

View File

@@ -214,7 +214,7 @@ eval("module.exports = __webpack_require__.p + \"img/Account.1bc4a418.png\";\n\n
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _Users_jerry_Desktop_CasaOS_CasaOS_UI_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./node_modules/@babel/runtime/helpers/esm/objectSpread2 */ \"./node_modules/@babel/runtime/helpers/esm/objectSpread2.js\");\n/* harmony import */ var vee_validate_dist_rules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! vee-validate/dist/rules */ \"./node_modules/vee-validate/dist/rules.js\");\n/* harmony import */ var vee_validate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! vee-validate */ \"./node_modules/vee-validate/dist/vee-validate.esm.js\");\n\n\n\nObject(vee_validate__WEBPACK_IMPORTED_MODULE_2__[\"extend\"])(\"required\", Object(_Users_jerry_Desktop_CasaOS_CasaOS_UI_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Object(_Users_jerry_Desktop_CasaOS_CasaOS_UI_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_0__[\"default\"])({}, vee_validate_dist_rules__WEBPACK_IMPORTED_MODULE_1__[\"required\"]), {}, {\n message: \"This field is required\"\n}));\nObject(vee_validate__WEBPACK_IMPORTED_MODULE_2__[\"extend\"])(\"email\", Object(_Users_jerry_Desktop_CasaOS_CasaOS_UI_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Object(_Users_jerry_Desktop_CasaOS_CasaOS_UI_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_0__[\"default\"])({}, vee_validate_dist_rules__WEBPACK_IMPORTED_MODULE_1__[\"email\"]), {}, {\n message: \"This field must be a valid email\"\n}));\nObject(vee_validate__WEBPACK_IMPORTED_MODULE_2__[\"extend\"])(\"confirmed\", Object(_Users_jerry_Desktop_CasaOS_CasaOS_UI_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Object(_Users_jerry_Desktop_CasaOS_CasaOS_UI_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_0__[\"default\"])({}, vee_validate_dist_rules__WEBPACK_IMPORTED_MODULE_1__[\"confirmed\"]), {}, {\n message: \"This field confirmation does not match\"\n}));\nObject(vee_validate__WEBPACK_IMPORTED_MODULE_2__[\"extend\"])(\"length\", Object(_Users_jerry_Desktop_CasaOS_CasaOS_UI_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Object(_Users_jerry_Desktop_CasaOS_CasaOS_UI_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_0__[\"default\"])({}, vee_validate_dist_rules__WEBPACK_IMPORTED_MODULE_1__[\"length\"]), {}, {\n message: \"This field must have 2 options\"\n}));\nObject(vee_validate__WEBPACK_IMPORTED_MODULE_2__[\"extend\"])(\"min\", Object(_Users_jerry_Desktop_CasaOS_CasaOS_UI_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Object(_Users_jerry_Desktop_CasaOS_CasaOS_UI_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_0__[\"default\"])({}, vee_validate_dist_rules__WEBPACK_IMPORTED_MODULE_1__[\"min\"]), {}, {\n message: \"This field must have more than {length} characters\"\n}));\n\n//# sourceURL=webpack:///./src/plugins/vee-validate.js?");
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _Users_liangjianli_go_CasaOSNew_CasaOS_UI_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./node_modules/@babel/runtime/helpers/esm/objectSpread2 */ \"./node_modules/@babel/runtime/helpers/esm/objectSpread2.js\");\n/* harmony import */ var vee_validate_dist_rules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! vee-validate/dist/rules */ \"./node_modules/vee-validate/dist/rules.js\");\n/* harmony import */ var vee_validate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! vee-validate */ \"./node_modules/vee-validate/dist/vee-validate.esm.js\");\n\n\n\nObject(vee_validate__WEBPACK_IMPORTED_MODULE_2__[\"extend\"])(\"required\", Object(_Users_liangjianli_go_CasaOSNew_CasaOS_UI_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Object(_Users_liangjianli_go_CasaOSNew_CasaOS_UI_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_0__[\"default\"])({}, vee_validate_dist_rules__WEBPACK_IMPORTED_MODULE_1__[\"required\"]), {}, {\n message: \"This field is required\"\n}));\nObject(vee_validate__WEBPACK_IMPORTED_MODULE_2__[\"extend\"])(\"email\", Object(_Users_liangjianli_go_CasaOSNew_CasaOS_UI_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Object(_Users_liangjianli_go_CasaOSNew_CasaOS_UI_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_0__[\"default\"])({}, vee_validate_dist_rules__WEBPACK_IMPORTED_MODULE_1__[\"email\"]), {}, {\n message: \"This field must be a valid email\"\n}));\nObject(vee_validate__WEBPACK_IMPORTED_MODULE_2__[\"extend\"])(\"confirmed\", Object(_Users_liangjianli_go_CasaOSNew_CasaOS_UI_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Object(_Users_liangjianli_go_CasaOSNew_CasaOS_UI_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_0__[\"default\"])({}, vee_validate_dist_rules__WEBPACK_IMPORTED_MODULE_1__[\"confirmed\"]), {}, {\n message: \"This field confirmation does not match\"\n}));\nObject(vee_validate__WEBPACK_IMPORTED_MODULE_2__[\"extend\"])(\"length\", Object(_Users_liangjianli_go_CasaOSNew_CasaOS_UI_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Object(_Users_liangjianli_go_CasaOSNew_CasaOS_UI_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_0__[\"default\"])({}, vee_validate_dist_rules__WEBPACK_IMPORTED_MODULE_1__[\"length\"]), {}, {\n message: \"This field must have 2 options\"\n}));\nObject(vee_validate__WEBPACK_IMPORTED_MODULE_2__[\"extend\"])(\"min\", Object(_Users_liangjianli_go_CasaOSNew_CasaOS_UI_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Object(_Users_liangjianli_go_CasaOSNew_CasaOS_UI_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_0__[\"default\"])({}, vee_validate_dist_rules__WEBPACK_IMPORTED_MODULE_1__[\"min\"]), {}, {\n message: \"This field must have more than {length} characters\"\n}));\n\n//# sourceURL=webpack:///./src/plugins/vee-validate.js?");
/***/ })

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long