├── model
├── receive
│ └── app.go
├── heart.go
├── notify.go
├── search.go
├── system_app
│ └── sync.go
├── ddns.go
├── user.go
├── version.go
├── share.go
├── notify
│ ├── result.go
│ ├── message.go
│ ├── storage.go
│ ├── application.go
│ └── file.go
├── docker.go
├── system_model
│ └── verify_information.go
├── connections.go
├── category.go
├── zima.go
├── net.go
├── file.go
├── sys_common.go
├── smartctl_model.go
├── disk.go
├── manifest.go
└── app.go
├── shell
├── assist.sh
├── usb-mount@.service
├── 11-usb-mount.rules
├── delete-old-service.sh
└── usb-mount.sh
├── service
├── data_ handling.go
├── app_test.go
├── docker_base
│ ├── model.go
│ ├── common.go
│ └── mysql.go
├── model
│ ├── o_notify.go
│ ├── o_shares.go
│ ├── o_disk.go
│ ├── o_connections.go
│ ├── o_user.go
│ ├── o_rely.go
│ └── o_container.go
├── docker_test.go
├── rely.go
├── file_test.go
├── service.go
├── connections.go
├── user.go
├── file.go
└── shares.go
├── web
├── robots.txt
├── img
│ ├── USB.3ba78dec.png
│ ├── disk.573d4b55.png
│ ├── default.be7833db.png
│ ├── gradient.1b76cb09.png
│ ├── storage.d487ddb6.png
│ ├── icon
│ │ ├── favicon-16x16.png
│ │ ├── favicon-32x32.png
│ │ ├── mstile-150x150.png
│ │ ├── apple-touch-icon.png
│ │ ├── android-chrome-192x192.png
│ │ └── safari-pinned-tab.svg
│ ├── casa-dark.b6d17cf2.svg
│ ├── Files.svg
│ └── CasaConnect.svg
├── static.go
├── browserconfig.xml
├── site.webmanifest
├── js
│ └── 14.js
├── favicon.svg
└── index.html
├── snapshot.png
├── types
├── rely.go
├── friend.go
├── search.go
├── person_download.go
├── task.go
├── notify.go
├── system.go
└── person.go
├── pkg
├── docker
│ ├── emum.go
│ ├── volumes_test.go
│ └── volumes.go
├── utils
│ ├── random
│ │ ├── random_test.go
│ │ └── random.go
│ ├── port
│ │ ├── port_test.go
│ │ └── port.go
│ ├── encryption
│ │ └── md5_helper.go
│ ├── ip_helper
│ │ ├── ip_test.go
│ │ └── ip.go
│ ├── env_helper
│ │ └── env.go
│ ├── network_detection_test.go
│ ├── network_detection.go
│ ├── udev_helper.go
│ ├── version
│ │ └── version.go
│ ├── jwt
│ │ ├── jwt.go
│ │ └── jwt_helper.go
│ ├── file
│ │ ├── block.go
│ │ ├── reader.go
│ │ └── image.go
│ ├── loger
│ │ └── log.go
│ ├── command
│ │ └── command_helper.go
│ ├── httper
│ │ └── httper.go
│ └── common_err
│ │ └── e.go
├── cache
│ └── cache.go
├── ddns
│ └── emum.go
├── sqlite
│ ├── db_test.go
│ └── db.go
├── config
│ ├── config.go
│ ├── update.go
│ └── init.go
├── github
│ └── github.go
├── gredis
│ └── redis.go
├── quic_helper
│ └── config.go
└── samba
│ └── smaba.go
├── snapshot-dark.jpg
├── snapshot-light.jpg
├── snapshot-mobile.png
├── .gitmodules
├── Makefile
├── .github
├── ISSUE_TEMPLATE
│ ├── submit-application.md
│ ├── config.yml
│ ├── feature_request.md
│ ├── bug_report.md
│ ├── alpha_bug_report.yaml
│ └── app_request.yaml
└── workflows
│ ├── move_alpha_bug_to_project.yml
│ ├── add_issues_to_projects.yml
│ ├── push_events_to_discord.yml
│ ├── demo.yml
│ └── casa.yml
├── route
├── darwin.go
├── ui.go
├── v1
│ ├── notify_old.go
│ ├── samba_test.go
│ ├── storage.go
│ └── samba.go
├── socket.go
└── init.go
├── conf
└── conf.conf.sample
├── .gitignore
├── DEVELOPING.md
├── middleware
└── gin.go
├── go.mod
├── main.go
└── .all-contributorsrc
/model/receive/app.go:
--------------------------------------------------------------------------------
1 | package receive
2 |
--------------------------------------------------------------------------------
/shell/assist.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 |
4 |
--------------------------------------------------------------------------------
/service/data_ handling.go:
--------------------------------------------------------------------------------
1 | package service
2 |
--------------------------------------------------------------------------------
/web/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow: /
--------------------------------------------------------------------------------
/snapshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/CasaOS/main/snapshot.png
--------------------------------------------------------------------------------
/types/rely.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | const RELY_TYPE_MYSQL = iota
4 |
--------------------------------------------------------------------------------
/pkg/docker/emum.go:
--------------------------------------------------------------------------------
1 | package docker
2 |
3 | const NETWORKNAME = "oasis"
4 |
--------------------------------------------------------------------------------
/snapshot-dark.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/CasaOS/main/snapshot-dark.jpg
--------------------------------------------------------------------------------
/snapshot-light.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/CasaOS/main/snapshot-light.jpg
--------------------------------------------------------------------------------
/snapshot-mobile.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/CasaOS/main/snapshot-mobile.png
--------------------------------------------------------------------------------
/web/img/USB.3ba78dec.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/CasaOS/main/web/img/USB.3ba78dec.png
--------------------------------------------------------------------------------
/web/img/disk.573d4b55.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/CasaOS/main/web/img/disk.573d4b55.png
--------------------------------------------------------------------------------
/web/img/default.be7833db.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/CasaOS/main/web/img/default.be7833db.png
--------------------------------------------------------------------------------
/web/img/gradient.1b76cb09.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/CasaOS/main/web/img/gradient.1b76cb09.png
--------------------------------------------------------------------------------
/web/img/storage.d487ddb6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/CasaOS/main/web/img/storage.d487ddb6.png
--------------------------------------------------------------------------------
/web/img/icon/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/CasaOS/main/web/img/icon/favicon-16x16.png
--------------------------------------------------------------------------------
/web/img/icon/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/CasaOS/main/web/img/icon/favicon-32x32.png
--------------------------------------------------------------------------------
/web/img/icon/mstile-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/CasaOS/main/web/img/icon/mstile-150x150.png
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "UI"]
2 | path = UI
3 | url = https://github.com/IceWhaleTech/CasaOS-UI.git
4 | branch = main
--------------------------------------------------------------------------------
/web/img/icon/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/CasaOS/main/web/img/icon/apple-touch-icon.png
--------------------------------------------------------------------------------
/web/img/icon/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/CasaOS/main/web/img/icon/android-chrome-192x192.png
--------------------------------------------------------------------------------
/types/friend.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | const (
4 | FRIENDSTATEDEFAULT = iota
5 | FRIENDSTATEWAIT
6 | FRIENDSTATEREQUEST
7 | )
8 |
--------------------------------------------------------------------------------
/model/heart.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | type CasaOSHeart struct {
4 | UuId string `json:"uuid"`
5 | Type string `json:"type"`
6 | }
7 |
--------------------------------------------------------------------------------
/model/notify.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | type NotifyMssage struct {
4 | Type string `json:"type"`
5 | Data string `json:"data"`
6 | }
7 |
--------------------------------------------------------------------------------
/service/app_test.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func TestGetCasaOSCount(t *testing.T) {
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/types/search.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | const (
4 | APPLICATION = iota
5 | MEDIA
6 | PICTURE
7 | MUSIC
8 | SEARCH
9 | UNKNOWN
10 | )
11 |
--------------------------------------------------------------------------------
/model/search.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | type SearchFileInfo struct {
4 | Path string `json:"path"`
5 | Name string `json:"name"`
6 | Type int `json:"type"`
7 | }
8 |
--------------------------------------------------------------------------------
/pkg/docker/volumes_test.go:
--------------------------------------------------------------------------------
1 | package docker
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | )
7 |
8 | func TestGetDir(t *testing.T) {
9 | fmt.Println(GetDir("", "config"))
10 | }
11 |
--------------------------------------------------------------------------------
/web/static.go:
--------------------------------------------------------------------------------
1 | package web
2 |
3 | import "embed"
4 |
5 | //go:embed index.html favicon.svg browserconfig.xml site.webmanifest robots.txt img js fonts css
6 | var Static embed.FS
7 |
--------------------------------------------------------------------------------
/model/system_app/sync.go:
--------------------------------------------------------------------------------
1 | package system_app
2 |
3 | import "encoding/xml"
4 |
5 | type SyncConfig struct {
6 | XMLName xml.Name `xml:"configuration"`
7 | Key string `xml:"gui>apikey"`
8 | }
9 |
--------------------------------------------------------------------------------
/pkg/utils/random/random_test.go:
--------------------------------------------------------------------------------
1 | package random
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | )
7 |
8 | func TestRandomString(t *testing.T) {
9 | fmt.Println(RandomString(6, true))
10 | }
11 |
--------------------------------------------------------------------------------
/types/person_download.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | const (
4 | DOWNLOADAWAIT = iota //default state
5 | DOWNLOADING
6 | DOWNLOADPAUSE
7 | DOWNLOADFINISH
8 | DOWNLOADERROR
9 | DOWNLOADFINISHED
10 | )
11 |
--------------------------------------------------------------------------------
/pkg/cache/cache.go:
--------------------------------------------------------------------------------
1 | package cache
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/patrickmn/go-cache"
7 | )
8 |
9 | func Init() *cache.Cache {
10 | return cache.New(5*time.Minute, 60*time.Second)
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/ddns/emum.go:
--------------------------------------------------------------------------------
1 | package ddns
2 |
3 | const (
4 | GOGADDY = iota
5 | GOOGLE
6 | )
7 |
8 | const (
9 | A = "A"
10 | AAAA = "AAAA"
11 | )
12 |
13 | const (
14 | GODADDYAPIURL = "https://api.godaddy.com"
15 | )
16 |
--------------------------------------------------------------------------------
/model/ddns.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | type GoDaddyModel struct {
4 | Type uint `json:"type"`
5 | ApiHost string `json:"api_host"`
6 | Key string `json:"key"`
7 | Secret string `json:"secret"`
8 | Host string `json:"host"`
9 | }
10 |
--------------------------------------------------------------------------------
/model/user.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | type UserInfo struct {
4 | NickName string `json:"nick_name"`
5 | Desc string `json:"desc"`
6 | ShareId string `json:"share_id"`
7 | Avatar string `json:"avatar"`
8 | Version int `json:"version,omitempty"`
9 | }
10 |
--------------------------------------------------------------------------------
/pkg/docker/volumes.go:
--------------------------------------------------------------------------------
1 | package docker
2 |
3 | import "strings"
4 |
5 | func GetDir(id, envName string) string {
6 |
7 | if strings.Contains(envName, "$AppID") && len(id) > 0 {
8 | return strings.ReplaceAll(envName, "$AppID", id)
9 | }
10 | return envName
11 | }
12 |
--------------------------------------------------------------------------------
/shell/usb-mount@.service:
--------------------------------------------------------------------------------
1 | # copy to /etc/systemd/system path
2 | [Unit]
3 | Description=Mount USB Drive on %i
4 | [Service]
5 | Type=oneshot
6 | RemainAfterExit=true
7 | ExecStart=/casaOS/server/shell/usb-mount.sh add %i
8 | ExecStop=/casaOS/server/shell/usb-mount.sh remove %i
9 |
--------------------------------------------------------------------------------
/types/task.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | const (
4 | TASK_TYPE_USER = iota
5 | TASK_TYPE_APP
6 | )
7 |
8 | const (
9 | TASK_DATA_TYPE_LINK = iota
10 | TASK_DATA_TYPE_TEXT
11 | )
12 |
13 | const (
14 | TASK_STATE_UNCOMPLETE = iota
15 | TASK_STATE_COMPLETED
16 | )
17 |
--------------------------------------------------------------------------------
/shell/11-usb-mount.rules:
--------------------------------------------------------------------------------
1 |
2 | # copy to /etc/udev/rules.d path
3 |
4 | KERNEL=="sd[a-z]*[0-9]", SUBSYSTEMS=="usb", ACTION=="add", RUN+="/bin/systemctl start usb-mount@%k.service"
5 |
6 | KERNEL=="sd[a-z]*[0-9]", SUBSYSTEMS=="usb", ACTION=="remove", RUN+="/bin/systemctl stop usb-mount@%k.service"
--------------------------------------------------------------------------------
/pkg/sqlite/db_test.go:
--------------------------------------------------------------------------------
1 | package sqlite
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func TestGetDb(t *testing.T) {
8 | // fmt.Println(GetDb())
9 | // db:=GetDb()
10 | // d:=model.DDNSTypeDBModel{
11 | // Name: "test",
12 | // ApiHost: "http://www.google.com",
13 | // }
14 | // db.Create(&d)
15 | }
16 |
--------------------------------------------------------------------------------
/web/browserconfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | #da532c
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY:build build-ui build-backend help
2 |
3 | build: build-ui build-backend
4 |
5 |
6 | build-ui:
7 | cd CasaOS-UI && yarn install && yarn build
8 |
9 | build-backend:
10 | export CGO_ENABLED=1;export CGO_LDFLAGS=-static;go build -o ./casa main.go;upx --lzma --best casa
11 |
12 | help:
13 | @echo "call john"
14 |
--------------------------------------------------------------------------------
/model/version.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import "time"
4 |
5 | type Version struct {
6 | Id uint `gorm:"column:id;primary_key" json:"id"`
7 | ChangeLog string `json:"change_log"`
8 | Version string `json:"version"`
9 | CreatedAt time.Time `json:"created_at"`
10 | UpdatedAt time.Time `json:"updated_at"`
11 | }
12 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/submit-application.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Submit application
3 | about: Add an app to this project
4 | title: ''
5 | labels: ''
6 | assignees: LinkLeong
7 |
8 | ---
9 |
10 | Tested platform
11 | e.g. linux/amd64,linux/arm-v7,linux-arm64
12 |
13 |
14 |
15 | Please export and upload the configuration file of this application
16 |
--------------------------------------------------------------------------------
/service/docker_base/model.go:
--------------------------------------------------------------------------------
1 | package docker_base
2 |
3 | type MysqlConfig struct {
4 | DataBaseHost string `json:"database_host"`
5 | DataBasePort string `json:"database_port"`
6 | DataBaseUser string `json:"database_user"`
7 | DataBasePassword string `json:"data_base_password"`
8 | DataBaseDB string `json:"data_base_db"`
9 | }
10 |
--------------------------------------------------------------------------------
/web/site.webmanifest:
--------------------------------------------------------------------------------
1 | {
2 | "name": "",
3 | "short_name": "",
4 | "icons": [
5 | {
6 | "src": "/ui/img/icon/android-chrome-192x192.png",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | }
10 | ],
11 | "theme_color": "#ffffff",
12 | "background_color": "#ffffff",
13 | "display": "standalone"
14 | }
15 |
--------------------------------------------------------------------------------
/types/notify.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | const (
4 | NOTIFY_DYNAMICE = iota
5 | NOTIFY_UNREAD
6 | NOTIFY_READ
7 | )
8 | const (
9 | NOTIFY_TYPE_UNIMPORTANT = iota + 1
10 | NOTIFY_TYPE_NEED_CONFIRM
11 | NOTIFY_TYPE_ERROR
12 | NOTIFY_TYPE_INSTALL_LOG
13 | NOTIFY_TYPE_PERSION_FIRNED_LEAVE
14 | NOTIFY_TYPE_PERSION_FIRNED_LIVE
15 | NOTIFY_TYPE_HEALTH_CHECK
16 | )
17 |
18 | const (
19 | NOTIFY_APP = iota
20 | )
21 |
--------------------------------------------------------------------------------
/types/system.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: LinkLeong link@icewhale.com
3 | * @Date: 2022-02-17 18:53:22
4 | * @LastEditors: LinkLeong
5 | * @LastEditTime: 2022-08-25 10:49:46
6 | * @FilePath: /CasaOS/types/system.go
7 | * @Description:
8 | * @Website: https://www.casaos.io
9 | * Copyright (c) 2022 by icewhale, All Rights Reserved.
10 | */
11 | package types
12 |
13 | const CURRENTVERSION = "0.3.5.1"
14 |
15 | const BODY = " "
16 |
--------------------------------------------------------------------------------
/pkg/config/config.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: LinkLeong link@icewhale.com
3 | * @Date: 2021-09-30 18:18:14
4 | * @LastEditors: LinkLeong
5 | * @LastEditTime: 2022-06-21 11:09:30
6 | * @FilePath: /CasaOS/pkg/config/config.go
7 | * @Description:
8 | * @Website: https://www.casaos.io
9 | * Copyright (c) 2022 by icewhale, All Rights Reserved.
10 | */
11 | package config
12 |
13 | const (
14 | USERCONFIGURL = "/etc/casaos.conf"
15 | )
16 |
--------------------------------------------------------------------------------
/pkg/utils/port/port_test.go:
--------------------------------------------------------------------------------
1 | package port
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | )
7 |
8 | func TestPortAvailable(t *testing.T) {
9 | // fmt.Println(PortAvailable())
10 | //fmt.Println(IsPortAvailable(6881,"tcp"))
11 | p, _ := GetAvailablePort("udp")
12 | fmt.Println("udp", p)
13 | fmt.Println(IsPortAvailable(p, "udp"))
14 |
15 | t1, _ := GetAvailablePort("tcp")
16 | fmt.Println("tcp", t1)
17 | fmt.Println(IsPortAvailable(t1, "tcp"))
18 | }
19 |
--------------------------------------------------------------------------------
/model/share.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: LinkLeong link@icewhale.org
3 | * @Date: 2022-07-26 11:12:12
4 | * @LastEditors: LinkLeong
5 | * @LastEditTime: 2022-07-27 14:58:55
6 | * @FilePath: /CasaOS/model/share.go
7 | * @Description:
8 | * @Website: https://www.casaos.io
9 | * Copyright (c) 2022 by icewhale, All Rights Reserved.
10 | */
11 | package model
12 |
13 | type Shares struct {
14 | ID uint `json:"id"`
15 | Anonymous bool `json:"anonymous"`
16 | Path string `json:"path"`
17 | }
18 |
--------------------------------------------------------------------------------
/web/js/14.js:
--------------------------------------------------------------------------------
1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[14],{"./node_modules/vue-plyr/dist/vue-plyr.css":
2 | /*!*************************************************!*\
3 | !*** ./node_modules/vue-plyr/dist/vue-plyr.css ***!
4 | \*************************************************/
5 | /*! no static exports found */function(module,exports,__webpack_require__){eval("// extracted by mini-css-extract-plugin\n if(false) { var cssReload; }\n \n\n//# sourceURL=webpack:///./node_modules/vue-plyr/dist/vue-plyr.css?")}}]);
--------------------------------------------------------------------------------
/model/notify/result.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: LinkLeong link@icewhale.com
3 | * @Date: 2022-05-26 14:21:11
4 | * @LastEditors: LinkLeong
5 | * @LastEditTime: 2022-05-27 11:15:59
6 | * @FilePath: /CasaOS/model/notify/result.go
7 | * @Description:
8 | * @Website: https://www.casaos.io
9 | * Copyright (c) 2022 by icewhale, All Rights Reserved.
10 | */
11 |
12 | package notify
13 |
14 | // Notify struct for Notify
15 | type NotifyModel struct {
16 | Data interface{} `json:"data"`
17 | State string `json:"state"`
18 | }
19 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 | contact_links:
3 | - name: Feature/Enhancement Ideas
4 | url: https://github.com/IceWhaleTech/CasaOS/discussions/164
5 | about: Have an idea for a new feature/enhancement?
6 | - name: Questions, Discussions
7 | url: https://github.com/IceWhaleTech/CasaOS/discussions
8 | about: Ask questions, propose ideas, or discuss anything related to CasaOS
9 | - name: Discord
10 | url: https://discord.gg/knqAbbBbeX
11 | about: Get help or share great ideas on Discord!
--------------------------------------------------------------------------------
/model/notify/message.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: LinkLeong link@icewhale.com
3 | * @Date: 2022-05-26 14:39:22
4 | * @LastEditors: LinkLeong
5 | * @LastEditTime: 2022-05-26 19:08:52
6 | * @FilePath: /CasaOS/model/notify/message.go
7 | * @Description:
8 | * @Website: https://www.casaos.io
9 | * Copyright (c) 2022 by icewhale, All Rights Reserved.
10 | */
11 | package notify
12 |
13 | import (
14 | f "github.com/ambelovsky/gosf"
15 | )
16 |
17 | type Message struct {
18 | Path string `json:"path"`
19 | Msg f.Message `json:"msg"`
20 | }
21 |
--------------------------------------------------------------------------------
/route/darwin.go:
--------------------------------------------------------------------------------
1 | //go:build darwin
2 | // +build darwin
3 |
4 | /*
5 | * @Author: LinkLeong link@icewhale.org
6 | * @Date: 2022-08-12 14:22:28
7 | * @LastEditors: LinkLeong
8 | * @LastEditTime: 2022-08-12 18:41:14
9 | * @FilePath: /CasaOS/route/darwin.go
10 | * @Description:
11 | * @Website: https://www.casaos.io
12 | * Copyright (c) 2022 by icewhale, All Rights Reserved.
13 | */
14 |
15 | package route
16 |
17 | func MonitoryUSB() {
18 |
19 | }
20 | func SendAllHardwareStatusBySocket() {
21 |
22 | }
23 | func SendUSBBySocket() {
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/model/docker.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: LinkLeong link@icewhale.com
3 | * @Date: 2021-12-08 18:10:25
4 | * @LastEditors: LinkLeong
5 | * @LastEditTime: 2022-07-13 10:49:16
6 | * @FilePath: /CasaOS/model/docker.go
7 | * @Description:
8 | * @Website: https://www.casaos.io
9 | * Copyright (c) 2022 by icewhale, All Rights Reserved.
10 | */
11 | package model
12 |
13 | type DockerStatsModel struct {
14 | Icon string `json:"icon"`
15 | Title string `json:"title"`
16 | Data interface{} `json:"data"`
17 | Previous interface{} `json:"previous"`
18 | }
19 |
--------------------------------------------------------------------------------
/pkg/utils/random/random.go:
--------------------------------------------------------------------------------
1 | package random
2 |
3 | import (
4 | "math/rand"
5 | "time"
6 | )
7 |
8 | func RandomString(n int, onlyLetter bool) string {
9 |
10 | var letters []rune
11 |
12 | if onlyLetter {
13 | letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
14 | } else {
15 | letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
16 | }
17 |
18 | b := make([]rune, n)
19 | rand.Seed(time.Now().UnixNano())
20 | for i := range b {
21 | b[i] = letters[rand.Intn(len(letters))]
22 | }
23 | return string(b)
24 | }
25 |
--------------------------------------------------------------------------------
/model/system_model/verify_information.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: LinkLeong link@icewhale.com
3 | * @Date: 2022-06-15 11:30:47
4 | * @LastEditors: LinkLeong
5 | * @LastEditTime: 2022-06-23 18:40:40
6 | * @FilePath: /CasaOS/model/system_model/verify_information.go
7 | * @Description:
8 | * @Website: https://www.casaos.io
9 | * Copyright (c) 2022 by icewhale, All Rights Reserved.
10 | */
11 | package system_model
12 |
13 | type VerifyInformation struct {
14 | RefreshToken string `json:"refresh_token"`
15 | AccessToken string `json:"access_token"`
16 | ExpiresAt int64 `json:"expires_at"`
17 | }
18 |
--------------------------------------------------------------------------------
/pkg/github/github.go:
--------------------------------------------------------------------------------
1 | package github
2 |
3 | import (
4 | "context"
5 | "github.com/google/go-github/v36/github"
6 | "golang.org/x/oauth2"
7 | )
8 |
9 | func GetGithubClient() *github.Client {
10 | ctx := context.Background()
11 | ts := oauth2.StaticTokenSource(
12 | &oauth2.Token{AccessToken: ""},
13 | )
14 | tc := oauth2.NewClient(ctx, ts)
15 | client := github.NewClient(tc)
16 | return client
17 |
18 | // list all repositories for the authenticated user
19 | //repos, _, err := client.Repositories.List(ctx, "", nil)
20 |
21 | //fmt.Print(err)
22 | //fmt.Print(repos)
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/service/model/o_notify.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | type AppNotify struct {
4 | State int `json:"state"` //0:一直在变动的未读消息 1:未读 2:已读
5 | Message string `json:"message"`
6 | CreatedAt string `json:"created_at"`
7 | UpdatedAt string `json:"updated_at"`
8 | Id string `json:"id"`
9 | Type int `json:"type"`
10 | Icon string `json:"icon"`
11 | Name string `json:"name"`
12 | Class int `json:"class"`
13 | CustomId string `gorm:"column:custom_id;primary_key" json:"custom_id"`
14 | }
15 |
16 | func (p *AppNotify) TableName() string {
17 | return "o_notify"
18 | }
19 |
--------------------------------------------------------------------------------
/pkg/utils/encryption/md5_helper.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: LinkLeong link@icewhale.com
3 | * @Date: 2022-06-14 14:33:25
4 | * @LastEditors: LinkLeong
5 | * @LastEditTime: 2022-06-14 14:33:49
6 | * @FilePath: /CasaOS/pkg/utils/encryption/md5_helper.go
7 | * @Description:
8 | * @Website: https://www.casaos.io
9 | * Copyright (c) 2022 by icewhale, All Rights Reserved.
10 | */
11 | package encryption
12 |
13 | import (
14 | "crypto/md5"
15 | "encoding/hex"
16 | )
17 |
18 | func GetMD5ByStr(str string) string {
19 | h := md5.New()
20 | h.Write([]byte(str))
21 | return hex.EncodeToString(h.Sum(nil))
22 | }
23 |
--------------------------------------------------------------------------------
/model/notify/storage.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: LinkLeong link@icewhale.com
3 | * @Date: 2022-07-15 10:43:00
4 | * @LastEditors: LinkLeong
5 | * @LastEditTime: 2022-07-15 10:56:17
6 | * @FilePath: /CasaOS/model/notify/storage.go
7 | * @Description:
8 | * @Website: https://www.casaos.io
9 | * Copyright (c) 2022 by icewhale, All Rights Reserved.
10 | */
11 | package notify
12 |
13 | type StorageMessage struct {
14 | Type string `json:"type"` //sata,usb
15 | Action string `json:"action"` //remove add
16 | Path string `json:"path"`
17 | Volume string `json:"volume"`
18 | Size uint64 `json:"size"`
19 | }
20 |
--------------------------------------------------------------------------------
/conf/conf.conf.sample:
--------------------------------------------------------------------------------
1 | [app]
2 | PAGE_SIZE = 10
3 | RuntimeRootPath = runtime/
4 | LogPath = /var/log/casaos/
5 | LogSaveName = log
6 | LogFileExt = log
7 | DateStrFormat = 20060102
8 | DateTimeFormat = 2006-01-02 15:04:05
9 | TimeFormat = 15:04:05
10 | DateFormat = 2006-01-02
11 | DBPath = /var/lib/casaos
12 | ShellPath = /usr/share/casaos/shell
13 | UserDataPath = /var/lib/casaos/conf
14 | TempPath = /var/lib/casaos/temp
15 |
16 | [server]
17 | HttpPort = 80
18 | RunMode = release
19 | ServerApi = https://api.casaos.io/casaos-api
20 | Handshake = socket.casaos.io
21 | Token =
22 | USBAutoMount =
23 |
24 | [system]
--------------------------------------------------------------------------------
/pkg/utils/ip_helper/ip_test.go:
--------------------------------------------------------------------------------
1 | package ip_helper
2 |
3 | import (
4 | "fmt"
5 | "net"
6 | "testing"
7 | )
8 |
9 | func TestGetExternalIPV4(t *testing.T) {
10 | ipv4 := make(chan string)
11 | go func() { ipv4 <- GetExternalIPV4() }()
12 | fmt.Println(<-ipv4)
13 | }
14 | func TestGetExternalIPV6(t *testing.T) {
15 | ipv6 := make(chan string)
16 | go func() { ipv6 <- GetExternalIPV6() }()
17 | fmt.Println(<-ipv6)
18 |
19 | }
20 |
21 | func TestGetLoclIp(t *testing.T) {
22 | fmt.Println(GetLoclIp())
23 | }
24 | func TestHasLocalIP(t *testing.T) {
25 | fmt.Println("dddd")
26 | fmt.Println(HasLocalIP(net.ParseIP("192.168.2.10")))
27 | }
28 |
--------------------------------------------------------------------------------
/model/connections.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: LinkLeong link@icewhale.org
3 | * @Date: 2022-07-27 10:30:43
4 | * @LastEditors: LinkLeong
5 | * @LastEditTime: 2022-08-04 20:06:04
6 | * @FilePath: /CasaOS/model/connections.go
7 | * @Description:
8 | * @Website: https://www.casaos.io
9 | * Copyright (c) 2022 by icewhale, All Rights Reserved.
10 | */
11 | package model
12 |
13 | type Connections struct {
14 | ID uint `json:"id"`
15 | Username string `json:"username"`
16 | Password string `json:"password,omitempty"`
17 | Host string `json:"host"`
18 | Port string `json:"port"`
19 | MountPoint string `json:"mount_point"`
20 | }
21 |
--------------------------------------------------------------------------------
/model/notify/application.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: LinkLeong link@icewhale.com
3 | * @Date: 2022-05-27 15:01:58
4 | * @LastEditors: LinkLeong
5 | * @LastEditTime: 2022-05-31 14:51:21
6 | * @FilePath: /CasaOS/model/notify/application.go
7 | * @Description:
8 | * @Website: https://www.casaos.io
9 | * Copyright (c) 2022 by icewhale, All Rights Reserved.
10 | */
11 | package notify
12 |
13 | type Application struct {
14 | Name string `json:"name"`
15 | State string `json:"state"`
16 | Type string `json:"type"`
17 | Icon string `json:"icon"`
18 | Message string `json:"message"`
19 | Finished bool `json:"finished"`
20 | Success bool `json:"success"`
21 | }
22 |
--------------------------------------------------------------------------------
/pkg/utils/env_helper/env.go:
--------------------------------------------------------------------------------
1 | package env_helper
2 |
3 | import "strings"
4 |
5 | func ReplaceDefaultENV(key, tz string) string {
6 | temp := ""
7 | switch key {
8 | case "$DefaultPassword":
9 | temp = "casaos"
10 | case "$DefaultUserName":
11 | temp = "admin"
12 |
13 | case "$PUID":
14 | temp = "1000"
15 | case "$PGID":
16 | temp = "1000"
17 | case "$TZ":
18 | temp = tz
19 | }
20 | return temp
21 | }
22 |
23 | //replace env default setting
24 | func ReplaceStringDefaultENV(str string) string {
25 | return strings.ReplaceAll(strings.ReplaceAll(str, "$DefaultPassword", ReplaceDefaultENV("$DefaultPassword", "")), "$DefaultUserName", ReplaceDefaultENV("$DefaultUserName", ""))
26 | }
27 |
--------------------------------------------------------------------------------
/route/ui.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: LinkLeong link@icewhale.com
3 | * @Date: 2022-06-23 17:27:43
4 | * @LastEditors: LinkLeong
5 | * @LastEditTime: 2022-06-23 17:27:48
6 | * @FilePath: /CasaOS/route/ui.go
7 | * @Description:
8 | * @Website: https://www.casaos.io
9 | * Copyright (c) 2022 by icewhale, All Rights Reserved.
10 | */
11 | package route
12 |
13 | import (
14 | "html/template"
15 |
16 | "github.com/IceWhaleTech/CasaOS/web"
17 | "github.com/gin-gonic/gin"
18 | )
19 |
20 | func WebUIHome(c *gin.Context) {
21 | c.Writer.Header().Set("Content-Type", "text/html; charset=utf-8")
22 | index, _ := template.ParseFS(web.Static, "index.html")
23 | index.Execute(c.Writer, nil)
24 | return
25 | }
26 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/service/docker_base/common.go:
--------------------------------------------------------------------------------
1 | package docker_base
2 |
3 | import "github.com/IceWhaleTech/CasaOS/model"
4 |
5 | //过滤mysql关键字
6 | func MysqlFilter(c MysqlConfig, envs model.EnvArray) model.EnvArray {
7 | for i := 0; i < len(envs); i++ {
8 | switch envs[i].Value {
9 | case "$MYSQL_HOST":
10 | envs[i].Value = c.DataBaseHost
11 | case "$MYSQL_PORT":
12 | envs[i].Value = c.DataBasePort
13 | case "$MYSQL_USERNAME":
14 | envs[i].Value = c.DataBaseUser
15 | case "$MYSQL_PASSWORD":
16 | envs[i].Value = c.DataBasePassword
17 | case "$MYSQL_DBNAME":
18 | envs[i].Value = c.DataBaseDB
19 | case "$MYSQL_HOST_AND_PORT":
20 | envs[i].Value = c.DataBaseHost + ":" + c.DataBasePort
21 | }
22 | }
23 | return envs
24 | }
25 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ### Go template
2 | # Binaries for programs and plugins
3 | *.exe
4 | *.exe~
5 | *.dll
6 | *.so
7 | *.dylib
8 |
9 | # Test binary, built with `go test -c`
10 | *.test
11 |
12 | # Output of the go coverage tool, specifically when used with LiteIDE
13 | *.out
14 |
15 | # Dependency directories (remove the comment below to include it)
16 | # vendor/
17 |
18 | ### Example user template template
19 | ### Example user template
20 |
21 | # IntelliJ project files
22 | .idea
23 | .vscode
24 | *.iml
25 | out
26 | gen
27 | /logs/
28 | /sql/
29 | /out/
30 | /db/
31 | /docs/
32 | /web/
33 | /conf/conf.ini
34 | /conf/conf.conf
35 | /conf/conf.json
36 | __debug_bin
37 | main
38 | CasaOS
39 | github.com
40 | .all-contributorsrc
41 | build
42 | dist
43 | .goreleaser.yaml
--------------------------------------------------------------------------------
/model/notify/file.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: LinkLeong link@icewhale.com
3 | * @Date: 2022-05-26 14:21:57
4 | * @LastEditors: LinkLeong
5 | * @LastEditTime: 2022-06-02 11:14:15
6 | * @FilePath: /CasaOS/model/notify/file.go
7 | * @Description:
8 | * @Website: https://www.casaos.io
9 | * Copyright (c) 2022 by icewhale, All Rights Reserved.
10 | */
11 | package notify
12 |
13 | type File struct {
14 | Finished bool `json:"finished"`
15 | ProcessedSize int64 `json:"processed_size"`
16 | ProcessingPath string `json:"processing_path"`
17 | Status string `json:"status"`
18 | TotalSize int64 `json:"total_size"`
19 | Id string `json:"id"`
20 | To string `json:"to"`
21 | Type string `json:"type"`
22 | }
23 |
--------------------------------------------------------------------------------
/web/img/casa-dark.b6d17cf2.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/service/model/o_shares.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: LinkLeong link@icewhale.org
3 | * @Date: 2022-07-26 11:17:17
4 | * @LastEditors: LinkLeong
5 | * @LastEditTime: 2022-07-27 15:25:07
6 | * @FilePath: /CasaOS/service/model/o_shares.go
7 | * @Description:
8 | * @Website: https://www.casaos.io
9 | * Copyright (c) 2022 by icewhale, All Rights Reserved.
10 | */
11 | package model
12 |
13 | type SharesDBModel struct {
14 | ID uint `gorm:"column:id;primary_key" json:"id"`
15 | Anonymous bool `json:"anonymous"`
16 | Path string `json:"path"`
17 | Name string `json:"name"`
18 | Updated int64 `gorm:"autoUpdateTime"`
19 | Created int64 `gorm:"autoCreateTime"`
20 | }
21 |
22 | func (p *SharesDBModel) TableName() string {
23 | return "o_shares"
24 | }
25 |
--------------------------------------------------------------------------------
/model/category.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: link a624669980@163.com
3 | * @Date: 2022-05-16 17:37:08
4 | * @LastEditors: LinkLeong
5 | * @LastEditTime: 2022-07-13 10:46:38
6 | * @FilePath: /CasaOS/model/category.go
7 | * @Description:
8 | */
9 | package model
10 |
11 | type ServerCategoryList struct {
12 | Version string `json:"version"`
13 | Item []CategoryList `json:"item"`
14 | }
15 | type CategoryList struct {
16 | Id uint `gorm:"column:id;primary_key" json:"id"`
17 | //CreatedAt time.Time `json:"created_at"`
18 | //
19 | //UpdatedAt time.Time `json:"updated_at"`
20 | Font string `json:"font"` // @tiger - 如果这个和前端有关,应该不属于后端的出参范围,而是前端去界定
21 | Name string `json:"name"`
22 | Count uint `json:"count"` // @tiger - count 属于动态信息,应该单独放在一个出参结构中(原因见另外一个关于 静态/动态 出参的注释)
23 | }
24 |
--------------------------------------------------------------------------------
/types/person.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | const PERSONADDFRIEND = "add_user"
4 | const PERSONAGREEFRIEND = "agree_user"
5 | const PERSONDOWNLOAD = "file_data"
6 | const PERSONSUMMARY = "summary"
7 | const PERSONGETIP = "get_ip"
8 | const PERSONCONNECTION = "connection"
9 | const PERSONDIRECTORY = "directory"
10 | const PERSONHELLO = "hello"
11 | const PERSONSHAREID = "share_id"
12 | const PERSONUPLOAD = "upload"
13 | const PERSONUPLOADDATA = "upload_data"
14 | const PERSONINTERNALINSPECTION = "internal_inspection"
15 | const PERSONPING = "ping"
16 | const PERSONIMAGETHUMBNAIL = "image_thumbnail"
17 |
18 | const PERSONCANCEL = "cancel" // Cancel Download
19 |
20 | const (
21 | PERSONFILEDOWNLOAD = iota //default state
22 | PERSONFILEUPLOAD
23 | PERSONFILERECEIVEUPLOAD //receive upload file
24 | )
25 |
--------------------------------------------------------------------------------
/service/model/o_disk.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: LinkLeong link@icewhale.org
3 | * @Date: 2021-12-07 17:14:41
4 | * @LastEditors: LinkLeong
5 | * @LastEditTime: 2022-08-17 18:46:43
6 | * @FilePath: /CasaOS/service/model/o_disk.go
7 | * @Description:
8 | * @Website: https://www.casaos.io
9 | * Copyright (c) 2022 by icewhale, All Rights Reserved.
10 | */
11 | package model
12 |
13 | //SerialAdvanced Technology Attachment (STAT)
14 | type SerialDisk struct {
15 | Id uint `gorm:"column:id;primary_key" json:"id"`
16 | UUID string `json:"uuid"`
17 | Path string `json:"path"`
18 | State int `json:"state"`
19 | MountPoint string `json:"mount_point"`
20 | CreatedAt int64 `json:"created_at"`
21 | }
22 |
23 | func (p *SerialDisk) TableName() string {
24 | return "o_disk"
25 | }
26 |
--------------------------------------------------------------------------------
/pkg/utils/network_detection_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: LinkLeong a624669980@163.com
3 | * @Date: 2022-05-08 15:07:31
4 | * @LastEditors: LinkLeong a624669980@163.com
5 | * @LastEditTime: 2022-05-09 11:43:30
6 | * @FilePath: /CasaOS/pkg/utils/network_detection_test.go
7 | * @Description:
8 | *
9 | * Copyright (c) 2022 by LinkLeong a624669980@163.com, All Rights Reserved.
10 | */
11 |
12 | package utils
13 |
14 | import (
15 | "fmt"
16 | "testing"
17 | )
18 |
19 | func TestGetResultTest(t *testing.T) {
20 | list := []string{"https://www.google.com", "https://www.bing.com", "https://www.baidu.com"}
21 | data := make(chan string)
22 | //data <- "init"
23 | for _, v := range list {
24 | go GetNetWorkTypeDetection(data, v)
25 | }
26 | result := <-data
27 | close(data)
28 | fmt.Println(result)
29 | }
30 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: '[Bug] '
5 | labels: 'bug'
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Additional context**
32 | Add any other context about the problem here.
33 |
--------------------------------------------------------------------------------
/pkg/gredis/redis.go:
--------------------------------------------------------------------------------
1 | package gredis
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/IceWhaleTech/CasaOS/model"
7 | "github.com/gomodule/redigo/redis"
8 | )
9 |
10 | func GetRedisConn(m *model.RedisModel) *redis.Pool {
11 | redisConn := &redis.Pool{
12 | MaxIdle: m.MaxIdle,
13 | MaxActive: m.MaxActive,
14 | IdleTimeout: m.IdleTimeout * time.Second,
15 | Dial: func() (redis.Conn, error) {
16 | c, err := redis.Dial("tcp", m.Host)
17 | if err != nil {
18 | return nil, err
19 | }
20 | if m.Password != "" {
21 | if _, err := c.Do("AUTH", m.Password); err != nil {
22 | c.Close()
23 | return nil, err
24 | }
25 | }
26 | return c, err
27 | },
28 | TestOnBorrow: func(c redis.Conn, t time.Time) error {
29 | _, err := c.Do("PING")
30 | return err
31 | },
32 | }
33 | return redisConn
34 | }
35 |
--------------------------------------------------------------------------------
/pkg/utils/network_detection.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: LinkLeong a624669980@163.com
3 | * @Date: 2022-05-08 14:58:46
4 | * @LastEditors: LinkLeong a624669980@163.com
5 | * @LastEditTime: 2022-05-09 13:42:26
6 | * @FilePath: /CasaOS/pkg/utils/network_detection.go
7 | * @Description:
8 | *
9 | * Copyright (c) 2022 by LinkLeong a624669980@163.com, All Rights Reserved.
10 | */
11 | package utils
12 |
13 | import natType "github.com/Curtis-Milo/nat-type-identifier-go"
14 |
15 | /**
16 | * @description:
17 | * @param {chanstring} data
18 | * @param {string} url
19 | * @return {*}
20 | */
21 | func GetNetWorkTypeDetection(data chan string, url string) {
22 | // fmt.Println("url:", url)
23 | // httper.Get(url, nil)
24 | // aaa <- url
25 | result, err := natType.GetDeterminedNatType(true, 5, url)
26 | if err == nil {
27 | data <- result
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/.github/workflows/move_alpha_bug_to_project.yml:
--------------------------------------------------------------------------------
1 | # This is a basic workflow to help you get started with Actions
2 |
3 | name: Move alpha bug to project
4 |
5 | on:
6 | issues:
7 | types:
8 | - opened
9 |
10 | jobs:
11 | track_issue:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - name: Generate token
15 | id: generate_token
16 | uses: tibdex/github-app-token@36464acb844fc53b9b8b2401da68844f6b05ebb0
17 | with:
18 | app_id: ${{ secrets.ALPHA_BOT_ID }}
19 | private_key: ${{ secrets.ALPHA_BOT_PEM }}
20 |
21 | - name: Add Issue To GitHub Projects Beta
22 | uses: actions/add-to-project@v0.1.0
23 | with:
24 | project-url: https://github.com/orgs/IceWhaleTech/projects/5
25 | github-token: ${{ steps.generate_token.outputs.token }}
26 | labeled: alpha, bug
27 | label-operator: AND
28 |
--------------------------------------------------------------------------------
/web/favicon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
15 |
--------------------------------------------------------------------------------
/model/zima.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: LinkLeong link@icewhale.org
3 | * @Date: 2022-05-13 18:15:46
4 | * @LastEditors: LinkLeong
5 | * @LastEditTime: 2022-08-01 18:32:57
6 | * @FilePath: /CasaOS/model/zima.go
7 | * @Description:
8 | * @Website: https://www.casaos.io
9 | * Copyright (c) 2022 by icewhale, All Rights Reserved.
10 | */
11 | package model
12 |
13 | import "time"
14 |
15 | type Path struct {
16 | Name string `json:"name"` //File name or document name
17 | Path string `json:"path"` //Full path to file or folder
18 | IsDir bool `json:"is_dir"` //Is it a folder
19 | Date time.Time `json:"date"`
20 | Size int64 `json:"size"` //File Size
21 | Type string `json:"type,omitempty"`
22 | Label string `json:"label,omitempty"`
23 | Write bool `json:"write"`
24 | Extensions map[string]interface{} `json:"extensions"`
25 | }
26 |
--------------------------------------------------------------------------------
/pkg/utils/udev_helper.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: LinkLeong link@icewhale.org
3 | * @Date: 2022-08-10 16:06:12
4 | * @LastEditors: LinkLeong
5 | * @LastEditTime: 2022-08-10 16:11:37
6 | * @FilePath: /CasaOS/pkg/utils/udev_helper.go
7 | * @Description:
8 | * @Website: https://www.casaos.io
9 | * Copyright (c) 2022 by icewhale, All Rights Reserved.
10 | */
11 | package utils
12 |
13 | // func getOptionnalMatcher() (matcher netlink.Matcher, err error) {
14 | // if filePath == nil || *filePath == "" {
15 | // return nil, nil
16 | // }
17 |
18 | // stream, err := ioutil.ReadFile(*filePath)
19 | // if err != nil {
20 | // return nil, err
21 | // }
22 |
23 | // if stream == nil {
24 | // return nil, fmt.Errorf("Empty, no rules provided in \"%s\", err: %w", *filePath, err)
25 | // }
26 |
27 | // var rules netlink.RuleDefinitions
28 | // if err := json.Unmarshal(stream, &rules); err != nil {
29 | // return nil, fmt.Errorf("Wrong rule syntax, err: %w", err)
30 | // }
31 |
32 | // return &rules, nil
33 | // }
34 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/alpha_bug_report.yaml:
--------------------------------------------------------------------------------
1 | name: "[Alpha Only] Bug Report"
2 | description: CasaOS Alpha Testing specific bug report form.
3 | title: "[Alpha][Bug] "
4 | labels: ["alpha", "bug"]
5 | body:
6 | - type: markdown
7 | attributes:
8 | value: |
9 | Thanks for taking the time to fill out this bug report!
10 | > If you haven't joined CasaOS Alpha Team yet.
11 | > Please join first on [Discord](https://discord.gg/knqAbbBbeX) to be updated on the Alpha test plan and test scope.
12 | - type: textarea
13 | id: what-happened
14 | attributes:
15 | label: What happened?
16 | description: Also tell us, what did you expect to happen?
17 | placeholder: Tell us what you see!
18 | validations:
19 | required: true
20 | - type: textarea
21 | id: screenshots
22 | attributes:
23 | label: Screenshots
24 | description: If applicable, add screenshots to help explain your problem.
25 | placeholder: Screenshots would be very helpful!
26 |
27 |
--------------------------------------------------------------------------------
/service/model/o_connections.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: LinkLeong link@icewhale.org
3 | * @Date: 2022-07-26 17:17:57
4 | * @LastEditors: LinkLeong
5 | * @LastEditTime: 2022-08-01 17:08:08
6 | * @FilePath: /CasaOS/service/model/o_connections.go
7 | * @Description:
8 | * @Website: https://www.casaos.io
9 | * Copyright (c) 2022 by icewhale, All Rights Reserved.
10 | */
11 | package model
12 |
13 | type ConnectionsDBModel struct {
14 | ID uint `gorm:"column:id;primary_key" json:"id"`
15 | Updated int64 `gorm:"autoUpdateTime"`
16 | Created int64 `gorm:"autoCreateTime"`
17 | Username string `json:"username"`
18 | Password string `json:"password"`
19 | Host string `json:"host"`
20 | Port string `json:"port"`
21 | Status string `json:"status"`
22 | Directories string `json:"directories"` // string array
23 | MountPoint string `json:"mount_point"` //parent directory of mount point
24 | }
25 |
26 | func (p *ConnectionsDBModel) TableName() string {
27 | return "o_connections"
28 | }
29 |
--------------------------------------------------------------------------------
/model/net.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | type IOCountersStat struct {
4 | Name string `json:"name"` // interface name
5 | BytesSent uint64 `json:"bytesSent"` // number of bytes sent
6 | BytesRecv uint64 `json:"bytesRecv"` // number of bytes received
7 | PacketsSent uint64 `json:"packetsSent"` // number of packets sent
8 | PacketsRecv uint64 `json:"packetsRecv"` // number of packets received
9 | Errin uint64 `json:"errin"` // total number of errors while receiving
10 | Errout uint64 `json:"errout"` // total number of errors while sending
11 | Dropin uint64 `json:"dropin"` // total number of incoming packets which were dropped
12 | Dropout uint64 `json:"dropout"` // total number of outgoing packets which were dropped (always 0 on OSX and BSD)
13 | Fifoin uint64 `json:"fifoin"` // total number of FIFO buffers errors while receiving
14 | Fifoout uint64 `json:"fifoout"` // total number of FIFO buffers errors while sending
15 | State string `json:"state"`
16 | Time int64 `json:"time"`
17 | }
18 |
--------------------------------------------------------------------------------
/pkg/utils/version/version.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: LinkLeong link@icewhale.com
3 | * @Date: 2022-05-13 18:15:46
4 | * @LastEditors: LinkLeong
5 | * @LastEditTime: 2022-07-21 15:27:53
6 | * @FilePath: /CasaOS/pkg/utils/version/version.go
7 | * @Description:
8 | * @Website: https://www.casaos.io
9 | * Copyright (c) 2022 by icewhale, All Rights Reserved.
10 | */
11 | package version
12 |
13 | import (
14 | "strconv"
15 | "strings"
16 |
17 | "github.com/IceWhaleTech/CasaOS/model"
18 | "github.com/IceWhaleTech/CasaOS/types"
19 | )
20 |
21 | func IsNeedUpdate(version model.Version) (bool, model.Version) {
22 |
23 | v1 := strings.Split(version.Version, ".")
24 |
25 | v2 := strings.Split(types.CURRENTVERSION, ".")
26 |
27 | for len(v1) < len(v2) {
28 | v1 = append(v1, "0")
29 | }
30 | for len(v2) < len(v1) {
31 | v2 = append(v2, "0")
32 | }
33 | for i := 0; i < len(v1); i++ {
34 | a, _ := strconv.Atoi(v1[i])
35 | b, _ := strconv.Atoi(v2[i])
36 | if a > b {
37 | return true, version
38 | }
39 | if a < b {
40 | return false, version
41 | }
42 | }
43 | return false, version
44 | }
45 |
--------------------------------------------------------------------------------
/service/model/o_user.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: LinkLeong link@icewhale.com
3 | * @Date: 2022-05-13 18:15:46
4 | * @LastEditors: LinkLeong
5 | * @LastEditTime: 2022-07-11 17:57:00
6 | * @FilePath: /CasaOS/service/model/o_user.go
7 | * @Description:
8 | * @Website: https://www.casaos.io
9 | * Copyright (c) 2022 by icewhale, All Rights Reserved.
10 | */
11 | package model
12 |
13 | import "time"
14 |
15 | //Soon to be removed
16 | type UserDBModel struct {
17 | Id int `gorm:"column:id;primary_key" json:"id"`
18 | Username string `json:"username"`
19 | Password string `json:"password,omitempty"`
20 | Role string `json:"role"`
21 | Email string `json:"email"`
22 | Nickname string `json:"nickname"`
23 | Avatar string `json:"avatar"`
24 | Description string `json:"description"`
25 | CreatedAt time.Time `gorm:"<-:create;autoCreateTime" json:"created_at,omitempty"`
26 | UpdatedAt time.Time `gorm:"<-:create;<-:update;autoUpdateTime" json:"updated_at,omitempty"`
27 | }
28 |
29 | func (p *UserDBModel) TableName() string {
30 | return "o_users"
31 | }
32 |
--------------------------------------------------------------------------------
/pkg/config/update.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "runtime"
5 |
6 | "github.com/IceWhaleTech/CasaOS/pkg/utils/file"
7 | )
8 |
9 | //检查目录是否存在
10 | func mkdirDATAAll() {
11 | sysType := runtime.GOOS
12 | var dirArray []string
13 | if sysType == "linux" {
14 | dirArray = []string{"/DATA/AppData", "/DATA/Documents", "/DATA/Downloads", "/DATA/Gallery", "/DATA/Media/Movies", "/DATA/Media/TV Shows", "/DATA/Media/Music"}
15 | }
16 |
17 | if sysType == "windows" {
18 | dirArray = []string{"C:\\CasaOS\\DATA\\AppData", "C:\\CasaOS\\DATA\\Documents", "C:\\CasaOS\\DATA\\Downloads", "C:\\CasaOS\\DATA\\Gallery", "C:\\CasaOS\\DATA\\Media/Movies", "C:\\CasaOS\\DATA\\Media\\TV Shows", "C:\\CasaOS\\DATA\\Media\\Music"}
19 | }
20 | if sysType == "darwin" {
21 | dirArray = []string{"./CasaOS/DATA/AppData", "./CasaOS/DATA/Documents", "./CasaOS/DATA/Downloads", "./CasaOS/DATA/Gallery", "./CasaOS/DATA/Media/Movies", "./CasaOS/DATA/Media/TV Shows", "./CasaOS/DATA/Media/Music"}
22 | }
23 |
24 | for _, v := range dirArray {
25 | file.IsNotExistMkDir(v)
26 | }
27 |
28 | }
29 |
30 | func UpdateSetup() {
31 | mkdirDATAAll()
32 | }
33 |
--------------------------------------------------------------------------------
/.github/workflows/add_issues_to_projects.yml:
--------------------------------------------------------------------------------
1 | name: Add Issues To Projects
2 |
3 | on:
4 | issues:
5 | types:
6 | - opened
7 |
8 | jobs:
9 | add-issues:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Generate token
13 | id: generate_token
14 | uses: tibdex/github-app-token@36464acb844fc53b9b8b2401da68844f6b05ebb0
15 | with:
16 | app_id: ${{ secrets.ALPHA_BOT_ID }}
17 | private_key: ${{ secrets.ALPHA_BOT_PEM }}
18 |
19 | - name: Add Alpha Bug Issue To project
20 | uses: actions/add-to-project@v0.3.0
21 | with:
22 | github-token: ${{ steps.generate_token.outputs.token }}
23 | project-url: https://github.com/orgs/IceWhaleTech/projects/5
24 | labeled: alpha, bug
25 | label-operator: AND
26 |
27 | - name: Add App Request Issue To project
28 | uses: actions/add-to-project@v0.3.0
29 | with:
30 | github-token: ${{ steps.generate_token.outputs.token }}
31 | project-url: https://github.com/orgs/IceWhaleTech/projects/8
32 | labeled: "App Request"
33 | label-operator: AND
34 |
--------------------------------------------------------------------------------
/model/file.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: LinkLeong link@icewhale.com
3 | * @Date: 2022-05-20 16:27:12
4 | * @LastEditors: LinkLeong
5 | * @LastEditTime: 2022-06-09 18:18:46
6 | * @FilePath: /CasaOS/model/file.go
7 | * @Description:
8 | * @Website: https://www.casaos.io
9 | * Copyright (c) 2022 by icewhale, All Rights Reserved.
10 | */
11 | package model
12 |
13 | type FileOperate struct {
14 | Type string `json:"type" binding:"required"`
15 | Item []FileItem `json:"item" binding:"required"`
16 | TotalSize int64 `json:"total_size"`
17 | ProcessedSize int64 `json:"processed_size"`
18 | To string `json:"to" binding:"required"`
19 | Style string `json:"style"`
20 | Finished bool `json:"finished"`
21 | }
22 |
23 | type FileItem struct {
24 | From string `json:"from" binding:"required"`
25 | Finished bool `json:"finished"`
26 | Size int64 `json:"size"`
27 | ProcessedSize int64 `json:"processed_size"`
28 | }
29 |
30 | type FileUpdate struct {
31 | FilePath string `json:"path" binding:"required"`
32 | FileContent string `json:"content" binding:"required"`
33 | }
34 |
--------------------------------------------------------------------------------
/service/docker_test.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | )
7 |
8 | //func TestDockerImageInfo(t *testing.T) {
9 | // //DockerImageInfo()
10 | //
11 | // address, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:0", "0.0.0.0"))
12 | // if err != nil {
13 | // fmt.Println(0, err)
14 | // }
15 | //
16 | // listener, err := net.ListenTCP("tcp", address)
17 | // if err != nil {
18 | // fmt.Println(0, err)
19 | // }
20 | //
21 | // defer listener.Close()
22 | // fmt.Println(listener.Addr().(*net.TCPAddr).Port, nil)
23 | //
24 | //}
25 |
26 | //func TestDockerNetwork(t *testing.T) {
27 | // DockerNetwork()
28 | //}
29 | //
30 | //func TestDockerPull(t *testing.T) {
31 | // DockerPull()
32 | //}
33 | //
34 | //func TestDockerLog(t *testing.T) {
35 | // DockerLog()
36 | //}
37 | //func TestDockerLogs(t *testing.T) {
38 | // DockerLogs()
39 | //}
40 |
41 | func TestDockerContainerStats(t *testing.T) {
42 | fmt.Println(DockerContainerStats1())
43 | }
44 |
45 | //func TestDockerImageRemove(t *testing.T) {
46 | // host, domain, tld := gotld.GetSubdomain("aaa.liru-05.top", 1)
47 | // fmt.Println(host)
48 | // fmt.Println(domain)
49 | // fmt.Println(tld)
50 | //}
51 |
--------------------------------------------------------------------------------
/service/model/o_rely.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "database/sql/driver"
5 | "encoding/json"
6 | "github.com/IceWhaleTech/CasaOS/service/docker_base"
7 | "time"
8 | )
9 |
10 | type RelyDBModel struct {
11 | Id uint `gorm:"column:id;primary_key" json:"id"`
12 | CustomId string ` json:"custom_id"`
13 | ContainerCustomId string `json:"container_custom_id"`
14 | Config MysqlConfigs `json:"config"`
15 | ContainerId string `json:"container_id,omitempty"`
16 | Type int `json:"type"` //目前暂未使用
17 | CreatedAt time.Time `gorm:"<-:create" json:"created_at"`
18 | UpdatedAt time.Time `gorm:"<-:create;<-:update" json:"updated_at"`
19 | }
20 |
21 | /****************使gorm支持[]string结构*******************/
22 | type MysqlConfigs docker_base.MysqlConfig
23 |
24 | func (c MysqlConfigs) Value() (driver.Value, error) {
25 | b, err := json.Marshal(c)
26 | return string(b), err
27 | }
28 |
29 | func (c *MysqlConfigs) Scan(input interface{}) error {
30 | return json.Unmarshal(input.([]byte), c)
31 | }
32 |
33 | /****************使gorm支持[]string结构*******************/
34 |
35 | func (p RelyDBModel) TableName() string {
36 | return "o_rely"
37 | }
38 |
--------------------------------------------------------------------------------
/service/rely.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: LinkLeong link@icewhale.com
3 | * @Date: 2021-09-30 18:18:14
4 | * @LastEditors: LinkLeong
5 | * @LastEditTime: 2022-06-02 18:00:57
6 | * @FilePath: /CasaOS/service/rely.go
7 | * @Description:
8 | * @Website: https://www.casaos.io
9 | * Copyright (c) 2022 by icewhale, All Rights Reserved.
10 | */
11 | package service
12 |
13 | import (
14 | model2 "github.com/IceWhaleTech/CasaOS/service/model"
15 | "gorm.io/gorm"
16 | )
17 |
18 | type RelyService interface {
19 | Create(rely model2.RelyDBModel)
20 | Delete(id string)
21 | GetInfo(id string) model2.RelyDBModel
22 | }
23 |
24 | type relyService struct {
25 | db *gorm.DB
26 | }
27 |
28 | func (r *relyService) Create(rely model2.RelyDBModel) {
29 |
30 | r.db.Create(&rely)
31 |
32 | }
33 |
34 | //获取我的应用列表
35 | func (r *relyService) GetInfo(id string) model2.RelyDBModel {
36 | var m model2.RelyDBModel
37 | r.db.Where("custom_id = ?", id).First(&m)
38 |
39 | // @tiger - 作为出参不应该直接返回数据库内的格式(见类似问题的注释)
40 | return m
41 | }
42 |
43 | func (r *relyService) Delete(id string) {
44 | var c model2.RelyDBModel
45 | r.db.Where("custom_id = ?", id).Delete(&c)
46 | }
47 |
48 | func NewRelyService(db *gorm.DB) RelyService {
49 | return &relyService{db: db}
50 | }
51 |
--------------------------------------------------------------------------------
/pkg/quic_helper/config.go:
--------------------------------------------------------------------------------
1 | package quic_helper
2 |
3 | import (
4 | "crypto/rand"
5 | "crypto/rsa"
6 | "crypto/tls"
7 | "crypto/x509"
8 | "encoding/pem"
9 | "math/big"
10 |
11 | "github.com/lucas-clemente/quic-go"
12 | )
13 |
14 | // Setup a bare-bones TLS config for the server
15 | func GetGenerateTLSConfig(token string) *tls.Config {
16 | key, err := rsa.GenerateKey(rand.Reader, 1024)
17 | if err != nil {
18 | panic(err)
19 | }
20 | template := x509.Certificate{SerialNumber: big.NewInt(1)}
21 | certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key)
22 | if err != nil {
23 | panic(err)
24 | }
25 | keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)})
26 | certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER})
27 |
28 | tlsCert, err := tls.X509KeyPair(certPEM, keyPEM)
29 | if err != nil {
30 | panic(err)
31 | }
32 | return &tls.Config{
33 | Certificates: []tls.Certificate{tlsCert},
34 | NextProtos: []string{token},
35 | SessionTicketsDisabled: true,
36 | }
37 | }
38 | func GetClientTlsConfig(otherToken string) *tls.Config {
39 | return &tls.Config{
40 | InsecureSkipVerify: true,
41 | NextProtos: []string{otherToken},
42 | SessionTicketsDisabled: true,
43 | }
44 | }
45 |
46 | func GetQUICConfig() *quic.Config {
47 | return &quic.Config{
48 | ConnectionIDLength: 4,
49 | KeepAlive: true,
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/web/img/icon/safari-pinned-tab.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
26 |
--------------------------------------------------------------------------------
/pkg/utils/port/port.go:
--------------------------------------------------------------------------------
1 | package port
2 |
3 | import (
4 | "fmt"
5 | "net"
6 | )
7 |
8 | // 获取可用端口
9 | func GetAvailablePort(t string) (int, error) {
10 | address := fmt.Sprintf("%s:0", "0.0.0.0")
11 | if t == "udp" {
12 | add, err := net.ResolveUDPAddr(t, address)
13 | if err != nil {
14 | return 0, err
15 | }
16 |
17 | listener, err := net.ListenUDP(t, add)
18 | if err != nil {
19 | return 0, err
20 | }
21 |
22 | defer listener.Close()
23 | return listener.LocalAddr().(*net.UDPAddr).Port, nil
24 | } else {
25 | add, err := net.ResolveTCPAddr(t, address)
26 | if err != nil {
27 | return 0, err
28 | }
29 |
30 | listener, err := net.ListenTCP(t, add)
31 | if err != nil {
32 | return 0, err
33 | }
34 |
35 | defer listener.Close()
36 | return listener.Addr().(*net.TCPAddr).Port, nil
37 | }
38 |
39 | }
40 |
41 | // 判断端口是否可以(未被占用)
42 | // param t tcp/udp
43 | func IsPortAvailable(port int, t string) bool {
44 | address := fmt.Sprintf("%s:%d", "0.0.0.0", port)
45 | if t == "udp" {
46 | sadd, err := net.ResolveUDPAddr("udp", address)
47 | uc, err := net.ListenUDP("udp", sadd)
48 |
49 | if err != nil {
50 | fmt.Println(err.Error())
51 | return false
52 | } else {
53 | defer uc.Close()
54 | return true
55 | }
56 |
57 | } else {
58 | listener, err := net.Listen(t, address)
59 |
60 | if err != nil {
61 | //log.Infof("port %s is taken: %s", address, err)
62 | return false
63 | }
64 | defer listener.Close()
65 | return true
66 | }
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/DEVELOPING.md:
--------------------------------------------------------------------------------
1 | # CasaOS Development
2 | Here we will describe the steps required to setup a development environment with CasaOS.
3 |
4 | - [Setting up development environment](#setting-up-development-environment)
5 | - [Pre-requisites](#pre-requisites)
6 | - [1. Fork the Repo](#1.-fork-the-repo)
7 | - [2. Clone the repo down](#2.-clone-the-repo-down)
8 | - [3. Install dependencies](#3.-install-dependencies)
9 |
10 |
11 | ## Setting up a development environment
12 | In this section we will walk you through the general process of setting up your development environment to get started.
13 |
14 | ### Pre-requisites
15 | The following must be installed in order to get started. The details of how to install them is outside the scope of this doc, but generally they should be able to be installed with your systems package manager (apt, yum, brew, choco, etc).
16 | - Go > v1.17.0
17 | - yarn
18 | - node.js
19 |
20 | ### 1. Fork the Repo
21 | [Fork the repo](https://docs.github.com/en/get-started/quickstart/fork-a-repo) onto your own GitHub account for developing.
22 |
23 | ### 2. Clone the repo down
24 | 1. Navigate into your go workspace (check with `go env GOPATH`).
25 | 2. Navigate to the appropriate path for github. It should look something like this: `/github.com//`. If it doesn't exist create it.
26 | 3. Clone down the repo with the following: `git clone --recurse-submodules --remote-submodules https://github.com//CasaOS.git`
27 |
28 | ### 3. Install dependencies
29 | 1. `cd UI`
30 | 2. `yarn install`
31 | 3. `yarn build`
32 | 4. `cd ..`
33 | 5. `go get`
34 |
--------------------------------------------------------------------------------
/route/v1/notify_old.go:
--------------------------------------------------------------------------------
1 | package v1
2 |
3 | import (
4 | "fmt"
5 | "net/http"
6 |
7 | "github.com/IceWhaleTech/CasaOS/service"
8 | "github.com/IceWhaleTech/CasaOS/types"
9 | "github.com/gin-gonic/gin"
10 | "github.com/gorilla/websocket"
11 | )
12 |
13 | var upGrader = websocket.Upgrader{
14 | CheckOrigin: func(r *http.Request) bool {
15 | return true
16 | },
17 | }
18 |
19 | // @Summary websocket 接口,连接成功后发送一个"notify"字符串
20 | // @Produce application/json
21 | // @Accept application/json
22 | // @Tags notify
23 | // @Security ApiKeyAuth
24 | // @Param token path string true "token"
25 | // @Success 200 {string} string "ok"
26 | // @Router /notify/ws [get]
27 | func NotifyWS(c *gin.Context) {
28 | //升级get请求为webSocket协议
29 | ws, err := upGrader.Upgrade(c.Writer, c.Request, nil)
30 | if err != nil {
31 | return
32 | }
33 | defer ws.Close()
34 | service.WebSocketConns = append(service.WebSocketConns, ws)
35 |
36 | if !service.SocketRun {
37 | service.SocketRun = true
38 | service.SendMeg()
39 | }
40 | for {
41 | mt, message, err := ws.ReadMessage()
42 | fmt.Println(mt, message, err)
43 | }
44 |
45 | }
46 |
47 | // @Summary 标记notify已读
48 | // @Produce application/json
49 | // @Accept application/json
50 | // @Tags notify
51 | // @Security ApiKeyAuth
52 | // @Success 200 {string} string "ok"
53 | // @Router /notify/read/{id} [put]
54 | func PutNotifyRead(c *gin.Context) {
55 | id := c.Param("id")
56 | // if len(id) == 0 {
57 | // c.JSON(http.StatusOK, model.Result{Success: oasis_err.INVALID_PARAMS, Message: oasis_err.GetMsg(oasis_err.INVALID_PARAMS)})
58 | // return
59 | // }
60 | fmt.Println(id)
61 | service.MyService.Notify().MarkRead(id, types.NOTIFY_READ)
62 | }
63 |
--------------------------------------------------------------------------------
/pkg/utils/jwt/jwt.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: LinkLeong link@icewhale.com
3 | * @Date: 2021-09-30 18:18:14
4 | * @LastEditors: LinkLeong
5 | * @LastEditTime: 2022-07-18 17:30:38
6 | * @FilePath: /CasaOS/pkg/utils/jwt/jwt.go
7 | * @Description:
8 | * @Website: https://www.casaos.io
9 | * Copyright (c) 2022 by icewhale, All Rights Reserved.
10 | */
11 | package jwt
12 |
13 | import (
14 | "time"
15 |
16 | jwt "github.com/golang-jwt/jwt/v4"
17 | )
18 |
19 | type Claims struct {
20 | Username string `json:"username"`
21 | PassWord string `json:"password"`
22 | Id int `json:"id"`
23 | jwt.RegisteredClaims
24 | }
25 |
26 | var jwtSecret []byte
27 |
28 | //创建token
29 | func GenerateToken(username, password string, id int, issuer string, t time.Duration) (string, error) {
30 | clims := Claims{
31 | username,
32 | password,
33 | id,
34 | jwt.RegisteredClaims{
35 | ExpiresAt: jwt.NewNumericDate(time.Now().Add(t)),
36 | IssuedAt: jwt.NewNumericDate(time.Now()),
37 | NotBefore: jwt.NewNumericDate(time.Now()),
38 | Issuer: issuer,
39 | },
40 | }
41 |
42 | tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, clims)
43 | token, err := tokenClaims.SignedString(jwtSecret)
44 | return token, err
45 |
46 | }
47 |
48 | //解析token
49 | func ParseToken(token string, valid bool) (*Claims, error) {
50 | tokenClaims, err := jwt.ParseWithClaims(token, &Claims{}, func(token *jwt.Token) (interface{}, error) {
51 | return jwtSecret, nil
52 | })
53 | if tokenClaims != nil {
54 | if clims, ok := tokenClaims.Claims.(*Claims); ok {
55 | if valid && tokenClaims.Valid {
56 | return clims, nil
57 | } else if !valid {
58 | return clims, nil
59 | }
60 | }
61 | }
62 | return nil, err
63 | }
64 |
--------------------------------------------------------------------------------
/pkg/samba/smaba.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: LinkLeong link@icewhale.org
3 | * @Date: 2022-07-27 10:35:29
4 | * @LastEditors: LinkLeong
5 | * @LastEditTime: 2022-08-01 13:56:44
6 | * @FilePath: /CasaOS/pkg/samba/smaba.go
7 | * @Description:
8 | * @Website: https://www.casaos.io
9 | * Copyright (c) 2022 by icewhale, All Rights Reserved.
10 | */
11 | package samba
12 |
13 | import (
14 | "errors"
15 | "net"
16 |
17 | "github.com/hirochachacha/go-smb2"
18 | )
19 |
20 | func ConnectSambaService(host, port, username, password, directory string) error {
21 | conn, err := net.Dial("tcp", host+":"+port)
22 | if err != nil {
23 | return err
24 | }
25 | defer conn.Close()
26 | d := &smb2.Dialer{
27 | Initiator: &smb2.NTLMInitiator{
28 | User: username,
29 | Password: password,
30 | },
31 | }
32 |
33 | s, err := d.Dial(conn)
34 | if err != nil {
35 | return err
36 | }
37 | defer s.Logoff()
38 | names, err := s.ListSharenames()
39 | if err != nil {
40 | return err
41 | }
42 |
43 | for _, name := range names {
44 | if name == directory {
45 | return nil
46 | }
47 | }
48 | return errors.New("directory not found")
49 | }
50 |
51 | //get share name list
52 | func GetSambaSharesList(host, port, username, password string) ([]string, error) {
53 | conn, err := net.Dial("tcp", host+":"+port)
54 | if err != nil {
55 | return nil, err
56 | }
57 | defer conn.Close()
58 | d := &smb2.Dialer{
59 | Initiator: &smb2.NTLMInitiator{
60 | User: username,
61 | Password: password,
62 | },
63 | }
64 |
65 | s, err := d.Dial(conn)
66 | if err != nil {
67 | return nil, err
68 | }
69 | defer s.Logoff()
70 | names, err := s.ListSharenames()
71 | if err != nil {
72 | return nil, err
73 | }
74 | return names, err
75 | }
76 |
--------------------------------------------------------------------------------
/service/file_test.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "io"
7 | "log"
8 | "os"
9 | "testing"
10 | "time"
11 | )
12 |
13 | var ctx context.Context
14 | var cancel context.CancelFunc
15 |
16 | func TestNewInteruptReader(t *testing.T) {
17 | ctx, cancel = context.WithCancel(context.Background())
18 |
19 | go func() {
20 | // 在初始上下文的基础上创建一个有取消功能的上下文
21 | // ctx, cancel := context.WithCancel(ctx)
22 | fmt.Println("开始")
23 | fIn, err := os.Open("/Users/liangjianli/Downloads/demo_data.tar.gz")
24 | if err != nil {
25 |
26 | }
27 | defer fIn.Close()
28 | fmt.Println("创建新文件")
29 | fOut, err := os.Create("/Users/liangjianli/Downloads/demo_data1.tar.gz")
30 | if err != nil {
31 | fmt.Println(err)
32 | }
33 |
34 | defer fOut.Close()
35 |
36 | fmt.Println("准备复制")
37 | // _, err = io.Copy(out, NewReader(ctx, f))
38 | // time.Sleep(time.Second * 2)
39 | //ctx.Done()
40 | // cancel()
41 |
42 | // interrupt context after 500ms
43 |
44 | // interrupt context with SIGTERM (CTRL+C)
45 | //sigs := make(chan os.Signal, 1)
46 | //signal.Notify(sigs, os.Interrupt)
47 |
48 | if err != nil {
49 | log.Fatal(err)
50 | }
51 |
52 | // Reader that fails when context is canceled
53 | in := NewReader(ctx, fIn)
54 | // Writer that fails when context is canceled
55 | out := NewWriter(ctx, fOut)
56 |
57 | //time.Sleep(2 * time.Second)
58 |
59 | //cancel()
60 |
61 | n, err := io.Copy(out, in)
62 | log.Println(n, "bytes copied.")
63 | if err != nil {
64 | fmt.Println("Err:", err)
65 | }
66 |
67 | fmt.Println("Closing.")
68 | }()
69 |
70 | go func() {
71 | //<-sigs
72 | time.Sleep(time.Second)
73 | fmt.Println("退出")
74 | ddd()
75 | }()
76 | time.Sleep(time.Second * 10)
77 | }
78 |
79 | func ddd() {
80 | cancel()
81 | }
82 |
--------------------------------------------------------------------------------
/pkg/utils/ip_helper/ip.go:
--------------------------------------------------------------------------------
1 | package ip_helper
2 |
3 | import (
4 | "net"
5 | "strings"
6 |
7 | httper2 "github.com/IceWhaleTech/CasaOS/pkg/utils/httper"
8 | )
9 |
10 | func IsIPv4(address string) bool {
11 | return strings.Count(address, ":") < 2
12 | }
13 | func IsIPv6(address string) bool {
14 | return strings.Count(address, ":") >= 2
15 | }
16 |
17 | //获取外网ip
18 | func GetExternalIPV4() string {
19 | return httper2.Get("https://api.ipify.org", nil)
20 | }
21 |
22 | //获取外网ip
23 | func GetExternalIPV6() string {
24 | return httper2.Get("https://api6.ipify.org", nil)
25 | }
26 |
27 | //获取本地ip
28 | func GetLoclIp() string {
29 | addrs, err := net.InterfaceAddrs()
30 | if err != nil {
31 | return "127.0.0.1"
32 | }
33 | for _, address := range addrs {
34 | if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
35 | if ipnet.IP.To4() != nil {
36 | return ipnet.IP.String()
37 | }
38 |
39 | }
40 | }
41 | return "127.0.0.1"
42 | }
43 | func GetDeviceAllIP(port string) []string {
44 | var address []string
45 | addrs, err := net.InterfaceAddrs()
46 | if err != nil {
47 | return address
48 | }
49 | for _, a := range addrs {
50 | if ipNet, ok := a.(*net.IPNet); ok && !ipNet.IP.IsLoopback() {
51 | if ipNet.IP.To16() != nil {
52 | address = append(address, ipNet.IP.String()+":"+port)
53 | }
54 | }
55 | }
56 | return address
57 | }
58 |
59 | func HasLocalIP(ip net.IP) bool {
60 | if ip.IsLoopback() {
61 | return true
62 | }
63 | ip.String()
64 |
65 | ip4 := ip.To4()
66 | if ip4 == nil {
67 | return false
68 | }
69 |
70 | return ip4[0] == 10 || // 10.0.0.0/8
71 | (ip4[0] == 172 && ip4[1] >= 16 && ip4[1] <= 31) || // 172.16.0.0/12
72 | (ip4[0] == 169 && ip4[1] == 254) || // 169.254.0.0/16
73 | (ip4[0] == 192 && ip4[1] == 168) // 192.168.0.0/16
74 | }
75 |
--------------------------------------------------------------------------------
/route/socket.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: LinkLeong link@icewhale.com
3 | * @Date: 2022-05-23 17:18:56
4 | * @LastEditors: LinkLeong
5 | * @LastEditTime: 2022-06-09 21:48:10
6 | * @FilePath: /CasaOS/route/socket.go
7 | * @Description:
8 | * @Website: https://www.casaos.io
9 | * Copyright (c) 2022 by icewhale, All Rights Reserved.
10 | */
11 | package route
12 |
13 | import (
14 | "strconv"
15 | "time"
16 |
17 | "github.com/IceWhaleTech/CasaOS/model/notify"
18 | "github.com/IceWhaleTech/CasaOS/pkg/config"
19 | "github.com/IceWhaleTech/CasaOS/pkg/utils/port"
20 | "github.com/IceWhaleTech/CasaOS/service"
21 | f "github.com/ambelovsky/gosf"
22 | )
23 |
24 | func SocketInit(msg chan notify.Message) {
25 |
26 | // set socket port
27 | socketPort := 0
28 | if len(config.ServerInfo.SocketPort) == 0 {
29 | socketPort, _ = port.GetAvailablePort("tcp")
30 | config.ServerInfo.SocketPort = strconv.Itoa(socketPort)
31 | config.Cfg.Section("server").Key("SocketPort").SetValue(strconv.Itoa(socketPort))
32 | config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath)
33 | } else {
34 | socketPort, _ = strconv.Atoi(config.ServerInfo.SocketPort)
35 | if !port.IsPortAvailable(socketPort, "tcp") {
36 | socketPort, _ := port.GetAvailablePort("tcp")
37 | config.ServerInfo.SocketPort = strconv.Itoa(socketPort)
38 | config.Cfg.Section("server").Key("SocketPort").SetValue(strconv.Itoa(socketPort))
39 | config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath)
40 | }
41 | }
42 |
43 | f.OnConnect(func(c *f.Client, request *f.Request) {
44 | service.ClientCount += 1
45 | })
46 | f.OnDisconnect(func(c *f.Client, request *f.Request) {
47 | service.ClientCount -= 1
48 | })
49 | go func(msg chan notify.Message) {
50 | for v := range msg {
51 | f.Broadcast("", v.Path, &v.Msg)
52 | time.Sleep(time.Millisecond * 100)
53 | }
54 |
55 | }(msg)
56 |
57 | f.Startup(map[string]interface{}{
58 | "port": socketPort})
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/.github/workflows/push_events_to_discord.yml:
--------------------------------------------------------------------------------
1 | name: Push Events to Discord
2 |
3 | on:
4 | issues:
5 | types:
6 | - opened
7 | issue_comment:
8 | types:
9 | - created
10 | discussion:
11 | types:
12 | - created
13 | - transferred
14 | - answered
15 | discussion_comment:
16 | types:
17 | - created
18 |
19 | jobs:
20 | push-events:
21 |
22 | runs-on: ubuntu-latest
23 |
24 | steps:
25 |
26 | - name: General Discussions & Comments
27 | if: ${{ ( github.event_name == 'discussion' || github.event_name == 'discussion_comment' ) && github.event.discussion.category.name == 'General' }}
28 | uses: joseph-montanez/forward-event-action@v3.0.0
29 | with:
30 | webhook: ${{ secrets.Discord_CasaOS_General_Webhook }}
31 |
32 | - name: App Request Issues & Comments
33 | if: ${{ ( github.event_name == 'issues' || github.event_name == 'issue_comment' ) && contains(github.event.issue.labels.*.name, 'App Request') }}
34 | uses: joseph-montanez/forward-event-action@v3.0.0
35 | with:
36 | webhook: ${{ secrets.Discord_CasaOS_App_Request_Webhook }}
37 |
38 | - name: Bug Issues & Comments
39 | if: ${{ ( github.event_name == 'issues' || github.event_name == 'issue_comment' ) && contains(github.event.issue.labels.*.name, 'bug') && !contains(github.event.issue.labels.*.name, 'alpha') }}
40 | uses: joseph-montanez/forward-event-action@v3.0.0
41 | with:
42 | webhook: ${{ secrets.Discord_CasaOS_Bug_Webhook }}
43 |
44 | - name: Alpha Issues & Comments
45 | if: ${{ ( github.event_name == 'issues' || github.event_name == 'issue_comment' ) && contains(github.event.issue.labels.*.name, 'alpha') }}
46 | uses: joseph-montanez/forward-event-action@v3.0.0
47 | with:
48 | webhook: ${{ secrets.Discord_CasaOS_Alpha_Webhook }}
49 |
--------------------------------------------------------------------------------
/model/sys_common.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: LinkLeong link@icewhale.com
3 | * @Date: 2022-05-13 18:15:46
4 | * @LastEditors: LinkLeong
5 | * @LastEditTime: 2022-07-14 11:02:06
6 | * @FilePath: /CasaOS/model/sys_common.go
7 | * @Description:
8 | * @Website: https://www.casaos.io
9 | * Copyright (c) 2022 by icewhale, All Rights Reserved.
10 | */
11 | package model
12 |
13 | import "time"
14 |
15 | //系统配置
16 | type SysInfoModel struct {
17 | Name string //系统名称
18 | }
19 |
20 | //用户相关
21 | type UserModel struct {
22 | UserName string
23 | PWD string
24 | Token string
25 | Head string
26 | Email string
27 | Description string
28 | Initialized bool
29 | Avatar string
30 | NickName string
31 | Public string
32 | }
33 |
34 | //服务配置
35 | type ServerModel struct {
36 | HttpPort string
37 | RunMode string
38 | ServerApi string
39 | LockAccount bool
40 | Token string
41 | USBAutoMount string
42 | SocketPort string
43 | }
44 |
45 | //服务配置
46 | type APPModel struct {
47 | LogPath string
48 | LogSaveName string
49 | LogFileExt string
50 | DateStrFormat string
51 | DateTimeFormat string
52 | UserDataPath string
53 | TimeFormat string
54 | DateFormat string
55 | DBPath string
56 | ShellPath string
57 | TempPath string
58 | }
59 |
60 | //公共返回模型
61 | type Result struct {
62 | Success int `json:"success" example:"200"`
63 | Message string `json:"message" example:"ok"`
64 | Data interface{} `json:"data" example:"返回结果"`
65 | }
66 |
67 | //redis配置文件
68 | type RedisModel struct {
69 | Host string
70 | Password string
71 | MaxIdle int
72 | MaxActive int
73 | IdleTimeout time.Duration
74 | }
75 |
76 | type SystemConfig struct {
77 | ConfigPath string `json:"config_path"`
78 | }
79 |
80 | type CasaOSGlobalVariables struct {
81 | AppChange bool
82 | }
83 |
84 | type FileSetting struct {
85 | ShareDir []string `json:"share_dir" delim:"|"`
86 | DownloadDir string `json:"download_dir"`
87 | }
88 |
--------------------------------------------------------------------------------
/pkg/utils/file/block.go:
--------------------------------------------------------------------------------
1 | package file
2 |
3 | import (
4 | "crypto/md5"
5 | "encoding/hex"
6 | "io"
7 | "math"
8 | "os"
9 | "strconv"
10 | )
11 |
12 | // Get info of block
13 | func GetBlockInfo(fileSize int64) (blockSize int, length int) {
14 | switch {
15 | case fileSize <= 1<<28: //256M
16 | blockSize = 1 << 17 //128kb
17 | case fileSize <= 1<<29: //512M
18 | blockSize = 1 << 18 //256kb
19 | case fileSize <= 1<<30: //1G
20 | blockSize = 1 << 19 //512kb
21 | case fileSize <= 1<<31: //2G
22 | blockSize = 1 << 20 //(mb)
23 | case fileSize <= 1<<32: //4G
24 | blockSize = 1 << 21 //2mb
25 | case fileSize <= 1<<33: //8G
26 | blockSize = 1 << 22 //4mb
27 | case fileSize <= 1<<34: //16g
28 | blockSize = 1 << 23 //8mb
29 | default:
30 | blockSize = 1 << 24 //16mb
31 | }
32 | temp := float64(fileSize) / float64(blockSize)
33 | length = int(math.Ceil(temp))
34 | return
35 | }
36 |
37 | //get the hash of the data
38 | func GetHashByContent(data []byte) string {
39 | sum := md5.Sum(data)
40 | return hex.EncodeToString(sum[:])
41 | }
42 |
43 | //get the hash of the data
44 | func GetHashByPath(path string) string {
45 | pFile, err := os.Open(path)
46 | if err != nil {
47 | return ""
48 | }
49 | defer pFile.Close()
50 | md5h := md5.New()
51 | io.Copy(md5h, pFile)
52 | return hex.EncodeToString(md5h.Sum(nil))
53 | }
54 |
55 | //Comparison data hash
56 | func ComparisonHash(data []byte, hash string) bool {
57 | sum := md5.Sum(data)
58 | return hex.EncodeToString(sum[:]) == hash
59 | }
60 |
61 | //get prefix byte length
62 | func PrefixLength(byteLength int) []byte {
63 | lengthByte := []byte{'0', '0', '0', '0', '0', '0'}
64 | bSize := strconv.Itoa(byteLength)
65 | cha := 6 - len(bSize)
66 | for i := len(bSize); i > 0; i-- {
67 | lengthByte[cha+i-1] = bSize[i-1]
68 | }
69 | return lengthByte
70 | }
71 |
72 | //get data byte length
73 | func DataLength(length int) []byte {
74 | lengthByte := []byte{'0', '0', '0', '0', '0', '0', '0', '0'}
75 | bSize := strconv.Itoa(length)
76 | cha := 8 - len(bSize)
77 | for i := len(bSize); i > 0; i-- {
78 | lengthByte[cha+i-1] = bSize[i-1]
79 | }
80 | return lengthByte
81 | }
82 |
--------------------------------------------------------------------------------
/middleware/gin.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: LinkLeong link@icewhale.com
3 | * @Date: 2021-10-08 10:29:08
4 | * @LastEditors: LinkLeong
5 | * @LastEditTime: 2022-07-22 11:06:07
6 | * @FilePath: /CasaOS/middleware/gin.go
7 | * @Description:
8 | * @Website: https://www.casaos.io
9 | * Copyright (c) 2022 by icewhale, All Rights Reserved.
10 | */
11 | package middleware
12 |
13 | import (
14 | "fmt"
15 | "net/http"
16 | "strings"
17 |
18 | "github.com/IceWhaleTech/CasaOS/pkg/utils/loger"
19 | "github.com/gin-gonic/gin"
20 | "go.uber.org/zap"
21 | )
22 |
23 | func Cors() gin.HandlerFunc {
24 | return func(c *gin.Context) {
25 | method := c.Request.Method
26 |
27 | c.Header("Access-Control-Allow-Origin", "*")
28 | c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE,UPDATE")
29 | //允许跨域设置可以返回其他子段,可以自定义字段
30 | c.Header("Access-Control-Allow-Headers", "Authorization, Content-Length, X-CSRF-Token, Token,session,Language,Content-Type,Access-Control-Allow-Origin,Access-Control-Allow-Headers,Access-Control-Allow-Methods,Connection,Host,Origin,Referer,User-Agent,X-Requested-With")
31 | // 允许浏览器(客户端)可以解析的头部 (重要)
32 | c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers")
33 | //c.Writer.Header().Set("Access-Control-Allow-Headers", "Accept, Authorization, Content-Type, Content-Length, X-CSRF-Token, Token, session, Origin, Host, Connection, Accept-Encoding, Accept-Language, X-Requested-With")
34 | //设置缓存时间
35 | c.Header("Access-Control-Max-Age", "172800")
36 | c.Header("Access-Control-Allow-Credentials", "true")
37 | c.Set("Content-Type", "application/json")
38 | //}
39 |
40 | //允许类型校验
41 | if method == "OPTIONS" {
42 | c.JSON(http.StatusOK, "ok!")
43 | }
44 |
45 | defer func() {
46 | if err := recover(); err != nil {
47 | fmt.Println(err)
48 | }
49 | }()
50 |
51 | c.Next()
52 | }
53 | }
54 | func WriteLog() gin.HandlerFunc {
55 | return func(c *gin.Context) {
56 | if !strings.Contains(c.Request.URL.String(), "password") {
57 | loger.Info("request:", zap.Any("path", c.Request.URL.String()), zap.Any("param", c.Params), zap.Any("query", c.Request.URL.Query()), zap.Any("method", c.Request.Method))
58 | c.Next()
59 | }
60 |
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/route/v1/samba_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: LinkLeong link@icewhale.org
3 | * @Date: 2022-08-02 15:10:56
4 | * @LastEditors: LinkLeong
5 | * @LastEditTime: 2022-08-02 16:58:42
6 | * @FilePath: /CasaOS/route/v1/samba_test.go
7 | * @Description:
8 | * @Website: https://www.casaos.io
9 | * Copyright (c) 2022 by icewhale, All Rights Reserved.
10 | */
11 | package v1
12 |
13 | import (
14 | "net/http"
15 | "net/http/httptest"
16 | "testing"
17 |
18 | "github.com/gin-gonic/gin"
19 | "github.com/golang/mock/gomock"
20 | "github.com/stretchr/testify/assert"
21 | )
22 |
23 | func performRequest(r http.Handler, method, path string) *httptest.ResponseRecorder {
24 | req, _ := http.NewRequest(method, path, nil)
25 | w := httptest.NewRecorder()
26 | r.ServeHTTP(w, req)
27 | return w
28 | }
29 |
30 | // func TestHelloWorld(t *testing.T) {
31 | // // Build our expected body
32 | // body := gin.H{
33 | // "hello": "world",
34 | // }
35 | // // Grab our router
36 | // router := "SetupRouter()"
37 | // // Perform a GET request with that handler.
38 | // w := performRequest(router, "GET", "/")
39 | // // Assert we encoded correctly,
40 | // // the request gives a 200
41 | // assert.Equal(t, http.StatusOK, w.Code)
42 | // // Convert the JSON response to a map
43 | // var response map[string]string
44 | // err := json.Unmarshal([]byte(w.Body.String()), &response)
45 | // // Grab the value & whether or not it exists
46 | // value, exists := response["hello"]
47 | // // Make some assertions on the correctness of the response.
48 | // assert.Nil(t, err)
49 | // assert.True(t, exists)
50 | // assert.Equal(t, body["hello"], value)
51 | // }
52 |
53 | func TestGetSambaSharesList(t *testing.T) {
54 | gin.SetMode(gin.TestMode)
55 | ctrl := gomock.NewController(t)
56 | defer ctrl.Finish()
57 | executeWithContext := func() *httptest.ResponseRecorder {
58 | response := httptest.NewRecorder()
59 | con, ginEngine := gin.CreateTestContext(response)
60 |
61 | requestUrl := "/v1/samba/shares"
62 | httpRequest, _ := http.NewRequest("GET", requestUrl, nil)
63 | GetSambaSharesList(con)
64 | ginEngine.ServeHTTP(response, httpRequest)
65 | return response
66 | }
67 |
68 | t.Run("Happy", func(t *testing.T) {
69 | res := executeWithContext()
70 | assert.Equal(t, http.StatusOK, res.Code)
71 | })
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/pkg/sqlite/db.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: LinkLeong link@icewhale.com
3 | * @Date: 2022-05-13 18:15:46
4 | * @LastEditors: LinkLeong
5 | * @LastEditTime: 2022-07-27 11:25:26
6 | * @FilePath: /CasaOS/pkg/sqlite/db.go
7 | * @Description:
8 | * @Website: https://www.casaos.io
9 | * Copyright (c) 2022 by icewhale, All Rights Reserved.
10 | */
11 | package sqlite
12 |
13 | import (
14 | "time"
15 |
16 | "github.com/IceWhaleTech/CasaOS/pkg/utils/file"
17 | "github.com/IceWhaleTech/CasaOS/pkg/utils/loger"
18 | model2 "github.com/IceWhaleTech/CasaOS/service/model"
19 | "go.uber.org/zap"
20 | "gorm.io/driver/sqlite"
21 | "gorm.io/gorm"
22 | )
23 |
24 | var gdb *gorm.DB
25 |
26 | func GetDb(dbPath string) *gorm.DB {
27 | if gdb != nil {
28 | return gdb
29 | }
30 | // Refer https://github.com/go-sql-driver/mysql#dsn-data-source-name
31 | //dsn := fmt.Sprintf("%v:%v@tcp(%v:%v)/%v?charset=utf8mb4&parseTime=True&loc=Local", m.User, m.PWD, m.IP, m.Port, m.DBName)
32 | //db, err := gorm.Open(mysql2.Open(dsn), &gorm.Config{})
33 | file.IsNotExistMkDir(dbPath)
34 | db, err := gorm.Open(sqlite.Open(dbPath+"/casaOS.db"), &gorm.Config{})
35 | c, _ := db.DB()
36 | c.SetMaxIdleConns(10)
37 | c.SetMaxOpenConns(100)
38 | c.SetConnMaxIdleTime(time.Second * 1000)
39 | if err != nil {
40 | loger.Error("sqlite connect error", zap.Any("db connect error", err))
41 | panic("sqlite connect error")
42 | return nil
43 | }
44 | gdb = db
45 |
46 | db.Exec(`alter table o_user rename to old_user;
47 |
48 | create table o_users ( id integer primary key,username text,password text,role text,email text,nickname text,avatar text,description text,created_at datetime,updated_at datetime);
49 |
50 | insert into o_users select id,user_name,password,role,email,nick_name,avatar,description,created_at,updated_at from old_user;
51 |
52 | drop table old_user;
53 | drop table o_user;
54 | `)
55 |
56 | err = db.AutoMigrate(&model2.AppNotify{}, &model2.AppListDBModel{}, &model2.SerialDisk{}, model2.UserDBModel{}, model2.SharesDBModel{}, model2.ConnectionsDBModel{})
57 | db.Exec("DROP TABLE IF EXISTS o_application")
58 | db.Exec("DROP TABLE IF EXISTS o_friend")
59 | db.Exec("DROP TABLE IF EXISTS o_person_download")
60 | db.Exec("DROP TABLE IF EXISTS o_person_down_record")
61 | if err != nil {
62 | loger.Error("check or create db error", zap.Any("error", err))
63 | }
64 | return db
65 | }
66 |
--------------------------------------------------------------------------------
/pkg/utils/jwt/jwt_helper.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: LinkLeong link@icewhale.com
3 | * @Date: 2022-06-17 14:01:25
4 | * @LastEditors: LinkLeong
5 | * @LastEditTime: 2022-07-29 16:22:25
6 | * @FilePath: /CasaOS/pkg/utils/jwt/jwt_helper.go
7 | * @Description:
8 | * @Website: https://www.casaos.io
9 | * Copyright (c) 2022 by icewhale, All Rights Reserved.
10 | */
11 | package jwt
12 |
13 | import (
14 | "fmt"
15 | "strconv"
16 | "time"
17 |
18 | "github.com/IceWhaleTech/CasaOS/model"
19 | "github.com/IceWhaleTech/CasaOS/pkg/utils/common_err"
20 | loger2 "github.com/IceWhaleTech/CasaOS/pkg/utils/loger"
21 | "github.com/gin-gonic/gin"
22 | )
23 |
24 | func JWT() gin.HandlerFunc {
25 | return func(c *gin.Context) {
26 | var code int
27 | code = common_err.SUCCESS
28 | token := c.GetHeader("Authorization")
29 | if len(token) == 0 {
30 | token = c.Query("token")
31 | }
32 | if token == "" {
33 | code = common_err.INVALID_PARAMS
34 | }
35 |
36 | claims, err := ParseToken(token, false)
37 |
38 | //_, err := ParseToken(token)
39 | if err != nil {
40 | code = common_err.ERROR_AUTH_TOKEN
41 | } else if (c.Request.URL.Path == "/v1/file" || c.Request.URL.Path == "/v1/image" || c.Request.URL.Path == "/v1/file/upload" || c.Request.URL.Path == "/v1/batch") && claims.VerifyIssuer("casaos", true) {
42 | //Special treatment
43 | } else if !claims.VerifyExpiresAt(time.Now(), true) || !claims.VerifyIssuer("casaos", true) {
44 | code = common_err.ERROR_AUTH_TOKEN
45 | }
46 | if code != common_err.SUCCESS {
47 | c.JSON(code, model.Result{Success: code, Message: common_err.GetMsg(code)})
48 | c.Abort()
49 | return
50 | }
51 | c.Request.Header.Add("user_id", strconv.Itoa(claims.Id))
52 | c.Next()
53 | }
54 | }
55 |
56 | //get AccessToken
57 | func GetAccessToken(username, pwd string, id int) string {
58 | token, err := GenerateToken(username, pwd, id, "casaos", 3*time.Hour*time.Duration(1))
59 | if err == nil {
60 | return token
61 | } else {
62 | loger2.Error(fmt.Sprintf("Get Token Fail: %V", err))
63 | return ""
64 | }
65 | }
66 |
67 | func GetRefreshToken(username, pwd string, id int) string {
68 | token, err := GenerateToken(username, pwd, id, "refresh", 7*24*time.Hour*time.Duration(1))
69 | if err == nil {
70 | return token
71 | } else {
72 | loger2.Error(fmt.Sprintf("Get Token Fail: %V", err))
73 | return ""
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/web/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | CasaOS
22 |
23 |
24 |
25 |
26 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/app_request.yaml:
--------------------------------------------------------------------------------
1 | name: "App Request"
2 | description: "Request to add an app to the app store."
3 | title: "[App Request] AppName"
4 | labels: ["App Request"]
5 | body:
6 | - type: markdown
7 | attributes:
8 | value: |
9 | ### ❤ Thanks for taking the time to fill out this app request!
10 | > Before proceeding, please make sure that this app is not in App Store and no one has [requested](https://github.com/IceWhaleTech/CasaOS/labels/App%20Request) the same app before.
11 | > If you have already requested the app, please ask your friends to help add a 👍 to this issue. Then be patient and wait for the developers to work on it.
12 | > If you have any questions, please ask them on [Discord](https://discord.gg/knqAbbBbeX) or [Github Discussions](https://github.com/IceWhaleTech/CasaOS/discussions).
13 |
14 | - type: textarea
15 | id: app-info
16 | attributes:
17 | label: "App Information"
18 | description: "The formal information of this app, as detailed as possible."
19 | value: |
20 | - Name:
21 | - Short Description:
22 | - Official Website:
23 | - GitHub Repository:
24 | - Docker Image:
25 | validations:
26 | required: true
27 |
28 | - type: textarea
29 | id: why
30 | attributes:
31 | label: "Why do you want this app?"
32 | description: "Detailed notes can help developers and others understand the importance of this app."
33 | placeholder: |
34 | As a [what role], it helps me solve [what problem], and especially [what function] is great!
35 | or
36 | It solves [what problem] and especially [what feature] works well, which is hard to do with other app.
37 | or
38 | This is the app that [some device/service] must use and will not work without it.
39 | or
40 | others
41 |
42 | - type: textarea
43 | id: additional-info
44 | attributes:
45 | label: "Additional information?"
46 | description: "Anything else you want to share with the developers and others?"
47 | placeholder: |
48 | Example:
49 | - Noteworthy matters.
50 | - Recommended Docker image.
51 | - Validated Docker deployment instructions.
52 | - Notable Docker setup details.
53 | - Recommended config files, user data, accessible directory settings.
--------------------------------------------------------------------------------
/service/service.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: LinkLeong link@icewhale.com
3 | * @Date: 2022-07-12 09:48:56
4 | * @LastEditors: LinkLeong
5 | * @LastEditTime: 2022-07-27 10:28:48
6 | * @FilePath: /CasaOS/service/service.go
7 | * @Description:
8 | * @Website: https://www.casaos.io
9 | * Copyright (c) 2022 by icewhale, All Rights Reserved.
10 | */
11 | package service
12 |
13 | import (
14 | "github.com/gorilla/websocket"
15 | "github.com/patrickmn/go-cache"
16 | "gorm.io/gorm"
17 | )
18 |
19 | var Cache *cache.Cache
20 |
21 | var MyService Repository
22 |
23 | var WebSocketConns []*websocket.Conn
24 | var NewVersionApp map[string]string
25 | var SocketRun bool
26 |
27 | type Repository interface {
28 | App() AppService
29 | User() UserService
30 | Docker() DockerService
31 | Casa() CasaService
32 | Disk() DiskService
33 | Notify() NotifyServer
34 | Rely() RelyService
35 | System() SystemService
36 | Shares() SharesService
37 | Connections() ConnectionsService
38 | }
39 |
40 | func NewService(db *gorm.DB) Repository {
41 | return &store{
42 | app: NewAppService(db),
43 | user: NewUserService(db),
44 | docker: NewDockerService(),
45 | casa: NewCasaService(),
46 | disk: NewDiskService(db),
47 | notify: NewNotifyService(db),
48 | rely: NewRelyService(db),
49 | system: NewSystemService(),
50 | shares: NewSharesService(db),
51 | connections: NewConnectionsService(db),
52 | }
53 | }
54 |
55 | type store struct {
56 | db *gorm.DB
57 | app AppService
58 | user UserService
59 | docker DockerService
60 | casa CasaService
61 | disk DiskService
62 | notify NotifyServer
63 | rely RelyService
64 | system SystemService
65 | shares SharesService
66 | connections ConnectionsService
67 | }
68 |
69 | func (s *store) Connections() ConnectionsService {
70 | return s.connections
71 | }
72 | func (s *store) Shares() SharesService {
73 | return s.shares
74 | }
75 |
76 | func (c *store) Rely() RelyService {
77 | return c.rely
78 | }
79 |
80 | func (c *store) System() SystemService {
81 | return c.system
82 | }
83 | func (c *store) Notify() NotifyServer {
84 |
85 | return c.notify
86 | }
87 |
88 | func (c *store) App() AppService {
89 | return c.app
90 | }
91 |
92 | func (c *store) User() UserService {
93 | return c.user
94 | }
95 |
96 | func (c *store) Docker() DockerService {
97 | return c.docker
98 | }
99 |
100 | func (c *store) Casa() CasaService {
101 | return c.casa
102 | }
103 |
104 | func (c *store) Disk() DiskService {
105 | return c.disk
106 | }
107 |
--------------------------------------------------------------------------------
/service/docker_base/mysql.go:
--------------------------------------------------------------------------------
1 | package docker_base
2 |
3 | import (
4 | "context"
5 | "github.com/docker/docker/api/types"
6 | "github.com/docker/docker/api/types/container"
7 | "github.com/docker/docker/api/types/filters"
8 | "github.com/docker/docker/api/types/network"
9 | client2 "github.com/docker/docker/client"
10 | "time"
11 | )
12 |
13 | //创建一个mysql数据库
14 | func MysqlCreate(mysqlConfig MysqlConfig, dbId string, cpuShares int64, memory int64) (string, error) {
15 | const imageName = "mysql"
16 | const imageVersion = "8"
17 | const imageNet = "oasis"
18 |
19 | cli, err := client2.NewClientWithOpts(client2.FromEnv)
20 | if err != nil {
21 | return "", err
22 | }
23 | defer cli.Close()
24 | _, err = cli.ImagePull(context.Background(), imageName+":"+imageVersion, types.ImagePullOptions{})
25 |
26 | isExist := true
27 | //检查到镜像才继续
28 | for isExist {
29 | filter := filters.NewArgs()
30 | filter.Add("before", imageName+":"+imageVersion)
31 | list, e := cli.ImageList(context.Background(), types.ImageListOptions{Filters: filter})
32 | if e == nil && len(list) > 0 {
33 | isExist = false
34 | }
35 | time.Sleep(time.Second)
36 | }
37 |
38 | var envArr = []string{"MYSQL_ROOT_PASSWORD=" + mysqlConfig.DataBasePassword, "MYSQL_DATABASE=" + mysqlConfig.DataBaseDB}
39 |
40 | res := container.Resources{}
41 | if cpuShares > 0 {
42 | res.CPUShares = cpuShares
43 | }
44 | if memory > 0 {
45 | res.Memory = memory << 20
46 | }
47 |
48 | rp := container.RestartPolicy{}
49 |
50 | rp.Name = "always"
51 |
52 | config := &container.Config{
53 | Image: imageName,
54 | Labels: map[string]string{"version": imageVersion, "author": "official"},
55 | Env: envArr,
56 | }
57 | hostConfig := &container.HostConfig{Resources: res, RestartPolicy: rp, NetworkMode: container.NetworkMode(imageNet)}
58 |
59 | containerCreate, err := cli.ContainerCreate(context.Background(),
60 | config,
61 | hostConfig,
62 | &network.NetworkingConfig{EndpointsConfig: map[string]*network.EndpointSettings{imageNet: {NetworkID: ""}}},
63 | nil,
64 | dbId)
65 |
66 | containerId := containerCreate.ID
67 |
68 | //启动容器
69 | err = cli.ContainerStart(context.Background(), dbId, types.ContainerStartOptions{})
70 | if err != nil {
71 | return containerId, err
72 | }
73 |
74 | return containerId, nil
75 |
76 | }
77 |
78 | func MysqlDelete(dbId string) error {
79 |
80 | cli, err := client2.NewClientWithOpts(client2.FromEnv)
81 | if err != nil {
82 | return err
83 | }
84 | defer cli.Close()
85 | err = cli.ContainerStop(context.Background(), dbId, nil)
86 | if err != nil {
87 | return err
88 | }
89 |
90 | err = cli.ContainerRemove(context.Background(), dbId, types.ContainerRemoveOptions{})
91 | return err
92 |
93 | }
94 |
--------------------------------------------------------------------------------
/model/smartctl_model.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | //
4 | type SmartctlA struct {
5 | Smartctl struct {
6 | Version []int `json:"version"`
7 | SvnRevision string `json:"svn_revision"`
8 | PlatformInfo string `json:"platform_info"`
9 | BuildInfo string `json:"build_info"`
10 | Argv []string `json:"argv"`
11 | ExitStatus int `json:"exit_status"`
12 | } `json:"smartctl"`
13 | Device struct {
14 | Name string `json:"name"`
15 | InfoName string `json:"info_name"`
16 | Type string `json:"type"`
17 | Protocol string `json:"protocol"`
18 | } `json:"device"`
19 | ModelName string `json:"model_name"`
20 | SerialNumber string `json:"serial_number"`
21 | FirmwareVersion string `json:"firmware_version"`
22 | UserCapacity struct {
23 | Blocks int `json:"blocks"`
24 | Bytes int64 `json:"bytes"`
25 | } `json:"user_capacity"`
26 | SmartStatus struct {
27 | Passed bool `json:"passed"`
28 | } `json:"smart_status"`
29 | AtaSmartData struct {
30 | OfflineDataCollection struct {
31 | Status struct {
32 | Value int `json:"value"`
33 | String string `json:"string"`
34 | } `json:"status"`
35 | CompletionSeconds int `json:"completion_seconds"`
36 | } `json:"offline_data_collection"`
37 | SelfTest struct {
38 | Status struct {
39 | Value int `json:"value"`
40 | String string `json:"string"`
41 | Passed bool `json:"passed"`
42 | } `json:"status"`
43 | PollingMinutes struct {
44 | Short int `json:"short"`
45 | Extended int `json:"extended"`
46 | Conveyance int `json:"conveyance"`
47 | } `json:"polling_minutes"`
48 | } `json:"self_test"`
49 | Capabilities struct {
50 | Values []int `json:"values"`
51 | ExecOfflineImmediateSupported bool `json:"exec_offline_immediate_supported"`
52 | OfflineIsAbortedUponNewCmd bool `json:"offline_is_aborted_upon_new_cmd"`
53 | OfflineSurfaceScanSupported bool `json:"offline_surface_scan_supported"`
54 | SelfTestsSupported bool `json:"self_tests_supported"`
55 | ConveyanceSelfTestSupported bool `json:"conveyance_self_test_supported"`
56 | SelectiveSelfTestSupported bool `json:"selective_self_test_supported"`
57 | AttributeAutosaveEnabled bool `json:"attribute_autosave_enabled"`
58 | ErrorLoggingSupported bool `json:"error_logging_supported"`
59 | GpLoggingSupported bool `json:"gp_logging_supported"`
60 | } `json:"capabilities"`
61 | } `json:"ata_smart_data"`
62 | PowerOnTime struct {
63 | Hours int `json:"hours"`
64 | } `json:"power_on_time"`
65 | PowerCycleCount int `json:"power_cycle_count"`
66 | Temperature struct {
67 | Current int `json:"current"`
68 | } `json:"temperature"`
69 | }
70 |
--------------------------------------------------------------------------------
/pkg/utils/loger/log.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: LinkLeong link@icewhale.com
3 | * @Date: 2022-06-02 15:09:38
4 | * @LastEditors: LinkLeong
5 | * @LastEditTime: 2022-06-27 15:47:49
6 | * @FilePath: /CasaOS/pkg/utils/loger/log.go
7 | * @Description:
8 | * @Website: https://www.casaos.io
9 | * Copyright (c) 2022 by icewhale, All Rights Reserved.
10 | */
11 | package loger
12 |
13 | import (
14 | "fmt"
15 | "os"
16 | "path"
17 | "path/filepath"
18 | "runtime"
19 |
20 | "github.com/IceWhaleTech/CasaOS/pkg/config"
21 | "go.uber.org/zap"
22 | "go.uber.org/zap/zapcore"
23 | "gopkg.in/natefinch/lumberjack.v2"
24 | )
25 |
26 | var loggers *zap.Logger
27 |
28 | func getFileLogWriter() (writeSyncer zapcore.WriteSyncer) {
29 | // 使用 lumberjack 实现 log rotate
30 | lumberJackLogger := &lumberjack.Logger{
31 | Filename: filepath.Join(config.AppInfo.LogPath, fmt.Sprintf("%s.%s",
32 | config.AppInfo.LogSaveName,
33 | config.AppInfo.LogFileExt,
34 | )),
35 | MaxSize: 10,
36 | MaxBackups: 60,
37 | MaxAge: 1,
38 | Compress: true,
39 | }
40 |
41 | return zapcore.AddSync(lumberJackLogger)
42 | }
43 |
44 | func LogInit() {
45 | encoderConfig := zap.NewProductionEncoderConfig()
46 | encoderConfig.EncodeTime = zapcore.EpochTimeEncoder
47 | encoder := zapcore.NewJSONEncoder(encoderConfig)
48 | fileWriteSyncer := getFileLogWriter()
49 | core := zapcore.NewTee(
50 | zapcore.NewCore(encoder, zapcore.AddSync(os.Stdout), zapcore.InfoLevel),
51 | zapcore.NewCore(encoder, fileWriteSyncer, zapcore.InfoLevel),
52 | )
53 | loggers = zap.New(core)
54 |
55 | }
56 |
57 | func Info(message string, fields ...zap.Field) {
58 | callerFields := getCallerInfoForLog()
59 | fields = append(fields, callerFields...)
60 | loggers.Info(message, fields...)
61 | }
62 |
63 | func Debug(message string, fields ...zap.Field) {
64 | callerFields := getCallerInfoForLog()
65 | fields = append(fields, callerFields...)
66 | loggers.Debug(message, fields...)
67 | }
68 |
69 | func Error(message string, fields ...zap.Field) {
70 | callerFields := getCallerInfoForLog()
71 | fields = append(fields, callerFields...)
72 | loggers.Error(message, fields...)
73 | }
74 |
75 | func Warn(message string, fields ...zap.Field) {
76 | callerFields := getCallerInfoForLog()
77 | fields = append(fields, callerFields...)
78 | loggers.Warn(message, fields...)
79 | }
80 |
81 | func getCallerInfoForLog() (callerFields []zap.Field) {
82 |
83 | pc, file, line, ok := runtime.Caller(2) // 回溯两层,拿到写日志的调用方的函数信息
84 | if !ok {
85 | return
86 | }
87 | funcName := runtime.FuncForPC(pc).Name()
88 | funcName = path.Base(funcName) //Base函数返回路径的最后一个元素,只保留函数名
89 |
90 | callerFields = append(callerFields, zap.String("func", funcName), zap.String("file", file), zap.Int("line", line))
91 | return
92 | }
93 |
--------------------------------------------------------------------------------
/pkg/utils/command/command_helper.go:
--------------------------------------------------------------------------------
1 | package command
2 |
3 | import (
4 | "bufio"
5 | "context"
6 | "fmt"
7 | "io/ioutil"
8 | "os/exec"
9 | "time"
10 | )
11 |
12 | func OnlyExec(cmdStr string) {
13 | cmd := exec.Command("/bin/bash", "-c", cmdStr)
14 | stdout, err := cmd.StdoutPipe()
15 | if err != nil {
16 | return
17 | }
18 | defer stdout.Close()
19 | if err := cmd.Start(); err != nil {
20 | return
21 | }
22 | cmd.Wait()
23 | return
24 | }
25 |
26 | func ExecResultStrArray(cmdStr string) []string {
27 | cmd := exec.Command("/bin/bash", "-c", cmdStr)
28 | stdout, err := cmd.StdoutPipe()
29 | if err != nil {
30 | fmt.Println(err)
31 | return nil
32 | }
33 | defer stdout.Close()
34 | if err = cmd.Start(); err != nil {
35 | fmt.Println(err)
36 | return nil
37 | }
38 | //str, err := ioutil.ReadAll(stdout)
39 | var networklist = []string{}
40 | outputBuf := bufio.NewReader(stdout)
41 | for {
42 | output, _, err := outputBuf.ReadLine()
43 | if err != nil {
44 | if err.Error() != "EOF" {
45 | fmt.Printf("Error :%s\n", err)
46 | }
47 | break
48 | }
49 | networklist = append(networklist, string(output))
50 | }
51 | cmd.Wait()
52 | return networklist
53 | }
54 |
55 | func ExecResultStr(cmdStr string) string {
56 | cmd := exec.Command("/bin/bash", "-c", cmdStr)
57 | stdout, err := cmd.StdoutPipe()
58 | if err != nil {
59 | fmt.Println(err)
60 | return ""
61 | }
62 | defer stdout.Close()
63 | if err := cmd.Start(); err != nil {
64 | fmt.Println(err)
65 | return ""
66 | }
67 | str, err := ioutil.ReadAll(stdout)
68 | cmd.Wait()
69 | if err != nil {
70 | fmt.Println(err)
71 | return ""
72 | }
73 | return string(str)
74 | }
75 |
76 | //执行 lsblk 命令
77 | func ExecLSBLK() []byte {
78 | output, err := exec.Command("lsblk", "-O", "-J", "-b").Output()
79 | if err != nil {
80 | fmt.Println("lsblk", err)
81 | return nil
82 | }
83 | return output
84 | }
85 |
86 | //执行 lsblk 命令
87 | func ExecLSBLKByPath(path string) []byte {
88 | output, err := exec.Command("lsblk", path, "-O", "-J", "-b").Output()
89 | if err != nil {
90 | fmt.Println("lsblk", err)
91 | return nil
92 | }
93 | return output
94 | }
95 |
96 | //exec smart
97 | func ExecSmartCTLByPath(path string) []byte {
98 | timeout := 3
99 | ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
100 | defer cancel()
101 | output, err := exec.CommandContext(ctx, "smartctl", "-a", path, "-j").Output()
102 | if err != nil {
103 | fmt.Println("smartctl", err)
104 | return nil
105 | }
106 | return output
107 | }
108 |
109 | func ExecEnabledSMART(path string) {
110 |
111 | exec.Command("smartctl", "-s on", path).Output()
112 | }
113 |
--------------------------------------------------------------------------------
/service/connections.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: LinkLeong link@icewhale.org
3 | * @Date: 2022-07-26 18:13:22
4 | * @LastEditors: LinkLeong
5 | * @LastEditTime: 2022-08-04 20:10:31
6 | * @FilePath: /CasaOS/service/connections.go
7 | * @Description:
8 | * @Website: https://www.casaos.io
9 | * Copyright (c) 2022 by icewhale, All Rights Reserved.
10 | */
11 | package service
12 |
13 | import (
14 | "github.com/IceWhaleTech/CasaOS/pkg/config"
15 | command2 "github.com/IceWhaleTech/CasaOS/pkg/utils/command"
16 | "github.com/IceWhaleTech/CasaOS/service/model"
17 | model2 "github.com/IceWhaleTech/CasaOS/service/model"
18 | "gorm.io/gorm"
19 | )
20 |
21 | type ConnectionsService interface {
22 | GetConnectionsList() (connections []model2.ConnectionsDBModel)
23 | GetConnectionByHost(host string) (connections []model2.ConnectionsDBModel)
24 | GetConnectionByID(id string) (connections model2.ConnectionsDBModel)
25 | CreateConnection(connection *model2.ConnectionsDBModel)
26 | DeleteConnection(id string)
27 | UpdateConnection(connection *model2.ConnectionsDBModel)
28 | MountSmaba(username, host, directory, port, mountPoint, password string) string
29 | UnmountSmaba(mountPoint string) string
30 | }
31 |
32 | type connectionsStruct struct {
33 | db *gorm.DB
34 | }
35 |
36 | func (s *connectionsStruct) GetConnectionByHost(host string) (connections []model2.ConnectionsDBModel) {
37 | s.db.Select("username,host,status,id").Where("host = ?", host).Find(&connections)
38 | return
39 | }
40 | func (s *connectionsStruct) GetConnectionByID(id string) (connections model2.ConnectionsDBModel) {
41 | s.db.Select("username,password,host,status,id,directories,mount_point,port").Where("id = ?", id).First(&connections)
42 | return
43 | }
44 | func (s *connectionsStruct) GetConnectionsList() (connections []model2.ConnectionsDBModel) {
45 | s.db.Select("username,host,port,status,id,mount_point").Find(&connections)
46 | return
47 | }
48 | func (s *connectionsStruct) CreateConnection(connection *model2.ConnectionsDBModel) {
49 | s.db.Create(connection)
50 | }
51 | func (s *connectionsStruct) UpdateConnection(connection *model2.ConnectionsDBModel) {
52 | s.db.Save(connection)
53 | }
54 | func (s *connectionsStruct) DeleteConnection(id string) {
55 | s.db.Where("id= ?", id).Delete(&model.ConnectionsDBModel{})
56 | }
57 |
58 | func (s *connectionsStruct) MountSmaba(username, host, directory, port, mountPoint, password string) string {
59 | str := command2.ExecResultStr("source " + config.AppInfo.ShellPath + "/helper.sh ;MountCIFS " + username + " " + host + " " + directory + " " + port + " " + mountPoint + " " + password)
60 | return str
61 | }
62 | func (s *connectionsStruct) UnmountSmaba(mountPoint string) string {
63 | str := command2.ExecResultStr("source " + config.AppInfo.ShellPath + "/helper.sh ;UMountPorintAndRemoveDir " + mountPoint)
64 | return str
65 | }
66 |
67 | func NewConnectionsService(db *gorm.DB) ConnectionsService {
68 | return &connectionsStruct{db: db}
69 | }
70 |
--------------------------------------------------------------------------------
/pkg/config/init.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: LinkLeong link@icewhale.com
3 | * @Date: 2022-05-13 18:15:46
4 | * @LastEditors: LinkLeong
5 | * @LastEditTime: 2022-07-14 10:58:45
6 | * @FilePath: /CasaOS/pkg/config/init.go
7 | * @Description:
8 | * @Website: https://www.casaos.io
9 | * Copyright (c) 2022 by icewhale, All Rights Reserved.
10 | */
11 | package config
12 |
13 | import (
14 | "fmt"
15 | "log"
16 | "os"
17 | "path"
18 | "path/filepath"
19 | "runtime"
20 | "strings"
21 |
22 | "github.com/IceWhaleTech/CasaOS/model"
23 | "github.com/go-ini/ini"
24 | )
25 |
26 | //系统配置
27 | var SysInfo = &model.SysInfoModel{}
28 |
29 | //用户相关
30 | var UserInfo = &model.UserModel{}
31 |
32 | //用户相关
33 | var AppInfo = &model.APPModel{}
34 |
35 | //var RedisInfo = &model.RedisModel{}
36 |
37 | //server相关
38 | var ServerInfo = &model.ServerModel{}
39 |
40 | var SystemConfigInfo = &model.SystemConfig{}
41 |
42 | var CasaOSGlobalVariables = &model.CasaOSGlobalVariables{}
43 |
44 | var FileSettingInfo = &model.FileSetting{}
45 |
46 | var Cfg *ini.File
47 |
48 | //初始化设置,获取系统的部分信息。
49 | func InitSetup(config string) {
50 |
51 | var configDir = USERCONFIGURL
52 | if len(config) > 0 {
53 | configDir = config
54 | }
55 | if runtime.GOOS == "darwin" {
56 | configDir = "./conf/conf.conf"
57 | }
58 | var err error
59 | //读取文件
60 | Cfg, err = ini.Load(configDir)
61 | if err != nil {
62 | fmt.Printf("Fail to read file: %v", err)
63 | os.Exit(1)
64 | }
65 |
66 | mapTo("user", UserInfo)
67 | mapTo("app", AppInfo)
68 | //mapTo("redis", RedisInfo)
69 | mapTo("server", ServerInfo)
70 | mapTo("system", SystemConfigInfo)
71 | mapTo("file", FileSettingInfo)
72 | SystemConfigInfo.ConfigPath = configDir
73 | if len(AppInfo.DBPath) == 0 {
74 | AppInfo.DBPath = "/var/lib/casaos"
75 | Cfg.SaveTo(configDir)
76 | }
77 | if len(AppInfo.LogPath) == 0 {
78 | AppInfo.LogPath = "/var/log/casaos/"
79 | Cfg.SaveTo(configDir)
80 | }
81 | if len(AppInfo.ShellPath) == 0 {
82 | AppInfo.ShellPath = "/usr/share/casaos/shell"
83 | Cfg.SaveTo(configDir)
84 | }
85 | if len(AppInfo.UserDataPath) == 0 {
86 | AppInfo.UserDataPath = "/var/lib/casaos/conf"
87 | Cfg.SaveTo(configDir)
88 | }
89 | if len(AppInfo.TempPath) == 0 {
90 | AppInfo.TempPath = "/var/lib/casaos/temp"
91 | Cfg.SaveTo(configDir)
92 | }
93 | // AppInfo.ProjectPath = getCurrentDirectory() //os.Getwd()
94 |
95 | }
96 |
97 | //映射
98 | func mapTo(section string, v interface{}) {
99 | err := Cfg.Section(section).MapTo(v)
100 | if err != nil {
101 | log.Fatalf("Cfg.MapTo %s err: %v", section, err)
102 | }
103 | }
104 |
105 | // 获取当前执行文件绝对路径(go run)
106 | func getCurrentAbPathByCaller() string {
107 | var abPath string
108 | _, filename, _, ok := runtime.Caller(0)
109 | if ok {
110 | abPath = path.Dir(filename)
111 | }
112 | return abPath
113 | }
114 | func getCurrentDirectory() string {
115 | dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
116 | if err != nil {
117 | log.Fatal(err)
118 | }
119 | return strings.Replace(dir, "\\", "/", -1)
120 | }
121 |
--------------------------------------------------------------------------------
/service/model/o_container.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: LinkLeong link@icewhale.com
3 | * @Date: 2022-05-13 18:15:46
4 | * @LastEditors: LinkLeong
5 | * @LastEditTime: 2022-07-13 10:56:34
6 | * @FilePath: /CasaOS/service/model/o_container.go
7 | * @Description:
8 | * @Website: https://www.casaos.io
9 | * Copyright (c) 2022 by icewhale, All Rights Reserved.
10 | */
11 | package model
12 |
13 | const CONTAINERTABLENAME = "o_container"
14 |
15 | //Soon to be removed
16 | type AppListDBModel struct {
17 | CustomId string `gorm:"column:custom_id;primary_key" json:"custom_id"`
18 | Title string `json:"title"`
19 | // ScreenshotLink model.Strings `gorm:"type:json" json:"screenshot_link,omitempty"`
20 | ScreenshotLink string `json:"screenshot_link"`
21 | Slogan string `json:"slogan"`
22 | Description string `json:"description"`
23 | //Tags model.Strings `gorm:"type:json" json:"tags"`
24 | Tags string `json:"tags"`
25 | Icon string `json:"icon"`
26 | Version string `json:"version"`
27 | ContainerId string `json:"container_id,omitempty"`
28 | Image string `json:"image,omitempty"`
29 | Index string `json:"index"`
30 | CreatedAt string `gorm:"<-:create;autoCreateTime" json:"created_at"`
31 | UpdatedAt string `gorm:"<-:create;<-:update;autoUpdateTime" json:"updated_at"`
32 | //Port string `json:"port,omitempty"`
33 | PortMap string `json:"port_map"`
34 | Label string `json:"label"`
35 | EnableUPNP bool `json:"enable_upnp"`
36 | Envs string `json:"envs"`
37 | Ports string `json:"ports"`
38 | Volumes string `json:"volumes"`
39 | Devices string `json:"devices"`
40 | //Envs []model.Env `json:"envs"`
41 | //Ports []model.PortMap `gorm:"type:json" json:"ports"`
42 | //Volumes []model.PathMap `gorm:"type:json" json:"volumes"`
43 | //Devices []model.PathMap `gorm:"type:json" json:"device"`
44 | Position bool `json:"position"`
45 | NetModel string `json:"net_model"`
46 | CpuShares int64 `json:"cpu_shares"`
47 | Memory int64 `json:"memory"`
48 | Restart string `json:"restart"`
49 | //Rely model.MapStrings `gorm:"type:json" json:"rely"` //[{"mysql":"id"},{"mysql":"id"}]
50 | Origin string `json:"origin"`
51 | HostName string `json:"host_name"`
52 | Privileged bool `json:"privileged"`
53 | CapAdd string `json:"cap_add"`
54 | Cmd string `gorm:"type:json" json:"cmd"`
55 | }
56 |
57 | func (p *AppListDBModel) TableName() string {
58 | return "o_container"
59 | }
60 |
61 | type MyAppList struct {
62 | Id string `json:"id"`
63 | Name string `json:"name"`
64 | Icon string `json:"icon"`
65 | State string `json:"state"`
66 | CustomId string `gorm:"column:custom_id;primary_key" json:"custom_id"`
67 | Index string `json:"index"`
68 | //Order string `json:"order"`
69 | Port string `json:"port"`
70 | Slogan string `json:"slogan"`
71 | Type string `json:"type"`
72 | //Rely model.MapStrings `json:"rely"` //[{"mysql":"id"},{"mysql":"id"}]
73 | Image string `json:"image"`
74 | Volumes string `json:"volumes"`
75 | Latest bool `json:"latest"`
76 | Host string `json:"host"`
77 | Protocol string `json:"protocol"`
78 | }
79 |
--------------------------------------------------------------------------------
/service/user.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: LinkLeong link@icewhale.com
3 | * @Date: 2022-03-18 11:40:55
4 | * @LastEditors: LinkLeong
5 | * @LastEditTime: 2022-07-12 10:05:37
6 | * @FilePath: /CasaOS/service/user.go
7 | * @Description:
8 | * @Website: https://www.casaos.io
9 | * Copyright (c) 2022 by icewhale, All Rights Reserved.
10 | */
11 | package service
12 |
13 | import (
14 | "io"
15 | "mime/multipart"
16 | "os"
17 |
18 | "github.com/IceWhaleTech/CasaOS/service/model"
19 | "gorm.io/gorm"
20 | )
21 |
22 | type UserService interface {
23 | UpLoadFile(file multipart.File, name string) error
24 | CreateUser(m model.UserDBModel) model.UserDBModel
25 | GetUserCount() (userCount int64)
26 | UpdateUser(m model.UserDBModel)
27 | UpdateUserPassword(m model.UserDBModel)
28 | GetUserInfoById(id string) (m model.UserDBModel)
29 | GetUserAllInfoById(id string) (m model.UserDBModel)
30 | GetUserAllInfoByName(userName string) (m model.UserDBModel)
31 | DeleteUserById(id string)
32 | DeleteAllUser()
33 | GetUserInfoByUserName(userName string) (m model.UserDBModel)
34 | GetAllUserName() (list []model.UserDBModel)
35 | }
36 |
37 | var UserRegisterHash = make(map[string]string)
38 |
39 | type userService struct {
40 | db *gorm.DB
41 | }
42 |
43 | func (u *userService) DeleteAllUser() {
44 | u.db.Where("1=1").Delete(&model.UserDBModel{})
45 | }
46 | func (u *userService) DeleteUserById(id string) {
47 | u.db.Where("id= ?", id).Delete(&model.UserDBModel{})
48 | }
49 |
50 | func (u *userService) GetAllUserName() (list []model.UserDBModel) {
51 | u.db.Select("username").Find(&list)
52 | return
53 | }
54 | func (u *userService) CreateUser(m model.UserDBModel) model.UserDBModel {
55 | u.db.Create(&m)
56 | return m
57 | }
58 |
59 | func (u *userService) GetUserCount() (userCount int64) {
60 | u.db.Find(&model.UserDBModel{}).Count(&userCount)
61 | return
62 | }
63 |
64 | func (u *userService) UpdateUser(m model.UserDBModel) {
65 | u.db.Model(&m).Omit("password").Updates(&m)
66 | }
67 | func (u *userService) UpdateUserPassword(m model.UserDBModel) {
68 | u.db.Model(&m).Update("password", m.Password)
69 | }
70 | func (u *userService) GetUserAllInfoById(id string) (m model.UserDBModel) {
71 | u.db.Where("id= ?", id).First(&m)
72 | return
73 | }
74 | func (u *userService) GetUserAllInfoByName(userName string) (m model.UserDBModel) {
75 | u.db.Where("username= ?", userName).First(&m)
76 | return
77 | }
78 | func (u *userService) GetUserInfoById(id string) (m model.UserDBModel) {
79 | u.db.Select("username", "id", "role", "nickname", "description", "avatar", "email").Where("id= ?", id).First(&m)
80 | return
81 | }
82 |
83 | func (u *userService) GetUserInfoByUserName(userName string) (m model.UserDBModel) {
84 | u.db.Select("username", "id", "role", "nickname", "description", "avatar", "email").Where("username= ?", userName).First(&m)
85 | return
86 | }
87 |
88 | //上传文件
89 | func (c *userService) UpLoadFile(file multipart.File, url string) error {
90 | out, _ := os.OpenFile(url, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644)
91 | defer out.Close()
92 | io.Copy(out, file)
93 | return nil
94 | }
95 |
96 | //获取用户Service
97 | func NewUserService(db *gorm.DB) UserService {
98 | return &userService{db: db}
99 | }
100 |
--------------------------------------------------------------------------------
/route/v1/storage.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: LinkLeong link@icewhale.com
3 | * @Date: 2022-07-11 16:02:29
4 | * @LastEditors: LinkLeong
5 | * @LastEditTime: 2022-08-17 19:14:50
6 | * @FilePath: /CasaOS/route/v1/storage.go
7 | * @Description:
8 | * @Website: https://www.casaos.io
9 | * Copyright (c) 2022 by icewhale, All Rights Reserved.
10 | */
11 | package v1
12 |
13 | import (
14 | "path/filepath"
15 | "reflect"
16 |
17 | "github.com/IceWhaleTech/CasaOS/model"
18 | "github.com/IceWhaleTech/CasaOS/pkg/utils/common_err"
19 | "github.com/IceWhaleTech/CasaOS/service"
20 | "github.com/gin-gonic/gin"
21 | )
22 |
23 | func GetStorageList(c *gin.Context) {
24 | system := c.Query("system")
25 | storages := []model.Storages{}
26 | disks := service.MyService.Disk().LSBLK(false)
27 | diskNumber := 1
28 | children := 1
29 | findSystem := 0
30 | for _, d := range disks {
31 | if d.Tran != "usb" {
32 | tempSystemDisk := false
33 | children = 1
34 | tempDisk := model.Storages{
35 | DiskName: d.Model,
36 | Path: d.Path,
37 | Size: d.Size,
38 | }
39 |
40 | storageArr := []model.Storage{}
41 | temp := service.MyService.Disk().SmartCTL(d.Path)
42 | if reflect.DeepEqual(temp, model.SmartctlA{}) {
43 | temp.SmartStatus.Passed = true
44 | }
45 | for _, v := range d.Children {
46 | if v.MountPoint != "" {
47 | if findSystem == 0 {
48 | if v.MountPoint == "/" {
49 | tempDisk.DiskName = "System"
50 | findSystem = 1
51 | tempSystemDisk = true
52 | }
53 | if len(v.Children) > 0 {
54 | for _, c := range v.Children {
55 | if c.MountPoint == "/" {
56 | tempDisk.DiskName = "System"
57 | findSystem = 1
58 | tempSystemDisk = true
59 | break
60 | }
61 | }
62 | }
63 | }
64 |
65 | stor := model.Storage{}
66 | stor.MountPoint = v.MountPoint
67 | stor.Size = v.FSSize
68 | stor.Avail = v.FSAvail
69 | stor.Path = v.Path
70 | stor.Type = v.FsType
71 | stor.DriveName = v.Name
72 | if len(v.Label) == 0 {
73 | if stor.MountPoint == "/" {
74 | stor.Label = "System"
75 | } else {
76 | stor.Label = filepath.Base(stor.MountPoint)
77 | }
78 |
79 | children += 1
80 | } else {
81 | stor.Label = v.Label
82 | }
83 | storageArr = append(storageArr, stor)
84 | }
85 | }
86 |
87 | if len(storageArr) > 0 {
88 | if tempSystemDisk && len(system) > 0 {
89 | tempStorageArr := []model.Storage{}
90 | for i := 0; i < len(storageArr); i++ {
91 | if storageArr[i].MountPoint != "/boot/efi" && storageArr[i].Type != "swap" {
92 | tempStorageArr = append(tempStorageArr, storageArr[i])
93 | }
94 | }
95 | tempDisk.Children = tempStorageArr
96 | storages = append(storages, tempDisk)
97 | diskNumber += 1
98 | } else if !tempSystemDisk {
99 | tempDisk.Children = storageArr
100 | storages = append(storages, tempDisk)
101 | diskNumber += 1
102 | }
103 |
104 | }
105 | }
106 | }
107 |
108 | c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: storages})
109 | }
110 |
--------------------------------------------------------------------------------
/shell/delete-old-service.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | ###
3 | # @Author: LinkLeong link@icewhale.com
4 | # @Date: 2022-06-30 10:08:33
5 | # @LastEditors: LinkLeong
6 | # @LastEditTime: 2022-07-01 11:17:54
7 | # @FilePath: /CasaOS/shell/delete-old-service.sh
8 | # @Description:
9 | ###
10 |
11 | ((EUID)) && sudo_cmd="sudo"
12 |
13 | # SYSTEM INFO
14 | readonly UNAME_M="$(uname -m)"
15 |
16 | # CasaOS PATHS
17 | readonly CASA_REPO=IceWhaleTech/CasaOS
18 | readonly CASA_UNZIP_TEMP_FOLDER=/tmp/casaos
19 | readonly CASA_BIN=casaos
20 | readonly CASA_BIN_PATH=/usr/bin/casaos
21 | readonly CASA_CONF_PATH=/etc/casaos.conf
22 | readonly CASA_SERVICE_PATH=/etc/systemd/system/casaos.service
23 | readonly CASA_HELPER_PATH=/usr/share/casaos/shell/
24 | readonly CASA_USER_CONF_PATH=/var/lib/casaos/conf/
25 | readonly CASA_DB_PATH=/var/lib/casaos/db/
26 | readonly CASA_TEMP_PATH=/var/lib/casaos/temp/
27 | readonly CASA_LOGS_PATH=/var/log/casaos/
28 | readonly CASA_PACKAGE_EXT=".tar.gz"
29 | readonly CASA_RELEASE_API="https://api.github.com/repos/${CASA_REPO}/releases"
30 | readonly CASA_OPENWRT_DOCS="https://github.com/IceWhaleTech/CasaOS-OpenWrt"
31 |
32 | readonly COLOUR_RESET='\e[0m'
33 | readonly aCOLOUR=(
34 | '\e[38;5;154m' # green | Lines, bullets and separators
35 | '\e[1m' # Bold white | Main descriptions
36 | '\e[90m' # Grey | Credits
37 | '\e[91m' # Red | Update notifications Alert
38 | '\e[33m' # Yellow | Emphasis
39 | )
40 |
41 | Target_Arch=""
42 | Target_Distro="debian"
43 | Target_OS="linux"
44 | Casa_Tag=""
45 |
46 |
47 | #######################################
48 | # Custom printing function
49 | # Globals:
50 | # None
51 | # Arguments:
52 | # $1 0:OK 1:FAILED 2:INFO 3:NOTICE
53 | # message
54 | # Returns:
55 | # None
56 | #######################################
57 |
58 | Show() {
59 | # OK
60 | if (($1 == 0)); then
61 | echo -e "${aCOLOUR[2]}[$COLOUR_RESET${aCOLOUR[0]} OK $COLOUR_RESET${aCOLOUR[2]}]$COLOUR_RESET $2"
62 | # FAILED
63 | elif (($1 == 1)); then
64 | echo -e "${aCOLOUR[2]}[$COLOUR_RESET${aCOLOUR[3]}FAILED$COLOUR_RESET${aCOLOUR[2]}]$COLOUR_RESET $2"
65 | # INFO
66 | elif (($1 == 2)); then
67 | echo -e "${aCOLOUR[2]}[$COLOUR_RESET${aCOLOUR[0]} INFO $COLOUR_RESET${aCOLOUR[2]}]$COLOUR_RESET $2"
68 | # NOTICE
69 | elif (($1 == 3)); then
70 | echo -e "${aCOLOUR[2]}[$COLOUR_RESET${aCOLOUR[4]}NOTICE$COLOUR_RESET${aCOLOUR[2]}]$COLOUR_RESET $2"
71 | fi
72 | }
73 |
74 | Warn() {
75 | echo -e "${aCOLOUR[3]}$1$COLOUR_RESET"
76 | }
77 |
78 | # 0 Check_exist
79 | Check_Exist() {
80 | #Create Dir
81 | Show 2 "Create Folders."
82 | ${sudo_cmd} mkdir -p ${CASA_HELPER_PATH}
83 | ${sudo_cmd} mkdir -p ${CASA_LOGS_PATH}
84 | ${sudo_cmd} mkdir -p ${CASA_USER_CONF_PATH}
85 | ${sudo_cmd} mkdir -p ${CASA_DB_PATH}
86 | ${sudo_cmd} mkdir -p ${CASA_TEMP_PATH}
87 |
88 |
89 | Show 2 "Start cleaning up the old version."
90 |
91 | ${sudo_cmd} rm -rf /usr/lib/systemd/system/casaos.service
92 |
93 | ${sudo_cmd} rm -rf /lib/systemd/system/casaos.service
94 |
95 | ${sudo_cmd} rm -rf /usr/local/bin/${CASA_BIN}
96 |
97 | #Clean
98 | if [[ -d "/casaOS" ]]; then
99 | ${sudo_cmd} rm -rf /casaOS
100 | fi
101 | Show 0 "Clearance completed."
102 |
103 | $sudo_cmd systemctl restart ${CASA_BIN}
104 | }
105 | Check_Exist
106 |
--------------------------------------------------------------------------------
/model/disk.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: LinkLeong link@icewhale.com
3 | * @Date: 2022-07-13 10:43:45
4 | * @LastEditors: LinkLeong
5 | * @LastEditTime: 2022-08-03 14:45:35
6 | * @FilePath: /CasaOS/model/disk.go
7 | * @Description:
8 | * @Website: https://www.casaos.io
9 | * Copyright (c) 2022 by icewhale, All Rights Reserved.
10 | */
11 | package model
12 |
13 | type LSBLKModel struct {
14 | Name string `json:"name"`
15 | FsType string `json:"fstype"`
16 | Size uint64 `json:"size"`
17 | FSSize string `json:"fssize"`
18 | Path string `json:"path"`
19 | Model string `json:"model"` //设备标识符
20 | RM bool `json:"rm"` //是否为可移动设备
21 | RO bool `json:"ro"` //是否为只读设备
22 | State string `json:"state"`
23 | PhySec int `json:"phy-sec"` //物理扇区大小
24 | Type string `json:"type"`
25 | Vendor string `json:"vendor"` //供应商
26 | Rev string `json:"rev"` //修订版本
27 | FSAvail string `json:"fsavail"` //可用空间
28 | FSUse string `json:"fsuse%"` //已用百分比
29 | MountPoint string `json:"mountpoint"`
30 | Format string `json:"format"`
31 | Health string `json:"health"`
32 | HotPlug bool `json:"hotplug"`
33 | UUID string `json:"uuid"`
34 | FSUsed string `json:"fsused"`
35 | Temperature int `json:"temperature"`
36 | Tran string `json:"tran"`
37 | MinIO uint64 `json:"min-io"`
38 | UsedPercent float64 `json:"used_percent"`
39 | Serial string `json:"serial"`
40 | Children []LSBLKModel `json:"children"`
41 | SubSystems string `json:"subsystems"`
42 | Label string `json:"label"`
43 | //详情特有
44 | StartSector uint64 `json:"start_sector,omitempty"`
45 | Rota bool `json:"rota"` //true(hhd) false(ssd)
46 | DiskType string `json:"disk_type"`
47 | EndSector uint64 `json:"end_sector,omitempty"`
48 | }
49 |
50 | type Drive struct {
51 | Name string `json:"name"`
52 | Size uint64 `json:"size"`
53 | Model string `json:"model"`
54 | Health string `json:"health"`
55 | Temperature int `json:"temperature"`
56 | DiskType string `json:"disk_type"`
57 | NeedFormat bool `json:"need_format"`
58 | Serial string `json:"serial"`
59 | Path string `json:"path"`
60 | ChildrenNumber int `json:"children_number"`
61 | }
62 |
63 | type DriveUSB struct {
64 | Name string `json:"name"`
65 | Size uint64 `json:"size"`
66 | Model string `json:"model"`
67 | Avail uint64 `json:"avail"`
68 | Children []USBChildren `json:"children"`
69 | }
70 | type USBChildren struct {
71 | Name string `json:"name"`
72 | Size uint64 `json:"size"`
73 | Avail uint64 `json:"avail"`
74 | MountPoint string `json:"mount_point"`
75 | }
76 |
77 | type Storage struct {
78 | MountPoint string `json:"mount_point"`
79 | Size string `json:"size"`
80 | Avail string `json:"avail"` //可用空间
81 | Type string `json:"type"`
82 | Path string `json:"path"`
83 | DriveName string `json:"drive_name"`
84 | Label string `json:"label"`
85 | }
86 | type Storages struct {
87 | DiskName string `json:"disk_name"`
88 | Size uint64 `json:"size"`
89 | Path string `json:"path"`
90 | Children []Storage `json:"children"`
91 | }
92 |
93 | type Summary struct {
94 | Size uint64 `json:"size"`
95 | Avail uint64 `json:"avail"` //可用空间
96 | Health bool `json:"health"`
97 | Used uint64 `json:"used"`
98 | }
99 |
--------------------------------------------------------------------------------
/.github/workflows/demo.yml:
--------------------------------------------------------------------------------
1 | name: Demo Reset
2 |
3 | # Controls when the workflow will run
4 | on:
5 | schedule:
6 | - cron: "0 * * * *"
7 |
8 | # Allows you to run this workflow manually from the Actions tab
9 | workflow_dispatch:
10 |
11 | # 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')
12 | # 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')
13 | # NEW_INSTANCE_NAME=CasaOS-Demo-$(date +%s)
14 |
15 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel
16 | jobs:
17 | # This workflow contains a single job called "build"
18 | reset:
19 | # The type of runner that the job will run on
20 | runs-on: ubuntu-latest
21 |
22 | # Steps represent a sequence of tasks that will be executed as part of the job
23 | steps:
24 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
25 | - uses: actions/checkout@v2
26 |
27 | - name: Configure AWS credentials from Test account
28 | uses: aws-actions/configure-aws-credentials@v1
29 | with:
30 | aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
31 | aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
32 | aws-region: us-west-2
33 |
34 | - name: Get old instance and snapshot name, create new instance name
35 | run: |
36 | echo "OLD_INSTANCE_SNAPSHOT_NAME=$(aws lightsail get-instance-snapshots | grep '"name": "0.3.3-demo-1658402149' | sed 's/ //g' | sed 's/"//g' | sed 's/,//g' | sed 's/name://g')" >> $GITHUB_ENV
37 | 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
38 | echo "NEW_INSTANCE_NAME=CasaOS-Demo-$(date +%s)" >> $GITHUB_ENV
39 |
40 | - name: Create instances from snapshot
41 | run: |
42 | aws lightsail create-instances-from-snapshot \
43 | --instance-snapshot-name ${{ env.OLD_INSTANCE_SNAPSHOT_NAME }} \
44 | --instance-names ${{ env.NEW_INSTANCE_NAME }} \
45 | --availability-zone us-west-2a \
46 | --bundle-id medium_2_0
47 |
48 | - name: Wait for new instance running
49 | run: |
50 | TIMEOUT=$(($(date +%s)+600))
51 | while [ $TIMEOUT -gt $(date +%s) ]
52 | do
53 | 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')
54 | if [ $NEW_INSTANCE_STATE == running ]
55 | then
56 | echo "New instance is running now"
57 | sleep 10s
58 | break
59 | fi
60 | done
61 |
62 | - name: Put instance public ports
63 | run: |
64 | aws lightsail put-instance-public-ports \
65 | --port-infos fromPort=0,toPort=65535,protocol=all \
66 | --instance-name ${{ env.NEW_INSTANCE_NAME }}
67 |
68 | - name: Attach static ip
69 | run: |
70 | aws lightsail attach-static-ip \
71 | --static-ip-name CasaOS-Demo-IP \
72 | --instance-name ${{ env.NEW_INSTANCE_NAME }}
73 |
74 | - name: Delete old instance
75 | run: |
76 | aws lightsail delete-instance \
77 | --instance-name ${{ env.OLD_INSTANCE_NAME }}
78 |
79 |
80 |
--------------------------------------------------------------------------------
/pkg/utils/file/reader.go:
--------------------------------------------------------------------------------
1 | package file
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "io"
7 | "os"
8 | )
9 |
10 | var (
11 | buffSize = 1 << 20
12 | )
13 |
14 | // ReadLineFromEnd --
15 | type ReadLineFromEnd struct {
16 | f *os.File
17 |
18 | fileSize int
19 | bwr *bytes.Buffer
20 | lineBuff []byte
21 | swapBuff []byte
22 |
23 | isFirst bool
24 | }
25 |
26 | // NewReadLineFromEnd --
27 | func NewReadLineFromEnd(name string) (rd *ReadLineFromEnd, err error) {
28 | f, err := os.Open(name)
29 | if err != nil {
30 | return nil, err
31 | }
32 | info, err := f.Stat()
33 | if info.IsDir() {
34 | return nil, fmt.Errorf("not file")
35 | }
36 | fileSize := int(info.Size())
37 | rd = &ReadLineFromEnd{
38 | f: f,
39 | fileSize: fileSize,
40 | bwr: bytes.NewBuffer([]byte{}),
41 | lineBuff: make([]byte, 0),
42 | swapBuff: make([]byte, buffSize),
43 | isFirst: true,
44 | }
45 | return rd, nil
46 | }
47 |
48 | // ReadLine 结尾包含'\n'
49 | func (c *ReadLineFromEnd) ReadLine() (line []byte, err error) {
50 | var ok bool
51 | for {
52 | ok, err = c.buff()
53 | if err != nil {
54 | return nil, err
55 | }
56 | if ok {
57 | break
58 | }
59 | }
60 | line, err = c.bwr.ReadBytes('\n')
61 | if err == io.EOF && c.fileSize > 0 {
62 | err = nil
63 | }
64 | return line, err
65 | }
66 |
67 | // Close --
68 | func (c *ReadLineFromEnd) Close() (err error) {
69 | return c.f.Close()
70 | }
71 |
72 | func (c *ReadLineFromEnd) buff() (ok bool, err error) {
73 | if c.fileSize == 0 {
74 | return true, nil
75 | }
76 |
77 | if c.bwr.Len() >= buffSize {
78 | return true, nil
79 | }
80 |
81 | offset := 0
82 | if c.fileSize > buffSize {
83 | offset = c.fileSize - buffSize
84 | }
85 | _, err = c.f.Seek(int64(offset), 0)
86 | if err != nil {
87 | return false, err
88 | }
89 |
90 | n, err := c.f.Read(c.swapBuff)
91 | if err != nil && err != io.EOF {
92 | return false, err
93 | }
94 | if c.fileSize < n {
95 | n = c.fileSize
96 | }
97 | if n == 0 {
98 | return true, nil
99 | }
100 |
101 | for {
102 | m := bytes.LastIndex(c.swapBuff[:n], []byte{'\n'})
103 | if m == -1 {
104 | break
105 | }
106 | if m < n-1 {
107 | err = c.writeLine(c.swapBuff[m+1 : n])
108 | if err != nil {
109 | return false, err
110 | }
111 | ok = true
112 | } else if m == n-1 && !c.isFirst {
113 | err = c.writeLine(nil)
114 | if err != nil {
115 | return false, err
116 | }
117 | ok = true
118 | }
119 | n = m
120 | if n == 0 {
121 | break
122 | }
123 | }
124 | if n > 0 {
125 | reverseBytes(c.swapBuff[:n])
126 | c.lineBuff = append(c.lineBuff, c.swapBuff[:n]...)
127 | }
128 | if offset == 0 {
129 | err = c.writeLine(nil)
130 | if err != nil {
131 | return false, err
132 | }
133 | ok = true
134 | }
135 | c.fileSize = offset
136 | if c.isFirst {
137 | c.isFirst = false
138 | }
139 | return ok, nil
140 | }
141 |
142 | func (c *ReadLineFromEnd) writeLine(b []byte) (err error) {
143 | if len(b) > 0 {
144 | _, err = c.bwr.Write(b)
145 | if err != nil {
146 | return err
147 | }
148 | }
149 | if len(c.lineBuff) > 0 {
150 | reverseBytes(c.lineBuff)
151 | _, err = c.bwr.Write(c.lineBuff)
152 | if err != nil {
153 | return err
154 | }
155 | c.lineBuff = c.lineBuff[:0]
156 | }
157 | _, err = c.bwr.Write([]byte{'\n'})
158 | if err != nil {
159 | return err
160 | }
161 | return nil
162 | }
163 |
164 | func reverseBytes(b []byte) {
165 | n := len(b)
166 | if n <= 1 {
167 | return
168 | }
169 | for i := 0; i < n; i++ {
170 | k := n - 1
171 | if k != i {
172 | b[i], b[k] = b[k], b[i]
173 | }
174 | n--
175 | }
176 | }
177 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/IceWhaleTech/CasaOS
2 |
3 | go 1.16
4 |
5 | require (
6 | github.com/Curtis-Milo/nat-type-identifier-go v0.0.0-20220215191915-18d42168c63d
7 | github.com/Microsoft/go-winio v0.5.0 // indirect
8 | github.com/Microsoft/hcsshim v0.8.22 // indirect
9 | github.com/ambelovsky/go-structs v1.1.0 // indirect
10 | github.com/ambelovsky/gosf v0.0.0-20201109201340-237aea4d6109
11 | github.com/ambelovsky/gosf-socketio v0.0.0-20201109193639-add9d32f8b19 // indirect
12 | github.com/bits-and-blooms/bitset v1.2.1 // indirect
13 | github.com/containerd/containerd v1.5.7
14 | github.com/containerd/continuity v0.2.0 // indirect
15 | github.com/disintegration/imaging v1.6.2
16 | github.com/docker/distribution v2.8.0+incompatible // indirect
17 | github.com/docker/docker v20.10.7+incompatible
18 | github.com/docker/go-connections v0.4.0
19 | github.com/dsoprea/go-exif/v3 v3.0.0-20210625224831-a6301f85c82b
20 | github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd // indirect
21 | github.com/gin-contrib/gzip v0.0.2
22 | github.com/gin-gonic/gin v1.7.2
23 | github.com/go-ini/ini v1.62.0
24 | github.com/go-playground/validator/v10 v10.6.1 // indirect
25 | github.com/gogo/googleapis v1.4.1 // indirect
26 | github.com/golang-jwt/jwt/v4 v4.4.1
27 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
28 | github.com/golang/mock v1.6.0
29 | github.com/gomodule/redigo v1.8.5
30 | github.com/google/go-github/v36 v36.0.0
31 | github.com/google/uuid v1.3.0 // indirect
32 | github.com/googollee/go-socket.io v1.6.2
33 | github.com/gorilla/mux v1.8.0 // indirect
34 | github.com/gorilla/websocket v1.4.2
35 | github.com/hirochachacha/go-smb2 v1.1.0
36 | github.com/jinzhu/copier v0.3.2
37 | github.com/json-iterator/go v1.1.11 // indirect
38 | github.com/klauspost/compress v1.13.6 // indirect
39 | github.com/kr/text v0.2.0 // indirect
40 | github.com/leodido/go-urn v1.2.1 // indirect
41 | github.com/lucas-clemente/quic-go v0.25.0
42 | github.com/mattn/go-isatty v0.0.14 // indirect
43 | github.com/mattn/go-sqlite3 v1.14.11 // indirect
44 | github.com/mholt/archiver/v3 v3.5.1
45 | github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect
46 | github.com/morikuni/aec v1.0.0 // indirect
47 | github.com/opencontainers/image-spec v1.0.2 // indirect
48 | github.com/opencontainers/selinux v1.8.5 // indirect
49 | github.com/patrickmn/go-cache v2.1.0+incompatible
50 | github.com/pilebones/go-udev v0.9.0
51 | github.com/pkg/errors v0.9.1
52 | github.com/prometheus/procfs v0.7.3 // indirect
53 | github.com/robfig/cron v1.2.0
54 | github.com/satori/go.uuid v1.2.0
55 | github.com/shirou/gopsutil/v3 v3.22.7
56 | github.com/sirupsen/logrus v1.8.1
57 | github.com/smartystreets/assertions v1.2.0 // indirect
58 | github.com/smartystreets/goconvey v1.6.4 // indirect
59 | github.com/stretchr/testify v1.8.0
60 | github.com/tidwall/gjson v1.10.2
61 | github.com/ugorji/go v1.2.6 // indirect
62 | go.opencensus.io v0.23.0 // indirect
63 | go.uber.org/zap v1.10.0
64 | golang.org/x/crypto v0.0.0-20210817164053-32db794688a5
65 | golang.org/x/mod v0.5.0 // indirect
66 | golang.org/x/net v0.0.0-20211020060615-d418f374d309 // indirect
67 | golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f
68 | golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b // indirect
69 | golang.org/x/text v0.3.7 // indirect
70 | golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
71 | golang.org/x/tools v0.1.7 // indirect
72 | google.golang.org/appengine v1.6.7 // indirect
73 | google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0 // indirect
74 | google.golang.org/grpc v1.41.0 // indirect
75 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
76 | gopkg.in/ini.v1 v1.62.0 // indirect
77 | gopkg.in/natefinch/lumberjack.v2 v2.0.0
78 | gorm.io/driver/sqlite v1.2.6
79 | gorm.io/gorm v1.22.5
80 | )
81 |
--------------------------------------------------------------------------------
/shell/usb-mount.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # copy to /casaOS/util/shell path
4 | # chmod 755
5 |
6 | log="logger -t usb-mount.sh -s "
7 |
8 | ACTION=$1
9 |
10 | DEVBASE=$2
11 |
12 | DEVICE="/dev/${DEVBASE}"
13 |
14 | # See if this drive is already mounted, and if so where
15 | MOUNT_POINT=$(lsblk -l -p -o name,mountpoint | grep ${DEVICE} | awk '{print $2}')
16 |
17 | do_mount() {
18 |
19 | if [ -n "${MOUNT_POINT}" ]; then
20 | ${log} "Warning: ${DEVICE} is already mounted at ${MOUNT_POINT}"
21 | exit 1
22 | fi
23 |
24 | # Get info for this drive: $ID_FS_LABEL and $ID_FS_TYPE
25 | eval $(blkid -o udev ${DEVICE} | grep -i -e "ID_FS_LABEL" -e "ID_FS_TYPE")
26 |
27 | #ID_FS_LABEL=新加卷
28 | #ID_FS_LABEL_ENC=新加卷
29 | #ID_FS_TYPE=ntfs
30 |
31 | # Figure out a mount point to use
32 | # LABEL=${ID_FS_LABEL}
33 | LABEL=${DEVBASE}
34 | if grep -q " /DATA/USB_Storage_${LABEL} " /etc/mtab; then
35 | # Already in use, make a unique one
36 | LABEL+="_${DEVBASE}"
37 | fi
38 | DEV_LABEL="${LABEL}"
39 |
40 | # Use the device name in case the drive doesn't have label
41 | if [ -z ${DEV_LABEL} ]; then
42 | DEV_LABEL="${DEVBASE}"
43 | fi
44 |
45 |
46 | MOUNT_POINT="/DATA/USB_Storage_${DEV_LABEL}"
47 |
48 | ${log} "Mount point: ${MOUNT_POINT}"
49 |
50 | mkdir -p ${MOUNT_POINT}
51 |
52 |
53 | # MOUNT_POINT="/DATA/USB_Storage1"
54 | # arr=("/DATA/USB_Storage1" "/DATA/USB_Storage2" "/DATA/USB_Storage3" "/DATA/USB_Storage4" "/DATA/USB_Storage5" "/DATA/USB_Storage6" "/DATA/USB_Storage7" "/DATA/USB_Storage8" "/DATA/USB_Storage9" "/DATA/USB_Storage10" "/DATA/USB_Storage11" "/DATA/USB_Storage12")
55 | # for folder in ${arr[@]}; do
56 | # #如果文件夹不存在,创建文件夹
57 | # if [ ! -d "$folder" ]; then
58 | # mkdir -p ${folder}
59 | # MOUNT_POINT=$folder
60 | # break
61 | # fi
62 | # done
63 |
64 | # ${log} "Mount point: ${MOUNT_POINT}"
65 |
66 |
67 |
68 | # # Global mount options
69 | # OPTS="rw,relatime"
70 | #
71 | # # File system type specific mount options
72 | # if [[ ${ID_FS_TYPE} == "vfat" ]]; then
73 | # OPTS+=",users,gid=100,umask=000,shortname=mixed,utf8=1,flush"
74 | # fi
75 |
76 | # if ! mount -o ${OPTS} ${DEVICE} ${MOUNT_POINT}; then
77 | # ${log} "Error mounting ${DEVICE} (status = $?)"
78 | # rmdir "${MOUNT_POINT}"
79 | # exit 1
80 | # else
81 | # # Track the mounted drives
82 | # echo "${MOUNT_POINT}:${DEVBASE}" | cat >>"/var/log/usb-mount.track"
83 | # fi
84 | #
85 | # ${log} "Mounted ${DEVICE} at ${MOUNT_POINT}"
86 |
87 | case ${ID_FS_TYPE} in
88 | vfat)
89 | mount -t vfat -o rw,relatime,users,gid=100,umask=000,shortname=mixed,utf8=1,flush ${DEVICE} ${MOUNT_POINT}
90 | ;;
91 | ext[2-4])
92 | mount -o noatime ${DEVICE} ${MOUNT_POINT} >/dev/null 2>&1
93 | ;;
94 | exfat)
95 | mount -t exfat ${DEVICE} ${MOUNT_POINT} >/dev/null 2>&1
96 | ;;
97 | ntfs)
98 | ntfs-3g ${DEVICE} ${MOUNT_POINT}
99 | ;;
100 | iso9660)
101 | mount -t iso9660 ${DEVICE} ${MOUNT_POINT}
102 | ;;
103 | *)
104 | /bin/rmdir "${MOUNT_POINT}"
105 | exit 0
106 | ;;
107 | esac
108 | }
109 |
110 | do_umount() {
111 |
112 | if [[ -z ${MOUNT_POINT} ]]; then
113 | ${log} "Warning: ${DEVICE} is not mounted"
114 | else
115 | #/bin/kill -9 $(lsof ${MOUNT_POINT})
116 | umount -l ${DEVICE}
117 | ${log} "Unmounted ${DEVICE} from ${MOUNT_POINT}"
118 | if [ "`ls -A ${MOUNT_POINT}`" = "" ]; then
119 | /bin/rm -fr "${MOUNT_POINT}"
120 | fi
121 | sed -i.bak "\@${MOUNT_POINT}@d" /var/log/usb-mount.track
122 | fi
123 |
124 | }
125 |
126 | case "${ACTION}" in
127 | add)
128 | do_mount
129 | ;;
130 | remove)
131 | do_umount
132 | ;;
133 | *)
134 | exit 1
135 | ;;
136 | esac
137 |
--------------------------------------------------------------------------------
/pkg/utils/httper/httper.go:
--------------------------------------------------------------------------------
1 | package httper
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "io"
7 | "io/ioutil"
8 | "net/http"
9 | "time"
10 |
11 | "github.com/IceWhaleTech/CasaOS/pkg/config"
12 | "github.com/tidwall/gjson"
13 | )
14 |
15 | //发送GET请求
16 | //url:请求地址
17 | //response:请求返回的内容
18 | func Get(url string, head map[string]string) (response string) {
19 | client := &http.Client{Timeout: 30 * time.Second}
20 | req, err := http.NewRequest("GET", url, nil)
21 |
22 | for k, v := range head {
23 | req.Header.Add(k, v)
24 | }
25 | if err != nil {
26 | return ""
27 | }
28 | resp, err := client.Do(req)
29 | if err != nil {
30 | fmt.Println(err)
31 | //需要错误日志的处理
32 | //loger.Error(error)
33 | return ""
34 | //panic(error)
35 | }
36 | defer resp.Body.Close()
37 | var buffer [512]byte
38 | result := bytes.NewBuffer(nil)
39 | for {
40 | n, err := resp.Body.Read(buffer[0:])
41 | result.Write(buffer[0:n])
42 | if err != nil && err == io.EOF {
43 | break
44 | } else if err != nil {
45 | //loger.Error(err)
46 | return ""
47 | // panic(err)
48 | }
49 | }
50 | response = result.String()
51 | return
52 | }
53 |
54 | //发送GET请求
55 | //url:请求地址
56 | //response:请求返回的内容
57 | func PersonGet(url string) (response string) {
58 | client := &http.Client{Timeout: 5 * time.Second}
59 | req, err := http.NewRequest("GET", url, nil)
60 |
61 | if err != nil {
62 | return ""
63 | }
64 | resp, err := client.Do(req)
65 | if err != nil {
66 | //需要错误日志的处理
67 | //loger.Error(error)
68 | return ""
69 | //panic(error)
70 | }
71 | defer resp.Body.Close()
72 | var buffer [512]byte
73 | result := bytes.NewBuffer(nil)
74 | for {
75 | n, err := resp.Body.Read(buffer[0:])
76 | result.Write(buffer[0:n])
77 | if err != nil && err == io.EOF {
78 | break
79 | } else if err != nil {
80 | //loger.Error(err)
81 | return ""
82 | // panic(err)
83 | }
84 | }
85 | response = result.String()
86 | return
87 | }
88 |
89 | //发送POST请求
90 | //url:请求地址,data:POST请求提交的数据,contentType:请求体格式,如:application/json
91 | //content:请求放回的内容
92 | func Post(url string, data []byte, contentType string, head map[string]string) (content string) {
93 |
94 | req, err := http.NewRequest("POST", url, bytes.NewBuffer(data))
95 | req.Header.Add("content-type", contentType)
96 | for k, v := range head {
97 | req.Header.Add(k, v)
98 | }
99 | if err != nil {
100 | panic(err)
101 | }
102 |
103 | client := &http.Client{Timeout: 5 * time.Second}
104 | resp, error := client.Do(req)
105 | if error != nil {
106 | fmt.Println(error)
107 | return
108 | }
109 | defer resp.Body.Close()
110 |
111 | result, _ := ioutil.ReadAll(resp.Body)
112 | content = string(result)
113 | return
114 | }
115 |
116 | //发送POST请求
117 | //url:请求地址,data:POST请求提交的数据,contentType:请求体格式,如:application/json
118 | //content:请求放回的内容
119 | func ZeroTierGet(url string, head map[string]string) (content string, code int) {
120 | req, err := http.NewRequest(http.MethodGet, url, nil)
121 | for k, v := range head {
122 | req.Header.Add(k, v)
123 | }
124 | if err != nil {
125 | panic(err)
126 | }
127 |
128 | client := &http.Client{Timeout: 20 * time.Second}
129 | resp, error := client.Do(req)
130 |
131 | if error != nil {
132 | panic(error)
133 | }
134 | defer resp.Body.Close()
135 | code = resp.StatusCode
136 | result, _ := ioutil.ReadAll(resp.Body)
137 | content = string(result)
138 | return
139 | }
140 |
141 | //发送GET请求
142 | //url:请求地址
143 | //response:请求返回的内容
144 | func OasisGet(url string) (response string) {
145 |
146 | head := make(map[string]string)
147 |
148 | t := make(chan string)
149 |
150 | go func() {
151 | str := Get(config.ServerInfo.ServerApi+"/token", nil)
152 |
153 | t <- gjson.Get(str, "data").String()
154 | }()
155 | head["Authorization"] = <-t
156 |
157 | return Get(url, head)
158 |
159 | }
160 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "fmt"
6 | "net/http"
7 | "time"
8 |
9 | "github.com/IceWhaleTech/CasaOS/model/notify"
10 | "github.com/IceWhaleTech/CasaOS/pkg/cache"
11 | "github.com/IceWhaleTech/CasaOS/pkg/config"
12 | "github.com/IceWhaleTech/CasaOS/pkg/sqlite"
13 | "github.com/IceWhaleTech/CasaOS/pkg/utils/encryption"
14 | "github.com/IceWhaleTech/CasaOS/pkg/utils/loger"
15 | "github.com/IceWhaleTech/CasaOS/pkg/utils/random"
16 | "github.com/IceWhaleTech/CasaOS/route"
17 | "github.com/IceWhaleTech/CasaOS/service"
18 | "github.com/IceWhaleTech/CasaOS/types"
19 |
20 | "github.com/robfig/cron"
21 | "gorm.io/gorm"
22 | )
23 |
24 | var sqliteDB *gorm.DB
25 |
26 | var configFlag = flag.String("c", "", "config address")
27 | var dbFlag = flag.String("db", "", "db path")
28 | var resetUser = flag.Bool("ru", false, "reset user")
29 | var user = flag.String("user", "", "user name")
30 | var version = flag.Bool("v", false, "show version")
31 |
32 | func init() {
33 | flag.Parse()
34 | if *version {
35 | fmt.Println("v" + types.CURRENTVERSION)
36 | return
37 | }
38 | config.InitSetup(*configFlag)
39 | config.UpdateSetup()
40 |
41 | loger.LogInit()
42 | if len(*dbFlag) == 0 {
43 | *dbFlag = config.AppInfo.DBPath + "/db"
44 | }
45 |
46 | sqliteDB = sqlite.GetDb(*dbFlag)
47 | //gredis.GetRedisConn(config.RedisInfo),
48 |
49 | service.MyService = service.NewService(sqliteDB)
50 |
51 | service.Cache = cache.Init()
52 |
53 | service.GetToken()
54 |
55 | service.NewVersionApp = make(map[string]string)
56 | route.InitFunction()
57 |
58 | // go service.LoopFriend()
59 | // go service.MyService.App().CheckNewImage()
60 |
61 | }
62 |
63 | // @title casaOS API
64 | // @version 1.0.0
65 | // @contact.name lauren.pan
66 | // @contact.url https://www.zimaboard.com
67 | // @contact.email lauren.pan@icewhale.org
68 | // @description casaOS v1版本api
69 | // @host 192.168.2.217:8089
70 | // @securityDefinitions.apikey ApiKeyAuth
71 | // @in header
72 | // @name Authorization
73 | // @BasePath /v1
74 | func main() {
75 | service.NotifyMsg = make(chan notify.Message, 10)
76 | if *version {
77 | return
78 | }
79 | if *resetUser {
80 | if user == nil || len(*user) == 0 {
81 | fmt.Println("user is empty")
82 | return
83 | }
84 | userData := service.MyService.User().GetUserAllInfoByName(*user)
85 | if userData.Id == 0 {
86 | fmt.Println("user not exist")
87 | return
88 | }
89 | password := random.RandomString(6, false)
90 | userData.Password = encryption.GetMD5ByStr(password)
91 | service.MyService.User().UpdateUserPassword(userData)
92 | fmt.Println("User reset successful")
93 | fmt.Println("UserName:" + userData.Username)
94 | fmt.Println("Password:" + password)
95 | return
96 | }
97 | go route.SocketInit(service.NotifyMsg)
98 | go route.MonitoryUSB()
99 | //model.Setup()
100 | //gredis.Setup()
101 | r := route.InitRouter()
102 | //service.SyncTask(sqliteDB)
103 | cron2 := cron.New()
104 | //every day execution
105 |
106 | err := cron2.AddFunc("0/5 * * * * *", func() {
107 | if service.ClientCount > 0 {
108 | //route.SendNetINfoBySocket()
109 | //route.SendCPUBySocket()
110 | //route.SendMemBySocket()
111 | // route.SendDiskBySocket()
112 | //route.SendUSBBySocket()
113 | route.SendAllHardwareStatusBySocket()
114 | }
115 | })
116 | if err != nil {
117 | fmt.Println(err)
118 | }
119 | cron2.Start()
120 | defer cron2.Stop()
121 | s := &http.Server{
122 | Addr: fmt.Sprintf(":%v", config.ServerInfo.HttpPort),
123 | Handler: r,
124 | ReadTimeout: 60 * time.Second,
125 | WriteTimeout: 60 * time.Second,
126 | MaxHeaderBytes: 1 << 20,
127 | }
128 |
129 | s.ListenAndServe()
130 |
131 | // if err := r.Run(fmt.Sprintf(":%v", config.ServerInfo.HttpPort)); err != nil {
132 | // fmt.Println("failed run app: ", err)
133 | // }
134 | }
135 |
--------------------------------------------------------------------------------
/pkg/utils/common_err/e.go:
--------------------------------------------------------------------------------
1 | package common_err
2 |
3 | const (
4 | SUCCESS = 200
5 | SERVICE_ERROR = 500
6 | CLIENT_ERROR = 400
7 | ERROR_AUTH_TOKEN = 401
8 |
9 | INVALID_PARAMS = 4000
10 | //user
11 | PWD_INVALID = 10001
12 | PWD_IS_EMPTY = 10002
13 | PWD_INVALID_OLD = 10003
14 | ACCOUNT_LOCK = 10004
15 | PWD_IS_TOO_SIMPLE = 10005
16 | USER_NOT_EXIST = 10006
17 | USER_EXIST = 10007
18 | KEY_NOT_EXIST = 10008
19 | NOT_IMAGE = 10009
20 | IMAGE_TOO_LARGE = 10010
21 | INSUFFICIENT_PERMISSIONS = 10011
22 |
23 | //system
24 | DIR_ALREADY_EXISTS = 20001
25 | FILE_ALREADY_EXISTS = 20002
26 | FILE_OR_DIR_EXISTS = 20003
27 | PORT_IS_OCCUPIED = 20004
28 | COMMAND_ERROR_INVALID_OPERATION = 20005
29 | VERIFICATION_FAILURE = 20006
30 | Record_NOT_EXIST = 20007
31 | Record_ALREADY_EXIST = 20008
32 | SERVICE_NOT_RUNNING = 20009
33 |
34 | //disk
35 | NAME_NOT_AVAILABLE = 40001
36 | DISK_NEEDS_FORMAT = 40002
37 | DISK_BUSYING = 40003
38 | REMOVE_MOUNT_POINT_ERROR = 40004
39 | FORMAT_ERROR = 40005
40 |
41 | //app
42 | UNINSTALL_APP_ERROR = 50001
43 | PULL_IMAGE_ERROR = 50002
44 | DEVICE_NOT_EXIST = 50003
45 | ERROR_APP_NAME_EXIST = 50004
46 |
47 | //file
48 | FILE_DOES_NOT_EXIST = 60001
49 | FILE_READ_ERROR = 60002
50 | FILE_DELETE_ERROR = 60003
51 | DIR_NOT_EXISTS = 60004
52 | SOURCE_DES_SAME = 60005
53 |
54 | //share
55 | SHARE_ALREADY_EXISTS = 70001
56 | SHARE_NAME_ALREADY_EXISTS = 70002
57 | )
58 |
59 | var MsgFlags = map[int]string{
60 | SUCCESS: "ok",
61 | SERVICE_ERROR: "Fail",
62 | CLIENT_ERROR: "Fail",
63 | INVALID_PARAMS: "Parameters Error",
64 | ERROR_AUTH_TOKEN: "Error auth token",
65 |
66 | //user
67 | PWD_INVALID: "Invalid password",
68 | PWD_IS_EMPTY: "Password is empty",
69 | PWD_INVALID_OLD: "Invalid old password",
70 | ACCOUNT_LOCK: "Account is locked",
71 | PWD_IS_TOO_SIMPLE: "Password is too simple",
72 | USER_NOT_EXIST: "User does not exist",
73 | USER_EXIST: "User already exists",
74 | KEY_NOT_EXIST: "Key does not exist",
75 | IMAGE_TOO_LARGE: "Image is too large",
76 | NOT_IMAGE: "Not an image",
77 | INSUFFICIENT_PERMISSIONS: "Insufficient permissions",
78 |
79 | //system
80 | DIR_ALREADY_EXISTS: "Folder already exists",
81 | FILE_ALREADY_EXISTS: "File already exists",
82 | FILE_OR_DIR_EXISTS: "File or folder already exists",
83 | PORT_IS_OCCUPIED: "Port is occupied",
84 | VERIFICATION_FAILURE: "Verification failure",
85 | Record_ALREADY_EXIST: "Record already exists",
86 | Record_NOT_EXIST: "Record does not exist",
87 | SERVICE_NOT_RUNNING: "Service is not running",
88 |
89 | //app
90 | UNINSTALL_APP_ERROR: "Error uninstalling app",
91 | PULL_IMAGE_ERROR: "Error pulling image",
92 | DEVICE_NOT_EXIST: "Device does not exist",
93 | ERROR_APP_NAME_EXIST: "App name already exists",
94 |
95 | //disk
96 | NAME_NOT_AVAILABLE: "Name not available",
97 | DISK_NEEDS_FORMAT: "Drive needs to be formatted",
98 | REMOVE_MOUNT_POINT_ERROR: "Failed to remove mount point",
99 | DISK_BUSYING: "Drive is busy",
100 | FORMAT_ERROR: "Formatting failed, please check if the directory is occupied",
101 | //share
102 | SHARE_ALREADY_EXISTS: "Share already exists",
103 | SHARE_NAME_ALREADY_EXISTS: "Share name already exists",
104 | //
105 | SOURCE_DES_SAME: "Source and destination cannot be the same.",
106 | FILE_DOES_NOT_EXIST: "File does not exist",
107 |
108 | DIR_NOT_EXISTS: "Directory does not exist",
109 |
110 | FILE_READ_ERROR: "File read error",
111 | FILE_DELETE_ERROR: "Delete error",
112 |
113 | COMMAND_ERROR_INVALID_OPERATION: "invalid operation",
114 | }
115 |
116 | //获取错误信息
117 | func GetMsg(code int) string {
118 | msg, ok := MsgFlags[code]
119 | if ok {
120 | return msg
121 | }
122 | return MsgFlags[SERVICE_ERROR]
123 | }
124 |
--------------------------------------------------------------------------------
/model/manifest.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "database/sql/driver"
5 | "encoding/json"
6 | )
7 |
8 | type TcpPorts struct {
9 | Desc string `json:"desc"`
10 | ContainerPort int `json:"container_port"`
11 | }
12 | type UdpPorts struct {
13 | Desc string `json:"desc"`
14 | ContainerPort int `json:"container_port"`
15 | }
16 |
17 | /*******************使用gorm支持json************************************/
18 |
19 | type PortMap struct {
20 | ContainerPort string `json:"container"`
21 | CommendPort string `json:"host"`
22 | Protocol string `json:"protocol"`
23 | Desc string `json:"desc"`
24 | Type int `json:"type"`
25 | }
26 |
27 | type PortArray []PortMap
28 |
29 | // Value 实现方法
30 | func (p PortArray) Value() (driver.Value, error) {
31 | return json.Marshal(p)
32 | }
33 |
34 | // Scan 实现方法
35 | func (p *PortArray) Scan(input interface{}) error {
36 | return json.Unmarshal(input.([]byte), p)
37 | }
38 |
39 | /************************************************************************/
40 |
41 | /*******************使用gorm支持json************************************/
42 |
43 | type Env struct {
44 | Name string `json:"container"`
45 | Value string `json:"host"`
46 | Desc string `json:"desc"`
47 | Type int `json:"type"`
48 | }
49 |
50 | type JSON json.RawMessage
51 |
52 | type EnvArray []Env
53 |
54 | // Value 实现方法
55 | func (p EnvArray) Value() (driver.Value, error) {
56 | return json.Marshal(p)
57 | //return .MarshalJSON()
58 | }
59 |
60 | // Scan 实现方法
61 | func (p *EnvArray) Scan(input interface{}) error {
62 | return json.Unmarshal(input.([]byte), p)
63 | }
64 |
65 | /************************************************************************/
66 |
67 | /*******************使用gorm支持json************************************/
68 |
69 | type PathMap struct {
70 | ContainerPath string `json:"container"`
71 | Path string `json:"host"`
72 | Type int `json:"type"`
73 | Desc string `json:"desc"`
74 | }
75 |
76 | type PathArray []PathMap
77 |
78 | // Value 实现方法
79 | func (p PathArray) Value() (driver.Value, error) {
80 | return json.Marshal(p)
81 | }
82 |
83 | // Scan 实现方法
84 | func (p *PathArray) Scan(input interface{}) error {
85 | return json.Unmarshal(input.([]byte), p)
86 | }
87 |
88 | /************************************************************************/
89 |
90 | //type PostData struct {
91 | // Envs EnvArrey `json:"envs,omitempty"`
92 | // Udp PortArrey `json:"udp_ports"`
93 | // Tcp PortArrey `json:"tcp_ports"`
94 | // Volumes PathArrey `json:"volumes"`
95 | // Devices PathArrey `json:"devices"`
96 | // Port string `json:"port,omitempty"`
97 | // PortMap string `json:"port_map"`
98 | // CpuShares int64 `json:"cpu_shares,omitempty"`
99 | // Memory int64 `json:"memory,omitempty"`
100 | // Restart string `json:"restart,omitempty"`
101 | // EnableUPNP bool `json:"enable_upnp"`
102 | // Label string `json:"label"`
103 | // Position bool `json:"position"`
104 | //}
105 |
106 | type CustomizationPostData struct {
107 | ContainerName string `json:"container_name"`
108 | CustomId string `json:"custom_id"`
109 | Origin string `json:"origin"`
110 | NetworkModel string `json:"network_model"`
111 | Index string `json:"index"`
112 | Icon string `json:"icon"`
113 | Image string `json:"image"`
114 | Envs EnvArray `json:"envs"`
115 | Ports PortArray `json:"ports"`
116 | Volumes PathArray `json:"volumes"`
117 | Devices PathArray `json:"devices"`
118 | //Port string `json:"port,omitempty"`
119 | PortMap string `json:"port_map"`
120 | CpuShares int64 `json:"cpu_shares"`
121 | Memory int64 `json:"memory"`
122 | Restart string `json:"restart"`
123 | EnableUPNP bool `json:"enable_upnp"`
124 | Label string `json:"label"`
125 | Description string `json:"description"`
126 | Position bool `json:"position"`
127 | HostName string `json:"host_name"`
128 | Privileged bool `json:"privileged"`
129 | CapAdd []string `json:"cap_add"`
130 | Cmd []string `json:"cmd"`
131 | Protocol string `json:"protocol"`
132 | Host string `json:"host"`
133 | }
134 |
--------------------------------------------------------------------------------
/service/file.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: LinkLeong link@icewhale.com
3 | * @Date: 2021-12-20 14:15:46
4 | * @LastEditors: LinkLeong
5 | * @LastEditTime: 2022-07-04 16:18:23
6 | * @FilePath: /CasaOS/service/file.go
7 | * @Description:
8 | * @Website: https://www.casaos.io
9 | * Copyright (c) 2022 by icewhale, All Rights Reserved.
10 | */
11 | package service
12 |
13 | import (
14 | "context"
15 | "io"
16 | "os"
17 | "path/filepath"
18 | "strings"
19 | "sync"
20 | "time"
21 |
22 | "github.com/IceWhaleTech/CasaOS/model"
23 | "github.com/IceWhaleTech/CasaOS/pkg/utils/file"
24 | "github.com/IceWhaleTech/CasaOS/pkg/utils/loger"
25 | "go.uber.org/zap"
26 | )
27 |
28 | var FileQueue sync.Map
29 |
30 | var OpStrArr []string
31 |
32 | type reader struct {
33 | ctx context.Context
34 | r io.Reader
35 | }
36 |
37 | // NewReader wraps an io.Reader to handle context cancellation.
38 | //
39 | // Context state is checked BEFORE every Read.
40 | func NewReader(ctx context.Context, r io.Reader) io.Reader {
41 | if r, ok := r.(*reader); ok && ctx == r.ctx {
42 | return r
43 | }
44 | return &reader{ctx: ctx, r: r}
45 | }
46 |
47 | func (r *reader) Read(p []byte) (n int, err error) {
48 | select {
49 | case <-r.ctx.Done():
50 | return 0, r.ctx.Err()
51 | default:
52 | return r.r.Read(p)
53 | }
54 | }
55 |
56 | type writer struct {
57 | ctx context.Context
58 | w io.Writer
59 | }
60 |
61 | type copier struct {
62 | writer
63 | }
64 |
65 | func NewWriter(ctx context.Context, w io.Writer) io.Writer {
66 | if w, ok := w.(*copier); ok && ctx == w.ctx {
67 | return w
68 | }
69 | return &copier{writer{ctx: ctx, w: w}}
70 | }
71 |
72 | // Write implements io.Writer, but with context awareness.
73 | func (w *writer) Write(p []byte) (n int, err error) {
74 | select {
75 | case <-w.ctx.Done():
76 | return 0, w.ctx.Err()
77 | default:
78 | return w.w.Write(p)
79 | }
80 | }
81 | func FileOperate(k string) {
82 |
83 | list, ok := FileQueue.Load(k)
84 | if !ok {
85 | return
86 | }
87 |
88 | temp := list.(model.FileOperate)
89 | if temp.ProcessedSize > 0 {
90 | return
91 | }
92 | for i := 0; i < len(temp.Item); i++ {
93 | v := temp.Item[i]
94 | if temp.Type == "move" {
95 | lastPath := v.From[strings.LastIndex(v.From, "/")+1:]
96 | if !file.CheckNotExist(temp.To + "/" + lastPath) {
97 | if temp.Style == "skip" {
98 | temp.Item[i].Finished = true
99 | continue
100 | } else {
101 | os.RemoveAll(temp.To + "/" + lastPath)
102 | }
103 | }
104 | err := os.Rename(v.From, temp.To+"/"+lastPath)
105 | if err != nil {
106 | loger.Error("file move error", zap.Any("err", err))
107 | err = file.MoveFile(v.From, temp.To+"/"+lastPath)
108 | if err != nil {
109 | loger.Error("MoveFile error", zap.Any("err", err))
110 | continue
111 | }
112 |
113 | }
114 | } else if temp.Type == "copy" {
115 | err := file.CopyDir(v.From, temp.To, temp.Style)
116 | if err != nil {
117 | continue
118 | }
119 | } else {
120 | continue
121 | }
122 |
123 | }
124 | temp.Finished = true
125 | FileQueue.Store(k, temp)
126 | }
127 |
128 | func ExecOpFile() {
129 | len := len(OpStrArr)
130 | if len == 0 {
131 | return
132 | }
133 | if len > 1 {
134 | len = 1
135 | }
136 | for i := 0; i < len; i++ {
137 | go FileOperate(OpStrArr[i])
138 | }
139 | }
140 |
141 | // file move or copy and send notify
142 | func CheckFileStatus() {
143 | for {
144 | if len(OpStrArr) == 0 {
145 | return
146 | }
147 | for _, v := range OpStrArr {
148 | var total int64 = 0
149 | item, ok := FileQueue.Load(v)
150 | if !ok {
151 | continue
152 | }
153 | temp := item.(model.FileOperate)
154 | for i := 0; i < len(temp.Item); i++ {
155 |
156 | if !temp.Item[i].Finished {
157 | size, err := file.GetFileOrDirSize(temp.To + "/" + filepath.Base(temp.Item[i].From))
158 | if err != nil {
159 | continue
160 | }
161 | temp.Item[i].ProcessedSize = size
162 | if size == temp.Item[i].Size {
163 | temp.Item[i].Finished = true
164 | }
165 | total += size
166 | } else {
167 | total += temp.Item[i].ProcessedSize
168 | }
169 |
170 | }
171 | temp.ProcessedSize = total
172 | FileQueue.Store(v, temp)
173 | }
174 | time.Sleep(time.Second * 3)
175 | }
176 | }
177 |
--------------------------------------------------------------------------------
/.github/workflows/casa.yml:
--------------------------------------------------------------------------------
1 | # This is a basic workflow to help you get started with Actions
2 |
3 | name: Build CasaOS
4 |
5 | on:
6 | repository_dispatch:
7 | workflow_dispatch:
8 | inputs:
9 | ssh:
10 | description: 'SSH connection to Actions'
11 | required: false
12 | default: 'false'
13 |
14 | #on:
15 | # push:
16 | # branches:
17 | # - 'main'
18 | # tags:
19 | # - 'v*'
20 | env:
21 | REPO_URL: https://github.com/IceWhaleTech/CasaOS.git
22 | REPO_BRANCH: main
23 | PACK_SH_URL: https://raw.githubusercontent.com/jerrykuku/actions-casa/main/pack.sh
24 | PACK_SH: pack.sh
25 | TZ: Asia/Shanghai
26 |
27 | jobs:
28 | xgo:
29 | strategy:
30 | fail-fast: false
31 | matrix:
32 | go_version:
33 | - 1.17.1
34 | runs-on: ubuntu-latest
35 | steps:
36 |
37 | # - name: Get release
38 | # id: get_release
39 | # uses: bruceadams/get-release@v1.2.3
40 | # env:
41 | # GITHUB_TOKEN: ${{ github.token }}
42 |
43 | - uses: actions/checkout@v2
44 | with:
45 | fetch-depth: 0
46 | submodules: true
47 | # - name: Initialization environment
48 | # env:
49 | # DEBIAN_FRONTEND: noninteractive
50 | # run: |
51 | # sudo timedatectl set-timezone "$TZ"
52 | # sudo mkdir -p /workdir
53 | # sudo chown $USER:$GROUPS /workdir
54 |
55 |
56 |
57 |
58 |
59 | # - name: Clone source code
60 | # working-directory: /workdir
61 | # run: |
62 | # df -hT $PWD
63 | # git clone $REPO_URL -b $REPO_BRANCH --recursive casa
64 | # ln -sf /workdir/casa $GITHUB_WORKSPACE/casa
65 | # ls
66 |
67 |
68 | - name: Set enviroment for github-release
69 | run: |
70 | echo "VERSION=$(cat types/system.go | grep CURRENTVERSION | awk '$2 == "CURRENTVERSION"{print $4}' | sed 's/"//g')" >>$GITHUB_ENV
71 | echo "BODY=$(cat types/system.go | grep BODY | awk -F= '{print $2}' | sed 's/"//g')" >>$GITHUB_ENV
72 |
73 |
74 |
75 | - name: Use Node.js
76 | uses: actions/setup-node@v2
77 | with:
78 | node-version: '14'
79 |
80 | - name: Build frontend with nodejs and yarn
81 | run: |
82 | cd UI
83 | ls
84 | yarn install
85 | yarn build
86 |
87 | - name: list work
88 | run: pwd
89 |
90 | - name: Build with xgo
91 | uses: crazy-max/ghaction-xgo@v1
92 | with:
93 | xgo_version: v0.7.5
94 | go_version: ${{ matrix.go_version }}
95 | dest: build
96 | prefix: casa
97 | targets: linux/amd64,linux/arm64,linux/arm-7
98 | v: true
99 | x: false
100 | race: false
101 | ldflags: -s -w
102 | buildmode: default
103 | #
104 | # - name: List Files
105 | # run: |
106 | # ls
107 | # mkdir build
108 | # ls
109 | # echo "::set-output name=status::success"
110 |
111 | - name: Pack builds
112 | run: |
113 | wget $PACK_SH_URL
114 | chmod +x $PACK_SH
115 | ./$PACK_SH
116 | echo "::set-output name=status::success"
117 | - name: list work
118 | run: ls
119 |
120 |
121 | - name: Update release
122 | uses: meeDamian/github-release@2.0
123 | with:
124 | token: ${{ secrets.GITHUB_TOKEN }}
125 | files: >
126 | linux-amd64-casaos.tar.gz
127 | linux-arm64-casaos.tar.gz
128 | linux-arm-7-casaos.tar.gz
129 | tag: v${{ env.VERSION }}
130 | body: >
131 | ${{ env.BODY }}
132 | name: v${{ env.VERSION }}
133 | gzip: false
134 | allow_override: false
135 | prerelease: true
136 | # - name: Upload linux-amd64-casaos.tar.gz
137 | # id: upload_assets_amd64
138 | # uses: shogo82148/actions-upload-release-asset@v1
139 | # with:
140 | # upload_url: ${{ steps.get_release.outputs.upload_url }}
141 | # asset_path: /workdir/casa/upload/linux-amd64-casaos.tar.gz
142 | #
143 | # - name: Upload linux-arm64-casaos.tar.gz
144 | # id: upload_assets_arm64
145 | # uses: shogo82148/actions-upload-release-asset@v1
146 | # with:
147 | # upload_url: ${{ steps.get_release.outputs.upload_url }}
148 | # asset_path: /workdir/casa/upload/linux-arm64-casaos.tar.gz
149 |
--------------------------------------------------------------------------------
/model/app.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "database/sql/driver"
5 | "encoding/json"
6 | "time"
7 | )
8 |
9 | type ServerAppListCollection struct {
10 | List []ServerAppList `json:"list"`
11 | Recommend []ServerAppList `json:"recommend"`
12 | Community []ServerAppList `json:"community"`
13 | Version string `json:"version"`
14 | }
15 |
16 | // @tiger - 对于用于出参的数据结构,静态信息(例如 title)和
17 | // 动态信息(例如 state、query_count)应该划分到不同的数据结构中
18 | //
19 | // 这样的好处是
20 | // 1 - 多次获取动态信息时可以减少出参复杂度,因为静态信息只获取一次就好
21 | // 2 - 在未来的迭代中,可以降低维护成本(所有字段都展开放在一个层级维护成本略高)
22 | //
23 | // 另外,一些针对性字段,例如 Docker 相关的,可以用 map 来保存。
24 | // 这样在未来增加多态 App,例如 Snap,不需要维护多个结构,或者一个结构保存不必要的字段
25 | type ServerAppList struct {
26 | Id uint `gorm:"column:id;primary_key" json:"id"`
27 | Title string `json:"title"`
28 | Description string `json:"description"`
29 | Tagline string `json:"tagline"`
30 | Tags Strings `gorm:"type:json" json:"tags"`
31 | Icon string `json:"icon"`
32 | ScreenshotLink Strings `gorm:"type:json" json:"screenshot_link"`
33 | Category string `json:"category"`
34 | CategoryId int `json:"category_id"`
35 | CategoryFont string `json:"category_font"`
36 | PortMap string `json:"port_map"`
37 | ImageVersion string `json:"image_version"`
38 | Tip string `json:"tip"`
39 | Envs EnvArray `json:"envs"`
40 | Ports PortArray `json:"ports"`
41 | Volumes PathArray `json:"volumes"`
42 | Devices PathArray `json:"devices"`
43 | NetworkModel string `json:"network_model"`
44 | Image string `json:"image"`
45 | Index string `json:"index"`
46 | CreatedAt time.Time `json:"created_at"`
47 | UpdatedAt time.Time `json:"updated_at"`
48 | State int `json:"state"`
49 | Author string `json:"author"`
50 | MinMemory int `json:"min_memory"`
51 | MinDisk int `json:"min_disk"`
52 | MaxMemory uint64 `json:"max_memory"`
53 | Thumbnail string `json:"thumbnail"`
54 | Healthy string `json:"healthy"`
55 | Plugins Strings `json:"plugins"`
56 | Origin string `json:"origin"`
57 | Type int `json:"type"`
58 | QueryCount int `json:"query_count"`
59 | Developer string `json:"developer"`
60 | HostName string `json:"host_name"`
61 | Privileged bool `json:"privileged"`
62 | CapAdd Strings `json:"cap_add"`
63 | Cmd Strings `json:"cmd"`
64 | }
65 |
66 | type Ports struct {
67 | ContainerPort uint `json:"container_port"`
68 | CommendPort int `json:"commend_port"`
69 | Desc string `json:"desc"`
70 | Type int `json:"type"` // 1:必选 2:可选 3:默认值不必显示 4:系统处理 5:container内容也可编辑
71 | }
72 |
73 | type Volume struct {
74 | ContainerPath string `json:"container_path"`
75 | Path string `json:"path"`
76 | Desc string `json:"desc"`
77 | Type int `json:"type"` // 1:必选 2:可选 3:默认值不必显示 4:系统处理 5:container内容也可编辑
78 | }
79 |
80 | type Envs struct {
81 | Name string `json:"name"`
82 | Value string `json:"value"`
83 | Desc string `json:"desc"`
84 | Type int `json:"type"` // 1:必选 2:可选 3:默认值不必显示 4:系统处理 5:container内容也可编辑
85 | }
86 |
87 | type Devices struct {
88 | ContainerPath string `json:"container_path"`
89 | Path string `json:"path"`
90 | Desc string `json:"desc"`
91 | Type int `json:"type"` // 1:必选 2:可选 3:默认值不必显示 4:系统处理 5:container内容也可编辑
92 | }
93 |
94 | type configures struct {
95 | TcpPorts []Ports `json:"tcp_ports"`
96 | UdpPorts []Ports `json:"udp_ports"`
97 | Envs []Envs `json:"envs"`
98 | Volumes []Volume `json:"volumes"`
99 | Devices []Devices `json:"devices"`
100 | }
101 |
102 | /****************使gorm支持[]string结构*******************/
103 | type Strings []string
104 |
105 | func (c Strings) Value() (driver.Value, error) {
106 | b, err := json.Marshal(c)
107 | return string(b), err
108 | }
109 |
110 | func (c *Strings) Scan(input interface{}) error {
111 | return json.Unmarshal(input.([]byte), c)
112 | }
113 |
114 | /****************使gorm支持[]string结构*******************/
115 |
116 | /****************使gorm支持[]string结构*******************/
117 | type MapStrings []map[string]string
118 |
119 | func (c MapStrings) Value() (driver.Value, error) {
120 | b, err := json.Marshal(c)
121 | return string(b), err
122 | }
123 |
124 | func (c *MapStrings) Scan(input interface{}) error {
125 | return json.Unmarshal(input.([]byte), c)
126 | }
127 |
128 | /****************使gorm支持[]string结构*******************/
129 |
--------------------------------------------------------------------------------
/pkg/utils/file/image.go:
--------------------------------------------------------------------------------
1 | package file
2 |
3 | import (
4 | "bytes"
5 | "errors"
6 | "fmt"
7 | "io"
8 | "net/http"
9 | "os"
10 | "path/filepath"
11 | "strings"
12 |
13 | "github.com/disintegration/imaging"
14 | "github.com/dsoprea/go-exif/v3"
15 | exifcommon "github.com/dsoprea/go-exif/v3/common"
16 | )
17 |
18 | func GetImage(path string, width, height int) ([]byte, error) {
19 | if thumbnail, err := GetThumbnailByOwnerPhotos(path); err == nil {
20 | return thumbnail, nil
21 | } else {
22 | return GetThumbnailByWebPhoto(path, width, height)
23 | }
24 | }
25 | func GetThumbnailByOwnerPhotos(path string) ([]byte, error) {
26 | file, err := os.Open(path)
27 | if err != nil {
28 | return nil, err
29 | }
30 | buff := &bytes.Buffer{}
31 |
32 | defer file.Close()
33 | offset := 0
34 | offsets := []int{12, 30}
35 |
36 | head := make([]byte, 0xffff)
37 |
38 | r := io.TeeReader(file, buff)
39 | _, err = r.Read(head)
40 | if err != nil {
41 | return nil, err
42 | }
43 |
44 | for _, offset = range offsets {
45 | if _, err = exif.ParseExifHeader(head[offset:]); err == nil {
46 | break
47 | }
48 | }
49 | if err != nil {
50 | return nil, err
51 | }
52 | im, err := exifcommon.NewIfdMappingWithStandard()
53 | if err != nil {
54 | return nil, err
55 | }
56 |
57 | _, index, err := exif.Collect(im, exif.NewTagIndex(), head[offset:])
58 | if err != nil {
59 | return nil, err
60 | }
61 |
62 | ifd := index.RootIfd.NextIfd()
63 | if ifd == nil {
64 | return nil, exif.ErrNoThumbnail
65 | }
66 | thumbnail, err := ifd.Thumbnail()
67 | if err != nil {
68 | return nil, err
69 | }
70 | return thumbnail, nil
71 | }
72 | func GetThumbnailByWebPhoto(path string, width, height int) ([]byte, error) {
73 | src, err := imaging.Open(path)
74 | if err != nil {
75 | fmt.Println(err)
76 | return nil, err
77 | }
78 |
79 | img := imaging.Resize(src, width, 0, imaging.Lanczos)
80 |
81 | f, err := imaging.FormatFromFilename(path)
82 | if err != nil {
83 | return nil, err
84 | }
85 | buf := bytes.Buffer{}
86 | imaging.Encode(&buf, img, f)
87 | return buf.Bytes(), nil
88 | }
89 |
90 | func ImageExtArray() []string {
91 |
92 | ext := []string{
93 | "ase",
94 | "art",
95 | "bmp",
96 | "blp",
97 | "cd5",
98 | "cit",
99 | "cpt",
100 | "cr2",
101 | "cut",
102 | "dds",
103 | "dib",
104 | "djvu",
105 | "egt",
106 | "exif",
107 | "gif",
108 | "gpl",
109 | "grf",
110 | "icns",
111 | "ico",
112 | "iff",
113 | "jng",
114 | "jpeg",
115 | "jpg",
116 | "jfif",
117 | "jp2",
118 | "jps",
119 | "lbm",
120 | "max",
121 | "miff",
122 | "mng",
123 | "msp",
124 | "nitf",
125 | "ota",
126 | "pbm",
127 | "pc1",
128 | "pc2",
129 | "pc3",
130 | "pcf",
131 | "pcx",
132 | "pdn",
133 | "pgm",
134 | "PI1",
135 | "PI2",
136 | "PI3",
137 | "pict",
138 | "pct",
139 | "pnm",
140 | "pns",
141 | "ppm",
142 | "psb",
143 | "psd",
144 | "pdd",
145 | "psp",
146 | "px",
147 | "pxm",
148 | "pxr",
149 | "qfx",
150 | "raw",
151 | "rle",
152 | "sct",
153 | "sgi",
154 | "rgb",
155 | "int",
156 | "bw",
157 | "tga",
158 | "tiff",
159 | "tif",
160 | "vtf",
161 | "xbm",
162 | "xcf",
163 | "xpm",
164 | "3dv",
165 | "amf",
166 | "ai",
167 | "awg",
168 | "cgm",
169 | "cdr",
170 | "cmx",
171 | "dxf",
172 | "e2d",
173 | "egt",
174 | "eps",
175 | "fs",
176 | "gbr",
177 | "odg",
178 | "svg",
179 | "stl",
180 | "vrml",
181 | "x3d",
182 | "sxd",
183 | "v2d",
184 | "vnd",
185 | "wmf",
186 | "emf",
187 | "art",
188 | "xar",
189 | "png",
190 | "webp",
191 | "jxr",
192 | "hdp",
193 | "wdp",
194 | "cur",
195 | "ecw",
196 | "iff",
197 | "lbm",
198 | "liff",
199 | "nrrd",
200 | "pam",
201 | "pcx",
202 | "pgf",
203 | "sgi",
204 | "rgb",
205 | "rgba",
206 | "bw",
207 | "int",
208 | "inta",
209 | "sid",
210 | "ras",
211 | "sun",
212 | "tga",
213 | }
214 |
215 | return ext
216 | }
217 |
218 | /**
219 | * @description:get a image's ext
220 | * @param {string} path "file path"
221 | * @return {string} ext "file ext"
222 | * @return {error} err "error info"
223 | */
224 | func GetImageExt(p string) (string, error) {
225 | file, err := os.Open(p)
226 | if err != nil {
227 | return "", err
228 | }
229 |
230 | buff := make([]byte, 512)
231 |
232 | _, err = file.Read(buff)
233 |
234 | if err != nil {
235 | return "", err
236 | }
237 |
238 | filetype := http.DetectContentType(buff)
239 |
240 | ext := ImageExtArray()
241 |
242 | for i := 0; i < len(ext); i++ {
243 | if strings.Contains(ext[i], filetype[6:]) {
244 | return ext[i], nil
245 | }
246 | }
247 |
248 | return "", errors.New("invalid image type")
249 | }
250 |
251 | func GetImageExtByName(p string) (string, error) {
252 |
253 | extArr := ImageExtArray()
254 | ext := filepath.Ext(p)
255 | for i := 0; i < len(extArr); i++ {
256 | if strings.Contains(ext, extArr[i]) {
257 | return extArr[i], nil
258 | }
259 | }
260 | return "", errors.New("invalid image type")
261 | }
262 |
--------------------------------------------------------------------------------
/service/shares.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: LinkLeong link@icewhale.org
3 | * @Date: 2022-07-26 11:21:14
4 | * @LastEditors: LinkLeong
5 | * @LastEditTime: 2022-08-18 11:16:25
6 | * @FilePath: /CasaOS/service/shares.go
7 | * @Description:
8 | * @Website: https://www.casaos.io
9 | * Copyright (c) 2022 by icewhale, All Rights Reserved.
10 | */
11 | package service
12 |
13 | import (
14 | "path/filepath"
15 | "strings"
16 |
17 | "github.com/IceWhaleTech/CasaOS/pkg/config"
18 | command2 "github.com/IceWhaleTech/CasaOS/pkg/utils/command"
19 | "github.com/IceWhaleTech/CasaOS/pkg/utils/file"
20 | "github.com/IceWhaleTech/CasaOS/service/model"
21 | model2 "github.com/IceWhaleTech/CasaOS/service/model"
22 | "gorm.io/gorm"
23 | )
24 |
25 | type SharesService interface {
26 | GetSharesList() (shares []model2.SharesDBModel)
27 | GetSharesByPath(path string) (shares []model2.SharesDBModel)
28 | GetSharesByName(name string) (shares []model2.SharesDBModel)
29 | CreateShare(share model2.SharesDBModel)
30 | DeleteShare(id string)
31 | UpdateConfigFile()
32 | InitSambaConfig()
33 | DeleteShareByPath(path string)
34 | }
35 |
36 | type sharesStruct struct {
37 | db *gorm.DB
38 | }
39 |
40 | func (s *sharesStruct) DeleteShareByPath(path string) {
41 | s.db.Where("path LIKE ?", path+"%").Delete(&model.SharesDBModel{})
42 | s.UpdateConfigFile()
43 | }
44 |
45 | func (s *sharesStruct) GetSharesByName(name string) (shares []model2.SharesDBModel) {
46 | s.db.Select("anonymous,path,id").Where("name = ?", name).Find(&shares)
47 |
48 | return
49 | }
50 | func (s *sharesStruct) GetSharesByPath(path string) (shares []model2.SharesDBModel) {
51 | s.db.Select("anonymous,path,id").Where("path = ?", path).Find(&shares)
52 | return
53 | }
54 | func (s *sharesStruct) GetSharesList() (shares []model2.SharesDBModel) {
55 | s.db.Select("anonymous,path,id").Find(&shares)
56 | return
57 | }
58 | func (s *sharesStruct) CreateShare(share model2.SharesDBModel) {
59 | s.db.Create(&share)
60 | s.InitSambaConfig()
61 | s.UpdateConfigFile()
62 | }
63 | func (s *sharesStruct) DeleteShare(id string) {
64 | s.db.Where("id= ?", id).Delete(&model.SharesDBModel{})
65 | s.UpdateConfigFile()
66 | }
67 |
68 | func (s *sharesStruct) UpdateConfigFile() {
69 | shares := []model2.SharesDBModel{}
70 | s.db.Select("anonymous,path").Find(&shares)
71 | //generated config file
72 | var configStr = ""
73 | for _, share := range shares {
74 | dirName := filepath.Base(share.Path)
75 | configStr += `
76 | [` + dirName + `]
77 | comment = CasaOS share ` + dirName + `
78 | public = Yes
79 | path = ` + share.Path + `
80 | browseable = Yes
81 | read only = No
82 | guest ok = Yes
83 | create mask = 0777
84 | directory mask = 0777
85 |
86 | `
87 | }
88 | //write config file
89 | file.WriteToPath([]byte(configStr), "/etc/samba", "smb.casa.conf")
90 | //restart samba
91 | command2.ExecResultStrArray("source " + config.AppInfo.ShellPath + "/helper.sh ;RestartSMBD")
92 |
93 | }
94 | func (s *sharesStruct) InitSambaConfig() {
95 | if file.Exists("/etc/samba/smb.conf") {
96 | str := file.ReadLine(1, "/etc/samba/smb.conf")
97 | if strings.Contains(str, "# Copyright (c) 2021-2022 CasaOS Inc. All rights reserved.") {
98 | return
99 | }
100 | file.MoveFile("/etc/samba/smb.conf", "/etc/samba/smb.conf.bak")
101 | var smbConf = ""
102 | smbConf += `# Copyright (c) 2021-2022 CasaOS Inc. All rights reserved.
103 | #
104 | #
105 | # ______ _______
106 | # ( __ \ ( ___ )
107 | # | ( \ ) | ( ) |
108 | # | | ) | | | | |
109 | # | | | | | | | |
110 | # | | ) | | | | |
111 | # | (__/ ) | (___) |
112 | # (______/ (_______)
113 | #
114 | # _ _______ _________
115 | # ( ( /| ( ___ ) \__ __/
116 | # | \ ( | | ( ) | ) (
117 | # | \ | | | | | | | |
118 | # | (\ \) | | | | | | |
119 | # | | \ | | | | | | |
120 | # | ) \ | | (___) | | |
121 | # |/ )_) (_______) )_(
122 | #
123 | # _______ _______ ______ _________ _______
124 | # ( ) ( ___ ) ( __ \ \__ __/ ( ____ \ |\ /|
125 | # | () () | | ( ) | | ( \ ) ) ( | ( \/ ( \ / )
126 | # | || || | | | | | | | ) | | | | (__ \ (_) /
127 | # | |(_)| | | | | | | | | | | | | __) \ /
128 | # | | | | | | | | | | ) | | | | ( ) (
129 | # | ) ( | | (___) | | (__/ ) ___) (___ | ) | |
130 | # |/ \| (_______) (______/ \_______/ |/ \_/
131 | #
132 | #
133 | # IMPORTANT: CasaOS will not provide technical support for any issues
134 | # caused by unauthorized modification to the configuration.
135 |
136 | [global]
137 | ## fruit settings
138 | min protocol = SMB2
139 | ea support = yes
140 | ## vfs objects = fruit streams_xattr
141 | fruit:metadata = stream
142 | fruit:model = Macmini
143 | fruit:veto_appledouble = no
144 | fruit:posix_rename = yes
145 | fruit:zero_file_id = yes
146 | fruit:wipe_intentionally_left_blank_rfork = yes
147 | fruit:delete_empty_adfiles = yes
148 | map to guest = bad user
149 | include=/etc/samba/smb.casa.conf`
150 | file.WriteToPath([]byte(smbConf), "/etc/samba", "smb.conf")
151 | }
152 |
153 | }
154 |
155 | func NewSharesService(db *gorm.DB) SharesService {
156 | return &sharesStruct{db: db}
157 | }
158 |
--------------------------------------------------------------------------------
/.all-contributorsrc:
--------------------------------------------------------------------------------
1 | {
2 | "files": [
3 | "README.md"
4 | ],
5 | "imageSize": 100,
6 | "badgeTemplate": "
&color=162453&style=flat-square&logo=Handshake&logoColor=fff\" />",
7 | "commit": false,
8 | "contributors": [
9 | {
10 | "login": "jerrykuku",
11 | "name": "老竭力",
12 | "avatar_url": "https://avatars.githubusercontent.com/u/9485680?v=4",
13 | "profile": "https://github.com/jerrykuku",
14 | "contributions": [
15 | "code",
16 | "doc",
17 | "ideas",
18 | "infra",
19 | "maintenance",
20 | "platform",
21 | "question",
22 | "review"
23 | ]
24 | },
25 | {
26 | "login": "LinkLeong",
27 | "name": "link",
28 | "avatar_url": "https://avatars.githubusercontent.com/u/13556972?v=4",
29 | "profile": "https://github.com/LinkLeong",
30 | "contributions": [
31 | "code",
32 | "doc",
33 | "ideas",
34 | "infra",
35 | "maintenance",
36 | "question",
37 | "review"
38 | ]
39 | },
40 | {
41 | "login": "tigerinus",
42 | "name": "Tiger Wang (王豫)",
43 | "avatar_url": "https://avatars.githubusercontent.com/u/7172560?v=4",
44 | "profile": "https://github.com/tigerinus",
45 | "contributions": [
46 | "code",
47 | "doc",
48 | "ideas",
49 | "infra",
50 | "maintenance",
51 | "mentoring",
52 | "security",
53 | "question",
54 | "review"
55 | ]
56 | },
57 | {
58 | "login": "Lauren-ED209",
59 | "name": "Lauren",
60 | "avatar_url": "https://avatars.githubusercontent.com/u/8243355?v=4",
61 | "profile": "https://github.com/Lauren-ED209",
62 | "contributions": [
63 | "ideas",
64 | "fundingFinding",
65 | "projectManagement",
66 | "question",
67 | "test"
68 | ]
69 | },
70 | {
71 | "login": "JohnGuan",
72 | "name": "John Guan",
73 | "avatar_url": "https://avatars.githubusercontent.com/u/3358477?v=4",
74 | "profile": "https://JohnGuan.Cn",
75 | "contributions": [
76 | "blog",
77 | "content",
78 | "doc",
79 | "ideas",
80 | "eventOrganizing",
81 | "mentoring",
82 | "question",
83 | "review"
84 | ]
85 | },
86 | {
87 | "login": "dtaivpp",
88 | "name": "David Tippett",
89 | "avatar_url": "https://avatars.githubusercontent.com/u/17506770?v=4",
90 | "profile": "https://blog.tippybits.com",
91 | "contributions": [
92 | "doc",
93 | "ideas",
94 | "question"
95 | ]
96 | },
97 | {
98 | "login": "zarevskaya",
99 | "name": "Skaya",
100 | "avatar_url": "https://avatars.githubusercontent.com/u/60230221?v=4",
101 | "profile": "https://github.com/zarevskaya",
102 | "contributions": [
103 | "mentoring",
104 | "question",
105 | "tutorial",
106 | "translation"
107 | ]
108 | },
109 | {
110 | "login": "AuthorShin",
111 | "name": "AuthorShin",
112 | "avatar_url": "https://avatars.githubusercontent.com/u/4959043?v=4",
113 | "profile": "https://github.com/AuthorShin",
114 | "contributions": [
115 | "test",
116 | "bug",
117 | "question",
118 | "ideas"
119 | ]
120 | },
121 | {
122 | "login": "baptiste313",
123 | "name": "baptiste313",
124 | "avatar_url": "https://avatars.githubusercontent.com/u/93325157?v=4",
125 | "profile": "https://github.com/baptiste313",
126 | "contributions": [
127 | "translation"
128 | ]
129 | },
130 | {
131 | "login": "DrMxrcy",
132 | "name": "DrMxrcy",
133 | "avatar_url": "https://avatars.githubusercontent.com/u/58747968?v=4",
134 | "profile": "https://github.com/DrMxrcy",
135 | "contributions": [
136 | "test",
137 | "ideas",
138 | "question"
139 | ]
140 | },
141 | {
142 | "login": "Joooost",
143 | "name": "Joooost",
144 | "avatar_url": "https://avatars.githubusercontent.com/u/12090673?v=4",
145 | "profile": "https://github.com/Joooost",
146 | "contributions": [
147 | "ideas"
148 | ]
149 | },
150 | {
151 | "login": "sio",
152 | "name": "Vitaly Potyarkin",
153 | "avatar_url": "https://avatars.githubusercontent.com/u/334908?v=4",
154 | "profile": "https://potyarkin.ml",
155 | "contributions": [
156 | "ideas"
157 | ]
158 | },
159 | {
160 | "login": "bearfrieze",
161 | "name": "Bjørn Friese",
162 | "avatar_url": "https://avatars.githubusercontent.com/u/1023813?v=4",
163 | "profile": "https://github.com/bearfrieze",
164 | "contributions": [
165 | "ideas"
166 | ]
167 | },
168 | {
169 | "login": "Protektor-Desura",
170 | "name": "Protektor",
171 | "avatar_url": "https://avatars.githubusercontent.com/u/1195496?v=4",
172 | "profile": "https://github.com/Protektor-Desura",
173 | "contributions": [
174 | "bug",
175 | "ideas",
176 | "question"
177 | ]
178 | },
179 | {
180 | "login": "llwaini",
181 | "name": "llwaini",
182 | "avatar_url": "https://avatars.githubusercontent.com/u/59589857?v=4",
183 | "profile": "https://github.com/llwaini",
184 | "contributions": [
185 | "projectManagement",
186 | "test",
187 | "tutorial"
188 | ]
189 | }
190 | ],
191 | "contributorsPerLine": 7,
192 | "projectName": "CasaOS",
193 | "projectOwner": "IceWhaleTech",
194 | "repoType": "github",
195 | "repoHost": "https://github.com",
196 | "skipCi": true
197 | }
198 |
--------------------------------------------------------------------------------
/web/img/Files.svg:
--------------------------------------------------------------------------------
1 |
2 |
68 |
--------------------------------------------------------------------------------
/route/init.go:
--------------------------------------------------------------------------------
1 | package route
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "strconv"
7 | "strings"
8 | "time"
9 |
10 | "github.com/IceWhaleTech/CasaOS/pkg/config"
11 | "github.com/IceWhaleTech/CasaOS/pkg/samba"
12 | "github.com/IceWhaleTech/CasaOS/pkg/utils/command"
13 | "github.com/IceWhaleTech/CasaOS/pkg/utils/encryption"
14 | "github.com/IceWhaleTech/CasaOS/pkg/utils/file"
15 | "github.com/IceWhaleTech/CasaOS/pkg/utils/loger"
16 | "github.com/IceWhaleTech/CasaOS/service"
17 | model2 "github.com/IceWhaleTech/CasaOS/service/model"
18 | uuid "github.com/satori/go.uuid"
19 | "go.uber.org/zap"
20 | )
21 |
22 | func InitFunction() {
23 | ShellInit()
24 | CheckSerialDiskMount()
25 | CheckToken2_11()
26 | ImportApplications()
27 | // Soon to be removed
28 | ChangeAPIUrl()
29 | MoveUserToDB()
30 | go InitNetworkMount()
31 |
32 | }
33 |
34 | func CheckSerialDiskMount() {
35 | // check mount point
36 | dbList := service.MyService.Disk().GetSerialAll()
37 |
38 | list := service.MyService.Disk().LSBLK(true)
39 | mountPoint := make(map[string]string, len(dbList))
40 | //remount
41 | for _, v := range dbList {
42 | mountPoint[v.UUID] = v.MountPoint
43 | }
44 | for _, v := range list {
45 | command.ExecEnabledSMART(v.Path)
46 | if v.Children != nil {
47 | for _, h := range v.Children {
48 | //if len(h.MountPoint) == 0 && len(v.Children) == 1 && h.FsType == "ext4" {
49 | if m, ok := mountPoint[h.UUID]; ok {
50 | //mount point check
51 | volume := m
52 | if !file.CheckNotExist(m) {
53 | for i := 0; file.CheckNotExist(volume); i++ {
54 | volume = m + strconv.Itoa(i+1)
55 | }
56 | }
57 | service.MyService.Disk().MountDisk(h.Path, volume)
58 | if volume != m {
59 | ms := model2.SerialDisk{}
60 | ms.UUID = v.UUID
61 | ms.MountPoint = volume
62 | service.MyService.Disk().UpdateMountPoint(ms)
63 | }
64 |
65 | }
66 | //}
67 | }
68 | }
69 | }
70 | service.MyService.Disk().RemoveLSBLKCache()
71 | command.OnlyExec("source " + config.AppInfo.ShellPath + "/helper.sh ;AutoRemoveUnuseDir")
72 | }
73 | func ShellInit() {
74 | command.OnlyExec("curl -fsSL https://raw.githubusercontent.com/IceWhaleTech/get/main/assist.sh | bash")
75 | if !file.CheckNotExist("/casaOS") {
76 | command.OnlyExec("source /casaOS/server/shell/update.sh ;")
77 | command.OnlyExec("source " + config.AppInfo.ShellPath + "/delete-old-service.sh ;")
78 | }
79 |
80 | }
81 | func CheckToken2_11() {
82 | if len(config.ServerInfo.Token) == 0 {
83 | token := uuid.NewV4().String
84 | config.ServerInfo.Token = token()
85 | config.Cfg.Section("server").Key("Token").SetValue(token())
86 | config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath)
87 | }
88 |
89 | if len(config.UserInfo.Description) == 0 {
90 | config.Cfg.Section("user").Key("Description").SetValue("nothing")
91 | config.UserInfo.Description = "nothing"
92 | config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath)
93 | }
94 |
95 | if service.MyService.System().GetSysInfo().KernelArch == "aarch64" && config.ServerInfo.USBAutoMount != "True" && strings.Contains(service.MyService.System().GetDeviceTree(), "Raspberry Pi") {
96 | service.MyService.System().UpdateUSBAutoMount("False")
97 | service.MyService.System().ExecUSBAutoMountShell("False")
98 | }
99 |
100 | // str := []string{}
101 | // str = append(str, "ddd")
102 | // str = append(str, "aaa")
103 | // ddd := strings.Join(str, "|")
104 | // config.Cfg.Section("file").Key("ShareDir").SetValue(ddd)
105 |
106 | // config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath)
107 |
108 | }
109 |
110 | func ImportApplications() {
111 | service.MyService.App().ImportApplications(true)
112 | }
113 |
114 | // 0.3.1
115 | func ChangeAPIUrl() {
116 |
117 | newAPIUrl := "https://api.casaos.io/casaos-api"
118 | if config.ServerInfo.ServerApi == "https://api.casaos.zimaboard.com" {
119 | config.ServerInfo.ServerApi = newAPIUrl
120 | config.Cfg.Section("server").Key("ServerApi").SetValue(newAPIUrl)
121 | config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath)
122 | }
123 |
124 | }
125 |
126 | //0.3.3
127 | //Transferring user data to the database
128 | func MoveUserToDB() {
129 |
130 | if len(config.UserInfo.UserName) > 0 && service.MyService.User().GetUserInfoByUserName(config.UserInfo.UserName).Id == 0 {
131 | user := model2.UserDBModel{}
132 | user.Username = config.UserInfo.UserName
133 | user.Email = config.UserInfo.Email
134 | user.Nickname = config.UserInfo.NickName
135 | user.Password = encryption.GetMD5ByStr(config.UserInfo.PWD)
136 | user.Role = "admin"
137 | user = service.MyService.User().CreateUser(user)
138 | if user.Id > 0 {
139 | userPath := config.AppInfo.UserDataPath + "/" + strconv.Itoa(user.Id)
140 | file.MkDir(userPath)
141 | os.Rename("/casaOS/server/conf/app_order.json", userPath+"/app_order.json")
142 | }
143 |
144 | }
145 | }
146 |
147 | func InitNetworkMount() {
148 | time.Sleep(time.Second * 10)
149 | connections := service.MyService.Connections().GetConnectionsList()
150 | for _, v := range connections {
151 | connection := service.MyService.Connections().GetConnectionByID(fmt.Sprint(v.ID))
152 | directories, err := samba.GetSambaSharesList(connection.Host, connection.Port, connection.Username, connection.Password)
153 | if err != nil {
154 | service.MyService.Connections().DeleteConnection(fmt.Sprint(connection.ID))
155 | loger.Error("mount samba err", zap.Any("err", err), zap.Any("info", connection))
156 | continue
157 | }
158 | baseHostPath := "/mnt/" + connection.Host
159 |
160 | mountPointList := service.MyService.System().GetDirPath(baseHostPath)
161 | for _, v := range mountPointList {
162 | service.MyService.Connections().UnmountSmaba(v.Path)
163 | }
164 |
165 | os.RemoveAll(baseHostPath)
166 |
167 | file.IsNotExistMkDir(baseHostPath)
168 | for _, v := range directories {
169 | mountPoint := baseHostPath + "/" + v
170 | file.IsNotExistMkDir(mountPoint)
171 | service.MyService.Connections().MountSmaba(connection.Username, connection.Host, v, connection.Port, mountPoint, connection.Password)
172 | }
173 | connection.Directories = strings.Join(directories, ",")
174 | service.MyService.Connections().UpdateConnection(&connection)
175 | }
176 | }
177 |
--------------------------------------------------------------------------------
/web/img/CasaConnect.svg:
--------------------------------------------------------------------------------
1 |
2 |
56 |
--------------------------------------------------------------------------------
/route/v1/samba.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: LinkLeong link@icewhale.com
3 | * @Date: 2022-07-26 11:08:48
4 | * @LastEditors: LinkLeong
5 | * @LastEditTime: 2022-08-17 18:25:42
6 | * @FilePath: /CasaOS/route/v1/samba.go
7 | * @Description:
8 | * @Website: https://www.casaos.io
9 | * Copyright (c) 2022 by icewhale, All Rights Reserved.
10 | */
11 | package v1
12 |
13 | import (
14 | "fmt"
15 | "os"
16 | "path/filepath"
17 | "strings"
18 |
19 | "github.com/IceWhaleTech/CasaOS/model"
20 | "github.com/IceWhaleTech/CasaOS/pkg/samba"
21 | "github.com/IceWhaleTech/CasaOS/pkg/utils/common_err"
22 | "github.com/IceWhaleTech/CasaOS/pkg/utils/file"
23 | "github.com/IceWhaleTech/CasaOS/service"
24 | model2 "github.com/IceWhaleTech/CasaOS/service/model"
25 | "github.com/gin-gonic/gin"
26 | )
27 |
28 | // service
29 |
30 | func GetSambaStatus(c *gin.Context) {
31 | status := service.MyService.System().IsServiceRunning("smbd")
32 |
33 | if !status {
34 | c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_NOT_RUNNING, Message: common_err.GetMsg(common_err.SERVICE_NOT_RUNNING)})
35 | return
36 | }
37 | needInit := true
38 | if file.Exists("/etc/samba/smb.conf") {
39 | str := file.ReadLine(1, "/etc/samba/smb.conf")
40 | if strings.Contains(str, "# Copyright (c) 2021-2022 CasaOS Inc. All rights reserved.") {
41 | needInit = false
42 | }
43 | }
44 | data := make(map[string]string, 1)
45 | data["need_init"] = fmt.Sprintf("%v", needInit)
46 | c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: data})
47 | }
48 |
49 | func GetSambaSharesList(c *gin.Context) {
50 | shares := service.MyService.Shares().GetSharesList()
51 | shareList := []model.Shares{}
52 | for _, v := range shares {
53 | shareList = append(shareList, model.Shares{
54 | Anonymous: v.Anonymous,
55 | Path: v.Path,
56 | ID: v.ID,
57 | })
58 | }
59 | c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: shareList})
60 | }
61 |
62 | func PostSambaSharesCreate(c *gin.Context) {
63 | shares := []model.Shares{}
64 | c.ShouldBindJSON(&shares)
65 | for _, v := range shares {
66 | if v.Path == "" {
67 | c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.INSUFFICIENT_PERMISSIONS, Message: common_err.GetMsg(common_err.INSUFFICIENT_PERMISSIONS)})
68 | return
69 | }
70 | if !file.Exists(v.Path) {
71 | c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.DIR_NOT_EXISTS, Message: common_err.GetMsg(common_err.DIR_NOT_EXISTS)})
72 | return
73 | }
74 | if len(service.MyService.Shares().GetSharesByPath(v.Path)) > 0 {
75 | c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.SHARE_ALREADY_EXISTS, Message: common_err.GetMsg(common_err.SHARE_ALREADY_EXISTS)})
76 | return
77 | }
78 | if len(service.MyService.Shares().GetSharesByPath(filepath.Base(v.Path))) > 0 {
79 | c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.SHARE_NAME_ALREADY_EXISTS, Message: common_err.GetMsg(common_err.SHARE_NAME_ALREADY_EXISTS)})
80 | return
81 | }
82 | }
83 | for _, v := range shares {
84 | shareDBModel := model2.SharesDBModel{}
85 | shareDBModel.Anonymous = true
86 | shareDBModel.Path = v.Path
87 | shareDBModel.Name = filepath.Base(v.Path)
88 | os.Chmod(v.Path, 0777)
89 | service.MyService.Shares().CreateShare(shareDBModel)
90 | }
91 |
92 | c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: shares})
93 | }
94 | func DeleteSambaShares(c *gin.Context) {
95 | id := c.Param("id")
96 | if id == "" {
97 | c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.INSUFFICIENT_PERMISSIONS, Message: common_err.GetMsg(common_err.INSUFFICIENT_PERMISSIONS)})
98 | return
99 | }
100 | service.MyService.Shares().DeleteShare(id)
101 | c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: id})
102 | }
103 |
104 | //client
105 |
106 | func GetSambaConnectionsList(c *gin.Context) {
107 | connections := service.MyService.Connections().GetConnectionsList()
108 | connectionList := []model.Connections{}
109 | for _, v := range connections {
110 | connectionList = append(connectionList, model.Connections{
111 | ID: v.ID,
112 | Username: v.Username,
113 | Port: v.Port,
114 | Host: v.Host,
115 | MountPoint: v.MountPoint,
116 | })
117 | }
118 | c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: connectionList})
119 | }
120 |
121 | func PostSambaConnectionsCreate(c *gin.Context) {
122 | connection := model.Connections{}
123 | c.ShouldBindJSON(&connection)
124 | if connection.Port == "" {
125 | connection.Port = "445"
126 | }
127 | if connection.Username == "" || connection.Host == "" {
128 | c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS)})
129 | return
130 | }
131 | connection.Host = strings.Split(connection.Host, "/")[0]
132 | // check is exists
133 | connections := service.MyService.Connections().GetConnectionByHost(connection.Host)
134 | if len(connections) > 0 {
135 | c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.Record_ALREADY_EXIST, Message: common_err.GetMsg(common_err.Record_ALREADY_EXIST), Data: common_err.GetMsg(common_err.Record_ALREADY_EXIST)})
136 | return
137 | }
138 | // check connect is ok
139 | directories, err := samba.GetSambaSharesList(connection.Host, connection.Port, connection.Username, connection.Password)
140 | if err != nil {
141 | c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: err.Error()})
142 | return
143 | }
144 |
145 | connectionDBModel := model2.ConnectionsDBModel{}
146 | connectionDBModel.Username = connection.Username
147 | connectionDBModel.Password = connection.Password
148 | connectionDBModel.Host = connection.Host
149 | connectionDBModel.Port = connection.Port
150 | connectionDBModel.Directories = strings.Join(directories, ",")
151 | baseHostPath := "/mnt/" + connection.Host
152 | connectionDBModel.MountPoint = baseHostPath
153 | connection.MountPoint = baseHostPath
154 | file.IsNotExistMkDir(baseHostPath)
155 | for _, v := range directories {
156 | mountPoint := baseHostPath + "/" + v
157 | file.IsNotExistMkDir(mountPoint)
158 | service.MyService.Connections().MountSmaba(connectionDBModel.Username, connectionDBModel.Host, v, connectionDBModel.Port, mountPoint, connectionDBModel.Password)
159 | }
160 |
161 | service.MyService.Connections().CreateConnection(&connectionDBModel)
162 |
163 | connection.ID = connectionDBModel.ID
164 | c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: connection})
165 | }
166 |
167 | func DeleteSambaConnections(c *gin.Context) {
168 | id := c.Param("id")
169 | connection := service.MyService.Connections().GetConnectionByID(id)
170 | if connection.Username == "" {
171 | c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.Record_NOT_EXIST, Message: common_err.GetMsg(common_err.Record_NOT_EXIST)})
172 | return
173 | }
174 | mountPointList := service.MyService.System().GetDirPath(connection.MountPoint)
175 | for _, v := range mountPointList {
176 | service.MyService.Connections().UnmountSmaba(v.Path)
177 | }
178 | os.RemoveAll(connection.MountPoint)
179 | service.MyService.Connections().DeleteConnection(id)
180 | c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: id})
181 | }
182 |
--------------------------------------------------------------------------------