Compare commits

...

75 Commits

Author SHA1 Message Date
link
75643287a5 finish (#742) 2022-12-07 14:29:22 +08:00
link
6bda9406fb fixed (#741) 2022-12-07 10:57:27 +08:00
Tiger Wang
be9a010d17 fix sqlite db lock by reducing maximum open connections to 1 (#732) 2022-12-02 17:25:44 -05:00
Tiger Wang
3875827b7a replace CGO implementation of sqlite with an implementation in pure Go (#724) 2022-11-30 00:10:05 -05:00
LinkLeong
5338d4662e Merge branch 'main' of https://github.com/IceWhaleTech/CasaOS 2022-11-30 04:11:36 +00:00
LinkLeong
3b9c4b62c5 Remove frequent logs 2022-11-30 04:10:46 +00:00
Tiger Wang
536eac788d move app domain to its own module (0.4.x) (#689) 2022-11-29 12:17:14 -05:00
Ns2Kracy
fc371bf068 add arch support (#707) 2022-11-29 10:44:45 -05:00
LinkLeong
299b20a087 Merge branch 'main' of https://github.com/IceWhaleTech/CasaOS 2022-11-29 02:25:36 +00:00
LinkLeong
eb31bf5586 wip 2022-11-29 02:25:26 +00:00
mojothemonkey2
04d4eacc60 Re-instate default zone0 for CPU Temp (fix #694) (#708) 2022-11-25 14:28:51 +08:00
LinkLeong
60608c5dc7 update update.sh 2022-11-21 11:23:03 +00:00
link
b582e8f53e Update CHANGELOG.md
Signed-off-by: link <a624669980@163.com>
2022-11-21 18:21:03 +08:00
mojothemonkey2
5690ccc1b5 CPU temp support for other platforms (fix #661) (#677)
Signed-off-by: link <a624669980@163.com>
Co-authored-by: link <a624669980@163.com>
2022-11-16 17:30:19 +08:00
LinkLeong
13c2967889 update changelog 2022-11-16 05:57:39 +00:00
a624669980@163.com
8a37f9c879 Merge branch 'heads/v0.3.7' 2022-11-15 15:56:08 +08:00
Tiger Wang
96e9284235 Update README.md
Signed-off-by: Tiger Wang <tigerwang@outlook.com>
2022-11-07 11:22:16 -05:00
Tiger Wang
06f83d3d68 Update bug_report.md
Signed-off-by: Tiger Wang <tigerwang@outlook.com>
2022-11-04 07:36:52 -04:00
LinkLeong
438b8a1dd2 Solve the problem of local application import failure 2022-11-04 07:41:09 +00:00
Tiger Wang
29e701cb7c fix potential goroutine leak due to http request without a timeout (#656) 2022-11-03 18:07:49 -04:00
Tiger Wang
43cc7596d2 bump version (#650) 2022-11-02 11:07:30 -04:00
John Guan
11f800d138 Update feedback.yml 2022-11-02 11:31:14 +08:00
John Guan
a58a9e5477 update issue templates 2022-11-02 11:29:21 +08:00
John Guan
5f015e9038 update issue templates 2022-11-02 11:26:24 +08:00
John Guan
3922296d9b Revert "Update alpha_bug_report.yml"
This reverts commit 908a16ace4.
2022-11-02 11:24:58 +08:00
John Guan
908a16ace4 Update alpha_bug_report.yml 2022-11-02 11:24:37 +08:00
John Guan
f7a3863720 Update .gitignore 2022-11-02 10:50:01 +08:00
link
e94c2ff5c5 Update release.yml
Signed-off-by: link <a624669980@163.com>
2022-10-31 14:40:32 +08:00
link
89487eb5db Update CHANGELOG.md
Signed-off-by: link <a624669980@163.com>
2022-10-29 10:36:51 +08:00
Tiger Wang
0fb5cab480 move common proxy to CasaOS-Common (#643)
...and use gateway proxy from CasaOS-Common
2022-10-28 17:34:18 -04:00
link
f210f29ae5 Update CHANGELOG.md
Signed-off-by: link <a624669980@163.com>
2022-10-28 08:27:00 +08:00
Tiger Wang
7bd5c6a2b4 remove repeating logging to reduce unnecessary verbosity 2022-10-27 11:59:35 -04:00
link
230f1585a3 V0.3.7 (#629)
* add send notify function

* add shell script

* add system notiry

* remove disk and test common package

* update http status

* add share function to common

* remove temp path

* remove /DATA directory initialization - moved to local-storage (#578)

* update goreleaser configuration

* wip

* change service type to notify for systemd so its status is OK only when service is initialized successfully

* update CasaOS-Common to fix runtime error

* wip

* add send notify function

* add shell script

* add system notiry

* remove disk and test common package

* update http status

* add share function to common

* remove temp path

* remove /DATA directory initialization - moved to local-storage (#578)

* update goreleaser configuration

* wip

* change service type to notify for systemd so its status is OK only when service is initialized successfully

* update CasaOS-Common to fix runtime error

* wip

* wip

* wip

* wip

* wip

* Utilization interface to supplement disk information

* fix upload file

* wip

* wip

* add update url

* wip

* wip

* add change log

* update changelog

* update release

Signed-off-by: link <a624669980@163.com>
Co-authored-by: Tiger Wang <tigerwang@outlook.com>
2022-10-26 15:46:40 +08:00
LinkLeong
ca967ec59c update release 2022-10-26 08:45:32 +01:00
Tiger Wang
c0b3260a6c use $BASH instead of $SHELL in setup and cleanup script in case of running under non-bash shell. (#617)
fixes https://github.com/IceWhaleTech/CasaOS/issues/572
2022-10-21 13:40:31 -04:00
link
9ed82cd55e Update CHANGELOG.md
Signed-off-by: link <a624669980@163.com>
2022-10-20 17:29:02 +08:00
Tiger Wang
381fb85b1d v0.3.7 (#600)
* add send notify function

* add shell script

* add system notiry

* remove disk and test common package

* update http status

* add share function to common

* remove temp path

* remove /DATA directory initialization - moved to local-storage (#578)

* update goreleaser configuration

* wip

* change service type to notify for systemd so its status is OK only when service is initialized successfully

* update CasaOS-Common to fix runtime error

* wip

* add send notify function

* add shell script

* add system notiry

* remove disk and test common package

* update http status

* add share function to common

* remove temp path

* remove /DATA directory initialization - moved to local-storage (#578)

* update goreleaser configuration

* wip

* change service type to notify for systemd so its status is OK only when service is initialized successfully

* update CasaOS-Common to fix runtime error

* wip

* wip

* wip

* wip

* wip

* Utilization interface to supplement disk information

* fix upload file

* wip

* wip

* add update url

* wip

* wip

* add change log

* update changelog

Co-authored-by: LinkLeong <a624669980@163.com>
2022-10-20 14:25:26 +08:00
LinkLeong
2beb1c0d82 update changelog 2022-10-19 09:08:11 +01:00
LinkLeong
772c3e0bc0 Merge branch 'v0.3.7' of https://github.com/IceWhaleTech/CasaOS into v0.3.7 2022-10-19 09:00:57 +01:00
LinkLeong
bcda992322 add change log 2022-10-19 09:00:45 +01:00
link
8cec47e4db Update demo.yml
Signed-off-by: link <a624669980@163.com>
2022-10-19 11:41:09 +08:00
Tiger Wang
87de9cec0a wip 2022-10-18 18:58:26 -04:00
Tiger Wang
6d47d4ff18 wip 2022-10-18 17:30:00 -04:00
LinkLeong
4bb81e4669 Merge branch 'v0.3.7' of https://github.com/IceWhaleTech/CasaOS into v0.3.7 2022-10-17 06:54:34 +01:00
LinkLeong
c05d837350 add update url 2022-10-17 06:53:23 +01:00
Tiger Wang
8908c39969 wip 2022-10-13 19:33:18 -04:00
Tiger Wang
df0f015944 wip 2022-10-13 16:09:19 -04:00
LinkLeong
46a37f0510 fix upload file 2022-10-13 11:32:30 +01:00
LinkLeong
6ea3cdb364 Utilization interface to supplement disk information 2022-10-13 04:45:17 +01:00
LinkLeong
be80d0cd95 Merge branch 'v0.3.7' of https://github.com/IceWhaleTech/CasaOS into v0.3.7 2022-10-13 04:32:13 +01:00
a624669980@163.com
296e88d099 Merge branch 'v0.3.7' of ssh://github.com/IceWhaleTech/CasaOS into v0.3.7 2022-10-13 11:16:52 +08:00
Tiger Wang
b61a3db611 wip 2022-10-12 16:12:14 -04:00
Tiger Wang
0f3d3e82f5 wip 2022-10-11 23:33:58 -04:00
Tiger Wang
dd66f73157 wip 2022-10-11 23:33:58 -04:00
Tiger Wang
cbbb907d6a wip 2022-10-11 23:33:58 -04:00
Tiger Wang
ff6cdb6fda wip 2022-10-11 23:33:58 -04:00
Tiger Wang
2eac040875 update CasaOS-Common to fix runtime error 2022-10-11 23:33:54 -04:00
Tiger Wang
b41d855f73 change service type to notify for systemd so its status is OK only when service is initialized successfully 2022-10-11 23:31:42 -04:00
Tiger Wang
182bc25343 wip 2022-10-11 23:31:42 -04:00
Tiger Wang
12d5e5db03 update goreleaser configuration 2022-10-11 23:31:42 -04:00
Tiger Wang
455d226dcd remove /DATA directory initialization - moved to local-storage (#578) 2022-10-11 23:31:42 -04:00
LinkLeong
f0448cd1b9 remove temp path 2022-10-11 23:31:42 -04:00
LinkLeong
aff18fa091 add share function to common 2022-10-11 23:31:42 -04:00
LinkLeong
80c347ac01 update http status 2022-10-11 23:31:42 -04:00
LinkLeong
8113f51cf7 remove disk and test common package 2022-10-11 23:31:42 -04:00
LinkLeong
4f491fa22f add system notiry 2022-10-11 23:31:42 -04:00
LinkLeong
3935489d8b add shell script 2022-10-11 23:31:37 -04:00
LinkLeong
d14381e6a2 add send notify function 2022-10-11 23:28:25 -04:00
Tiger Wang
42ebd5f325 wip 2022-10-11 00:19:02 -04:00
Tiger Wang
a51bf70b79 update CasaOS-Common to fix runtime error 2022-09-29 00:05:12 -04:00
Tiger Wang
3787c7bf99 change service type to notify for systemd so its status is OK only when service is initialized successfully 2022-09-28 18:19:31 -04:00
LinkLeong
466350dd21 update CasaOS-Common 2022-09-13 07:02:21 +01:00
LinkLeong
fb39529e8f update common package 2022-09-10 01:00:01 +01:00
LinkLeong
4434ba522b update upgrade script address 2022-09-09 07:36:49 +01:00
LinkLeong
aac8fe85ba update the way environment variables are displayed 2022-09-09 06:46:51 +01:00
83 changed files with 1610 additions and 6090 deletions

View File

@@ -28,5 +28,19 @@ If applicable, add screenshots to help explain your problem.
- Browser [e.g. chrome, safari] - Browser [e.g. chrome, safari]
- Version [e.g. 22] - Version [e.g. 22]
**Logs**
run following command to collect corresponding logs:
```bash
sudo journalctl -xef -u casaos-gateway
sudo journalctl -xef -u casaos-user-service
sudo journalctl -xef -u casaos-local-storage
sudo journalctl -xef -u casaos.service
```
**Additional context** **Additional context**
Add any other context about the problem here. Add any other context about the problem here.

23
.github/ISSUE_TEMPLATE/feedback.yml vendored Normal file
View File

@@ -0,0 +1,23 @@
name: "Feedback"
description: Feedback, showcases, thoughts, needs and questions, etc.
title: "[Feedback] "
labels: ["feedback"]
body:
- type: markdown
attributes:
value: |
### ❤️ Thanks for your feedback!
> Come join our [Discord community](https://discord.gg/knqAbbBbeX) and paint the ideal home cloud with us.
- type: textarea
id: description
attributes:
label: Description
placeholder: What do you want to tell us?
validations:
required: true
- type: textarea
id: additional
attributes:
label: Additional Information
description: Please add logs/files/screenshots if you have them to help us better understanding.

View File

@@ -33,7 +33,7 @@ jobs:
- name: Get old instance and snapshot name, create new instance name - name: Get old instance and snapshot name, create new instance name
run: | run: |
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 echo "OLD_INSTANCE_SNAPSHOT_NAME=$(aws lightsail get-instance-snapshots | grep '"name": "casaos-0.3.6-1666150291' | sed 's/ //g' | sed 's/"//g' | sed 's/,//g' | sed 's/name://g')" >> $GITHUB_ENV
echo "OLD_INSTANCE_NAME=$(aws lightsail get-instances | grep '"name": "CasaOS-Demo-[0-9]' | sed 's/ //g' | sed 's/"//g' | sed 's/,//g' | sed 's/name://g')" >> $GITHUB_ENV echo "OLD_INSTANCE_NAME=$(aws lightsail get-instances | grep '"name": "CasaOS-Demo-[0-9]' | sed 's/ //g' | sed 's/"//g' | sed 's/,//g' | sed 's/name://g')" >> $GITHUB_ENV
echo "NEW_INSTANCE_NAME=CasaOS-Demo-$(date +%s)" >> $GITHUB_ENV echo "NEW_INSTANCE_NAME=CasaOS-Demo-$(date +%s)" >> $GITHUB_ENV

View File

@@ -28,6 +28,10 @@ jobs:
- -
name: Fetch all tags name: Fetch all tags
run: git fetch --force --tags run: git fetch --force --tags
- name: Get version
id: get_version
run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//}
- -
name: Set up Go name: Set up Go
uses: actions/setup-go@v2 uses: actions/setup-go@v2
@@ -45,3 +49,23 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Your GoReleaser Pro key, if you are using the 'goreleaser-pro' distribution # Your GoReleaser Pro key, if you are using the 'goreleaser-pro' distribution
# GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }} # GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
- name: Upload to oss
id: upload_to_oss
uses: tvrcgo/upload-to-oss@master
with:
key-id: ${{ secrets.OSS_KEY_ID }}
key-secret: ${{ secrets.OSS_KEY_SECRET }}
region: oss-cn-shanghai
bucket: casaos
assets: |
dist/checksums.txt:/IceWhaleTech/CasaOS/releases/download/${{ steps.get_version.outputs.VERSION }}/checksums.txt
dist/linux-arm-7-casaos-${{ steps.get_version.outputs.VERSION }}.tar.gz:/IceWhaleTech/CasaOS/releases/download/${{ steps.get_version.outputs.VERSION }}/linux-arm-7-casaos-${{ steps.get_version.outputs.VERSION }}.tar.gz
dist/linux-arm64-casaos-${{ steps.get_version.outputs.VERSION }}.tar.gz:/IceWhaleTech/CasaOS/releases/download/${{ steps.get_version.outputs.VERSION }}/linux-arm64-casaos-${{ steps.get_version.outputs.VERSION }}.tar.gz
dist/linux-amd64-casaos-${{ steps.get_version.outputs.VERSION }}.tar.gz:/IceWhaleTech/CasaOS/releases/download/${{ steps.get_version.outputs.VERSION }}/linux-amd64-casaos-${{ steps.get_version.outputs.VERSION }}.tar.gz
dist/linux-arm-7-casaos-migration-tool-${{ steps.get_version.outputs.VERSION }}.tar.gz:/IceWhaleTech/CasaOS/releases/download/${{ steps.get_version.outputs.VERSION }}/linux-arm-7-casaos-migration-tool-${{ steps.get_version.outputs.VERSION }}.tar.gz
dist/linux-arm64-casaos-migration-tool-${{ steps.get_version.outputs.VERSION }}.tar.gz:/IceWhaleTech/CasaOS/releases/download/${{ steps.get_version.outputs.VERSION }}/linux-arm64-casaos-migration-tool-${{ steps.get_version.outputs.VERSION }}.tar.gz
dist/linux-amd64-casaos-migration-tool-${{ steps.get_version.outputs.VERSION }}.tar.gz:/IceWhaleTech/CasaOS/releases/download/${{ steps.get_version.outputs.VERSION }}/linux-amd64-casaos-migration-tool-${{ steps.get_version.outputs.VERSION }}.tar.gz

3
.gitignore vendored
View File

@@ -37,3 +37,6 @@ github.com
.all-contributorsrc .all-contributorsrc
dist dist
CasaOS CasaOS
# System Files
.DS_Store

167
.goreleaser.debug.yaml Normal file
View File

@@ -0,0 +1,167 @@
# This is an example .goreleaser.yml file with some sensible defaults.
# Make sure to check the documentation at https://goreleaser.com
project_name: casaos
before:
hooks:
# You may remove this if you don't use go modules.
- go mod tidy
builds:
- id: casaos-amd64
binary: build/sysroot/usr/bin/casaos
env:
- CC=x86_64-linux-gnu-gcc
gcflags:
- all=-N -l
ldflags:
- -extldflags "-static"
tags:
- musl
- netgo
- osusergo
goos:
- linux
goarch:
- amd64
hooks:
post:
- find build/sysroot -type f | xargs -L 1 realpath --relative-to=build/sysroot > build/sysroot.manifest
- id: casaos-arm64
binary: build/sysroot/usr/bin/casaos
env:
- CC=aarch64-linux-gnu-gcc
gcflags:
- all=-N -l
ldflags:
- -extldflags "-static"
tags:
- musl
- netgo
- osusergo
goos:
- linux
goarch:
- arm64
hooks:
post:
- find build/sysroot -type f | xargs -L 1 realpath --relative-to=build/sysroot > build/sysroot.manifest
- id: casaos-arm-7
binary: build/sysroot/usr/bin/casaos
env:
- CC=arm-linux-gnueabihf-gcc
gcflags:
- all=-N -l
ldflags:
- -extldflags "-static"
tags:
- musl
- netgo
- osusergo
goos:
- linux
goarch:
- arm
goarm:
- "7"
hooks:
post:
- find build/sysroot -type f | xargs -L 1 realpath --relative-to=build/sysroot > build/sysroot.manifest
- id: casaos-migration-tool-amd64
binary: build/sysroot/usr/bin/casaos-migration-tool
main: ./cmd/migration-tool
env:
- CC=x86_64-linux-gnu-gcc
gcflags:
- all=-N -l
ldflags:
- -extldflags "-static"
tags:
- musl
- netgo
- osusergo
goos:
- linux
goarch:
- amd64
- id: casaos-migration-tool-arm64
binary: build/sysroot/usr/bin/casaos-migration-tool
main: ./cmd/migration-tool
env:
- CC=aarch64-linux-gnu-gcc
gcflags:
- all=-N -l
ldflags:
- -extldflags "-static"
tags:
- musl
- netgo
- osusergo
goos:
- linux
goarch:
- arm64
- id: casaos-migration-tool-arm-7
binary: build/sysroot/usr/bin/casaos-migration-tool
main: ./cmd/migration-tool
env:
- CC=arm-linux-gnueabihf-gcc
gcflags:
- all=-N -l
ldflags:
- -extldflags "-static"
tags:
- musl
- netgo
- osusergo
goos:
- linux
goarch:
- arm
goarm:
- "7"
archives:
- name_template: "{{ .Os }}-{{ .Arch }}-{{ .ProjectName }}-v{{ .Version }}"
id: casaos
builds:
- casaos-amd64
- casaos-arm64
- casaos-arm-7
replacements:
arm: arm-7
files:
- build/**/*
- name_template: "{{ .Os }}-{{ .Arch }}-{{ .ProjectName }}-migration-tool-v{{ .Version }}"
id: casaos-migration-tool
builds:
- casaos-migration-tool-amd64
- casaos-migration-tool-arm64
- casaos-migration-tool-arm-7
replacements:
arm: arm-7
files:
- build/sysroot/etc/**/*
checksum:
name_template: "checksums.txt"
snapshot:
name_template: "{{ incpatch .Version }}"
changelog:
sort: asc
filters:
exclude:
- "^docs:"
- "^test:"
# release:
# github:
# owner: IceWhaleTech
# name: CasaOS
# draft: true
# prerelease: auto
# mode: replace
# name_template: "v{{ .Version }}"
release:
github:
owner: IceWhaleTech
name: CasaOS
draft: true
prerelease: auto
mode: replace
name_template: "v{{ .Version }}"

View File

@@ -9,7 +9,6 @@ builds:
- id: casaos-amd64 - id: casaos-amd64
binary: build/sysroot/usr/bin/casaos binary: build/sysroot/usr/bin/casaos
env: env:
- CGO_ENABLED=1
- CC=x86_64-linux-gnu-gcc - CC=x86_64-linux-gnu-gcc
ldflags: ldflags:
- -s - -s
@@ -18,6 +17,7 @@ builds:
tags: tags:
- musl - musl
- netgo - netgo
- osusergo
goos: goos:
- linux - linux
goarch: goarch:
@@ -28,7 +28,6 @@ builds:
- id: casaos-arm64 - id: casaos-arm64
binary: build/sysroot/usr/bin/casaos binary: build/sysroot/usr/bin/casaos
env: env:
- CGO_ENABLED=1
- CC=aarch64-linux-gnu-gcc - CC=aarch64-linux-gnu-gcc
ldflags: ldflags:
- -s - -s
@@ -37,6 +36,7 @@ builds:
tags: tags:
- musl - musl
- netgo - netgo
- osusergo
goos: goos:
- linux - linux
goarch: goarch:
@@ -47,7 +47,6 @@ builds:
- id: casaos-arm-7 - id: casaos-arm-7
binary: build/sysroot/usr/bin/casaos binary: build/sysroot/usr/bin/casaos
env: env:
- CGO_ENABLED=1
- CC=arm-linux-gnueabihf-gcc - CC=arm-linux-gnueabihf-gcc
ldflags: ldflags:
- -s - -s
@@ -56,6 +55,7 @@ builds:
tags: tags:
- musl - musl
- netgo - netgo
- osusergo
goos: goos:
- linux - linux
goarch: goarch:
@@ -69,7 +69,6 @@ builds:
binary: build/sysroot/usr/bin/casaos-migration-tool binary: build/sysroot/usr/bin/casaos-migration-tool
main: ./cmd/migration-tool main: ./cmd/migration-tool
env: env:
- CGO_ENABLED=1
- CC=x86_64-linux-gnu-gcc - CC=x86_64-linux-gnu-gcc
ldflags: ldflags:
- -s - -s
@@ -78,6 +77,7 @@ builds:
tags: tags:
- musl - musl
- netgo - netgo
- osusergo
goos: goos:
- linux - linux
goarch: goarch:
@@ -86,7 +86,6 @@ builds:
binary: build/sysroot/usr/bin/casaos-migration-tool binary: build/sysroot/usr/bin/casaos-migration-tool
main: ./cmd/migration-tool main: ./cmd/migration-tool
env: env:
- CGO_ENABLED=1
- CC=aarch64-linux-gnu-gcc - CC=aarch64-linux-gnu-gcc
ldflags: ldflags:
- -s - -s
@@ -95,6 +94,7 @@ builds:
tags: tags:
- musl - musl
- netgo - netgo
- osusergo
goos: goos:
- linux - linux
goarch: goarch:
@@ -103,7 +103,6 @@ builds:
binary: build/sysroot/usr/bin/casaos-migration-tool binary: build/sysroot/usr/bin/casaos-migration-tool
main: ./cmd/migration-tool main: ./cmd/migration-tool
env: env:
- CGO_ENABLED=1
- CC=arm-linux-gnueabihf-gcc - CC=arm-linux-gnueabihf-gcc
ldflags: ldflags:
- -s - -s
@@ -112,6 +111,7 @@ builds:
tags: tags:
- musl - musl
- netgo - netgo
- osusergo
goos: goos:
- linux - linux
goarch: goarch:

View File

@@ -16,10 +16,76 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Security ### Security
## [0.4.0 - alpha]
### Added
- [Developer] Included `casaos-cli` command tool for debugging
- [Developer] Added message bus for events and actions - Use `casaos-cli message-bus` to manage.
- [Disk] Disk notification in Dashboard
- [System] Restart/shutdown directly from CasaOS Dashboard
### Changed
- [General] CasaOS new logo!
- [App] Redesign of Featured App
- [App] Now you can choose to delete userdata along with app uninstallation
### Security
- [System] Fixed a shell injection issue for better security
### Fixed ### Fixed
- [System] Re-instate default zone0 for CPU Temp ([#694](https://github.com/IceWhaleTech/CasaOS/issues/694))
- [Disk] Fixed storage name with extra `-1` after rebooting ([#698](https://github.com/IceWhaleTech/CasaOS/issues/698))
- [Disk] Fixed disk check so it does not impact disk going into idle ([#704](https://github.com/IceWhaleTech/CasaOS/issues/704))
## [0.3.6-alpha.1] - 2022-09-06 ## [0.3.8] 2022-11-21
### Added
- [System] Add system announcement
- [App] Allow to turn off the display of "Existing Docker Apps" in the settings.
### Changed
- [System] Improve the feedback function, you can submit feedback in the bottom right corner of WebUI.
### Fixed
- [System] Fix CPU Temp for other platforms ([#661](https://github.com/IceWhaleTech/CasaOS/issues/661))
## [0.3.7.1] 2022-11-04
### Fixed
- Fix memory leak issue ([#658](https://github.com/IceWhaleTech/CasaOS/issues/658)[#646](https://github.com/IceWhaleTech/CasaOS/issues/646))
- Solve the problem of local application import failure ([#490](https://github.com/IceWhaleTech/CasaOS/issues/490))
## [0.3.7] 2022-10-28
### Added
- [Storage] Disk merge (Beta), you can merge multiple disks into a single storage space (currently you need to enable this feature from the command line)
### Changed
- [Files] Changed the cache file storage location, now the file upload size is not limited by the system disk capacity.
- [Scripts] Updated installation and upgrade scripts to support more Debian-based Linux distributions.
- [Engineering] Refactored Local Storage into a standalone service as part of CasaOS modularization.
### Fixed
- [Apps] App list update mechanism improved, now you can see the latest apps in App Store immediately.
- [Storage] Fixed a lot of known issues
### Added
- [Storage] Disk merge (Beta), you can merge multiple disks into a single storage space (currently you need to enable this feature from the command line)
### Changed
- [Files] Changed the cache file storage location, now the file upload size is not limited by the system disk capacity.
- [Scripts] Updated installation and upgrade scripts to support more Debian-based Linux distributions.
- [Engineering] Refactored Local Storage into a standalone service as part of CasaOS modularization.
### Fixed
- [Apps] App list update mechanism improved, now you can see the latest apps in App Store immediately.
- [Storage] Fixed a lot of known issues
## [0.3.6] - 2022-09-06
### Added ### Added
- [System] Added power and temperature info to performance widget (Intel) - [System] Added power and temperature info to performance widget (Intel)

View File

@@ -114,19 +114,20 @@ Community Support
- Armbian 22.04 (✅ Tested) - Armbian 22.04 (✅ Tested)
- Alpine (🚧 Not Fully Tested Yet) - Alpine (🚧 Not Fully Tested Yet)
- OpenWrt (🚧 Not Fully Tested Yet) - OpenWrt (🚧 Not Fully Tested Yet)
- ArchLinux (🚧 Not Fully Tested Yet)
### Quick Setup CasaOS ### Quick Setup CasaOS
Freshly install a system from the list above and run this command: Freshly install a system from the list above and run this command:
```sh ```sh
wget -qO- https://get.casaos.io | bash wget -qO- https://get.casaos.io | sudo bash
``` ```
or or
```sh ```sh
curl -fsSL https://get.casaos.io | bash curl -fsSL https://get.casaos.io | sudo bash
``` ```
### Uninstall CasaOS ### Uninstall CasaOS
@@ -141,7 +142,7 @@ casaos-uninstall
Before v0.3.3 Before v0.3.3
```sh ```sh
curl -fsSL https://get.icewhale.io/casaos-uninstall.sh | bash curl -fsSL https://get.icewhale.io/casaos-uninstall.sh | sudo bash
``` ```
## Community ## Community
@@ -162,38 +163,8 @@ We believes that through community-driven collaborative innovation and open comm
CasaOS is a community-driven open source project and the people involved are CasaOS users. That means CasaOS will always need contributions from community members just like you! CasaOS is a community-driven open source project and the people involved are CasaOS users. That means CasaOS will always need contributions from community members just like you!
<details> - See <https://wiki.casaos.io/en/contribute> for ways of contribution to CasaOS
<summary><b>How can I get involved? 🧐</b></summary> - See <https://wiki.casaos.io/en/contribute/development> if you want to be involved in code contribution specificially
<p>
### Coding 💻 (WIP)
We are refining documentation that can be used for effective community collaboration. Feel free to start a discussion if you have a good idea.
### Helping Users 💬
If you have extensive knowledge of CasaOS and related areas. We highly encourage you to help others as much as you can in Discord and Discussions.
Discord: [https://discord.gg/knqAbbBbeX](https://discord.gg/knqAbbBbeX)
GitHub Discussions: [https://github.com/IceWhaleTech/CasaOS/discussions](https://github.com/IceWhaleTech/CasaOS/discussions)
### Helping with Translations 🌍 (WIP)
CasaOS officially supports English and Chinese. You are welcome to help make CasaOS available in more languages.
### Performing Alpha Testing ⚠️
Alpha testing is quality assurance testing that is engaged and driven by the community. It's a great way to get involved in contributing and experiencing the latest features before a new release.
The documentation is being refined and you can contact @JohnGuan via [Discord](https://discord.gg/knqAbbBbeX). Ask to join the #casaos-alpha channel.
### Writing Documentation 📖 (WIP)
Help make our documentation better by writing new content for the CasaOS Wiki, correcting existing material, or translating content into new languages.
</p>
</details>
## Credits ## Credits

View File

@@ -60,8 +60,6 @@ BUILD_PATH=$(dirname "${BASH_SOURCE[0]}")/../../..
SOURCE_ROOT=${BUILD_PATH}/sysroot SOURCE_ROOT=${BUILD_PATH}/sysroot
APP_NAME="casaos" APP_NAME="casaos"
APP_NAME_FORMAL="CasaOS"
#APP_NAME_FORMAL="casaos-alpha"
# check if migration is needed # check if migration is needed
SOURCE_BIN_PATH=${SOURCE_ROOT}/usr/bin SOURCE_BIN_PATH=${SOURCE_ROOT}/usr/bin
@@ -85,45 +83,6 @@ if [ "${NEED_MIGRATION}" = "false" ]; then
exit 0 exit 0
fi fi
MIGRATION_SERVICE_DIR=${1}
if [ -z "${MIGRATION_SERVICE_DIR}" ]; then
MIGRATION_SERVICE_DIR=${BUILD_PATH}/scripts/migration/service.d/${APP_NAME}
fi
MIGRATION_LIST_FILE=${MIGRATION_SERVICE_DIR}/migration.list
MIGRATION_PATH=()
CURRENT_VERSION_FOUND="false"
# a VERSION_PAIR looks like "v0.3.5 v0.3.6-alpha2"
#
# - "v0.3.5" is the current version installed on this host
# - "v0.3.6-alpha2" is the version of the migration tool from GitHub
while read -r VERSION_PAIR; do
if [ -z "${VERSION_PAIR}" ]; then
continue
fi
# obtain "v0.3.5" from "v0.3.5 v0.3.6-alpha2"
VER1=$(echo "${VERSION_PAIR}" | cut -d' ' -f1)
# obtain "v0.3.6-alpha2" from "v0.3.5 v0.3.6-alpha2"
VER2=$(echo "${VERSION_PAIR}" | cut -d' ' -f2)
if [ "${CURRENT_VERSION}" = "${VER1// /}" ] || [ "${CURRENT_VERSION}" = "LEGACY_WITHOUT_VERSION" ]; then
CURRENT_VERSION_FOUND="true"
fi
if [ "${CURRENT_VERSION_FOUND}" = "true" ]; then
MIGRATION_PATH+=("${VER2// /}")
fi
done < "${MIGRATION_LIST_FILE}"
if [ ${#MIGRATION_PATH[@]} -eq 0 ]; then
__warning "No migration path found from ${CURRENT_VERSION} to ${SOURCE_VERSION}"
exit 0
fi
ARCH="unknown" ARCH="unknown"
case $(uname -m) in case $(uname -m) in
@@ -141,22 +100,59 @@ case $(uname -m) in
;; ;;
esac esac
__info "ARCH: ${ARCH}"
MIGRATION_SERVICE_DIR=${1}
if [ -z "${MIGRATION_SERVICE_DIR}" ]; then
MIGRATION_SERVICE_DIR=${BUILD_PATH}/scripts/migration/service.d/${APP_NAME}
fi
MIGRATION_LIST_FILE=${MIGRATION_SERVICE_DIR}/migration.list
MIGRATION_PATH=()
CURRENT_VERSION_FOUND="false"
# a VERSION_PAIR looks like "v0.3.5 <url>"
#
# - "v0.3.5" is the current version installed on this host
# - "<url>" is the url of the migration tool
while read -r VERSION_PAIR; do
if [ -z "${VERSION_PAIR}" ]; then
continue
fi
# obtain "v0.3.5" from "v0.3.5 v0.3.6-alpha2"
VER1=$(echo "${VERSION_PAIR}" | cut -d' ' -f1)
# obtain "<url>" from "v0.3.5 <url>"
URL=$(eval echo "${VERSION_PAIR}" | cut -d' ' -f2)
if [ "${CURRENT_VERSION}" = "${VER1// /}" ] || [ "${CURRENT_VERSION}" = "LEGACY_WITHOUT_VERSION" ]; then
CURRENT_VERSION_FOUND="true"
fi
if [ "${CURRENT_VERSION_FOUND}" = "true" ]; then
MIGRATION_PATH+=("${URL// /}")
fi
done < "${MIGRATION_LIST_FILE}"
if [ ${#MIGRATION_PATH[@]} -eq 0 ]; then
__warning "No migration path found from ${CURRENT_VERSION} to ${SOURCE_VERSION}"
exit 0
fi
pushd "${MIGRATION_SERVICE_DIR}" pushd "${MIGRATION_SERVICE_DIR}"
{ for VER2 in "${MIGRATION_PATH[@]}"; do { for URL in "${MIGRATION_PATH[@]}"; do
MIGRATION_TOOL_FILE=$(basename "${URL}")
MIGRATION_TOOL_FILE=linux-"${ARCH}"-"${APP_NAME}"-migration-tool-"${VER2}".tar.gz
if [ -f "${MIGRATION_TOOL_FILE}" ]; then if [ -f "${MIGRATION_TOOL_FILE}" ]; then
__info "Migration tool ${MIGRATION_TOOL_FILE} exists. Skip downloading." __info "Migration tool ${MIGRATION_TOOL_FILE} exists. Skip downloading."
continue continue
fi fi
# MIGRATION_TOOL_URL=http://192.168.2.197:8000/v1/package/migration?type=release&name="${APP_NAME_FORMAL}"&version=${VER2}&arch=${ARCH} __info "Dowloading ${URL}..."
MIGRATION_TOOL_URL=https://github.com/IceWhaleTech/"${APP_NAME_FORMAL}"/releases/download/"${VER2}"/linux-"${ARCH}"-"${APP_NAME}"-migration-tool-"${VER2}".tar.gz curl -fsSL -o "${MIGRATION_TOOL_FILE}" -O "${URL}"
echo "Dowloading ${MIGRATION_TOOL_URL}..."
curl -sL -O "${MIGRATION_TOOL_URL}"
done done
} || { } || {
popd popd
@@ -164,8 +160,8 @@ pushd "${MIGRATION_SERVICE_DIR}"
} }
{ {
for VER2 in "${MIGRATION_PATH[@]}"; do for URL in "${MIGRATION_PATH[@]}"; do
MIGRATION_TOOL_FILE=linux-"${ARCH}"-"${APP_NAME}"-migration-tool-"${VER2}".tar.gz MIGRATION_TOOL_FILE=$(basename "${URL}")
__info "Extracting ${MIGRATION_TOOL_FILE}..." __info "Extracting ${MIGRATION_TOOL_FILE}..."
tar zxvf "${MIGRATION_TOOL_FILE}" || __error "Failed to extract ${MIGRATION_TOOL_FILE}" tar zxvf "${MIGRATION_TOOL_FILE}" || __error "Failed to extract ${MIGRATION_TOOL_FILE}"

View File

@@ -1,4 +1,3 @@
LEGACY_WITHOUT_VERSION v0.3.6 LEGACY_WITHOUT_VERSION https://github.com/IceWhaleTech/CasaOS/releases/download/v0.3.6/linux-${ARCH}-casaos-migration-tool-v0.3.6.tar.gz
v0.3.5 v0.3.6 v0.3.5 https://github.com/IceWhaleTech/CasaOS/releases/download/v0.3.6/linux-${ARCH}-casaos-migration-tool-v0.3.6.tar.gz
v0.3.5.1 v0.3.6 v0.3.5.1 https://github.com/IceWhaleTech/CasaOS/releases/download/v0.3.6/linux-${ARCH}-casaos-migration-tool-v0.3.6.tar.gz
v0.3.6 v0.3.7

View File

@@ -45,7 +45,7 @@ SETUP_SCRIPT_FILEPATH="${SETUP_SCRIPT_DIRECTORY}/${SETUP_SCRIPT_FILENAME}"
{ {
echo "🟩 Running ${SETUP_SCRIPT_FILENAME}..." echo "🟩 Running ${SETUP_SCRIPT_FILENAME}..."
$SHELL "${SETUP_SCRIPT_FILEPATH}" "${BUILD_PATH}" $BASH "${SETUP_SCRIPT_FILEPATH}" "${BUILD_PATH}"
} || { } || {
echo "🟥 ${SETUP_SCRIPT_FILENAME} failed." echo "🟥 ${SETUP_SCRIPT_FILENAME} failed."
exit 1 exit 1

View File

@@ -0,0 +1,40 @@
#!/bin/bash
###
# @Author: LinkLeong link@icewhale.org
# @Date: 2022-08-25 11:41:22
# @LastEditors: LinkLeong
# @LastEditTime: 2022-08-31 17:54:17
# @FilePath: /CasaOS/build/scripts/setup/service.d/casaos/debian/setup-casaos.sh
# @Description:
# @Website: https://www.casaos.io
# Copyright (c) 2022 by icewhale, All Rights Reserved.
###
set -e
APP_NAME="casaos"
# copy config files
CONF_PATH=/etc/casaos
OLD_CONF_PATH=/etc/casaos.conf
CONF_FILE=${CONF_PATH}/${APP_NAME}.conf
CONF_FILE_SAMPLE=${CONF_PATH}/${APP_NAME}.conf.sample
if [ -f "${OLD_CONF_PATH}" ]; then
echo "copy old conf"
cp "${OLD_CONF_PATH}" "${CONF_FILE}"
fi
if [ ! -f "${CONF_FILE}" ]; then
echo "Initializing config file..."
cp -v "${CONF_FILE_SAMPLE}" "${CONF_FILE}"
fi
rm -rf /etc/systemd/system/casaos.service # remove old service file
systemctl daemon-reload
# enable service (without starting)
echo "Enabling service..."
systemctl enable --force --no-ask-password "${APP_NAME}.service"

View File

@@ -31,15 +31,10 @@ if [ ! -f "${CONF_FILE}" ]; then
cp -v "${CONF_FILE_SAMPLE}" "${CONF_FILE}" cp -v "${CONF_FILE_SAMPLE}" "${CONF_FILE}"
fi fi
if systemctl is-active "${APP_NAME}.service" &>/dev/null ;then rm -rf /etc/systemd/system/casaos.service # remove old service file
echo "server started"
else
# enable and start service
systemctl daemon-reload
echo "Enabling service..." systemctl daemon-reload
systemctl enable --force --no-ask-password "${APP_NAME}.service"
echo "Starting service..." # enable service (without starting)
systemctl start --force --no-ask-password "${APP_NAME}.service" echo "Enabling service..."
fi systemctl enable --force --no-ask-password "${APP_NAME}.service"

View File

@@ -0,0 +1,56 @@
#!/bin/bash
###
# @Author: LinkLeong link@icewhale.org
# @Date: 2022-11-15 15:51:44
# @LastEditors: LinkLeong
# @LastEditTime: 2022-11-15 15:53:37
# @FilePath: /CasaOS/build/sysroot/usr/share/casaos/cleanup/script.d/03-cleanup-casaos.sh
# @Description:
# @Website: https://www.casaos.io
# Copyright (c) 2022 by icewhale, All Rights Reserved.
###
set -e
readonly APP_NAME_SHORT=casaos
__get_setup_script_directory_by_os_release() {
pushd "$(dirname "${BASH_SOURCE[0]}")/../service.d/${APP_NAME_SHORT}" &>/dev/null
{
# shellcheck source=/dev/null
{
source /etc/os-release
{
pushd "${ID}"/"${VERSION_CODENAME}" &>/dev/null
} || {
pushd "${ID}" &>/dev/null
} || {
pushd "${ID_LIKE}" &>/dev/null
} || {
echo "Unsupported OS: ${ID} ${VERSION_CODENAME} (${ID_LIKE})"
exit 1
}
pwd
popd &>/dev/null
} || {
echo "Unsupported OS: unknown"
exit 1
}
}
popd &>/dev/null
}
SETUP_SCRIPT_DIRECTORY=$(__get_setup_script_directory_by_os_release)
readonly SETUP_SCRIPT_DIRECTORY
readonly SETUP_SCRIPT_FILENAME="cleanup-${APP_NAME_SHORT}.sh"
readonly SETUP_SCRIPT_FILEPATH="${SETUP_SCRIPT_DIRECTORY}/${SETUP_SCRIPT_FILENAME}"
echo "🟩 Running ${SETUP_SCRIPT_FILENAME}..."
$SHELL "${SETUP_SCRIPT_FILEPATH}" "${BUILD_PATH}"

View File

@@ -0,0 +1,204 @@
#!/bin/bash
set -e
readonly CASA_SERVICES=(
"casaos.service"
"devmon@devmon.service"
)
readonly CASA_EXEC=casaos
readonly CASA_CONF=/etc/casaos/casaos.conf
readonly CASA_URL=/var/run/casaos/casaos.url
readonly CASA_SERVICE_USR=/usr/lib/systemd/system/casaos.service
readonly CASA_SERVICE_LIB=/lib/systemd/system/casaos.service
readonly CASA_SERVICE_ETC=/etc/systemd/system/casaos.service
# Old Casa Files
readonly CASA_PATH=/casaOS
readonly CASA_CONF_PATH_OLD=/etc/casaos.conf
readonly aCOLOUR=(
'\e[38;5;154m' # green | Lines, bullets and separators
'\e[1m' # Bold white | Main descriptions
'\e[90m' # Grey | Credits
'\e[91m' # Red | Update notifications Alert
'\e[33m' # Yellow | Emphasis
)
Show() {
# OK
if (($1 == 0)); then
echo -e "${aCOLOUR[2]}[$COLOUR_RESET${aCOLOUR[0]} OK $COLOUR_RESET${aCOLOUR[2]}]$COLOUR_RESET $2"
# FAILED
elif (($1 == 1)); then
echo -e "${aCOLOUR[2]}[$COLOUR_RESET${aCOLOUR[3]}FAILED$COLOUR_RESET${aCOLOUR[2]}]$COLOUR_RESET $2"
# INFO
elif (($1 == 2)); then
echo -e "${aCOLOUR[2]}[$COLOUR_RESET${aCOLOUR[0]} INFO $COLOUR_RESET${aCOLOUR[2]}]$COLOUR_RESET $2"
# NOTICE
elif (($1 == 3)); then
echo -e "${aCOLOUR[2]}[$COLOUR_RESET${aCOLOUR[4]}NOTICE$COLOUR_RESET${aCOLOUR[2]}]$COLOUR_RESET $2"
fi
}
Warn() {
echo -e "${aCOLOUR[3]}$1$COLOUR_RESET"
}
trap 'onCtrlC' INT
onCtrlC() {
echo -e "${COLOUR_RESET}"
exit 1
}
Detecting_CasaOS() {
if [[ ! -x "$(command -v ${CASA_EXEC})" ]]; then
Show 2 "CasaOS is not detected, exit the script."
exit 1
else
Show 0 "This script will delete the containers you no longer use, and the CasaOS configuration files."
fi
}
Uninstall_Container() {
if [[ ${UNINSTALL_ALL_CONTAINER} == true && "$(docker ps -aq)" != "" ]]; then
Show 2 "Start deleting containers."
docker stop "$(docker ps -aq)" || Show 1 "Failed to stop all containers."
docker rm "$(docker ps -aq)" || Show 1 "Failed to delete all containers."
fi
}
Remove_Images() {
if [[ ${REMOVE_IMAGES} == "all" && "$(docker images -q)" != "" ]]; then
Show 2 "Start deleting all images."
docker rmi "$(docker images -q)" || Show 1 "Failed to delete all images."
elif [[ ${REMOVE_IMAGES} == "unuse" && "$(docker images -q)" != "" ]]; then
Show 2 "Start deleting unuse images."
docker image prune -af || Show 1 "Failed to delete unuse images."
fi
}
Uninstall_Casaos() {
for SERVICE in "${CASA_SERVICES[@]}"; do
Show 2 "Stopping ${SERVICE}..."
systemctl disable --now "${SERVICE}" || Show 3 "Failed to disable ${SERVICE}"
done
# Remove Service file
if [[ -f ${CASA_SERVICE_USR} ]]; then
rm -rvf ${CASA_SERVICE_USR}
fi
if [[ -f ${CASA_SERVICE_LIB} ]]; then
rm -rvf ${CASA_SERVICE_LIB}
fi
if [[ -f ${CASA_SERVICE_ETC} ]]; then
rm -rvf ${CASA_SERVICE_ETC}
fi
# Old Casa Files
if [[ -d ${CASA_PATH} ]]; then
rm -rvf ${CASA_PATH} || Show 1 "Failed to delete legacy CasaOS files."
fi
if [[ -f ${CASA_CONF_PATH_OLD} ]]; then
rm -rvf ${CASA_CONF_PATH_OLD}
fi
# New Casa Files
if [[ ${REMOVE_APP_DATA} = true ]]; then
rm -rvf /DATA/AppData || Show 1 "Failed to delete AppData."
fi
rm -rvf "$(which ${CASA_EXEC})" || Show 3 "Failed to remove ${CASA_EXEC}"
rm -rvf ${CASA_CONF} || Show 3 "Failed to remove ${CASA_CONF}"
rm -rvf ${CASA_URL} || Show 3 "Failed to remove ${CASA_URL}"
rm -rvf /var/lib/casaos/app_category.json
rm -rvf /var/lib/casaos/app_list.json
rm -rvf /var/lib/casaos/docker_root
}
Detecting_CasaOS
while true; do
echo -n -e " ${aCOLOUR[4]}Do you want delete all containers? Y/n :${COLOUR_RESET}"
read -r input
case $input in
[yY][eE][sS] | [yY])
UNINSTALL_ALL_CONTAINER=true
break
;;
[nN][oO] | [nN])
UNINSTALL_ALL_CONTAINER=false
break
;;
*)
Warn " Invalid input..."
;;
esac
done
if [[ ${UNINSTALL_ALL_CONTAINER} == true ]]; then
while true; do
echo -n -e " ${aCOLOUR[4]}Do you want delete all images? Y/n :${COLOUR_RESET}"
read -r input
case $input in
[yY][eE][sS] | [yY])
REMOVE_IMAGES="all"
break
;;
[nN][oO] | [nN])
REMOVE_IMAGES="none"
break
;;
*)
Warn " Invalid input..."
;;
esac
done
while true; do
echo -n -e " ${aCOLOUR[4]}Do you want delete all AppData of CasaOS? Y/n :${COLOUR_RESET}"
read -r input
case $input in
[yY][eE][sS] | [yY])
REMOVE_APP_DATA=true
break
;;
[nN][oO] | [nN])
REMOVE_APP_DATA=false
break
;;
*)
Warn " Invalid input..."
;;
esac
done
else
while true; do
echo -n -e " ${aCOLOUR[4]}Do you want to delete all images that are not used by the container? Y/n :${COLOUR_RESET}"
read -r input
case $input in
[yY][eE][sS] | [yY])
REMOVE_IMAGES="unuse"
break
;;
[nN][oO] | [nN])
REMOVE_IMAGES="none"
break
;;
*)
Warn " Invalid input..."
;;
esac
done
fi
Uninstall_Container
Remove_Images
Uninstall_Casaos

View File

@@ -0,0 +1 @@
../cleanup-casaos.sh

View File

@@ -0,0 +1,204 @@
#!/bin/bash
set -e
readonly CASA_SERVICES=(
"casaos.service"
"devmon@devmon.service"
)
readonly CASA_EXEC=casaos
readonly CASA_CONF=/etc/casaos/casaos.conf
readonly CASA_URL=/var/run/casaos/casaos.url
readonly CASA_SERVICE_USR=/usr/lib/systemd/system/casaos.service
readonly CASA_SERVICE_LIB=/lib/systemd/system/casaos.service
readonly CASA_SERVICE_ETC=/etc/systemd/system/casaos.service
# Old Casa Files
readonly CASA_PATH=/casaOS
readonly CASA_CONF_PATH_OLD=/etc/casaos.conf
readonly aCOLOUR=(
'\e[38;5;154m' # green | Lines, bullets and separators
'\e[1m' # Bold white | Main descriptions
'\e[90m' # Grey | Credits
'\e[91m' # Red | Update notifications Alert
'\e[33m' # Yellow | Emphasis
)
Show() {
# OK
if (($1 == 0)); then
echo -e "${aCOLOUR[2]}[$COLOUR_RESET${aCOLOUR[0]} OK $COLOUR_RESET${aCOLOUR[2]}]$COLOUR_RESET $2"
# FAILED
elif (($1 == 1)); then
echo -e "${aCOLOUR[2]}[$COLOUR_RESET${aCOLOUR[3]}FAILED$COLOUR_RESET${aCOLOUR[2]}]$COLOUR_RESET $2"
# INFO
elif (($1 == 2)); then
echo -e "${aCOLOUR[2]}[$COLOUR_RESET${aCOLOUR[0]} INFO $COLOUR_RESET${aCOLOUR[2]}]$COLOUR_RESET $2"
# NOTICE
elif (($1 == 3)); then
echo -e "${aCOLOUR[2]}[$COLOUR_RESET${aCOLOUR[4]}NOTICE$COLOUR_RESET${aCOLOUR[2]}]$COLOUR_RESET $2"
fi
}
Warn() {
echo -e "${aCOLOUR[3]}$1$COLOUR_RESET"
}
trap 'onCtrlC' INT
onCtrlC() {
echo -e "${COLOUR_RESET}"
exit 1
}
Detecting_CasaOS() {
if [[ ! -x "$(command -v ${CASA_EXEC})" ]]; then
Show 2 "CasaOS is not detected, exit the script."
exit 1
else
Show 0 "This script will delete the containers you no longer use, and the CasaOS configuration files."
fi
}
Uninstall_Container() {
if [[ ${UNINSTALL_ALL_CONTAINER} == true && "$(docker ps -aq)" != "" ]]; then
Show 2 "Start deleting containers."
docker stop "$(docker ps -aq)" || Show 1 "Failed to stop all containers."
docker rm "$(docker ps -aq)" || Show 1 "Failed to delete all containers."
fi
}
Remove_Images() {
if [[ ${REMOVE_IMAGES} == "all" && "$(docker images -q)" != "" ]]; then
Show 2 "Start deleting all images."
docker rmi "$(docker images -q)" || Show 1 "Failed to delete all images."
elif [[ ${REMOVE_IMAGES} == "unuse" && "$(docker images -q)" != "" ]]; then
Show 2 "Start deleting unuse images."
docker image prune -af || Show 1 "Failed to delete unuse images."
fi
}
Uninstall_Casaos() {
for SERVICE in "${CASA_SERVICES[@]}"; do
Show 2 "Stopping ${SERVICE}..."
systemctl disable --now "${SERVICE}" || Show 3 "Failed to disable ${SERVICE}"
done
# Remove Service file
if [[ -f ${CASA_SERVICE_USR} ]]; then
rm -rvf ${CASA_SERVICE_USR}
fi
if [[ -f ${CASA_SERVICE_LIB} ]]; then
rm -rvf ${CASA_SERVICE_LIB}
fi
if [[ -f ${CASA_SERVICE_ETC} ]]; then
rm -rvf ${CASA_SERVICE_ETC}
fi
# Old Casa Files
if [[ -d ${CASA_PATH} ]]; then
rm -rvf ${CASA_PATH} || Show 1 "Failed to delete legacy CasaOS files."
fi
if [[ -f ${CASA_CONF_PATH_OLD} ]]; then
rm -rvf ${CASA_CONF_PATH_OLD}
fi
# New Casa Files
if [[ ${REMOVE_APP_DATA} = true ]]; then
rm -rvf /DATA/AppData || Show 1 "Failed to delete AppData."
fi
rm -rvf "$(which ${CASA_EXEC})" || Show 3 "Failed to remove ${CASA_EXEC}"
rm -rvf ${CASA_CONF} || Show 3 "Failed to remove ${CASA_CONF}"
rm -rvf ${CASA_URL} || Show 3 "Failed to remove ${CASA_URL}"
rm -rvf /var/lib/casaos/app_category.json
rm -rvf /var/lib/casaos/app_list.json
rm -rvf /var/lib/casaos/docker_root
}
Detecting_CasaOS
while true; do
echo -n -e " ${aCOLOUR[4]}Do you want delete all containers? Y/n :${COLOUR_RESET}"
read -r input
case $input in
[yY][eE][sS] | [yY])
UNINSTALL_ALL_CONTAINER=true
break
;;
[nN][oO] | [nN])
UNINSTALL_ALL_CONTAINER=false
break
;;
*)
Warn " Invalid input..."
;;
esac
done
if [[ ${UNINSTALL_ALL_CONTAINER} == true ]]; then
while true; do
echo -n -e " ${aCOLOUR[4]}Do you want delete all images? Y/n :${COLOUR_RESET}"
read -r input
case $input in
[yY][eE][sS] | [yY])
REMOVE_IMAGES="all"
break
;;
[nN][oO] | [nN])
REMOVE_IMAGES="none"
break
;;
*)
Warn " Invalid input..."
;;
esac
done
while true; do
echo -n -e " ${aCOLOUR[4]}Do you want delete all AppData of CasaOS? Y/n :${COLOUR_RESET}"
read -r input
case $input in
[yY][eE][sS] | [yY])
REMOVE_APP_DATA=true
break
;;
[nN][oO] | [nN])
REMOVE_APP_DATA=false
break
;;
*)
Warn " Invalid input..."
;;
esac
done
else
while true; do
echo -n -e " ${aCOLOUR[4]}Do you want to delete all images that are not used by the container? Y/n :${COLOUR_RESET}"
read -r input
case $input in
[yY][eE][sS] | [yY])
REMOVE_IMAGES="unuse"
break
;;
[nN][oO] | [nN])
REMOVE_IMAGES="none"
break
;;
*)
Warn " Invalid input..."
;;
esac
done
fi
Uninstall_Container
Remove_Images
Uninstall_Casaos

View File

@@ -0,0 +1 @@
../debian/cleanup-casaos.sh

View File

@@ -0,0 +1 @@
../../debian/bullseye/cleanup-casaos.sh

View File

@@ -9,4 +9,4 @@
### ###
curl -fsSL https://raw.githubusercontent.com/LinkLeong/casaos-alpha/main/new.update.sh | bash curl -fsSL https://raw.githubusercontent.com/IceWhaleTech/get/main/update.sh | bash

View File

@@ -83,8 +83,7 @@ func main() {
} }
migrationTools := []interfaces.MigrationTool{ migrationTools := []interfaces.MigrationTool{
NewMigrationToolFor_035(), // nothing to migrate from last version
NewMigrationToolFor_036(),
} }
var selectedMigrationTool interfaces.MigrationTool var selectedMigrationTool interfaces.MigrationTool
@@ -115,8 +114,7 @@ func main() {
panic(err) panic(err)
} }
selectedMigrationTool.PostMigrate() if err := selectedMigrationTool.PostMigrate(); err != nil {
_logger.Info("casaos migration ok") _logger.Error("Migration succeeded, but post-migration failed: %s", err)
// panic(err) }
} }

View File

@@ -1,182 +0,0 @@
/*
* @Author: LinkLeong link@icewhale.org
* @Date: 2022-08-24 17:36:00
* @LastEditors: LinkLeong
* @LastEditTime: 2022-09-05 11:24:27
* @FilePath: /CasaOS/cmd/migration-tool/migration-034-035.go
* @Description:
* @Website: https://www.casaos.io
* Copyright (c) 2022 by icewhale, All Rights Reserved.
*/
package main
import (
"io"
"io/ioutil"
"os"
"path"
"strings"
interfaces "github.com/IceWhaleTech/CasaOS-Common"
"github.com/IceWhaleTech/CasaOS-Common/utils/version"
"github.com/IceWhaleTech/CasaOS/pkg/config"
"github.com/IceWhaleTech/CasaOS/pkg/utils/command"
"github.com/IceWhaleTech/CasaOS/pkg/utils/file"
"github.com/IceWhaleTech/CasaOS/service"
)
type migrationTool036 struct{}
func (u *migrationTool036) IsMigrationNeeded() (bool, error) {
majorVersion, minorVersion, patchVersion, err := version.DetectLegacyVersion()
if err != nil {
if err == version.ErrLegacyVersionNotFound {
return false, nil
}
return false, err
}
if majorVersion > 0 {
return false, nil
}
if minorVersion > 3 {
return false, nil
}
if minorVersion == 3 && patchVersion > 5 {
return false, nil
}
_logger.Info("Migration is needed for a CasaOS version 0.3.5 and older...")
return true, nil
}
func (u *migrationTool036) PreMigrate() error {
return nil
}
func (u *migrationTool036) Migrate() error {
if service.MyService.System().GetSysInfo().KernelArch == "aarch64" && config.ServerInfo.USBAutoMount != "True" && strings.Contains(service.MyService.System().GetDeviceTree(), "Raspberry Pi") {
service.MyService.System().UpdateUSBAutoMount("False")
service.MyService.System().ExecUSBAutoMountShell("False")
}
newAPIUrl := "https://api.casaos.io/casaos-api"
if config.ServerInfo.ServerApi == "https://api.casaos.zimaboard.com" {
config.ServerInfo.ServerApi = newAPIUrl
config.Cfg.Section("server").Key("ServerApi").SetValue(newAPIUrl)
config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath)
}
command.OnlyExec("curl -fsSL https://raw.githubusercontent.com/IceWhaleTech/get/main/assist.sh | bash")
if !file.CheckNotExist("/casaOS") {
command.OnlyExec("source /casaOS/server/shell/update.sh ;")
command.OnlyExec("source " + config.AppInfo.ShellPath + "/delete-old-service.sh ;")
}
service.MyService.App().ImportApplications(true)
src := "/casaOS/server/conf/conf.ini"
if file.Exists(src) {
dst := "/etc/casaos/casaos.conf"
source, err := os.Open(src)
if err != nil {
return err
}
defer source.Close()
destination, err := os.Create(dst)
if err != nil {
return err
}
defer destination.Close()
_, err = io.Copy(destination, source)
if err != nil {
return err
}
}
if file.Exists("/casaOS/server/db") {
var fds []os.FileInfo
var err error
to := "/var/lib/casaos/db"
file.IsNotExistMkDir(to)
from := "/casaOS/server/db"
if fds, err = ioutil.ReadDir(from); err != nil {
return err
}
for _, fd := range fds {
srcfp := path.Join(from, fd.Name())
dstfp := path.Join(to, fd.Name())
source, err := os.Open(srcfp)
if err != nil {
return err
}
defer source.Close()
destination, err := os.Create(dstfp)
if err != nil {
return err
}
defer destination.Close()
_, err = io.Copy(destination, source)
if err != nil {
return err
}
}
}
if file.Exists("/casaOS/server/conf") {
var fds []os.FileInfo
var err error
to := "/var/lib/casaos/conf"
file.IsNotExistMkDir(to)
from := "/casaOS/server/conf"
if fds, err = ioutil.ReadDir(from); err != nil {
return err
}
for _, fd := range fds {
fExt := path.Ext(fd.Name())
if fExt != ".json" {
continue
}
srcfp := path.Join(from, fd.Name())
dstfp := path.Join(to, fd.Name())
source, err := os.Open(srcfp)
if err != nil {
return err
}
defer source.Close()
destination, err := os.Create(dstfp)
if err != nil {
return err
}
defer destination.Close()
_, err = io.Copy(destination, source)
if err != nil {
return err
}
}
}
_logger.Info("update done")
return nil
}
func (u *migrationTool036) PostMigrate() error {
return nil
}
func NewMigrationToolFor_035() interfaces.MigrationTool {
return &migrationTool{}
}

View File

@@ -11,18 +11,13 @@
package main package main
import ( import (
"strings"
interfaces "github.com/IceWhaleTech/CasaOS-Common" interfaces "github.com/IceWhaleTech/CasaOS-Common"
"github.com/IceWhaleTech/CasaOS-Common/utils/version" "github.com/IceWhaleTech/CasaOS-Common/utils/version"
"github.com/IceWhaleTech/CasaOS/pkg/config"
"github.com/IceWhaleTech/CasaOS/service"
) )
type migrationTool struct{} type migrationTool struct{}
func (u *migrationTool) IsMigrationNeeded() (bool, error) { func (u *migrationTool) IsMigrationNeeded() (bool, error) {
majorVersion, minorVersion, patchVersion, err := version.DetectLegacyVersion() majorVersion, minorVersion, patchVersion, err := version.DetectLegacyVersion()
if err != nil { if err != nil {
if err == version.ErrLegacyVersionNotFound { if err == version.ErrLegacyVersionNotFound {
@@ -46,22 +41,13 @@ func (u *migrationTool) IsMigrationNeeded() (bool, error) {
_logger.Info("Migration is needed for a CasaOS version 0.3.5 and older...") _logger.Info("Migration is needed for a CasaOS version 0.3.5 and older...")
return true, nil return true, nil
} }
func (u *migrationTool) PreMigrate() error { func (u *migrationTool) PreMigrate() error {
return nil return nil
} }
func (u *migrationTool) Migrate() error { func (u *migrationTool) Migrate() error {
if service.MyService.System().GetSysInfo().KernelArch == "aarch64" && config.ServerInfo.USBAutoMount != "True" && strings.Contains(service.MyService.System().GetDeviceTree(), "Raspberry Pi") {
service.MyService.System().UpdateUSBAutoMount("False")
service.MyService.System().ExecUSBAutoMountShell("False")
}
_logger.Info("update done")
return nil return nil
} }

View File

@@ -1,89 +0,0 @@
package common
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"net/http"
"os"
"path/filepath"
"strings"
)
const (
CasaOSURLFilename = "casaos.url"
APICasaOSNotify = "/v1/notify"
)
type NotifyService interface {
SendNotify(path string, message map[string]interface{}) error
SendSystemStatusNotify(message map[string]interface{}) error
}
type notifyService struct {
address string
}
func (n *notifyService) SendNotify(path string, message map[string]interface{}) error {
url := strings.TrimSuffix(n.address, "/") + APICasaOSNotify + "/" + path
body, err := json.Marshal(message)
if err != nil {
return err
}
response, err := http.Post(url, "application/json", bytes.NewBuffer(body))
if err != nil {
return err
}
if response.StatusCode != http.StatusOK {
return errors.New("failed to send notify (status code: " + fmt.Sprint(response.StatusCode) + ")")
}
return nil
}
// disk: "sys_disk":{"size":56866869248,"avail":5855485952,"health":true,"used":48099700736}
// usb: "sys_usb":[{"name": "sdc","size": 7747397632,"model": "DataTraveler_2.0","avail": 7714418688,"children": null}]
func (n *notifyService) SendSystemStatusNotify(message map[string]interface{}) error {
url := strings.TrimSuffix(n.address, "/") + APICasaOSNotify + "/system_status"
fmt.Println(url)
body, err := json.Marshal(message)
if err != nil {
return err
}
response, err := http.Post(url, "application/json", bytes.NewBuffer(body))
if err != nil {
return err
}
if response.StatusCode != http.StatusOK {
return errors.New("failed to send notify (status code: " + fmt.Sprint(response.StatusCode) + ")")
}
return nil
}
func NewNotifyService(runtimePath string) (NotifyService, error) {
casaosAddressFile := filepath.Join(runtimePath, CasaOSURLFilename)
buf, err := os.ReadFile(casaosAddressFile)
if err != nil {
return nil, err
}
address := string(buf)
response, err := http.Get(address + "/ping")
if err != nil {
return nil, err
}
if response.StatusCode != 200 {
return nil, errors.New("failed to ping casaos service")
}
return &notifyService{
address: address,
}, nil
}

View File

@@ -1,29 +0,0 @@
package common
import "testing"
func TestSendNotify(t *testing.T) {
notify, err := NewNotifyService("/var/run/casaos")
if err != nil {
t.Fatal(err)
}
err = notify.SendNotify("test", map[string]interface{}{
"test": "test",
})
if err != nil {
t.Fatal(err)
}
}
func TestSendSystemStatusNotify(t *testing.T) {
notify, err := NewNotifyService("/var/run/casaos")
if err != nil {
t.Fatal(err)
}
err = notify.SendSystemStatusNotify(map[string]interface{}{
"sys_usb": `[{"name": "sdc","size": 7747397632,"model": "DataTraveler_2.0","avail": 7714418688,"children": null}]`,
})
if err != nil {
t.Fatal(err)
}
}

View File

@@ -1,78 +0,0 @@
package common
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"net/http"
"os"
"path/filepath"
"strings"
)
const (
APICasaOSShare = "/v1/samba/shares"
)
type ShareService interface {
DeleteShare(id string) error
}
type shareService struct {
address string
}
func (n *shareService) DeleteShare(id string) error {
url := strings.TrimSuffix(n.address, "/") + APICasaOSShare + "/" + id
fmt.Println(url)
message := "{}"
body, err := json.Marshal(message)
if err != nil {
return err
}
client := &http.Client{}
// Create request
req, err := http.NewRequest("DELETE", url, bytes.NewBuffer(body))
if err != nil {
return err
}
// Fetch Request
response, err := client.Do(req)
if err != nil {
return err
}
defer response.Body.Close()
if response.StatusCode != http.StatusOK {
return errors.New("failed to send share (status code: " + fmt.Sprint(response.StatusCode) + ")")
}
return nil
}
func NewShareService(runtimePath string) (ShareService, error) {
casaosAddressFile := filepath.Join(runtimePath, CasaOSURLFilename)
buf, err := os.ReadFile(casaosAddressFile)
if err != nil {
return nil, err
}
address := string(buf)
response, err := http.Get(address + "/ping")
if err != nil {
return nil, err
}
if response.StatusCode != 200 {
return nil, errors.New("failed to ping casaos service")
}
return &shareService{
address: address,
}, nil
}

View File

@@ -1,14 +0,0 @@
package common
import "testing"
func TestDeleteShare(t *testing.T) {
share, err := NewShareService("/var/run/casaos")
if err != nil {
t.Fatal(err)
}
err = share.DeleteShare("1")
if err != nil {
t.Fatal(err)
}
}

22
go.mod
View File

@@ -4,42 +4,31 @@ go 1.16
require ( require (
github.com/Curtis-Milo/nat-type-identifier-go v0.0.0-20220215191915-18d42168c63d github.com/Curtis-Milo/nat-type-identifier-go v0.0.0-20220215191915-18d42168c63d
github.com/IceWhaleTech/CasaOS-Common v0.0.0-20220901034123-ca130f6b5ce9 github.com/IceWhaleTech/CasaOS-Common v0.4.0-alpha1
github.com/IceWhaleTech/CasaOS-Gateway v0.3.6 github.com/IceWhaleTech/CasaOS-Gateway v0.3.6
github.com/Microsoft/go-winio v0.5.0 // indirect
github.com/ambelovsky/go-structs v1.1.0 // indirect github.com/ambelovsky/go-structs v1.1.0 // indirect
github.com/ambelovsky/gosf v0.0.0-20201109201340-237aea4d6109 github.com/ambelovsky/gosf v0.0.0-20201109201340-237aea4d6109
github.com/ambelovsky/gosf-socketio v0.0.0-20201109193639-add9d32f8b19 // indirect github.com/ambelovsky/gosf-socketio v0.0.0-20201109193639-add9d32f8b19 // indirect
github.com/containerd/containerd v1.5.7 // indirect
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e
github.com/disintegration/imaging v1.6.2 github.com/disintegration/imaging v1.6.2
github.com/docker/distribution v2.8.0+incompatible // indirect
github.com/docker/docker v20.10.7+incompatible
github.com/docker/go-connections v0.4.0
github.com/dsoprea/go-exif/v3 v3.0.0-20210625224831-a6301f85c82b github.com/dsoprea/go-exif/v3 v3.0.0-20210625224831-a6301f85c82b
github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd // indirect github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd // indirect
github.com/gin-contrib/gzip v0.0.6 github.com/gin-contrib/gzip v0.0.6
github.com/gin-gonic/gin v1.8.1 github.com/gin-gonic/gin v1.8.1
github.com/glebarez/sqlite v1.5.0
github.com/go-ini/ini v1.62.0 github.com/go-ini/ini v1.62.0
github.com/golang/mock v1.6.0 github.com/golang/mock v1.6.0
github.com/gomodule/redigo v1.8.5 github.com/gomodule/redigo v1.8.5
github.com/google/go-github/v36 v36.0.0 github.com/google/go-github/v36 v36.0.0
github.com/googollee/go-socket.io v1.6.2 github.com/googollee/go-socket.io v1.6.2
github.com/gorilla/mux v1.8.0 // indirect github.com/gorilla/websocket v1.5.0
github.com/gorilla/websocket v1.4.2
github.com/hirochachacha/go-smb2 v1.1.0 github.com/hirochachacha/go-smb2 v1.1.0
github.com/jinzhu/copier v0.3.2
github.com/lucas-clemente/quic-go v0.25.0 github.com/lucas-clemente/quic-go v0.25.0
github.com/mholt/archiver/v3 v3.5.1 github.com/mholt/archiver/v3 v3.5.1
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.2 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/pkg/errors v0.9.1
github.com/robfig/cron v1.2.0 github.com/robfig/cron v1.2.0
github.com/satori/go.uuid v1.2.0 github.com/satori/go.uuid v1.2.0
github.com/shirou/gopsutil/v3 v3.22.7 github.com/shirou/gopsutil/v3 v3.22.7
github.com/sirupsen/logrus v1.8.1
github.com/smartystreets/assertions v1.2.0 // indirect github.com/smartystreets/assertions v1.2.0 // indirect
github.com/smartystreets/goconvey v1.6.4 // indirect github.com/smartystreets/goconvey v1.6.4 // indirect
github.com/stretchr/testify v1.8.0 github.com/stretchr/testify v1.8.0
@@ -48,9 +37,6 @@ require (
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4
golang.org/x/mod v0.5.0 // indirect golang.org/x/mod v0.5.0 // indirect
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
golang.org/x/tools v0.1.7 // indirect golang.org/x/tools v0.1.7 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0 gorm.io/gorm v1.24.0
gorm.io/driver/sqlite v1.2.6
gorm.io/gorm v1.22.5
) )

576
go.sum

File diff suppressed because it is too large Load Diff

31
main.go
View File

@@ -8,13 +8,13 @@ import (
"path/filepath" "path/filepath"
"time" "time"
"github.com/IceWhaleTech/CasaOS-Gateway/common" "github.com/IceWhaleTech/CasaOS-Common/model"
"github.com/IceWhaleTech/CasaOS-Common/utils/logger"
"github.com/IceWhaleTech/CasaOS/model/notify" "github.com/IceWhaleTech/CasaOS/model/notify"
"github.com/IceWhaleTech/CasaOS/pkg/cache" "github.com/IceWhaleTech/CasaOS/pkg/cache"
"github.com/IceWhaleTech/CasaOS/pkg/config" "github.com/IceWhaleTech/CasaOS/pkg/config"
"github.com/IceWhaleTech/CasaOS/pkg/sqlite" "github.com/IceWhaleTech/CasaOS/pkg/sqlite"
"github.com/IceWhaleTech/CasaOS/pkg/utils/file" "github.com/IceWhaleTech/CasaOS/pkg/utils/file"
"github.com/IceWhaleTech/CasaOS/pkg/utils/loger"
"github.com/IceWhaleTech/CasaOS/route" "github.com/IceWhaleTech/CasaOS/route"
"github.com/IceWhaleTech/CasaOS/service" "github.com/IceWhaleTech/CasaOS/service"
"github.com/IceWhaleTech/CasaOS/types" "github.com/IceWhaleTech/CasaOS/types"
@@ -43,7 +43,7 @@ func init() {
} }
config.InitSetup(*configFlag) config.InitSetup(*configFlag)
loger.LogInit() logger.LogInit(config.AppInfo.LogPath, config.AppInfo.LogSaveName, config.AppInfo.LogFileExt)
if len(*dbFlag) == 0 { if len(*dbFlag) == 0 {
*dbFlag = config.AppInfo.DBPath + "/db" *dbFlag = config.AppInfo.DBPath + "/db"
} }
@@ -55,13 +55,9 @@ func init() {
service.Cache = cache.Init() service.Cache = cache.Init()
service.GetToken() service.GetCPUThermalZone()
service.NewVersionApp = make(map[string]string)
route.InitFunction() route.InitFunction()
// go service.LoopFriend()
// go service.MyService.App().CheckNewImage()
} }
// @title casaOS API // @title casaOS API
@@ -110,9 +106,9 @@ func main() {
if err != nil { if err != nil {
panic(err) panic(err)
} }
routers := []string{"sys", "apps", "container", "app-categories", "port", "file", "folder", "batch", "image", "samba", "notify"} routers := []string{"sys", "port", "file", "folder", "batch", "image", "samba", "notify"}
for _, v := range routers { for _, v := range routers {
err = service.MyService.Gateway().CreateRoute(&common.Route{ err = service.MyService.Gateway().CreateRoute(&model.Route{
Path: "/v1/" + v, Path: "/v1/" + v,
Target: "http://" + listener.Addr().String(), Target: "http://" + listener.Addr().String(),
}) })
@@ -126,7 +122,7 @@ func main() {
time.Sleep(time.Second * 2) time.Sleep(time.Second * 2)
// v0.3.6 // v0.3.6
if config.ServerInfo.HttpPort != "" { if config.ServerInfo.HttpPort != "" {
changePort := common.ChangePortRequest{} changePort := model.ChangePortRequest{}
changePort.Port = config.ServerInfo.HttpPort changePort.Port = config.ServerInfo.HttpPort
err := service.MyService.Gateway().ChangePort(&changePort) err := service.MyService.Gateway().ChangePort(&changePort)
if err == nil { if err == nil {
@@ -137,20 +133,19 @@ func main() {
}() }()
urlFilePath := filepath.Join(config.CommonInfo.RuntimePath, "casaos.url") urlFilePath := filepath.Join(config.CommonInfo.RuntimePath, "casaos.url")
err = file.CreateFileAndWriteContent(urlFilePath, "http://"+listener.Addr().String()) if err := file.CreateFileAndWriteContent(urlFilePath, "http://"+listener.Addr().String()); err != nil {
if err != nil { logger.Error("error when creating address file", zap.Error(err),
loger.Error("Management service is listening...",
zap.Any("address", listener.Addr().String()), zap.Any("address", listener.Addr().String()),
zap.Any("filepath", urlFilePath), zap.Any("filepath", urlFilePath),
) )
} }
if supported, err := daemon.SdNotify(false, daemon.SdNotifyReady); err != nil { if supported, err := daemon.SdNotify(false, daemon.SdNotifyReady); err != nil {
loger.Error("Failed to notify systemd that casaos main service is ready", zap.Any("error", err)) logger.Error("Failed to notify systemd that casaos main service is ready", zap.Any("error", err))
} else if supported { } else if supported {
loger.Info("Notified systemd that casaos main service is ready") logger.Info("Notified systemd that casaos main service is ready")
} else { } else {
loger.Info("This process is not running as a systemd service.") logger.Info("This process is not running as a systemd service.")
} }
s := &http.Server{ s := &http.Server{
@@ -158,6 +153,8 @@ func main() {
ReadHeaderTimeout: 5 * time.Second, // fix G112: Potential slowloris attack (see https://github.com/securego/gosec) ReadHeaderTimeout: 5 * time.Second, // fix G112: Potential slowloris attack (see https://github.com/securego/gosec)
} }
logger.Info("CasaOS main service is listening...", zap.Any("address", listener.Addr().String()))
err = s.Serve(listener) // not using http.serve() to fix G114: Use of net/http serve function that has no support for setting timeouts (see https://github.com/securego/gosec) err = s.Serve(listener) // not using http.serve() to fix G114: Use of net/http serve function that has no support for setting timeouts (see https://github.com/securego/gosec)
if err != nil { if err != nil {
panic(err) panic(err)

View File

@@ -1,62 +0,0 @@
/*
* @Author: LinkLeong link@icewhale.com
* @Date: 2021-10-08 10:29:08
* @LastEditors: LinkLeong
* @LastEditTime: 2022-07-22 11:06:07
* @FilePath: /CasaOS/middleware/gin.go
* @Description:
* @Website: https://www.casaos.io
* Copyright (c) 2022 by icewhale, All Rights Reserved.
*/
package middleware
import (
"fmt"
"net/http"
"strings"
"github.com/IceWhaleTech/CasaOS/pkg/utils/loger"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
func Cors() gin.HandlerFunc {
return func(c *gin.Context) {
method := c.Request.Method
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE,UPDATE")
//允许跨域设置可以返回其他子段,可以自定义字段
c.Header("Access-Control-Allow-Headers", "Authorization, Content-Length, X-CSRF-Token, Token,session,Language,Content-Type,Access-Control-Allow-Origin,Access-Control-Allow-Headers,Access-Control-Allow-Methods,Connection,Host,Origin,Referer,User-Agent,X-Requested-With")
// 允许浏览器(客户端)可以解析的头部 (重要)
c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers")
//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")
//设置缓存时间
c.Header("Access-Control-Max-Age", "172800")
c.Header("Access-Control-Allow-Credentials", "true")
c.Set("Content-Type", "application/json")
//}
//允许类型校验
if method == "OPTIONS" {
c.JSON(http.StatusOK, "ok!")
}
defer func() {
if err := recover(); err != nil {
fmt.Println(err)
}
}()
c.Next()
}
}
func WriteLog() gin.HandlerFunc {
return func(c *gin.Context) {
if !strings.Contains(c.Request.URL.String(), "password") {
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))
c.Next()
}
}
}

View File

@@ -1,128 +0,0 @@
package model
import (
"database/sql/driver"
"encoding/json"
"time"
)
type ServerAppListCollection struct {
List []ServerAppList `json:"list"`
Recommend []ServerAppList `json:"recommend"`
Community []ServerAppList `json:"community"`
Version string `json:"version"`
}
// @tiger - 对于用于出参的数据结构,静态信息(例如 title
// 动态信息(例如 state、query_count应该划分到不同的数据结构中
//
// 这样的好处是
// 1 - 多次获取动态信息时可以减少出参复杂度,因为静态信息只获取一次就好
// 2 - 在未来的迭代中,可以降低维护成本(所有字段都展开放在一个层级维护成本略高)
//
// 另外,一些针对性字段,例如 Docker 相关的,可以用 map 来保存。
// 这样在未来增加多态 App例如 Snap不需要维护多个结构或者一个结构保存不必要的字段
type ServerAppList struct {
Id uint `gorm:"column:id;primary_key" json:"id"`
Title string `json:"title"`
Description string `json:"description"`
Tagline string `json:"tagline"`
Tags Strings `gorm:"type:json" json:"tags"`
Icon string `json:"icon"`
ScreenshotLink Strings `gorm:"type:json" json:"screenshot_link"`
Category string `json:"category"`
CategoryId int `json:"category_id"`
CategoryFont string `json:"category_font"`
PortMap string `json:"port_map"`
ImageVersion string `json:"image_version"`
Tip string `json:"tip"`
Envs EnvArray `json:"envs"`
Ports PortArray `json:"ports"`
Volumes PathArray `json:"volumes"`
Devices PathArray `json:"devices"`
NetworkModel string `json:"network_model"`
Image string `json:"image"`
Index string `json:"index"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
State int `json:"state"`
Author string `json:"author"`
MinMemory int `json:"min_memory"`
MinDisk int `json:"min_disk"`
MaxMemory uint64 `json:"max_memory"`
Thumbnail string `json:"thumbnail"`
Healthy string `json:"healthy"`
Plugins Strings `json:"plugins"`
Origin string `json:"origin"`
Type int `json:"type"`
QueryCount int `json:"query_count"`
Developer string `json:"developer"`
HostName string `json:"host_name"`
Privileged bool `json:"privileged"`
CapAdd Strings `json:"cap_add"`
Cmd Strings `json:"cmd"`
}
type Ports struct {
ContainerPort uint `json:"container_port"`
CommendPort int `json:"commend_port"`
Desc string `json:"desc"`
Type int `json:"type"` // 1:必选 2:可选 3:默认值不必显示 4:系统处理 5:container内容也可编辑
}
type Volume struct {
ContainerPath string `json:"container_path"`
Path string `json:"path"`
Desc string `json:"desc"`
Type int `json:"type"` // 1:必选 2:可选 3:默认值不必显示 4:系统处理 5:container内容也可编辑
}
type Envs struct {
Name string `json:"name"`
Value string `json:"value"`
Desc string `json:"desc"`
Type int `json:"type"` // 1:必选 2:可选 3:默认值不必显示 4:系统处理 5:container内容也可编辑
}
type Devices struct {
ContainerPath string `json:"container_path"`
Path string `json:"path"`
Desc string `json:"desc"`
Type int `json:"type"` // 1:必选 2:可选 3:默认值不必显示 4:系统处理 5:container内容也可编辑
}
type configures struct {
TcpPorts []Ports `json:"tcp_ports"`
UdpPorts []Ports `json:"udp_ports"`
Envs []Envs `json:"envs"`
Volumes []Volume `json:"volumes"`
Devices []Devices `json:"devices"`
}
/****************使gorm支持[]string结构*******************/
type Strings []string
func (c Strings) Value() (driver.Value, error) {
b, err := json.Marshal(c)
return string(b), err
}
func (c *Strings) Scan(input interface{}) error {
return json.Unmarshal(input.([]byte), c)
}
/****************使gorm支持[]string结构*******************/
/****************使gorm支持[]string结构*******************/
type MapStrings []map[string]string
func (c MapStrings) Value() (driver.Value, error) {
b, err := json.Marshal(c)
return string(b), err
}
func (c *MapStrings) Scan(input interface{}) error {
return json.Unmarshal(input.([]byte), c)
}
/****************使gorm支持[]string结构*******************/

View File

@@ -1,23 +0,0 @@
/*
* @Author: link a624669980@163.com
* @Date: 2022-05-16 17:37:08
* @LastEditors: LinkLeong
* @LastEditTime: 2022-07-13 10:46:38
* @FilePath: /CasaOS/model/category.go
* @Description:
*/
package model
type ServerCategoryList struct {
Version string `json:"version"`
Item []CategoryList `json:"item"`
}
type CategoryList struct {
Id uint `gorm:"column:id;primary_key" json:"id"`
//CreatedAt time.Time `json:"created_at"`
//
//UpdatedAt time.Time `json:"updated_at"`
Font string `json:"font"` // @tiger - 如果这个和前端有关,应该不属于后端的出参范围,而是前端去界定
Name string `json:"name"`
Count uint `json:"count"` // @tiger - count 属于动态信息,应该单独放在一个出参结构中(原因见另外一个关于 静态/动态 出参的注释)
}

View File

@@ -1,22 +0,0 @@
/*
* @Author: LinkLeong link@icewhale.com
* @Date: 2021-12-08 18:10:25
* @LastEditors: LinkLeong
* @LastEditTime: 2022-07-13 10:49:16
* @FilePath: /CasaOS/model/docker.go
* @Description:
* @Website: https://www.casaos.io
* Copyright (c) 2022 by icewhale, All Rights Reserved.
*/
package model
type DockerStatsModel struct {
Icon string `json:"icon"`
Title string `json:"title"`
Data interface{} `json:"data"`
Previous interface{} `json:"previous"`
}
type DeckerDaemonModel struct {
Graph string `json:"graph"`
}

View File

@@ -1,133 +0,0 @@
package model
import (
"database/sql/driver"
"encoding/json"
)
type TcpPorts struct {
Desc string `json:"desc"`
ContainerPort int `json:"container_port"`
}
type UdpPorts struct {
Desc string `json:"desc"`
ContainerPort int `json:"container_port"`
}
/*******************使用gorm支持json************************************/
type PortMap struct {
ContainerPort string `json:"container"`
CommendPort string `json:"host"`
Protocol string `json:"protocol"`
Desc string `json:"desc"`
Type int `json:"type"`
}
type PortArray []PortMap
// Value 实现方法
func (p PortArray) Value() (driver.Value, error) {
return json.Marshal(p)
}
// Scan 实现方法
func (p *PortArray) Scan(input interface{}) error {
return json.Unmarshal(input.([]byte), p)
}
/************************************************************************/
/*******************使用gorm支持json************************************/
type Env struct {
Name string `json:"container"`
Value string `json:"host"`
Desc string `json:"desc"`
Type int `json:"type"`
}
type JSON json.RawMessage
type EnvArray []Env
// Value 实现方法
func (p EnvArray) Value() (driver.Value, error) {
return json.Marshal(p)
//return .MarshalJSON()
}
// Scan 实现方法
func (p *EnvArray) Scan(input interface{}) error {
return json.Unmarshal(input.([]byte), p)
}
/************************************************************************/
/*******************使用gorm支持json************************************/
type PathMap struct {
ContainerPath string `json:"container"`
Path string `json:"host"`
Type int `json:"type"`
Desc string `json:"desc"`
}
type PathArray []PathMap
// Value 实现方法
func (p PathArray) Value() (driver.Value, error) {
return json.Marshal(p)
}
// Scan 实现方法
func (p *PathArray) Scan(input interface{}) error {
return json.Unmarshal(input.([]byte), p)
}
/************************************************************************/
//type PostData struct {
// Envs EnvArrey `json:"envs,omitempty"`
// Udp PortArrey `json:"udp_ports"`
// Tcp PortArrey `json:"tcp_ports"`
// Volumes PathArrey `json:"volumes"`
// Devices PathArrey `json:"devices"`
// Port string `json:"port,omitempty"`
// PortMap string `json:"port_map"`
// CpuShares int64 `json:"cpu_shares,omitempty"`
// Memory int64 `json:"memory,omitempty"`
// Restart string `json:"restart,omitempty"`
// EnableUPNP bool `json:"enable_upnp"`
// Label string `json:"label"`
// Position bool `json:"position"`
//}
type CustomizationPostData struct {
ContainerName string `json:"container_name"`
CustomId string `json:"custom_id"`
Origin string `json:"origin"`
NetworkModel string `json:"network_model"`
Index string `json:"index"`
Icon string `json:"icon"`
Image string `json:"image"`
Envs EnvArray `json:"envs"`
Ports PortArray `json:"ports"`
Volumes PathArray `json:"volumes"`
Devices PathArray `json:"devices"`
//Port string `json:"port,omitempty"`
PortMap string `json:"port_map"`
CpuShares int64 `json:"cpu_shares"`
Memory int64 `json:"memory"`
Restart string `json:"restart"`
EnableUPNP bool `json:"enable_upnp"`
Label string `json:"label"`
Description string `json:"description"`
Position bool `json:"position"`
HostName string `json:"host_name"`
Privileged bool `json:"privileged"`
CapAdd []string `json:"cap_add"`
Cmd []string `json:"cmd"`
Protocol string `json:"protocol"`
Host string `json:"host"`
}

View File

@@ -1,21 +0,0 @@
/*
* @Author: LinkLeong link@icewhale.com
* @Date: 2022-05-27 15:01:58
* @LastEditors: LinkLeong
* @LastEditTime: 2022-05-31 14:51:21
* @FilePath: /CasaOS/model/notify/application.go
* @Description:
* @Website: https://www.casaos.io
* Copyright (c) 2022 by icewhale, All Rights Reserved.
*/
package notify
type Application struct {
Name string `json:"name"`
State string `json:"state"`
Type string `json:"type"`
Icon string `json:"icon"`
Message string `json:"message"`
Finished bool `json:"finished"`
Success bool `json:"success"`
}

View File

@@ -14,7 +14,7 @@ import "time"
// 系统配置 // 系统配置
type SysInfoModel struct { type SysInfoModel struct {
Name string //系统名称 Name string // 系统名称
} }
// 服务配置 // 服务配置
@@ -26,6 +26,7 @@ type ServerModel struct {
Token string Token string
USBAutoMount string USBAutoMount string
SocketPort string SocketPort string
UpdateUrl string
} }
// 服务配置 // 服务配置
@@ -65,11 +66,12 @@ type SystemConfig struct {
ConfigPath string `json:"config_path"` ConfigPath string `json:"config_path"`
} }
type CasaOSGlobalVariables struct {
AppChange bool
}
type FileSetting struct { type FileSetting struct {
ShareDir []string `json:"share_dir" delim:"|"` ShareDir []string `json:"share_dir" delim:"|"`
DownloadDir string `json:"download_dir"` DownloadDir string `json:"download_dir"`
} }
type BaseInfo struct {
Hash string `json:"i"`
Version string `json:"v"`
Channel string `json:"c,omitempty"`
}

View File

@@ -31,23 +31,20 @@ var AppInfo = &model.APPModel{}
var CommonInfo = &model.CommonModel{} var CommonInfo = &model.CommonModel{}
//var RedisInfo = &model.RedisModel{} // var RedisInfo = &model.RedisModel{}
// server相关 // server相关
var ServerInfo = &model.ServerModel{} var ServerInfo = &model.ServerModel{}
var SystemConfigInfo = &model.SystemConfig{} var SystemConfigInfo = &model.SystemConfig{}
var CasaOSGlobalVariables = &model.CasaOSGlobalVariables{}
var FileSettingInfo = &model.FileSetting{} var FileSettingInfo = &model.FileSetting{}
var Cfg *ini.File var Cfg *ini.File
// 初始化设置,获取系统的部分信息。 // 初始化设置,获取系统的部分信息。
func InitSetup(config string) { func InitSetup(config string) {
configDir := USERCONFIGURL
var configDir = USERCONFIGURL
if len(config) > 0 { if len(config) > 0 {
configDir = config configDir = config
} }
@@ -55,7 +52,7 @@ func InitSetup(config string) {
configDir = "./conf/conf.conf" configDir = "./conf/conf.conf"
} }
var err error var err error
//读取文件 // 读取文件
Cfg, err = ini.Load(configDir) Cfg, err = ini.Load(configDir)
if err != nil { if err != nil {
Cfg, err = ini.Load("/etc/casaos.conf") Cfg, err = ini.Load("/etc/casaos.conf")
@@ -68,7 +65,7 @@ func InitSetup(config string) {
} }
} }
mapTo("app", AppInfo) mapTo("app", AppInfo)
//mapTo("redis", RedisInfo) // mapTo("redis", RedisInfo)
mapTo("server", ServerInfo) mapTo("server", ServerInfo)
mapTo("system", SystemConfigInfo) mapTo("system", SystemConfigInfo)
mapTo("file", FileSettingInfo) mapTo("file", FileSettingInfo)
@@ -91,7 +88,6 @@ func InitSetup(config string) {
} }
Cfg.SaveTo(configDir) Cfg.SaveTo(configDir)
// AppInfo.ProjectPath = getCurrentDirectory() //os.Getwd() // AppInfo.ProjectPath = getCurrentDirectory() //os.Getwd()
} }
// 映射 // 映射
@@ -111,6 +107,7 @@ func getCurrentAbPathByCaller() string {
} }
return abPath return abPath
} }
func getCurrentDirectory() string { func getCurrentDirectory() string {
dir, err := filepath.Abs(filepath.Dir(os.Args[0])) dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
if err != nil { if err != nil {

View File

@@ -1,3 +0,0 @@
package docker
const NETWORKNAME = "oasis"

View File

@@ -1,402 +0,0 @@
package docker
import (
"bytes"
json2 "encoding/json"
"fmt"
"io"
"regexp"
"sync"
"time"
"github.com/gorilla/websocket"
"github.com/sirupsen/logrus"
"golang.org/x/crypto/ssh"
)
func NewSshClient(user, password string, port string) (*ssh.Client, error) {
// connet to ssh
// addr = fmt.Sprintf("%s:%d", host, port)
config := &ssh.ClientConfig{
Timeout: time.Second * 5,
User: user,
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
//HostKeyCallback: ,
//HostKeyCallback: hostKeyCallBackFunc(h.Host),
}
//if h.Type == "password" {
config.Auth = []ssh.AuthMethod{ssh.Password(password)}
//} else {
// config.Auth = []ssh.AuthMethod{publicKeyAuthFunc(h.Key)}
//}
addr := fmt.Sprintf("%s:%s", "127.0.0.1", port)
c, err := ssh.Dial("tcp", addr, config)
if err != nil {
return nil, err
}
return c, nil
}
// setup ssh shell session
// set Session and StdinPipe here,
// and the Session.Stdout and Session.Sdterr are also set.
func NewSshConn(cols, rows int, sshClient *ssh.Client) (*SshConn, error) {
sshSession, err := sshClient.NewSession()
if err != nil {
return nil, err
}
stdinP, err := sshSession.StdinPipe()
if err != nil {
return nil, err
}
comboWriter := new(wsBufferWriter)
sshSession.Stdout = comboWriter
sshSession.Stderr = comboWriter
modes := ssh.TerminalModes{
ssh.ECHO: 1, // disable echo
ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
}
// Request pseudo terminal
if err := sshSession.RequestPty("xterm", rows, cols, modes); err != nil {
return nil, err
}
// Start remote shell
if err := sshSession.Shell(); err != nil {
return nil, err
}
return &SshConn{StdinPipe: stdinP, ComboOutput: comboWriter, Session: sshSession}, nil
}
type SshConn struct {
// calling Write() to write data into ssh server
StdinPipe io.WriteCloser
// Write() be called to receive data from ssh server
ComboOutput *wsBufferWriter
Session *ssh.Session
}
type wsBufferWriter struct {
buffer bytes.Buffer
mu sync.Mutex
}
func (w *wsBufferWriter) Write(p []byte) (int, error) {
w.mu.Lock()
defer w.mu.Unlock()
return w.buffer.Write(p)
}
func (s *SshConn) Close() {
if s.Session != nil {
s.Session.Close()
}
}
const (
wsMsgCmd = "cmd"
wsMsgResize = "resize"
)
//ReceiveWsMsg receive websocket msg do some handling then write into ssh.session.stdin
func ReceiveWsMsgUser(wsConn *websocket.Conn, logBuff *bytes.Buffer) string {
//tells other go routine quit
username := ""
for {
//read websocket msg
_, wsData, err := wsConn.ReadMessage()
if err != nil {
return ""
}
msgObj := wsMsg{}
if err := json2.Unmarshal(wsData, &msgObj); err != nil {
msgObj.Type = "cmd"
msgObj.Cmd = string(wsData)
}
//if err := json.Unmarshal(wsData, &msgObj); err != nil {
// logrus.WithError(err).WithField("wsData", string(wsData)).Error("unmarshal websocket message failed")
//}
switch msgObj.Type {
case wsMsgCmd:
//handle xterm.js stdin
//decodeBytes, err := base64.StdEncoding.DecodeString(msgObj.Cmd)
decodeBytes := []byte(msgObj.Cmd)
if msgObj.Cmd == "\u007f" {
if len(username) == 0 {
continue
}
wsConn.WriteMessage(websocket.TextMessage, []byte("\b\x1b[K"))
username = username[:len(username)-1]
continue
}
if msgObj.Cmd == "\r" {
return username
}
username += msgObj.Cmd
if err := wsConn.WriteMessage(websocket.TextMessage, decodeBytes); err != nil {
logrus.WithError(err).Error("ws cmd bytes write to ssh.stdin pipe failed")
}
//write input cmd to log buffer
if _, err := logBuff.Write(decodeBytes); err != nil {
logrus.WithError(err).Error("write received cmd into log buffer failed")
}
}
}
}
func ReceiveWsMsgPassword(wsConn *websocket.Conn, logBuff *bytes.Buffer) string {
//tells other go routine quit
password := ""
for {
//read websocket msg
_, wsData, err := wsConn.ReadMessage()
if err != nil {
logrus.WithError(err).Error("reading webSocket message failed")
return ""
}
msgObj := wsMsg{}
if err := json2.Unmarshal(wsData, &msgObj); err != nil {
msgObj.Type = "cmd"
msgObj.Cmd = string(wsData)
}
//if err := json.Unmarshal(wsData, &msgObj); err != nil {
// logrus.WithError(err).WithField("wsData", string(wsData)).Error("unmarshal websocket message failed")
//}
switch msgObj.Type {
case wsMsgCmd:
//handle xterm.js stdin
//decodeBytes, err := base64.StdEncoding.DecodeString(msgObj.Cmd)
if msgObj.Cmd == "\r" {
return password
}
if msgObj.Cmd == "\u007f" {
if len(password) == 0 {
continue
}
password = password[:len(password)-1]
continue
}
password += msgObj.Cmd
}
}
}
//ReceiveWsMsg receive websocket msg do some handling then write into ssh.session.stdin
func (ssConn *SshConn) ReceiveWsMsg(wsConn *websocket.Conn, logBuff *bytes.Buffer, exitCh chan bool) {
//tells other go routine quit
defer setQuit(exitCh)
for {
select {
case <-exitCh:
return
default:
//read websocket msg
_, wsData, err := wsConn.ReadMessage()
if err != nil {
logrus.WithError(err).Error("reading webSocket message failed")
return
}
//unmashal bytes into struct
//msgObj := wsMsg{
// Type: "cmd",
// Cmd: "",
// Rows: 50,
// Cols: 180,
//}
msgObj := wsMsg{}
if err := json2.Unmarshal(wsData, &msgObj); err != nil {
msgObj.Type = "cmd"
msgObj.Cmd = string(wsData)
}
//if err := json.Unmarshal(wsData, &msgObj); err != nil {
// logrus.WithError(err).WithField("wsData", string(wsData)).Error("unmarshal websocket message failed")
//}
switch msgObj.Type {
case wsMsgResize:
//handle xterm.js size change
if msgObj.Cols > 0 && msgObj.Rows > 0 {
if err := ssConn.Session.WindowChange(msgObj.Rows, msgObj.Cols); err != nil {
logrus.WithError(err).Error("ssh pty change windows size failed")
}
}
case wsMsgCmd:
//handle xterm.js stdin
//decodeBytes, err := base64.StdEncoding.DecodeString(msgObj.Cmd)
decodeBytes := []byte(msgObj.Cmd)
if err != nil {
logrus.WithError(err).Error("websock cmd string base64 decoding failed")
}
if _, err := ssConn.StdinPipe.Write(decodeBytes); err != nil {
logrus.WithError(err).Error("ws cmd bytes write to ssh.stdin pipe failed")
}
//write input cmd to log buffer
if _, err := logBuff.Write(decodeBytes); err != nil {
logrus.WithError(err).Error("write received cmd into log buffer failed")
}
}
}
}
}
func (ssConn *SshConn) SendComboOutput(wsConn *websocket.Conn, exitCh chan bool) {
//tells other go routine quit
//defer setQuit(exitCh)
//every 120ms write combine output bytes into websocket response
tick := time.NewTicker(time.Millisecond * time.Duration(120))
//for range time.Tick(120 * time.Millisecond){}
defer tick.Stop()
for {
select {
case <-tick.C:
//write combine output bytes into websocket response
if err := flushComboOutput(ssConn.ComboOutput, wsConn); err != nil {
logrus.WithError(err).Error("ssh sending combo output to webSocket failed")
return
}
case <-exitCh:
return
}
}
}
func flushComboOutput(w *wsBufferWriter, wsConn *websocket.Conn) error {
if w.buffer.Len() != 0 {
err := wsConn.WriteMessage(websocket.TextMessage, w.buffer.Bytes())
if err != nil {
return err
}
w.buffer.Reset()
}
return nil
}
//ReceiveWsMsg receive websocket msg do some handling then write into ssh.session.stdin
func (ssConn *SshConn) Login(wsConn *websocket.Conn, logBuff *bytes.Buffer, exitCh chan bool) {
//tells other go routine quit
defer setQuit(exitCh)
for {
select {
case <-exitCh:
return
default:
//read websocket msg
_, wsData, err := wsConn.ReadMessage()
if err != nil {
logrus.WithError(err).Error("reading webSocket message failed")
return
}
//unmashal bytes into struct
//msgObj := wsMsg{
// Type: "cmd",
// Cmd: "",
// Rows: 50,
// Cols: 180,
//}
msgObj := wsMsg{}
if err := json2.Unmarshal(wsData, &msgObj); err != nil {
msgObj.Type = "cmd"
msgObj.Cmd = string(wsData)
}
//if err := json.Unmarshal(wsData, &msgObj); err != nil {
// logrus.WithError(err).WithField("wsData", string(wsData)).Error("unmarshal websocket message failed")
//}
switch msgObj.Type {
case wsMsgResize:
//handle xterm.js size change
if msgObj.Cols > 0 && msgObj.Rows > 0 {
if err := ssConn.Session.WindowChange(msgObj.Rows, msgObj.Cols); err != nil {
logrus.WithError(err).Error("ssh pty change windows size failed")
}
}
case wsMsgCmd:
//handle xterm.js stdin
//decodeBytes, err := base64.StdEncoding.DecodeString(msgObj.Cmd)
decodeBytes := []byte(msgObj.Cmd)
if err != nil {
logrus.WithError(err).Error("websock cmd string base64 decoding failed")
}
if _, err := ssConn.StdinPipe.Write(decodeBytes); err != nil {
logrus.WithError(err).Error("ws cmd bytes write to ssh.stdin pipe failed")
}
//write input cmd to log buffer
if _, err := logBuff.Write(decodeBytes); err != nil {
logrus.WithError(err).Error("write received cmd into log buffer failed")
}
}
}
}
}
func (ssConn *SshConn) SessionWait(quitChan chan bool) {
if err := ssConn.Session.Wait(); err != nil {
logrus.WithError(err).Error("ssh session wait failed")
setQuit(quitChan)
}
}
func setQuit(ch chan bool) {
ch <- true
}
type wsMsg struct {
Type string `json:"type"`
Cmd string `json:"cmd"`
Cols int `json:"cols"`
Rows int `json:"rows"`
}
// 将终端的输出转发到前端
func WsWriterCopy(reader io.Reader, writer *websocket.Conn) {
buf := make([]byte, 8192)
reg1 := regexp.MustCompile(`stty rows \d+ && stty cols \d+ `)
for {
nr, err := reader.Read(buf)
if nr > 0 {
result1 := reg1.FindIndex(buf[0:nr])
if len(result1) > 0 {
fmt.Println(result1)
} else {
err := writer.WriteMessage(websocket.BinaryMessage, buf[0:nr])
if err != nil {
return
}
}
}
if err != nil {
return
}
}
}
// 将前端的输入转发到终端
func WsReaderCopy(reader *websocket.Conn, writer io.Writer) {
for {
messageType, p, err := reader.ReadMessage()
if err != nil {
return
}
if messageType == websocket.TextMessage {
msgObj := wsMsg{}
if err = json2.Unmarshal(p, &msgObj); err != nil {
writer.Write(p)
} else if msgObj.Type == wsMsgResize {
//writer.Write([]byte("stty rows " + strconv.Itoa(msgObj.Rows) + " && stty cols " + strconv.Itoa(msgObj.Cols) + " \r"))
}
}
}
}

View File

@@ -1,11 +0,0 @@
package docker
import "strings"
func GetDir(id, envName string) string {
if strings.Contains(envName, "$AppID") && len(id) > 0 {
return strings.ReplaceAll(envName, "$AppID", id)
}
return envName
}

View File

@@ -1,10 +0,0 @@
package docker
import (
"fmt"
"testing"
)
func TestGetDir(t *testing.T) {
fmt.Println(GetDir("", "config"))
}

View File

@@ -13,11 +13,11 @@ package sqlite
import ( import (
"time" "time"
"github.com/IceWhaleTech/CasaOS-Common/utils/logger"
"github.com/IceWhaleTech/CasaOS/pkg/utils/file" "github.com/IceWhaleTech/CasaOS/pkg/utils/file"
"github.com/IceWhaleTech/CasaOS/pkg/utils/loger"
model2 "github.com/IceWhaleTech/CasaOS/service/model" model2 "github.com/IceWhaleTech/CasaOS/service/model"
"github.com/glebarez/sqlite"
"go.uber.org/zap" "go.uber.org/zap"
"gorm.io/driver/sqlite"
"gorm.io/gorm" "gorm.io/gorm"
) )
@@ -28,28 +28,27 @@ func GetDb(dbPath string) *gorm.DB {
return gdb return gdb
} }
// Refer https://github.com/go-sql-driver/mysql#dsn-data-source-name // Refer https://github.com/go-sql-driver/mysql#dsn-data-source-name
//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) // 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)
//db, err := gorm.Open(mysql2.Open(dsn), &gorm.Config{}) // db, err := gorm.Open(mysql2.Open(dsn), &gorm.Config{})
file.IsNotExistMkDir(dbPath) file.IsNotExistMkDir(dbPath)
db, err := gorm.Open(sqlite.Open(dbPath+"/casaOS.db"), &gorm.Config{}) db, err := gorm.Open(sqlite.Open(dbPath+"/casaOS.db"), &gorm.Config{})
c, _ := db.DB() c, _ := db.DB()
c.SetMaxIdleConns(10) c.SetMaxIdleConns(10)
c.SetMaxOpenConns(100) c.SetMaxOpenConns(1)
c.SetConnMaxIdleTime(time.Second * 1000) c.SetConnMaxIdleTime(time.Second * 1000)
if err != nil { if err != nil {
loger.Error("sqlite connect error", zap.Any("db connect error", err)) logger.Error("sqlite connect error", zap.Any("db connect error", err))
panic("sqlite connect error") panic("sqlite connect error")
return nil
} }
gdb = db gdb = db
err = db.AutoMigrate(&model2.AppNotify{}, &model2.AppListDBModel{}, model2.SharesDBModel{}, model2.ConnectionsDBModel{}) err = db.AutoMigrate(&model2.AppNotify{}, model2.SharesDBModel{}, model2.ConnectionsDBModel{})
db.Exec("DROP TABLE IF EXISTS o_application") db.Exec("DROP TABLE IF EXISTS o_application")
db.Exec("DROP TABLE IF EXISTS o_friend") db.Exec("DROP TABLE IF EXISTS o_friend")
db.Exec("DROP TABLE IF EXISTS o_person_download") db.Exec("DROP TABLE IF EXISTS o_person_download")
db.Exec("DROP TABLE IF EXISTS o_person_down_record") db.Exec("DROP TABLE IF EXISTS o_person_down_record")
if err != nil { if err != nil {
loger.Error("check or create db error", zap.Any("error", err)) logger.Error("check or create db error", zap.Any("error", err))
} }
return db return db
} }

View File

@@ -35,8 +35,8 @@ func ExecResultStrArray(cmdStr string) []string {
fmt.Println(err) fmt.Println(err)
return nil return nil
} }
//str, err := ioutil.ReadAll(stdout) // str, err := ioutil.ReadAll(stdout)
var networklist = []string{} networklist := []string{}
outputBuf := bufio.NewReader(stdout) outputBuf := bufio.NewReader(stdout)
for { for {
output, _, err := outputBuf.ReadLine() output, _, err := outputBuf.ReadLine()
@@ -54,6 +54,8 @@ func ExecResultStrArray(cmdStr string) []string {
func ExecResultStr(cmdStr string) string { func ExecResultStr(cmdStr string) string {
cmd := exec.Command("/bin/bash", "-c", cmdStr) cmd := exec.Command("/bin/bash", "-c", cmdStr)
println(cmd.String())
stdout, err := cmd.StdoutPipe() stdout, err := cmd.StdoutPipe()
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
@@ -73,7 +75,7 @@ func ExecResultStr(cmdStr string) string {
return string(str) return string(str)
} }
//执行 lsblk 命令 // 执行 lsblk 命令
func ExecLSBLK() []byte { func ExecLSBLK() []byte {
output, err := exec.Command("lsblk", "-O", "-J", "-b").Output() output, err := exec.Command("lsblk", "-O", "-J", "-b").Output()
if err != nil { if err != nil {
@@ -83,7 +85,7 @@ func ExecLSBLK() []byte {
return output return output
} }
//执行 lsblk 命令 // 执行 lsblk 命令
func ExecLSBLKByPath(path string) []byte { func ExecLSBLKByPath(path string) []byte {
output, err := exec.Command("lsblk", path, "-O", "-J", "-b").Output() output, err := exec.Command("lsblk", path, "-O", "-J", "-b").Output()
if err != nil { if err != nil {
@@ -93,7 +95,7 @@ func ExecLSBLKByPath(path string) []byte {
return output return output
} }
//exec smart // exec smart
func ExecSmartCTLByPath(path string) []byte { func ExecSmartCTLByPath(path string) []byte {
timeout := 3 timeout := 3
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second) ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
@@ -107,6 +109,5 @@ func ExecSmartCTLByPath(path string) []byte {
} }
func ExecEnabledSMART(path string) { func ExecEnabledSMART(path string) {
exec.Command("smartctl", "-s on", path).Output() exec.Command("smartctl", "-s on", path).Output()
} }

View File

@@ -30,6 +30,7 @@ const (
Record_NOT_EXIST = 20007 Record_NOT_EXIST = 20007
Record_ALREADY_EXIST = 20008 Record_ALREADY_EXIST = 20008
SERVICE_NOT_RUNNING = 20009 SERVICE_NOT_RUNNING = 20009
CHARACTER_LIMIT = 20010
//disk //disk
NAME_NOT_AVAILABLE = 40001 NAME_NOT_AVAILABLE = 40001
@@ -85,6 +86,7 @@ var MsgFlags = map[int]string{
Record_ALREADY_EXIST: "Record already exists", Record_ALREADY_EXIST: "Record already exists",
Record_NOT_EXIST: "Record does not exist", Record_NOT_EXIST: "Record does not exist",
SERVICE_NOT_RUNNING: "Service is not running", SERVICE_NOT_RUNNING: "Service is not running",
CHARACTER_LIMIT: "Only uppercase letters, lowercase letters and numbers are allowed for username and password.",
//app //app
UNINSTALL_APP_ERROR: "Error uninstalling app", UNINSTALL_APP_ERROR: "Error uninstalling app",
@@ -113,7 +115,7 @@ var MsgFlags = map[int]string{
COMMAND_ERROR_INVALID_OPERATION: "invalid operation", COMMAND_ERROR_INVALID_OPERATION: "invalid operation",
} }
//获取错误信息 // 获取错误信息
func GetMsg(code int) string { func GetMsg(code int) string {
msg, ok := MsgFlags[code] msg, ok := MsgFlags[code]
if ok { if ok {

View File

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

View File

@@ -5,6 +5,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"io/fs"
"io/ioutil" "io/ioutil"
"log" "log"
"mime/multipart" "mime/multipart"
@@ -60,7 +61,7 @@ func MkDir(src string) error {
if err != nil { if err != nil {
return err return err
} }
os.Chmod(src, 0777) os.Chmod(src, 0o777)
return nil return nil
} }
@@ -103,7 +104,7 @@ func MustOpen(fileName, filePath string) (*os.File, error) {
return nil, fmt.Errorf("file.IsNotExistMkDir src: %s, err: %v", src, err) return nil, fmt.Errorf("file.IsNotExistMkDir src: %s, err: %v", src, err)
} }
f, err := Open(src+fileName, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0644) f, err := Open(src+fileName, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0o644)
if err != nil { if err != nil {
return nil, fmt.Errorf("Fail to OpenFile :%v", err) return nil, fmt.Errorf("Fail to OpenFile :%v", err)
} }
@@ -113,7 +114,7 @@ func MustOpen(fileName, filePath string) (*os.File, error) {
// 判断所给路径文件/文件夹是否存在 // 判断所给路径文件/文件夹是否存在
func Exists(path string) bool { func Exists(path string) bool {
_, err := os.Stat(path) //os.Stat获取文件信息 _, err := os.Stat(path) // os.Stat获取文件信息
if err != nil { if err != nil {
if os.IsExist(err) { if os.IsExist(err) {
return true return true
@@ -147,7 +148,7 @@ func CreateFile(path string) error {
} }
func CreateFileAndWriteContent(path string, content string) error { func CreateFileAndWriteContent(path string, content string) error {
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0666) file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0o666)
if err != nil { if err != nil {
return err return err
} }
@@ -163,7 +164,7 @@ func CreateFileAndWriteContent(path string, content string) error {
// IsNotExistMkDir create a directory if it does not exist // IsNotExistMkDir create a directory if it does not exist
func IsNotExistCreateFile(src string) error { func IsNotExistCreateFile(src string) error {
if notExist := CheckNotExist(src); notExist == true { if notExist := CheckNotExist(src); notExist {
if err := CreateFile(src); err != nil { if err := CreateFile(src); err != nil {
return err return err
} }
@@ -267,7 +268,7 @@ func CopySingleFile(src, dst, style string) error {
return os.Chmod(dst, srcinfo.Mode()) return os.Chmod(dst, srcinfo.Mode())
} }
//Check for duplicate file names // Check for duplicate file names
func GetNoDuplicateFileName(fullPath string) string { func GetNoDuplicateFileName(fullPath string) string {
path, fileName := filepath.Split(fullPath) path, fileName := filepath.Split(fullPath)
fileSuffix := path2.Ext(fileName) fileSuffix := path2.Ext(fileName)
@@ -293,7 +294,7 @@ func CopyDir(src string, dst string, style string) error {
} }
return nil return nil
} }
//dstPath := dst // dstPath := dst
lastPath := src[strings.LastIndex(src, "/")+1:] lastPath := src[strings.LastIndex(src, "/")+1:]
dst += "/" + lastPath dst += "/" + lastPath
// for i := 0; Exists(dst); i++ { // for i := 0; Exists(dst); i++ {
@@ -314,7 +315,7 @@ func CopyDir(src string, dst string, style string) error {
} }
for _, fd := range fds { for _, fd := range fds {
srcfp := path.Join(src, fd.Name()) srcfp := path.Join(src, fd.Name())
dstfp := dst //path.Join(dst, fd.Name()) dstfp := dst // path.Join(dst, fd.Name())
if fd.IsDir() { if fd.IsDir() {
if err = CopyDir(srcfp, dstfp, style); err != nil { if err = CopyDir(srcfp, dstfp, style); err != nil {
@@ -336,10 +337,17 @@ func WriteToPath(data []byte, path, name string) error {
} else { } else {
fullPath += "/" + name fullPath += "/" + name
} }
IsNotExistCreateFile(fullPath) return WriteToFullPath(data, fullPath, 0o666)
}
func WriteToFullPath(data []byte, fullPath string, perm fs.FileMode) error {
if err := IsNotExistCreateFile(fullPath); err != nil {
return err
}
file, err := os.OpenFile(fullPath, file, err := os.OpenFile(fullPath,
os.O_WRONLY|os.O_TRUNC|os.O_CREATE, os.O_WRONLY|os.O_TRUNC|os.O_CREATE,
0666, perm,
) )
if err != nil { if err != nil {
return err return err
@@ -350,26 +358,31 @@ func WriteToPath(data []byte, path, name string) error {
return err return err
} }
//最终拼接 // 最终拼接
func SpliceFiles(dir, path string, length int, startPoint int) error { func SpliceFiles(dir, path string, length int, startPoint int) error {
fullPath := path fullPath := path
IsNotExistCreateFile(fullPath) if err := IsNotExistCreateFile(fullPath); err != nil {
return err
}
file, _ := os.OpenFile(fullPath, file, _ := os.OpenFile(fullPath,
os.O_WRONLY|os.O_TRUNC|os.O_CREATE, os.O_WRONLY|os.O_TRUNC|os.O_CREATE,
0666, 0o666,
) )
defer file.Close() defer file.Close()
bufferedWriter := bufio.NewWriter(file) bufferedWriter := bufio.NewWriter(file)
for i := 0; i < length+startPoint; i++ {
// todo: here should have a goroutine to remove each partial file after it is read, to save disk space
for i := 0; i < length+startPoint-1; i++ {
data, err := ioutil.ReadFile(dir + "/" + strconv.Itoa(i+startPoint)) data, err := ioutil.ReadFile(dir + "/" + strconv.Itoa(i+startPoint))
if err != nil { if err != nil {
return err return err
} }
_, err = bufferedWriter.Write(data) if _, err := bufferedWriter.Write(data); err != nil { // recommend to use https://github.com/iceber/iouring-go for faster write
if err != nil {
return err return err
} }
} }
@@ -380,7 +393,6 @@ func SpliceFiles(dir, path string, length int, startPoint int) error {
} }
func GetCompressionAlgorithm(t string) (string, archiver.Writer, error) { func GetCompressionAlgorithm(t string) (string, archiver.Writer, error) {
switch t { switch t {
case "zip", "": case "zip", "":
return ".zip", archiver.NewZip(), nil return ".zip", archiver.NewZip(), nil
@@ -400,8 +412,8 @@ func GetCompressionAlgorithm(t string) (string, archiver.Writer, error) {
return "", nil, errors.New("format not implemented") return "", nil, errors.New("format not implemented")
} }
} }
func AddFile(ar archiver.Writer, path, commonPath string) error {
func AddFile(ar archiver.Writer, path, commonPath string) error {
info, err := os.Stat(path) info, err := os.Stat(path)
if err != nil { if err != nil {
return err return err
@@ -447,6 +459,7 @@ func AddFile(ar archiver.Writer, path, commonPath string) error {
return nil return nil
} }
func CommonPrefix(sep byte, paths ...string) string { func CommonPrefix(sep byte, paths ...string) string {
// Handle special cases. // Handle special cases.
switch len(paths) { switch len(paths) {
@@ -513,7 +526,7 @@ func GetFileOrDirSize(path string) (int64, error) {
return fileInfo.Size(), nil return fileInfo.Size(), nil
} }
//getFileSize get file size by path(B) // getFileSize get file size by path(B)
func DirSizeB(path string) (int64, error) { func DirSizeB(path string) (int64, error) {
var size int64 var size int64
err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error { err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error {

View File

@@ -12,9 +12,9 @@ import (
"github.com/tidwall/gjson" "github.com/tidwall/gjson"
) )
//发送GET请求 // 发送GET请求
//url:请求地址 // url:请求地址
//response:请求返回的内容 // response:请求返回的内容
func Get(url string, head map[string]string) (response string) { func Get(url string, head map[string]string) (response string) {
client := &http.Client{Timeout: 30 * time.Second} client := &http.Client{Timeout: 30 * time.Second}
req, err := http.NewRequest("GET", url, nil) req, err := http.NewRequest("GET", url, nil)
@@ -28,10 +28,10 @@ func Get(url string, head map[string]string) (response string) {
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
//需要错误日志的处理 // 需要错误日志的处理
//loger.Error(error) // logger.Error(error)
return "" return ""
//panic(error) // panic(error)
} }
defer resp.Body.Close() defer resp.Body.Close()
var buffer [512]byte var buffer [512]byte
@@ -42,7 +42,7 @@ func Get(url string, head map[string]string) (response string) {
if err != nil && err == io.EOF { if err != nil && err == io.EOF {
break break
} else if err != nil { } else if err != nil {
//loger.Error(err) // logger.Error(err)
return "" return ""
// panic(err) // panic(err)
} }
@@ -51,22 +51,21 @@ func Get(url string, head map[string]string) (response string) {
return return
} }
//发送GET请求 // 发送GET请求
//url:请求地址 // url:请求地址
//response:请求返回的内容 // response:请求返回的内容
func PersonGet(url string) (response string) { func PersonGet(url string) (response string) {
client := &http.Client{Timeout: 5 * time.Second} client := &http.Client{Timeout: 5 * time.Second}
req, err := http.NewRequest("GET", url, nil) req, err := http.NewRequest("GET", url, nil)
if err != nil { if err != nil {
return "" return ""
} }
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
//需要错误日志的处理 // 需要错误日志的处理
//loger.Error(error) // logger.Error(error)
return "" return ""
//panic(error) // panic(error)
} }
defer resp.Body.Close() defer resp.Body.Close()
var buffer [512]byte var buffer [512]byte
@@ -77,7 +76,7 @@ func PersonGet(url string) (response string) {
if err != nil && err == io.EOF { if err != nil && err == io.EOF {
break break
} else if err != nil { } else if err != nil {
//loger.Error(err) // logger.Error(err)
return "" return ""
// panic(err) // panic(err)
} }
@@ -86,11 +85,10 @@ func PersonGet(url string) (response string) {
return return
} }
//发送POST请求 // 发送POST请求
//url:请求地址data:POST请求提交的数据,contentType:请求体格式application/json // url:请求地址data:POST请求提交的数据,contentType:请求体格式application/json
//content:请求放回的内容 // content:请求放回的内容
func Post(url string, data []byte, contentType string, head map[string]string) (content string) { func Post(url string, data []byte, contentType string, head map[string]string) (content string) {
req, err := http.NewRequest("POST", url, bytes.NewBuffer(data)) req, err := http.NewRequest("POST", url, bytes.NewBuffer(data))
req.Header.Add("content-type", contentType) req.Header.Add("content-type", contentType)
for k, v := range head { for k, v := range head {
@@ -113,9 +111,9 @@ func Post(url string, data []byte, contentType string, head map[string]string) (
return return
} }
//发送POST请求 // 发送POST请求
//url:请求地址data:POST请求提交的数据,contentType:请求体格式application/json // url:请求地址data:POST请求提交的数据,contentType:请求体格式application/json
//content:请求放回的内容 // content:请求放回的内容
func ZeroTierGet(url string, head map[string]string) (content string, code int) { func ZeroTierGet(url string, head map[string]string) (content string, code int) {
req, err := http.NewRequest(http.MethodGet, url, nil) req, err := http.NewRequest(http.MethodGet, url, nil)
for k, v := range head { for k, v := range head {
@@ -138,11 +136,10 @@ func ZeroTierGet(url string, head map[string]string) (content string, code int)
return return
} }
//发送GET请求 // 发送GET请求
//url:请求地址 // url:请求地址
//response:请求返回的内容 // response:请求返回的内容
func OasisGet(url string) (response string) { func OasisGet(url string) (response string) {
head := make(map[string]string) head := make(map[string]string)
t := make(chan string) t := make(chan string)
@@ -155,5 +152,4 @@ func OasisGet(url string) (response string) {
head["Authorization"] = <-t head["Authorization"] = <-t
return Get(url, head) return Get(url, head)
} }

View File

@@ -1,92 +0,0 @@
/*
* @Author: LinkLeong link@icewhale.com
* @Date: 2022-06-02 15:09:38
* @LastEditors: LinkLeong
* @LastEditTime: 2022-06-27 15:47:49
* @FilePath: /CasaOS/pkg/utils/loger/log.go
* @Description:
* @Website: https://www.casaos.io
* Copyright (c) 2022 by icewhale, All Rights Reserved.
*/
package loger
import (
"fmt"
"os"
"path"
"path/filepath"
"runtime"
"github.com/IceWhaleTech/CasaOS/pkg/config"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"gopkg.in/natefinch/lumberjack.v2"
)
var loggers *zap.Logger
func getFileLogWriter() (writeSyncer zapcore.WriteSyncer) {
// 使用 lumberjack 实现 log rotate
lumberJackLogger := &lumberjack.Logger{
Filename: filepath.Join(config.AppInfo.LogPath, fmt.Sprintf("%s.%s",
config.AppInfo.LogSaveName,
config.AppInfo.LogFileExt,
)),
MaxSize: 10,
MaxBackups: 60,
MaxAge: 1,
Compress: true,
}
return zapcore.AddSync(lumberJackLogger)
}
func LogInit() {
encoderConfig := zap.NewProductionEncoderConfig()
encoderConfig.EncodeTime = zapcore.EpochTimeEncoder
encoder := zapcore.NewJSONEncoder(encoderConfig)
fileWriteSyncer := getFileLogWriter()
core := zapcore.NewTee(
zapcore.NewCore(encoder, zapcore.AddSync(os.Stdout), zapcore.InfoLevel),
zapcore.NewCore(encoder, fileWriteSyncer, zapcore.InfoLevel),
)
loggers = zap.New(core)
}
func Info(message string, fields ...zap.Field) {
callerFields := getCallerInfoForLog()
fields = append(fields, callerFields...)
loggers.Info(message, fields...)
}
func Debug(message string, fields ...zap.Field) {
callerFields := getCallerInfoForLog()
fields = append(fields, callerFields...)
loggers.Debug(message, fields...)
}
func Error(message string, fields ...zap.Field) {
callerFields := getCallerInfoForLog()
fields = append(fields, callerFields...)
loggers.Error(message, fields...)
}
func Warn(message string, fields ...zap.Field) {
callerFields := getCallerInfoForLog()
fields = append(fields, callerFields...)
loggers.Warn(message, fields...)
}
func getCallerInfoForLog() (callerFields []zap.Field) {
pc, file, line, ok := runtime.Caller(2) // 回溯两层,拿到写日志的调用方的函数信息
if !ok {
return
}
funcName := runtime.FuncForPC(pc).Name()
funcName = path.Base(funcName) //Base函数返回路径的最后一个元素只保留函数名
callerFields = append(callerFields, zap.String("func", funcName), zap.String("file", file), zap.Int("line", line))
return
}

View File

@@ -1,68 +0,0 @@
package port
import (
"fmt"
"net"
)
// 获取可用端口
func GetAvailablePort(t string) (int, error) {
address := fmt.Sprintf("%s:0", "0.0.0.0")
if t == "udp" {
add, err := net.ResolveUDPAddr(t, address)
if err != nil {
return 0, err
}
listener, err := net.ListenUDP(t, add)
if err != nil {
return 0, err
}
defer listener.Close()
return listener.LocalAddr().(*net.UDPAddr).Port, nil
} else {
add, err := net.ResolveTCPAddr(t, address)
if err != nil {
return 0, err
}
listener, err := net.ListenTCP(t, add)
if err != nil {
return 0, err
}
defer listener.Close()
return listener.Addr().(*net.TCPAddr).Port, nil
}
}
// 判断端口是否可以(未被占用)
// param t tcp/udp
func IsPortAvailable(port int, t string) bool {
address := fmt.Sprintf("%s:%d", "0.0.0.0", port)
if t == "udp" {
sadd, err := net.ResolveUDPAddr("udp", address)
uc, err := net.ListenUDP("udp", sadd)
if err != nil {
fmt.Println(err.Error())
return false
} else {
defer uc.Close()
return true
}
} else {
listener, err := net.Listen(t, address)
if err != nil {
//log.Infof("port %s is taken: %s", address, err)
return false
}
defer listener.Close()
return true
}
}

View File

@@ -1,18 +0,0 @@
package port
import (
"fmt"
"testing"
)
func TestPortAvailable(t *testing.T) {
// fmt.Println(PortAvailable())
//fmt.Println(IsPortAvailable(6881,"tcp"))
p, _ := GetAvailablePort("udp")
fmt.Println("udp", p)
fmt.Println(IsPortAvailable(p, "udp"))
t1, _ := GetAvailablePort("tcp")
fmt.Println("tcp", t1)
fmt.Println(IsPortAvailable(t1, "tcp"))
}

View File

@@ -1,24 +0,0 @@
package random
import (
"math/rand"
"time"
)
func RandomString(n int, onlyLetter bool) string {
var letters []rune
if onlyLetter {
letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
} else {
letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
}
b := make([]rune, n)
rand.Seed(time.Now().UnixNano())
for i := range b {
b[i] = letters[rand.Intn(len(letters))]
}
return string(b)
}

View File

@@ -1,10 +0,0 @@
package random
import (
"fmt"
"testing"
)
func TestRandomString(t *testing.T) {
fmt.Println(RandomString(6, true))
}

View File

@@ -1,21 +1,65 @@
/*
* @Author: LinkLeong link@icewhale.org
* @Date: 2022-11-15 15:51:44
* @LastEditors: LinkLeong
* @LastEditTime: 2022-11-15 15:55:16
* @FilePath: /CasaOS/route/init.go
* @Description:
* @Website: https://www.casaos.io
* Copyright (c) 2022 by icewhale, All Rights Reserved.
*/
package route package route
import ( import (
"encoding/json"
"fmt" "fmt"
"os" "os"
"strings" "strings"
"time" "time"
"github.com/IceWhaleTech/CasaOS-Common/utils/logger"
"github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/pkg/config"
"github.com/IceWhaleTech/CasaOS/pkg/samba" "github.com/IceWhaleTech/CasaOS/pkg/samba"
"github.com/IceWhaleTech/CasaOS/pkg/utils/encryption"
"github.com/IceWhaleTech/CasaOS/pkg/utils/file" "github.com/IceWhaleTech/CasaOS/pkg/utils/file"
"github.com/IceWhaleTech/CasaOS/pkg/utils/loger"
"github.com/IceWhaleTech/CasaOS/service" "github.com/IceWhaleTech/CasaOS/service"
"github.com/IceWhaleTech/CasaOS/types"
"go.uber.org/zap" "go.uber.org/zap"
) )
func InitFunction() { func InitFunction() {
go InitNetworkMount() go InitNetworkMount()
go InitInfo()
} }
func InitInfo() {
mb := model.BaseInfo{}
if file.Exists(config.AppInfo.DBPath + "/baseinfo.conf") {
err := json.Unmarshal(file.ReadFullFile(config.AppInfo.DBPath+"/baseinfo.conf"), &mb)
if err != nil {
logger.Error("baseinfo.conf", zap.String("error", err.Error()))
}
}
if file.Exists("/etc/CHANNEL") {
channel := file.ReadFullFile("/etc/CHANNEL")
mb.Channel = string(channel)
}
mac, err := service.MyService.System().GetMacAddress()
if err != nil {
logger.Error("GetMacAddress", zap.String("error", err.Error()))
}
mb.Hash = encryption.GetMD5ByStr(mac)
mb.Version = types.CURRENTVERSION
os.Remove(config.AppInfo.DBPath + "/baseinfo.conf")
by, err := json.Marshal(mb)
if err != nil {
logger.Error("init info err", zap.Any("err", err))
return
}
file.WriteToFullPath(by, config.AppInfo.DBPath+"/baseinfo.conf", 0o666)
}
func InitNetworkMount() { func InitNetworkMount() {
time.Sleep(time.Second * 10) time.Sleep(time.Second * 10)
connections := service.MyService.Connections().GetConnectionsList() connections := service.MyService.Connections().GetConnectionsList()
@@ -24,7 +68,7 @@ func InitNetworkMount() {
directories, err := samba.GetSambaSharesList(connection.Host, connection.Port, connection.Username, connection.Password) directories, err := samba.GetSambaSharesList(connection.Host, connection.Port, connection.Username, connection.Password)
if err != nil { if err != nil {
service.MyService.Connections().DeleteConnection(fmt.Sprint(connection.ID)) service.MyService.Connections().DeleteConnection(fmt.Sprint(connection.ID))
loger.Error("mount samba err", zap.Any("err", err), zap.Any("info", connection)) logger.Error("mount samba err", zap.Any("err", err), zap.Any("info", connection))
continue continue
} }
baseHostPath := "/mnt/" + connection.Host baseHostPath := "/mnt/" + connection.Host

View File

@@ -54,7 +54,6 @@ func SendMemBySocket() {
} }
func SendAllHardwareStatusBySocket() { func SendAllHardwareStatusBySocket() {
netList := service.MyService.System().GetNetInfo() netList := service.MyService.System().GetNetInfo()
newNet := []model.IOCountersStat{} newNet := []model.IOCountersStat{}
nets := service.MyService.System().GetNet(true) nets := service.MyService.System().GetNet(true)
@@ -70,17 +69,27 @@ func SendAllHardwareStatusBySocket() {
} }
} }
cpu := service.MyService.System().GetCpuPercent() cpu := service.MyService.System().GetCpuPercent()
var cpuModel = "arm"
if cpu := service.MyService.System().GetCpuInfo(); len(cpu) > 0 {
if strings.Count(strings.ToLower(strings.TrimSpace(cpu[0].ModelName)), "intel") > 0 {
cpuModel = "intel"
} else if strings.Count(strings.ToLower(strings.TrimSpace(cpu[0].ModelName)), "amd") > 0 {
cpuModel = "amd"
}
}
num := service.MyService.System().GetCpuCoreNum() num := service.MyService.System().GetCpuCoreNum()
cpuData := make(map[string]interface{}) cpuData := make(map[string]interface{})
cpuData["percent"] = cpu cpuData["percent"] = cpu
cpuData["num"] = num cpuData["num"] = num
cpuData["temperature"] = service.MyService.System().GetCPUTemperature() cpuData["temperature"] = service.MyService.System().GetCPUTemperature()
cpuData["power"] = service.MyService.System().GetCPUPower() cpuData["power"] = service.MyService.System().GetCPUPower()
cpuData["model"] = cpuModel
memInfo := service.MyService.System().GetMemInfo() memInfo := service.MyService.System().GetMemInfo()
service.MyService.Notify().SendAllHardwareStatusBySocket(memInfo, cpuData, newNet) service.MyService.Notify().SendAllHardwareStatusBySocket(memInfo, cpuData, newNet)
} }
// func MonitoryUSB() { // func MonitoryUSB() {
@@ -88,7 +97,7 @@ func SendAllHardwareStatusBySocket() {
// conn := new(netlink.UEventConn) // conn := new(netlink.UEventConn)
// if err := conn.Connect(netlink.UdevEvent); err != nil { // if err := conn.Connect(netlink.UdevEvent); err != nil {
// loger.Error("udev err", zap.Any("Unable to connect to Netlink Kobject UEvent socket", err)) // logger.Error("udev err", zap.Any("Unable to connect to Netlink Kobject UEvent socket", err))
// } // }
// defer conn.Close() // defer conn.Close()
@@ -113,7 +122,7 @@ func SendAllHardwareStatusBySocket() {
// continue // continue
// } // }
// case err := <-errors: // case err := <-errors:
// loger.Error("udev err", zap.Any("err", err)) // logger.Error("udev err", zap.Any("err", err))
// } // }
// } // }

View File

@@ -1,8 +1,10 @@
package route package route
import ( import (
"os"
"github.com/IceWhaleTech/CasaOS-Common/middleware"
"github.com/IceWhaleTech/CasaOS-Common/utils/jwt" "github.com/IceWhaleTech/CasaOS-Common/utils/jwt"
"github.com/IceWhaleTech/CasaOS/middleware"
"github.com/IceWhaleTech/CasaOS/pkg/config" "github.com/IceWhaleTech/CasaOS/pkg/config"
v1 "github.com/IceWhaleTech/CasaOS/route/v1" v1 "github.com/IceWhaleTech/CasaOS/route/v1"
@@ -11,13 +13,22 @@ import (
) )
func InitRouter() *gin.Engine { func InitRouter() *gin.Engine {
ginMode := gin.ReleaseMode
if config.ServerInfo.RunMode != "" {
ginMode = config.ServerInfo.RunMode
}
if os.Getenv(gin.EnvGinMode) != "" {
ginMode = os.Getenv(gin.EnvGinMode)
}
gin.SetMode(ginMode)
r := gin.Default() r := gin.New()
r.Use(gin.Recovery())
r.Use(middleware.Cors()) r.Use(middleware.Cors())
r.Use(middleware.WriteLog())
r.Use(gzip.Gzip(gzip.DefaultCompression)) r.Use(gzip.Gzip(gzip.DefaultCompression))
gin.SetMode(config.ServerInfo.RunMode) if ginMode != gin.ReleaseMode {
r.Use(middleware.WriteLog())
}
// r.StaticFS("/ui", http.FS(web.Static)) // r.StaticFS("/ui", http.FS(web.Static))
// r.GET("/", WebUIHome) // r.GET("/", WebUIHome)
@@ -35,7 +46,7 @@ func InitRouter() *gin.Engine {
// r.GET("/v1/users/image", v1.GetUserImage) // r.GET("/v1/users/image", v1.GetUserImage)
// r.GET("/v1/users/status", v1.GetUserStatus) //init/check // r.GET("/v1/users/status", v1.GetUserStatus) //init/check
//r.GET("/v1/guide/check", v1.GetGuideCheck) // /v1/sys/guide_check // r.GET("/v1/guide/check", v1.GetGuideCheck) // /v1/sys/guide_check
r.GET("/v1/sys/debug", v1.GetSystemConfigDebug) // //debug r.GET("/v1/sys/debug", v1.GetSystemConfigDebug) // //debug
r.GET("/v1/sys/socket-port", v1.GetSystemSocketPort) //sys/socket_port r.GET("/v1/sys/socket-port", v1.GetSystemSocketPort) //sys/socket_port
@@ -47,85 +58,23 @@ func InitRouter() *gin.Engine {
v1Group.Use(jwt.ExceptLocalhost()) v1Group.Use(jwt.ExceptLocalhost())
{ {
// v1UsersGroup := v1Group.Group("/users")
// v1UsersGroup.Use()
// {
// v1UsersGroup.GET("/current", v1.GetUserInfo)
// v1UsersGroup.PUT("/current", v1.PutUserInfo)
// v1UsersGroup.PUT("/current/password", v1.PutUserPassword)
// v1UsersGroup.GET("/current/custom/:key", v1.GetUserCustomConf)
// v1UsersGroup.POST("/current/custom/:key", v1.PostUserCustomConf)
// v1UsersGroup.DELETE("/current/custom/:key", v1.DeleteUserCustomConf)
// v1UsersGroup.POST("/current/image/:key", v1.PostUserUploadImage)
// v1UsersGroup.PUT("/current/image/:key", v1.PutUserImage)
// //v1UserGroup.POST("/file/image/:key", v1.PostUserFileImage)
// v1UsersGroup.DELETE("/current/image", v1.DeleteUserImage)
// //v1UserGroup.PUT("/avatar", v1.PutUserAvatar)
// //v1UserGroup.GET("/avatar", v1.GetUserAvatar)
// v1UsersGroup.DELETE("/:id", v1.DeleteUser)
// v1UsersGroup.GET("/:username", v1.GetUserInfoByUsername)
// v1UsersGroup.DELETE("", v1.DeleteUserAll)
// }
v1AppsGroup := v1Group.Group("/apps")
v1AppsGroup.Use()
{
v1AppsGroup.GET("", v1.AppList) //list
v1AppsGroup.GET("/:id", v1.AppInfo)
}
v1ContainerGroup := v1Group.Group("/container")
v1ContainerGroup.Use()
{
v1ContainerGroup.GET("", v1.MyAppList) ///my/list
v1ContainerGroup.GET("/usage", v1.AppUsageList)
v1ContainerGroup.GET("/:id", v1.ContainerUpdateInfo) ///update/:id/info
v1ContainerGroup.GET("/:id/logs", v1.ContainerLog) // /app/logs/:id
v1ContainerGroup.GET("/networks", v1.GetDockerNetworks) //app/install/config
v1ContainerGroup.GET("/:id/state", v1.GetContainerState) //app/state/:id ?state=install_progress
// there are problems, temporarily do not deal with
v1ContainerGroup.GET("/:id/terminal", v1.DockerTerminal) //app/terminal/:id
v1ContainerGroup.POST("", v1.InstallApp) //app/install
//v1ContainerGroup.GET("/:id", v1.ContainerInfo) // /app/info/:id
v1ContainerGroup.PUT("/:id", v1.UpdateSetting) ///update/:id/setting
v1ContainerGroup.PUT("/:id/state", v1.ChangAppState) // /app/state/:id
v1ContainerGroup.DELETE("/:id", v1.UnInstallApp) //app/uninstall/:id
//Not used
v1ContainerGroup.PUT("/:id/latest", v1.PutAppUpdate)
//Not used
v1ContainerGroup.POST("/share", v1.ShareAppFile)
v1ContainerGroup.GET("/info", v1.GetcontainerInfo)
v1ContainerGroup.PUT("/info", v1.PutcontainerInfo)
}
v1AppCategoriesGroup := v1Group.Group("/app-categories")
v1AppCategoriesGroup.Use()
{
v1AppCategoriesGroup.GET("", v1.CategoryList)
}
v1SysGroup := v1Group.Group("/sys") v1SysGroup := v1Group.Group("/sys")
v1SysGroup.Use() v1SysGroup.Use()
{ {
v1SysGroup.GET("/version", v1.GetSystemCheckVersion) //version/check v1SysGroup.GET("/version", v1.GetSystemCheckVersion) // version/check
v1SysGroup.POST("/update", v1.SystemUpdate) v1SysGroup.POST("/update", v1.SystemUpdate)
v1SysGroup.GET("/hardware", v1.GetSystemHardwareInfo) //hardware/info v1SysGroup.GET("/hardware", v1.GetSystemHardwareInfo) // hardware/info
v1SysGroup.GET("/wsssh", v1.WsSsh) v1SysGroup.GET("/wsssh", v1.WsSsh)
v1SysGroup.POST("/ssh-login", v1.PostSshLogin) v1SysGroup.POST("/ssh-login", v1.PostSshLogin)
//v1SysGroup.GET("/config", v1.GetSystemConfig) //delete // v1SysGroup.GET("/config", v1.GetSystemConfig) //delete
//v1SysGroup.POST("/config", v1.PostSetSystemConfig) // v1SysGroup.POST("/config", v1.PostSetSystemConfig)
v1SysGroup.GET("/logs", v1.GetCasaOSErrorLogs) //error/logs v1SysGroup.GET("/logs", v1.GetCasaOSErrorLogs) // error/logs
//v1SysGroup.GET("/widget/config", v1.GetWidgetConfig)//delete // v1SysGroup.GET("/widget/config", v1.GetWidgetConfig)//delete
//v1SysGroup.POST("/widget/config", v1.PostSetWidgetConfig)//delete // v1SysGroup.POST("/widget/config", v1.PostSetWidgetConfig)//delete
v1SysGroup.POST("/stop", v1.PostKillCasaOS) v1SysGroup.POST("/stop", v1.PostKillCasaOS)
@@ -135,37 +84,34 @@ func InitRouter() *gin.Engine {
// v1SysGroup.GET("/disk", v1.GetSystemDiskInfo) // v1SysGroup.GET("/disk", v1.GetSystemDiskInfo)
// v1SysGroup.GET("/network", v1.GetSystemNetInfo) // v1SysGroup.GET("/network", v1.GetSystemNetInfo)
v1SysGroup.PUT("/usb-auto-mount", v1.PutSystemUSBAutoMount) ///sys/usb/:status
v1SysGroup.GET("/usb-auto-mount", v1.GetSystemUSBAutoMount) ///sys/usb/status
v1SysGroup.GET("/server-info", nil) v1SysGroup.GET("/server-info", nil)
v1SysGroup.PUT("/server-info", nil) v1SysGroup.PUT("/server-info", nil)
v1SysGroup.GET("/apps-state", v1.GetSystemAppsStatus) // v1SysGroup.GET("/port", v1.GetCasaOSPort)
//v1SysGroup.GET("/port", v1.GetCasaOSPort) // v1SysGroup.PUT("/port", v1.PutCasaOSPort)
//v1SysGroup.PUT("/port", v1.PutCasaOSPort)
v1SysGroup.GET("/proxy", v1.GetSystemProxy) v1SysGroup.GET("/proxy", v1.GetSystemProxy)
v1SysGroup.PUT("/state/:state", v1.PutSystemState)
} }
v1PortGroup := v1Group.Group("/port") v1PortGroup := v1Group.Group("/port")
v1PortGroup.Use() v1PortGroup.Use()
{ {
v1PortGroup.GET("/", v1.GetPort) //app/port v1PortGroup.GET("/", v1.GetPort) // app/port
v1PortGroup.GET("/state/:port", v1.PortCheck) //app/check/:port v1PortGroup.GET("/state/:port", v1.PortCheck) // app/check/:port
} }
v1FileGroup := v1Group.Group("/file") v1FileGroup := v1Group.Group("/file")
v1FileGroup.Use() v1FileGroup.Use()
{ {
v1FileGroup.GET("", v1.GetDownloadSingleFile) //download/:path v1FileGroup.GET("", v1.GetDownloadSingleFile) // download/:path
v1FileGroup.POST("", v1.PostCreateFile) v1FileGroup.POST("", v1.PostCreateFile)
v1FileGroup.PUT("", v1.PutFileContent) v1FileGroup.PUT("", v1.PutFileContent)
v1FileGroup.PUT("/name", v1.RenamePath) v1FileGroup.PUT("/name", v1.RenamePath)
//file/rename // file/rename
v1FileGroup.GET("/content", v1.GetFilerContent) //file/read v1FileGroup.GET("/content", v1.GetFilerContent) // file/read
//File uploads need to be handled separately, and will not be modified here // File uploads need to be handled separately, and will not be modified here
v1FileGroup.POST("/upload", v1.PostFileUpload) v1FileGroup.POST("/upload", v1.PostFileUpload)
v1FileGroup.GET("/upload", v1.GetFileUpload) v1FileGroup.GET("/upload", v1.GetFileUpload)
//v1FileGroup.GET("/download", v1.UserFileDownloadCommonService) // v1FileGroup.GET("/download", v1.UserFileDownloadCommonService)
} }
v1FolderGroup := v1Group.Group("/folder") v1FolderGroup := v1Group.Group("/folder")
v1FolderGroup.Use() v1FolderGroup.Use()
@@ -178,9 +124,9 @@ func InitRouter() *gin.Engine {
v1BatchGroup.Use() v1BatchGroup.Use()
{ {
v1BatchGroup.DELETE("", v1.DeleteFile) //file/delete v1BatchGroup.DELETE("", v1.DeleteFile) // file/delete
v1BatchGroup.DELETE("/:id/task", v1.DeleteOperateFileOrDir) v1BatchGroup.DELETE("/:id/task", v1.DeleteOperateFileOrDir)
v1BatchGroup.POST("/task", v1.PostOperateFileOrDir) //file/operate v1BatchGroup.POST("/task", v1.PostOperateFileOrDir) // file/operate
v1BatchGroup.GET("", v1.GetDownloadFile) v1BatchGroup.GET("", v1.GetDownloadFile)
} }
v1ImageGroup := v1Group.Group("/image") v1ImageGroup := v1Group.Group("/image")
@@ -211,8 +157,10 @@ func InitRouter() *gin.Engine {
v1NotifyGroup.Use() v1NotifyGroup.Use()
{ {
v1NotifyGroup.POST("/:path", v1.PostNotifyMessage) v1NotifyGroup.POST("/:path", v1.PostNotifyMessage)
//merge to system // merge to system
v1NotifyGroup.POST("/system_status", v1.PostSystemStatusNotify) v1NotifyGroup.POST("/system_status", v1.PostSystemStatusNotify)
v1NotifyGroup.POST("/install_app", v1.PostInstallAppNotify)
v1NotifyGroup.POST("/uninstall_app", v1.PostUninstallAppNotify)
} }
} }
return r return r

View File

@@ -14,15 +14,14 @@ import (
"strconv" "strconv"
"time" "time"
"github.com/IceWhaleTech/CasaOS-Common/utils/port"
"github.com/IceWhaleTech/CasaOS/model/notify" "github.com/IceWhaleTech/CasaOS/model/notify"
"github.com/IceWhaleTech/CasaOS/pkg/config" "github.com/IceWhaleTech/CasaOS/pkg/config"
"github.com/IceWhaleTech/CasaOS/pkg/utils/port"
"github.com/IceWhaleTech/CasaOS/service" "github.com/IceWhaleTech/CasaOS/service"
f "github.com/ambelovsky/gosf" f "github.com/ambelovsky/gosf"
) )
func SocketInit(msg chan notify.Message) { func SocketInit(msg chan notify.Message) {
// set socket port // set socket port
socketPort := 0 socketPort := 0
if len(config.ServerInfo.SocketPort) == 0 { if len(config.ServerInfo.SocketPort) == 0 {
@@ -51,10 +50,9 @@ func SocketInit(msg chan notify.Message) {
f.Broadcast("", v.Path, &v.Msg) f.Broadcast("", v.Path, &v.Msg)
time.Sleep(time.Millisecond * 100) time.Sleep(time.Millisecond * 100)
} }
}(msg) }(msg)
f.Startup(map[string]interface{}{ f.Startup(map[string]interface{}{
"port": socketPort}) "port": socketPort,
})
} }

View File

@@ -1,317 +0,0 @@
package v1
import (
"encoding/json"
"fmt"
"io/ioutil"
"strconv"
"github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/pkg/utils/command"
"github.com/IceWhaleTech/CasaOS/pkg/utils/common_err"
"github.com/IceWhaleTech/CasaOS/pkg/utils/file"
port2 "github.com/IceWhaleTech/CasaOS/pkg/utils/port"
"github.com/IceWhaleTech/CasaOS/service"
"github.com/gin-gonic/gin"
)
// @Summary 获取远程列表
// @Produce application/json
// @Accept application/json
// @Tags app
// @Param index query int false "页码"
// @Param size query int false "每页数量"
// @Param category_id query int false "分类id"
// @Param type query string false "rank,new"
// @Param key query string false "search key"
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /app/list [get]
func AppList(c *gin.Context) {
//service.MyService.Docker().DockerContainerCommit("test2")
index := c.DefaultQuery("index", "1")
size := c.DefaultQuery("size", "10000")
t := c.DefaultQuery("type", "rank")
categoryId := c.DefaultQuery("category_id", "0")
key := c.DefaultQuery("key", "")
if len(index) == 0 || len(size) == 0 || len(t) == 0 || len(categoryId) == 0 {
c.JSON(common_err.CLIENT_ERROR, &model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS)})
return
}
collection, err := service.MyService.Casa().GetServerList(index, size, t, categoryId, key)
if err != nil {
c.JSON(common_err.SERVICE_ERROR, &model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: err.Error()})
return
}
// for i := 0; i < len(recommend); i++ {
// ct, _ := service.MyService.Docker().DockerListByImage(recommend[i].Image, recommend[i].ImageVersion)
// if ct != nil {
// recommend[i].State = ct.State
// }
// }
// for i := 0; i < len(list); i++ {
// ct, _ := service.MyService.Docker().DockerListByImage(list[i].Image, list[i].ImageVersion)
// if ct != nil {
// list[i].State = ct.State
// }
// }
// for i := 0; i < len(community); i++ {
// ct, _ := service.MyService.Docker().DockerListByImage(community[i].Image, community[i].ImageVersion)
// if ct != nil {
// community[i].State = ct.State
// }
// }
data := make(map[string]interface{}, 3)
data["recommend"] = collection.Recommend
data["list"] = collection.List
data["community"] = collection.Community
c.JSON(common_err.SUCCESS, &model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: data})
}
// @Summary 获取一个可用端口
// @Produce application/json
// @Accept application/json
// @Tags app
// @Param type query string true "端口类型 udp/tcp"
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /app/getport [get]
func GetPort(c *gin.Context) {
t := c.DefaultQuery("type", "tcp")
var p int
ok := true
for ok {
p, _ = port2.GetAvailablePort(t)
ok = !port2.IsPortAvailable(p, t)
}
// @tiger 这里最好封装成 {'port': ...} 的形式,来体现出参的上下文
c.JSON(common_err.SUCCESS, &model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: p})
}
// @Summary 检查端口是否可用
// @Produce application/json
// @Accept application/json
// @Tags app
// @Param port path int true "端口号"
// @Param type query string true "端口类型 udp/tcp"
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /app/check/{port} [get]
func PortCheck(c *gin.Context) {
p, _ := strconv.Atoi(c.Param("port"))
t := c.DefaultQuery("type", "tcp")
c.JSON(common_err.SUCCESS, &model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: port2.IsPortAvailable(p, t)})
}
// @Summary 我的应用列表
// @Produce application/json
// @Accept application/json
// @Tags app
// @Security ApiKeyAuth
// @Param index query int false "index"
// @Param size query int false "size"
// @Param position query bool false "是否是首页应用"
// @Success 200 {string} string "ok"
// @Router /app/my/list [get]
func MyAppList(c *gin.Context) {
index, _ := strconv.Atoi(c.DefaultQuery("index", "1"))
size, _ := strconv.Atoi(c.DefaultQuery("size", "0"))
position, _ := strconv.ParseBool(c.DefaultQuery("position", "true"))
list, unTranslation := service.MyService.App().GetMyList(index, size, position)
data := make(map[string]interface{}, 2)
data["casaos_apps"] = list
data["local_apps"] = unTranslation
c.JSON(common_err.SUCCESS, &model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: data})
}
// @Summary my app hardware usage list
// @Produce application/json
// @Accept application/json
// @Tags app
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /app/usage [get]
func AppUsageList(c *gin.Context) {
list := service.MyService.App().GetHardwareUsage()
c.JSON(common_err.SUCCESS, &model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: list})
//c.JSON(common_err.SUCCESS, &model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: nil})
}
// @Summary 应用详情
// @Produce application/json
// @Accept application/json
// @Tags app
// @Param id path int true "id"
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /app/appinfo/{id} [get]
func AppInfo(c *gin.Context) {
id := c.Param("id")
language := c.GetHeader("Language")
info, err := service.MyService.Casa().GetServerAppInfo(id, "", language)
if err != nil {
c.JSON(common_err.SERVICE_ERROR, &model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: err.Error()})
return
}
if info.NetworkModel != "host" {
for i := 0; i < len(info.Ports); i++ {
if p, _ := strconv.Atoi(info.Ports[i].ContainerPort); port2.IsPortAvailable(p, info.Ports[i].Protocol) {
info.Ports[i].CommendPort = strconv.Itoa(p)
} else {
if info.Ports[i].Protocol == "tcp" {
if p, err := port2.GetAvailablePort("tcp"); err == nil {
info.Ports[i].CommendPort = strconv.Itoa(p)
}
} else if info.Ports[i].Protocol == "upd" {
if p, err := port2.GetAvailablePort("udp"); err == nil {
info.Ports[i].CommendPort = strconv.Itoa(p)
}
}
}
if info.Ports[i].Type == 0 {
info.PortMap = info.Ports[i].CommendPort
}
}
} else {
for i := 0; i < len(info.Ports); i++ {
if info.Ports[i].Type == 0 {
info.PortMap = info.Ports[i].ContainerPort
break
}
}
}
for i := 0; i < len(info.Devices); i++ {
if !file.CheckNotExist(info.Devices[i].ContainerPath) {
info.Devices[i].Path = info.Devices[i].ContainerPath
}
}
// if len(info.Tip) > 0 {
// info.Tip = env_helper.ReplaceStringDefaultENV(info.Tip)
// }
// portOrder := func(c1, c2 *model.Ports) bool {
// return c1.Type < c2.Type
// }
// envOrder := func(c1, c2 *model.Envs) bool {
// return c1.Type < c2.Type
// }
// volOrder := func(c1, c2 *model.Volume) bool {
// return c1.Type < c2.Type
// }
// devOrder := func(c1, c2 *model.Devices) bool {
// return c1.Type < c2.Type
// }
//sort
// if info.NetworkModel != "host" {
// sort.PortsSort(portOrder).Sort(info.Configures.TcpPorts)
// sort.PortsSort(portOrder).Sort(info.Configures.UdpPorts)
// }
// sort.EnvSort(envOrder).Sort(info.Envs)
// sort.VolSort(volOrder).Sort(info.Volumes.([]model.PathMap))
// sort.DevSort(devOrder).Sort(info.Devices)
info.Image += ":" + info.ImageVersion
info.MaxMemory = (service.MyService.System().GetMemInfo()["total"]).(uint64) >> 20
c.JSON(common_err.SUCCESS, &model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: info})
}
// @Summary 获取远程分类列表
// @Produce application/json
// @Accept application/json
// @Tags app
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /app/category [get]
func CategoryList(c *gin.Context) {
list, err := service.MyService.Casa().GetServerCategoryList()
if err != nil {
c.JSON(common_err.SERVICE_ERROR, &model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: err.Error()})
return
}
var count uint = 0
for _, category := range list {
count += category.Count
}
rear := append([]model.CategoryList{}, list[0:]...)
list = append(list[:0], model.CategoryList{Count: count, Name: "All", Font: "apps"})
list = append(list, rear...)
c.JSON(common_err.SUCCESS, &model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: list})
}
// @Summary 分享该应用配置
// @Produce application/json
// @Accept application/json
// @Tags app
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /app/share [post]
func ShareAppFile(c *gin.Context) {
str, _ := ioutil.ReadAll(c.Request.Body)
content := service.MyService.Casa().ShareAppFile(str)
c.JSON(common_err.SUCCESS, json.RawMessage(content))
}
func GetcontainerInfo(c *gin.Context) {
// info, err := service.MyService.Docker().GetDockerInfo()
// if err != nil {
// c.JSON(common_err.SERVICE_ERROR, &model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: err.Error()})
// return
// }
daemon := model.DeckerDaemonModel{}
data := make(map[string]interface{}, 1)
data["docker_root_dir"] = ""
if file.Exists("/etc/docker/daemon.json") {
byteResult := file.ReadFullFile("/etc/docker/daemon.json")
err := json.Unmarshal(byteResult, &daemon)
if err != nil {
c.JSON(common_err.CLIENT_ERROR, &model.Result{Success: common_err.CLIENT_ERROR, Message: common_err.GetMsg(common_err.INVALID_PARAMS), Data: err.Error()})
return
}
data["docker_root_dir"] = daemon.Graph
}
c.JSON(common_err.SUCCESS, &model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: data})
}
func PutcontainerInfo(c *gin.Context) {
js := make(map[string]interface{})
err := c.BindJSON(&js)
if err != nil {
c.JSON(common_err.CLIENT_ERROR, &model.Result{Success: common_err.CLIENT_ERROR, Message: common_err.GetMsg(common_err.INVALID_PARAMS), Data: err.Error()})
return
}
dockerRootDir := js["docker_root_dir"].(string)
daemon := model.DeckerDaemonModel{}
if file.Exists("/etc/docker/daemon.json") {
byteResult := file.ReadFullFile("/etc/docker/daemon.json")
err := json.Unmarshal(byteResult, &daemon)
if err != nil {
c.JSON(common_err.CLIENT_ERROR, &model.Result{Success: common_err.CLIENT_ERROR, Message: common_err.GetMsg(common_err.INVALID_PARAMS), Data: err.Error()})
return
}
}
if !file.Exists(dockerRootDir) {
c.JSON(common_err.CLIENT_ERROR, &model.Result{Success: common_err.CLIENT_ERROR, Message: common_err.GetMsg(common_err.DIR_NOT_EXISTS), Data: common_err.GetMsg(common_err.DIR_NOT_EXISTS)})
return
}
daemon.Graph = dockerRootDir
byteMode, _ := json.Marshal(daemon)
file.WriteToPath(byteMode, "/etc/docker", "daemon.json")
fmt.Println(command.ExecResultStr("systemctl daemon-reload"))
fmt.Println(command.ExecResultStr("systemctl restart docker"))
c.JSON(common_err.SUCCESS, &model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: js})
}

File diff suppressed because it is too large Load Diff

View File

@@ -15,12 +15,14 @@ import (
"strings" "strings"
"sync" "sync"
"github.com/IceWhaleTech/CasaOS-Common/utils/logger"
"github.com/IceWhaleTech/CasaOS/model" "github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/pkg/utils/common_err" "github.com/IceWhaleTech/CasaOS/pkg/utils/common_err"
"github.com/IceWhaleTech/CasaOS/pkg/utils/file" "github.com/IceWhaleTech/CasaOS/pkg/utils/file"
"github.com/IceWhaleTech/CasaOS/service" "github.com/IceWhaleTech/CasaOS/service"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
uuid "github.com/satori/go.uuid" uuid "github.com/satori/go.uuid"
"go.uber.org/zap"
) )
// @Summary 读取文件 // @Summary 读取文件
@@ -47,7 +49,7 @@ func GetFilerContent(c *gin.Context) {
}) })
return return
} }
//文件读取任务是将文件内容读取到内存中。 // 文件读取任务是将文件内容读取到内存中。
info, err := ioutil.ReadFile(filePath) info, err := ioutil.ReadFile(filePath)
if err != nil { if err != nil {
c.JSON(common_err.SERVICE_ERROR, model.Result{ c.JSON(common_err.SERVICE_ERROR, model.Result{
@@ -83,7 +85,6 @@ func GetLocalFile(c *gin.Context) {
return return
} }
c.File(path) c.File(path)
return
} }
// @Summary download // @Summary download
@@ -96,7 +97,6 @@ func GetLocalFile(c *gin.Context) {
// @Success 200 {string} string "ok" // @Success 200 {string} string "ok"
// @Router /file/download [get] // @Router /file/download [get]
func GetDownloadFile(c *gin.Context) { func GetDownloadFile(c *gin.Context) {
t := c.Query("format") t := c.Query("format")
files := c.Query("files") files := c.Query("files")
@@ -135,11 +135,11 @@ func GetDownloadFile(c *gin.Context) {
} }
if !info.IsDir() { if !info.IsDir() {
//打开文件 // 打开文件
fileTmp, _ := os.Open(filePath) fileTmp, _ := os.Open(filePath)
defer fileTmp.Close() defer fileTmp.Close()
//获取文件的名称 // 获取文件的名称
fileName := path.Base(filePath) fileName := path.Base(filePath)
c.Header("Content-Disposition", "attachment; filename*=utf-8''"+url2.PathEscape(fileName)) c.Header("Content-Disposition", "attachment; filename*=utf-8''"+url2.PathEscape(fileName))
c.File(filePath) c.File(filePath)
@@ -179,7 +179,6 @@ func GetDownloadFile(c *gin.Context) {
log.Printf("Failed to archive %s: %v", fname, err) log.Printf("Failed to archive %s: %v", fname, err)
} }
} }
} }
func GetDownloadSingleFile(c *gin.Context) { func GetDownloadSingleFile(c *gin.Context) {
@@ -202,7 +201,7 @@ func GetDownloadSingleFile(c *gin.Context) {
defer fileTmp.Close() defer fileTmp.Close()
fileName := path.Base(filePath) fileName := path.Base(filePath)
//c.Header("Content-Disposition", "inline") // c.Header("Content-Disposition", "inline")
c.Header("Content-Disposition", "attachment; filename*=utf-8''"+url2.PathEscape(fileName)) c.Header("Content-Disposition", "attachment; filename*=utf-8''"+url2.PathEscape(fileName))
c.File(filePath) c.File(filePath)
} }
@@ -223,19 +222,6 @@ func DirPath(c *gin.Context) {
for _, v := range shares { for _, v := range shares {
sharesMap[v.Path] = fmt.Sprint(v.ID) sharesMap[v.Path] = fmt.Sprint(v.ID)
} }
if path == "/DATA/AppData" {
list := service.MyService.Docker().DockerContainerList()
apps := make(map[string]string, len(list))
for _, v := range list {
apps[strings.ReplaceAll(v.Names[0], "/", "")] = strings.ReplaceAll(v.Names[0], "/", "")
}
for i := 0; i < len(info); i++ {
if v, ok := apps[info[i].Name]; ok {
info[i].Label = v
info[i].Type = "application"
}
}
}
for i := 0; i < len(info); i++ { for i := 0; i < len(info); i++ {
if v, ok := sharesMap[info[i].Path]; ok { if v, ok := sharesMap[info[i].Path]; ok {
@@ -248,7 +234,7 @@ func DirPath(c *gin.Context) {
info[i].Extensions = ex info[i].Extensions = ex
} }
} }
//Hide the files or folders in operation // Hide the files or folders in operation
fileQueue := make(map[string]string) fileQueue := make(map[string]string)
if len(service.OpStrArr) > 0 { if len(service.OpStrArr) > 0 {
for _, v := range service.OpStrArr { for _, v := range service.OpStrArr {
@@ -361,7 +347,6 @@ func PostCreateFile(c *gin.Context) {
// @Success 200 {string} string "ok" // @Success 200 {string} string "ok"
// @Router /file/upload [get] // @Router /file/upload [get]
func GetFileUpload(c *gin.Context) { func GetFileUpload(c *gin.Context) {
relative := c.Query("relativePath") relative := c.Query("relativePath")
fileName := c.Query("filename") fileName := c.Query("filename")
chunkNumber := c.Query("chunkNumber") chunkNumber := c.Query("chunkNumber")
@@ -405,7 +390,8 @@ func PostFileUpload(c *gin.Context) {
hash := file.GetHashByContent([]byte(fileName)) hash := file.GetHashByContent([]byte(fileName))
if len(path) == 0 { if len(path) == 0 {
c.JSON(common_err.INVALID_PARAMS, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS)}) logger.Error("path should not be empty")
c.JSON(http.StatusBadRequest, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS)})
return return
} }
tempDir := filepath.Join(path, ".temp", hash+strconv.Itoa(totalChunks)) + "/" tempDir := filepath.Join(path, ".temp", hash+strconv.Itoa(totalChunks)) + "/"
@@ -413,47 +399,82 @@ func PostFileUpload(c *gin.Context) {
if fileName != relative { if fileName != relative {
dirPath = strings.TrimSuffix(relative, fileName) dirPath = strings.TrimSuffix(relative, fileName)
tempDir += dirPath tempDir += dirPath
file.MkDir(path + "/" + dirPath) if err := file.MkDir(path + "/" + dirPath); err != nil {
logger.Error("error when trying to create `"+path+"/"+dirPath+"`", zap.Error(err))
c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.SERVICE_ERROR, Message: err.Error()})
return
}
} }
path += "/" + relative path += "/" + relative
if !file.CheckNotExist(tempDir + chunkNumber) { if !file.CheckNotExist(tempDir + chunkNumber) {
file.RMDir(tempDir + chunkNumber) if err := file.RMDir(tempDir + chunkNumber); err != nil {
logger.Error("error when trying to remove existing `"+tempDir+chunkNumber+"`", zap.Error(err))
c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.SERVICE_ERROR, Message: err.Error()})
return
}
} }
if totalChunks > 1 { if totalChunks > 1 {
file.IsNotExistMkDir(tempDir) if err := file.IsNotExistMkDir(tempDir); err != nil {
logger.Error("error when trying to create `"+tempDir+"`", zap.Error(err))
c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.SERVICE_ERROR, Message: err.Error()})
return
}
out, _ := os.OpenFile(tempDir+chunkNumber, os.O_WRONLY|os.O_CREATE, 0644) out, err := os.OpenFile(tempDir+chunkNumber, os.O_WRONLY|os.O_CREATE, 0o644)
defer out.Close()
_, err := io.Copy(out, f)
if err != nil { if err != nil {
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: err.Error()}) logger.Error("error when trying to open `"+tempDir+chunkNumber+"` for creation", zap.Error(err))
c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.SERVICE_ERROR, Message: err.Error()})
return return
} }
} else {
out, _ := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0644)
defer out.Close() defer out.Close()
_, err := io.Copy(out, f)
if err != nil { if _, err := io.Copy(out, f); err != nil { // recommend to use https://github.com/iceber/iouring-go for faster copy
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: err.Error()}) logger.Error("error when trying to write to `"+tempDir+chunkNumber+"`", zap.Error(err))
return c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.SERVICE_ERROR, Message: err.Error()})
}
c.JSON(http.StatusOK, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS)})
return return
} }
fileNum, err := ioutil.ReadDir(tempDir) fileNum, err := ioutil.ReadDir(tempDir)
if err != nil { if err != nil {
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: err.Error()}) logger.Error("error when trying to read number of files under `"+tempDir+"`", zap.Error(err))
c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.SERVICE_ERROR, Message: err.Error()})
return return
} }
if totalChunks == len(fileNum) { if totalChunks == len(fileNum) {
file.SpliceFiles(tempDir, path, totalChunks, 1) if err := file.SpliceFiles(tempDir, path, totalChunks, 1); err != nil {
file.RMDir(tempDir) logger.Error("error when trying to splice files under `"+tempDir+"`", zap.Error(err))
c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.SERVICE_ERROR, Message: err.Error()})
return
} }
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS)}) if err := file.RMDir(tempDir); err != nil {
logger.Error("error when trying to remove `"+tempDir+"`", zap.Error(err))
c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.SERVICE_ERROR, Message: err.Error()})
return
}
}
} else {
out, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0o644)
if err != nil {
logger.Error("error when trying to open `"+path+"` for creation", zap.Error(err))
c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.SERVICE_ERROR, Message: err.Error()})
return
}
defer out.Close()
if _, err := io.Copy(out, f); err != nil { // recommend to use https://github.com/iceber/iouring-go for faster copy
logger.Error("error when trying to write to `"+path+"`", zap.Error(err))
c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: err.Error()})
return
}
}
c.JSON(http.StatusOK, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS)})
} }
// @Summary copy or move file // @Summary copy or move file
@@ -465,7 +486,6 @@ func PostFileUpload(c *gin.Context) {
// @Success 200 {string} string "ok" // @Success 200 {string} string "ok"
// @Router /file/operate [post] // @Router /file/operate [post]
func PostOperateFileOrDir(c *gin.Context) { func PostOperateFileOrDir(c *gin.Context) {
list := model.FileOperate{} list := model.FileOperate{}
c.ShouldBind(&list) c.ShouldBind(&list)
@@ -515,7 +535,6 @@ func PostOperateFileOrDir(c *gin.Context) {
// @Success 200 {string} string "ok" // @Success 200 {string} string "ok"
// @Router /file/delete [delete] // @Router /file/delete [delete]
func DeleteFile(c *gin.Context) { func DeleteFile(c *gin.Context) {
paths := []string{} paths := []string{}
c.ShouldBind(&paths) c.ShouldBind(&paths)
if len(paths) == 0 { if len(paths) == 0 {
@@ -547,7 +566,6 @@ func DeleteFile(c *gin.Context) {
// @Success 200 {string} string "ok" // @Success 200 {string} string "ok"
// @Router /file/update [put] // @Router /file/update [put]
func PutFileContent(c *gin.Context) { func PutFileContent(c *gin.Context) {
fi := model.FileUpdate{} fi := model.FileUpdate{}
c.ShouldBind(&fi) c.ShouldBind(&fi)
@@ -557,7 +575,7 @@ func PutFileContent(c *gin.Context) {
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.FILE_ALREADY_EXISTS, Message: common_err.GetMsg(common_err.FILE_ALREADY_EXISTS)}) c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.FILE_ALREADY_EXISTS, Message: common_err.GetMsg(common_err.FILE_ALREADY_EXISTS)})
return return
} }
//err := os.Remove(path) // err := os.Remove(path)
err := os.RemoveAll(fi.FilePath) err := os.RemoveAll(fi.FilePath)
if err != nil { if err != nil {
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.FILE_DELETE_ERROR, Message: common_err.GetMsg(common_err.FILE_DELETE_ERROR), Data: err}) c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.FILE_DELETE_ERROR, Message: common_err.GetMsg(common_err.FILE_DELETE_ERROR), Data: err})

56
route/v1/notify.go Normal file
View File

@@ -0,0 +1,56 @@
package v1
import (
"net/http"
"github.com/IceWhaleTech/CasaOS-Common/model/notify"
"github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/pkg/utils/common_err"
"github.com/IceWhaleTech/CasaOS/service"
"github.com/gin-gonic/gin"
)
func PostNotifyMessage(c *gin.Context) {
path := c.Param("path")
message := make(map[string]interface{})
if err := c.ShouldBind(&message); err != nil {
c.JSON(http.StatusBadRequest, model.Result{Success: common_err.INVALID_PARAMS, Message: err.Error()})
return
}
service.MyService.Notify().SendNotify(path, message)
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS)})
}
func PostSystemStatusNotify(c *gin.Context) {
message := make(map[string]interface{})
if err := c.ShouldBind(&message); err != nil {
c.JSON(http.StatusBadRequest, model.Result{Success: common_err.INVALID_PARAMS, Message: err.Error()})
return
}
service.MyService.Notify().SettingSystemTempData(message)
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS)})
}
func PostInstallAppNotify(c *gin.Context) {
app := notify.Application{}
if err := c.ShouldBind(&app); err != nil {
c.JSON(http.StatusBadRequest, model.Result{Success: common_err.INVALID_PARAMS, Message: err.Error()})
return
}
service.MyService.Notify().SendInstallAppBySocket(app)
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS)})
}
func PostUninstallAppNotify(c *gin.Context) {
app := notify.Application{}
if err := c.ShouldBind(&app); err != nil {
c.JSON(http.StatusBadRequest, model.Result{Success: common_err.INVALID_PARAMS, Message: err.Error()})
return
}
service.MyService.Notify().SendUninstallAppBySocket(app)
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS)})
}

View File

@@ -1,23 +0,0 @@
package v1
import (
"github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/pkg/utils/common_err"
"github.com/IceWhaleTech/CasaOS/service"
"github.com/gin-gonic/gin"
)
func PostNotifyMessage(c *gin.Context) {
path := c.Param("path")
message := make(map[string]interface{})
c.ShouldBind(&message)
service.MyService.Notify().SendNotify(path, message)
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS)})
}
func PostSystemStatusNotify(c *gin.Context) {
message := make(map[string]interface{})
c.ShouldBind(&message)
service.MyService.Notify().SettingSystemTempData(message)
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS)})
}

View File

@@ -14,12 +14,14 @@ import (
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
"regexp"
"strings" "strings"
"github.com/IceWhaleTech/CasaOS/model" "github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/pkg/samba" "github.com/IceWhaleTech/CasaOS/pkg/samba"
"github.com/IceWhaleTech/CasaOS/pkg/utils/common_err" "github.com/IceWhaleTech/CasaOS/pkg/utils/common_err"
"github.com/IceWhaleTech/CasaOS/pkg/utils/file" "github.com/IceWhaleTech/CasaOS/pkg/utils/file"
"github.com/IceWhaleTech/CasaOS/pkg/utils/ip_helper"
"github.com/IceWhaleTech/CasaOS/service" "github.com/IceWhaleTech/CasaOS/service"
model2 "github.com/IceWhaleTech/CasaOS/service/model" model2 "github.com/IceWhaleTech/CasaOS/service/model"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@@ -125,9 +127,27 @@ func PostSambaConnectionsCreate(c *gin.Context) {
connection.Port = "445" connection.Port = "445"
} }
if connection.Username == "" || connection.Host == "" { if connection.Username == "" || connection.Host == "" {
c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.CHARACTER_LIMIT, Message: common_err.GetMsg(common_err.CHARACTER_LIMIT)})
return
}
if ok, _ := regexp.MatchString("^[a-zA-Z0-9]{4,30}$", connection.Password); !ok {
c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.CHARACTER_LIMIT, Message: common_err.GetMsg(common_err.CHARACTER_LIMIT)})
return
}
if ok, _ := regexp.MatchString("^[a-zA-Z0-9]{4,30}$", connection.Username); !ok {
c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS)}) c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS)})
return return
} }
if !ip_helper.IsIPv4(connection.Host) && !ip_helper.IsIPv6(connection.Host) {
c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS)})
return
}
if ok, _ := regexp.MatchString("^[0-9]{1,6}$", connection.Port); !ok {
c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS)})
return
}
connection.Host = strings.Split(connection.Host, "/")[0] connection.Host = strings.Split(connection.Host, "/")[0]
// check is exists // check is exists
connections := service.MyService.Connections().GetConnectionByHost(connection.Host) connections := service.MyService.Connections().GetConnectionByHost(connection.Host)

96
route/v1/ssh.go Normal file
View File

@@ -0,0 +1,96 @@
package v1
import (
"bytes"
"net/http"
"os/exec"
"strconv"
"time"
"github.com/IceWhaleTech/CasaOS-Common/utils/common_err"
"github.com/IceWhaleTech/CasaOS-Common/utils/logger"
sshHelper "github.com/IceWhaleTech/CasaOS-Common/utils/ssh"
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
"go.uber.org/zap"
"golang.org/x/crypto/ssh"
modelCommon "github.com/IceWhaleTech/CasaOS-Common/model"
)
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool { return true },
HandshakeTimeout: time.Duration(time.Second * 5),
}
func PostSshLogin(c *gin.Context) {
j := make(map[string]string)
c.ShouldBind(&j)
userName := j["username"]
password := j["password"]
port := j["port"]
if userName == "" || password == "" || port == "" {
c.JSON(common_err.CLIENT_ERROR, modelCommon.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS), Data: "Username or password or port is empty"})
return
}
_, err := sshHelper.NewSshClient(userName, password, port)
if err != nil {
c.JSON(common_err.CLIENT_ERROR, modelCommon.Result{Success: common_err.CLIENT_ERROR, Message: common_err.GetMsg(common_err.CLIENT_ERROR), Data: "Please check if the username and port are correct, and make sure that ssh server is installed."})
logger.Error("connect ssh error", zap.Any("error", err))
return
}
c.JSON(common_err.SUCCESS, modelCommon.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS)})
}
func WsSsh(c *gin.Context) {
_, e := exec.LookPath("ssh")
if e != nil {
c.JSON(common_err.SERVICE_ERROR, modelCommon.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: "ssh server not found"})
return
}
userName := c.Query("username")
password := c.Query("password")
port := c.Query("port")
wsConn, _ := upgrader.Upgrade(c.Writer, c.Request, nil)
logBuff := new(bytes.Buffer)
quitChan := make(chan bool, 3)
// user := ""
// password := ""
var login int = 1
cols, _ := strconv.Atoi(c.DefaultQuery("cols", "200"))
rows, _ := strconv.Atoi(c.DefaultQuery("rows", "32"))
var client *ssh.Client
for login != 0 {
var err error
if userName == "" || password == "" || port == "" {
wsConn.WriteMessage(websocket.TextMessage, []byte("username or password or port is empty"))
}
client, err = sshHelper.NewSshClient(userName, password, port)
if err != nil && client == nil {
wsConn.WriteMessage(websocket.TextMessage, []byte(err.Error()))
wsConn.WriteMessage(websocket.TextMessage, []byte("\r\n\x1b[0m"))
} else {
login = 0
}
}
if client != nil {
defer client.Close()
}
ssConn, _ := sshHelper.NewSshConn(cols, rows, client)
defer ssConn.Close()
go ssConn.ReceiveWsMsg(wsConn, logBuff, quitChan)
go ssConn.SendComboOutput(wsConn, quitChan)
go ssConn.SessionWait(quitChan)
<-quitChan
}

View File

@@ -12,10 +12,11 @@ import (
"time" "time"
"unsafe" "unsafe"
http2 "github.com/IceWhaleTech/CasaOS-Common/utils/http"
"github.com/IceWhaleTech/CasaOS-Common/utils/port"
"github.com/IceWhaleTech/CasaOS/model" "github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/pkg/config" "github.com/IceWhaleTech/CasaOS/pkg/config"
"github.com/IceWhaleTech/CasaOS/pkg/utils/common_err" "github.com/IceWhaleTech/CasaOS/pkg/utils/common_err"
port2 "github.com/IceWhaleTech/CasaOS/pkg/utils/port"
"github.com/IceWhaleTech/CasaOS/pkg/utils/version" "github.com/IceWhaleTech/CasaOS/pkg/utils/version"
"github.com/IceWhaleTech/CasaOS/service" "github.com/IceWhaleTech/CasaOS/service"
model2 "github.com/IceWhaleTech/CasaOS/service/model" model2 "github.com/IceWhaleTech/CasaOS/service/model"
@@ -126,7 +127,7 @@ func PutCasaOSPort(c *gin.Context) {
json := make(map[string]string) json := make(map[string]string)
c.ShouldBind(&json) c.ShouldBind(&json)
portStr := json["port"] portStr := json["port"]
port, err := strconv.Atoi(portStr) portNumber, err := strconv.Atoi(portStr)
if err != nil { if err != nil {
c.JSON(common_err.SERVICE_ERROR, c.JSON(common_err.SERVICE_ERROR,
model.Result{ model.Result{
@@ -136,7 +137,7 @@ func PutCasaOSPort(c *gin.Context) {
return return
} }
isAvailable := port2.IsPortAvailable(port, "tcp") isAvailable := port.IsPortAvailable(portNumber, "tcp")
if !isAvailable { if !isAvailable {
c.JSON(common_err.SERVICE_ERROR, c.JSON(common_err.SERVICE_ERROR,
model.Result{ model.Result{
@@ -145,7 +146,7 @@ func PutCasaOSPort(c *gin.Context) {
}) })
return return
} }
service.MyService.System().UpSystemPort(strconv.Itoa(port)) service.MyService.System().UpSystemPort(strconv.Itoa(portNumber))
c.JSON(common_err.SUCCESS, c.JSON(common_err.SUCCESS,
model.Result{ model.Result{
Success: common_err.SUCCESS, Success: common_err.SUCCESS,
@@ -164,85 +165,6 @@ func PostKillCasaOS(c *gin.Context) {
os.Exit(0) os.Exit(0)
} }
// @Summary Turn off usb auto-mount
// @Produce application/json
// @Accept application/json
// @Tags sys
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /sys/usb/off [put]
func PutSystemUSBAutoMount(c *gin.Context) {
js := make(map[string]string)
c.ShouldBind(&js)
status := js["state"]
if status == "on" {
service.MyService.System().UpdateUSBAutoMount("True")
service.MyService.System().ExecUSBAutoMountShell("True")
} else {
service.MyService.System().UpdateUSBAutoMount("False")
service.MyService.System().ExecUSBAutoMountShell("False")
}
c.JSON(common_err.SUCCESS,
model.Result{
Success: common_err.SUCCESS,
Message: common_err.GetMsg(common_err.SUCCESS),
})
}
// @Summary Turn off usb auto-mount
// @Produce application/json
// @Accept application/json
// @Tags sys
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /sys/usb [get]
func GetSystemUSBAutoMount(c *gin.Context) {
state := "True"
if config.ServerInfo.USBAutoMount == "False" {
state = "False"
}
c.JSON(common_err.SUCCESS,
model.Result{
Success: common_err.SUCCESS,
Message: common_err.GetMsg(common_err.SUCCESS),
Data: state,
})
}
func GetSystemAppsStatus(c *gin.Context) {
systemAppList := service.MyService.App().GetSystemAppList()
appList := []model2.MyAppList{}
for _, v := range systemAppList {
name := strings.ReplaceAll(v.Names[0], "/", "")
if len(v.Labels["name"]) > 0 {
name = v.Labels["name"]
}
appList = append(appList, model2.MyAppList{
Name: name,
Icon: v.Labels["icon"],
State: v.State,
CustomId: v.Labels["custom_id"],
Id: v.ID,
Port: v.Labels["web"],
Index: v.Labels["index"],
//Order: m.Labels["order"],
Image: v.Image,
Latest: false,
//Type: m.Labels["origin"],
//Slogan: m.Slogan,
//Rely: m.Rely,
Host: v.Labels["host"],
Protocol: v.Labels["protocol"],
})
}
c.JSON(common_err.SUCCESS,
model.Result{
Success: common_err.SUCCESS,
Message: common_err.GetMsg(common_err.SUCCESS),
Data: appList,
})
}
// @Summary get system hardware info // @Summary get system hardware info
// @Produce application/json // @Produce application/json
// @Accept application/json // @Accept application/json
@@ -251,7 +173,6 @@ func GetSystemAppsStatus(c *gin.Context) {
// @Success 200 {string} string "ok" // @Success 200 {string} string "ok"
// @Router /sys/hardware/info [get] // @Router /sys/hardware/info [get]
func GetSystemHardwareInfo(c *gin.Context) { func GetSystemHardwareInfo(c *gin.Context) {
data := make(map[string]string, 1) data := make(map[string]string, 1)
data["drive_model"] = service.MyService.System().GetDeviceTree() data["drive_model"] = service.MyService.System().GetDeviceTree()
c.JSON(common_err.SUCCESS, c.JSON(common_err.SUCCESS,
@@ -270,19 +191,28 @@ func GetSystemHardwareInfo(c *gin.Context) {
// @Success 200 {string} string "ok" // @Success 200 {string} string "ok"
// @Router /sys/utilization [get] // @Router /sys/utilization [get]
func GetSystemUtilization(c *gin.Context) { func GetSystemUtilization(c *gin.Context) {
var data = make(map[string]interface{}, 6) data := make(map[string]interface{})
cpu := service.MyService.System().GetCpuPercent() cpu := service.MyService.System().GetCpuPercent()
num := service.MyService.System().GetCpuCoreNum() num := service.MyService.System().GetCpuCoreNum()
cpuModel := "arm"
if cpu := service.MyService.System().GetCpuInfo(); len(cpu) > 0 {
if strings.Count(strings.ToLower(strings.TrimSpace(cpu[0].ModelName)), "intel") > 0 {
cpuModel = "intel"
} else if strings.Count(strings.ToLower(strings.TrimSpace(cpu[0].ModelName)), "amd") > 0 {
cpuModel = "amd"
}
}
cpuData := make(map[string]interface{}) cpuData := make(map[string]interface{})
cpuData["percent"] = cpu cpuData["percent"] = cpu
cpuData["num"] = num cpuData["num"] = num
cpuData["temperature"] = service.MyService.System().GetCPUTemperature() cpuData["temperature"] = service.MyService.System().GetCPUTemperature()
cpuData["power"] = service.MyService.System().GetCPUPower() cpuData["power"] = service.MyService.System().GetCPUPower()
cpuData["model"] = cpuModel
data["cpu"] = cpuData data["cpu"] = cpuData
data["mem"] = service.MyService.System().GetMemInfo() data["mem"] = service.MyService.System().GetMemInfo()
//拼装网络信息 // 拼装网络信息
netList := service.MyService.System().GetNetInfo() netList := service.MyService.System().GetNetInfo()
newNet := []model.IOCountersStat{} newNet := []model.IOCountersStat{}
nets := service.MyService.System().GetNet(true) nets := service.MyService.System().GetNet(true)
@@ -299,7 +229,9 @@ func GetSystemUtilization(c *gin.Context) {
} }
data["net"] = newNet data["net"] = newNet
for k, v := range service.MyService.Notify().GetSystemTempMap() {
data[k] = v
}
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: data}) c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: data})
} }
@@ -311,7 +243,6 @@ func GetSystemUtilization(c *gin.Context) {
// @Success 200 {string} string "ok" // @Success 200 {string} string "ok"
// @Router /sys/socket/port [get] // @Router /sys/socket/port [get]
func GetSystemSocketPort(c *gin.Context) { func GetSystemSocketPort(c *gin.Context) {
c.JSON(common_err.SUCCESS, c.JSON(common_err.SUCCESS,
model.Result{ model.Result{
Success: common_err.SUCCESS, Success: common_err.SUCCESS,
@@ -334,7 +265,6 @@ func GetSystemCupInfo(c *gin.Context) {
data["percent"] = cpu data["percent"] = cpu
data["num"] = num data["num"] = num
c.JSON(http.StatusOK, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: data}) c.JSON(http.StatusOK, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: data})
} }
// @Summary get mem info // @Summary get mem info
@@ -347,7 +277,6 @@ func GetSystemCupInfo(c *gin.Context) {
func GetSystemMemInfo(c *gin.Context) { func GetSystemMemInfo(c *gin.Context) {
mem := service.MyService.System().GetMemInfo() mem := service.MyService.System().GetMemInfo()
c.JSON(http.StatusOK, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: mem}) c.JSON(http.StatusOK, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: mem})
} }
// @Summary get disk info // @Summary get disk info
@@ -389,7 +318,7 @@ func GetSystemNetInfo(c *gin.Context) {
func GetSystemProxy(c *gin.Context) { func GetSystemProxy(c *gin.Context) {
url := c.Query("url") url := c.Query("url")
resp, err := http.Get(url) resp, err := http2.Get(url, 30*time.Second)
if err != nil { if err != nil {
return return
} }
@@ -404,3 +333,49 @@ func GetSystemProxy(c *gin.Context) {
// 复制转发的响应Body到响应Body // 复制转发的响应Body到响应Body
io.Copy(c.Writer, ioutil.NopCloser(bytes.NewBuffer(rda))) io.Copy(c.Writer, ioutil.NopCloser(bytes.NewBuffer(rda)))
} }
func PutSystemState(c *gin.Context) {
state := c.Param("state")
if state == "off" {
service.MyService.System().SystemShutdown()
} else if state == "restart" {
service.MyService.System().SystemReboot()
}
c.JSON(http.StatusOK, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: "The operation will be completed shortly."})
}
// @Summary 获取一个可用端口
// @Produce application/json
// @Accept application/json
// @Tags app
// @Param type query string true "端口类型 udp/tcp"
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /app/getport [get]
func GetPort(c *gin.Context) {
t := c.DefaultQuery("type", "tcp")
var p int
ok := true
for ok {
p, _ = port.GetAvailablePort(t)
ok = !port.IsPortAvailable(p, t)
}
// @tiger 这里最好封装成 {'port': ...} 的形式,来体现出参的上下文
c.JSON(common_err.SUCCESS, &model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: p})
}
// @Summary 检查端口是否可用
// @Produce application/json
// @Accept application/json
// @Tags app
// @Param port path int true "端口号"
// @Param type query string true "端口类型 udp/tcp"
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /app/check/{port} [get]
func PortCheck(c *gin.Context) {
p, _ := strconv.Atoi(c.Param("port"))
t := c.DefaultQuery("type", "tcp")
c.JSON(common_err.SUCCESS, &model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: port.IsPortAvailable(p, t)})
}

View File

@@ -1,480 +0,0 @@
package service
import (
"context"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"runtime"
"strings"
"sync"
"time"
"github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/pkg/config"
"github.com/IceWhaleTech/CasaOS/pkg/utils/command"
"github.com/IceWhaleTech/CasaOS/pkg/utils/loger"
model2 "github.com/IceWhaleTech/CasaOS/service/model"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
client2 "github.com/docker/docker/client"
"github.com/pkg/errors"
uuid "github.com/satori/go.uuid"
"go.uber.org/zap"
"gorm.io/gorm"
)
type AppService interface {
GetMyList(index, size int, position bool) (*[]model2.MyAppList, *[]model2.MyAppList)
SaveContainer(m model2.AppListDBModel)
GetUninstallInfo(id string) model2.AppListDBModel
DeleteApp(id string)
GetContainerInfo(id string) (types.Container, error)
GetAppDBInfo(id string) model2.AppListDBModel
UpdateApp(m model2.AppListDBModel)
GetSimpleContainerInfo(id string) (types.Container, error)
DelAppConfigDir(path string)
GetSystemAppList() []types.Container
GetHardwareUsageStream()
GetHardwareUsage() []model.DockerStatsModel
GetAppStats(id string) string
GetAllDBApps() []model2.AppListDBModel
ImportApplications(casaApp bool)
CheckNewImage()
}
type appStruct struct {
db *gorm.DB
}
func (a *appStruct) CheckNewImage() {
list := MyService.Docker().DockerContainerList()
for _, v := range list {
inspect, err := MyService.Docker().DockerImageInfo(strings.Split(v.Image, ":")[0])
if err != nil {
NewVersionApp[v.ID] = inspect.ID
continue
}
if inspect.ID == v.ImageID {
delete(NewVersionApp, v.ID)
continue
}
NewVersionApp[v.ID] = inspect.ID
}
}
func (a *appStruct) ImportApplications(casaApp bool) {
if casaApp {
list := MyService.App().GetAllDBApps()
for _, app := range list {
info, err := MyService.Docker().DockerContainerInfo(app.CustomId)
if err != nil {
MyService.App().DeleteApp(app.CustomId)
continue
}
//info.NetworkSettings
info.Config.Labels["casaos"] = "casaos"
info.Config.Labels["web"] = app.PortMap
info.Config.Labels["icon"] = app.Icon
info.Config.Labels["desc"] = app.Description
info.Config.Labels["index"] = app.Index
info.Config.Labels["custom_id"] = app.CustomId
info.Name = app.Title
container_id, err := MyService.Docker().DockerContainerCopyCreate(info)
if err != nil {
fmt.Println(err)
continue
}
MyService.App().DeleteApp(app.CustomId)
MyService.Docker().DockerContainerStop(app.CustomId)
MyService.Docker().DockerContainerRemove(app.CustomId, false)
MyService.Docker().DockerContainerStart(container_id)
}
} else {
list := MyService.Docker().DockerContainerList()
for _, app := range list {
info, err := MyService.Docker().DockerContainerInfo(app.ID)
if err != nil || info.Config.Labels["casaos"] == "casaos" {
continue
}
info.Config.Labels["casaos"] = "casaos"
info.Config.Labels["web"] = ""
info.Config.Labels["icon"] = ""
info.Config.Labels["desc"] = ""
info.Config.Labels["index"] = ""
info.Config.Labels["custom_id"] = uuid.NewV4().String()
_, err = MyService.Docker().DockerContainerCopyCreate(info)
if err != nil {
continue
}
}
}
// allcontainer := MyService.Docker().DockerContainerList()
// for _, app := range allcontainer {
// info, err := MyService.Docker().DockerContainerInfo(app.ID)
// if err != nil {
// continue
// }
// MyService.Docker().DockerContainerStop(app.ID)
// MyService.Docker().DockerContainerRemove(app.ID, false)
// //info.NetworkSettings
// info.Config.Labels["custom_id"] = uuid.NewV4().String()
// container_id, err := MyService.Docker().DockerContainerCopyCreate(info)
// if err != nil {
// fmt.Println(err)
// continue
// }
// MyService.Docker().DockerContainerStart(container_id)
//}
}
//获取我的应用列表
func (a *appStruct) GetMyList(index, size int, position bool) (*[]model2.MyAppList, *[]model2.MyAppList) {
cli, err := client2.NewClientWithOpts(client2.FromEnv, client2.WithTimeout(time.Second*5))
if err != nil {
loger.Error("Failed to init client", zap.Any("err", err))
}
defer cli.Close()
// fts := filters.NewArgs()
// fts.Add("label", "casaos=casaos")
//fts.Add("label", "casaos")
//fts.Add("casaos", "casaos")
containers, err := cli.ContainerList(context.Background(), types.ContainerListOptions{All: true})
if err != nil {
loger.Error("Failed to get container_list", zap.Any("err", err))
}
//获取本地数据库应用
unTranslation := []model2.MyAppList{}
list := []model2.MyAppList{}
for _, m := range containers {
if m.Labels["casaos"] == "casaos" {
_, newVersion := NewVersionApp[m.ID]
name := strings.ReplaceAll(m.Names[0], "/", "")
icon := m.Labels["icon"]
if len(m.Labels["name"]) > 0 {
name = m.Labels["name"]
}
if m.Labels["origin"] == "system" {
name = strings.Split(m.Image, ":")[0]
if len(strings.Split(name, "/")) > 1 {
icon = "https://icon.casaos.io/main/all/" + strings.Split(name, "/")[1] + ".png"
}
}
list = append(list, model2.MyAppList{
Name: name,
Icon: icon,
State: m.State,
CustomId: m.Labels["custom_id"],
Id: m.ID,
Port: m.Labels["web"],
Index: m.Labels["index"],
//Order: m.Labels["order"],
Image: m.Image,
Latest: newVersion,
//Type: m.Labels["origin"],
//Slogan: m.Slogan,
//Rely: m.Rely,
Host: m.Labels["host"],
Protocol: m.Labels["protocol"],
})
} else {
unTranslation = append(unTranslation, model2.MyAppList{
Name: strings.ReplaceAll(m.Names[0], "/", ""),
Icon: "",
State: m.State,
CustomId: m.ID,
Id: m.ID,
Port: "",
Latest: false,
Host: "",
Protocol: "",
Image: m.Image,
})
}
}
//lMap := make(map[string]interface{})
// for _, dbModel := range lm {
// if position {
// if dbModel.Position {
// lMap[dbModel.ContainerId] = dbModel
// }
// } else {
// lMap[dbModel.ContainerId] = dbModel
// }
// }
// for _, container := range containers {
// if lMap[container.ID] != nil && container.Labels["origin"] != "system" {
// m := lMap[container.ID].(model2.AppListDBModel)
// if len(m.Label) == 0 {
// m.Label = m.Title
// }
// // info, err := cli.ContainerInspect(context.Background(), container.ID)
// // var tm string
// // if err != nil {
// // tm = time.Now().String()
// // } else {
// // tm = info.State.StartedAt
// //}
// list = append(list, model2.MyAppList{
// Name: m.Label,
// Icon: m.Icon,
// State: container.State,
// CustomId: strings.ReplaceAll(container.Names[0], "/", ""),
// Port: m.PortMap,
// Index: m.Index,
// //UpTime: tm,
// Image: m.Image,
// Slogan: m.Slogan,
// //Rely: m.Rely,
// })
// }
// }
return &list, &unTranslation
}
//system application list
func (a *appStruct) GetSystemAppList() []types.Container {
//获取docker应用
cli, err := client2.NewClientWithOpts(client2.FromEnv)
if err != nil {
loger.Error("Failed to init client", zap.Any("err", err))
}
defer cli.Close()
fts := filters.NewArgs()
fts.Add("label", "origin=system")
containers, err := cli.ContainerList(context.Background(), types.ContainerListOptions{All: true, Filters: fts})
if err != nil {
loger.Error("Failed to get container_list", zap.Any("err", err))
}
//获取本地数据库应用
// var lm []model2.AppListDBModel
// a.db.Table(model2.CONTAINERTABLENAME).Select("title,icon,port_map,`index`,container_id,position,label,slogan,image,volumes").Find(&lm)
//list := []model2.MyAppList{}
//lMap := make(map[string]interface{})
// for _, dbModel := range lm {
// lMap[dbModel.ContainerId] = dbModel
// }
return containers
}
func (a *appStruct) GetAllDBApps() []model2.AppListDBModel {
var lm []model2.AppListDBModel
a.db.Table(model2.CONTAINERTABLENAME).Select("custom_id,title,icon,container_id,label,slogan,image,port_map").Find(&lm)
return lm
}
//获取我的应用列表
func (a *appStruct) GetContainerInfo(id string) (types.Container, error) {
//获取docker应用
cli, err := client2.NewClientWithOpts(client2.FromEnv)
if err != nil {
loger.Error("Failed to init client", zap.Any("err", err))
}
filters := filters.NewArgs()
filters.Add("id", id)
containers, err := cli.ContainerList(context.Background(), types.ContainerListOptions{All: true, Filters: filters})
if err != nil {
loger.Error("Failed to get container_list", zap.Any("err", err))
}
if len(containers) > 0 {
return containers[0], nil
}
return types.Container{}, nil
}
func (a *appStruct) GetSimpleContainerInfo(id string) (types.Container, error) {
//获取docker应用
cli, err := client2.NewClientWithOpts(client2.FromEnv)
if err != nil {
return types.Container{}, err
}
defer cli.Close()
filters := filters.NewArgs()
filters.Add("id", id)
containers, err := cli.ContainerList(context.Background(), types.ContainerListOptions{All: true, Filters: filters})
if err != nil {
return types.Container{}, err
}
if len(containers) > 0 {
return containers[0], nil
}
return types.Container{}, errors.New("container not existent")
}
//获取我的应用列表
func (a *appStruct) GetAppDBInfo(id string) model2.AppListDBModel {
var m model2.AppListDBModel
a.db.Table(model2.CONTAINERTABLENAME).Where("custom_id = ?", id).First(&m)
return m
}
//根据容器id获取镜像名称
func (a *appStruct) GetUninstallInfo(id string) model2.AppListDBModel {
var m model2.AppListDBModel
a.db.Table(model2.CONTAINERTABLENAME).Select("image,version,enable_upnp,ports,envs,volumes,origin").Where("custom_id = ?", id).First(&m)
return m
}
//创建容器成功后保存容器
func (a *appStruct) SaveContainer(m model2.AppListDBModel) {
a.db.Table(model2.CONTAINERTABLENAME).Create(&m)
}
func (a *appStruct) UpdateApp(m model2.AppListDBModel) {
a.db.Table(model2.CONTAINERTABLENAME).Save(&m)
}
func (a *appStruct) DelAppConfigDir(path string) {
command.OnlyExec("source " + config.AppInfo.ShellPath + "/helper.sh ;DelAppConfigDir " + path)
}
func (a *appStruct) DeleteApp(id string) {
a.db.Table(model2.CONTAINERTABLENAME).Where("custom_id = ?", id).Delete(&model2.AppListDBModel{})
}
var dataStats sync.Map
var isFinish bool = false
func (a *appStruct) GetAppStats(id string) string {
cli, err := client2.NewClientWithOpts(client2.FromEnv)
if err != nil {
return ""
}
defer cli.Close()
con, err := cli.ContainerStats(context.Background(), id, false)
if err != nil {
return err.Error()
}
defer con.Body.Close()
c, _ := ioutil.ReadAll(con.Body)
return string(c)
}
func (a *appStruct) GetHardwareUsage() []model.DockerStatsModel {
stream := true
for !isFinish {
if stream {
stream = false
go func() {
a.GetHardwareUsageStream()
}()
}
runtime.Gosched()
}
list := []model.DockerStatsModel{}
dataStats.Range(func(key, value interface{}) bool {
list = append(list, value.(model.DockerStatsModel))
return true
})
return list
}
func (a *appStruct) GetHardwareUsageStream() {
cli, err := client2.NewClientWithOpts(client2.FromEnv)
if err != nil {
return
}
defer cli.Close()
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
fts := filters.NewArgs()
fts.Add("label", "casaos=casaos")
//fts.Add("status", "running")
containers, err := cli.ContainerList(context.Background(), types.ContainerListOptions{All: true, Filters: fts})
if err != nil {
loger.Error("Failed to get container_list", zap.Any("err", err))
}
for i := 0; i < 100; i++ {
if i%10 == 0 {
containers, err = cli.ContainerList(context.Background(), types.ContainerListOptions{All: true, Filters: fts})
if err != nil {
loger.Error("Failed to get container_list", zap.Any("err", err))
continue
}
}
if config.CasaOSGlobalVariables.AppChange {
config.CasaOSGlobalVariables.AppChange = false
dataStats.Range(func(key, value interface{}) bool {
dataStats.Delete(key)
return true
})
}
var temp sync.Map
var wg sync.WaitGroup
for _, v := range containers {
if v.State != "running" {
continue
}
wg.Add(1)
go func(v types.Container, i int) {
defer wg.Done()
stats, err := cli.ContainerStatsOneShot(ctx, v.ID)
if err != nil {
return
}
decode := json.NewDecoder(stats.Body)
var data interface{}
if err := decode.Decode(&data); err == io.EOF {
return
}
m, _ := dataStats.Load(v.ID)
dockerStats := model.DockerStatsModel{}
if m != nil {
dockerStats.Previous = m.(model.DockerStatsModel).Data
}
dockerStats.Data = data
dockerStats.Icon = v.Labels["icon"]
dockerStats.Title = strings.ReplaceAll(v.Names[0], "/", "")
// @tiger - 不建议直接把依赖的数据结构封装返回。
// 如果依赖的数据结构有变化,应该在这里适配或者保存,这样更加对客户端负责
temp.Store(v.ID, dockerStats)
if i == 99 {
stats.Body.Close()
}
}(v, i)
}
wg.Wait()
dataStats = temp
isFinish = true
time.Sleep(time.Second * 1)
}
isFinish = false
cancel()
}
func NewAppService(db *gorm.DB) AppService {
return &appStruct{db: db}
}

View File

@@ -1,266 +1,20 @@
package service package service
import ( import (
"encoding/json"
json2 "encoding/json" json2 "encoding/json"
"fmt"
"sort"
"strconv"
"time" "time"
"github.com/IceWhaleTech/CasaOS/model" "github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/pkg/config" "github.com/IceWhaleTech/CasaOS/pkg/config"
"github.com/IceWhaleTech/CasaOS/pkg/utils/file"
"github.com/IceWhaleTech/CasaOS/pkg/utils/httper" "github.com/IceWhaleTech/CasaOS/pkg/utils/httper"
httper2 "github.com/IceWhaleTech/CasaOS/pkg/utils/httper"
"github.com/IceWhaleTech/CasaOS/pkg/utils/loger"
"github.com/pkg/errors"
"github.com/tidwall/gjson" "github.com/tidwall/gjson"
"go.uber.org/zap"
) )
type CasaService interface { type CasaService interface {
GetServerList(index, size, tp, categoryId, key string) (model.ServerAppListCollection, error)
GetServerCategoryList() (list []model.CategoryList, err error)
GetServerAppInfo(id, t string, language string) (model.ServerAppList, error)
ShareAppFile(body []byte) string
GetCasaosVersion() model.Version GetCasaosVersion() model.Version
AsyncGetServerList() (collection model.ServerAppListCollection, err error)
AsyncGetServerCategoryList() ([]model.CategoryList, error)
} }
type casaService struct { type casaService struct{}
}
func (o *casaService) ShareAppFile(body []byte) string {
head := make(map[string]string)
head["Authorization"] = GetToken()
content := httper2.Post(config.ServerInfo.ServerApi+"/v1/community/add", body, "application/json", head)
return content
}
func (o *casaService) GetServerList(index, size, tp, categoryId, key string) (model.ServerAppListCollection, error) {
keyName := fmt.Sprintf("list_%s_%s_%s_%s_%s", index, size, tp, categoryId, "en")
collection := model.ServerAppListCollection{}
if result, ok := Cache.Get(keyName); ok {
res, ok := result.(string)
if ok {
json2.Unmarshal([]byte(res), &collection)
return collection, nil
}
}
collectionStr := file.ReadFullFile(config.AppInfo.DBPath + "/app_list.json")
err := json2.Unmarshal(collectionStr, &collection)
if err != nil {
loger.Error("marshal error", zap.Any("err", err), zap.Any("content", string(collectionStr)))
collection, err = o.AsyncGetServerList()
if err != nil {
return collection, err
}
}
go o.AsyncGetServerList()
if categoryId != "0" {
categoryInt, _ := strconv.Atoi(categoryId)
nList := []model.ServerAppList{}
for _, v := range collection.List {
if v.CategoryId == categoryInt {
nList = append(nList, v)
}
}
collection.List = nList
nCommunity := []model.ServerAppList{}
for _, v := range collection.Community {
if v.CategoryId == categoryInt {
nCommunity = append(nCommunity, v)
}
}
collection.Community = nCommunity
}
if tp != "name" {
if tp == "new" {
sort.Slice(collection.List, func(i, j int) bool {
return collection.List[i].CreatedAt.After(collection.List[j].CreatedAt)
})
sort.Slice(collection.Community, func(i, j int) bool {
return collection.Community[i].CreatedAt.After(collection.Community[j].CreatedAt)
})
} else if tp == "rank" {
sort.Slice(collection.List, func(i, j int) bool {
return collection.List[i].QueryCount > collection.List[j].QueryCount
})
sort.Slice(collection.Community, func(i, j int) bool {
return collection.Community[i].QueryCount > collection.Community[j].QueryCount
})
}
}
sizeInt, _ := strconv.Atoi(size)
if index != "1" {
indexInt, _ := strconv.Atoi(index)
collection.List = collection.List[(indexInt-1)*sizeInt : indexInt*sizeInt]
collection.Community = collection.Community[(indexInt-1)*sizeInt : indexInt*sizeInt]
} else {
if len(collection.List) > sizeInt {
collection.List = collection.List[:sizeInt]
}
if len(collection.Community) > sizeInt {
collection.Community = collection.Community[:sizeInt]
}
}
if len(collection.List) > 0 {
by, _ := json.Marshal(collection)
Cache.Set(keyName, string(by), time.Minute*10)
}
return collection, nil
}
func (o *casaService) AsyncGetServerList() (collection model.ServerAppListCollection, err error) {
results := file.ReadFullFile(config.AppInfo.DBPath + "/app_list.json")
errr := json2.Unmarshal(results, &collection)
if errr != nil {
loger.Error("marshal error", zap.Any("err", err), zap.Any("content", string(results)))
}
head := make(map[string]string)
head["Authorization"] = GetToken()
listS := httper2.Get(config.ServerInfo.ServerApi+"/v2/app/newlist?index=1&size=1000&rank=name&category_id=0&key=&language=en", head)
listModel := []model.ServerAppList{}
communityModel := []model.ServerAppList{}
recommendModel := []model.ServerAppList{}
err = json2.Unmarshal([]byte(gjson.Get(listS, "data.list").String()), &listModel)
json2.Unmarshal([]byte(gjson.Get(listS, "data.recommend").String()), &recommendModel)
json2.Unmarshal([]byte(gjson.Get(listS, "data.community").String()), &communityModel)
if len(listModel) > 0 {
collection.Community = communityModel
collection.List = listModel
collection.Recommend = recommendModel
collection.Version = o.GetCasaosVersion().Version
var by []byte
by, err = json.Marshal(collection)
if err != nil {
loger.Error("marshal error", zap.Any("err", err))
}
file.WriteToPath(by, config.AppInfo.DBPath, "app_list.json")
}
return
}
// func (o *casaService) GetServerCategoryList() (list []model.ServerCategoryList) {
// keyName := fmt.Sprintf("category_list")
// if result, ok := Cache.Get(keyName); ok {
// res, ok := result.(string)
// if ok {
// json2.Unmarshal([]byte(gjson.Get(res, "data").String()), &list)
// return list
// }
// }
// head := make(map[string]string)
// head["Authorization"] = GetToken()
// listS := httper2.Get(config.ServerInfo.ServerApi+"/v2/app/category", head)
// json2.Unmarshal([]byte(gjson.Get(listS, "data").String()), &list)
// if len(list) > 0 {
// Cache.Set(keyName, listS, time.Hour*24)
// }
// return list
// }
func (o *casaService) GetServerCategoryList() (list []model.CategoryList, err error) {
category := model.ServerCategoryList{}
results := file.ReadFullFile(config.AppInfo.DBPath + "/app_category.json")
err = json2.Unmarshal(results, &category)
if err != nil {
loger.Error("marshal error", zap.Any("err", err), zap.Any("content", string(results)))
return o.AsyncGetServerCategoryList()
}
go o.AsyncGetServerCategoryList()
return category.Item, err
}
func (o *casaService) AsyncGetServerCategoryList() ([]model.CategoryList, error) {
list := model.ServerCategoryList{}
results := file.ReadFullFile(config.AppInfo.DBPath + "/app_category.json")
err := json2.Unmarshal(results, &list)
if err != nil {
loger.Error("marshal error", zap.Any("err", err), zap.Any("content", string(results)))
}
item := []model.CategoryList{}
head := make(map[string]string)
head["Authorization"] = GetToken()
listS := httper2.Get(config.ServerInfo.ServerApi+"/v2/app/category", head)
if len(listS) == 0 {
return item, errors.New("server error")
}
json2.Unmarshal([]byte(gjson.Get(listS, "data").String()), &item)
if len(item) > 0 {
list.Version = o.GetCasaosVersion().Version
list.Item = item
by, err := json.Marshal(list)
if err != nil {
loger.Error("marshal error", zap.Any("err", err))
}
file.WriteToPath(by, config.AppInfo.DBPath, "app_category.json")
}
return item, nil
}
func (o *casaService) GetServerAppInfo(id, t string, language string) (model.ServerAppList, error) {
head := make(map[string]string)
head["Authorization"] = GetToken()
infoS := httper2.Get(config.ServerInfo.ServerApi+"/v2/app/info/"+id+"?t="+t+"&language="+language, head)
info := model.ServerAppList{}
if infoS == "" {
return info, errors.New("server error")
}
err := json2.Unmarshal([]byte(gjson.Get(infoS, "data").String()), &info)
if err != nil {
fmt.Println(infoS)
return info, err
}
return info, nil
}
func GetToken() string {
t := make(chan string)
keyName := "casa_token"
var auth string
if result, ok := Cache.Get(keyName); ok {
auth, ok = result.(string)
if ok {
return auth
}
}
go func() {
str := httper2.Get(config.ServerInfo.ServerApi+"/token", nil)
t <- gjson.Get(str, "data").String()
}()
auth = <-t
Cache.SetDefault(keyName, auth)
return auth
}
/** /**
* @description: get remote version * @description: get remote version

View File

@@ -1,961 +0,0 @@
package service
import (
"bytes"
"context"
"encoding/base64"
"encoding/binary"
json2 "encoding/json"
"fmt"
"github.com/IceWhaleTech/CasaOS/model/notify"
"github.com/pkg/errors"
"go.uber.org/zap"
"github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/pkg/docker"
command2 "github.com/IceWhaleTech/CasaOS/pkg/utils/command"
"github.com/IceWhaleTech/CasaOS/pkg/utils/env_helper"
"github.com/IceWhaleTech/CasaOS/pkg/utils/file"
"github.com/IceWhaleTech/CasaOS/pkg/utils/loger"
//"github.com/containerd/containerd/oci"
"io"
"io/ioutil"
"log"
"os"
"strconv"
"strings"
"time"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/mount"
"github.com/docker/docker/api/types/network"
client2 "github.com/docker/docker/client"
"github.com/docker/go-connections/nat"
)
type DockerService interface {
DockerPullImage(imageName string, icon, name string) error
IsExistImage(imageName string) bool
DockerContainerCreate(m model.CustomizationPostData, id string) (containerId string, err error)
DockerContainerCopyCreate(info *types.ContainerJSON) (containerId string, err error)
DockerContainerStart(name string) error
DockerContainerStats(name string) (string, error)
DockerListByName(name string) (*types.Container, error)
DockerListByImage(image, version string) (*types.Container, error)
DockerContainerInfo(name string) (*types.ContainerJSON, error)
DockerImageRemove(name string) error
DockerContainerRemove(name string, update bool) error
DockerContainerStop(id string) error
DockerContainerUpdateName(name, id string) (err error)
DockerContainerUpdate(m model.CustomizationPostData, id string) (err error)
DockerContainerLog(name string) ([]byte, error)
DockerContainerCommit(name string)
DockerContainerList() []types.Container
DockerNetworkModelList() []types.NetworkResource
DockerImageInfo(image string) (types.ImageInspect, error)
GetNetWorkNameByNetWorkID(id string) (string, error)
ContainerExecShell(container_id string) string
GetDockerInfo() (types.Info, error)
}
type dockerService struct {
rootDir string
}
func (ds *dockerService) DockerContainerList() []types.Container {
cli, err := client2.NewClientWithOpts(client2.FromEnv, client2.WithTimeout(time.Second*5))
if err != nil {
return nil
}
defer cli.Close()
containers, err := cli.ContainerList(context.Background(), types.ContainerListOptions{All: true})
if err != nil {
return containers
}
return containers
}
func (ds *dockerService) ContainerExecShell(container_id string) string {
cli, _ := client2.NewClientWithOpts(client2.FromEnv)
exec, err := cli.ContainerExecCreate(context.Background(), container_id, types.ExecConfig{
User: "1000:1000",
Cmd: []string{"echo -e \"hellow\nworld\" >> /a.txt"},
})
if err != nil {
os.Exit(5)
}
err = cli.ContainerExecStart(context.Background(), exec.ID, types.ExecStartCheck{})
if err != nil {
fmt.Println("exec script error ", err)
}
return exec.ID
}
// 创建默认网络
func DockerNetwork() {
cli, _ := client2.NewClientWithOpts(client2.FromEnv)
defer cli.Close()
d, _ := cli.NetworkList(context.Background(), types.NetworkListOptions{})
for _, resource := range d {
if resource.Name == docker.NETWORKNAME {
return
}
}
cli.NetworkCreate(context.Background(), docker.NETWORKNAME, types.NetworkCreate{})
}
// 根据网络id获取网络名
func (ds *dockerService) GetNetWorkNameByNetWorkID(id string) (string, error) {
cli, _ := client2.NewClientWithOpts(client2.FromEnv)
defer cli.Close()
filter := filters.NewArgs()
filter.Add("id", id)
d, err := cli.NetworkList(context.Background(), types.NetworkListOptions{Filters: filter})
if err == nil && len(d) > 0 {
return d[0].Name, nil
}
return "", err
}
// 拉取镜像
func DockerPull() {
cli, _ := client2.NewClientWithOpts(client2.FromEnv)
defer cli.Close()
authConfig := types.AuthConfig{}
encodedJSON, err := json2.Marshal(authConfig)
fmt.Println(err)
authStr := base64.URLEncoding.EncodeToString(encodedJSON)
reader, err := cli.ImagePull(context.Background(), "swr.cn-north-4.myhuaweicloud.com/root/swr-demo-2048:latest", types.ImagePullOptions{RegistryAuth: authStr})
buf := new(bytes.Buffer)
buf.ReadFrom(reader)
fmt.Println(buf.String())
}
// 拉取镜像
func DockerEx() {
cli, _ := client2.NewClientWithOpts(client2.FromEnv)
defer cli.Close()
importResponse, err := cli.ImageImport(context.Background(), types.ImageImportSource{
Source: strings.NewReader("source"),
SourceName: "image_source",
}, "repository_name:imported", types.ImageImportOptions{
Tag: "imported",
Message: "A message",
Changes: []string{"change1", "change2"},
})
response, err := ioutil.ReadAll(importResponse)
if err != nil {
fmt.Println(err)
}
importResponse.Close()
println(string(response))
if string(response) != "response" {
fmt.Printf("expected response to contain 'response', got %s", string(response))
}
}
//func DockerContainerSize() {
// cli, err := client2.NewClientWithOpts(client2.FromEnv)
// //but := bytes.Buffer{}
// d, err := cli.ContainerExecCreate(context.Background(), "c3adcef92bae648890941ac00e6c4024d7f2959c2e629f0b581d6a19d77b5eda")
// fmt.Println(d)
// st, _ := ioutil.ReadAll(d.Body)
// fmt.Println(string(st))
// if err != nil {
// fmt.Print(err)
// }
//
//}
func (ds *dockerService) DockerImageInfo(image string) (types.ImageInspect, error) {
cli, err := client2.NewClientWithOpts(client2.FromEnv)
if err != nil {
return types.ImageInspect{}, err
}
inspect, _, err := cli.ImageInspectWithRaw(context.Background(), image)
if err != nil {
return inspect, err
}
return inspect, nil
}
func MsqlExec(container string) error {
cli, err := client2.NewClientWithOpts(client2.FromEnv)
ctx := context.Background()
// 执行/bin/bash命令
ir, err := cli.ContainerExecCreate(ctx, container, types.ExecConfig{
AttachStdin: false,
AttachStdout: true,
AttachStderr: true,
Cmd: []string{"date"},
Tty: true,
Env: []string{"aaa=ddd"},
})
err = cli.ContainerExecStart(ctx, ir.ID, types.ExecStartCheck{})
fmt.Println(err)
return err
}
func Exec(container, row, col string) (hr types.HijackedResponse, err error) {
cli, err := client2.NewClientWithOpts(client2.FromEnv)
ctx := context.Background()
// 执行/bin/bash命令
ir, err := cli.ContainerExecCreate(ctx, container, types.ExecConfig{
AttachStdin: true,
AttachStdout: true,
AttachStderr: true,
Env: []string{"COLUMNS=" + col, "LINES=" + row},
Cmd: []string{"/bin/bash"},
Tty: true,
})
if err != nil {
return
}
// 附加到上面创建的/bin/bash进程中
hr, err = cli.ContainerExecAttach(ctx, ir.ID, types.ExecStartCheck{Detach: false, Tty: true})
if err != nil {
return
}
return
}
func DockerLog() {
//cli, err := client2.NewClientWithOpts(client2.FromEnv)
//ctx := context.Background()
//ir, err := cli.ContainerLogs(ctx, "79c6fa382c330b9149e2d28d24f4d2c231cdb8cfc0710c2d268ccee13c5b24f8", types.ContainerLogsOptions{})
//str, err := ioutil.ReadAll(ir)
//fmt.Println(string(str))
//fmt.Println(err)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
client, _ := client2.NewClientWithOpts(client2.FromEnv)
reader, err := client.ContainerLogs(ctx, "79c6fa382c330b9149e2d28d24f4d2c231cdb8cfc0710c2d268ccee13c5b24f8", types.ContainerLogsOptions{})
if err != nil {
log.Fatal(err)
}
_, err = io.Copy(os.Stdout, reader)
if err != nil && err != io.EOF {
log.Fatal(err)
}
}
func DockerLogs() {
cli, err := client2.NewClientWithOpts(client2.FromEnv)
i, err := cli.ContainerLogs(context.Background(), "79c6fa382c330b9149e2d28d24f4d2c231cdb8cfc0710c2d268ccee13c5b24f8", types.ContainerLogsOptions{
ShowStderr: true,
ShowStdout: true,
Timestamps: false,
Follow: true,
Tail: "40",
})
if err != nil {
log.Fatal(err)
}
defer i.Close()
hdr := make([]byte, 8)
for {
_, err := i.Read(hdr)
if err != nil {
log.Fatal(err)
}
var w io.Writer
switch hdr[0] {
case 1:
w = os.Stdout
default:
w = os.Stderr
}
count := binary.BigEndian.Uint32(hdr[4:])
dat := make([]byte, count)
_, err = i.Read(dat)
fmt.Fprint(w, string(dat))
}
}
//正式内容
// 检查镜像是否存在
func (ds *dockerService) IsExistImage(imageName string) bool {
cli, err := client2.NewClientWithOpts(client2.FromEnv)
if err != nil {
return false
}
defer cli.Close()
filter := filters.NewArgs()
filter.Add("reference", imageName)
list, err := cli.ImageList(context.Background(), types.ImageListOptions{Filters: filter})
if err == nil && len(list) > 0 {
return true
}
return false
}
// 安装镜像
func (ds *dockerService) DockerPullImage(imageName string, icon, name string) error {
cli, err := client2.NewClientWithOpts(client2.FromEnv)
if err != nil {
return err
}
defer cli.Close()
out, err := cli.ImagePull(context.Background(), imageName, types.ImagePullOptions{})
if err != nil {
return err
}
defer out.Close()
if err != nil {
return err
}
//io.Copy()
buf := make([]byte, 2048*4)
for {
n, err := out.Read(buf)
if err != nil {
if err != io.EOF {
fmt.Println("read error:", err)
}
break
}
if len(icon) > 0 && len(name) > 0 {
notify := notify.Application{}
notify.Icon = icon
notify.Name = name
notify.State = "PULLING"
notify.Type = "INSTALL"
notify.Finished = false
notify.Success = true
notify.Message = string(buf[:n])
MyService.Notify().SendInstallAppBySocket(notify)
}
}
return err
}
func (ds *dockerService) DockerContainerCopyCreate(info *types.ContainerJSON) (containerId string, err error) {
cli, err := client2.NewClientWithOpts(client2.FromEnv)
if err != nil {
return "", err
}
defer cli.Close()
container, err := cli.ContainerCreate(context.Background(), info.Config, info.HostConfig, &network.NetworkingConfig{info.NetworkSettings.Networks}, nil, info.Name)
if err != nil {
return "", err
}
return container.ID, err
}
// param imageName 镜像名称
// param containerDbId 数据库的id
// param port 容器内部主端口
// param mapPort 容器主端口映射到外部的端口
// param tcp 容器其他tcp端口
// param udp 容器其他udp端口
func (ds *dockerService) DockerContainerCreate(m model.CustomizationPostData, id string) (containerId string, err error) {
if len(m.NetworkModel) == 0 {
m.NetworkModel = "bridge"
}
cli, err := client2.NewClientWithOpts(client2.FromEnv)
if err != nil {
return "", err
}
defer cli.Close()
ports := make(nat.PortSet)
portMaps := make(nat.PortMap)
// ports[nat.Port(fmt.Sprint(m.PortMap)+"/tcp")] = struct{}{}
// if net != "host" {
// portMaps[nat.Port(fmt.Sprint(m.Port)+"/tcp")] = []nat.PortBinding{{HostIP: "", HostPort: m.PortMap}}
// }
//port := ""
for _, portMap := range m.Ports {
// if portMap.CommendPort == m.PortMap && portMap.Protocol == "tcp" || portMap.Protocol == "both" {
// port = portMap.ContainerPort
// }
if portMap.Protocol == "tcp" {
tContainer, _ := strconv.Atoi(portMap.ContainerPort)
if tContainer > 0 {
ports[nat.Port(portMap.ContainerPort+"/tcp")] = struct{}{}
if m.NetworkModel != "host" {
portMaps[nat.Port(portMap.ContainerPort+"/tcp")] = []nat.PortBinding{{HostPort: portMap.CommendPort}}
}
}
} else if portMap.Protocol == "both" {
tContainer, _ := strconv.Atoi(portMap.ContainerPort)
if tContainer > 0 {
ports[nat.Port(portMap.ContainerPort+"/tcp")] = struct{}{}
if m.NetworkModel != "host" {
portMaps[nat.Port(portMap.ContainerPort+"/tcp")] = []nat.PortBinding{{HostPort: portMap.CommendPort}}
}
}
uContainer, _ := strconv.Atoi(portMap.ContainerPort)
if uContainer > 0 {
ports[nat.Port(portMap.ContainerPort+"/udp")] = struct{}{}
if m.NetworkModel != "host" {
portMaps[nat.Port(portMap.ContainerPort+"/udp")] = []nat.PortBinding{{HostPort: portMap.CommendPort}}
}
}
} else {
uContainer, _ := strconv.Atoi(portMap.ContainerPort)
if uContainer > 0 {
ports[nat.Port(portMap.ContainerPort+"/udp")] = struct{}{}
if m.NetworkModel != "host" {
portMaps[nat.Port(portMap.ContainerPort+"/udp")] = []nat.PortBinding{{HostPort: portMap.CommendPort}}
}
}
}
}
var envArr []string
var showENV []string
showENV = append(showENV, "casaos")
for _, e := range m.Envs {
showENV = append(showENV, e.Name)
if strings.HasPrefix(e.Value, "$") {
envArr = append(envArr, e.Name+"="+env_helper.ReplaceDefaultENV(e.Value, MyService.System().GetTimeZone()))
continue
}
if len(e.Value) > 0 {
if e.Value == "port_map" {
envArr = append(envArr, e.Name+"="+m.PortMap)
continue
}
envArr = append(envArr, e.Name+"="+e.Value)
}
}
res := container.Resources{}
if m.CpuShares > 0 {
res.CPUShares = m.CpuShares
}
if m.Memory > 0 {
res.Memory = m.Memory << 20
}
for _, p := range m.Devices {
if len(p.Path) > 0 {
res.Devices = append(res.Devices, container.DeviceMapping{PathOnHost: p.Path, PathInContainer: p.ContainerPath, CgroupPermissions: "rwm"})
}
}
hostConfingBind := []string{}
// volumes bind
volumes := []mount.Mount{}
for _, v := range m.Volumes {
path := v.Path
if len(path) == 0 {
path = docker.GetDir(m.Label, v.Path)
if len(path) == 0 {
continue
}
}
path = strings.ReplaceAll(path, "$AppID", m.Label)
//reg1 := regexp.MustCompile(`([^<>/\\\|:""\*\?]+\.\w+$)`)
//result1 := reg1.FindAllStringSubmatch(path, -1)
//if len(result1) == 0 {
err = file.IsNotExistMkDir(path)
if err != nil {
loger.Error("Failed to create a folder", zap.Any("err", err))
continue
}
//}
// else {
// err = file.IsNotExistCreateFile(path)
// if err != nil {
// ds.log.Error("mkdir error", err)
// continue
// }
// }
volumes = append(volumes, mount.Mount{
Type: mount.TypeBind,
Source: path,
Target: v.ContainerPath,
})
hostConfingBind = append(hostConfingBind, v.Path+":"+v.ContainerPath)
}
rp := container.RestartPolicy{}
if len(m.Restart) > 0 {
rp.Name = m.Restart
}
// healthTest := []string{}
// if len(port) > 0 {
// healthTest = []string{"CMD-SHELL", "curl -f http://localhost:" + port + m.Index + " || exit 1"}
// }
// health := &container.HealthConfig{
// Test: healthTest,
// StartPeriod: 0,
// Retries: 1000,
// }
// fmt.Print(health)
if len(m.HostName) == 0 {
m.HostName = m.Label
}
info, err := cli.ContainerInspect(context.Background(), id)
hostConfig := &container.HostConfig{}
config := &container.Config{}
config.Labels = map[string]string{}
if err == nil {
// info.HostConfig = &container.HostConfig{}
// info.Config = &container.Config{}
// info.NetworkSettings = &types.NetworkSettings{}
hostConfig = info.HostConfig
config = info.Config
if config.Labels["casaos"] == "casaos" {
config.Cmd = m.Cmd
config.Image = m.Image
config.Env = envArr
config.Hostname = m.HostName
config.ExposedPorts = ports
}
} else {
config.Cmd = m.Cmd
config.Image = m.Image
config.Env = envArr
config.Hostname = m.HostName
config.ExposedPorts = ports
}
config.Labels["origin"] = m.Origin
config.Labels["casaos"] = "casaos"
config.Labels["web"] = m.PortMap
config.Labels["icon"] = m.Icon
config.Labels["desc"] = m.Description
config.Labels["index"] = m.Index
config.Labels["custom_id"] = m.CustomId
config.Labels["show_env"] = strings.Join(showENV, ",")
config.Labels["protocol"] = m.Protocol
config.Labels["host"] = m.Host
config.Labels["name"] = m.Label
//container, err := cli.ContainerCreate(context.Background(), info.Config, info.HostConfig, &network.NetworkingConfig{info.NetworkSettings.Networks}, nil, info.Name)
hostConfig.Mounts = volumes
hostConfig.Privileged = m.Privileged
hostConfig.CapAdd = m.CapAdd
hostConfig.NetworkMode = container.NetworkMode(m.NetworkModel)
hostConfig.RestartPolicy = rp
hostConfig.Resources = res
//hostConfig := &container.HostConfig{Resources: res, Mounts: volumes, RestartPolicy: rp, NetworkMode: , Privileged: m.Privileged, CapAdd: m.CapAdd}
//if net != "host" {
hostConfig.PortBindings = portMaps
//}
containerDb, err := cli.ContainerCreate(context.Background(),
config,
hostConfig,
&network.NetworkingConfig{EndpointsConfig: map[string]*network.EndpointSettings{m.NetworkModel: {NetworkID: "", Aliases: []string{}}}},
nil,
m.ContainerName)
if err != nil {
return "", err
}
return containerDb.ID, err
}
// 删除容器
func (ds *dockerService) DockerContainerRemove(name string, update bool) error {
cli, err := client2.NewClientWithOpts(client2.FromEnv)
if err != nil {
return err
}
defer cli.Close()
err = cli.ContainerRemove(context.Background(), name, types.ContainerRemoveOptions{})
//路径处理
if !update {
path := docker.GetDir(name, "/config")
if !file.CheckNotExist(path) {
file.RMDir(path)
}
}
if err != nil {
return err
}
return err
}
// 删除镜像
func (ds *dockerService) DockerImageRemove(name string) error {
cli, err := client2.NewClientWithOpts(client2.FromEnv)
if err != nil {
return err
}
defer cli.Close()
imageList, err := cli.ImageList(context.Background(), types.ImageListOptions{})
imageId := ""
Loop:
for _, ig := range imageList {
for _, i := range ig.RepoTags {
if i == name {
imageId = ig.ID
break Loop
}
}
}
_, err = cli.ImageRemove(context.Background(), imageId, types.ImageRemoveOptions{})
return err
}
func DockerImageRemove(name string) error {
cli, err := client2.NewClientWithOpts(client2.FromEnv)
if err != nil {
return err
}
defer cli.Close()
imageList, err := cli.ImageList(context.Background(), types.ImageListOptions{})
imageId := ""
Loop:
for _, ig := range imageList {
fmt.Println(ig.RepoDigests)
fmt.Println(ig.Containers)
for _, i := range ig.RepoTags {
if i == name {
imageId = ig.ID
break Loop
}
}
}
_, err = cli.ImageRemove(context.Background(), imageId, types.ImageRemoveOptions{})
return err
}
// 停止镜像
func (ds *dockerService) DockerContainerStop(id string) error {
cli, err := client2.NewClientWithOpts(client2.FromEnv)
if err != nil {
return err
}
defer cli.Close()
err = cli.ContainerStop(context.Background(), id, nil)
return err
}
// 启动容器
func (ds *dockerService) DockerContainerStart(name string) error {
cli, err := client2.NewClientWithOpts(client2.FromEnv)
if err != nil {
return err
}
defer cli.Close()
err = cli.ContainerStart(context.Background(), name, types.ContainerStartOptions{})
return err
}
// 查看日志
func (ds *dockerService) DockerContainerLog(name string) ([]byte, error) {
cli, err := client2.NewClientWithOpts(client2.FromEnv)
if err != nil {
return []byte(""), err
}
defer cli.Close()
//body, err := cli.ContainerAttach(context.Background(), name, types.ContainerAttachOptions{Logs: true, Stream: false, Stdin: false, Stdout: false, Stderr: false})
body, err := cli.ContainerLogs(context.Background(), name, types.ContainerLogsOptions{ShowStdout: true, ShowStderr: true})
if err != nil {
return []byte(""), err
}
defer body.Close()
content, err := ioutil.ReadAll(body)
//content, err := ioutil.ReadAll(body)
if err != nil {
return []byte(""), err
}
return content, nil
}
func DockerContainerStats1() error {
cli, err := client2.NewClientWithOpts(client2.FromEnv)
if err != nil {
return err
}
defer cli.Close()
dss, err := cli.ContainerStats(context.Background(), "dockermysql", false)
if err != nil {
return err
}
defer dss.Body.Close()
sts, err := ioutil.ReadAll(dss.Body)
if err != nil {
return err
}
fmt.Println(string(sts))
return nil
}
// 获取容器状态
func (ds *dockerService) DockerContainerStats(name string) (string, error) {
cli, err := client2.NewClientWithOpts(client2.FromEnv)
if err != nil {
return "", err
}
defer cli.Close()
dss, err := cli.ContainerStats(context.Background(), name, false)
if err != nil {
return "", err
}
defer dss.Body.Close()
sts, err := ioutil.ReadAll(dss.Body)
if err != nil {
return "", err
}
return string(sts), nil
}
// 备份容器
func (ds *dockerService) DockerContainerCommit(name string) {
cli, err := client2.NewClientWithOpts(client2.FromEnv)
if err != nil {
fmt.Println(err)
}
defer cli.Close()
d, err := cli.ContainerInspect(context.Background(), name)
dss, err := cli.ContainerCommit(context.Background(), name, types.ContainerCommitOptions{Reference: "test", Config: d.Config})
if err != nil {
fmt.Println(err)
}
fmt.Println(dss)
}
func (ds *dockerService) DockerListByName(name string) (*types.Container, error) {
cli, _ := client2.NewClientWithOpts(client2.FromEnv)
defer cli.Close()
filter := filters.NewArgs()
filter.Add("name", name)
containers, err := cli.ContainerList(context.Background(), types.ContainerListOptions{Filters: filter})
if err != nil {
return &types.Container{}, err
}
if len(containers) == 0 {
return &types.Container{}, errors.New("not found")
}
return &containers[0], nil
}
func (ds *dockerService) DockerListByImage(image, version string) (*types.Container, error) {
cli, _ := client2.NewClientWithOpts(client2.FromEnv)
defer cli.Close()
filter := filters.NewArgs()
filter.Add("ancestor", image+":"+version)
containers, err := cli.ContainerList(context.Background(), types.ContainerListOptions{Filters: filter})
if err != nil {
return nil, err
}
if len(containers) == 0 {
return nil, nil
}
return &containers[0], nil
}
// 获取容器详情
func (ds *dockerService) DockerContainerInfo(name string) (*types.ContainerJSON, error) {
cli, err := client2.NewClientWithOpts(client2.FromEnv)
if err != nil {
return &types.ContainerJSON{}, err
}
defer cli.Close()
d, err := cli.ContainerInspect(context.Background(), name)
if err != nil {
return &types.ContainerJSON{}, err
}
return &d, nil
}
// 更新容器
// param shares cpu优先级
// param containerDbId 数据库的id
// param port 容器内部主端口
// param mapPort 容器主端口映射到外部的端口
// param tcp 容器其他tcp端口
// param udp 容器其他udp端口
func (ds *dockerService) DockerContainerUpdate(m model.CustomizationPostData, id string) (err error) {
cli, err := client2.NewClientWithOpts(client2.FromEnv)
if err != nil {
return err
}
defer cli.Close()
//重启策略
rp := container.RestartPolicy{
Name: "",
MaximumRetryCount: 0,
}
if len(m.Restart) > 0 {
rp.Name = m.Restart
}
res := container.Resources{}
if m.Memory > 0 {
res.Memory = m.Memory * 1024 * 1024
res.MemorySwap = -1
}
if m.CpuShares > 0 {
res.CPUShares = m.CpuShares
}
for _, p := range m.Devices {
res.Devices = append(res.Devices, container.DeviceMapping{PathOnHost: p.Path, PathInContainer: p.ContainerPath, CgroupPermissions: "rwm"})
}
_, err = cli.ContainerUpdate(context.Background(), id, container.UpdateConfig{RestartPolicy: rp, Resources: res})
if err != nil {
return err
}
return
}
// 更新容器名称
// param name 容器名称
// param id 老的容器名称
func (ds *dockerService) DockerContainerUpdateName(name, id string) (err error) {
cli, err := client2.NewClientWithOpts(client2.FromEnv)
if err != nil {
return err
}
defer cli.Close()
err = cli.ContainerRename(context.Background(), id, name)
if err != nil {
return err
}
return
}
// 获取网络列表
func (ds *dockerService) DockerNetworkModelList() []types.NetworkResource {
cli, _ := client2.NewClientWithOpts(client2.FromEnv)
defer cli.Close()
networks, _ := cli.NetworkList(context.Background(), types.NetworkListOptions{})
return networks
}
func NewDockerService() DockerService {
return &dockerService{rootDir: command2.ExecResultStr(`source ./shell/helper.sh ;GetDockerRootDir`)}
}
func (ds *dockerService) GetDockerInfo() (types.Info, error) {
cli, err := client2.NewClientWithOpts(client2.FromEnv)
if err != nil {
return types.Info{}, err
}
defer cli.Close()
return cli.Info(context.Background())
}
// ---------------------------------------test------------------------------------
//func ServiceCreate() {
// cli, err := client2.NewClientWithOpts(client2.FromEnv)
// r, err := cli.ServiceCreate(context.Background(), swarm.ServiceSpec{}, types.ServiceCreateOptions{})
// if err != nil {
// fmt.Println("error", err)
// }
//
//
//}
// func Containerd() {
// // create a new client connected to the default socket path for containerd
// cli, err := containerd.New("/run/containerd/containerd.sock")
// if err != nil {
// fmt.Println("111")
// fmt.Println(err)
// }
// defer cli.Close()
// // create a new context with an "example" namespace
// ctx := namespaces.WithNamespace(context.Background(), "default")
// // pull the redis image from DockerHub
// image, err := cli.Pull(ctx, "docker.io/library/busybox:latest", containerd.WithPullUnpack)
// if err != nil {
// fmt.Println("222")
// fmt.Println(err)
// }
// // create a container
// container, err := cli.NewContainer(
// ctx,
// "test1",
// containerd.WithImage(image),
// containerd.WithNewSnapshot("redis-server-snapshot1", image),
// containerd.WithNewSpec(oci.WithImageConfig(image)),
// )
// if err != nil {
// fmt.Println(err)
// }
// defer container.Delete(ctx, containerd.WithSnapshotCleanup)
// // create a task from the container
// task, err := container.NewTask(ctx, cio.NewCreator(cio.WithStdio))
// if err != nil {
// fmt.Println(err)
// }
// defer task.Delete(ctx)
// // make sure we wait before calling start
// exitStatusC, err := task.Wait(ctx)
// if err != nil {
// fmt.Println(err)
// }
// // call start on the task to execute the redis server
// if err = task.Start(ctx); err != nil {
// fmt.Println(err)
// }
// fmt.Println("执行完成等待")
// // sleep for a lil bit to see the logs
// time.Sleep(3 * time.Second)
// // kill the process and get the exit status
// if err = task.Kill(ctx, syscall.SIGTERM); err != nil {
// fmt.Println(err)
// }
// // wait for the process to fully exit and print out the exit status
// status := <-exitStatusC
// code, _, err := status.Result()
// if err != nil {
// fmt.Println(err)
// }
// fmt.Printf("redis-server exited with status: %d\n", code)
// }

View File

@@ -1,24 +0,0 @@
package docker_base
import "github.com/IceWhaleTech/CasaOS/model"
//过滤mysql关键字
func MysqlFilter(c MysqlConfig, envs model.EnvArray) model.EnvArray {
for i := 0; i < len(envs); i++ {
switch envs[i].Value {
case "$MYSQL_HOST":
envs[i].Value = c.DataBaseHost
case "$MYSQL_PORT":
envs[i].Value = c.DataBasePort
case "$MYSQL_USERNAME":
envs[i].Value = c.DataBaseUser
case "$MYSQL_PASSWORD":
envs[i].Value = c.DataBasePassword
case "$MYSQL_DBNAME":
envs[i].Value = c.DataBaseDB
case "$MYSQL_HOST_AND_PORT":
envs[i].Value = c.DataBaseHost + ":" + c.DataBasePort
}
}
return envs
}

View File

@@ -1,9 +0,0 @@
package docker_base
type MysqlConfig struct {
DataBaseHost string `json:"database_host"`
DataBasePort string `json:"database_port"`
DataBaseUser string `json:"database_user"`
DataBasePassword string `json:"data_base_password"`
DataBaseDB string `json:"data_base_db"`
}

View File

@@ -1,93 +0,0 @@
package docker_base
import (
"context"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/network"
client2 "github.com/docker/docker/client"
"time"
)
//创建一个mysql数据库
func MysqlCreate(mysqlConfig MysqlConfig, dbId string, cpuShares int64, memory int64) (string, error) {
const imageName = "mysql"
const imageVersion = "8"
const imageNet = "oasis"
cli, err := client2.NewClientWithOpts(client2.FromEnv)
if err != nil {
return "", err
}
defer cli.Close()
_, err = cli.ImagePull(context.Background(), imageName+":"+imageVersion, types.ImagePullOptions{})
isExist := true
//检查到镜像才继续
for isExist {
filter := filters.NewArgs()
filter.Add("before", imageName+":"+imageVersion)
list, e := cli.ImageList(context.Background(), types.ImageListOptions{Filters: filter})
if e == nil && len(list) > 0 {
isExist = false
}
time.Sleep(time.Second)
}
var envArr = []string{"MYSQL_ROOT_PASSWORD=" + mysqlConfig.DataBasePassword, "MYSQL_DATABASE=" + mysqlConfig.DataBaseDB}
res := container.Resources{}
if cpuShares > 0 {
res.CPUShares = cpuShares
}
if memory > 0 {
res.Memory = memory << 20
}
rp := container.RestartPolicy{}
rp.Name = "always"
config := &container.Config{
Image: imageName,
Labels: map[string]string{"version": imageVersion, "author": "official"},
Env: envArr,
}
hostConfig := &container.HostConfig{Resources: res, RestartPolicy: rp, NetworkMode: container.NetworkMode(imageNet)}
containerCreate, err := cli.ContainerCreate(context.Background(),
config,
hostConfig,
&network.NetworkingConfig{EndpointsConfig: map[string]*network.EndpointSettings{imageNet: {NetworkID: ""}}},
nil,
dbId)
containerId := containerCreate.ID
//启动容器
err = cli.ContainerStart(context.Background(), dbId, types.ContainerStartOptions{})
if err != nil {
return containerId, err
}
return containerId, nil
}
func MysqlDelete(dbId string) error {
cli, err := client2.NewClientWithOpts(client2.FromEnv)
if err != nil {
return err
}
defer cli.Close()
err = cli.ContainerStop(context.Background(), dbId, nil)
if err != nil {
return err
}
err = cli.ContainerRemove(context.Background(), dbId, types.ContainerRemoveOptions{})
return err
}

View File

@@ -1,50 +0,0 @@
package service
import (
"fmt"
"testing"
)
//func TestDockerImageInfo(t *testing.T) {
// //DockerImageInfo()
//
// address, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:0", "0.0.0.0"))
// if err != nil {
// fmt.Println(0, err)
// }
//
// listener, err := net.ListenTCP("tcp", address)
// if err != nil {
// fmt.Println(0, err)
// }
//
// defer listener.Close()
// fmt.Println(listener.Addr().(*net.TCPAddr).Port, nil)
//
//}
//func TestDockerNetwork(t *testing.T) {
// DockerNetwork()
//}
//
//func TestDockerPull(t *testing.T) {
// DockerPull()
//}
//
//func TestDockerLog(t *testing.T) {
// DockerLog()
//}
//func TestDockerLogs(t *testing.T) {
// DockerLogs()
//}
func TestDockerContainerStats(t *testing.T) {
fmt.Println(DockerContainerStats1())
}
//func TestDockerImageRemove(t *testing.T) {
// host, domain, tld := gotld.GetSubdomain("aaa.liru-05.top", 1)
// fmt.Println(host)
// fmt.Println(domain)
// fmt.Println(tld)
//}

View File

@@ -19,9 +19,9 @@ import (
"sync" "sync"
"time" "time"
"github.com/IceWhaleTech/CasaOS-Common/utils/logger"
"github.com/IceWhaleTech/CasaOS/model" "github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/pkg/utils/file" "github.com/IceWhaleTech/CasaOS/pkg/utils/file"
"github.com/IceWhaleTech/CasaOS/pkg/utils/loger"
"go.uber.org/zap" "go.uber.org/zap"
) )
@@ -78,8 +78,8 @@ func (w *writer) Write(p []byte) (n int, err error) {
return w.w.Write(p) return w.w.Write(p)
} }
} }
func FileOperate(k string) {
func FileOperate(k string) {
list, ok := FileQueue.Load(k) list, ok := FileQueue.Load(k)
if !ok { if !ok {
return return
@@ -103,10 +103,10 @@ func FileOperate(k string) {
} }
err := os.Rename(v.From, temp.To+"/"+lastPath) err := os.Rename(v.From, temp.To+"/"+lastPath)
if err != nil { if err != nil {
loger.Error("file move error", zap.Any("err", err)) logger.Error("file move error", zap.Any("err", err))
err = file.MoveFile(v.From, temp.To+"/"+lastPath) err = file.MoveFile(v.From, temp.To+"/"+lastPath)
if err != nil { if err != nil {
loger.Error("MoveFile error", zap.Any("err", err)) logger.Error("MoveFile error", zap.Any("err", err))
continue continue
} }
@@ -152,7 +152,6 @@ func CheckFileStatus() {
} }
temp := item.(model.FileOperate) temp := item.(model.FileOperate)
for i := 0; i < len(temp.Item); i++ { for i := 0; i < len(temp.Item); i++ {
if !temp.Item[i].Finished { if !temp.Item[i].Finished {
size, err := file.GetFileOrDirSize(temp.To + "/" + filepath.Base(temp.Item[i].From)) size, err := file.GetFileOrDirSize(temp.To + "/" + filepath.Base(temp.Item[i].From))
if err != nil { if err != nil {
@@ -166,7 +165,6 @@ func CheckFileStatus() {
} else { } else {
total += temp.Item[i].ProcessedSize total += temp.Item[i].ProcessedSize
} }
} }
temp.ProcessedSize = total temp.ProcessedSize = total
FileQueue.Store(v, temp) FileQueue.Store(v, temp)

View File

@@ -1,78 +0,0 @@
/*
* @Author: LinkLeong link@icewhale.com
* @Date: 2022-05-13 18:15:46
* @LastEditors: LinkLeong
* @LastEditTime: 2022-07-13 10:56:34
* @FilePath: /CasaOS/service/model/o_container.go
* @Description:
* @Website: https://www.casaos.io
* Copyright (c) 2022 by icewhale, All Rights Reserved.
*/
package model
const CONTAINERTABLENAME = "o_container"
//Soon to be removed
type AppListDBModel struct {
CustomId string `gorm:"column:custom_id;primary_key" json:"custom_id"`
Title string `json:"title"`
// ScreenshotLink model.Strings `gorm:"type:json" json:"screenshot_link,omitempty"`
ScreenshotLink string `json:"screenshot_link"`
Slogan string `json:"slogan"`
Description string `json:"description"`
//Tags model.Strings `gorm:"type:json" json:"tags"`
Tags string `json:"tags"`
Icon string `json:"icon"`
Version string `json:"version"`
ContainerId string `json:"container_id,omitempty"`
Image string `json:"image,omitempty"`
Index string `json:"index"`
CreatedAt string `gorm:"<-:create;autoCreateTime" json:"created_at"`
UpdatedAt string `gorm:"<-:create;<-:update;autoUpdateTime" json:"updated_at"`
//Port string `json:"port,omitempty"`
PortMap string `json:"port_map"`
Label string `json:"label"`
EnableUPNP bool `json:"enable_upnp"`
Envs string `json:"envs"`
Ports string `json:"ports"`
Volumes string `json:"volumes"`
Devices string `json:"devices"`
//Envs []model.Env `json:"envs"`
//Ports []model.PortMap `gorm:"type:json" json:"ports"`
//Volumes []model.PathMap `gorm:"type:json" json:"volumes"`
//Devices []model.PathMap `gorm:"type:json" json:"device"`
Position bool `json:"position"`
NetModel string `json:"net_model"`
CpuShares int64 `json:"cpu_shares"`
Memory int64 `json:"memory"`
Restart string `json:"restart"`
//Rely model.MapStrings `gorm:"type:json" json:"rely"` //[{"mysql":"id"},{"mysql":"id"}]
Origin string `json:"origin"`
HostName string `json:"host_name"`
Privileged bool `json:"privileged"`
CapAdd string `json:"cap_add"`
Cmd string `gorm:"type:json" json:"cmd"`
}
func (p *AppListDBModel) TableName() string {
return "o_container"
}
type MyAppList struct {
Id string `json:"id"`
Name string `json:"name"`
Icon string `json:"icon"`
State string `json:"state"`
CustomId string `gorm:"column:custom_id;primary_key" json:"custom_id"`
Index string `json:"index"`
//Order string `json:"order"`
Port string `json:"port"`
Slogan string `json:"slogan"`
Type string `json:"type"`
//Rely model.MapStrings `json:"rely"` //[{"mysql":"id"},{"mysql":"id"}]
Image string `json:"image"`
Volumes string `json:"volumes"`
Latest bool `json:"latest"`
Host string `json:"host"`
Protocol string `json:"protocol"`
}

View File

@@ -1,9 +1,6 @@
package model package model
import ( import (
"database/sql/driver"
"encoding/json"
"github.com/IceWhaleTech/CasaOS/service/docker_base"
"time" "time"
) )
@@ -11,25 +8,12 @@ type RelyDBModel struct {
Id uint `gorm:"column:id;primary_key" json:"id"` Id uint `gorm:"column:id;primary_key" json:"id"`
CustomId string ` json:"custom_id"` CustomId string ` json:"custom_id"`
ContainerCustomId string `json:"container_custom_id"` ContainerCustomId string `json:"container_custom_id"`
Config MysqlConfigs `json:"config"`
ContainerId string `json:"container_id,omitempty"` ContainerId string `json:"container_id,omitempty"`
Type int `json:"type"` //目前暂未使用 Type int `json:"type"` // 目前暂未使用
CreatedAt time.Time `gorm:"<-:create" json:"created_at"` CreatedAt time.Time `gorm:"<-:create" json:"created_at"`
UpdatedAt time.Time `gorm:"<-:create;<-:update" json:"updated_at"` UpdatedAt time.Time `gorm:"<-:create;<-:update" json:"updated_at"`
} }
/****************使gorm支持[]string结构*******************/
type MysqlConfigs docker_base.MysqlConfig
func (c MysqlConfigs) Value() (driver.Value, error) {
b, err := json.Marshal(c)
return string(b), err
}
func (c *MysqlConfigs) Scan(input interface{}) error {
return json.Unmarshal(input.([]byte), c)
}
/****************使gorm支持[]string结构*******************/ /****************使gorm支持[]string结构*******************/
func (p RelyDBModel) TableName() string { func (p RelyDBModel) TableName() string {

View File

@@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"time" "time"
notifyCommon "github.com/IceWhaleTech/CasaOS-Common/model/notify"
model2 "github.com/IceWhaleTech/CasaOS/model" model2 "github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/model/notify" "github.com/IceWhaleTech/CasaOS/model/notify"
"github.com/IceWhaleTech/CasaOS/service/model" "github.com/IceWhaleTech/CasaOS/service/model"
@@ -15,28 +16,31 @@ import (
"gorm.io/gorm" "gorm.io/gorm"
) )
var NotifyMsg chan notify.Message var (
var ClientCount int = 0 NotifyMsg chan notify.Message
ClientCount int
)
type NotifyServer interface { type NotifyServer interface {
GetLog(id string) model.AppNotify GetLog(id string) model.AppNotify
AddLog(log model.AppNotify) AddLog(log model.AppNotify)
UpdateLog(log model.AppNotify) UpdateLog(log model.AppNotify)
UpdateLogByCustomId(log model.AppNotify) UpdateLogByCustomID(log model.AppNotify)
DelLog(id string) DelLog(id string)
GetList(c int) (list []model.AppNotify) GetList(c int) (list []model.AppNotify)
MarkRead(id string, state int) MarkRead(id string, state int)
// SendText(m model.AppNotify) // SendText(m model.AppNotify)
SendUninstallAppBySocket(app notify.Application) SendUninstallAppBySocket(app notifyCommon.Application)
SendNetInfoBySocket(netList []model2.IOCountersStat) SendNetInfoBySocket(netList []model2.IOCountersStat)
SendCPUInfoBySocket(cpu map[string]interface{}) SendCPUInfoBySocket(cpu map[string]interface{})
SendMemInfoBySocket(mem map[string]interface{}) SendMemInfoBySocket(mem map[string]interface{})
SendFileOperateNotify(nowSend bool) SendFileOperateNotify(nowSend bool)
SendInstallAppBySocket(app notify.Application) SendInstallAppBySocket(app notifyCommon.Application)
SendAllHardwareStatusBySocket(mem map[string]interface{}, cpu map[string]interface{}, netList []model2.IOCountersStat) SendAllHardwareStatusBySocket(mem map[string]interface{}, cpu map[string]interface{}, netList []model2.IOCountersStat)
SendStorageBySocket(message notify.StorageMessage) SendStorageBySocket(message notify.StorageMessage)
SendNotify(path string, message map[string]interface{}) SendNotify(path string, message map[string]interface{})
SettingSystemTempData(message map[string]interface{}) SettingSystemTempData(message map[string]interface{})
GetSystemTempMap() map[string]interface{}
} }
type notifyServer struct { type notifyServer struct {
@@ -61,7 +65,6 @@ func (i *notifyServer) SendNotify(path string, message map[string]interface{}) {
notify.Msg = msg notify.Msg = msg
NotifyMsg <- notify NotifyMsg <- notify
} }
func (i *notifyServer) SendStorageBySocket(message notify.StorageMessage) { func (i *notifyServer) SendStorageBySocket(message notify.StorageMessage) {
@@ -79,8 +82,8 @@ func (i *notifyServer) SendStorageBySocket(message notify.StorageMessage) {
NotifyMsg <- notify NotifyMsg <- notify
} }
func (i *notifyServer) SendAllHardwareStatusBySocket(mem map[string]interface{}, cpu map[string]interface{}, netList []model2.IOCountersStat) {
func (i *notifyServer) SendAllHardwareStatusBySocket(mem map[string]interface{}, cpu map[string]interface{}, netList []model2.IOCountersStat) {
body := make(map[string]interface{}) body := make(map[string]interface{})
body["sys_mem"] = mem body["sys_mem"] = mem
@@ -103,12 +106,10 @@ func (i *notifyServer) SendAllHardwareStatusBySocket(mem map[string]interface{},
notify.Msg = msg notify.Msg = msg
NotifyMsg <- notify NotifyMsg <- notify
} }
// Send periodic broadcast messages // Send periodic broadcast messages
func (i *notifyServer) SendFileOperateNotify(nowSend bool) { func (i *notifyServer) SendFileOperateNotify(nowSend bool) {
if nowSend { if nowSend {
len := 0 len := 0
@@ -258,7 +259,6 @@ func (i *notifyServer) SendFileOperateNotify(nowSend bool) {
time.Sleep(time.Second * 3) time.Sleep(time.Second * 3)
} }
} }
} }
func (i *notifyServer) SendMemInfoBySocket(mem map[string]interface{}) { func (i *notifyServer) SendMemInfoBySocket(mem map[string]interface{}) {
@@ -277,7 +277,7 @@ func (i *notifyServer) SendMemInfoBySocket(mem map[string]interface{}) {
NotifyMsg <- notify NotifyMsg <- notify
} }
func (i *notifyServer) SendInstallAppBySocket(app notify.Application) { func (i *notifyServer) SendInstallAppBySocket(app notifyCommon.Application) {
body := make(map[string]interface{}) body := make(map[string]interface{})
body["data"] = app body["data"] = app
@@ -308,6 +308,7 @@ func (i *notifyServer) SendCPUInfoBySocket(cpu map[string]interface{}) {
NotifyMsg <- notify NotifyMsg <- notify
} }
func (i *notifyServer) SendNetInfoBySocket(netList []model2.IOCountersStat) { func (i *notifyServer) SendNetInfoBySocket(netList []model2.IOCountersStat) {
body := make(map[string]interface{}) body := make(map[string]interface{})
body["data"] = netList body["data"] = netList
@@ -324,7 +325,7 @@ func (i *notifyServer) SendNetInfoBySocket(netList []model2.IOCountersStat) {
NotifyMsg <- notify NotifyMsg <- notify
} }
func (i *notifyServer) SendUninstallAppBySocket(app notify.Application) { func (i *notifyServer) SendUninstallAppBySocket(app notifyCommon.Application) {
body := make(map[string]interface{}) body := make(map[string]interface{})
body["data"] = app body["data"] = app
@@ -357,17 +358,20 @@ func (i *notifyServer) AddLog(log model.AppNotify) {
func (i *notifyServer) UpdateLog(log model.AppNotify) { func (i *notifyServer) UpdateLog(log model.AppNotify) {
i.db.Save(&log) i.db.Save(&log)
} }
func (i *notifyServer) UpdateLogByCustomId(log model.AppNotify) {
func (i *notifyServer) UpdateLogByCustomID(log model.AppNotify) {
if len(log.CustomId) == 0 { if len(log.CustomId) == 0 {
return return
} }
i.db.Model(&model.AppNotify{}).Select("*").Where("custom_id = ? ", log.CustomId).Updates(log) i.db.Model(&model.AppNotify{}).Select("*").Where("custom_id = ? ", log.CustomId).Updates(log)
} }
func (i *notifyServer) GetLog(id string) model.AppNotify { func (i *notifyServer) GetLog(id string) model.AppNotify {
var log model.AppNotify var log model.AppNotify
i.db.Where("custom_id = ? ", id).First(&log) i.db.Where("custom_id = ? ", id).First(&log)
return log return log
} }
func (i *notifyServer) MarkRead(id string, state int) { func (i *notifyServer) MarkRead(id string, state int) {
if id == "0" { if id == "0" {
i.db.Model(&model.AppNotify{}).Where("1 = ?", 1).Update("state", state) i.db.Model(&model.AppNotify{}).Where("1 = ?", 1).Update("state", state)
@@ -375,6 +379,7 @@ func (i *notifyServer) MarkRead(id string, state int) {
} }
i.db.Model(&model.AppNotify{}).Where("id = ? ", id).Update("state", state) i.db.Model(&model.AppNotify{}).Where("id = ? ", id).Update("state", state)
} }
func (i *notifyServer) DelLog(id string) { func (i *notifyServer) DelLog(id string) {
var log model.AppNotify var log model.AppNotify
i.db.Where("custom_id = ?", id).Delete(&log) i.db.Where("custom_id = ?", id).Delete(&log)
@@ -442,6 +447,9 @@ func SendMeg() {
// } // }
// } // }
func (i *notifyServer) GetSystemTempMap() map[string]interface{} {
return i.SystemTempMap
}
func NewNotifyService(db *gorm.DB) NotifyServer { func NewNotifyService(db *gorm.DB) NotifyServer {
return &notifyServer{db: db, SystemTempMap: make(map[string]interface{})} return &notifyServer{db: db, SystemTempMap: make(map[string]interface{})}

View File

@@ -11,7 +11,7 @@
package service package service
import ( import (
gateway "github.com/IceWhaleTech/CasaOS-Gateway/common" "github.com/IceWhaleTech/CasaOS-Common/external"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"github.com/patrickmn/go-cache" "github.com/patrickmn/go-cache"
"gorm.io/gorm" "gorm.io/gorm"
@@ -21,34 +21,30 @@ var Cache *cache.Cache
var MyService Repository var MyService Repository
var WebSocketConns []*websocket.Conn var (
var NewVersionApp map[string]string WebSocketConns []*websocket.Conn
var SocketRun bool SocketRun bool
)
type Repository interface { type Repository interface {
App() AppService // User() UserService
//User() UserService
Docker() DockerService
Casa() CasaService Casa() CasaService
Notify() NotifyServer Notify() NotifyServer
Rely() RelyService Rely() RelyService
System() SystemService System() SystemService
Shares() SharesService Shares() SharesService
Connections() ConnectionsService Connections() ConnectionsService
Gateway() gateway.ManagementService Gateway() external.ManagementService
} }
func NewService(db *gorm.DB, RuntimePath string) Repository { func NewService(db *gorm.DB, RuntimePath string) Repository {
gatewayManagement, err := external.NewManagementService(RuntimePath)
gatewayManagement, err := gateway.NewManagementService(RuntimePath)
if err != nil && len(RuntimePath) > 0 { if err != nil && len(RuntimePath) > 0 {
panic(err) panic(err)
} }
return &store{ return &store{
gateway: gatewayManagement, gateway: gatewayManagement,
app: NewAppService(db),
docker: NewDockerService(),
casa: NewCasaService(), casa: NewCasaService(),
notify: NewNotifyService(db), notify: NewNotifyService(db),
rely: NewRelyService(db), rely: NewRelyService(db),
@@ -60,23 +56,23 @@ func NewService(db *gorm.DB, RuntimePath string) Repository {
type store struct { type store struct {
db *gorm.DB db *gorm.DB
app AppService
docker DockerService
casa CasaService casa CasaService
notify NotifyServer notify NotifyServer
rely RelyService rely RelyService
system SystemService system SystemService
shares SharesService shares SharesService
connections ConnectionsService connections ConnectionsService
gateway gateway.ManagementService gateway external.ManagementService
} }
func (c *store) Gateway() gateway.ManagementService { func (c *store) Gateway() external.ManagementService {
return c.gateway return c.gateway
} }
func (s *store) Connections() ConnectionsService { func (s *store) Connections() ConnectionsService {
return s.connections return s.connections
} }
func (s *store) Shares() SharesService { func (s *store) Shares() SharesService {
return s.shares return s.shares
} }
@@ -88,19 +84,11 @@ func (c *store) Rely() RelyService {
func (c *store) System() SystemService { func (c *store) System() SystemService {
return c.system return c.system
} }
func (c *store) Notify() NotifyServer { func (c *store) Notify() NotifyServer {
return c.notify return c.notify
} }
func (c *store) App() AppService {
return c.app
}
func (c *store) Docker() DockerService {
return c.docker
}
func (c *store) Casa() CasaService { func (c *store) Casa() CasaService {
return c.casa return c.casa
} }

View File

@@ -5,12 +5,14 @@ import (
"io/ioutil" "io/ioutil"
net2 "net" net2 "net"
"os" "os"
"os/exec"
"path/filepath" "path/filepath"
"runtime" "runtime"
"strconv" "strconv"
"strings" "strings"
"time" "time"
"github.com/IceWhaleTech/CasaOS-Common/utils/logger"
"github.com/IceWhaleTech/CasaOS/model" "github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/pkg/config" "github.com/IceWhaleTech/CasaOS/pkg/config"
command2 "github.com/IceWhaleTech/CasaOS/pkg/utils/command" command2 "github.com/IceWhaleTech/CasaOS/pkg/utils/command"
@@ -30,8 +32,6 @@ type SystemService interface {
UpdateAssist() UpdateAssist()
UpSystemPort(port string) UpSystemPort(port string)
GetTimeZone() string GetTimeZone() string
UpdateUSBAutoMount(state string)
ExecUSBAutoMountShell(state string)
UpAppOrderFile(str, id string) UpAppOrderFile(str, id string)
GetAppOrderFile(id string) []byte GetAppOrderFile(id string) []byte
GetNet(physics bool) []string GetNet(physics bool) []string
@@ -52,14 +52,19 @@ type SystemService interface {
IsServiceRunning(name string) bool IsServiceRunning(name string) bool
GetCPUTemperature() int GetCPUTemperature() int
GetCPUPower() map[string]string GetCPUPower() map[string]string
GetMacAddress() (string, error)
SystemReboot() error
SystemShutdown() error
} }
type systemService struct { type systemService struct{}
}
func (s *systemService) UpdateUSBAutoMount(state string) { func (c *systemService) GetMacAddress() (string, error) {
config.ServerInfo.USBAutoMount = state interfaces, err := net.Interfaces()
config.Cfg.Section("server").Key("USBAutoMount").SetValue(state) if err != nil {
config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath) return "", err
}
inter := interfaces[0]
return inter.HardwareAddr, nil
} }
func (c *systemService) MkdirAll(path string) (int, error) { func (c *systemService) MkdirAll(path string) (int, error) {
@@ -76,8 +81,8 @@ func (c *systemService) MkdirAll(path string) (int, error) {
} }
return common_err.SERVICE_ERROR, err return common_err.SERVICE_ERROR, err
} }
func (c *systemService) RenameFile(oldF, newF string) (int, error) {
func (c *systemService) RenameFile(oldF, newF string) (int, error) {
_, err := os.Stat(newF) _, err := os.Stat(newF)
if err == nil { if err == nil {
return common_err.DIR_ALREADY_EXISTS, nil return common_err.DIR_ALREADY_EXISTS, nil
@@ -92,6 +97,7 @@ func (c *systemService) RenameFile(oldF, newF string) (int, error) {
} }
return common_err.SERVICE_ERROR, err return common_err.SERVICE_ERROR, err
} }
func (c *systemService) CreateFile(path string) (int, error) { func (c *systemService) CreateFile(path string) (int, error) {
_, err := os.Stat(path) _, err := os.Stat(path)
if err == nil { if err == nil {
@@ -104,9 +110,11 @@ func (c *systemService) CreateFile(path string) (int, error) {
} }
return common_err.SERVICE_ERROR, err return common_err.SERVICE_ERROR, err
} }
func (c *systemService) GetDeviceTree() string { func (c *systemService) GetDeviceTree() string {
return command2.ExecResultStr("source " + config.AppInfo.ShellPath + "/helper.sh ;GetDeviceTree") return command2.ExecResultStr("source " + config.AppInfo.ShellPath + "/helper.sh ;GetDeviceTree")
} }
func (c *systemService) GetSysInfo() host.InfoStat { func (c *systemService) GetSysInfo() host.InfoStat {
info, _ := host.Info() info, _ := host.Info()
return *info return *info
@@ -128,9 +136,7 @@ func (c *systemService) GetNetState(name string) string {
} }
func (c *systemService) GetDirPathOne(path string) (m model.Path) { func (c *systemService) GetDirPathOne(path string) (m model.Path) {
f, err := os.Stat(path) f, err := os.Stat(path)
if err != nil { if err != nil {
return return
} }
@@ -175,6 +181,7 @@ func (c *systemService) GetDirPath(path string) []model.Path {
} }
return dirs return dirs
} }
func (c *systemService) GetCpuInfo() []cpu.InfoStat { func (c *systemService) GetCpuInfo() []cpu.InfoStat {
info, _ := cpu.Info() info, _ := cpu.Info()
return info return info
@@ -207,6 +214,7 @@ func (c *systemService) GetNetInfo() []net.IOCountersStat {
parts, _ := net.IOCounters(true) parts, _ := net.IOCounters(true)
return parts return parts
} }
func (c *systemService) GetNet(physics bool) []string { func (c *systemService) GetNet(physics bool) []string {
t := "1" t := "1"
if physics { if physics {
@@ -220,11 +228,17 @@ func (s *systemService) UpdateSystemVersion(version string) {
os.Remove(config.AppInfo.LogPath + "/upgrade.log") os.Remove(config.AppInfo.LogPath + "/upgrade.log")
} }
file.CreateFile(config.AppInfo.LogPath + "/upgrade.log") file.CreateFile(config.AppInfo.LogPath + "/upgrade.log")
//go command2.OnlyExec("curl -fsSL https://raw.githubusercontent.com/LinkLeong/casaos-alpha/main/update.sh | bash") // go command2.OnlyExec("curl -fsSL https://raw.githubusercontent.com/LinkLeong/casaos-alpha/main/update.sh | bash")
go command2.OnlyExec("curl -fsSL https://raw.githubusercontent.com/IceWhaleTech/get/main/update.sh | sudo bash") if len(config.ServerInfo.UpdateUrl) > 0 {
//s.log.Error(config.AppInfo.ProjectPath + "/shell/tool.sh -r " + version) go command2.OnlyExec("curl -fsSL " + config.ServerInfo.UpdateUrl + " | bash")
//s.log.Error(command2.ExecResultStr(config.AppInfo.ProjectPath + "/shell/tool.sh -r " + version)) } else {
go command2.OnlyExec("curl -fsSL https://get.casaos.io/update | bash")
}
// s.log.Error(config.AppInfo.ProjectPath + "/shell/tool.sh -r " + version)
// s.log.Error(command2.ExecResultStr(config.AppInfo.ProjectPath + "/shell/tool.sh -r " + version))
} }
func (s *systemService) UpdateAssist() { func (s *systemService) UpdateAssist() {
command2.ExecResultStrArray("source " + config.AppInfo.ShellPath + "/assist.sh") command2.ExecResultStrArray("source " + config.AppInfo.ShellPath + "/assist.sh")
} }
@@ -233,14 +247,6 @@ func (s *systemService) GetTimeZone() string {
return command2.ExecResultStr("source " + config.AppInfo.ShellPath + "/helper.sh ;GetTimeZone") return command2.ExecResultStr("source " + config.AppInfo.ShellPath + "/helper.sh ;GetTimeZone")
} }
func (s *systemService) ExecUSBAutoMountShell(state string) {
if state == "False" {
command2.OnlyExec("source " + config.AppInfo.ShellPath + "/helper.sh ;USB_Stop_Auto")
} else {
command2.OnlyExec("source " + config.AppInfo.ShellPath + "/helper.sh ;USB_Start_Auto")
}
}
func (s *systemService) GetSystemConfigDebug() []string { func (s *systemService) GetSystemConfigDebug() []string {
return command2.ExecResultStrArray("source " + config.AppInfo.ShellPath + "/helper.sh ;GetSysInfo") return command2.ExecResultStrArray("source " + config.AppInfo.ShellPath + "/helper.sh ;GetSysInfo")
} }
@@ -248,9 +254,11 @@ func (s *systemService) GetSystemConfigDebug() []string {
func (s *systemService) UpAppOrderFile(str, id string) { func (s *systemService) UpAppOrderFile(str, id string) {
file.WriteToPath([]byte(str), config.AppInfo.DBPath+"/"+id, "app_order.json") file.WriteToPath([]byte(str), config.AppInfo.DBPath+"/"+id, "app_order.json")
} }
func (s *systemService) GetAppOrderFile(id string) []byte { func (s *systemService) GetAppOrderFile(id string) []byte {
return file.ReadFullFile(config.AppInfo.UserDataPath + "/" + id + "/app_order.json") return file.ReadFullFile(config.AppInfo.UserDataPath + "/" + id + "/app_order.json")
} }
func (s *systemService) UpSystemPort(port string) { func (s *systemService) UpSystemPort(port string) {
if len(port) > 0 && port != config.ServerInfo.HttpPort { if len(port) > 0 && port != config.ServerInfo.HttpPort {
config.Cfg.Section("server").Key("HttpPort").SetValue(port) config.Cfg.Section("server").Key("HttpPort").SetValue(port)
@@ -258,6 +266,7 @@ func (s *systemService) UpSystemPort(port string) {
} }
config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath) config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath)
} }
func (s *systemService) GetCasaOSLogs(lineNumber int) string { func (s *systemService) GetCasaOSLogs(lineNumber int) string {
file, err := os.Open(filepath.Join(config.AppInfo.LogPath, fmt.Sprintf("%s.%s", file, err := os.Open(filepath.Join(config.AppInfo.LogPath, fmt.Sprintf("%s.%s",
config.AppInfo.LogSaveName, config.AppInfo.LogSaveName,
@@ -294,14 +303,55 @@ func GetDeviceAllIP() []string {
func (s *systemService) IsServiceRunning(name string) bool { func (s *systemService) IsServiceRunning(name string) bool {
status := command2.ExecResultStr("source " + config.AppInfo.ShellPath + "/helper.sh ;CheckServiceStatus smbd") status := command2.ExecResultStr("source " + config.AppInfo.ShellPath + "/helper.sh ;CheckServiceStatus smbd")
return strings.TrimSpace(status) == "running" return strings.TrimSpace(status) == "running"
} }
// find thermal_zone of cpu.
// assertions:
// - thermal_zone "type" and "temp" are required fields
// (https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-class-thermal)
func GetCPUThermalZone() string {
keyName := "cpu_thermal_zone"
var path string
if result, ok := Cache.Get(keyName); ok {
path, ok = result.(string)
if ok {
return path
}
}
var name string
cpu_types := []string{"x86_pkg_temp", "cpu", "CPU", "soc"}
stub := "/sys/devices/virtual/thermal/thermal_zone"
for i := 0; i < 100; i++ {
path = stub + strconv.Itoa(i)
if _, err := os.Stat(path); !os.IsNotExist(err) {
name = strings.TrimSuffix(string(file.ReadFullFile(path+"/type")), "\n")
for _, s := range cpu_types {
if strings.HasPrefix(name, s) {
logger.Info(fmt.Sprintf("CPU thermal zone found: %s, path: %s.", name, path))
Cache.SetDefault(keyName, path)
return path
}
}
} else {
if len(name) > 0 { //proves at least one zone
path = stub + "0"
} else {
path = ""
}
break
}
}
Cache.SetDefault(keyName, path)
return path
}
func (s *systemService) GetCPUTemperature() int { func (s *systemService) GetCPUTemperature() int {
outPut := "" outPut := ""
if file.Exists("/sys/class/thermal/thermal_zone0/temp") { path := GetCPUThermalZone()
outPut = string(file.ReadFullFile("/sys/class/thermal/thermal_zone0/temp")) if len(path) > 0 {
} else if file.Exists("/sys/class/hwmon/hwmon0/temp1_input") { outPut = string(file.ReadFullFile(path + "/temp"))
outPut = string(file.ReadFullFile("/sys/class/hwmon/hwmon0/temp1_input"))
} else { } else {
outPut = "0" outPut = "0"
} }
@@ -313,6 +363,7 @@ func (s *systemService) GetCPUTemperature() int {
} }
return celsius return celsius
} }
func (s *systemService) GetCPUPower() map[string]string { func (s *systemService) GetCPUPower() map[string]string {
data := make(map[string]string, 2) data := make(map[string]string, 2)
data["timestamp"] = strconv.FormatInt(time.Now().Unix(), 10) data["timestamp"] = strconv.FormatInt(time.Now().Unix(), 10)
@@ -323,6 +374,27 @@ func (s *systemService) GetCPUPower() map[string]string {
} }
return data return data
} }
func (s *systemService) SystemReboot() error {
arg := []string{"6"}
cmd := exec.Command("init", arg...)
_, err := cmd.CombinedOutput()
if err != nil {
return err
}
return nil
}
func (s *systemService) SystemShutdown() error {
arg := []string{"0"}
cmd := exec.Command("init", arg...)
_, err := cmd.CombinedOutput()
if err != nil {
return err
}
return nil
}
func NewSystemService() SystemService { func NewSystemService() SystemService {
return &systemService{} return &systemService{}
} }

View File

@@ -1,3 +1,13 @@
/*
* @Author: LinkLeong link@icewhale.org
* @Date: 2022-11-15 15:51:44
* @LastEditors: LinkLeong
* @LastEditTime: 2022-11-15 15:56:03
* @FilePath: /CasaOS/types/system.go
* @Description:
* @Website: https://www.casaos.io
* Copyright (c) 2022 by icewhale, All Rights Reserved.
*/
/*@Author: LinkLeong link@icewhale.com /*@Author: LinkLeong link@icewhale.com
*@Date: 2022-02-17 18:53:22 *@Date: 2022-02-17 18:53:22
*@LastEditors: LinkLeong *@LastEditors: LinkLeong
@@ -9,6 +19,6 @@
*/ */
package types package types
const CURRENTVERSION = "0.3.7" const CURRENTVERSION = "0.4.0"
const BODY = " " const BODY = " "