Compare commits

...

75 Commits

Author SHA1 Message Date
link
c7b7a30210 Dev (#264)
* fix bug

* updata UI

* 0.3.2

### Added

- [Files] Files can now be selected multiple files and downloaded, deleted, moved, etc.
- [Apps] Support to modify the application opening address.([#204](https://github.com/IceWhaleTech/CasaOS/issues/204))

### Changed

- [Apps] Hide the display of non-essential environment variables in the application.
- [System] Network, disk, cpu, memory, etc. information is modified to be pushed via socket.
- [System] Optimize opening speed.([#214](https://github.com/IceWhaleTech/CasaOS/issues/214))
### Fixed

- [System] Fixed the problem that sync data cannot submit the device ID ([#68](https://github.com/IceWhaleTech/CasaOS/issues/68))
- [Files] Fixed the code editor center alignment display problem.([#210](https://github.com/IceWhaleTech/CasaOS/issues/210))
- [Files] Fixed the problem of wrong name when downloading files.([#240](https://github.com/IceWhaleTech/CasaOS/issues/240))
- [System] Fixed the network display as a negative number problem.([#224](https://github.com/IceWhaleTech/CasaOS/issues/224))

* Modify log help class

* Fix some bugs in 0.3.2

* Solve the operation file queue problem

* Exclude web folders

* update UI

* add cancel file operate

* Update UI
2022-06-08 19:44:05 +08:00
link
fde665cd4d Dev (#263)
* fix bug

* updata UI

* 0.3.2

### Added

- [Files] Files can now be selected multiple files and downloaded, deleted, moved, etc.
- [Apps] Support to modify the application opening address.([#204](https://github.com/IceWhaleTech/CasaOS/issues/204))

### Changed

- [Apps] Hide the display of non-essential environment variables in the application.
- [System] Network, disk, cpu, memory, etc. information is modified to be pushed via socket.
- [System] Optimize opening speed.([#214](https://github.com/IceWhaleTech/CasaOS/issues/214))
### Fixed

- [System] Fixed the problem that sync data cannot submit the device ID ([#68](https://github.com/IceWhaleTech/CasaOS/issues/68))
- [Files] Fixed the code editor center alignment display problem.([#210](https://github.com/IceWhaleTech/CasaOS/issues/210))
- [Files] Fixed the problem of wrong name when downloading files.([#240](https://github.com/IceWhaleTech/CasaOS/issues/240))
- [System] Fixed the network display as a negative number problem.([#224](https://github.com/IceWhaleTech/CasaOS/issues/224))

* Modify log help class

* Fix some bugs in 0.3.2

* Solve the operation file queue problem

* Exclude web folders

* update UI

* add cancel file operate
2022-06-08 19:32:05 +08:00
link
eaf2341a2a Dev (#262)
* fix bug

* updata UI

* 0.3.2

### Added

- [Files] Files can now be selected multiple files and downloaded, deleted, moved, etc.
- [Apps] Support to modify the application opening address.([#204](https://github.com/IceWhaleTech/CasaOS/issues/204))

### Changed

- [Apps] Hide the display of non-essential environment variables in the application.
- [System] Network, disk, cpu, memory, etc. information is modified to be pushed via socket.
- [System] Optimize opening speed.([#214](https://github.com/IceWhaleTech/CasaOS/issues/214))
### Fixed

- [System] Fixed the problem that sync data cannot submit the device ID ([#68](https://github.com/IceWhaleTech/CasaOS/issues/68))
- [Files] Fixed the code editor center alignment display problem.([#210](https://github.com/IceWhaleTech/CasaOS/issues/210))
- [Files] Fixed the problem of wrong name when downloading files.([#240](https://github.com/IceWhaleTech/CasaOS/issues/240))
- [System] Fixed the network display as a negative number problem.([#224](https://github.com/IceWhaleTech/CasaOS/issues/224))

* Modify log help class

* Fix some bugs in 0.3.2

* Solve the operation file queue problem

* Exclude web folders

* update UI

* add cancel file operate
2022-06-08 19:31:01 +08:00
link
d4bed3e5c7 Dev (#261)
* fix bug

* updata UI

* 0.3.2

### Added

- [Files] Files can now be selected multiple files and downloaded, deleted, moved, etc.
- [Apps] Support to modify the application opening address.([#204](https://github.com/IceWhaleTech/CasaOS/issues/204))

### Changed

- [Apps] Hide the display of non-essential environment variables in the application.
- [System] Network, disk, cpu, memory, etc. information is modified to be pushed via socket.
- [System] Optimize opening speed.([#214](https://github.com/IceWhaleTech/CasaOS/issues/214))
### Fixed

- [System] Fixed the problem that sync data cannot submit the device ID ([#68](https://github.com/IceWhaleTech/CasaOS/issues/68))
- [Files] Fixed the code editor center alignment display problem.([#210](https://github.com/IceWhaleTech/CasaOS/issues/210))
- [Files] Fixed the problem of wrong name when downloading files.([#240](https://github.com/IceWhaleTech/CasaOS/issues/240))
- [System] Fixed the network display as a negative number problem.([#224](https://github.com/IceWhaleTech/CasaOS/issues/224))

* Modify log help class

* Fix some bugs in 0.3.2

* Solve the operation file queue problem

* Exclude web folders

* update UI
2022-06-08 18:19:45 +08:00
Tiger Wang (王豫)
fcb2b3f5a5 Create config.yml 2022-06-02 20:30:00 -04:00
link
60349c941a Update demo.yml 2022-05-18 16:59:43 +08:00
link
11bc70a710 Update demo.yml 2022-05-18 16:58:33 +08:00
link
665766019f Update demo.yml 2022-05-18 16:39:59 +08:00
link
57cef9624c Dev (#199)
* fix bug

* updata UI
2022-05-17 16:31:23 +08:00
link
6bd41ad016 fix bug (#198) 2022-05-17 12:57:34 +08:00
link
05425d638f Update CHANGELOG.md 2022-05-16 20:45:38 +08:00
link
bd5a2e35d4 update ui (#192) 2022-05-16 15:57:54 +08:00
link
123e7e8758 Merge pull request #183 from IceWhaleTech/dev
Update UI
2022-05-13 20:27:53 +08:00
LinkLeong
1ec3e2e9fb Update UI 2022-05-13 20:27:26 +08:00
link
0719c3cc0c Merge pull request #182 from IceWhaleTech/dev
update api url
2022-05-13 19:11:28 +08:00
LinkLeong
0a4ceb7c4c update api url 2022-05-13 19:09:35 +08:00
link
655f59f00a Merge pull request #181 from IceWhaleTech/dev
Dev
2022-05-13 18:56:35 +08:00
LinkLeong
5289f471d6 Update .gitignore 2022-05-13 18:55:52 +08:00
LinkLeong
59597befb6 delete github.com 2022-05-13 18:55:34 +08:00
link
df2477a12f Merge pull request #179 from IceWhaleTech/dev
Dev
2022-05-13 18:21:44 +08:00
LinkLeong
b709abe682 Merge branch 'main' into dev 2022-05-13 18:15:56 +08:00
LinkLeong
d0f3dc806e New Feature
- [Apps] This is a feature that has been highly requested by the community. Import the original Docker application into CasaOS. Now it's easy to import with just a few clicks!
- [Apps] App list supports a custom sorting function! You can arrange apps in different orders by dragging the icons.
- [Apps] App custom installation supports Docker Compose configuration import in YAML format.
- [Files] Added thumbnail preview function for image files.
- [Connect] Multiple CasaConenct devices in the LAN will be transmitted through the LAN network.
- [System] Added a switch for auto-mounting USB disk devices.

🎈 Enhancement
- [System] Optimized the system update alert, you will see the new version update log from the next version.
- [Apps] Added live preview for icons in custom installed apps.
- [Apps] Optimized the input of WebUI.
- [Files] Completely updated the image preview, now it supports switching all images in the same folder, as well as dragging, zooming, rotating and resetting.
- [Widgets] Added color levels for CPU and RAM charts.
- [Conenct] Optimized the display of the right-click menu of the Connect friends list.

🎈 Changed
- [Files] Change the initial display directory to /DATA

🐞 Fixed
- [System] Fixed an issue with Raspberry Pi devices failing to boot using USB disks. (Achieved by disabling USB disk auto-mount)
- [Apps] Fixed the issue that some Docker CLI commands failed to import.
- [Apps] Fixed the issue that the app is not easily recognized in /DATA/AppData directory and docker command line after installation, it will be shown as the app name. (Newly installed apps only)
- [Apps] Fixed the issue that Pi-hole cannot be launched after installation in the app store.
- [Apps] Fixed the issue that apps cannot be updated with WatchTower.
- [Files] Fixed the issue that when there is an upload task, the task status is lost after closing Files.
2022-05-13 18:12:26 +08:00
LinkLeong
92d085acf9 update changelog 2022-05-05 13:46:55 +08:00
link
20dbae21c8 Update github.go 2022-04-24 17:53:57 +08:00
link
9258cb4b9e Merge pull request #151 from IceWhaleTech/dev
Update UI
2022-04-08 14:44:28 +08:00
link
d9794851f9 Update UI 2022-04-08 14:44:02 +08:00
link
1fcb530ff2 Merge pull request #149 from IceWhaleTech/dev
Update UI
2022-04-07 16:20:19 +08:00
link
364d411438 Update UI 2022-04-07 16:17:47 +08:00
link
0dacc5db12 Merge pull request #141 from igorpecovnik/patch-1
Tested on Rockpro64, Odroid C2, Neo2, Cubox-i, amd64
2022-04-06 12:14:38 +08:00
link
fcbff194d2 Merge branch 'main' into patch-1 2022-04-06 12:14:31 +08:00
link
04e93b0e3c Merge pull request #138 from alvarosamudio/main
add a new OS to systems
2022-04-06 12:12:07 +08:00
link
2eff7fa8bc Merge pull request #148 from IceWhaleTech/dev
Dev
2022-04-06 12:11:39 +08:00
link
dd0645ee0f Add CasaConnect function 2022-04-06 12:10:51 +08:00
Igor Pečovnik
c93aa6286e Tested on Rockpro64, Odroid C2, Neo2, Cubox-i, x86
Basically it should work on all.
https://www.armbian.com/download/
https://docs.armbian.com/#what-is-the-difference-between-armbian-and-debianubuntu
2022-03-29 15:57:16 +02:00
Alvaro Samudio
bf7b32309f add Elementary OS 6.1 Jólnir to systems 2022-03-25 14:49:21 +00:00
Alvaro Samudio
cd779448ce add Deepin to systems 2022-03-25 14:41:05 +00:00
link
c3b2c1d599 Merge branch 'main' into dev 2022-03-18 11:41:04 +08:00
link
b7949e100f Merge Branches 2022-03-18 11:40:38 +08:00
link
80d24570d6 Merge pull request #130 from IceWhaleTech/Adding-ipv6-Support
fixed
2022-03-18 11:31:05 +08:00
link
539b72a388 fixed 2022-03-18 11:30:06 +08:00
link
a7d15a896e Need to fix bugs 2022-03-18 11:13:07 +08:00
link
9796c5aad7 prepare for test 2022-03-16 15:41:14 +08:00
link
892cd99eb3 Add some acquaintance network interface 2022-03-11 18:01:09 +08:00
link
02cb4cd392 Merge pull request #126 from IceWhaleTech/dev
fixed file upload bug
2022-03-10 16:26:00 +08:00
link
9213f9e379 fixed file upload bug 2022-03-10 16:25:33 +08:00
link
4473056d5c Merge pull request #125 from IceWhaleTech/dev
upload file manage
2022-03-10 13:41:15 +08:00
link
07e96511cd upload file manage 2022-03-10 13:40:42 +08:00
link
ce1dc52bc2 Merge pull request #123 from IceWhaleTech/dev
update file
2022-03-09 19:44:47 +08:00
link
d890b16644 update file 2022-03-09 19:44:08 +08:00
link
5737936318 Merge pull request #122 from IceWhaleTech/dev
Dev
2022-03-09 16:38:18 +08:00
link
33acfababd new file manager
Added CasaOS own file manager, now you can browse, upload, download files from the system, even edit code online, preview photos and videos through it. It will appear in the first position of Apps.
Added CPU core count display and memory capacity display.
2022-03-09 16:37:03 +08:00
link
28c1a52171 Determine the hole punching technique 2022-02-28 14:14:39 +08:00
link
892eef424c Merge branch 'main' into dev 2022-02-18 19:14:30 +08:00
link
f3c73ba79b Update usb-mount.sh 2022-02-18 19:14:11 +08:00
link
c6928c333d Add websocket 2022-02-18 19:06:03 +08:00
link
63c6c2cebb Merge pull request #111 from IceWhaleTech/dev
Delete CasaOS-linux-amd64
2022-02-17 19:22:14 +08:00
link
6cba025679 Delete CasaOS-linux-amd64 2022-02-17 19:21:19 +08:00
link
d25b93c972 Merge pull request #110 from IceWhaleTech/dev
Dev
2022-02-17 18:53:54 +08:00
link
b14861703c Merge branch 'dev' of ssh://github.com/IceWhaleTech/CasaOS into dev 2022-02-17 18:53:29 +08:00
link
32604225ce update ui 2022-02-17 18:52:14 +08:00
link
f623b29c38 Merge pull request #109 from IceWhaleTech/dev
Dev
2022-02-17 18:47:44 +08:00
link
94ae6ee8ec Merge branch 'main' into dev 2022-02-17 18:47:36 +08:00
link
5115bf72e1 update desc 2022-02-17 18:44:51 +08:00
link
e1a928cd78 Fixed issues 2022-02-17 18:43:25 +08:00
link
3977519c43 Update assist.sh 2022-01-30 11:21:24 +08:00
link
2f87834ac9 Merge pull request #93 from IceWhaleTech/dev
Update route.go
2022-01-28 16:25:01 +08:00
link
53335012d8 Update route.go 2022-01-28 16:24:28 +08:00
link
9d6302f7e3 Merge pull request #92 from IceWhaleTech/dev
Update UI
2022-01-28 16:14:59 +08:00
link
20950e2a60 Update UI 2022-01-28 16:14:35 +08:00
link
0e0e507f8f Merge pull request #91 from IceWhaleTech/dev
update ui
2022-01-28 15:35:15 +08:00
link
46e14617b6 update ui 2022-01-28 15:34:32 +08:00
link
903c73fafb Merge pull request #90 from IceWhaleTech/dev
update web
2022-01-28 14:33:40 +08:00
link
42a56df4a5 update web 2022-01-28 14:32:49 +08:00
link
493dc5c032 Merge pull request #89 from IceWhaleTech/dev
Compatible with more types of disks
2022-01-28 14:06:58 +08:00
link
9a73bc2a18 Compatible with more types of disks
Add usb display
2022-01-28 14:06:27 +08:00
154 changed files with 14146 additions and 14653 deletions

5
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Questions, Ideas, Discussions
url: https://github.com/IceWhaleTech/CasaOS/discussions
about: Ask questions, propose ideas, or discuss anything related to CasaOS

View File

@@ -33,7 +33,7 @@ jobs:
- name: Get old instance and snapshot name, create new instance name
run: |
echo "OLD_INSTANCE_SNAPSHOT_NAME=$(aws lightsail get-instance-snapshots | grep '"name": "CasaOS-Demo-Snapshot-[0-9]' | sed 's/ //g' | sed 's/"//g' | sed 's/,//g' | sed 's/name://g')" >> $GITHUB_ENV
echo "OLD_INSTANCE_SNAPSHOT_NAME=$(aws lightsail get-instance-snapshots | grep '"name": "CasaOS-Demo-Snapshot-1652856810' | 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
@@ -43,7 +43,7 @@ jobs:
--instance-snapshot-name ${{ env.OLD_INSTANCE_SNAPSHOT_NAME }} \
--instance-names ${{ env.NEW_INSTANCE_NAME }} \
--availability-zone us-west-2a \
--bundle-id large_2_0
--bundle-id medium_2_0
- name: Wait for new instance running
run: |

2
.gitignore vendored
View File

@@ -29,7 +29,9 @@ gen
/out/
/db/
/docs/
/web/
/conf/conf.ini
__debug_bin
main
CasaOS
github.com

392
CHANGELOG.md Normal file
View File

@@ -0,0 +1,392 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Added
### Changed
### Removed
### Security
### Fixed
## [0.3.2-pre] - 2022-06-08
### Added
- [Files] Files can now be selected multiple files and downloaded, deleted, moved, etc.
- [Apps] Support to modify the application opening address.([#204](https://github.com/IceWhaleTech/CasaOS/issues/204))
### Changed
- [Apps] Hide the display of non-essential environment variables in the application.([#196](https://github.com/IceWhaleTech/CasaOS/issues/196))
- [System] Network, disk, cpu, memory, etc. information is modified to be pushed via socket.
- [System] Optimize opening speed.([#214](https://github.com/IceWhaleTech/CasaOS/issues/214))
- [Language] Update language pack [zarevskaya](https://github.com/zarevskaya) [patrickhilker](https://github.com/patrickhilker)
- [System] Interface path adjustment
### Removed
- [Files] Remove the online preview function of PDF files
### Fixed
- [System] Fixed the problem that sync data cannot submit the device ID ([#68](https://github.com/IceWhaleTech/CasaOS/issues/68))
- [Files] Fixed the code editor center alignment display problem.([#210](https://github.com/IceWhaleTech/CasaOS/issues/210))
- [Files] Fixed the problem of wrong name when downloading files.([#240](https://github.com/IceWhaleTech/CasaOS/issues/240))
- [System] Fixed the network display as a negative number problem.([#224](https://github.com/IceWhaleTech/CasaOS/issues/224))
- [System] Fixed the problem of wireless network card traffic display.([#222](https://github.com/IceWhaleTech/CasaOS/issues/222))
## [0.3.1.1] - 2022-05-17
### Fixed
- Fix the data loss problem when importing local applications
## [0.3.1] - 2022-05-16
### Added
- CasaConnect and file add image thumbnail function
- Import of docker applications
- List support custom sorting function
- CasaConnect gives priority to LAN connections
- USB auto-mount switch (Raspberry Pi is off by default)
- Application custom installation supports Docker Compose configuration import in YAML format
- You will see the new version changelog from the next version
- Added live preview for icons in custom installed applications
### Changed
- Application data is no longer saved to the database
- Optimize app store speed issues
- Optimize the way WebUI is filled in
- Image preview has been completely upgraded and now supports switching between all images in the same folder, as well as dragging, zooming, rotating and resetting.
- Added color levels to the CPU and RAM charts
- Optimized the display of the Connect friends list right-click menu
- Change the initial display directory to /DATA
### Removed
- Historical Application Data
### Fixed
- Fixed the problem that some Docker CLI commands failed to import
- Fix the problem that the application is not easily recognized in /DATA/AppData directory and docker command line after installation, it will be shown as application name
- Fix Pi-hole installation failure
- Fixed the issue that the app could not be updated using WatchTower
- Fixed the problem that the task status was lost after closing Files when there was an upload task
## [0.3.0] - 2022-04-08
### Added
- Add CasaConnect function, now you can share private files peer-to-peer with your friends.
- Add a widget for network traffic monitoring.
- 12 new popular apps added to App Center
### Changed
- Updated the sidebar of Files.
- Updated the initial directory of Files to the Root directory.
- Armbian 22.02 armhf/arm64/amd64 platform tests passed [@igorpecovnik ](https://github.com/igorpecovnik)
- Elementary OS 6.1 Jólnir amd64 platform tests passed [@alvarosamudio ](https://github.com/alvarosamudio)
### Fixed
- Fix an issue in Files where the backspace button would trigger a return to the previous level of the directory when creating a folder.
- Fix the display problem of application list in CPU widget.
- Fix the problem that the ipv6 of the application cannot be opened
### Removed
- Interfaces related to "zerotier"
## [0.2.10] - 2022-03-10
### Added
- Added CasaOS own file manager, now you can browse, upload, download files from the system, even edit code online, preview photos and videos through it. It will appear in the first position of Apps.
- Added CPU core count display and memory capacity display.
### Changed
- Optimized the rendering performance of the home page.
- Optimized the internationalization display of the time widget.
- Show the icon of the stopped application as gray.
- Unify the animation of the drop-down menu.
- Optimize the display of the application drop-down menu.
- Replaced the default font to optimize the display.
### Fixed
- Fix the problem of failed to create storage space
## [0.2.9] - 2022-02-18
### Added
- Add a simple notification function
### Changed
- Custom installation of new parameters(Capabilities,Hostname,Privileged)
- Update front-end translation [@SemVer](https://github.com/zarevskaya) [@koboldMaki](https://github.com/koboldMaki) [@sgastol](https://github.com/sgastol) [@delki8](https://github.com/delki8)
- Modify the default location and name of the usb mount
### Fixed
- Fix the problem of being indexed by search engines
- Fix some style display issues
- Solve hard drive can't be formatted, can't finish adding storage
## [0.2.8] - 2022-01-30
### Added
- Add USB disk device display
### Changed
- Update translation [@baptiste313](https://github.com/baptiste313) [@thueske](https://github.com/thueske)
- Compatible with more types of drives
### Fixed
- Fix the language initialization bug
- Fix the problem that the login page could not be displayed
- Fix missing translated content
## [0.2.7] - 2022.01.26
### Changed
- Apply multilingual support
### Security
- Fix an injectable execution bug
## [0.2.6] - 2022.01.26
### Added
- Add a bug report panel.
- App Store apps start supporting multiple languages
### Fixed
- Fix a disk that cannot be formatted under certain circumstances
## [0.2.5] - 2022.01.24
### Added
- Storage Manager
### Changed
- Update Disk widget
- Update language files [@ImOstrovskiy](https://github.com/ImOstrovskiy) [@baptiste313](https://github.com/baptiste313)
### Fixed
- File synchronization issues
- Fix the app store classification problem
## [0.2.4] - 2021.12.30
### Changed
- Brand new App Store
- Optimize request method
### Fixed
- Fix Sync panel width display error.
- Fix App panel width display error.
## [0.2.3] - 2021.12.11
### Added
- Add detailed CPU and memory statistics.
- Add the multi-language function and add Chinese translation.
- Add the function to modify the search engine.
- Add the function of modifying the WebUI port
### Changed
- Update update script
- Preprocessing usb automounting
### Fixed
- Volume path problem when customizing the installation of applications
- Fix Cpu and Ram usage display error
- Fix translation errors
- Fixed an error when importing and exporting appfile.
## [0.2.2] - 2021.12.02
### Changed
- UI adjustment
### Fixed
- Fix the problem of data display error when manually installing apps
- Fix some spelling problems
- Fix the bug of synchronization module
## [0.2.1] - 2021.11.25
### Fixed
- Fix Sync display error
- Fix Sync Downoad url error
- Fix Smart Block display error
- Fix widgets settings dispaly error
- Fix application installation path error
## [0.2.0] - 2021.11.25
### Added
- Add sync function
## [0.1.11] - 2021.11.10
### Changed
- Adaptation of cell phone terminals
- Optimize user experience
- Replaced the default background
- Optimized the display performance and fixed some bugs
### Fixed
- Resolve application installation path errors
## [0.1.10] - 2021.11.04
### Added
- Add application terminal
- Add application logs
- Add system logs
- Add App Store for installation
## [0.1.9] - 2021.11.01 [YANKED]
## [0.1.8] - 2021.10.27
### Added
- Add system terminal
- Add the ability to modify the user name and password
### Changed
- Experience optimization
- Improve single user management function
- Fixed Disk widget display error
- Fixed Username display error after change
- Adaptation for mobile access
## [0.1.7] - 2021.10.22
### Added
- Add user authentication module, Login page and initialization page.
### Fixed
- Fix the problem that the application could not start after the system restarted.
- Home storage space data display exception
- Script override causes application loss after installation
- Fix docker network error
## [0.1.6] - 2021.10.19
### Added
- Add app icon auto-fill via docker image name.
- Add a file selector for app install.
### Changed
- Modify import reminder.
- Optimize the application installation process
### Fixed
- Fixed an issue with the app were it would disappear when the app was modified.
- Fixed device selector default dir to /dev
## [0.1.5] - 2021.10.15
### Added
- Add CPU RAM Status with widget
- Add Disk Info with widget
- Realize automatic loading of widgets
### Changed
- Enhance the Docker cli import experience and automatically fill in the folders that need to be mounted
### Removed
- Remove Weather widget.
### Fixed
- AppFile upload does not pass verification
- The setting menu of the app is displayed abnormally when the browser window is too narrow
- The port is occupied and the program cannot start
- Fix display bugs when windows size less than 1024px
## [0.1.4] - 2021.09.30
### Added
- Import and export of application configuration files
- Automatic parsing of docker commands
### Changed
- Improve the program release process
- Application installation process UX/UI optimization
### Fixed
- Authentication failure during the operation, resulting in the need to re-login
## [0.1.3] - 2021.09.29 [YANKED]
## [0.1.2] - 2021.09.28
### Fixed
- Application modification and new creation failure issues
## [0.1.1] - 2021.09.27
## [0.1.0] - 2021.09.26
### Added
- Application Center

View File

@@ -44,6 +44,9 @@ curl -fsSL https://get.icewhale.io/casaos-uninstall.sh | bash
### System Compatibility
- Ubuntu Server 20.04 amd64 (✅ Recommend, Tested)
- Armbian 22.02 armhf/arm64/amd64 (✅ Recommend, Tested)
- Elementary OS 6.1 Jólnir amd64 (✅ Recommend, Tested)
- Deepin 20.4 amd64 (⚠️ Not Fully Tested Yet)
- Raspberry Pi Lite OS aarch64/arm64 (⚠️ Not Fully Tested Yet)
- Debian 11 amd64 (⚠️ Not Fully Tested Yet)
- OpenWrt 21.02 amd64 (⚠️ Not Fully Tested Yet)

2
UI

Submodule UI updated: a6074812f4...694c60e3a1

View File

@@ -9,34 +9,34 @@ DateTimeFormat = 2006-01-02 15:04:05
TimeFormat = 15:04:05
DateFormat = 2006-01-02
ProjectPath = /casaOS/server
RootPath = /casaOS
[server]
HttpPort = 8089
UDPPort =
RunMode = release
ServerApi = https://api.casaos.zimaboard.com
ServerApi = https://api.casaos.io/casaos-api
Handshake = socket.casaos.io
Token =
USBAutoMount =
[user]
UserName = admin
PWD = zimaboard
UserName =
PWD =
Email = user@gmail.com
Description = description
Initialized = false
Token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImVyZXJlIiwicGFzc3dvcmQiOiJhZHNmZGYiLCJleHAiOjE2MjQwMDU0ODEsImlzcyI6Imdpbi1ibG9nIn0.JNsCccZuFCwlSMLJg62iOIB2xymk_k7xGa11xhZ07bc
[zerotier]
UserName = user
PWD = pwd
Token = yBKYyavr2RdFAIVN7iTpzlsB1o6CqTgm
[redis]
Host = 127.0.0.1:6379
Password =
MaxIdle = 30
MaxActive = 30
IdleTimeout = 200
Avatar =
NickName =
PublicKey =
[system]
ConfigStr =
WidgetList =
Analyse =
[file]
ShareDir =
DownloadDir =

23
go.mod
View File

@@ -3,17 +3,24 @@ module github.com/IceWhaleTech/CasaOS
go 1.16
require (
github.com/Curtis-Milo/nat-type-identifier-go v0.0.0-20220215191915-18d42168c63d
github.com/Microsoft/go-winio v0.5.0 // indirect
github.com/Microsoft/hcsshim v0.8.22 // indirect
github.com/PuerkitoBio/goquery v1.7.0
github.com/StackExchange/wmi v0.0.0-20210224194228-fe8f1750fd46 // indirect
github.com/ambelovsky/go-structs v1.1.0 // indirect
github.com/ambelovsky/gosf v0.0.0-20201109201340-237aea4d6109
github.com/ambelovsky/gosf-socketio v0.0.0-20201109193639-add9d32f8b19 // indirect
github.com/bits-and-blooms/bitset v1.2.1 // indirect
github.com/containerd/containerd v1.5.7
github.com/containerd/continuity v0.2.0 // indirect
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-logging v0.0.0-20200710184922-b02d349568dd // indirect
github.com/forease/gotld v0.0.0-20190808124948-c50ff635576b
github.com/gin-contrib/gzip v0.0.2 // indirect
github.com/gin-contrib/gzip v0.0.2
github.com/gin-gonic/gin v1.7.2
github.com/go-ini/ini v1.62.0
github.com/go-ole/go-ole v1.2.5 // indirect
@@ -25,16 +32,21 @@ require (
github.com/gomodule/redigo v1.8.5
github.com/google/go-github/v36 v36.0.0
github.com/google/uuid v1.3.0 // indirect
github.com/googollee/go-socket.io v1.6.2
github.com/gorilla/mux v1.8.0 // indirect
github.com/gorilla/websocket v1.4.2
github.com/jinzhu/copier v0.3.2
github.com/json-iterator/go v1.1.11 // indirect
github.com/klauspost/compress v1.13.6 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/lucas-clemente/quic-go v0.25.0
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-sqlite3 v1.14.11 // indirect
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/opencontainers/selinux v1.8.5 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/pkg/errors v0.9.1
@@ -49,10 +61,10 @@ require (
github.com/swaggo/gin-swagger v1.3.0
github.com/swaggo/swag v1.7.3
github.com/tidwall/gjson v1.10.2
github.com/tidwall/sjson v1.2.3
github.com/tklauser/go-sysconf v0.3.6 // indirect
github.com/ugorji/go v1.2.6 // indirect
go.opencensus.io v0.23.0 // indirect
go.uber.org/zap v1.10.0
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5
golang.org/x/mod v0.5.0 // indirect
golang.org/x/net v0.0.0-20211020060615-d418f374d309 // indirect
@@ -66,7 +78,8 @@ require (
google.golang.org/grpc v1.41.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/ini.v1 v1.62.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
gorm.io/driver/sqlite v1.1.5
gorm.io/gorm v1.21.15
gorm.io/driver/sqlite v1.2.6
gorm.io/gorm v1.22.5
)

224
go.sum
View File

@@ -1,7 +1,9 @@
bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8=
bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
@@ -32,7 +34,12 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
@@ -47,8 +54,11 @@ github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935
github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Curtis-Milo/nat-type-identifier-go v0.0.0-20220215191915-18d42168c63d h1:62lEBImTxZ83pgzywgDNIrPPuQ+j4ep9QjqrWBn1hrU=
github.com/Curtis-Milo/nat-type-identifier-go v0.0.0-20220215191915-18d42168c63d/go.mod h1:lW9x+yEjqKdPbE3+cf2fGPJXCw/hChX3Omi9QHTLFsQ=
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
@@ -75,8 +85,6 @@ github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5
github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/PuerkitoBio/goquery v1.7.0 h1:O5SP3b9JWqMSVMG69zMfj577zwkSNpxrFf7ybS74eiw=
github.com/PuerkitoBio/goquery v1.7.0/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
@@ -92,8 +100,15 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0=
github.com/andybalholm/cascadia v1.1.0 h1:BuuO6sSfQNFRu1LppgbD25Hr2vLYW25JvxHs5zzsLTo=
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/ambelovsky/go-structs v1.1.0 h1:LXj4/mHnYw0qhXQhOo96+ULGQ88H8qMcZd5SHef8boY=
github.com/ambelovsky/go-structs v1.1.0/go.mod h1:zN3RBXQvxgjjq/Q/WZS7p5AEK+qC9mNg7ycnvoQ63Ak=
github.com/ambelovsky/gosf v0.0.0-20201109201340-237aea4d6109 h1:Tp8GVfUOEmJftBqi4+/aXTwJzm24POo6wIHeuTqaT+Y=
github.com/ambelovsky/gosf v0.0.0-20201109201340-237aea4d6109/go.mod h1:MUREokfMKREm1fOm2babarrkYdk/dGHWY+ITC3qHHPQ=
github.com/ambelovsky/gosf-socketio v0.0.0-20201109193639-add9d32f8b19 h1:suVCm9PiIhz7ftTbWQNe7u2YjVfr8AEuUiNWKWApdMM=
github.com/ambelovsky/gosf-socketio v0.0.0-20201109193639-add9d32f8b19/go.mod h1:o0+8DH+3X+FEOgSdNud0+8jJAsjtR9H3hF+O10Zcj/c=
github.com/andybalholm/brotli v1.0.1 h1:KqhlKozYbRtJvsPrrEeXcO+N2l6NYT5A2QAFmSULpEc=
github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
@@ -110,8 +125,10 @@ github.com/bits-and-blooms/bitset v1.2.1/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edY
github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
@@ -120,6 +137,8 @@ github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghf
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw=
github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M=
github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
@@ -233,6 +252,7 @@ github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
@@ -256,11 +276,14 @@ github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8l
github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY=
github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/distribution v2.8.0+incompatible h1:l9EaZDICImO1ngI+uTifW+ZYvvz7fKISBAKpg+MbWbY=
github.com/docker/distribution v2.8.0+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v20.10.7+incompatible h1:Z6O9Nhsjv+ayUEeI1IojKbYcsGdgYSNqxe1s2MYzUhQ=
github.com/docker/docker v20.10.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
@@ -275,6 +298,21 @@ github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD
github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY=
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s=
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
github.com/dsoprea/go-exif/v2 v2.0.0-20200321225314-640175a69fe4/go.mod h1:Lm2lMM2zx8p4a34ZemkaUV95AnMl4ZvLbCUbwOvLC2E=
github.com/dsoprea/go-exif/v3 v3.0.0-20200717053412-08f1b6708903/go.mod h1:0nsO1ce0mh5czxGeLo4+OCZ/C6Eo6ZlMWsz7rH/Gxv8=
github.com/dsoprea/go-exif/v3 v3.0.0-20210625224831-a6301f85c82b h1:NgNuLvW/gAFKU30ULWW0gtkCt56JfB7FrZ2zyo0wT8I=
github.com/dsoprea/go-exif/v3 v3.0.0-20210625224831-a6301f85c82b/go.mod h1:cg5SNYKHMmzxsr9X6ZeLh/nfBRHHp5PngtEPcujONtk=
github.com/dsoprea/go-logging v0.0.0-20190624164917-c4f10aab7696/go.mod h1:Nm/x2ZUNRW6Fe5C3LxdY1PyZY5wmDv/s5dkPJ/VB3iA=
github.com/dsoprea/go-logging v0.0.0-20200517223158-a10564966e9d/go.mod h1:7I+3Pe2o/YSU88W0hWlm9S22W7XI1JFNJ86U0zPKMf8=
github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd h1:l+vLbuxptsC6VQyQsfD7NnEC8BZuFpz45PgY+pH8YTg=
github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd/go.mod h1:7I+3Pe2o/YSU88W0hWlm9S22W7XI1JFNJ86U0zPKMf8=
github.com/dsoprea/go-utility v0.0.0-20200711062821-fab8125e9bdf h1:/w4QxepU4AHh3AuO6/g8y/YIIHH5+aKP3Bj8sg5cqhU=
github.com/dsoprea/go-utility v0.0.0-20200711062821-fab8125e9bdf/go.mod h1:95+K3z2L0mqsVYd6yveIv1lmtT3tcQQ3dVakPySffW8=
github.com/dsoprea/go-utility/v2 v2.0.0-20200717064901-2fccff4aa15e h1:IxIbA7VbCNrwumIYjDoMOdf4KOSkMC6NJE4s8oRbE7E=
github.com/dsoprea/go-utility/v2 v2.0.0-20200717064901-2fccff4aa15e/go.mod h1:uAzdkPTub5Y9yQwXe8W4m2XuP0tK4a9Q/dantD0+uaU=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
@@ -289,11 +327,14 @@ github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/forease/gotld v0.0.0-20190808124948-c50ff635576b h1:r13MvtFTtnvxtuKK7z0ZSQ2EfMmTzWDHwfDvGCoqUQE=
github.com/forease/gotld v0.0.0-20190808124948-c50ff635576b/go.mod h1:EfR6AU78zUiZ36oVS5YrmTzc2um3zDXWPx4L4st++jo=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA=
github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
@@ -311,6 +352,11 @@ github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
github.com/gin-gonic/gin v1.7.2 h1:Tg03T9yM2xa8j6I3Z3oqLaQRSmKvxPd6g/2HJ6zICFA=
github.com/gin-gonic/gin v1.7.2/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
github.com/go-errors/errors v1.1.1 h1:ljK/pL5ltg3qoN+OtN6yCv9HWSfMwxSx90GJCZQxYNg=
github.com/go-errors/errors v1.1.1/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
@@ -360,11 +406,14 @@ github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn
github.com/go-playground/validator/v10 v10.6.1 h1:W6TRDXt4WcWp4c4nf/G+6BkGdhiIo0k417gfr+V6u4I=
github.com/go-playground/validator/v10 v10.6.1/go.mod h1:xm76BBt941f7yWdGnI2DVPFFg1UK3YY04qifoXU3lOk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU=
github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c=
@@ -379,6 +428,9 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
github.com/golang/geo v0.0.0-20200319012246-673a6f80352d h1:C/hKUcHT483btRbeGkrRjJz+Zbcj8audldIi9tRJDCc=
github.com/golang/geo v0.0.0-20200319012246-673a6f80352d/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -387,6 +439,7 @@ github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4er
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
@@ -394,6 +447,8 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -411,6 +466,9 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.2 h1:aeE13tS0IiQgFjYdoL8qN3K1N2bXXtI6Vi51/y7BpMw=
github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/gomodule/redigo v1.8.4/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
github.com/gomodule/redigo v1.8.5 h1:nRAxCa+SVsyjSBrtZmG/cqb6VbTmuRzpg/PoTFlpumc=
github.com/gomodule/redigo v1.8.5/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
@@ -428,6 +486,8 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-github/v36 v36.0.0 h1:ndCzM616/oijwufI7nBRa+5eZHLldT+4yIB68ib5ogs=
github.com/google/go-github/v36 v36.0.0/go.mod h1:LFlKC047IOqiglRGNqNb9s/iAPTnnjtlshm+bxp+kwk=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
@@ -450,9 +510,13 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg=
github.com/googollee/go-socket.io v1.6.2 h1:olKLLHJtHz1IkL/OrTyNriZZvVQYEORNkJAqsOwPask=
github.com/googollee/go-socket.io v1.6.2/go.mod h1:0vGP8/dXR9SZUMMD4+xxaGo/lohOw3YWMh2WRiWeKxg=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
@@ -467,6 +531,7 @@ github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:Fecb
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
@@ -486,12 +551,15 @@ github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA=
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jinzhu/copier v0.3.2 h1:QdBOCbaouLDYaIPFfi1bKv5F5tPpeTwXe4sD0jqtz5w=
github.com/jinzhu/copier v0.3.2/go.mod h1:24xnZezI2Yqac9J61UC6/dG/k76ttpq0DdJI3QmUvro=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.2 h1:eVKgfIdy9b6zbWBMgFpfDPoAMifwSZagU9HmEU6zgiI=
github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.4 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas=
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
@@ -513,10 +581,15 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE=
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@@ -526,6 +599,7 @@ github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -533,8 +607,12 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
github.com/lucas-clemente/quic-go v0.25.0 h1:K+X9Gvd7JXsOHtU0N2icZ2Nw3rx82uBej3mP4CLgibc=
github.com/lucas-clemente/quic-go v0.25.0/go.mod h1:YtzP8bxRVCBlO77yRanE264+fY/T2U9ZlW1AaHOsMOg=
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
@@ -542,6 +620,14 @@ github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJ
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho=
github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=
github.com/marten-seemann/qtls-go1-15 v0.1.4/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I=
github.com/marten-seemann/qtls-go1-16 v0.1.4 h1:xbHbOGGhrenVtII6Co8akhLEdrawwB2iHl5yhJRpnco=
github.com/marten-seemann/qtls-go1-16 v0.1.4/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk=
github.com/marten-seemann/qtls-go1-17 v0.1.0 h1:P9ggrs5xtwiqXv/FHNwntmuLMNq3KaSIG93AtAZ48xk=
github.com/marten-seemann/qtls-go1-17 v0.1.0/go.mod h1:fz4HIxByo+LlWcreM4CZOYNuz3taBQ8rN2X6FqvaWo8=
github.com/marten-seemann/qtls-go1-18 v0.1.0-beta.1 h1:EnzzN9fPUkUck/1CuY1FlzBaIYMoiBsdwTNmNGkwUUM=
github.com/marten-seemann/qtls-go1-18 v0.1.0-beta.1/go.mod h1:PUhIQk19LoFt2174H4+an8TYvWOGjb/hHwphBeaDHwI=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
@@ -551,13 +637,18 @@ github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
github.com/mattn/go-sqlite3 v1.14.8 h1:gDp86IdQsN/xWjIEmr9MF6o9mpksUgh0fu+9ByFxzIU=
github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.11 h1:gt+cp9c0XGqe9S/wAHTL3n/7MqY+siPWgWJgqdsFrzQ=
github.com/mattn/go-sqlite3 v1.14.11/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo=
github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4=
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
@@ -583,8 +674,14 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM=
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7MQ=
github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@@ -594,11 +691,18 @@ github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+
github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E=
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
github.com/onsi/gomega v1.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak=
github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY=
github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
@@ -606,8 +710,9 @@ github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go.
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
@@ -628,11 +733,14 @@ github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3
github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8=
github.com/opencontainers/selinux v1.8.5 h1:OkT6bMHOQ1JQQO4ihjQ49sj0+wciDcjziSVTRn8VeTA=
github.com/opencontainers/selinux v1.8.5/go.mod h1:HTvjPFoGMbpQsG886e3lQwnsRWtE4TC1OF3OUvG9FAo=
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/pierrec/lz4/v4 v4.1.2 h1:qvY3YFXRQE/XB8MlLzJH7mSzBs74eA2gg52YTk6jUPM=
github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -644,6 +752,7 @@ github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prY
github.com/prestonTao/upnp v0.0.0-20150206124352-f4370df5e109 h1:h9WYaTCQJ7hap8C5vQniEum2YZbc+iRad/ROafTjy10=
github.com/prestonTao/upnp v0.0.0-20150206124352-f4370df5e109/go.mod h1:U7VCLF6LMHzOFD/6Kww2MTQuwaNeEA1U1dOxFyZBoBE=
github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
@@ -655,12 +764,14 @@ github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
@@ -679,15 +790,39 @@ github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfm
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shirou/gopsutil/v3 v3.21.5 h1:YUBf0w/KPLk7w1803AYBnH7BmA+1Z/Q5MEZxpREUaB4=
github.com/shirou/gopsutil/v3 v3.21.5/go.mod h1:ghfMypLDrFSWN2c9cDYFLHyynQ+QUht0cv/18ZqVczw=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=
github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI=
github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU=
github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag=
github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg=
github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw=
github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y=
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q=
github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ=
github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I=
github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0=
github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ=
github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk=
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
@@ -704,6 +839,8 @@ github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:s
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
@@ -740,6 +877,7 @@ github.com/swaggo/swag v1.7.3/go.mod h1:zD8h6h4SPv7t3l+4BKdRquqW1ASWjKZgT6Qv9z3k
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I=
github.com/tidwall/gjson v1.10.2 h1:APbLGOM0rrEkd8WBw9C24nllro4ajFuJu0Sc9hRz8Bo=
github.com/tidwall/gjson v1.10.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
@@ -747,8 +885,6 @@ github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/sjson v1.2.3 h1:5+deguEhHSEjmuICXZ21uSSsXotWMA0orU783+Z7Cp8=
github.com/tidwall/sjson v1.2.3/go.mod h1:5WdjKx3AQMvCJ4RG6/2UYT7dLrGvJUV1x4jdTAyGvZs=
github.com/tklauser/go-sysconf v0.3.4/go.mod h1:Cl2c8ZRWfHD5IrfHo9VN+FX9kCFjIOyVklgXycLB6ek=
github.com/tklauser/go-sysconf v0.3.6 h1:oc1sJWvKkmvIxhDHeKWvZS4f6AW+YcoguSfRF2/Hmo4=
github.com/tklauser/go-sysconf v0.3.6/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI=
@@ -768,11 +904,16 @@ github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLY
github.com/ugorji/go/codec v1.1.13/go.mod h1:oNVt3Dq+FO91WNQ/9JnHKQP2QJxTzoN7wCBFCq1OeuU=
github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ=
github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw=
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ulikunitz/xz v0.5.9 h1:RsKRIA2MO8x56wkkcd3LbtcE/uMszhb6DpRf+3uwa3I=
github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
@@ -784,12 +925,15 @@ github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs=
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
@@ -799,6 +943,7 @@ go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg=
go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk=
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
@@ -808,18 +953,26 @@ go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@@ -838,6 +991,9 @@ golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EH
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 h1:hVwzHzIUGRjiF7EcUjqNxk3NCfkPxbDKRdnNE1Rpg0U=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -859,17 +1015,19 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.0 h1:UG21uOlmZabA4fW5i7ZX6bjw1xELEGg/ZLgZq9auk/Q=
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -890,10 +1048,12 @@ golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200320220750-118fecf932d8/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
@@ -906,16 +1066,20 @@ golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211020060615-d418f374d309 h1:A0lJIi+hcTR6aajJH4YqKWwohY4aW9RO7oRMcdv+HKI=
golang.org/x/net v0.0.0-20211020060615-d418f374d309/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f h1:Qmd2pbz05z7z6lm0DrgQVVPuBm92jqujBKMHMOlOQEw=
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -931,12 +1095,14 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -980,6 +1146,7 @@ golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -995,6 +1162,7 @@ golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210217105451-b926d437f341/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1032,7 +1200,9 @@ golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxb
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs=
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@@ -1079,8 +1249,10 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.7 h1:6j8CgantCy3yc8JGBqkDLMKWqZ0RDU2g1HVgacojGWQ=
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1089,6 +1261,9 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
@@ -1106,6 +1281,8 @@ google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
@@ -1115,6 +1292,10 @@ google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
@@ -1150,6 +1331,9 @@ google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0 h1:5Tbluzus3QxoAJx4IefGt1W0HQZW4nuMrVk684jI74Q=
google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -1203,11 +1387,13 @@ gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/R
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@@ -1215,6 +1401,7 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
@@ -1223,15 +1410,18 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/sqlite v1.1.5 h1:JU8G59VyKu1x1RMQgjefQnkZjDe9wHc1kARDZPu5dZs=
gorm.io/driver/sqlite v1.1.5/go.mod h1:NpaYMcVKEh6vLJ47VP6T7Weieu4H1Drs3dGD/K6GrGc=
gorm.io/gorm v1.21.15 h1:gAyaDoPw0lCyrSFWhBlahbUA1U4P5RViC1uIqoB+1Rk=
gorm.io/gorm v1.21.15/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
gorm.io/driver/sqlite v1.2.6 h1:SStaH/b+280M7C8vXeZLz/zo9cLQmIGwwj3cSj7p6l4=
gorm.io/driver/sqlite v1.2.6/go.mod h1:gyoX0vHiiwi0g49tv+x2E7l8ksauLK0U/gShcdUsjWY=
gorm.io/gorm v1.22.3/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
gorm.io/gorm v1.22.5 h1:lYREBgc02Be/5lSCTuysZZDb6ffL2qrat6fg9CFbvXU=
gorm.io/gorm v1.22.5/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0=
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@@ -1273,3 +1463,5 @@ sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK
sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=

68
main.go
View File

@@ -6,10 +6,11 @@ import (
"net/http"
"time"
"github.com/IceWhaleTech/CasaOS/model/notify"
"github.com/IceWhaleTech/CasaOS/pkg/cache"
"github.com/IceWhaleTech/CasaOS/pkg/config"
"github.com/IceWhaleTech/CasaOS/pkg/sqlite"
loger2 "github.com/IceWhaleTech/CasaOS/pkg/utils/loger"
"github.com/IceWhaleTech/CasaOS/pkg/utils/loger"
"github.com/IceWhaleTech/CasaOS/route"
"github.com/IceWhaleTech/CasaOS/service"
@@ -20,19 +21,36 @@ import (
var sqliteDB *gorm.DB
var configFlag = flag.String("c", "", "config address")
var dbFlag = flag.String("db", "", "db path")
var showUserInfo = flag.Bool("show-user-info", false, "show user info")
func init() {
flag.Parse()
config.InitSetup(*configFlag)
config.UpdateSetup()
loger2.LogSetup()
sqliteDB = sqlite.GetDb(config.AppInfo.ProjectPath)
loger.LogInit()
if len(*dbFlag) == 0 {
*dbFlag = config.AppInfo.ProjectPath + "/db"
}
sqliteDB = sqlite.GetDb(*dbFlag)
//gredis.GetRedisConn(config.RedisInfo),
service.MyService = service.NewService(sqliteDB, loger2.NewOLoger())
service.MyService = service.NewService(sqliteDB)
service.Cache = cache.Init()
go service.UDPService()
fmt.Println("t", service.GetToken())
service.UDPAddressMap = make(map[string]string)
//go service.SocketConnect()
service.CancelList = make(map[string]string)
service.InternalInspection = make(map[string][]string)
service.NewVersionApp = make(map[string]string)
route.InitFunction()
go service.SendIPToServer()
// go service.LoopFriend()
// go service.MyService.App().CheckNewImage()
}
// @title casaOS API
@@ -47,27 +65,53 @@ func init() {
// @name Authorization
// @BasePath /v1
func main() {
service.NotifyMsg = make(chan notify.Message, 10)
if *showUserInfo {
fmt.Println("CasaOS User Info")
fmt.Println("UserName:" + config.UserInfo.UserName)
fmt.Println("Password:" + config.UserInfo.PWD)
return
}
go route.SocketInit(service.NotifyMsg)
go func() {
for i := 0; i < 1000; i++ {
time.Sleep(2 * time.Second)
//service.NotifyMsg <- strconv.Itoa(i)
}
}()
//model.Setup()
//gredis.Setup()
r := route.InitRouter()
//service.SyncTask(sqliteDB)
cron2 := cron.New() //创建一个cron实例
//执行定时任务每5秒执行一次
err := cron2.AddFunc("0 0 0 1/1 * *", func() {
cron2 := cron.New()
//every day execution
err := cron2.AddFunc("0 0/5 * * * *", func() {
//service.PushIpInfo(*&config.ServerInfo.Token)
//service.UpdataDDNSList(mysqldb)
//service.SyncTask(sqliteDB)
service.SendIPToServer()
service.LoopFriend()
//service.MyService.App().CheckNewImage()
})
if err != nil {
fmt.Println(err)
}
err = cron2.AddFunc("0/3 * * * * *", func() {
if service.ClientCount > 0 {
route.SendNetINfoBySocket()
route.SendCPUBySocket()
route.SendMemBySocket()
route.SendDiskBySocket()
route.SendUSBBySocket()
}
})
if err != nil {
fmt.Println(err)
}
//启动/关闭
cron2.Start()
defer cron2.Stop()
s := &http.Server{
@@ -77,6 +121,10 @@ func main() {
WriteTimeout: 60 * time.Second,
MaxHeaderBytes: 1 << 20,
}
s.ListenAndServe()
// if err := r.Run(fmt.Sprintf(":%v", config.ServerInfo.HttpPort)); err != nil {
// fmt.Println("failed run app: ", err)
// }
}

View File

@@ -1,3 +1,13 @@
/*
* @Author: LinkLeong link@icewhale.com
* @Date: 2021-10-08 10:29:08
* @LastEditors: LinkLeong
* @LastEditTime: 2022-05-25 19:17:45
* @FilePath: /CasaOS/middleware/gin.go
* @Description:
* @Website: https://www.casaos.io
* Copyright (c) 2022 by icewhale, All Rights Reserved.
*/
package middleware
import (
@@ -10,20 +20,16 @@ import (
func Cors() gin.HandlerFunc {
return func(c *gin.Context) {
method := c.Request.Method
//origin := c.Request.Header.Get("Origin") //请求头部
//if origin != "" {
//接收客户端发送的origin (重要!)
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
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")
// 允许浏览器(客户端)可以解析的头部 (重要)
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")
//允许客户端传递校验信息比如 cookie (重要)
c.Header("Access-Control-Allow-Credentials", "true")
c.Set("content-type", "application/json")
//}

27
model/app-analyse.go Normal file
View File

@@ -0,0 +1,27 @@
/*
* @Author: LinkLeong link@icewhale.com
* @Date: 2022-03-18 11:40:55
* @LastEditors: LinkLeong
* @LastEditTime: 2022-05-13 14:48:01
* @FilePath: /CasaOS/model/app-analyse.go
* @Description:
* @Website: https://www.casaos.io
* Copyright (c) 2022 by icewhale, All Rights Reserved.
*/
package model
type AppAnalyse struct {
Name string `json:"name"`
Type string `json:"type"`
UUId string `json:"uuid"`
Language string `json:"language"`
Version string `json:"version"`
}
type ConnectionStatus struct {
From string `json:"from"`
To string `json:"to"`
Error string `json:"error"`
UUId string `json:"uuid"`
Event string `json:"event"`
}

View File

@@ -39,6 +39,10 @@ type ServerAppList struct {
Origin string `json:"origin"`
Type int `json:"type"`
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 {

View File

@@ -1,5 +1,18 @@
/*
* @Author: link a624669980@163.com
* @Date: 2022-05-16 17:37:08
* @LastEditors: link a624669980@163.com
* @LastEditTime: 2022-06-07 17:12:30
* @FilePath: \CasaOS\model\category.go
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
package model
// type ServerCategoryList struct {
// Version string `json:"version"`
// Item []CategoryList `json:"item"`
// }
type ServerCategoryList struct {
Id uint `gorm:"column:id;primary_key" json:"id"`
//CreatedAt time.Time `json:"created_at"`

View File

@@ -20,6 +20,7 @@ type LSBLKModel struct {
Format string `json:"format"`
Health string `json:"health"`
HotPlug bool `json:"hotplug"`
UUID string `json:"uuid"`
FSUsed string `json:"fsused"`
Temperature int `json:"temperature"`
Tran string `json:"tran"`
@@ -47,6 +48,15 @@ type Drive struct {
Path string `json:"path"`
}
type DriveUSB struct {
Name string `json:"name"`
Size uint64 `json:"size"`
Used uint64 `json:"use"`
Model string `json:"model"`
Mount bool `json:"mount"` //是否完全挂载
Avail uint64 `json:"avail"` //可用空间
}
type Storage struct {
Name string `json:"name"`
MountPoint string `json:"mountpoint"`

31
model/file.go Normal file
View File

@@ -0,0 +1,31 @@
/*
* @Author: LinkLeong link@icewhale.com
* @Date: 2022-05-20 16:27:12
* @LastEditors: LinkLeong
* @LastEditTime: 2022-06-08 15:40:33
* @FilePath: /CasaOS/model/file.go
* @Description:
* @Website: https://www.casaos.io
* Copyright (c) 2022 by icewhale, All Rights Reserved.
*/
package model
type FileOperate struct {
Type string `json:"type" binding:"required"`
Item []FileItem `json:"item" binding:"required"`
TotalSize int64 `json:"total_size"`
ProcessedSize int64 `json:"processed_size"`
To string `json:"to" binding:"required"`
}
type FileItem struct {
From string `json:"from" binding:"required"`
Finished bool `json:"finished"`
Size int64 `json:"size"`
ProcessedSize int64 `json:"processed_size"`
}
type FileUpdate struct {
FilePath string `json:"path" binding:"required"`
FileContent string `json:"content" binding:"required"`
}

6
model/heart.go Normal file
View File

@@ -0,0 +1,6 @@
package model
type CasaOSHeart struct {
UuId string `json:"uuid"`
Type string `json:"type"`
}

View File

@@ -104,6 +104,7 @@ func (p *PathArray) Scan(input interface{}) error {
//}
type CustomizationPostData struct {
CustomId string `json:"custom_id"`
Origin string `json:"origin"`
NetworkModel string `json:"network_model"`
Index string `json:"index"`
@@ -122,4 +123,10 @@ type CustomizationPostData struct {
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,7 +1,5 @@
package model
import "time"
type IOCountersStat struct {
Name string `json:"name"` // interface name
BytesSent uint64 `json:"bytesSent"` // number of bytes sent
@@ -15,5 +13,5 @@ type IOCountersStat struct {
Fifoin uint64 `json:"fifoin"` // total number of FIFO buffers errors while receiving
Fifoout uint64 `json:"fifoout"` // total number of FIFO buffers errors while sending
State string `json:"state"`
DateTime time.Time `json:"date_time"`
Time int64 `json:"time"`
}

6
model/notify.go Normal file
View File

@@ -0,0 +1,6 @@
package model
type NotifyMssage struct {
Type string `json:"type"`
Data string `json:"data"`
}

View File

@@ -0,0 +1,21 @@
/*
* @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"`
}

22
model/notify/file.go Normal file
View File

@@ -0,0 +1,22 @@
/*
* @Author: LinkLeong link@icewhale.com
* @Date: 2022-05-26 14:21:57
* @LastEditors: LinkLeong
* @LastEditTime: 2022-06-02 11:14:15
* @FilePath: /CasaOS/model/notify/file.go
* @Description:
* @Website: https://www.casaos.io
* Copyright (c) 2022 by icewhale, All Rights Reserved.
*/
package notify
type File struct {
Finished bool `json:"finished"`
ProcessedSize int64 `json:"processed_size"`
ProcessingPath string `json:"processing_path"`
Status string `json:"status"`
TotalSize int64 `json:"total_size"`
Id string `json:"id"`
To string `json:"to"`
Type string `json:"type"`
}

20
model/notify/message.go Normal file
View File

@@ -0,0 +1,20 @@
/*
* @Author: LinkLeong link@icewhale.com
* @Date: 2022-05-26 14:39:22
* @LastEditors: LinkLeong
* @LastEditTime: 2022-05-26 19:08:52
* @FilePath: /CasaOS/model/notify/message.go
* @Description:
* @Website: https://www.casaos.io
* Copyright (c) 2022 by icewhale, All Rights Reserved.
*/
package notify
import (
f "github.com/ambelovsky/gosf"
)
type Message struct {
Path string `json:"path"`
Msg f.Message `json:"msg"`
}

16
model/notify/person.go Normal file
View File

@@ -0,0 +1,16 @@
/*
* @Author: LinkLeong link@icewhale.com
* @Date: 2022-05-27 18:42:42
* @LastEditors: LinkLeong
* @LastEditTime: 2022-05-27 18:43:08
* @FilePath: /CasaOS/model/notify/person.go
* @Description:
* @Website: https://www.casaos.io
* Copyright (c) 2022 by icewhale, All Rights Reserved.
*/
package notify
type Person struct {
ShareId string `json:"share_id"`
Type string `json:"type"`
}

18
model/notify/result.go Normal file
View File

@@ -0,0 +1,18 @@
/*
* @Author: LinkLeong link@icewhale.com
* @Date: 2022-05-26 14:21:11
* @LastEditors: LinkLeong
* @LastEditTime: 2022-05-27 11:15:59
* @FilePath: /CasaOS/model/notify/result.go
* @Description:
* @Website: https://www.casaos.io
* Copyright (c) 2022 by icewhale, All Rights Reserved.
*/
package notify
// Notify struct for Notify
type NotifyModel struct {
Data interface{} `json:"data"`
State string `json:"state"`
}

54
model/person.go Normal file
View File

@@ -0,0 +1,54 @@
package model
import "time"
type PersionModel struct {
Token string `json:"token"`
Ips []string `json:"ips"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
//记录链接状态
type ConnectState struct {
From string `json:"from"`
To string `json:"to"`
Type string `json:"type"` //current state 1:ready 2:ok
CreatedAt time.Time `json:"created_at"`
UUId string `json:"uuid"` //对接标识
}
type MessageModel struct {
Type string `json:"type"`
Data interface{} `json:"data"`
UUId string `json:"uuid"`
From string `json:"from"`
To string `json:"to"`
}
type TranFileModel struct {
Hash string `json:"hash"` //Verify current fragment integrity
Length int `json:"length"`
Index int `json:"index"`
}
//需要获取文件详情
type FileDetailModel struct {
Path string `json:"path"`
}
//返回文件详情
type FileSummaryModel struct {
Hash string `json:"hash"` //Verify file
Name string `json:"name"`
BlockSize int `json:"block_size"`
Length int `json:"length"`
Size int64 `json:"size"`
Message string `json:"message"`
}
type FriendsModel struct {
Id uint `gorm:"column:id;primary_key" json:"id"`
NickName string `json:"nick_name"`
Desc string `json:"desc"`
ShareId string `json:"share_id"`
}

1
model/receive/app.go Normal file
View File

@@ -0,0 +1 @@
package receive

View File

@@ -1,3 +1,13 @@
/*
* @Author: LinkLeong link@icewhale.com
* @Date: 2022-05-13 18:15:46
* @LastEditors: LinkLeong
* @LastEditTime: 2022-05-30 16:43:59
* @FilePath: /CasaOS/model/sys_common.go
* @Description:
* @Website: https://www.casaos.io
* Copyright (c) 2022 by icewhale, All Rights Reserved.
*/
package model
import "time"
@@ -16,6 +26,9 @@ type UserModel struct {
Email string
Description string
Initialized bool
Avatar string
NickName string
Public string
}
//服务配置
@@ -24,6 +37,11 @@ type ServerModel struct {
RunMode string
ServerApi string
LockAccount bool
Handshake string
Token string
UDPPort string
USBAutoMount string
SocketPort string
}
//服务配置
@@ -36,6 +54,7 @@ type APPModel struct {
TimeFormat string
DateFormat string
ProjectPath string
RootPath string
}
//公共返回模型
@@ -45,13 +64,6 @@ type Result struct {
Data interface{} `json:"data" example:"返回结果"`
}
//zeritier相关
type ZeroTierModel struct {
UserName string
PWD string
Token string
}
//redis配置文件
type RedisModel struct {
Host string
@@ -67,8 +79,14 @@ type SystemConfig struct {
ConfigPath string `json:"config_path"`
SyncPort string `json:"sync_port"`
SyncKey string `json:"sync_key"`
Analyse string `json:"analyse"`
}
type CasaOSGlobalVariables struct {
AppChange bool
}
type FileSetting struct {
ShareDir []string `json:"share_dir" delim:"|"`
DownloadDir string `json:"download_dir"`
}

9
model/user.go Normal file
View File

@@ -0,0 +1,9 @@
package model
type UserInfo struct {
NickName string `json:"nick_name"`
Desc string `json:"desc"`
ShareId string `json:"share_id"`
Avatar string `json:"avatar"`
Version int `json:"version,omitempty"`
}

View File

@@ -4,7 +4,7 @@ import "time"
type Version struct {
Id uint `gorm:"column:id;primary_key" json:"id"`
ChangLog string `json:"chang_log"`
ChangeLog string `json:"change_log"`
Version string `json:"version"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`

View File

@@ -1,9 +0,0 @@
package model
type ZeroTierUpData struct {
Config ZeroTierConfig `json:"config"`
}
type ZeroTierConfig struct {
Private bool `json:"private"`
}

View File

@@ -3,8 +3,12 @@ package model
import "time"
type Path struct {
Name string `json:"name"`
Path string `json:"path"`
IsDir bool `json:"is_dir"`
Name string `json:"name"` //File name or document name
Path string `json:"path"` //Full path to file or folder
IsDir bool `json:"is_dir"` //Is it a folder
Date time.Time `json:"date"`
Size int64 `json:"size"` //File Size
Type string `json:"type,omitempty"`
Label string `json:"label,omitempty"`
Write bool `json:"write"`
}

View File

@@ -22,11 +22,7 @@ var UserInfo = &model.UserModel{}
//用户相关
var AppInfo = &model.APPModel{}
//redis相关配置
var RedisInfo = &model.RedisModel{}
//zerotier相关
var ZeroTierInfo = &model.ZeroTierModel{}
//var RedisInfo = &model.RedisModel{}
//server相关
var ServerInfo = &model.ServerModel{}
@@ -35,6 +31,8 @@ var SystemConfigInfo = &model.SystemConfig{}
var CasaOSGlobalVariables = &model.CasaOSGlobalVariables{}
var FileSettingInfo = &model.FileSetting{}
var Cfg *ini.File
//初始化设置,获取系统的部分信息。
@@ -54,10 +52,10 @@ func InitSetup(config string) {
mapTo("user", UserInfo)
mapTo("app", AppInfo)
mapTo("zerotier", ZeroTierInfo)
mapTo("redis", RedisInfo)
//mapTo("redis", RedisInfo)
mapTo("server", ServerInfo)
mapTo("system", SystemConfigInfo)
mapTo("file", FileSettingInfo)
SystemConfigInfo.ConfigPath = configDir
// AppInfo.ProjectPath = getCurrentDirectory() //os.Getwd()

View File

@@ -1,13 +1,30 @@
package config
import "github.com/IceWhaleTech/CasaOS/pkg/utils/file"
import (
"runtime"
"github.com/IceWhaleTech/CasaOS/pkg/utils/file"
)
//检查目录是否存在
func mkdirDATAAll() {
dirArray := [7]string{"/DATA/AppData", "/DATA/Documents", "/DATA/Downloads", "/DATA/Gallery", "/DATA/Media/Movies", "/DATA/Media/TV Shows", "/DATA/Media/Music"}
sysType := runtime.GOOS
var dirArray []string
if sysType == "linux" {
dirArray = []string{"/DATA/AppData", "/DATA/Documents", "/DATA/Downloads", "/DATA/Gallery", "/DATA/Media/Movies", "/DATA/Media/TV Shows", "/DATA/Media/Music"}
}
if sysType == "windows" {
dirArray = []string{"C:\\CasaOS\\DATA\\AppData", "C:\\CasaOS\\DATA\\Documents", "C:\\CasaOS\\DATA\\Downloads", "C:\\CasaOS\\DATA\\Gallery", "C:\\CasaOS\\DATA\\Media/Movies", "C:\\CasaOS\\DATA\\Media\\TV Shows", "C:\\CasaOS\\DATA\\Media\\Music"}
}
if sysType == "darwin" {
dirArray = []string{"./CasaOS/DATA/AppData", "./CasaOS/DATA/Documents", "./CasaOS/DATA/Downloads", "./CasaOS/DATA/Gallery", "./CasaOS/DATA/Media/Movies", "./CasaOS/DATA/Media/TV Shows", "./CasaOS/DATA/Media/Music"}
}
for _, v := range dirArray {
file.IsNotExistMkDir(v)
}
}
func UpdateSetup() {

View File

@@ -9,7 +9,7 @@ import (
func GetGithubClient() *github.Client {
ctx := context.Background()
ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: "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"},
&oauth2.Token{AccessToken: ""},
)
tc := oauth2.NewClient(ctx, ts)
client := github.NewClient(tc)

51
pkg/quic_helper/config.go Normal file
View File

@@ -0,0 +1,51 @@
package quic_helper
import (
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"encoding/pem"
"math/big"
"github.com/lucas-clemente/quic-go"
)
// Setup a bare-bones TLS config for the server
func GetGenerateTLSConfig(token string) *tls.Config {
key, err := rsa.GenerateKey(rand.Reader, 1024)
if err != nil {
panic(err)
}
template := x509.Certificate{SerialNumber: big.NewInt(1)}
certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key)
if err != nil {
panic(err)
}
keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)})
certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER})
tlsCert, err := tls.X509KeyPair(certPEM, keyPEM)
if err != nil {
panic(err)
}
return &tls.Config{
Certificates: []tls.Certificate{tlsCert},
NextProtos: []string{token},
SessionTicketsDisabled: true,
}
}
func GetClientTlsConfig(otherToken string) *tls.Config {
return &tls.Config{
InsecureSkipVerify: true,
NextProtos: []string{otherToken},
SessionTicketsDisabled: true,
}
}
func GetQUICConfig() *quic.Config {
return &quic.Config{
ConnectionIDLength: 4,
KeepAlive: true,
}
}

View File

@@ -12,15 +12,15 @@ import (
var gdb *gorm.DB
func GetDb(projectPath string) *gorm.DB {
func GetDb(dbPath string) *gorm.DB {
if gdb != nil {
return gdb
}
// 参考 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)
//db, err := gorm.Open(mysql2.Open(dsn), &gorm.Config{})
file.IsNotExistMkDir(projectPath + "/db/")
db, err := gorm.Open(sqlite.Open(projectPath+"/db/casaOS.db"), &gorm.Config{})
file.IsNotExistMkDir(dbPath)
db, err := gorm.Open(sqlite.Open(dbPath+"/casaOS.db"), &gorm.Config{})
c, _ := db.DB()
c.SetMaxIdleConns(10)
c.SetMaxOpenConns(100)
@@ -31,7 +31,7 @@ func GetDb(projectPath string) *gorm.DB {
return nil
}
gdb = db
err = db.AutoMigrate(&model2.TaskDBModel{}, &model2.AppNotify{}, &model2.AppListDBModel{}, &model2.SerialDisk{})
err = db.AutoMigrate(&model2.TaskDBModel{}, &model2.AppNotify{}, &model2.AppListDBModel{}, &model2.SerialDisk{}, model2.PersonDownloadDBModel{}, model2.FriendModel{}, model2.PersonDownRecordDBModel{}, model2.ApplicationModel{})
if err != nil {
fmt.Println("检查和创建数据库出错", err)
}

View File

@@ -101,7 +101,6 @@ func ExecSmartCTLByPath(path string) []byte {
output, err := exec.CommandContext(ctx, "smartctl", "-a", path, "-j").Output()
if err != nil {
fmt.Println("smartctl", err)
fmt.Println("output", string(output))
return nil
}
return output

81
pkg/utils/file/block.go Normal file
View File

@@ -0,0 +1,81 @@
package file
import (
"crypto/md5"
"encoding/hex"
"io"
"math"
"os"
"strconv"
)
// Get info of block
func GetBlockInfo(fileSize int64) (blockSize int, length int) {
switch {
case fileSize <= 1<<28: //256M
blockSize = 1 << 17 //128kb
case fileSize <= 1<<29: //512M
blockSize = 1 << 18 //256kb
case fileSize <= 1<<30: //1G
blockSize = 1 << 19 //512kb
case fileSize <= 1<<31: //2G
blockSize = 1 << 20 //(mb)
case fileSize <= 1<<32: //4G
blockSize = 1 << 21 //2mb
case fileSize <= 1<<33: //8G
blockSize = 1 << 22 //4mb
case fileSize <= 1<<34: //16g
blockSize = 1 << 23 //8mb
default:
blockSize = 1 << 24 //16mb
}
temp := float64(fileSize) / float64(blockSize)
length = int(math.Ceil(temp))
return
}
//get the hash of the data
func GetHashByContent(data []byte) string {
sum := md5.Sum(data)
return hex.EncodeToString(sum[:])
}
//get the hash of the data
func GetHashByPath(path string) string {
pFile, err := os.Open(path)
if err != nil {
return ""
}
defer pFile.Close()
md5h := md5.New()
io.Copy(md5h, pFile)
return hex.EncodeToString(md5h.Sum(nil))
}
//Comparison data hash
func ComparisonHash(data []byte, hash string) bool {
sum := md5.Sum(data)
return hex.EncodeToString(sum[:]) == hash
}
//get prefix byte length
func PrefixLength(byteLength int) []byte {
lengthByte := []byte{'0', '0', '0', '0', '0', '0'}
bSize := strconv.Itoa(byteLength)
cha := 6 - len(bSize)
for i := len(bSize); i > 0; i-- {
lengthByte[cha+i-1] = bSize[i-1]
}
return lengthByte
}
//get data byte length
func DataLength(length int) []byte {
lengthByte := []byte{'0', '0', '0', '0', '0', '0', '0', '0'}
bSize := strconv.Itoa(length)
cha := 8 - len(bSize)
for i := len(bSize); i > 0; i-- {
lengthByte[cha+i-1] = bSize[i-1]
}
return lengthByte
}

View File

@@ -1,11 +1,21 @@
package file
import (
"bufio"
"errors"
"fmt"
"io"
"io/ioutil"
"log"
"mime/multipart"
"os"
"path"
path2 "path"
"path/filepath"
"strconv"
"strings"
"github.com/mholt/archiver/v3"
)
// GetSize get the file size
@@ -35,7 +45,7 @@ func CheckPermission(src string) bool {
// IsNotExistMkDir create a directory if it does not exist
func IsNotExistMkDir(src string) error {
if notExist := CheckNotExist(src); notExist == true {
if notExist := CheckNotExist(src); notExist {
if err := MkDir(src); err != nil {
return err
}
@@ -136,6 +146,21 @@ func CreateFile(path string) error {
return nil
}
func CreateFileAndWriteContent(path string, content string) error {
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
return err
}
defer file.Close()
write := bufio.NewWriter(file)
write.WriteString(content)
write.Flush()
return nil
}
// IsNotExistMkDir create a directory if it does not exist
func IsNotExistCreateFile(src string) error {
if notExist := CheckNotExist(src); notExist == true {
@@ -159,3 +184,301 @@ func ReadFullFile(path string) []byte {
}
return content
}
// File copies a single file from src to dst
func CopyFile(src, dst string) error {
var err error
var srcfd *os.File
var dstfd *os.File
var srcinfo os.FileInfo
lastPath := src[strings.LastIndex(src, "/")+1:]
if !strings.HasSuffix(dst, "/") {
dst += "/"
}
dstPath := dst
dst += lastPath
for i := 0; Exists(dst); i++ {
name := strings.Split(lastPath, ".")
nameIndex := 0
if len(name) > 2 {
nameIndex = len(name) - 2
}
name[nameIndex] = name[nameIndex] + "(Copy)"
dst = dstPath
for _, v := range name {
dst += v + "."
}
dst = strings.TrimSuffix(dst, ".")
}
if srcfd, err = os.Open(src); err != nil {
return err
}
defer srcfd.Close()
if dstfd, err = os.Create(dst); err != nil {
return err
}
defer dstfd.Close()
if _, err = io.Copy(dstfd, srcfd); err != nil {
return err
}
if srcinfo, err = os.Stat(src); err != nil {
return err
}
return os.Chmod(dst, srcinfo.Mode())
}
//Check for duplicate file names
func GetNoDuplicateFileName(fullPath string) string {
path, fileName := filepath.Split(fullPath)
fileSuffix := path2.Ext(fileName)
filenameOnly := strings.TrimSuffix(fileName, fileSuffix)
for i := 0; Exists(fullPath); i++ {
fullPath = path2.Join(path, filenameOnly+"("+strconv.Itoa(i+1)+")"+fileSuffix)
}
return fullPath
}
// Dir copies a whole directory recursively
func CopyDir(src string, dst string) error {
var err error
var fds []os.FileInfo
var srcinfo os.FileInfo
if srcinfo, err = os.Stat(src); err != nil {
return err
}
if !srcinfo.IsDir() {
if err = CopyFile(src, dst); err != nil {
fmt.Println(err)
}
return nil
}
dstPath := dst
lastPath := src[strings.LastIndex(src, "/")+1:]
dst += "/" + lastPath
for i := 0; Exists(dst); i++ {
dst = dstPath + "/" + lastPath + strconv.Itoa(i+1)
}
if err = os.MkdirAll(dst, srcinfo.Mode()); err != nil {
return err
}
if fds, err = ioutil.ReadDir(src); err != nil {
return err
}
for _, fd := range fds {
srcfp := path.Join(src, fd.Name())
dstfp := dst //path.Join(dst, fd.Name())
if fd.IsDir() {
if err = CopyDir(srcfp, dstfp); err != nil {
fmt.Println(err)
}
} else {
if err = CopyFile(srcfp, dstfp); err != nil {
fmt.Println(err)
}
}
}
return nil
}
func WriteToPath(data []byte, path, name string) error {
fullPath := path
if strings.HasSuffix(path, "/") {
fullPath += name
} else {
fullPath += "/" + name
}
IsNotExistCreateFile(fullPath)
file, err := os.OpenFile(fullPath,
os.O_WRONLY|os.O_TRUNC|os.O_CREATE,
0666,
)
if err != nil {
return err
}
defer file.Close()
_, err = file.Write(data)
return err
}
//最终拼接
func SpliceFiles(dir, path string, length int, startPoint int) error {
fullPath := path
IsNotExistCreateFile(fullPath)
file, _ := os.OpenFile(fullPath,
os.O_WRONLY|os.O_TRUNC|os.O_CREATE,
0666,
)
defer file.Close()
bufferedWriter := bufio.NewWriter(file)
for i := 0; i < length+startPoint; i++ {
data, err := ioutil.ReadFile(dir + "/" + strconv.Itoa(i+startPoint))
if err != nil {
return err
}
_, err = bufferedWriter.Write(data)
if err != nil {
return err
}
}
bufferedWriter.Flush()
return nil
}
func GetCompressionAlgorithm(t string) (string, archiver.Writer, error) {
switch t {
case "zip", "":
return ".zip", archiver.NewZip(), nil
case "tar":
return ".tar", archiver.NewTar(), nil
case "targz":
return ".tar.gz", archiver.NewTarGz(), nil
case "tarbz2":
return ".tar.bz2", archiver.NewTarBz2(), nil
case "tarxz":
return ".tar.xz", archiver.NewTarXz(), nil
case "tarlz4":
return ".tar.lz4", archiver.NewTarLz4(), nil
case "tarsz":
return ".tar.sz", archiver.NewTarSz(), nil
default:
return "", nil, errors.New("format not implemented")
}
}
func AddFile(ar archiver.Writer, path, commonPath string) error {
info, err := os.Stat(path)
if err != nil {
return err
}
if !info.IsDir() && !info.Mode().IsRegular() {
return nil
}
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
if path != commonPath {
filename := info.Name()
err = ar.Write(archiver.File{
FileInfo: archiver.FileInfo{
FileInfo: info,
CustomName: filename,
},
ReadCloser: file,
})
if err != nil {
return err
}
}
if info.IsDir() {
names, err := file.Readdirnames(0)
if err != nil {
return err
}
for _, name := range names {
err = AddFile(ar, filepath.Join(path, name), commonPath)
if err != nil {
log.Printf("Failed to archive %v", err)
}
}
}
return nil
}
func CommonPrefix(sep byte, paths ...string) string {
// Handle special cases.
switch len(paths) {
case 0:
return ""
case 1:
return path.Clean(paths[0])
}
// Note, we treat string as []byte, not []rune as is often
// done in Go. (And sep as byte, not rune). This is because
// most/all supported OS' treat paths as string of non-zero
// bytes. A filename may be displayed as a sequence of Unicode
// runes (typically encoded as UTF-8) but paths are
// not required to be valid UTF-8 or in any normalized form
// (e.g. "é" (U+00C9) and "é" (U+0065,U+0301) are different
// file names.
c := []byte(path.Clean(paths[0]))
// We add a trailing sep to handle the case where the
// common prefix directory is included in the path list
// (e.g. /home/user1, /home/user1/foo, /home/user1/bar).
// path.Clean will have cleaned off trailing / separators with
// the exception of the root directory, "/" (in which case we
// make it "//", but this will get fixed up to "/" bellow).
c = append(c, sep)
// Ignore the first path since it's already in c
for _, v := range paths[1:] {
// Clean up each path before testing it
v = path.Clean(v) + string(sep)
// Find the first non-common byte and truncate c
if len(v) < len(c) {
c = c[:len(v)]
}
for i := 0; i < len(c); i++ {
if v[i] != c[i] {
c = c[:i]
break
}
}
}
// Remove trailing non-separator characters and the final separator
for i := len(c) - 1; i >= 0; i-- {
if c[i] == sep {
c = c[:i]
break
}
}
return string(c)
}
func GetFileOrDirSize(path string) (int64, error) {
fileInfo, err := os.Stat(path)
if err != nil {
return 0, err
}
if fileInfo.IsDir() {
return DirSizeB(path + "/")
}
return fileInfo.Size(), nil
}
//getFileSize get file size by path(B)
func DirSizeB(path string) (int64, error) {
var size int64
err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error {
if !info.IsDir() {
size += info.Size()
}
return err
})
return size, err
}

84
pkg/utils/file/image.go Normal file
View File

@@ -0,0 +1,84 @@
package file
import (
"bytes"
"fmt"
"io"
"os"
"github.com/disintegration/imaging"
"github.com/dsoprea/go-exif/v3"
exifcommon "github.com/dsoprea/go-exif/v3/common"
)
func GetImage(path string, width, height int) ([]byte, error) {
if thumbnail, err := GetThumbnailByOwnerPhotos(path); err == nil {
return thumbnail, nil
} else {
return GetThumbnailByWebPhoto(path, width, height)
}
}
func GetThumbnailByOwnerPhotos(path string) ([]byte, error) {
file, err := os.Open(path)
if err != nil {
return nil, err
}
buff := &bytes.Buffer{}
defer file.Close()
offset := 0
offsets := []int{12, 30}
head := make([]byte, 0xffff)
r := io.TeeReader(file, buff)
_, err = r.Read(head)
if err != nil {
return nil, err
}
for _, offset = range offsets {
if _, err = exif.ParseExifHeader(head[offset:]); err == nil {
break
}
}
if err != nil {
return nil, err
}
im, err := exifcommon.NewIfdMappingWithStandard()
if err != nil {
return nil, err
}
_, index, err := exif.Collect(im, exif.NewTagIndex(), head[offset:])
if err != nil {
return nil, err
}
ifd := index.RootIfd.NextIfd()
if ifd == nil {
return nil, exif.ErrNoThumbnail
}
thumbnail, err := ifd.Thumbnail()
if err != nil {
return nil, err
}
return thumbnail, nil
}
func GetThumbnailByWebPhoto(path string, width, height int) ([]byte, error) {
src, err := imaging.Open(path)
if err != nil {
fmt.Println(err)
return nil, err
}
img := imaging.Resize(src, width, 0, imaging.Lanczos)
f, err := imaging.FormatFromFilename(path)
if err != nil {
return nil, err
}
buf := bytes.Buffer{}
imaging.Encode(&buf, img, f)
return buf.Bytes(), nil
}

View File

@@ -2,7 +2,7 @@ package httper
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
@@ -16,12 +16,48 @@ import (
//url:请求地址
//response:请求返回的内容
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.BasicAuth()
for k, v := range head {
req.Header.Add(k, v)
}
if err != nil {
return ""
}
resp, err := client.Do(req)
if err != nil {
fmt.Println(err)
//需要错误日志的处理
//loger.Error(error)
return ""
//panic(error)
}
defer resp.Body.Close()
var buffer [512]byte
result := bytes.NewBuffer(nil)
for {
n, err := resp.Body.Read(buffer[0:])
result.Write(buffer[0:n])
if err != nil && err == io.EOF {
break
} else if err != nil {
//loger.Error(err)
return ""
// panic(err)
}
}
response = result.String()
return
}
//发送GET请求
//url:请求地址
//response:请求返回的内容
func PersonGet(url string) (response string) {
client := &http.Client{Timeout: 5 * time.Second}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return ""
}
@@ -67,97 +103,16 @@ func Post(url string, data []byte, contentType string, head map[string]string) (
client := &http.Client{Timeout: 5 * time.Second}
resp, error := client.Do(req)
if error != nil {
panic(error)
}
defer resp.Body.Close()
result, _ := ioutil.ReadAll(resp.Body)
content = string(result)
fmt.Println(error)
return
}
//发送POST请求
//url:请求地址data:POST请求提交的数据,contentType:请求体格式application/json
//content:请求放回的内容
func ZeroTierPost(url string, data map[string]string, head map[string]string, cookies []*http.Cookie) (content string, code int) {
b, _ := json.Marshal(data)
req, err := http.NewRequest("POST", url, bytes.NewReader(b))
for _, cookie := range cookies {
req.AddCookie(cookie)
}
for k, v := range head {
req.Header.Add(k, v)
}
if err != nil {
panic(err)
}
client := &http.Client{Timeout: 20 * time.Second}
resp, error := client.Do(req)
if error != nil {
panic(error)
}
defer resp.Body.Close()
code = resp.StatusCode
result, _ := ioutil.ReadAll(resp.Body)
content = string(result)
return
}
//发送POST请求
//url:请求地址data:POST请求提交的数据,contentType:请求体格式application/json
//content:请求放回的内容
func ZeroTierPostJson(url string, data string, head map[string]string) (content string, code int) {
var postData *bytes.Buffer
jsonStr := []byte(data)
postData = bytes.NewBuffer(jsonStr)
req, err := http.NewRequest("POST", url, postData)
for k, v := range head {
req.Header.Add(k, v)
}
if err != nil {
panic(err)
}
client := &http.Client{Timeout: 20 * time.Second}
resp, error := client.Do(req)
if error != nil {
panic(error)
}
defer resp.Body.Close()
result, _ := ioutil.ReadAll(resp.Body)
content = string(result)
code = resp.StatusCode
return
}
func ZeroTierDelete(url string, head map[string]string) (content string, code int) {
req, err := http.NewRequest("DELETE", url, nil)
for k, v := range head {
req.Header.Add(k, v)
}
if err != nil {
panic(err)
}
client := &http.Client{Timeout: 20 * time.Second}
resp, error := client.Do(req)
if error != nil {
panic(error)
}
defer resp.Body.Close()
result, _ := ioutil.ReadAll(resp.Body)
content = string(result)
code = resp.StatusCode
return
}
//发送POST请求
//url:请求地址data:POST请求提交的数据,contentType:请求体格式application/json
//content:请求放回的内容

View File

@@ -1 +0,0 @@
package utils

View File

@@ -1,9 +1,10 @@
package ip_helper
import (
httper2 "github.com/IceWhaleTech/CasaOS/pkg/utils/httper"
"net"
"strings"
httper2 "github.com/IceWhaleTech/CasaOS/pkg/utils/httper"
)
func IsIPv4(address string) bool {
@@ -39,3 +40,35 @@ func GetLoclIp() string {
}
return "127.0.0.1"
}
func GetDeviceAllIP(port string) []string {
var address []string
addrs, err := net.InterfaceAddrs()
if err != nil {
return address
}
for _, a := range addrs {
if ipNet, ok := a.(*net.IPNet); ok && !ipNet.IP.IsLoopback() {
if ipNet.IP.To16() != nil {
address = append(address, ipNet.IP.String()+":"+port)
}
}
}
return address
}
func HasLocalIP(ip net.IP) bool {
if ip.IsLoopback() {
return true
}
ip.String()
ip4 := ip.To4()
if ip4 == nil {
return false
}
return ip4[0] == 10 || // 10.0.0.0/8
(ip4[0] == 172 && ip4[1] >= 16 && ip4[1] <= 31) || // 172.16.0.0/12
(ip4[0] == 169 && ip4[1] == 254) || // 169.254.0.0/16
(ip4[0] == 192 && ip4[1] == 168) // 192.168.0.0/16
}

View File

@@ -2,6 +2,7 @@ package ip_helper
import (
"fmt"
"net"
"testing"
)
@@ -20,3 +21,7 @@ func TestGetExternalIPV6(t *testing.T) {
func TestGetLoclIp(t *testing.T) {
fmt.Println(GetLoclIp())
}
func TestHasLocalIP(t *testing.T) {
fmt.Println("dddd")
fmt.Println(HasLocalIP(net.ParseIP("192.168.2.10")))
}

View File

@@ -1,109 +1,92 @@
/*
* @Author: LinkLeong link@icewhale.com
* @Date: 2022-06-02 15:09:38
* @LastEditors: LinkLeong
* @LastEditTime: 2022-06-02 17:43:38
* @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"
"log"
"os"
"path"
"path/filepath"
"runtime"
"github.com/IceWhaleTech/CasaOS/pkg/config"
file2 "github.com/IceWhaleTech/CasaOS/pkg/utils/file"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"gopkg.in/natefinch/lumberjack.v2"
)
//定义一个int的别名
type Level int
var loggers *zap.Logger
type OLog interface {
Debug(v ...interface{})
Info(v ...interface{})
Warn(v ...interface{})
Error(v ...interface{})
Fatal(v ...interface{})
Path() string
}
type oLog struct {
}
var (
F *os.File
DefaultPrefix = ""
DefaultCallerDepth = 2
logger *log.Logger
logPrefix = ""
levelFlags = []string{"DEBUG", "INFO", "WARN", "ERROR", "FATAL"}
)
//iota在const关键字出现时将被重置为0(const内部的第一行之前)const中每新增一行常量声明将使iota计数一次(iota可理解为const语句块中的行索引)。
const (
DEBUG Level = iota
INFO
WARN
ERROR
FATAL
)
//日志初始化
func LogSetup() {
var err error
filePath := fmt.Sprintf("%s", config.AppInfo.LogSavePath)
fileName := fmt.Sprintf("%s.%s",
func getFileLogWriter() (writeSyncer zapcore.WriteSyncer) {
// 使用 lumberjack 实现 log rotate
lumberJackLogger := &lumberjack.Logger{
Filename: filepath.Join(config.AppInfo.LogSavePath, fmt.Sprintf("%s.%s",
config.AppInfo.LogSaveName,
config.AppInfo.LogFileExt,
)
F, err = file2.MustOpen(fileName, filePath)
if err != nil {
log.Fatalf("logging.Setup err: %v", err)
)),
MaxSize: 100,
MaxBackups: 60,
MaxAge: 1,
Compress: true,
}
logger = log.New(F, DefaultPrefix, log.LstdFlags)
return zapcore.AddSync(lumberJackLogger)
}
func (o *oLog) Path() string {
filePath := fmt.Sprintf("%s", config.AppInfo.LogSavePath)
fileName := fmt.Sprintf("%s.%s",
config.AppInfo.LogSaveName,
config.AppInfo.LogFileExt,
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.DebugLevel),
zapcore.NewCore(encoder, fileWriteSyncer, zapcore.DebugLevel),
)
return filePath + fileName
}
func (o *oLog) Debug(v ...interface{}) {
setPrefix(DEBUG)
logger.Println(v)
loggers = zap.New(core)
}
func (o *oLog) Info(v ...interface{}) {
setPrefix(INFO)
logger.Println(v)
func Info(message string, fields ...zap.Field) {
callerFields := getCallerInfoForLog()
fields = append(fields, callerFields...)
loggers.Info(message, fields...)
}
func (o *oLog) Warn(v ...interface{}) {
setPrefix(WARN)
logger.Println(v)
func Debug(message string, fields ...zap.Field) {
callerFields := getCallerInfoForLog()
fields = append(fields, callerFields...)
loggers.Debug(message, fields...)
}
func (o *oLog) Error(v ...interface{}) {
setPrefix(ERROR)
logger.Println(v)
func Error(message string, fields ...zap.Field) {
callerFields := getCallerInfoForLog()
fields = append(fields, callerFields...)
loggers.Error(message, fields...)
}
func (o *oLog) Fatal(v ...interface{}) {
setPrefix(FATAL)
logger.Println(v)
func Warn(message string, fields ...zap.Field) {
callerFields := getCallerInfoForLog()
fields = append(fields, callerFields...)
loggers.Warn(message, fields...)
}
func setPrefix(level Level) {
_, file, line, ok := runtime.Caller(DefaultCallerDepth)
if ok {
logPrefix = fmt.Sprintf("[%s][%s:%d]", levelFlags[level], filepath.Base(file), line)
} else {
logPrefix = fmt.Sprintf("[%s]", levelFlags[level])
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函数返回路径的最后一个元素只保留函数名
logger.SetPrefix(logPrefix)
}
func NewOLoger() OLog {
return &oLog{}
callerFields = append(callerFields, zap.String("func", funcName), zap.String("file", file), zap.Int("line", line))
return
}

109
pkg/utils/loger/log_old.go Normal file
View File

@@ -0,0 +1,109 @@
package loger
import (
"fmt"
"log"
"os"
"path/filepath"
"runtime"
"github.com/IceWhaleTech/CasaOS/pkg/config"
file2 "github.com/IceWhaleTech/CasaOS/pkg/utils/file"
)
//定义一个int的别名
type Level int
type OLog interface {
Debug(v ...interface{})
Info(v ...interface{})
Warn(v ...interface{})
Error(v ...interface{})
Fatal(v ...interface{})
Path() string
}
type oLog struct {
}
var (
F *os.File
DefaultPrefix = ""
DefaultCallerDepth = 2
logger *log.Logger
logPrefix = ""
levelFlags = []string{"DEBUG", "INFO", "WARN", "ERROR", "FATAL"}
)
//iota在const关键字出现时将被重置为0(const内部的第一行之前)const中每新增一行常量声明将使iota计数一次(iota可理解为const语句块中的行索引)。
const (
DEBUG Level = iota
INFO
WARN
ERROR
FATAL
)
//日志初始化
func LogSetupOld() {
var err error
filePath := fmt.Sprintf("%s", config.AppInfo.LogSavePath)
fileName := fmt.Sprintf("%s.%s",
config.AppInfo.LogSaveName,
config.AppInfo.LogFileExt,
)
F, err = file2.MustOpen(fileName, filePath)
if err != nil {
log.Fatalf("logging.Setup err: %v", err)
}
logger = log.New(F, DefaultPrefix, log.LstdFlags)
}
func (o *oLog) Path() string {
filePath := fmt.Sprintf("%s", config.AppInfo.LogSavePath)
fileName := fmt.Sprintf("%s.%s",
config.AppInfo.LogSaveName,
config.AppInfo.LogFileExt,
)
return filePath + fileName
}
func (o *oLog) Debug(v ...interface{}) {
setPrefix(DEBUG)
logger.Println(v)
}
func (o *oLog) Info(v ...interface{}) {
setPrefix(INFO)
logger.Println(v)
}
func (o *oLog) Warn(v ...interface{}) {
setPrefix(WARN)
logger.Println(v)
}
func (o *oLog) Error(v ...interface{}) {
setPrefix(ERROR)
logger.Println(v)
}
func (o *oLog) Fatal(v ...interface{}) {
setPrefix(FATAL)
logger.Println(v)
}
func setPrefix(level Level) {
_, file, line, ok := runtime.Caller(DefaultCallerDepth)
if ok {
logPrefix = fmt.Sprintf("[%s][%s:%d]", levelFlags[level], filepath.Base(file), line)
} else {
logPrefix = fmt.Sprintf("[%s]", levelFlags[level])
}
logger.SetPrefix(logPrefix)
}
func NewOLoger() OLog {
return &oLog{}
}

View File

@@ -0,0 +1,30 @@
/*
* @Author: LinkLeong a624669980@163.com
* @Date: 2022-05-08 14:58:46
* @LastEditors: LinkLeong a624669980@163.com
* @LastEditTime: 2022-05-09 13:42:26
* @FilePath: /CasaOS/pkg/utils/network_detection.go
* @Description:
*
* Copyright (c) 2022 by LinkLeong a624669980@163.com, All Rights Reserved.
*/
package utils
import natType "github.com/Curtis-Milo/nat-type-identifier-go"
/**
* @description:
* @param {chanstring} data
* @param {string} url
* @return {*}
*/
func GetNetWorkTypeDetection(data chan string, url string) {
// fmt.Println("url:", url)
// httper.Get(url, nil)
// aaa <- url
result, err := natType.GetDeterminedNatType(true, 5, url)
if err == nil {
data <- result
}
}

View File

@@ -0,0 +1,29 @@
/*
* @Author: LinkLeong a624669980@163.com
* @Date: 2022-05-08 15:07:31
* @LastEditors: LinkLeong a624669980@163.com
* @LastEditTime: 2022-05-09 11:43:30
* @FilePath: /CasaOS/pkg/utils/network_detection_test.go
* @Description:
*
* Copyright (c) 2022 by LinkLeong a624669980@163.com, All Rights Reserved.
*/
package utils
import (
"fmt"
"testing"
)
func TestGetResultTest(t *testing.T) {
list := []string{"https://www.google.com", "https://www.bing.com", "https://www.baidu.com"}
data := make(chan string)
//data <- "init"
for _, v := range list {
go GetNetWorkTypeDetection(data, v)
}
result := <-data
close(data)
fmt.Println(result)
}

View File

@@ -17,6 +17,7 @@ const (
FILE_ALREADY_EXISTS = 20002
FILE_OR_DIR_EXISTS = 20003
PORT_IS_OCCUPIED = 20004
COMMAND_ERROR_INVALID_OPERATION = 20005
//zerotier
GET_TOKEN_ERROR = 30001
@@ -32,13 +33,24 @@ const (
UNINSTALL_APP_ERROR = 50001
PULL_IMAGE_ERROR = 50002
DEVICE_NOT_EXIST = 50003
ERROR_APP_NAME_EXIST = 50004
//file
FILE_DOES_NOT_EXIST = 60001
FILE_READ_ERROR = 60002
FILE_DELETE_ERROR = 60003
DIR_NOT_EXISTS = 60004
//shortcuts
SHORTCUTS_URL_ERROR = 70001
//person
PERSON_REMOTE_ERROR = 80001
PERSON_DOWN_NOT_EXIST = 80002
PERSON_EXIST_DOWNLOAD = 80003
PERSON_NOT_EXIST_USER = 80004
PERSON_EXIST_FRIEND = 80005
PERSON_MYSELF = 80006
)
var MsgFlags = map[int]string{
@@ -66,6 +78,7 @@ var MsgFlags = map[int]string{
UNINSTALL_APP_ERROR: "Error uninstalling app",
PULL_IMAGE_ERROR: "Error pulling image",
DEVICE_NOT_EXIST: "Device does not exist",
ERROR_APP_NAME_EXIST: "App name already exists",
//disk
NAME_NOT_AVAILABLE: "Name not available",
@@ -77,8 +90,19 @@ var MsgFlags = map[int]string{
//
FILE_DOES_NOT_EXIST: "File does not exist",
DIR_NOT_EXISTS: "Directory does not exist",
FILE_READ_ERROR: "File read error",
FILE_DELETE_ERROR: "Delete error",
SHORTCUTS_URL_ERROR: "URL error",
PERSON_REMOTE_ERROR: "Remote connection error",
PERSON_DOWN_NOT_EXIST: "Download record does not exist",
PERSON_EXIST_DOWNLOAD: "The same download task exists",
PERSON_EXIST_FRIEND: "Friend already exist",
PERSON_NOT_EXIST_USER: "User does not exist",
PERSON_MYSELF: "You can not add yourself",
COMMAND_ERROR_INVALID_OPERATION: "invalid operation",
}
//获取错误信息

View File

@@ -1,73 +0,0 @@
package sort
import (
"github.com/IceWhaleTech/CasaOS/model"
"sort"
)
// 数据集类型, 与上一篇排序文章(多字段单独排序)比较, less字段的数据类型不再是 func(p1, p2 *Change) bool
// 而是 []func(p1, p2 *Change) bool 因为在第一个比较的值相等的情况下, 还要比较第二个值, 所以这里需要多个比较函数
type devSorter struct {
dev []model.Devices
less []lessFuncDev
}
// sort接口方法之一(Less)
type lessFuncDev func(p1, p2 *model.Devices) bool
// Sort 函数有两个作用
// 第一, 将参数(实际的数据集)赋值给ms对象
// 第二, 调用内置sort函数进行排序操作
func (ms *devSorter) Sort(dev []model.Devices) {
ms.dev = dev
sort.Sort(ms)
}
// OrderedBy 函数的作用是返回一个multiSorter实例, 并将所有的实际排序函数赋值给实例的less字段,
// 上面已经为multiSorter结构体定义了Sort方法, 所以该函数的返回值可以直接调用Sort方法进行排序
// 该函数中, 为multiSorter结构体中的less字段赋值, Sort方法中又将实际数据集传入, 赋值给multiSorter的ports字段
// 一个函数, 一个方法调用过后, multiSorter实例中两个字段就已经全部被正确赋值, 可以调用系统sort函数进行排序
// 该函数也可看作是一个工厂方法, 用来生成less字段已经被赋值的multiSorter实例
func DevSort(less ...lessFuncDev) *devSorter {
return &devSorter{
less: less,
}
}
// Len 为sort接口方法之一
func (ms *devSorter) Len() int {
return len(ms.dev)
}
// Swap 为sort接口方法之一
func (ms *devSorter) Swap(i, j int) {
ms.dev[i], ms.dev[j] = ms.dev[j], ms.dev[i]
}
// Less 为sort接口方法之一
func (ms *devSorter) Less(i, j int) bool {
temp := ms.dev
p, q := &temp[i], &temp[j]
// Try all but the last comparison.
var k int
// 由于可能有多个需要排序的字段, 也就对应了多个less函数, 当第一个字段的值相等时,
// 需要依次尝试比对后续其他字段的值得大小, 所以这里需要获取比较函数的长度, 以便遍历比较
for k = 0; k < len(ms.less)-1; k++ {
// 提取比较函数, 将函数赋值到新的变量中以便调用
less := ms.less[k]
switch {
case less(p, q):
// 如果 p < q, 返回值为true, 不存在两个值相等需要比较后续字段的情况, 所以这里直接返回
// 如果 p > q, 返回值为false, 则调到下一个case中处理
return true
case less(q, p):
// 如果 p > q, 返回值为false, 不存在两个值相等需要比较后续字段的情况, 所以这里直接返回
return false
}
// 如果代码走到这里, 说明ms.less[k]函数比较后 p == q; 重新开始下一次循环, 更换到下一个比较函数处理
continue
}
// 如果代码走到这里, 说明所有的比较函数执行过后, 所有比较的值都相等
// 直接返回最后一次的比较结果数据即可
return ms.less[k](p, q)
}

View File

@@ -1,74 +0,0 @@
package sort
import (
"sort"
"github.com/IceWhaleTech/CasaOS/model"
)
// 数据集类型, 与上一篇排序文章(多字段单独排序)比较, less字段的数据类型不再是 func(p1, p2 *Change) bool
// 而是 []func(p1, p2 *Change) bool 因为在第一个比较的值相等的情况下, 还要比较第二个值, 所以这里需要多个比较函数
type evnSorter struct {
evn []model.Envs
less []lessFuncEnv
}
// sort接口方法之一(Less)
type lessFuncEnv func(p1, p2 *model.Envs) bool
// Sort 函数有两个作用
// 第一, 将参数(实际的数据集)赋值给ms对象
// 第二, 调用内置sort函数进行排序操作
func (ms *evnSorter) Sort(env []model.Envs) {
ms.evn = env
sort.Sort(ms)
}
// OrderedBy 函数的作用是返回一个multiSorter实例, 并将所有的实际排序函数赋值给实例的less字段,
// 上面已经为multiSorter结构体定义了Sort方法, 所以该函数的返回值可以直接调用Sort方法进行排序
// 该函数中, 为multiSorter结构体中的less字段赋值, Sort方法中又将实际数据集传入, 赋值给multiSorter的ports字段
// 一个函数, 一个方法调用过后, multiSorter实例中两个字段就已经全部被正确赋值, 可以调用系统sort函数进行排序
// 该函数也可看作是一个工厂方法, 用来生成less字段已经被赋值的multiSorter实例
func EnvSort(less ...lessFuncEnv) *evnSorter {
return &evnSorter{
less: less,
}
}
// Len 为sort接口方法之一
func (ms *evnSorter) Len() int {
return len(ms.evn)
}
// Swap 为sort接口方法之一
func (ms *evnSorter) Swap(i, j int) {
ms.evn[i], ms.evn[j] = ms.evn[j], ms.evn[i]
}
// Less 为sort接口方法之一
func (ms *evnSorter) Less(i, j int) bool {
temp := ms.evn
p, q := &temp[i], &temp[j]
// Try all but the last comparison.
var k int
// 由于可能有多个需要排序的字段, 也就对应了多个less函数, 当第一个字段的值相等时,
// 需要依次尝试比对后续其他字段的值得大小, 所以这里需要获取比较函数的长度, 以便遍历比较
for k = 0; k < len(ms.less)-1; k++ {
// 提取比较函数, 将函数赋值到新的变量中以便调用
less := ms.less[k]
switch {
case less(p, q):
// 如果 p < q, 返回值为true, 不存在两个值相等需要比较后续字段的情况, 所以这里直接返回
// 如果 p > q, 返回值为false, 则调到下一个case中处理
return true
case less(q, p):
// 如果 p > q, 返回值为false, 不存在两个值相等需要比较后续字段的情况, 所以这里直接返回
return false
}
// 如果代码走到这里, 说明ms.less[k]函数比较后 p == q; 重新开始下一次循环, 更换到下一个比较函数处理
continue
}
// 如果代码走到这里, 说明所有的比较函数执行过后, 所有比较的值都相等
// 直接返回最后一次的比较结果数据即可
return ms.less[k](p, q)
}

View File

@@ -1,73 +0,0 @@
package sort
import (
"github.com/IceWhaleTech/CasaOS/model"
"sort"
)
// 数据集类型, 与上一篇排序文章(多字段单独排序)比较, less字段的数据类型不再是 func(p1, p2 *Change) bool
// 而是 []func(p1, p2 *Change) bool 因为在第一个比较的值相等的情况下, 还要比较第二个值, 所以这里需要多个比较函数
type multiSorter struct {
ports []model.Ports
less []lessFunc
}
// sort接口方法之一(Less)
type lessFunc func(p1, p2 *model.Ports) bool
// Sort 函数有两个作用
// 第一, 将参数(实际的数据集)赋值给ms对象
// 第二, 调用内置sort函数进行排序操作
func (ms *multiSorter) Sort(ports []model.Ports) {
ms.ports = ports
sort.Sort(ms)
}
// OrderedBy 函数的作用是返回一个multiSorter实例, 并将所有的实际排序函数赋值给实例的less字段,
// 上面已经为multiSorter结构体定义了Sort方法, 所以该函数的返回值可以直接调用Sort方法进行排序
// 该函数中, 为multiSorter结构体中的less字段赋值, Sort方法中又将实际数据集传入, 赋值给multiSorter的ports字段
// 一个函数, 一个方法调用过后, multiSorter实例中两个字段就已经全部被正确赋值, 可以调用系统sort函数进行排序
// 该函数也可看作是一个工厂方法, 用来生成less字段已经被赋值的multiSorter实例
func PortsSort(less ...lessFunc) *multiSorter {
return &multiSorter{
less: less,
}
}
// Len 为sort接口方法之一
func (ms *multiSorter) Len() int {
return len(ms.ports)
}
// Swap 为sort接口方法之一
func (ms *multiSorter) Swap(i, j int) {
ms.ports[i], ms.ports[j] = ms.ports[j], ms.ports[i]
}
// Less 为sort接口方法之一
func (ms *multiSorter) Less(i, j int) bool {
port := ms.ports
p, q := &port[i], &port[j]
// Try all but the last comparison.
var k int
// 由于可能有多个需要排序的字段, 也就对应了多个less函数, 当第一个字段的值相等时,
// 需要依次尝试比对后续其他字段的值得大小, 所以这里需要获取比较函数的长度, 以便遍历比较
for k = 0; k < len(ms.less)-1; k++ {
// 提取比较函数, 将函数赋值到新的变量中以便调用
less := ms.less[k]
switch {
case less(p, q):
// 如果 p < q, 返回值为true, 不存在两个值相等需要比较后续字段的情况, 所以这里直接返回
// 如果 p > q, 返回值为false, 则调到下一个case中处理
return true
case less(q, p):
// 如果 p > q, 返回值为false, 不存在两个值相等需要比较后续字段的情况, 所以这里直接返回
return false
}
// 如果代码走到这里, 说明ms.less[k]函数比较后 p == q; 重新开始下一次循环, 更换到下一个比较函数处理
continue
}
// 如果代码走到这里, 说明所有的比较函数执行过后, 所有比较的值都相等
// 直接返回最后一次的比较结果数据即可
return ms.less[k](p, q)
}

View File

@@ -1,73 +0,0 @@
package sort
import (
"github.com/IceWhaleTech/CasaOS/model"
"sort"
)
// 数据集类型, 与上一篇排序文章(多字段单独排序)比较, less字段的数据类型不再是 func(p1, p2 *Change) bool
// 而是 []func(p1, p2 *Change) bool 因为在第一个比较的值相等的情况下, 还要比较第二个值, 所以这里需要多个比较函数
type volSorter struct {
vol []model.Volume
less []lessFuncVol
}
// sort接口方法之一(Less)
type lessFuncVol func(p1, p2 *model.Volume) bool
// Sort 函数有两个作用
// 第一, 将参数(实际的数据集)赋值给ms对象
// 第二, 调用内置sort函数进行排序操作
func (ms *volSorter) Sort(vol []model.Volume) {
ms.vol = vol
sort.Sort(ms)
}
// OrderedBy 函数的作用是返回一个multiSorter实例, 并将所有的实际排序函数赋值给实例的less字段,
// 上面已经为multiSorter结构体定义了Sort方法, 所以该函数的返回值可以直接调用Sort方法进行排序
// 该函数中, 为multiSorter结构体中的less字段赋值, Sort方法中又将实际数据集传入, 赋值给multiSorter的ports字段
// 一个函数, 一个方法调用过后, multiSorter实例中两个字段就已经全部被正确赋值, 可以调用系统sort函数进行排序
// 该函数也可看作是一个工厂方法, 用来生成less字段已经被赋值的multiSorter实例
func VolSort(less ...lessFuncVol) *volSorter {
return &volSorter{
less: less,
}
}
// Len 为sort接口方法之一
func (ms *volSorter) Len() int {
return len(ms.vol)
}
// Swap 为sort接口方法之一
func (ms *volSorter) Swap(i, j int) {
ms.vol[i], ms.vol[j] = ms.vol[j], ms.vol[i]
}
// Less 为sort接口方法之一
func (ms *volSorter) Less(i, j int) bool {
temp := ms.vol
p, q := &temp[i], &temp[j]
// Try all but the last comparison.
var k int
// 由于可能有多个需要排序的字段, 也就对应了多个less函数, 当第一个字段的值相等时,
// 需要依次尝试比对后续其他字段的值得大小, 所以这里需要获取比较函数的长度, 以便遍历比较
for k = 0; k < len(ms.less)-1; k++ {
// 提取比较函数, 将函数赋值到新的变量中以便调用
less := ms.less[k]
switch {
case less(p, q):
// 如果 p < q, 返回值为true, 不存在两个值相等需要比较后续字段的情况, 所以这里直接返回
// 如果 p > q, 返回值为false, 则调到下一个case中处理
return true
case less(q, p):
// 如果 p > q, 返回值为false, 不存在两个值相等需要比较后续字段的情况, 所以这里直接返回
return false
}
// 如果代码走到这里, 说明ms.less[k]函数比较后 p == q; 重新开始下一次循环, 更换到下一个比较函数处理
continue
}
// 如果代码走到这里, 说明所有的比较函数执行过后, 所有比较的值都相等
// 直接返回最后一次的比较结果数据即可
return ms.less[k](p, q)
}

View File

@@ -1,3 +1,13 @@
/*
* @Author: LinkLeong link@icewhale.com
* @Date: 2022-05-13 18:15:46
* @LastEditors: LinkLeong
* @LastEditTime: 2022-06-02 17:41:47
* @FilePath: /CasaOS/pkg/utils/version/version.go
* @Description:
* @Website: https://www.casaos.io
* Copyright (c) 2022 by icewhale, All Rights Reserved.
*/
package version
import (
@@ -32,6 +42,8 @@ func IsNeedUpdate() (bool, model.Version) {
b, _ := strconv.Atoi(v2[i])
if a > b {
return true, version
} else {
return false, version
}
}
return false, version

View File

@@ -1,47 +0,0 @@
package zerotier
import (
httper2 "github.com/IceWhaleTech/CasaOS/pkg/utils/httper"
"github.com/tidwall/gjson"
"net/http"
)
func PostData(url, token string, data string) interface{} {
body, code := httper2.ZeroTierPostJson(url, data, GetHead(token))
if code != http.StatusOK {
return ""
}
result := gjson.Parse(body)
return result.Value()
}
func GetData(url, token string) interface{} {
body, code := httper2.ZeroTierGet(url, GetHead(token))
if code != http.StatusOK {
return ""
}
result := gjson.Parse(body)
return result.Value()
}
func DeleteMember(url, token string) interface{} {
body, code := httper2.ZeroTierDelete(url, GetHead(token))
if code != http.StatusOK {
return ""
}
result := gjson.Parse(body)
return result.Value()
}
func GetHead(token string) map[string]string {
var head = make(map[string]string)
head["Authorization"] = "Bearer " + token
head["Content-Type"] = "application/json"
return head
}

View File

@@ -1,16 +1,17 @@
package route
import (
"encoding/json"
"encoding/xml"
"fmt"
"path/filepath"
"runtime"
"strconv"
"strings"
"time"
"github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/model/system_app"
"github.com/IceWhaleTech/CasaOS/pkg/config"
"github.com/IceWhaleTech/CasaOS/pkg/docker"
"github.com/IceWhaleTech/CasaOS/pkg/utils/command"
"github.com/IceWhaleTech/CasaOS/pkg/utils/env_helper"
"github.com/IceWhaleTech/CasaOS/pkg/utils/file"
@@ -25,6 +26,11 @@ func InitFunction() {
Update2_3()
CheckSerialDiskMount()
CheckToken2_11()
ImportApplications()
ChangeAPIUrl()
InitSystemApplication()
}
var syncIsExistence = false
@@ -35,7 +41,7 @@ func installSyncthing(appId string) {
m := model.CustomizationPostData{}
var dockerImage string
var dockerImageVersion string
appInfo = service.MyService.OAPI().GetServerAppInfo(appId, "system", "us_en")
appInfo = service.MyService.Casa().GetServerAppInfo(appId, "system", "us_en")
dockerImage = appInfo.Image
dockerImageVersion = appInfo.ImageVersion
@@ -74,14 +80,12 @@ func installSyncthing(appId string) {
appInfo.Tip = env_helper.ReplaceStringDefaultENV(appInfo.Tip)
}
appInfo.MaxMemory = service.MyService.ZiMa().GetMemInfo().Total >> 20
appInfo.MaxMemory = service.MyService.System().GetMemInfo().Total >> 20
id := uuid.NewV4().String()
installLog := model2.AppNotify{}
// step下载镜像
err := service.MyService.Docker().DockerPullImage(dockerImage+":"+dockerImageVersion, installLog)
err := service.MyService.Docker().DockerPullImage(dockerImage+":"+dockerImageVersion, "", "")
if err != nil {
//pull image error
fmt.Println("pull image error", err, dockerImage, dockerImageVersion)
@@ -99,8 +103,9 @@ func installSyncthing(appId string) {
m.Ports = appInfo.Ports
m.Restart = "always"
m.Volumes = appInfo.Volumes
containerId, err := service.MyService.Docker().DockerContainerCreate(dockerImage+":"+dockerImageVersion, id, m, appInfo.NetworkModel)
m.Label = id
m.CustomId = id
containerId, err := service.MyService.Docker().DockerContainerCreate(dockerImage+":"+dockerImageVersion, m, appInfo.NetworkModel)
if err != nil {
fmt.Println("container create error", err)
// create container error
@@ -108,83 +113,45 @@ func installSyncthing(appId string) {
}
//stepstart container
err = service.MyService.Docker().DockerContainerStart(id)
err = service.MyService.Docker().DockerContainerStart(containerId)
if err != nil {
//start container error
return
}
portsStr, _ := json.Marshal(appInfo.Ports)
envsStr, _ := json.Marshal(appInfo.Envs)
volumesStr, _ := json.Marshal(appInfo.Volumes)
devicesStr, _ := json.Marshal(appInfo.Devices)
//step: 保存数据到数据库
md := model2.AppListDBModel{
CustomId: id,
Title: appInfo.Title,
//ScreenshotLink: appInfo.ScreenshotLink,
Slogan: appInfo.Tagline,
Description: appInfo.Description,
//Tags: appInfo.Tags,
Icon: appInfo.Icon,
Version: dockerImageVersion,
ContainerId: containerId,
Image: dockerImage,
Index: appInfo.Index,
PortMap: appInfo.PortMap,
Label: appInfo.Title,
EnableUPNP: false,
Ports: string(portsStr),
Envs: string(envsStr),
Volumes: string(volumesStr),
Position: true,
NetModel: appInfo.NetworkModel,
Restart: m.Restart,
CpuShares: 50,
Memory: int64(appInfo.MaxMemory),
Devices: string(devicesStr),
Origin: m.Origin,
CreatedAt: strconv.FormatInt(time.Now().Unix(), 10),
UpdatedAt: strconv.FormatInt(time.Now().Unix(), 10),
}
service.MyService.App().SaveContainer(md)
checkSystemApp()
}
// check if the system application is installed
func checkSystemApp() {
list := service.MyService.App().GetSystemAppList()
for _, v := range *list {
if v.Image == "linuxserver/syncthing" {
for _, v := range list {
info, err := service.MyService.Docker().DockerContainerInfo(v.ID)
if err != nil {
continue
}
if strings.Contains(info.Config.Image, "linuxserver/syncthing") {
if v.State != "running" {
//stepstart container
service.MyService.Docker().DockerContainerStart(v.CustomId)
service.MyService.Docker().DockerContainerStart(v.ID)
}
syncIsExistence = true
if config.SystemConfigInfo.SyncPort != v.Port {
config.SystemConfigInfo.SyncPort = v.Port
if config.SystemConfigInfo.SyncPort != v.Labels["web"] {
config.SystemConfigInfo.SyncPort = v.Labels["web"]
}
var paths []model.PathMap
json.Unmarshal([]byte(v.Volumes), &paths)
path := ""
for _, i := range paths {
if i.ContainerPath == "/config" {
path = docker.GetDir(v.CustomId, i.Path) + "/config.xml"
for i := 0; i < 10; i++ {
if file.CheckNotExist(path) {
time.Sleep(1 * time.Second)
} else {
for _, i := range info.Mounts {
if i.Destination == "/config" {
path = i.Source
break
}
}
break
}
}
content := file.ReadFullFile(path)
content := file.ReadFullFile(filepath.Join(path, "config.xml"))
syncConfig := &system_app.SyncConfig{}
xml.Unmarshal(content, &syncConfig)
config.SystemConfigInfo.SyncKey = syncConfig.Key
break
}
}
if !syncIsExistence {
@@ -195,18 +162,18 @@ func CheckSerialDiskMount() {
// check mount point
dbList := service.MyService.Disk().GetSerialAll()
list := service.MyService.Disk().LSBLK()
list := service.MyService.Disk().LSBLK(true)
mountPoint := make(map[string]string, len(dbList))
//remount
for _, v := range dbList {
mountPoint[v.Path] = v.MountPoint
mountPoint[v.UUID] = v.MountPoint
}
for _, v := range list {
command.ExecEnabledSMART(v.Path)
if v.Children != nil {
for _, h := range v.Children {
if len(h.MountPoint) == 0 && len(v.Children) == 1 && h.FsType == "ext4" {
if m, ok := mountPoint[h.Path]; ok {
if m, ok := mountPoint[h.UUID]; ok {
//mount point check
volume := m
if !file.CheckNotExist(m) {
@@ -217,7 +184,8 @@ func CheckSerialDiskMount() {
service.MyService.Disk().MountDisk(h.Path, volume)
if volume != m {
ms := model2.SerialDisk{}
ms.Serial = v.Serial
ms.UUID = v.UUID
ms.MountPoint = volume
service.MyService.Disk().UpdateMountPoint(ms)
}
@@ -228,8 +196,101 @@ func CheckSerialDiskMount() {
}
service.MyService.Disk().RemoveLSBLKCache()
command.OnlyExec("source " + config.AppInfo.ProjectPath + "/shell/helper.sh ;AutoRemoveUnuseDir")
}
func Update2_3() {
command.OnlyExec("source " + config.AppInfo.ProjectPath + "/shell/assist.sh")
}
func CheckToken2_11() {
if len(config.ServerInfo.Token) == 0 {
token := uuid.NewV4().String
config.ServerInfo.Token = token()
config.Cfg.Section("server").Key("Token").SetValue(token())
config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath)
}
if len(config.AppInfo.RootPath) == 0 {
config.Cfg.Section("app").Key("RootPath").SetValue("/casaOS")
config.AppInfo.RootPath = "/casaOS"
config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath)
}
// if len(config.ServerInfo.Handshake) == 0 {
// config.Cfg.Section("app").Key("RootPath").SetValue("/casaOS")
// config.AppInfo.RootPath = "/casaOS"
// config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath)
// }
sysType := runtime.GOOS
if len(config.FileSettingInfo.DownloadDir) == 0 {
downloadPath := "/DATA/Downloads"
if sysType == "windows" {
downloadPath = "C:\\CasaOS\\DATA\\Downloads"
}
if sysType == "darwin" {
downloadPath = "./CasaOS/DATA/Downloads"
}
config.Cfg.Section("file").Key("DownloadDir").SetValue(downloadPath)
config.FileSettingInfo.DownloadDir = downloadPath
file.IsNotExistMkDir(config.FileSettingInfo.DownloadDir)
config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath)
}
if len(config.UserInfo.Description) == 0 {
config.Cfg.Section("user").Key("Description").SetValue("nothing")
config.UserInfo.Description = "nothing"
config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath)
}
if len(config.ServerInfo.Handshake) == 0 {
config.Cfg.Section("server").Key("Handshake").SetValue("socket.casaos.io")
config.ServerInfo.Handshake = "socket.casaos.io"
config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath)
}
if service.MyService.ZiMa().GetSysInfo().KernelArch == "aarch64" && config.ServerInfo.USBAutoMount != "True" && strings.Contains(service.MyService.ZiMa().GetDeviceTree(), "Raspberry Pi") {
service.MyService.System().UpdateUSBAutoMount("False")
service.MyService.System().ExecUSBAutoMountShell("False")
}
// str := []string{}
// str = append(str, "ddd")
// str = append(str, "aaa")
// ddd := strings.Join(str, "|")
// config.Cfg.Section("file").Key("ShareDir").SetValue(ddd)
// config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath)
}
func ImportApplications() {
service.MyService.App().ImportApplications(true)
}
// 0.3.1
func ChangeAPIUrl() {
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)
}
}
// 0.3.1
func InitSystemApplication() {
list := service.MyService.App().GetApplicationList()
if len(list) != 2 {
application := model2.ApplicationModel{}
application.Name = "Files"
application.Icon = "/ui/img/Files.svg"
application.Type = "system"
application.Order = 0
service.MyService.App().CreateApplication(application)
application.Name = "CasaConnect"
application.Icon = "/ui/img/CasaConnect.svg"
application.Type = "system"
application.Order = 0
service.MyService.App().CreateApplication(application)
}
}

155
route/periodical.go Normal file
View File

@@ -0,0 +1,155 @@
/*
* @Author: LinkLeong link@icewhale.com
* @Date: 2022-05-27 15:55:36
* @LastEditors: LinkLeong
* @LastEditTime: 2022-05-27 18:57:40
* @FilePath: /CasaOS/route/periodical.go
* @Description:
* @Website: https://www.casaos.io
* Copyright (c) 2022 by icewhale, All Rights Reserved.
*/
package route
import (
"reflect"
"strconv"
"strings"
"time"
"unsafe"
"github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/service"
)
func SendNetINfoBySocket() {
netList := service.MyService.System().GetNetInfo()
newNet := []model.IOCountersStat{}
nets := service.MyService.System().GetNet(true)
for _, n := range netList {
for _, netCardName := range nets {
if n.Name == netCardName {
item := *(*model.IOCountersStat)(unsafe.Pointer(&n))
item.State = strings.TrimSpace(service.MyService.ZiMa().GetNetState(n.Name))
item.Time = time.Now().Unix()
newNet = append(newNet, item)
break
}
}
}
service.MyService.Notify().SendNetInfoBySocket(newNet)
}
func SendCPUBySocket() {
cpu := service.MyService.System().GetCpuPercent()
num := service.MyService.System().GetCpuCoreNum()
cpuData := make(map[string]interface{})
cpuData["percent"] = cpu
cpuData["num"] = num
service.MyService.Notify().SendCPUInfoBySocket(cpuData)
}
func SendMemBySocket() {
service.MyService.Notify().SendMemInfoBySocket(service.MyService.System().GetMemInfo())
}
func SendDiskBySocket() {
list := service.MyService.Disk().LSBLK(true)
summary := model.Summary{}
healthy := true
findSystem := 0
for i := 0; i < len(list); i++ {
if len(list[i].Children) > 0 && findSystem == 0 {
for j := 0; j < len(list[i].Children); j++ {
if len(list[i].Children[j].Children) > 0 {
for _, v := range list[i].Children[j].Children {
if v.MountPoint == "/" {
s, _ := strconv.ParseUint(v.FSSize, 10, 64)
a, _ := strconv.ParseUint(v.FSAvail, 10, 64)
u, _ := strconv.ParseUint(v.FSUsed, 10, 64)
summary.Size += s
summary.Avail += a
summary.Used += u
findSystem = 1
break
}
}
} else {
if list[i].Children[j].MountPoint == "/" {
s, _ := strconv.ParseUint(list[i].Children[j].FSSize, 10, 64)
a, _ := strconv.ParseUint(list[i].Children[j].FSAvail, 10, 64)
u, _ := strconv.ParseUint(list[i].Children[j].FSUsed, 10, 64)
summary.Size += s
summary.Avail += a
summary.Used += u
findSystem = 1
break
}
}
}
}
if findSystem == 1 {
findSystem += 1
continue
}
if list[i].Tran == "sata" || list[i].Tran == "nvme" || list[i].Tran == "spi" || list[i].Tran == "sas" {
temp := service.MyService.Disk().SmartCTL(list[i].Path)
if reflect.DeepEqual(temp, model.SmartctlA{}) {
continue
}
//list[i].Temperature = temp.Temperature.Current
if !temp.SmartStatus.Passed {
healthy = false
}
if len(list[i].Children) > 0 {
for _, v := range list[i].Children {
s, _ := strconv.ParseUint(v.FSSize, 10, 64)
a, _ := strconv.ParseUint(v.FSAvail, 10, 64)
u, _ := strconv.ParseUint(v.FSUsed, 10, 64)
summary.Size += s
summary.Avail += a
summary.Used += u
}
}
}
}
summary.Health = healthy
service.MyService.Notify().SendDiskInfoBySocket(summary)
}
func SendUSBBySocket() {
usbList := service.MyService.Disk().LSBLK(false)
usb := []model.DriveUSB{}
for _, v := range usbList {
if v.Tran == "usb" {
temp := model.DriveUSB{}
temp.Model = v.Model
temp.Name = v.Name
temp.Size = v.Size
mountTemp := true
if len(v.Children) == 0 {
mountTemp = false
}
for _, child := range v.Children {
if len(child.MountPoint) > 0 {
avail, _ := strconv.ParseUint(child.FSAvail, 10, 64)
temp.Avail += avail
used, _ := strconv.ParseUint(child.FSUsed, 10, 64)
temp.Used += used
} else {
mountTemp = false
}
}
temp.Mount = mountTemp
usb = append(usb, temp)
}
}
service.MyService.Notify().SendUSBInfoBySocket(usb)
}

View File

@@ -8,6 +8,7 @@ import (
jwt2 "github.com/IceWhaleTech/CasaOS/pkg/utils/jwt"
v1 "github.com/IceWhaleTech/CasaOS/route/v1"
"github.com/IceWhaleTech/CasaOS/web"
"github.com/gin-contrib/gzip"
"github.com/gin-gonic/gin"
)
@@ -17,7 +18,9 @@ var OnlineDemo bool = false
func InitRouter() *gin.Engine {
r := gin.Default()
r.Use(middleware.Cors())
r.Use(gzip.Gzip(gzip.DefaultCompression))
gin.SetMode(config.ServerInfo.RunMode)
r.StaticFS("/ui", http.FS(web.Static))
@@ -28,7 +31,6 @@ func InitRouter() *gin.Engine {
if swagHandler != nil {
r.GET("/swagger/*any", swagHandler)
}
r.POST("/v1/user/login", v1.Login)
r.GET("/v1/guide/check", v1.GetGuideCheck)
@@ -37,8 +39,10 @@ func InitRouter() *gin.Engine {
//set user
r.POST("/v1/user/setusernamepwd", v1.Set_Name_Pwd)
//get user info
r.GET("/v1/user/info", v1.UserInfo)
r.GET("/v1/user/info", v1.GetUserInfo)
//get user info
r.GET("/v1/person/shareid", v1.GetPersonShareId)
r.GET("/v1/sys/socket/port", v1.GetSystemSocketPort)
v1Group := r.Group("/v1")
v1Group.Use(jwt2.JWT(swagHandler))
@@ -48,98 +52,25 @@ func InitRouter() *gin.Engine {
{
//chang head
v1UserGroup.POST("/changhead", v1.Up_Load_Head)
v1UserGroup.POST("/head", v1.PostUserHead)
//chang user name
v1UserGroup.PUT("/changusername", v1.Chang_User_Name)
v1UserGroup.PUT("/username", v1.PutUserName)
//chang pwd
v1UserGroup.PUT("/changuserpwd", v1.Chang_User_Pwd)
v1UserGroup.PUT("/password", v1.PutUserPwd)
//edit user info
v1UserGroup.POST("/changuserinfo", v1.Chang_User_Info)
v1UserGroup.POST("/info", v1.PostUserChangeInfo)
v1UserGroup.PUT("/nick", v1.PutUserChangeNick)
v1UserGroup.PUT("/desc", v1.PutUserChangeDesc)
v1UserGroup.POST("/person/info", v1.PostUserPersonInfo)
}
v1UserGroup.GET("/shareid", v1.GetUserShareID)
v1ZiMaGroup := v1Group.Group("/zima")
v1ZiMaGroup.Use()
{
//获取cpu信息
v1ZiMaGroup.GET("/getcpuinfo", v1.CupInfo)
//获取内存信息
v1ZiMaGroup.GET("/getmeminfo", v1.MemInfo)
//获取硬盘信息
v1ZiMaGroup.GET("/getdiskinfo", v1.DiskInfo)
//获取网络信息
v1ZiMaGroup.GET("/getnetinfo", v1.NetInfo)
//获取系统信息
v1ZiMaGroup.GET("/sysinfo", v1.SysInfo)
}
v1ZeroTierGroup := v1Group.Group("/zerotier")
v1ZeroTierGroup.Use()
{
//获取zerotier token
v1ZeroTierGroup.POST("/login", v1.ZeroTierGetToken)
//注册zerotier
v1ZeroTierGroup.POST("/register", v1.ZeroTierRegister)
//是否需要登录
v1ZeroTierGroup.GET("/islogin", v1.ZeroTierIsNeedLogin)
//获取网络列表
v1ZeroTierGroup.GET("/list", v1.ZeroTierGetNetworkList)
//加入网络
v1ZeroTierGroup.POST("/join/:id", v1.ZeroTierJoinNetwork)
//离开网络
v1ZeroTierGroup.POST("/leave/:id", v1.ZeroTierLeaveNetwork)
//详情
v1ZeroTierGroup.GET("/info/:id", v1.ZeroTierGetNetworkGetInfo)
////网络状态
//v1ZeroTierGroup.GET("/status", v1.ZeroTierGetNetworkGetStatus)
//修改网络类型
//v1ZeroTierGroup.PUT("/type/:id", v1.ZeroTierEditType)
//修改网络类型
//v1ZeroTierGroup.PUT("/name/:id", v1.ZeroTierEditName)
//修改v6 assign
//v1ZeroTierGroup.PUT("/v6assign/:id", v1.ZeroTierEditV6Assign)
//修改 broadcast
//v1ZeroTierGroup.PUT("/broadcast/:id", v1.ZeroTierEditBroadcast)
//create new network
v1ZeroTierGroup.POST("/create", v1.ZeroTierCreateNetwork)
//获取用户列表
v1ZeroTierGroup.GET("/member/:id", v1.ZeroTierMemberList)
//修改用户信息
//v1ZeroTierGroup.PUT("/members/:id/auth/:mId", v1.ZeroTierMemberAuth)
//修改网络用户name
//v1ZeroTierGroup.PUT("/members/:id/name/:mId", v1.ZeroTierMemberName)
v1ZeroTierGroup.DELETE("/members/:id/del/:mId", v1.ZeroTierMemberDelete)
v1ZeroTierGroup.DELETE("/network/:id/del", v1.ZeroTierDeleteNetwork)
//修改网络用户bridge功能
//v1ZeroTierGroup.PUT("/members/:id/bridge/:mId", v1.ZeroTierMemberBridge)
v1ZeroTierGroup.PUT("/edit/:id", v1.ZeroTierEdit)
v1ZeroTierGroup.GET("/joined/list", v1.ZeroTierJoinedList)
v1ZeroTierGroup.PUT("/member/:id/edit/:mId", v1.ZeroTierMemberEdit)
}
v1DDNSGroup := v1Group.Group("/ddns")
v1DDNSGroup.Use()
{
//获取ddns列表
v1DDNSGroup.GET("/getlist", v1.DDNSGetDomainList)
//测试连接性
v1DDNSGroup.GET("/ping/:api_host", v1.DDNSPing)
//获取ip
v1DDNSGroup.GET("/ip", v1.DDNSGetIP)
//设置ddns
v1DDNSGroup.POST("/set", v1.DDNSAddConfig)
//获取ddns
v1DDNSGroup.GET("/list", v1.DDNSConfigList)
//获取ddns
v1DDNSGroup.DELETE("/delete/:id", v1.DDNSDelete)
}
v1AppGroup := v1Group.Group("/app")
v1AppGroup.Use()
{
//获取我的已安装的列表
v1AppGroup.GET("/mylist", v1.MyAppList)
v1AppGroup.GET("/my/list", v1.MyAppList)
//
v1AppGroup.GET("/usage", v1.AppUsageList)
//app详情
@@ -154,8 +85,8 @@ func InitRouter() *gin.Engine {
v1AppGroup.GET("/category", v1.CategoryList)
//容器相关
v1AppGroup.GET("/terminal/:id", v1.DockerTerminal)
//准备安装
//v1AppGroup.GET("/ready/:id", v1.ReadyInstall)
v1AppGroup.GET("/order", v1.GetAppOrder)
v1AppGroup.POST("/order", v1.PostAppOrder)
//app容器详情
v1AppGroup.GET("/info/:id", v1.ContainerInfo)
//app容器日志
@@ -163,11 +94,9 @@ func InitRouter() *gin.Engine {
//暂停或启动容器
v1AppGroup.PUT("/state/:id", v1.ChangAppState)
//安装app
v1AppGroup.POST("/install/:id", v1.InstallApp)
v1AppGroup.POST("/install", v1.InstallApp)
//卸载app
v1AppGroup.DELETE("/uninstall/:id", v1.UnInstallApp)
//获取安装进度
v1AppGroup.GET("/speed/:id", v1.GetInstallSpeed)
//获取进度
v1AppGroup.GET("/state/:id", v1.GetContainerState)
//更新容器配置
@@ -176,17 +105,16 @@ func InitRouter() *gin.Engine {
v1AppGroup.GET("/update/:id/info", v1.ContainerUpdateInfo)
v1AppGroup.GET("/rely/:id/info", v1.ContainerRelyInfo)
v1AppGroup.GET("/install/config", v1.GetDockerInstallConfig)
//v1AppGroup.POST("/custom/install", v1.CustomInstallApp)
v1AppGroup.PUT("/update/:id", v1.PutAppUpdate)
v1AppGroup.POST("/share", v1.ShareAppFile)
}
v1SysGroup := v1Group.Group("/sys")
v1SysGroup.Use()
{
//获取检查版本是否需要升级
v1SysGroup.GET("/check", v1.CheckVersion)
v1SysGroup.GET("/hardware/info", v1.GetSystemHardwareInfo)
v1SysGroup.POST("/update", v1.SystemUpdate)
v1SysGroup.GET("/sys", v1.Sys)
v1SysGroup.GET("/wsssh", v1.WsSsh)
v1SysGroup.GET("/config", v1.GetSystemConfig)
v1SysGroup.GET("/error/logs", v1.GetCasaOSErrorLogs)
@@ -197,21 +125,35 @@ func InitRouter() *gin.Engine {
v1SysGroup.PUT("/port", v1.PutCasaOSPort)
v1SysGroup.POST("/kill", v1.PostKillCasaOS)
v1SysGroup.GET("/info", v1.Info)
v1SysGroup.PUT("/usb/off", v1.PutSystemOffUSBAutoMount)
v1SysGroup.PUT("/usb/on", v1.PutSystemOnUSBAutoMount)
v1SysGroup.GET("/usb", v1.GetSystemUSBAutoMount)
v1SysGroup.GET("/cpu", v1.CupInfo)
v1SysGroup.GET("/mem", v1.MemInfo)
v1SysGroup.GET("/disk", v1.DiskInfo)
v1SysGroup.GET("/network", v1.NetInfo)
}
v1FileGroup := v1Group.Group("/file")
v1FileGroup.Use()
{
//修改文件名称/目录名称
v1FileGroup.PUT("/rename", v1.RenamePath)
v1FileGroup.GET("/read", v1.GetFilerContent)
v1FileGroup.POST("/upload", v1.PostFileUpload)
v1FileGroup.GET("/catalog", v1.DirPath)
//创建目录
v1FileGroup.GET("/upload", v1.GetFileUpload)
v1FileGroup.GET("/dirpath", v1.DirPath)
//create folder
v1FileGroup.POST("/mkdir", v1.MkdirAll)
v1FileGroup.POST("/create", v1.PostCreateFile)
v1FileGroup.GET("/download", v1.GetDownloadFile)
v1FileGroup.PUT("/move", v1.PutFileMove)
v1FileGroup.GET("/download/*path", v1.GetDownloadSingleFile)
v1FileGroup.POST("/operate", v1.PostOperateFileOrDir)
v1FileGroup.DELETE("/delete", v1.DeleteFile)
v1FileGroup.PUT("/update", v1.PutFileContent)
v1FileGroup.GET("/image", v1.GetFileImage)
v1FileGroup.DELETE("/operate/:id", v1.DeleteOperateFileOrDir)
//v1FileGroup.GET("/download", v1.UserFileDownloadCommonService)
}
v1DiskGroup := v1Group.Group("/disk")
@@ -241,6 +183,7 @@ func InitRouter() *gin.Engine {
//删除分区
v1DiskGroup.DELETE("/delpart", v1.RemovePartition)
v1DiskGroup.GET("/usb", v1.GetUSBList)
}
v1ShareGroup := v1Group.Group("/share")
@@ -259,26 +202,38 @@ func InitRouter() *gin.Engine {
v1TaskGroup.PUT("/update", v1.PutTaskUpdate)
v1TaskGroup.POST("/add", v1.PostTaskAdd)
v1TaskGroup.PUT("/completion/:id", v1.PutTaskMarkerCompletion)
}
v1PersonGroup := v1Group.Group("/person")
v1PersonGroup.Use()
{
v1PersonGroup.GET("/detection", v1.GetPersonDetection)
v1PersonGroup.GET("/users", v1.GetPersonFriend)
v1PersonGroup.POST("/user/:shareids", v1.PostAddPersonFriend)
v1PersonGroup.DELETE("/user/:shareid", v1.DeletePersonFriend)
v1PersonGroup.GET("/directory", v1.GetPersonDirectory)
v1PersonGroup.GET("/file", v1.GetPersonFile)
v1PersonGroup.GET("/refile/:uuid", v1.GetPersonReFile)
v1PersonGroup.PUT("/remarks/:shareid", v1.PutPersonRemarks)
v1PersonGroup.GET("/list", v1.GetPersonDownloadList)
v1PersonGroup.DELETE("/file/:uuid", v1.DeletePersonDownloadFile)
v1PersonGroup.POST("/share", v1.PostPersonShare)
v1PersonGroup.POST("/file/:shareid", v1.PostPersonFile)
v1PersonGroup.GET("/share", v1.GetPersonShare)
v1PersonGroup.POST("/down/dir", v1.PostPersonDownDir)
v1PersonGroup.GET("/down/dir", v1.GetPersonDownDir)
v1PersonGroup.PUT("/block/:shareid", v1.PutPersonBlock)
v1PersonGroup.GET("/public", v1.GetPersonPublic)
v1PersonGroup.PUT("/friend/:shareid", v1.PutPersonAgreeFriend)
v1PersonGroup.PUT("/write/:shareid", v1.PutPersonWrite)
v1PersonGroup.GET("/image/thumbnail/:shareid", v1.GetPersonImageThumbnail)
}
v1ShortcutsGroup := v1Group.Group("/shortcuts")
v1ShortcutsGroup.Use()
v1AnalyseGroup := v1Group.Group("/analyse")
v1AnalyseGroup.Use()
{
v1ShortcutsGroup.GET("/list", v1.GetShortcutsList)
v1ShortcutsGroup.POST("/add", v1.PostShortcutsAdd)
v1ShortcutsGroup.PUT("/edit", v1.PutShortcutsEdit)
v1ShortcutsGroup.DELETE("/del/:id", v1.DeleteShortcutsDelete)
}
v1NotifyGroup := v1Group.Group("/notify")
v1NotifyGroup.Use()
{
v1NotifyGroup.GET("/ws", v1.NotifyWS)
v1NotifyGroup.PUT("/read/:id", v1.PutNotifyRead)
}
v1SearchGroup := v1Group.Group("/search")
v1SearchGroup.Use()
{
v1SearchGroup.GET("/search", v1.GetSearchList)
v1AnalyseGroup.POST("/app", v1.PostAppAnalyse)
}
v1Group.GET("/sync/config", v1.GetSyncConfig)
v1Group.Any("/syncthing/*url", v1.SyncToSyncthing)

60
route/socket.go Normal file
View File

@@ -0,0 +1,60 @@
/*
* @Author: LinkLeong link@icewhale.com
* @Date: 2022-05-23 17:18:56
* @LastEditors: LinkLeong
* @LastEditTime: 2022-06-08 16:31:24
* @FilePath: /CasaOS/route/socket.go
* @Description:
* @Website: https://www.casaos.io
* Copyright (c) 2022 by icewhale, All Rights Reserved.
*/
package route
import (
"strconv"
"time"
"github.com/IceWhaleTech/CasaOS/model/notify"
"github.com/IceWhaleTech/CasaOS/pkg/config"
"github.com/IceWhaleTech/CasaOS/pkg/utils/port"
"github.com/IceWhaleTech/CasaOS/service"
f "github.com/ambelovsky/gosf"
)
func SocketInit(msg chan notify.Message) {
// set socket port
socketPort := 0
if len(config.ServerInfo.SocketPort) == 0 {
socketPort, _ = port.GetAvailablePort("tcp")
config.ServerInfo.SocketPort = strconv.Itoa(socketPort)
config.Cfg.Section("server").Key("SocketPort").SetValue(strconv.Itoa(socketPort))
config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath)
} else {
socketPort, _ = strconv.Atoi(config.ServerInfo.SocketPort)
if !port.IsPortAvailable(socketPort, "tcp") {
socketPort, _ := port.GetAvailablePort("tcp")
config.ServerInfo.SocketPort = strconv.Itoa(socketPort)
config.Cfg.Section("server").Key("SocketPort").SetValue(strconv.Itoa(socketPort))
config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath)
}
}
f.OnConnect(func(c *f.Client, request *f.Request) {
service.ClientCount += 1
})
f.OnDisconnect(func(c *f.Client, request *f.Request) {
service.ClientCount -= 1
})
go func(msg chan notify.Message) {
for v := range msg {
f.Broadcast("", v.Path, &v.Msg)
time.Sleep(time.Millisecond * 300)
}
}(msg)
f.Startup(map[string]interface{}{
"port": socketPort})
}

38
route/v1/analyse.go Normal file
View File

@@ -0,0 +1,38 @@
package v1
import (
"net/http"
"github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/pkg/config"
oasis_err2 "github.com/IceWhaleTech/CasaOS/pkg/utils/oasis_err"
"github.com/IceWhaleTech/CasaOS/service"
"github.com/gin-gonic/gin"
)
// @Summary post app analyse
// @Produce application/json
// @Accept multipart/form-data
// @Tags analyse
// @Param name formData string true "app name"
// @Param type formData string true "action" Enums(open,delete)
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /analyse/app [post]
func PostAppAnalyse(c *gin.Context) {
if config.SystemConfigInfo.Analyse == "False" {
c.JSON(http.StatusOK, &model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
return
}
name := c.PostForm("name")
t := c.PostForm("type")
language := c.GetHeader("Language")
if len(name) == 0 || len(t) == 0 {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
return
}
service.MyService.Casa().PushAppAnalyse(config.ServerInfo.Token, t, name, language)
c.JSON(http.StatusOK, &model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
}

View File

@@ -36,7 +36,7 @@ func AppList(c *gin.Context) {
categoryId := c.DefaultQuery("category_id", "0")
key := c.DefaultQuery("key", "")
language := c.GetHeader("Language")
recommend, list, community := service.MyService.OAPI().GetServerList(index, size, t, categoryId, key, language)
recommend, list, community := service.MyService.Casa().GetServerList(index, size, t, categoryId, key, language)
// for i := 0; i < len(recommend); i++ {
// ct, _ := service.MyService.Docker().DockerListByImage(recommend[i].Image, recommend[i].ImageVersion)
// if ct != nil {
@@ -106,13 +106,18 @@ func PortCheck(c *gin.Context) {
// @Param size query int false "size"
// @Param position query bool false "是否是首页应用"
// @Success 200 {string} string "ok"
// @Router /app/mylist [get]
// @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 := service.MyService.App().GetMyList(index, size, position)
c.JSON(http.StatusOK, &model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: list})
list, unTranslation := service.MyService.App().GetMyList(index, size, position)
data := make(map[string]interface{}, 2)
data["list"] = list
data["local"] = unTranslation
c.JSON(http.StatusOK, &model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: data})
}
// @Summary my app hardware usage list
@@ -139,7 +144,7 @@ func AppInfo(c *gin.Context) {
id := c.Param("id")
language := c.GetHeader("Language")
info := service.MyService.OAPI().GetServerAppInfo(id, "", language)
info := service.MyService.Casa().GetServerAppInfo(id, "", language)
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) {
@@ -204,7 +209,7 @@ func AppInfo(c *gin.Context) {
// sort.VolSort(volOrder).Sort(info.Volumes.([]model.PathMap))
// sort.DevSort(devOrder).Sort(info.Devices)
info.MaxMemory = service.MyService.ZiMa().GetMemInfo().Total >> 20
info.MaxMemory = service.MyService.System().GetMemInfo().Total >> 20
c.JSON(http.StatusOK, &model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: info})
}
@@ -217,7 +222,7 @@ func AppInfo(c *gin.Context) {
// @Success 200 {string} string "ok"
// @Router /app/category [get]
func CategoryList(c *gin.Context) {
list := service.MyService.OAPI().GetServerCategoryList()
list := service.MyService.Casa().GetServerCategoryList()
var count uint = 0
for _, category := range list {
count += category.Count
@@ -238,17 +243,6 @@ func CategoryList(c *gin.Context) {
// @Router /app/share [post]
func ShareAppFile(c *gin.Context) {
str, _ := ioutil.ReadAll(c.Request.Body)
content := service.MyService.OAPI().ShareAppFile(str)
content := service.MyService.Casa().ShareAppFile(str)
c.JSON(http.StatusOK, json.RawMessage(content))
}
// @Summary Resource Usage
// @Produce application/json
// @Accept application/json
// @Tags app
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /app/share [post]
func AppListResourceUsage() {
}

View File

@@ -1,6 +1,7 @@
package v1
import (
"fmt"
"net/http"
"reflect"
"strconv"
@@ -27,7 +28,7 @@ var diskMap = make(map[string]string)
// @Success 200 {string} string "ok"
// @Router /disk/list [get]
func GetDiskList(c *gin.Context) {
list := service.MyService.Disk().LSBLK()
list := service.MyService.Disk().LSBLK(false)
dbList := service.MyService.Disk().GetSerialAll()
part := make(map[string]int64, len(dbList))
for _, v := range dbList {
@@ -53,6 +54,32 @@ func GetDiskList(c *gin.Context) {
if len(list[i].Children) > 0 && findSystem == 0 {
for j := 0; j < len(list[i].Children); j++ {
if len(list[i].Children[j].Children) > 0 {
for _, v := range list[i].Children[j].Children {
if v.MountPoint == "/" {
stor := model.Storage{}
stor.Name = "System"
stor.MountPoint = v.MountPoint
stor.Size = v.FSSize
stor.Avail = v.FSAvail
stor.Path = v.Path
stor.Type = v.FsType
stor.DriveName = "System"
disk.Model = "System"
if strings.Contains(v.SubSystems, "mmc") {
disk.DiskType = "MMC"
} else if strings.Contains(v.SubSystems, "usb") {
disk.DiskType = "USB"
}
disk.Health = "true"
disks = append(disks, disk)
storage = append(storage, stor)
findSystem = 1
break
}
}
} else {
if list[i].Children[j].MountPoint == "/" {
stor := model.Storage{}
stor.Name = "System"
@@ -76,16 +103,18 @@ func GetDiskList(c *gin.Context) {
break
}
}
}
}
if findSystem == 1 {
findSystem += 1
continue
}
if list[i].Tran == "sata" || list[i].Tran == "nvme" {
if list[i].Tran == "sata" || list[i].Tran == "nvme" || list[i].Tran == "spi" || list[i].Tran == "sas" {
temp := service.MyService.Disk().SmartCTL(list[i].Path)
if reflect.DeepEqual(temp, model.SmartctlA{}) {
continue
temp.SmartStatus.Passed = true
}
if len(list[i].Children) == 1 && len(list[i].Children[0].MountPoint) > 0 {
stor := model.Storage{}
@@ -137,7 +166,7 @@ func GetDiskList(c *gin.Context) {
// @Router /disk/lists [get]
func GetPlugInDisks(c *gin.Context) {
list := service.MyService.Disk().LSBLK()
list := service.MyService.Disk().LSBLK(true)
var result []*disk.UsageStat
for _, item := range list {
result = append(result, service.MyService.Disk().GetDiskInfoByPath(item.Path))
@@ -252,13 +281,12 @@ func RemovePartition(c *gin.Context) {
func AddPartition(c *gin.Context) {
name := c.PostForm("name")
path := c.PostForm("path")
serial := c.PostForm("serial")
format, _ := strconv.ParseBool(c.PostForm("format"))
if len(name) == 0 || len(path) == 0 || len(serial) == 0 {
if len(name) == 0 || len(path) == 0 {
c.JSON(http.StatusOK, model.Result{Success: oasis_err.INVALID_PARAMS, Message: oasis_err.GetMsg(oasis_err.INVALID_PARAMS)})
return
}
if _, ok := diskMap[serial]; ok {
if _, ok := diskMap[path]; ok {
c.JSON(http.StatusOK, model.Result{Success: oasis_err.DISK_BUSYING, Message: oasis_err.GetMsg(oasis_err.DISK_BUSYING)})
return
}
@@ -267,36 +295,49 @@ func AddPartition(c *gin.Context) {
c.JSON(http.StatusOK, model.Result{Success: oasis_err.NAME_NOT_AVAILABLE, Message: oasis_err.GetMsg(oasis_err.NAME_NOT_AVAILABLE)})
return
}
diskMap[serial] = "busying"
diskMap[path] = "busying"
currentDisk := service.MyService.Disk().GetDiskInfo(path)
if !format {
if len(currentDisk.Children) != 1 || !(len(currentDisk.Children) > 0 && currentDisk.Children[0].FsType == "ext4") {
c.JSON(http.StatusOK, model.Result{Success: oasis_err.DISK_NEEDS_FORMAT, Message: oasis_err.GetMsg(oasis_err.DISK_NEEDS_FORMAT)})
delete(diskMap, serial)
delete(diskMap, path)
return
}
} else {
service.MyService.Disk().AddPartition(path)
}
formatBool := true
for formatBool {
currentDisk = service.MyService.Disk().GetDiskInfo(path)
fmt.Println(currentDisk.Children)
if len(currentDisk.Children) > 0 {
formatBool = false
break
}
time.Sleep(time.Second)
}
currentDisk = service.MyService.Disk().GetDiskInfo(path)
if len(currentDisk.Children) != 1 {
c.JSON(http.StatusOK, model.Result{Success: oasis_err.DISK_NEEDS_FORMAT, Message: oasis_err.GetMsg(oasis_err.DISK_NEEDS_FORMAT)})
return
}
mountPath := "/DATA/" + name
service.MyService.Disk().MountDisk(path, mountPath)
m := model2.SerialDisk{}
m.MountPoint = mountPath
m.Path = path + "1"
m.Serial = serial
m.Path = currentDisk.Children[0].Path
m.UUID = currentDisk.Children[0].UUID
m.State = 0
m.CreatedAt = time.Now().Unix()
service.MyService.Disk().SaveMountPoint(m)
//mount dir
service.MyService.Disk().MountDisk(path+"1", mountPath)
service.MyService.Disk().MountDisk(currentDisk.Children[0].Path, mountPath)
service.MyService.Disk().RemoveLSBLKCache()
delete(diskMap, serial)
delete(diskMap, path)
c.JSON(http.StatusOK, model.Result{Success: oasis_err.SUCCESS, Message: oasis_err.GetMsg(oasis_err.SUCCESS)})
}
@@ -334,7 +375,7 @@ func PostMountDisk(c *gin.Context) {
m := model2.SerialDisk{}
m.MountPoint = mountPath
m.Path = path
m.Serial = serial
m.UUID = serial
m.State = 0
//service.MyService.Disk().SaveMountPoint(m)
c.JSON(http.StatusOK, model.Result{Success: oasis_err.SUCCESS, Message: oasis_err.GetMsg(oasis_err.SUCCESS)})
@@ -401,7 +442,7 @@ func DeleteDisk(c *gin.Context) {
func GetDiskCheck(c *gin.Context) {
dbList := service.MyService.Disk().GetSerialAll()
list := service.MyService.Disk().LSBLK()
list := service.MyService.Disk().LSBLK(true)
mapList := make(map[string]string)
@@ -410,7 +451,7 @@ func GetDiskCheck(c *gin.Context) {
}
for _, v := range dbList {
if _, ok := mapList[v.Serial]; !ok {
if _, ok := mapList[v.UUID]; !ok {
//disk undefind
c.JSON(http.StatusOK, model.Result{Success: oasis_err.ERROR, Message: oasis_err.GetMsg(oasis_err.ERROR), Data: "disk undefind"})
return
@@ -419,3 +460,38 @@ func GetDiskCheck(c *gin.Context) {
c.JSON(http.StatusOK, model.Result{Success: oasis_err.SUCCESS, Message: oasis_err.GetMsg(oasis_err.SUCCESS)})
}
// @Summary check mount point
// @Produce application/json
// @Accept application/json
// @Tags disk
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /disk/usb [get]
func GetUSBList(c *gin.Context) {
list := service.MyService.Disk().LSBLK(false)
data := []model.DriveUSB{}
for _, v := range list {
if v.Tran == "usb" {
temp := model.DriveUSB{}
temp.Model = v.Model
temp.Name = v.Name
temp.Size = v.Size
mountTemp := true
if len(v.Children) == 0 {
mountTemp = false
}
for _, child := range v.Children {
if len(child.MountPoint) > 0 {
avail, _ := strconv.ParseUint(child.FSAvail, 10, 64)
temp.Avail += avail
} else {
mountTemp = false
}
}
temp.Mount = mountTemp
data = append(data, temp)
}
}
c.JSON(http.StatusOK, model.Result{Success: oasis_err.SUCCESS, Message: oasis_err.GetMsg(oasis_err.SUCCESS), Data: data})
}

View File

@@ -2,19 +2,22 @@ package v1
import (
"bytes"
"encoding/json"
json2 "encoding/json"
"net/http"
"reflect"
"path/filepath"
"strconv"
"strings"
"time"
"github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/model/notify"
"github.com/IceWhaleTech/CasaOS/pkg/config"
"github.com/IceWhaleTech/CasaOS/pkg/docker"
upnp2 "github.com/IceWhaleTech/CasaOS/pkg/upnp"
"github.com/IceWhaleTech/CasaOS/pkg/utils/file"
ip_helper2 "github.com/IceWhaleTech/CasaOS/pkg/utils/ip_helper"
"github.com/IceWhaleTech/CasaOS/pkg/utils/oasis_err"
oasis_err2 "github.com/IceWhaleTech/CasaOS/pkg/utils/oasis_err"
port2 "github.com/IceWhaleTech/CasaOS/pkg/utils/port"
"github.com/IceWhaleTech/CasaOS/pkg/utils/random"
@@ -26,7 +29,6 @@ import (
"github.com/gorilla/websocket"
"github.com/jinzhu/copier"
uuid "github.com/satori/go.uuid"
"github.com/tidwall/gjson"
"golang.org/x/crypto/ssh"
)
@@ -142,10 +144,8 @@ func SpeedPush(c *gin.Context) {
// @Param env formData string false "环境变量"
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /app/install/{id} [post]
// @Router /app/install [post]
func InstallApp(c *gin.Context) {
appId := c.Param("id")
language := c.GetHeader("Language")
var appInfo model.ServerAppList
m := model.CustomizationPostData{}
c.BindJSON(&m)
@@ -153,7 +153,30 @@ func InstallApp(c *gin.Context) {
const CUSTOM = "custom"
var dockerImage string
var dockerImageVersion string
//检查端口
//check app name is exist
if len(m.Protocol) == 0 {
m.Protocol = "http"
}
if m.Origin != "custom" {
oldName := m.Label
for i := 0; true; i++ {
if i != 0 {
m.Label = oldName + "-" + strconv.Itoa(i)
}
if _, err := service.MyService.Docker().DockerListByName(m.Label); err != nil {
break
}
}
} else {
if _, err := service.MyService.Docker().DockerListByName(m.Label); err == nil {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.ERROR_APP_NAME_EXIST, Message: oasis_err2.GetMsg(oasis_err2.ERROR_APP_NAME_EXIST)})
return
}
}
//check port
if len(m.PortMap) > 0 && m.PortMap != "0" {
//c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
portMap, _ := strconv.Atoi(m.PortMap)
@@ -174,21 +197,6 @@ func InstallApp(c *gin.Context) {
dockerImage = m.Image
dockerImageVersion = "latest"
}
if m.Origin != "custom" {
appInfo = service.MyService.OAPI().GetServerAppInfo(appId, "", language)
} else {
appInfo.Title = m.Label
appInfo.Description = m.Description
appInfo.Icon = m.Icon
appInfo.ScreenshotLink = model.Strings{}
appInfo.NetworkModel = m.NetworkModel
appInfo.Tags = model.Strings{}
appInfo.Tagline = ""
appInfo.Index = m.Index
}
for _, u := range m.Ports {
@@ -247,17 +255,19 @@ func InstallApp(c *gin.Context) {
//
//}
id := uuid.NewV4().String()
m.CustomId = id
var relyMap = make(map[string]string)
go func() {
installLog := model2.AppNotify{}
installLog.State = 0
installLog.CustomId = id
installLog.Message = "installing rely"
installLog.Type = types.NOTIFY_TYPE_UNIMPORTANT
installLog.CreatedAt = strconv.FormatInt(time.Now().Unix(), 10)
installLog.UpdatedAt = strconv.FormatInt(time.Now().Unix(), 10)
service.MyService.Notify().AddLog(installLog)
// installLog := model2.AppNotify{}
// installLog.State = 0
// installLog.CustomId = m.Label
// installLog.Message = "installing rely"
// installLog.Class = types.NOTIFY_APP
// installLog.Type = types.NOTIFY_TYPE_UNIMPORTANT
// installLog.CreatedAt = strconv.FormatInt(time.Now().Unix(), 10)
// installLog.UpdatedAt = strconv.FormatInt(time.Now().Unix(), 10)
// installLog.Id = uuid.NewV4().String()
// service.MyService.Notify().AddLog(installLog)
if m.Origin != "custom" {
for _, plugin := range appInfo.Plugins {
if plugin == "mysql" {
@@ -278,7 +288,7 @@ func InstallApp(c *gin.Context) {
rely.Type = types.RELY_TYPE_MYSQL
rely.ContainerId = mysqlContainerId
rely.CustomId = mid
rely.ContainerCustomId = id
rely.ContainerCustomId = m.Label
var mysqlConfig model2.MysqlConfigs
//结构体转换
@@ -290,24 +300,26 @@ func InstallApp(c *gin.Context) {
} else {
docker_base.MysqlDelete(mysqlContainerId)
installLog.State = 0
installLog.Message = err.Error()
service.MyService.Notify().UpdateLog(installLog)
// installLog.State = 0
// installLog.Message = err.Error()
// service.MyService.Notify().UpdateLog(installLog)
}
}
}
}
installLog.Message = "pulling"
service.MyService.Notify().UpdateLog(installLog)
// step下载镜像
err := service.MyService.Docker().DockerPullImage(dockerImage+":"+dockerImageVersion, installLog)
err := service.MyService.Docker().DockerPullImage(dockerImage+":"+dockerImageVersion, m.Icon, m.Label)
if err != nil {
installLog.State = 0
installLog.Message = err.Error()
installLog.Type = types.NOTIFY_TYPE_ERROR
service.MyService.Notify().UpdateLog(installLog)
notify := notify.Application{}
notify.Icon = m.Icon
notify.Name = m.Label
notify.State = "PULLING"
notify.Type = "INSTALL"
notify.Success = false
notify.Finished = false
notify.Message = err.Error()
service.MyService.Notify().SendInstallAppBySocket(notify)
return
}
@@ -315,60 +327,57 @@ func InstallApp(c *gin.Context) {
time.Sleep(time.Second)
}
//if {
//}
//step创建容器
// networkName, err := service.MyService.Docker().GetNetWorkNameByNetWorkID(appInfo.NetworkModel)
// if err != nil {
// //service.MyService.Redis().Set(id, "{\"id\"\""+id+"\",\"state\":false,\"message\":\""+err.Error()+"\",\"speed\":80}", 100)
// installLog.State = 0
// installLog.Speed = 75
// installLog.Type = types.NOTIFY_TYPE_ERROR
// installLog.Message = err.Error()
// service.MyService.Notify().UpdateLog(installLog)
// return
// }
containerId, err := service.MyService.Docker().DockerContainerCreate(dockerImage+":"+dockerImageVersion, id, m, appInfo.NetworkModel)
installLog.Name = appInfo.Title
installLog.Icon = appInfo.Icon
_, err = service.MyService.Docker().DockerContainerCreate(dockerImage+":"+dockerImageVersion, m, appInfo.NetworkModel)
if err != nil {
//service.MyService.Redis().Set(id, "{\"id\"\""+id+"\",\"state\":false,\"message\":\""+err.Error()+"\",\"speed\":80}", 100)
installLog.State = 0
installLog.Type = types.NOTIFY_TYPE_ERROR
installLog.Message = err.Error()
service.MyService.Notify().UpdateLog(installLog)
notify := notify.Application{}
notify.Icon = m.Icon
notify.Name = m.Label
notify.State = "STARTING"
notify.Type = "INSTALL"
notify.Success = false
notify.Finished = false
notify.Message = err.Error()
service.MyService.Notify().SendInstallAppBySocket(notify)
return
} else {
//service.MyService.Redis().Set(id, "{\"id\":\""+id+"\",\"state\":true,\"message\":\"starting\",\"speed\":80}", 100)
installLog.Message = "starting"
service.MyService.Notify().UpdateLog(installLog)
notify := notify.Application{}
notify.Icon = m.Icon
notify.Name = m.Label
notify.State = "STARTING"
notify.Type = "INSTALL"
notify.Success = true
notify.Finished = false
service.MyService.Notify().SendInstallAppBySocket(notify)
}
// echo -e "hellow\nworld" >>
//step启动容器
err = service.MyService.Docker().DockerContainerStart(id)
err = service.MyService.Docker().DockerContainerStart(m.Label)
if err != nil {
//service.MyService.Redis().Set(id, "{\"id\"\""+id+"\",\"state\":false,\"message\":\""+err.Error()+"\",\"speed\":90}", 100)
installLog.State = 0
installLog.Type = types.NOTIFY_TYPE_ERROR
installLog.Message = err.Error()
service.MyService.Notify().UpdateLog(installLog)
notify := notify.Application{}
notify.Icon = m.Icon
notify.Name = m.Label
notify.State = "STARTING"
notify.Type = "INSTALL"
notify.Success = false
notify.Finished = false
notify.Message = err.Error()
service.MyService.Notify().SendInstallAppBySocket(notify)
return
} else {
//service.MyService.Redis().Set(id, "{\"id\":\""+id+"\",\"state\":true,\"message\":\"setting upnp\",\"speed\":90}", 100)
if m.Origin != CUSTOM {
installLog.Message = "setting upnp"
} else {
installLog.Message = "nearing completion"
}
service.MyService.Notify().UpdateLog(installLog)
// if m.Origin != CUSTOM {
// installLog.Message = "setting upnp"
// } else {
// installLog.Message = "nearing completion"
// }
// service.MyService.Notify().UpdateLog(installLog)
}
if m.Origin != CUSTOM {
//step:启动upnp
//step:enable upnp
if m.EnableUPNP {
upnp, err := upnp2.Gateway()
if err == nil {
@@ -400,88 +409,55 @@ func InstallApp(c *gin.Context) {
}
}
if err != nil {
//service.MyService.Redis().Set(id, "{\"id\"\""+id+"\",\"state\":false,\"message\":\""+err.Error()+"\",\"speed\":95}", 100)
installLog.State = 0
installLog.Type = types.NOTIFY_TYPE_ERROR
installLog.Message = err.Error()
service.MyService.Notify().UpdateLog(installLog)
} else {
//service.MyService.Redis().Set(id, "{\"id\":\""+id+"\",\"state\":true,\"message\":\"checking\",\"speed\":95}", 100)
installLog.Message = "checking"
service.MyService.Notify().UpdateLog(installLog)
}
// if err != nil {
// //service.MyService.Redis().Set(id, "{\"id\"\""+id+"\",\"state\":false,\"message\":\""+err.Error()+"\",\"speed\":95}", 100)
// installLog.State = 0
// installLog.Type = types.NOTIFY_TYPE_ERROR
// installLog.Message = err.Error()
// service.MyService.Notify().UpdateLog(installLog)
// } else {
// //service.MyService.Redis().Set(id, "{\"id\":\""+id+"\",\"state\":true,\"message\":\"checking\",\"speed\":95}", 100)
// installLog.Message = "checking"
// service.MyService.Notify().UpdateLog(installLog)
// }
}
}
//step: 启动成功 检查容器状态确认启动成功
container, err := service.MyService.Docker().DockerContainerInfo(id)
container, err := service.MyService.Docker().DockerContainerInfo(m.Label)
if err != nil && container.ContainerJSONBase.State.Running {
//service.MyService.Redis().Set(id, "{\"id\"\""+id+"\",\"state\":false,\"message\":\""+err.Error()+"\",\"speed\":100}", 100)
installLog.State = 0
installLog.Type = types.NOTIFY_TYPE_ERROR
installLog.Message = err.Error()
service.MyService.Notify().UpdateLog(installLog)
notify := notify.Application{}
notify.Icon = m.Icon
notify.Name = m.Label
notify.State = "INSTALLED"
notify.Type = "INSTALL"
notify.Success = false
notify.Finished = true
notify.Message = err.Error()
service.MyService.Notify().SendInstallAppBySocket(notify)
return
} else {
//service.MyService.Redis().Set(id, "{\"id\":\""+id+"\",\"state\":true,\"message\":\"installed\",\"speed\":100}", 100)
installLog.Message = "installed"
service.MyService.Notify().UpdateLog(installLog)
notify := notify.Application{}
notify.Icon = m.Icon
notify.Name = m.Label
notify.State = "INSTALLED"
notify.Type = "INSTALL"
notify.Success = true
notify.Finished = true
service.MyService.Notify().SendInstallAppBySocket(notify)
}
rely := model.MapStrings{}
copier.Copy(&rely, &relyMap)
// if m.Origin != "custom" {
// for i := 0; i < len(m.Volumes); i++ {
// m.Volumes[i].Path = docker.GetDir(id, m.Volumes[i].Path)
// }
// }
portsStr, _ := json2.Marshal(m.Ports)
envsStr, _ := json2.Marshal(m.Envs)
volumesStr, _ := json2.Marshal(m.Volumes)
devicesStr, _ := json2.Marshal(m.Devices)
//step: 保存数据到数据库
md := model2.AppListDBModel{
CustomId: id,
Title: appInfo.Title,
//ScreenshotLink: appInfo.ScreenshotLink,
Slogan: appInfo.Tagline,
Description: appInfo.Description,
//Tags: appInfo.Tags,
Icon: appInfo.Icon,
Version: dockerImageVersion,
ContainerId: containerId,
Image: dockerImage,
Index: appInfo.Index,
//Port: m.Port,
PortMap: m.PortMap,
Label: m.Label,
EnableUPNP: m.EnableUPNP,
Ports: string(portsStr),
Envs: string(envsStr),
Volumes: string(volumesStr),
Position: m.Position,
NetModel: appInfo.NetworkModel,
Restart: m.Restart,
CpuShares: m.CpuShares,
Memory: m.Memory,
Devices: string(devicesStr),
//Rely: rely,
Origin: m.Origin,
CreatedAt: strconv.FormatInt(time.Now().Unix(), 10),
UpdatedAt: strconv.FormatInt(time.Now().Unix(), 10),
}
//if appInfo.NetworkModel == "host" {
// m.PortMap = m.Port
//}
service.MyService.App().SaveContainer(md)
//service.MyService.App().SaveContainer(md)
config.CasaOSGlobalVariables.AppChange = true
}()
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: id})
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: m.Label})
}
@@ -682,40 +658,41 @@ func UnInstallApp(c *gin.Context) {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
return
}
info := service.MyService.App().GetUninstallInfo(appId)
//info := service.MyService.App().GetUninstallInfo(appId)
info, err := service.MyService.Docker().DockerContainerInfo(appId)
if err != nil {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.ERROR, Message: oasis_err2.GetMsg(oasis_err2.ERROR), Data: err.Error()})
return
}
//step停止容器
err := service.MyService.Docker().DockerContainerStop(appId)
err = service.MyService.Docker().DockerContainerStop(appId)
if err != nil {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.UNINSTALL_APP_ERROR, Message: oasis_err2.GetMsg(oasis_err2.UNINSTALL_APP_ERROR), Data: err.Error()})
return
}
//step删除容器
err = service.MyService.Docker().DockerContainerRemove(appId, false)
if err != nil {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.UNINSTALL_APP_ERROR, Message: oasis_err2.GetMsg(oasis_err2.UNINSTALL_APP_ERROR), Data: err.Error()})
return
}
//存在镜像正在使用的情况
// step删除镜像
service.MyService.Docker().DockerImageRemove(info.Image + ":" + info.Version)
//step: 删除本地数据
service.MyService.App().RemoveContainerById(appId)
if info.Origin != "custom" {
// stepremove image
service.MyService.Docker().DockerImageRemove(info.Config.Image)
if info.Config.Labels["origin"] != "custom" {
//step: 删除文件夹
vol := gjson.Get(info.Volumes, "#.host")
for _, v := range vol.Array() {
if strings.Contains(v.String(), appId) {
service.MyService.App().DelAppConfigDir(v.String())
for _, v := range info.Mounts {
if strings.Contains(v.Source, info.Name) {
path := filepath.Join(strings.Split(v.Source, info.Name)[0], info.Name)
service.MyService.App().DelAppConfigDir(path)
}
}
//step: 删除install log
service.MyService.Notify().DelLog(appId)
//service.MyService.Notify().DelLog(appId)
// for k, v := range info.Rely {
//
@@ -755,6 +732,14 @@ func UnInstallApp(c *gin.Context) {
//}
}
config.CasaOSGlobalVariables.AppChange = true
notify := notify.Application{}
notify.Icon = info.Config.Labels["icon"]
notify.Name = strings.ReplaceAll(info.Name, "/", "")
notify.State = "FINISHED"
notify.Type = "UNINSTALL"
notify.Success = true
notify.Finished = true
service.MyService.Notify().SendUninstallAppBySocket(notify)
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
}
@@ -777,7 +762,7 @@ func ChangAppState(c *gin.Context) {
} else if state == "start" {
err = service.MyService.Docker().DockerContainerStart(appId)
} else if state == "restart" {
err = service.MyService.Docker().DockerContainerStop(appId)
service.MyService.Docker().DockerContainerStop(appId)
err = service.MyService.Docker().DockerContainerStart(appId)
}
if err != nil {
@@ -806,20 +791,6 @@ func ContainerLog(c *gin.Context) {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: log})
}
// @Summary 获取安装进度
// @Produce application/json
// @Accept application/json
// @Tags app
// @Param id path string true "容器id"
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /app/speed/{id} [get]
func GetInstallSpeed(c *gin.Context) {
id := c.Param("id")
b := service.MyService.Notify().GetLog(id)
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: b})
}
// @Summary 获取容器状态
// @Produce application/json
// @Accept application/json
@@ -873,19 +844,23 @@ func UpdateSetting(c *gin.Context) {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
return
}
var cpd model.CustomizationPostData
//var cpd model.CustomizationPostData
copier.Copy(&cpd, &m)
//copier.Copy(&cpd, &m)
appInfo := service.MyService.App().GetAppDBInfo(id)
//appInfo := service.MyService.App().GetAppDBInfo(id)
//info, err := service.MyService.Docker().DockerContainerInfo(id)
var containerId string
containerId = appInfo.ContainerId
// //check app name is exist
// if _, err := service.MyService.Docker().DockerListByName(m.Label); err == nil {
// c.JSON(http.StatusOK, model.Result{Success: oasis_err2.ERROR_APP_NAME_EXIST, Message: oasis_err2.GetMsg(oasis_err2.ERROR_APP_NAME_EXIST)})
// return
// }
service.MyService.Docker().DockerContainerStop(id)
portMap, _ := strconv.Atoi(m.PortMap)
if !port2.IsPortAvailable(portMap, "tcp") {
service.MyService.Docker().DockerContainerStart(id)
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.ERROR, Message: "Duplicate port:" + m.PortMap})
return
}
@@ -895,75 +870,54 @@ func UpdateSetting(c *gin.Context) {
if u.Protocol == "udp" {
t, _ := strconv.Atoi(u.CommendPort)
if !port2.IsPortAvailable(t, "udp") {
service.MyService.Docker().DockerContainerStart(id)
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.ERROR, Message: "Duplicate port:" + u.CommendPort})
return
}
} else if u.Protocol == "tcp" {
te, _ := strconv.Atoi(u.CommendPort)
if !port2.IsPortAvailable(te, "tcp") {
service.MyService.Docker().DockerContainerStart(id)
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.ERROR, Message: "Duplicate port:" + u.CommendPort})
return
}
} else if u.Protocol == "both" {
t, _ := strconv.Atoi(u.CommendPort)
if !port2.IsPortAvailable(t, "udp") {
service.MyService.Docker().DockerContainerStart(id)
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.ERROR, Message: "Duplicate port:" + u.CommendPort})
return
}
te, _ := strconv.Atoi(u.CommendPort)
if !port2.IsPortAvailable(te, "tcp") {
service.MyService.Docker().DockerContainerStart(id)
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.ERROR, Message: "Duplicate port:" + u.CommendPort})
return
}
}
}
service.MyService.Docker().DockerContainerUpdateName(id, id)
//service.MyService.Docker().DockerContainerRemove(id, true)
//如果容器端口均未修改,这不进行处理
portsStr, _ := json2.Marshal(m.Ports)
containerId, err := service.MyService.Docker().DockerContainerCreate(m.Image, m, m.NetworkModel)
if err != nil {
service.MyService.Docker().DockerContainerUpdateName(m.Label, id)
service.MyService.Docker().DockerContainerStart(id)
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.ERROR, Message: oasis_err2.GetMsg(oasis_err2.ERROR)})
return
}
// echo -e "hellow\nworld" >>
envsStr, _ := json2.Marshal(m.Envs)
volumesStr, _ := json2.Marshal(m.Volumes)
devicesStr, _ := json2.Marshal(m.Devices)
if !reflect.DeepEqual(string(portsStr), appInfo.Ports) || !reflect.DeepEqual(string(envsStr), appInfo.Envs) || !reflect.DeepEqual(string(volumesStr), appInfo.Volumes) || m.PortMap != appInfo.PortMap || m.NetworkModel != appInfo.NetModel {
var newUUid = uuid.NewV4().String()
var err error
// networkName, err := service.MyService.Docker().GetNetWorkNameByNetWorkID(appInfo.NetModel)
// if err != nil {
// c.JSON(http.StatusOK, model.Result{Success: oasis_err2.ERROR, Message: oasis_err2.GetMsg(oasis_err2.ERROR)})
// return
// }
containerId, err = service.MyService.Docker().DockerContainerCreate(appInfo.Image+":"+appInfo.Version, newUUid, cpd, m.NetworkModel)
//step启动容器
err = service.MyService.Docker().DockerContainerStart(containerId)
if err != nil {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.ERROR, Message: oasis_err2.GetMsg(oasis_err2.ERROR)})
return
}
err = service.MyService.Docker().DockerContainerRemove(id, true)
if err != nil {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.ERROR, Message: oasis_err2.GetMsg(oasis_err2.ERROR)})
return
}
service.MyService.Docker().DockerContainerUpdateName(appInfo.CustomId, newUUid)
if err != nil {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.ERROR, Message: oasis_err2.GetMsg(oasis_err2.ERROR)})
return
}
} else if !reflect.DeepEqual(string(devicesStr), appInfo.Devices) || m.CpuShares != appInfo.CpuShares || m.Memory != appInfo.Memory || m.Restart != appInfo.Restart {
service.MyService.Docker().DockerContainerUpdate(cpd, id)
}
err := service.MyService.Docker().DockerContainerStart(id)
if err != nil {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.ERROR, Message: oasis_err2.GetMsg(oasis_err2.ERROR)})
return
}
service.MyService.Docker().DockerContainerRemove(id, true)
//更新upnp
if m.Origin != CUSTOM {
//if appInfo.EnableUPNP != appInfo.EnableUPNP {
@@ -1029,27 +983,96 @@ func UpdateSetting(c *gin.Context) {
//}
}
appInfo.ContainerId = containerId
appInfo.PortMap = m.PortMap
appInfo.Label = m.Label
appInfo.Index = m.Index
appInfo.Ports = string(portsStr)
appInfo.Envs = string(envsStr)
appInfo.Icon = m.Icon
appInfo.Volumes = string(volumesStr)
appInfo.Devices = string(devicesStr)
appInfo.NetModel = m.NetworkModel
appInfo.Position = m.Position
appInfo.EnableUPNP = m.EnableUPNP
appInfo.Restart = m.Restart
appInfo.Memory = m.Memory
appInfo.CpuShares = m.CpuShares
appInfo.UpdatedAt = strconv.FormatInt(time.Now().Unix(), 10)
service.MyService.App().UpdateApp(appInfo)
//service.MyService.App().UpdateApp(appInfo)
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
}
// @Summary update app version
// @Produce application/json
// @Accept multipart/form-data
// @Tags app
// @Param id path string true "容器id"
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /app/update/{id} [put]
func PutAppUpdate(c *gin.Context) {
id := c.Param("id")
if len(id) == 0 {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
return
}
inspect, err := service.MyService.Docker().DockerContainerInfo(id)
if err != nil {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.ERROR, Message: oasis_err2.GetMsg(oasis_err2.ERROR), Data: err.Error()})
return
}
imageLatest := strings.Split(inspect.Config.Image, ":")[0] + ":latest"
err = service.MyService.Docker().DockerPullImage(imageLatest, "", "")
if err != nil {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.ERROR, Message: oasis_err2.GetMsg(oasis_err2.ERROR), Data: err.Error()})
return
}
service.MyService.Docker().DockerContainerStop(id)
service.MyService.Docker().DockerContainerUpdateName(id, id)
//service.MyService.Docker().DockerContainerRemove(id, true)
inspect.Image = imageLatest
inspect.Config.Image = imageLatest
containerId, err := service.MyService.Docker().DockerContainerCopyCreate(inspect)
if err != nil {
service.MyService.Docker().DockerContainerUpdateName(inspect.Name, id)
service.MyService.Docker().DockerContainerStart(id)
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.ERROR, Message: oasis_err2.GetMsg(oasis_err2.ERROR)})
return
}
//step启动容器
err = service.MyService.Docker().DockerContainerStart(containerId)
if err != nil {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.ERROR, Message: oasis_err2.GetMsg(oasis_err2.ERROR)})
return
}
service.MyService.Docker().DockerContainerRemove(id, true)
delete(service.NewVersionApp, id)
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
}
// @Summary get app index
// @Produce application/json
// @Accept application/json
// @Tags app
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /app/order [get]
func GetAppOrder(c *gin.Context) {
data := service.MyService.System().GetAppOrderFile()
c.JSON(http.StatusOK, model.Result{Success: oasis_err.SUCCESS, Message: oasis_err.GetMsg(oasis_err.SUCCESS), Data: json.RawMessage(data)})
}
// @Summary update app index
// @Produce application/json
// @Accept application/json
// @Tags app
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /app/order [post]
func PostAppOrder(c *gin.Context) {
data := c.PostForm("data")
service.MyService.System().UpAppOrderFile(data)
c.JSON(http.StatusOK,
model.Result{
Success: oasis_err.SUCCESS,
Message: oasis_err.GetMsg(oasis_err.SUCCESS),
Data: json.RawMessage(data),
})
}
// @Summary 获取容器详情
// @Produce application/json
// @Accept application/json
@@ -1087,7 +1110,7 @@ func ContainerInfo(c *gin.Context) {
data := make(map[string]interface{}, 5)
data["app"] = appInfo
data["cpu"] = cpuModel
data["memory"] = service.MyService.ZiMa().GetMemInfo().Total
data["memory"] = service.MyService.System().GetMemInfo().Total
data["container"] = json2.RawMessage(containerInfo)
data["info"] = con
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: data})
@@ -1110,7 +1133,7 @@ func GetDockerInstallConfig(c *gin.Context) {
}
}
data["networks"] = list
data["memory"] = service.MyService.ZiMa().GetMemInfo()
data["memory"] = service.MyService.System().GetMemInfo()
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: data})
}
@@ -1138,50 +1161,103 @@ func ContainerRelyInfo(c *gin.Context) {
// @Router /app/update/{id}/info [get]
func ContainerUpdateInfo(c *gin.Context) {
appId := c.Param("id")
appInfo := service.MyService.App().GetAppDBInfo(appId)
//appInfo := service.MyService.App().GetAppDBInfo(appId)
info, err := service.MyService.Docker().DockerContainerInfo(appId)
if err != nil {
//todo 需要自定义错误
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: err.Error()})
return
}
var port model.PortArray
json2.Unmarshal([]byte(appInfo.Ports), &port)
// json2.Unmarshal([]byte(appInfo.Ports), &port)
var envs model.EnvArray
json2.Unmarshal([]byte(appInfo.Envs), &envs)
for k, v := range info.HostConfig.PortBindings {
temp := model.PortMap{
CommendPort: v[0].HostPort,
ContainerPort: k.Port(),
var vol model.PathArray
json2.Unmarshal([]byte(appInfo.Volumes), &vol)
for i := 0; i < len(vol); i++ {
vol[i].Path = strings.ReplaceAll(vol[i].Path, "$AppID", appId)
Protocol: k.Proto(),
}
port = append(port, temp)
}
var dir model.PathArray
json2.Unmarshal([]byte(appInfo.Devices), &dir)
var envs model.EnvArray
// json2.Unmarshal([]byte(appInfo.Envs), &envs)
for _, v := range info.Config.Env {
showENV := info.Config.Labels["show_env"]
showENVList := strings.Split(showENV, ",")
showENVMap := make(map[string]string)
for _, name := range showENVList {
showENVMap[name] = "1"
}
if _, ok := showENVMap[v]; ok {
temp := model.Env{
Name: strings.Split(v, "=")[0],
Value: strings.Split(v, "=")[1],
}
envs = append(envs, temp)
}
}
var vol model.PathArray
// json2.Unmarshal([]byte(appInfo.Volumes), &vol)
for i := 0; i < len(info.Mounts); i++ {
temp := model.PathMap{
Path: strings.ReplaceAll(info.Mounts[i].Source, "$AppID", info.Name),
ContainerPath: info.Mounts[i].Destination,
}
vol = append(vol, temp)
}
var driver model.PathArray
//volumesStr, _ := json2.Marshal(m.Volumes)
//devicesStr, _ := json2.Marshal(m.Devices)
for _, v := range info.HostConfig.Resources.Devices {
temp := model.PathMap{
Path: v.PathOnHost,
ContainerPath: v.PathInContainer,
}
driver = append(driver, temp)
}
m := model.CustomizationPostData{}
m.Index = appInfo.Index
m.Icon = appInfo.Icon
m.Icon = info.Config.Labels["icon"]
m.Ports = port
m.Image = appInfo.Image + ":" + appInfo.Version
m.Origin = appInfo.Origin
m.NetworkModel = appInfo.NetModel
m.Description = appInfo.Description
m.Label = appInfo.Label
m.PortMap = appInfo.PortMap
m.Devices = dir //appInfo.Devices
m.Image = info.Config.Image
m.Origin = info.Config.Labels["origin"]
if len(m.Origin) == 0 {
m.Origin = "local"
}
m.NetworkModel = string(info.HostConfig.NetworkMode)
m.Description = info.Config.Labels["desc"]
m.Label = strings.ReplaceAll(info.Name, "/", "")
m.PortMap = info.Config.Labels["web"]
m.Devices = driver
m.Envs = envs
m.Memory = info.HostConfig.Memory >> 20
m.CpuShares = info.HostConfig.CPUShares
m.Volumes = vol //appInfo.Volumes
m.Restart = info.HostConfig.RestartPolicy.Name
m.EnableUPNP = appInfo.EnableUPNP
m.Position = appInfo.Position
m.EnableUPNP = false
m.Index = info.Config.Labels["index"]
m.Position = false
m.CustomId = info.Config.Labels["custom_id"]
m.Host = info.Config.Labels["host"]
if len(m.CustomId) == 0 {
m.CustomId = uuid.NewV4().String()
}
m.CapAdd = info.HostConfig.CapAdd
m.Cmd = info.Config.Cmd
m.HostName = info.Config.Hostname
m.Privileged = info.HostConfig.Privileged
m.Protocol = info.Config.Labels["protocol"]
if m.Protocol == "" {
m.Protocol = "http"
}
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: m})
}

View File

@@ -6,17 +6,25 @@ import (
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"net/url"
url2 "net/url"
"os"
"path"
"path/filepath"
"time"
"strconv"
"strings"
"sync"
"github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/pkg/config"
"github.com/IceWhaleTech/CasaOS/pkg/utils/file"
"github.com/IceWhaleTech/CasaOS/pkg/utils/oasis_err"
oasis_err2 "github.com/IceWhaleTech/CasaOS/pkg/utils/oasis_err"
"github.com/IceWhaleTech/CasaOS/service"
"github.com/gin-gonic/gin"
uuid "github.com/satori/go.uuid"
)
func downloadReadFile(c *gin.Context) {
@@ -45,24 +53,6 @@ func downloadReadFile(c *gin.Context) {
}
}
func downloadWriteFile(c *gin.Context) {
//写文件
var filename = "./output1.csv"
file, err := os.Create(filename) //创建文件
if err != nil {
c.String(400, err.Error())
return
}
buf := bufio.NewWriter(file) //创建新的 Writer 对象
buf.WriteString("test")
buf.Flush()
defer file.Close()
//返回文件流
c.File(filename)
}
// @Summary 读取文件
// @Produce application/json
// @Accept application/json
@@ -127,45 +117,112 @@ func GetLocalFile(c *gin.Context) {
return
}
// @Summary 下载文件
// @Summary download
// @Produce application/json
// @Accept application/json
// @Tags file
// @Security ApiKeyAuth
// @Param path query string true "路径"
// @Param t query string false "Compression format" Enums(zip,tar,targz)
// @Param files query string true "file list eg: filename1,filename2,filename3 "
// @Success 200 {string} string "ok"
// @Router /file/download [get]
func GetDownloadFile(c *gin.Context) {
filePath := c.Query("path")
if len(filePath) == 0 {
t := c.Query("t")
files := c.Query("files")
if len(files) == 0 {
c.JSON(http.StatusOK, model.Result{
Success: oasis_err2.INVALID_PARAMS,
Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS),
})
return
}
if !file.Exists(filePath) {
list := strings.Split(files, ",")
for _, v := range list {
if !file.Exists(v) {
c.JSON(http.StatusOK, model.Result{
Success: oasis_err2.FILE_DOES_NOT_EXIST,
Message: oasis_err2.GetMsg(oasis_err2.FILE_DOES_NOT_EXIST),
})
return
}
}
c.Header("Content-Type", "application/octet-stream")
c.Header("Content-Transfer-Encoding", "binary")
c.Header("Cache-Control", "no-cache")
// handles only single files not folders and multiple files
if len(list) == 1 {
filePath := list[0]
info, err := os.Stat(filePath)
if err != nil {
c.JSON(http.StatusOK, model.Result{
Success: oasis_err2.FILE_DOES_NOT_EXIST,
Message: oasis_err2.GetMsg(oasis_err2.FILE_DOES_NOT_EXIST),
})
return
}
if !info.IsDir() {
//打开文件
fileTmp, _ := os.Open(filePath)
defer fileTmp.Close()
//获取文件的名称
fileName := path.Base(filePath)
c.Header("Content-Type", "application/octet-stream")
c.Header("Content-Disposition", "attachment; filename="+fileName)
c.Header("Content-Transfer-Encoding", "binary")
c.Header("Cache-Control", "no-cache")
c.Header("Content-Type", "application/octet-stream")
c.Header("Content-Disposition", "attachment; filename="+fileName)
c.Header("Content-Transfer-Encoding", "binary")
c.Header("Content-Disposition", "attachment; filename*=utf-8''"+url2.PathEscape(fileName))
c.File(filePath)
return
}
}
extension, ar, err := file.GetCompressionAlgorithm(t)
if err != nil {
c.JSON(http.StatusOK, model.Result{
Success: oasis_err2.INVALID_PARAMS,
Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS),
})
return
}
err = ar.Create(c.Writer)
if err != nil {
c.JSON(http.StatusOK, model.Result{
Success: oasis_err.ERROR,
Message: oasis_err2.GetMsg(oasis_err2.ERROR),
Data: err.Error(),
})
return
}
defer ar.Close()
commonDir := file.CommonPrefix(filepath.Separator, list...)
currentPath := filepath.Base(commonDir)
name := "_" + currentPath
name += extension
c.Header("Content-Disposition", "attachment; filename*=utf-8''"+url.PathEscape(name))
for _, fname := range list {
err = file.AddFile(ar, fname, commonDir)
if err != nil {
log.Printf("Failed to archive %s: %v", fname, err)
}
}
}
func GetDownloadSingleFile(c *gin.Context) {
filePath := c.Param("path")
fileTmp, _ := os.Open(filePath)
defer fileTmp.Close()
fileName := path.Base(filePath)
//c.Header("Content-Disposition", "inline")
c.Header("Content-Disposition", "attachment; filename*=utf-8''"+url2.PathEscape(fileName))
c.File(filePath)
}
// @Summary 获取目录列表
@@ -177,145 +234,418 @@ func GetDownloadFile(c *gin.Context) {
// @Success 200 {string} string "ok"
// @Router /file/dirpath [get]
func DirPath(c *gin.Context) {
path := c.DefaultQuery("path", "/")
path := c.DefaultQuery("path", "")
info := service.MyService.ZiMa().GetDirPath(path)
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: info})
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"
}
}
} else if path == "/DATA" {
disk := make(map[string]string)
lsblk := service.MyService.Disk().LSBLK(true)
for _, v := range lsblk {
if len(v.Children) > 0 {
t := v.Tran
for _, c := range v.Children {
if len(c.Children) > 0 {
for _, gc := range c.Children {
if len(gc.MountPoint) > 0 {
disk[gc.MountPoint] = t
}
}
}
if len(c.MountPoint) > 0 {
disk[c.MountPoint] = t
}
}
}
}
for i := 0; i < len(info); i++ {
if v, ok := disk[info[i].Path]; ok {
info[i].Type = v
}
}
}
//Hide the files or folders in operation
fileQueue := make(map[string]string)
for _, v := range service.OpStrArr {
v, ok := service.FileQueue.Load(v)
if !ok {
continue
}
vt := v.(model.FileOperate)
for _, i := range vt.Item {
lastPath := i.From[strings.LastIndex(i.From, "/")+1:]
fileQueue[vt.To+"/"+lastPath] = i.From
}
}
pathList := []model.Path{}
for i := 0; i < len(info); i++ {
if _, ok := fileQueue[info[i].Path]; !ok {
pathList = append(pathList, info[i])
}
}
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: pathList})
}
// @Summary 重命名目录或文件
// @Summary rename file or dir
// @Produce application/json
// @Accept application/json
// @Tags file
// @Security ApiKeyAuth
// @Param oldpath formData string true "旧的路径"
// @Param newpath formData string true "新路径"
// @Param oldpath body string true "path of old"
// @Param newpath body string true "path of new"
// @Success 200 {string} string "ok"
// @Router /file/rename [put]
func RenamePath(c *gin.Context) {
op := c.PostForm("oldpath")
np := c.PostForm("newpath")
json := make(map[string]string)
c.BindJSON(&json)
op := json["oldpath"]
np := json["newpath"]
if len(op) == 0 || len(np) == 0 {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
return
}
service.MyService.ZiMa().RenameFile(op, np)
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
success, err := service.MyService.ZiMa().RenameFile(op, np)
c.JSON(http.StatusOK, model.Result{Success: success, Message: oasis_err2.GetMsg(success), Data: err})
}
// @Summary 创建文件夹
// @Summary create folder
// @Produce application/json
// @Accept multipart/form-data
// @Accept application/json
// @Tags file
// @Security ApiKeyAuth
// @Param path formData string false "路径"
// @Param path body string true "path of folder"
// @Success 200 {string} string "ok"
// @Router /file/mkdir [post]
func MkdirAll(c *gin.Context) {
path := c.PostForm("path")
json := make(map[string]string)
c.BindJSON(&json)
path := json["path"]
var code int
if len(path) == 0 {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
return
}
// decodedPath, err := url.QueryUnescape(path)
// if err != nil {
// c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
// return
// }
code, _ = service.MyService.ZiMa().MkdirAll(path)
c.JSON(http.StatusOK, model.Result{Success: code, Message: oasis_err2.GetMsg(code)})
}
// @Summary 创建文件
// @Summary create file
// @Produce application/json
// @Accept multipart/form-data
// @Accept application/json
// @Tags file
// @Security ApiKeyAuth
// @Param path formData string false "路径"
// @Param path body string true "path of folder (path need to url encode)"
// @Success 200 {string} string "ok"
// @Router /file/create [post]
func PostCreateFile(c *gin.Context) {
path := c.PostForm("path")
json := make(map[string]string)
c.BindJSON(&json)
path := json["path"]
var code int
if len(path) == 0 {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
return
}
// decodedPath, err := url.QueryUnescape(path)
// if err != nil {
// c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
// return
// }
code, _ = service.MyService.ZiMa().CreateFile(path)
c.JSON(http.StatusOK, model.Result{Success: code, Message: oasis_err2.GetMsg(code)})
}
// @Summary 上传文件
// @Summary upload file
// @Produce application/json
// @Accept application/json
// @Tags file
// @Security ApiKeyAuth
// @Param path formData string false "file path"
// @Param file formData file true "file"
// @Success 200 {string} string "ok"
// @Router /file/upload [get]
func GetFileUpload(c *gin.Context) {
relative := c.Query("relativePath")
fileName := c.Query("filename")
chunkNumber := c.Query("chunkNumber")
totalChunks, _ := strconv.Atoi(c.DefaultQuery("totalChunks", "0"))
path := c.Query("path")
dirPath := ""
hash := file.GetHashByContent([]byte(fileName))
tempDir := config.AppInfo.RootPath + "/temp/" + hash + strconv.Itoa(totalChunks) + "/"
if fileName != relative {
dirPath = strings.TrimSuffix(relative, fileName)
tempDir += dirPath
file.MkDir(path + "/" + dirPath)
}
tempDir += chunkNumber
if !file.CheckNotExist(tempDir) {
c.JSON(200, model.Result{Success: 200, Message: oasis_err2.GetMsg(oasis_err2.FILE_ALREADY_EXISTS)})
return
}
c.JSON(204, model.Result{Success: 204, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
}
// @Summary upload file
// @Produce application/json
// @Accept multipart/form-data
// @Tags file
// @Security ApiKeyAuth
// @Param path formData string false "路径"
// @Param path formData string false "file path"
// @Param file formData file true "file"
// @Success 200 {string} string "ok"
// @Router /file/mkdir [post]
// @Router /file/upload [post]
func PostFileUpload(c *gin.Context) {
file, _, _ := c.Request.FormFile("file")
//file.Read()
path := c.Query("path")
//上传文件
f, _, _ := c.Request.FormFile("file")
relative := c.PostForm("relativePath")
fileName := c.PostForm("filename")
totalChunks, _ := strconv.Atoi(c.DefaultPostForm("totalChunks", "0"))
chunkNumber := c.PostForm("chunkNumber")
dirPath := ""
path := c.PostForm("path")
hash := file.GetHashByContent([]byte(fileName))
if len(path) == 0 {
c.JSON(oasis_err2.INVALID_PARAMS, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
return
}
tempDir := config.AppInfo.RootPath + "/temp/" + hash + strconv.Itoa(totalChunks) + "/"
if fileName != relative {
dirPath = strings.TrimSuffix(relative, fileName)
tempDir += dirPath
file.MkDir(path + "/" + dirPath)
}
path += "/" + relative
if !file.CheckNotExist(tempDir + chunkNumber) {
file.RMDir(tempDir + chunkNumber)
}
if totalChunks > 1 {
file.IsNotExistMkDir(tempDir)
out, _ := os.OpenFile(tempDir+chunkNumber, os.O_WRONLY|os.O_CREATE, 0644)
defer out.Close()
_, err := io.Copy(out, f)
if err != nil {
c.JSON(oasis_err2.ERROR, model.Result{Success: oasis_err2.ERROR, Message: oasis_err2.GetMsg(oasis_err2.ERROR), Data: err.Error()})
return
}
} else {
out, _ := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0644)
defer out.Close()
io.Copy(out, file)
_, err := io.Copy(out, f)
if err != nil {
c.JSON(oasis_err2.ERROR, model.Result{Success: oasis_err2.ERROR, Message: oasis_err2.GetMsg(oasis_err2.ERROR), Data: err.Error()})
return
}
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
return
}
fileNum, err := ioutil.ReadDir(tempDir)
if err != nil {
c.JSON(oasis_err2.ERROR, model.Result{Success: oasis_err2.ERROR, Message: oasis_err2.GetMsg(oasis_err2.ERROR), Data: err.Error()})
return
}
if totalChunks == len(fileNum) {
file.SpliceFiles(tempDir, path, totalChunks, 1)
file.RMDir(tempDir)
}
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
}
func PutFileMove(c *gin.Context) {
from := "/Users/liangjianli/go/CasaOS"
to := "/Users/liangjianli/go/CasaOS/test"
//t := 1 //是否覆盖
// @Summary copy or move file
// @Produce application/json
// @Accept application/json
// @Tags file
// @Security ApiKeyAuth
// @Param body body model.FileOperate true "type:move,copy"
// @Success 200 {string} string "ok"
// @Router /file/operate [post]
func PostOperateFileOrDir(c *gin.Context) {
//方法体
stopCh := make(chan int)
f, err := os.Stat(from)
list := model.FileOperate{}
c.BindJSON(&list)
if len(list.Item) == 0 {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
return
}
var total int64 = 0
for i := 0; i < len(list.Item); i++ {
size, err := file.GetFileOrDirSize(list.Item[i].From)
if err != nil {
//未拿到文件信息
fmt.Println("stat", err)
continue
}
//未创建新的文件夹
if f.IsDir() {
//from 是文件夹,定义to也是文件夹
if list, err := ioutil.ReadDir(from); err == nil {
for _, v := range list {
time.Sleep(time.Second)
if err = Copy(stopCh, filepath.Join(from, v.Name()), filepath.Join(to, v.Name())); err != nil {
fmt.Printf("copy %s ,err %d", v.Name(), err)
list.Item[i].Size = size
total += size
}
list.TotalSize = total
list.ProcessedSize = 0
uid := uuid.NewV4().String()
service.FileQueue.Store(uid, list)
service.OpStrArr = append(service.OpStrArr, uid)
if len(service.OpStrArr) == 1 {
go service.ExecOpFile()
go service.CheckFileStatus()
go service.MyService.Notify().SendFileOperateNotify(false)
}
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
}
// @Summary delete file
// @Produce application/json
// @Accept application/json
// @Tags file
// @Security ApiKeyAuth
// @Param body body string true "paths eg ["/a/b/c","/d/e/f"]"
// @Success 200 {string} string "ok"
// @Router /file/delete [delete]
func DeleteFile(c *gin.Context) {
paths := []string{}
c.BindJSON(&paths)
if len(paths) == 0 {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
return
}
// path := c.Query("path")
// paths := strings.Split(path, ",")
for _, v := range paths {
err := os.RemoveAll(v)
if err != nil {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.FILE_DELETE_ERROR, Message: oasis_err2.GetMsg(oasis_err2.FILE_DELETE_ERROR), Data: err})
return
}
}
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
}
// @Summary update file
// @Produce application/json
// @Accept application/json
// @Tags file
// @Security ApiKeyAuth
// @Param path body string true "path"
// @Param content body string true "content"
// @Success 200 {string} string "ok"
// @Router /file/update [put]
func PutFileContent(c *gin.Context) {
fi := model.FileUpdate{}
c.BindJSON(&fi)
// path := c.PostForm("path")
// content := c.PostForm("content")
if !file.Exists(fi.FilePath) {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.FILE_ALREADY_EXISTS, Message: oasis_err2.GetMsg(oasis_err2.FILE_ALREADY_EXISTS)})
return
}
//err := os.Remove(path)
err := os.RemoveAll(fi.FilePath)
if err != nil {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.FILE_DELETE_ERROR, Message: oasis_err2.GetMsg(oasis_err2.FILE_DELETE_ERROR), Data: err})
return
}
err = file.CreateFileAndWriteContent(fi.FilePath, fi.FileContent)
if err != nil {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.ERROR, Message: oasis_err2.GetMsg(oasis_err2.ERROR), Data: err})
return
}
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
}
// @Summary image thumbnail/original image
// @Produce application/json
// @Accept application/json
// @Tags file
// @Security ApiKeyAuth
// @Param path query string true "path"
// @Param type query string false "original,thumbnail" Enums(original,thumbnail)
// @Success 200 {string} string "ok"
// @Router /file/image [get]
func GetFileImage(c *gin.Context) {
t := c.Query("type")
path := c.Query("path")
if !file.Exists(path) {
c.JSON(http.StatusInternalServerError, model.Result{Success: oasis_err2.FILE_ALREADY_EXISTS, Message: oasis_err2.GetMsg(oasis_err2.FILE_ALREADY_EXISTS)})
return
}
if t == "thumbnail" {
f, err := file.GetImage(path, 100, 0)
if err != nil {
c.JSON(http.StatusInternalServerError, model.Result{Success: oasis_err2.ERROR, Message: oasis_err2.GetMsg(oasis_err2.ERROR), Data: err.Error()})
return
}
c.Writer.WriteString(string(f))
return
}
f, err := os.Open(path)
if err != nil {
c.JSON(http.StatusInternalServerError, model.Result{Success: oasis_err2.ERROR, Message: oasis_err2.GetMsg(oasis_err2.ERROR), Data: err.Error()})
return
}
defer f.Close()
data, err := ioutil.ReadAll(f)
if err != nil {
c.JSON(http.StatusInternalServerError, model.Result{Success: oasis_err2.ERROR, Message: oasis_err2.GetMsg(oasis_err2.ERROR), Data: err.Error()})
return
}
c.Writer.WriteString(string(data))
}
func DeleteOperateFileOrDir(c *gin.Context) {
id := c.Param("id")
if id == "0" {
service.FileQueue = sync.Map{}
service.OpStrArr = []string{}
} else {
p := filepath.Dir(to)
if _, err = os.Stat(p); err != nil {
if err = os.MkdirAll(p, 0777); err != nil {
fmt.Println("mkdir", err)
service.FileQueue.Delete(id)
tempList := []string{}
for _, v := range service.OpStrArr {
if v != id {
tempList = append(tempList, v)
}
}
service.OpStrArr = tempList
}
file, err := os.Open(from)
if err != nil {
fmt.Println("open file error ", err)
}
defer file.Close()
out, err := os.Create(to)
if err != nil {
fmt.Println("create to file err", err)
}
defer out.Close()
io.Copy(out, file)
time.Sleep(time.Second * 4)
close(stopCh)
}
func Copy(stop chan int, from, to string) error {
for {
select {
case <-stop:
return nil
default:
fmt.Println(from)
}
}
return nil
go service.MyService.Notify().SendFileOperateNotify(true)
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
}

View File

@@ -1,70 +1,5 @@
package v1
import (
json2 "encoding/json"
"github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/pkg/utils/oasis_err"
"github.com/IceWhaleTech/CasaOS/service"
"github.com/IceWhaleTech/CasaOS/types"
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
"net/http"
"time"
)
func aaa() {
var upGrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true
},
}
// @Summary websocket 接口,连接成功后发送一个"notify"字符串
// @Produce application/json
// @Accept application/json
// @Tags notify
// @Security ApiKeyAuth
// @Param token path string true "token"
// @Success 200 {string} string "ok"
// @Router /notify/ws [get]
func NotifyWS(c *gin.Context) {
//升级get请求为webSocket协议
ws, err := upGrader.Upgrade(c.Writer, c.Request, nil)
if err != nil {
return
}
defer ws.Close()
for {
mt, message, err := ws.ReadMessage()
if err != nil {
break
}
if string(message) != "notify" {
return
}
for {
list := service.MyService.Notify().GetList()
json, _ := json2.Marshal(list)
err = ws.WriteMessage(mt, json)
if err != nil {
break
}
time.Sleep(time.Second * 2)
}
}
}
// @Summary 标记notify已读
// @Produce application/json
// @Accept application/json
// @Tags notify
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /notify/read/{id} [put]
func PutNotifyRead(c *gin.Context) {
id := c.Param("id")
if len(id) == 0 {
c.JSON(http.StatusOK, model.Result{Success: oasis_err.INVALID_PARAMS, Message: oasis_err.GetMsg(oasis_err.INVALID_PARAMS)})
return
}
service.MyService.Notify().MarkRead(id, types.NOTIFY_READ)
}

62
route/v1/notify_old.go Normal file
View File

@@ -0,0 +1,62 @@
package v1
import (
"fmt"
"net/http"
"github.com/IceWhaleTech/CasaOS/service"
"github.com/IceWhaleTech/CasaOS/types"
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
)
var upGrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true
},
}
// @Summary websocket 接口,连接成功后发送一个"notify"字符串
// @Produce application/json
// @Accept application/json
// @Tags notify
// @Security ApiKeyAuth
// @Param token path string true "token"
// @Success 200 {string} string "ok"
// @Router /notify/ws [get]
func NotifyWS(c *gin.Context) {
//升级get请求为webSocket协议
ws, err := upGrader.Upgrade(c.Writer, c.Request, nil)
if err != nil {
return
}
defer ws.Close()
service.WebSocketConns = append(service.WebSocketConns, ws)
if !service.SocketRun {
service.SocketRun = true
service.SendMeg()
}
for {
mt, message, err := ws.ReadMessage()
fmt.Println(mt, message, err)
}
}
// @Summary 标记notify已读
// @Produce application/json
// @Accept application/json
// @Tags notify
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /notify/read/{id} [put]
func PutNotifyRead(c *gin.Context) {
id := c.Param("id")
// if len(id) == 0 {
// c.JSON(http.StatusOK, model.Result{Success: oasis_err.INVALID_PARAMS, Message: oasis_err.GetMsg(oasis_err.INVALID_PARAMS)})
// return
// }
fmt.Println(id)
service.MyService.Notify().MarkRead(id, types.NOTIFY_READ)
}

803
route/v1/person.go Normal file
View File

@@ -0,0 +1,803 @@
package v1
import (
"bytes"
"encoding/base64"
"encoding/gob"
"encoding/json"
"io/ioutil"
"net"
"net/http"
"os"
"reflect"
"strconv"
"strings"
"time"
natType "github.com/Curtis-Milo/nat-type-identifier-go"
"github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/pkg/config"
"github.com/IceWhaleTech/CasaOS/pkg/utils/file"
"github.com/IceWhaleTech/CasaOS/pkg/utils/ip_helper"
oasis_err2 "github.com/IceWhaleTech/CasaOS/pkg/utils/oasis_err"
"github.com/IceWhaleTech/CasaOS/service"
model2 "github.com/IceWhaleTech/CasaOS/service/model"
"github.com/IceWhaleTech/CasaOS/types"
"github.com/gin-gonic/gin"
uuid "github.com/satori/go.uuid"
)
// @Summary Retry the file that failed to download
// @Produce application/json
// @Accept application/json
// @Tags person
// @Param uui path string true "download uuid"
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /person/refile/{uuid} [get]
func GetPersonReFile(c *gin.Context) {
uid := c.Param("uuid")
_, err := uuid.FromString(uid)
if err != nil {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
return
}
task := service.MyService.Download().GetDownloadById(uid)
if reflect.DeepEqual(task, model2.PersonDownloadDBModel{}) {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.PERSON_REMOTE_ERROR, Message: oasis_err2.GetMsg(oasis_err2.PERSON_REMOTE_ERROR)})
return
}
token := task.From
if _, ok := service.UDPAddressMap[token]; !ok {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.PERSON_REMOTE_ERROR, Message: oasis_err2.GetMsg(oasis_err2.PERSON_REMOTE_ERROR)})
return
}
m := model.MessageModel{}
m.Data = task.Path
m.From = config.ServerInfo.Token
m.To = token
m.Type = types.PERSONDOWNLOAD
m.UUId = uid
go service.Dial(m, false)
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
}
// @Summary download file
// @Produce application/json
// @Accept application/json
// @Tags person
// @Param share_id query string true "opponent share_id"
// @Param path query string true "file path"
// @Param file_name query string true "file name"
// @Param local_path query string true "local_path"
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /person/file [get]
func GetPersonFile(c *gin.Context) {
path := c.Query("path")
localPath := c.Query("local_path")
token := c.Query("share_id")
fileName := c.Query("file_name")
_, err := uuid.FromString(token)
if len(path) == 0 || err != nil || len(localPath) == 0 || len(fileName) == 0 {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
return
}
if file.CheckNotExist(localPath) {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.DIR_NOT_EXISTS, Message: oasis_err2.GetMsg(oasis_err2.DIR_NOT_EXISTS)})
return
}
if _, ok := service.UDPAddressMap[token]; !ok {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.PERSON_REMOTE_ERROR, Message: oasis_err2.GetMsg(oasis_err2.PERSON_REMOTE_ERROR)})
return
}
if _, ok := service.UDPAddressMap[token]; !ok {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.PERSON_REMOTE_ERROR, Message: oasis_err2.GetMsg(oasis_err2.PERSON_REMOTE_ERROR)})
return
}
// task id
uuid := uuid.NewV4().String()
task := model2.PersonDownloadDBModel{}
task.UUID = uuid
task.Name = fileName
task.Length = 0
task.From = token
task.Path = path
task.Size = 0
task.State = types.DOWNLOADAWAIT
task.Created = time.Now().Unix()
task.Type = types.PERSONFILEDOWNLOAD
task.LocalPath = localPath
if service.MyService.Download().GetDownloadListByPath(task) > 0 {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.PERSON_EXIST_DOWNLOAD, Message: oasis_err2.GetMsg(oasis_err2.PERSON_EXIST_DOWNLOAD)})
return
}
service.MyService.Download().AddDownloadTask(task)
m := model.MessageModel{}
m.Data = path
m.From = config.ServerInfo.Token
m.To = token
m.Type = types.PERSONDOWNLOAD
m.UUId = uuid
go service.Dial(m, false)
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
}
// @Summary delete download file records
// @Produce application/json
// @Accept application/json
// @Tags person
// @Param uuid path string true "download uuid"
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /person/file/{uuid} [delete]
func DeletePersonDownloadFile(c *gin.Context) {
id := c.Param("uuid")
_, err := uuid.FromString(id)
if err != nil {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
return
}
task := service.MyService.Download().GetDownloadById(id)
if task.State == types.DOWNLOADING {
m := model.MessageModel{}
m.Data = ""
m.From = config.ServerInfo.Token
m.To = task.From
m.Type = types.PERSONCANCEL
m.UUId = task.UUID
service.CancelList[task.UUID] = task.UUID
service.Dial(m, false)
}
service.MyService.Download().DelDownload(id)
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
}
// @Summary Get file download list
// @Produce application/json
// @Accept application/json
// @Tags person
// @Param state query int false "wait:0,downloading:1,pause:2,finish:3,error:4,finished:5" Enums(0,1,2,3,4,5)
// @Security ApiKeyAuth
// @Success 200 {object} []model2.PersonDownloadDBModel
// @Router /person/list [get]
func GetPersonDownloadList(c *gin.Context) {
state := c.DefaultQuery("state", "")
list := service.MyService.Download().GetDownloadListByState(state, types.PERSONFILEDOWNLOAD)
//if it is downloading, it need to add 'already'
for i := 0; i < len(list); i++ {
if list[i].State == types.DOWNLOADING {
tempDir := config.AppInfo.RootPath + "/temp" + "/" + list[i].UUID
files, err := ioutil.ReadDir(tempDir)
if err == nil {
list[i].Already = len(files)
}
}
list[i].Duration = time.Now().Unix() - list[i].Created
}
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: list})
}
// @Summary edit friend's remarks
// @Produce application/json
// @Accept application/json
// @Tags person
// @Param remarks formData string true "remarks name"
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /person/remarks/{shareid} [put]
func PutPersonRemarks(c *gin.Context) {
token := c.Param("shareid")
_, err := uuid.FromString(token)
mark := c.PostForm("remarks")
if err != nil || len(mark) == 0 {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
return
}
friend := model2.FriendModel{}
friend.Token = token
friend.Mark = mark
service.MyService.Friend().EditFriendMark(friend)
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
}
// @Summary edit friend's
// @Produce application/json
// @Accept application/json
// @Tags person
// @Param write formData bool true "write"
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /person/write/{shareid} [put]
func PutPersonWrite(c *gin.Context) {
token := c.Param("shareid")
_, err := uuid.FromString(token)
if err != nil {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
return
}
write, _ := strconv.ParseBool(c.PostForm("write"))
friend := model2.FriendModel{}
friend.Token = token
friend.Write = write
service.MyService.Friend().EditFriendMark(friend)
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
}
// @Summary image thumbnail
// @Produce application/json
// @Accept application/json
// @Tags person
// @Param write formData bool true "write"
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /person/image/thumbnail/{shareid} [get]
func GetPersonImageThumbnail(c *gin.Context) {
token := c.Param("shareid")
path := c.Query("path")
_, err := uuid.FromString(token)
if err != nil || len(path) == 0 {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
return
}
uuid := uuid.NewV4().String()
m := model.MessageModel{}
m.Data = path
m.From = config.ServerInfo.Token
m.To = token
m.Type = types.PERSONIMAGETHUMBNAIL
m.UUId = uuid
img, err := service.Dial(m, false)
if err != nil {
c.JSON(http.StatusInternalServerError, model.Result{Success: oasis_err2.ERROR, Message: oasis_err2.GetMsg(oasis_err2.ERROR), Data: err.Error()})
return
}
// var buf bytes.Buffer
//err = gob.NewEncoder(&buf).Encode(img.Data)
if err != nil {
c.JSON(http.StatusInternalServerError, model.Result{Success: oasis_err2.ERROR, Message: oasis_err2.GetMsg(oasis_err2.ERROR), Data: err.Error()})
return
}
var buf bytes.Buffer
err = gob.NewEncoder(&buf).Encode(img.Data)
if err != nil {
c.JSON(http.StatusInternalServerError, model.Result{Success: oasis_err2.ERROR, Message: oasis_err2.GetMsg(oasis_err2.ERROR), Data: err.Error()})
return
}
imageBuffer, _ := base64.StdEncoding.DecodeString(img.Data.(string))
c.Writer.WriteString(string(imageBuffer))
// c.String(http.StatusOK, "data:image/"+filesuffix+";base64,"+img.Data.(string))
//c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: img.Data.(string)})
}
// @Summary get my friend list
// @Produce application/json
// @Accept application/json
// @Tags person
// @Security ApiKeyAuth
// @Success 200 {object} []model2.FriendModel
// @Router /person/users [get]
func GetPersonFriend(c *gin.Context) {
list := service.MyService.Friend().GetFriendList()
for i := 0; i < len(list); i++ {
if v, ok := service.UDPAddressMap[list[i].Token]; ok && len(v) > 0 {
list[i].OnLine = true
if ip_helper.HasLocalIP(net.ParseIP(strings.Split(v, ":")[0])) {
list[i].LocalIP = strings.Split(v, ":")[0]
}
}
}
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: list})
}
// @Summary network type detection
// @Produce application/json
// @Accept application/json
// @Tags person
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /person/detection [get]
func GetPersonDetection(c *gin.Context) {
// - Blocked
// - Open Internet
// - Full Cone
// - Symmetric UDP Firewall
// - Restric NAT
// - Restric Port NAT
// - Symmetric NAT
result, err := natType.GetDeterminedNatType(true, 5, "stun.l.google.com")
if err != nil {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.ERROR, Message: oasis_err2.GetMsg(oasis_err2.ERROR), Data: err.Error()})
return
}
//result := service.MyService.Person().GetPersionNetWorkTypeDetection()
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: result})
}
// @Summary add friend
// @Produce application/json
// @Accept application/json
// @Tags person
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /person/user/{shareids} [post]
func PostAddPersonFriend(c *gin.Context) {
token := c.Param("shareids")
tokenList := strings.Split(token, ",")
for _, v := range tokenList {
_, err := uuid.FromString(v)
if err != nil {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
return
}
if v == config.ServerInfo.Token {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.PERSON_MYSELF, Message: oasis_err2.GetMsg(oasis_err2.PERSON_MYSELF)})
return
}
udb := service.MyService.Friend().GetFriendById(model2.FriendModel{Token: v})
if !reflect.DeepEqual(udb, model2.FriendModel{Token: v}) {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.PERSON_EXIST_FRIEND, Message: oasis_err2.GetMsg(oasis_err2.PERSON_EXIST_FRIEND)})
return
}
user := service.MyService.Casa().GetUserInfoByShareId(v)
if reflect.DeepEqual(user, model.UserInfo{}) {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.PERSON_NOT_EXIST_USER, Message: oasis_err2.GetMsg(oasis_err2.PERSON_NOT_EXIST_USER)})
return
}
message := model.MessageModel{}
message.Type = types.PERSONCONNECTION
message.Data = v
message.From = config.ServerInfo.Token
message.To = v
message.UUId = uuid.NewV4().String()
go service.Dial(message, true)
msg := model.MessageModel{}
msg.Type = types.PERSONGETIP
msg.Data = ""
msg.From = config.ServerInfo.Token
msg.To = v
msg.UUId = uuid.NewV4().String()
service.Dial(msg, true)
friend := model2.FriendModel{}
friend.Token = v
friend.Avatar = user.Avatar
friend.Block = false
friend.State = types.FRIENDSTATEWAIT
friend.NickName = user.NickName
friend.Profile = user.Desc
friend.Version = user.Version
service.MyService.Friend().AddFriend(friend)
}
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
}
// @Summary Get a list of directories
// @Produce application/json
// @Accept application/json
// @Tags person
// @Param share_id query string true "Opponent share_id"
// @Param path query string true "dir path"
// @Security ApiKeyAuth
// @Success 200 {object} []model.Path
// @Router /person/directory [get]
func GetPersonDirectory(c *gin.Context) {
path := c.Query("path")
token := c.Query("share_id")
_, err := uuid.FromString(token)
if len(path) == 0 || err != nil {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
return
}
if _, ok := service.UDPAddressMap[token]; !ok {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.PERSON_REMOTE_ERROR, Message: oasis_err2.GetMsg(oasis_err2.PERSON_REMOTE_ERROR)})
return
}
uuid := uuid.NewV4().String()
m := model.MessageModel{}
m.Data = path
m.From = config.ServerInfo.Token
m.To = token
m.Type = types.PERSONDIRECTORY
m.UUId = uuid
result, err := service.Dial(m, false)
if err != nil {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.ERROR, Message: oasis_err2.GetMsg(oasis_err2.ERROR), Data: err.Error()})
return
}
dataModel := []model.Path{}
if uuid == m.UUId {
dataModelByte, _ := json.Marshal(result.Data)
err := json.Unmarshal(dataModelByte, &dataModel)
if err != nil {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.ERROR, Message: oasis_err2.GetMsg(oasis_err2.ERROR), Data: err.Error()})
return
}
}
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: dataModel})
}
// @Summary Modify the download storage directory
// @Produce application/json
// @Accept multipart/form-data
// @Tags person
// @Security ApiKeyAuth
// @Param path formData string true "path"
// @Success 200 {string} string "ok"
// @Router /person/down/dir [post]
func PostPersonDownDir(c *gin.Context) {
downPath := c.PostForm("path")
if len(downPath) == 0 {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
return
}
if file.CheckNotExist(downPath) {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.DIR_NOT_EXISTS, Message: oasis_err2.GetMsg(oasis_err2.DIR_NOT_EXISTS)})
return
}
config.Cfg.Section("file").Key("DownloadDir").SetValue(downPath)
config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath)
config.FileSettingInfo.DownloadDir = downPath
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
}
// @Summary Get the download storage directory
// @Produce application/json
// @Accept application/json
// @Tags person
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /person/down/dir [get]
func GetPersonDownDir(c *gin.Context) {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: config.FileSettingInfo.DownloadDir})
}
// @Summary Modify the shared directory
// @Produce application/json
// @Accept multipart/form-data
// @Tags person
// @Security ApiKeyAuth
// @Param share formData string true "share"
// @Success 200 {string} string "ok"
// @Router /person/share [post]
func PostPersonShare(c *gin.Context) {
share := c.PostForm("share")
if len(share) == 0 {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
return
}
var list []string
json.Unmarshal([]byte(share), &list)
if len(list) == 0 {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
return
}
for _, v := range list {
if !file.Exists(v) {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.FILE_ALREADY_EXISTS, Message: oasis_err2.GetMsg(oasis_err2.FILE_ALREADY_EXISTS)})
return
}
}
config.Cfg.Section("file").Key("ShareDir").SetValue(strings.Join(list, "|"))
config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath)
config.FileSettingInfo.ShareDir = list
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
}
// @Summary Get the shared directory
// @Produce application/json
// @Accept application/json
// @Tags person
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /person/share [get]
func GetPersonShare(c *gin.Context) {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: config.FileSettingInfo.ShareDir})
}
// @Summary Get the shareid
// @Produce application/json
// @Accept application/json
// @Tags person
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /person/shareid [get]
func GetPersonShareId(c *gin.Context) {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: config.ServerInfo.Token})
}
// @Summary Modify disabled status
// @Produce application/json
// @Accept application/json
// @Tags person
// @Param block formData bool false "Disable or not,Default:false "
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /person/block/{shareid} [put]
func PutPersonBlock(c *gin.Context) {
token := c.Param("shareid")
_, err := uuid.FromString(token)
block, _ := strconv.ParseBool(c.PostForm("block"))
if err != nil {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
return
}
friend := model2.FriendModel{}
friend.Token = token
friend.Block = block
service.MyService.Friend().EditFriendBlock(friend)
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
}
// @Summary Delete my friend
// @Produce application/json
// @Accept application/json
// @Tags person
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /person/user/{shareid} [delete]
func DeletePersonFriend(c *gin.Context) {
token := c.Param("shareid")
_, err := uuid.FromString(token)
if err != nil {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
return
}
friend := model2.FriendModel{}
friend.Token = token
service.MyService.Friend().DeleteFriend(friend)
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
}
// @Summary Get public person
// @Produce application/json
// @Accept application/json
// @Tags person
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /person/public [get]
func GetPersonPublic(c *gin.Context) {
list := service.MyService.Casa().GetPersonPublic()
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: list})
}
// @Summary upload file to friend
// @Produce application/json
// @Accept application/json
// @Tags person
// @Security ApiKeyAuth
// @Param path formData string true "Destination path"
// @Param local_path formData string true "Full path of the file to be uploaded"
// @Success 200 {string} string "ok"
// @Router /person/file/{shareid} [post]
func PostPersonFile(c *gin.Context) {
token := c.Param("shareid")
_, err := uuid.FromString(token)
path := c.PostForm("path")
localPath := c.PostForm("local_path")
if err != nil {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
return
}
if !file.Exists(localPath) {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.FILE_DOES_NOT_EXIST, Message: oasis_err2.GetMsg(oasis_err2.FILE_DOES_NOT_EXIST)})
return
}
uuid := uuid.NewV4().String()
m := model.MessageModel{}
m.Data = path
m.From = config.ServerInfo.Token
m.To = token
m.Type = types.PERSONUPLOAD
m.UUId = uuid
go service.UDPSendData(m, localPath)
f, _ := os.Stat(localPath)
task := model2.PersonDownloadDBModel{}
task.UUID = uuid
task.Name = f.Name()
task.Length = 0
task.From = token
task.Path = path
task.Size = f.Size()
task.State = types.DOWNLOADFINISHED
task.Created = time.Now().Unix()
task.Type = types.PERSONFILEUPLOAD
task.LocalPath = localPath
if service.MyService.Download().GetDownloadListByPath(task) > 0 {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.PERSON_EXIST_DOWNLOAD, Message: oasis_err2.GetMsg(oasis_err2.PERSON_EXIST_DOWNLOAD)})
return
}
service.MyService.Download().AddDownloadTask(task)
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
}
// @Summary agree add friend
// @Produce application/json
// @Accept application/json
// @Tags person
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /person/friend/{shareid} [put]
func PutPersonAgreeFriend(c *gin.Context) {
token := c.Param("shareid")
_, err := uuid.FromString(token)
if err != nil {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
return
}
user := service.MyService.Friend().GetFriendById(model2.FriendModel{Token: token})
if user.State != types.FRIENDSTATEREQUEST {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.COMMAND_ERROR_INVALID_OPERATION, Message: oasis_err2.GetMsg(oasis_err2.COMMAND_ERROR_INVALID_OPERATION)})
return
}
service.MyService.Friend().AgreeFrined(user.Token)
uuid := uuid.NewV4().String()
m := model.MessageModel{}
m.Data = ""
m.From = config.ServerInfo.Token
m.To = token
m.Type = types.PERSONAGREEFRIEND
m.UUId = uuid
go service.Dial(m, true)
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
}
// // @Summary upload file
// // @Produce application/json
// // @Accept multipart/form-data
// // @Tags person
// // @Security ApiKeyAuth
// // @Param path formData string false "file path"
// // @Param file formData file true "file"
// // @Success 200 {string} string "ok"
// // @Router /person/upload/{shareid} [get]
// func GetPersonFileUpload(c *gin.Context) {
// token := c.Param("shareid")
// _, err := uuid.FromString(token)
// path := c.Query("path")
// if err != nil {
// c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
// return
// }
// relative := c.Query("relativePath")
// fileName := c.Query("filename")
// chunkNumber := c.Query("chunkNumber")
// totalChunks, _ := strconv.Atoi(c.DefaultQuery("totalChunks", "0"))
// dirPath := ""
// hash := file.GetHashByContent([]byte(fileName))
// tempDir := "/casaOS/temp/" + hash + strconv.Itoa(totalChunks) + "/"
// if fileName != relative {
// dirPath = strings.TrimSuffix(relative, fileName)
// tempDir += dirPath
// file.MkDir(path + "/" + dirPath)
// }
// tempDir += chunkNumber
// if !file.CheckNotExist(tempDir) {
// c.JSON(200, model.Result{Success: 200, Message: oasis_err2.GetMsg(oasis_err2.FILE_ALREADY_EXISTS)})
// return
// }
// c.JSON(204, model.Result{Success: 204, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
// }
// // @Summary upload file
// // @Produce application/json
// // @Accept multipart/form-data
// // @Tags person
// // @Security ApiKeyAuth
// // @Param path formData string false "file path"
// // @Param file formData file true "file"
// // @Success 200 {string} string "ok"
// // @Router /person/upload [post]
// func PostPersonFileUpload(c *gin.Context) {
// token := c.Param("shareid")
// _, err := uuid.FromString(token)
// if err != nil {
// c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
// return
// }
// f, _, _ := c.Request.FormFile("file")
// relative := c.PostForm("relativePath")
// fileName := c.PostForm("filename")
// totalChunks, _ := strconv.Atoi(c.DefaultPostForm("totalChunks", "0"))
// chunkNumber := c.PostForm("chunkNumber")
// dirPath := ""
// path := c.PostForm("path")
// hash := file.GetHashByContent([]byte(fileName))
// if len(path) == 0 {
// c.JSON(oasis_err2.INVALID_PARAMS, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
// return
// }
// tempDir := "/casaOS/temp/" + hash + strconv.Itoa(totalChunks) + "/"
// if fileName != relative {
// dirPath = strings.TrimSuffix(relative, fileName)
// tempDir += dirPath
// file.MkDir(path + "/" + dirPath)
// }
// path += "/" + relative
// if !file.CheckNotExist(tempDir + chunkNumber) {
// file.RMDir(tempDir + chunkNumber)
// }
// if totalChunks > 1 {
// file.IsNotExistMkDir(tempDir)
// out, _ := os.OpenFile(tempDir+chunkNumber, os.O_WRONLY|os.O_CREATE, 0644)
// defer out.Close()
// _, err := io.Copy(out, f)
// if err != nil {
// c.JSON(oasis_err2.ERROR, model.Result{Success: oasis_err2.ERROR, Message: oasis_err2.GetMsg(oasis_err2.ERROR), Data: err.Error()})
// return
// }
// } else {
// out, _ := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0644)
// defer out.Close()
// _, err := io.Copy(out, f)
// if err != nil {
// c.JSON(oasis_err2.ERROR, model.Result{Success: oasis_err2.ERROR, Message: oasis_err2.GetMsg(oasis_err2.ERROR), Data: err.Error()})
// return
// }
// c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
// return
// }
// fileNum, err := ioutil.ReadDir(tempDir)
// if err != nil {
// c.JSON(oasis_err2.ERROR, model.Result{Success: oasis_err2.ERROR, Message: oasis_err2.GetMsg(oasis_err2.ERROR), Data: err.Error()})
// return
// }
// if totalChunks == len(fileNum) {
// file.RMDir(tempDir)
// }
// c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
// }

View File

@@ -23,13 +23,13 @@ import (
"github.com/gin-gonic/gin"
)
// @Summary 系统信息
// @Summary check version
// @Produce application/json
// @Accept application/json
// @Tags sys
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /sys/chackversion [get]
// @Router /sys/check [get]
func CheckVersion(c *gin.Context) {
need, version := version.IsNeedUpdate()
if need {
@@ -115,7 +115,7 @@ func GetSystemConfigDebug(c *gin.Context) {
systemAppStatus += "Sync img: " + strconv.FormatBool(images) + "\n\t"
list := service.MyService.App().GetSystemAppList()
for _, v := range *list {
for _, v := range list {
systemAppStatus += v.Image + ",\n\t"
}
@@ -136,9 +136,6 @@ func GetSystemConfigDebug(c *gin.Context) {
c.JSON(http.StatusOK, model.Result{Success: oasis_err.SUCCESS, Message: oasis_err.GetMsg(oasis_err.SUCCESS), Data: bugContent})
}
func Sys(c *gin.Context) {
service.DockerPull()
}
//widget配置
func GetWidgetConfig(c *gin.Context) {
@@ -155,9 +152,7 @@ func GetWidgetConfig(c *gin.Context) {
func PostSetWidgetConfig(c *gin.Context) {
buf := make([]byte, 1024)
n, _ := c.Request.Body.Read(buf)
fmt.Println("错误", strconv.Itoa(n))
service.MyService.System().UpSystemConfig("", string(buf[0:n]))
fmt.Println("错误1", string(buf[0:n]))
c.JSON(http.StatusOK,
model.Result{
Success: oasis_err.SUCCESS,
@@ -251,6 +246,80 @@ func PostKillCasaOS(c *gin.Context) {
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 PutSystemOffUSBAutoMount(c *gin.Context) {
service.MyService.System().UpdateUSBAutoMount("False")
service.MyService.System().ExecUSBAutoMountShell("False")
c.JSON(http.StatusOK,
model.Result{
Success: oasis_err.SUCCESS,
Message: oasis_err.GetMsg(oasis_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(http.StatusOK,
model.Result{
Success: oasis_err.SUCCESS,
Message: oasis_err.GetMsg(oasis_err.SUCCESS),
Data: state,
})
}
// @Summary get system hardware info
// @Produce application/json
// @Accept application/json
// @Tags sys
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /sys/hardware/info [get]
func GetSystemHardwareInfo(c *gin.Context) {
data := make(map[string]string, 1)
data["drive_model"] = service.MyService.ZiMa().GetDeviceTree()
c.JSON(http.StatusOK,
model.Result{
Success: oasis_err.SUCCESS,
Message: oasis_err.GetMsg(oasis_err.SUCCESS),
Data: data,
})
}
// @Summary Turn off usb auto-mount
// @Produce application/json
// @Accept application/json
// @Tags sys
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /sys/usb/on [put]
func PutSystemOnUSBAutoMount(c *gin.Context) {
service.MyService.System().UpdateUSBAutoMount("True")
service.MyService.System().ExecUSBAutoMountShell("True")
c.JSON(http.StatusOK,
model.Result{
Success: oasis_err.SUCCESS,
Message: oasis_err.GetMsg(oasis_err.SUCCESS),
})
}
// @Summary system info
// @Produce application/json
// @Accept application/json
@@ -259,9 +328,9 @@ func PostKillCasaOS(c *gin.Context) {
// @Success 200 {string} string "ok"
// @Router /sys/info [get]
func Info(c *gin.Context) {
var data = make(map[string]interface{}, 5)
var data = make(map[string]interface{}, 6)
list := service.MyService.Disk().LSBLK()
list := service.MyService.Disk().LSBLK(true)
summary := model.Summary{}
healthy := true
@@ -269,7 +338,23 @@ func Info(c *gin.Context) {
for i := 0; i < len(list); i++ {
if len(list[i].Children) > 0 && findSystem == 0 {
for j := 0; j < len(list[i].Children); j++ {
if len(list[i].Children[j].Children) > 0 {
for _, v := range list[i].Children[j].Children {
if v.MountPoint == "/" {
s, _ := strconv.ParseUint(v.FSSize, 10, 64)
a, _ := strconv.ParseUint(v.FSAvail, 10, 64)
u, _ := strconv.ParseUint(v.FSUsed, 10, 64)
summary.Size += s
summary.Avail += a
summary.Used += u
findSystem = 1
break
}
}
} else {
if list[i].Children[j].MountPoint == "/" {
s, _ := strconv.ParseUint(list[i].Children[j].FSSize, 10, 64)
a, _ := strconv.ParseUint(list[i].Children[j].FSAvail, 10, 64)
@@ -282,11 +367,13 @@ func Info(c *gin.Context) {
}
}
}
}
if findSystem == 1 {
findSystem += 1
continue
}
if list[i].Tran == "sata" || list[i].Tran == "nvme" {
if list[i].Tran == "sata" || list[i].Tran == "nvme" || list[i].Tran == "spi" || list[i].Tran == "sas" {
temp := service.MyService.Disk().SmartCTL(list[i].Path)
if reflect.DeepEqual(temp, model.SmartctlA{}) {
continue
@@ -312,24 +399,51 @@ func Info(c *gin.Context) {
summary.Health = healthy
data["disk"] = summary
cpu := service.MyService.ZiMa().GetCpuPercent()
num := service.MyService.ZiMa().GetCpuCoreNum()
usbList := service.MyService.Disk().LSBLK(false)
usb := []model.DriveUSB{}
for _, v := range usbList {
if v.Tran == "usb" {
temp := model.DriveUSB{}
temp.Model = v.Model
temp.Name = v.Name
temp.Size = v.Size
mountTemp := true
if len(v.Children) == 0 {
mountTemp = false
}
for _, child := range v.Children {
if len(child.MountPoint) > 0 {
avail, _ := strconv.ParseUint(child.FSAvail, 10, 64)
temp.Avail += avail
used, _ := strconv.ParseUint(child.FSUsed, 10, 64)
temp.Used += used
} else {
mountTemp = false
}
}
temp.Mount = mountTemp
usb = append(usb, temp)
}
}
data["usb"] = usb
cpu := service.MyService.System().GetCpuPercent()
num := service.MyService.System().GetCpuCoreNum()
cpuData := make(map[string]interface{})
cpuData["percent"] = cpu
cpuData["num"] = num
data["cpu"] = cpuData
data["mem"] = service.MyService.ZiMa().GetMemInfo()
data["mem"] = service.MyService.System().GetMemInfo()
//拼装网络信息
netList := service.MyService.ZiMa().GetNetInfo()
netList := service.MyService.System().GetNetInfo()
newNet := []model.IOCountersStat{}
nets := service.MyService.ZiMa().GetNet(true)
nets := service.MyService.System().GetNet(true)
for _, n := range netList {
for _, netCardName := range nets {
if n.Name == netCardName {
item := *(*model.IOCountersStat)(unsafe.Pointer(&n))
item.State = strings.TrimSpace(service.MyService.ZiMa().GetNetState(n.Name))
item.DateTime = time.Now()
item.Time = time.Now().Unix()
newNet = append(newNet, item)
break
}
@@ -340,3 +454,20 @@ func Info(c *gin.Context) {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: data})
}
// @Summary Get notification port
// @Produce application/json
// @Accept application/json
// @Tags sys
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /sys/socket/port [get]
func GetSystemSocketPort(c *gin.Context) {
c.JSON(http.StatusOK,
model.Result{
Success: oasis_err.SUCCESS,
Message: oasis_err.GetMsg(oasis_err.SUCCESS),
Data: config.ServerInfo.SocketPort,
})
}

View File

@@ -40,7 +40,7 @@ func Set_Name_Pwd(c *gin.Context) {
return
}
//开始设置
err := user_service.SetUser(username, pwd, "", "", "")
err := user_service.SetUser(username, pwd, "", "", "", "")
if err != nil {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: fmt.Sprintf("%v", err)})
return
@@ -102,7 +102,7 @@ func Login(c *gin.Context) {
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /user/changhead [post]
func Up_Load_Head(c *gin.Context) {
func PostUserHead(c *gin.Context) {
file, _, _ := c.Request.FormFile("file")
user_service.UpLoadFile(file, config.UserInfo.Head)
c.JSON(http.StatusOK,
@@ -121,8 +121,8 @@ func Up_Load_Head(c *gin.Context) {
// @Param oldname formData string true "Old user name"
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /user/changusername [put]
func Chang_User_Name(c *gin.Context) {
// @Router /user/username [put]
func PutUserName(c *gin.Context) {
if config.ServerInfo.LockAccount {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.ACCOUNT_LOCK, Message: oasis_err2.GetMsg(oasis_err2.ACCOUNT_LOCK)})
return
@@ -133,7 +133,7 @@ func Chang_User_Name(c *gin.Context) {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.ERROR, Message: oasis_err2.GetMsg(oasis_err2.ERROR)})
return
}
user_service.SetUser(username, "", "", "", "")
user_service.SetUser(username, "", "", "", "", "")
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
}
@@ -142,14 +142,14 @@ func Chang_User_Name(c *gin.Context) {
// @Accept multipart/form-data
// @Tags user
// @Param pwd formData string true "Password"
// @Param oldpwd formData string true "Old password"
// @Param old_pwd formData string true "Old password"
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /user/changuserpwd [put]
func Chang_User_Pwd(c *gin.Context) {
oldpwd := c.PostForm("oldpwd")
// @Router /user/password [put]
func PutUserPwd(c *gin.Context) {
oldPwd := c.PostForm("old_pwd")
pwd := c.PostForm("pwd")
if config.UserInfo.PWD != oldpwd {
if config.UserInfo.PWD != oldPwd {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.PWD_INVALID_OLD, Message: oasis_err2.GetMsg(oasis_err2.PWD_INVALID_OLD)})
return
}
@@ -161,53 +161,126 @@ func Chang_User_Pwd(c *gin.Context) {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.PWD_IS_EMPTY, Message: oasis_err2.GetMsg(oasis_err2.PWD_IS_EMPTY)})
return
}
user_service.SetUser("", pwd, "", "", "")
user_service.SetUser("", pwd, "", "", "", "")
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
}
// @Summary 修改用户信息
// @Summary edit user info
// @Produce application/json
// @Accept multipart/form-data
// @Tags user
// @Param username formData string false "User Name"
// @Param user_name formData string false "User Name"
// @Param email formData string false "Email"
// @Param description formData string false "Description"
// @Param pwd formData string false "Password"
// @Param oldpwd formData string false "Old password"
// @Param old_pwd formData string false "Old password"
// @Param nick_name formData string false "nick name"
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /user/changuserinfo [post]
func Chang_User_Info(c *gin.Context) {
username := c.PostForm("username")
// @Router /user/info [post]
func PostUserChangeInfo(c *gin.Context) {
username := c.PostForm("user_name")
email := c.PostForm("email")
description := c.PostForm("description")
oldpwd := c.PostForm("oldpwd")
nickName := c.PostForm("nick_name")
oldpwd := c.PostForm("old_pwd")
pwd := c.PostForm("pwd")
if len(pwd) > 0 && config.UserInfo.PWD != oldpwd {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.PWD_INVALID, Message: oasis_err2.GetMsg(oasis_err2.PWD_INVALID)})
return
}
user_service.SetUser(username, pwd, "", email, description)
data := make(map[string]string, 2)
user_service.SetUser(username, pwd, "", email, description, nickName)
data := make(map[string]string, 4)
data["token"] = jwt2.GetToken(username, pwd)
data["user_name"] = username
data["head"] = config.UserInfo.Head
data["nick_name"] = config.UserInfo.NickName
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: data})
}
// @Summary 获取用户详情
// @Summary edit user nick
// @Produce application/json
// @Accept multipart/form-data
// @Tags user
// @Param nick_name formData string false "nick name"
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /user/nick [put]
func PutUserChangeNick(c *gin.Context) {
nickName := c.PostForm("nick_name")
if len(nickName) == 0 {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
return
}
user_service.SetUser("", "", "", "", "", nickName)
data := make(map[string]string, 1)
data["nick_name"] = config.UserInfo.NickName
go service.MyService.Casa().PushUserInfo()
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: data})
}
// @Summary edit user description
// @Produce application/json
// @Accept multipart/form-data
// @Tags user
// @Param description formData string false "Description"
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /user/desc [put]
func PutUserChangeDesc(c *gin.Context) {
desc := c.PostForm("description")
if len(desc) == 0 {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
return
}
user_service.SetUser("", "", "", "", desc, "")
data := make(map[string]string, 1)
data["description"] = config.UserInfo.Description
go service.MyService.Casa().PushUserInfo()
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: data})
}
// @Summary Modify user person information (Initialization use)
// @Produce application/json
// @Accept multipart/form-data
// @Tags user
// @Param nick_name formData string false "user nick name"
// @Param description formData string false "Description"
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /user/person/info [post]
func PostUserPersonInfo(c *gin.Context) {
desc := c.PostForm("description")
nickName := c.PostForm("nick_name")
if len(desc) == 0 || len(nickName) == 0 {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
return
}
user_service.SetUser("", "", "", "", desc, nickName)
data := make(map[string]string, 2)
data["description"] = config.UserInfo.Description
data["nick_name"] = config.UserInfo.NickName
go service.MyService.Casa().PushUserInfo()
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: data})
}
// @Summary get user info
// @Produce application/json
// @Accept mapplication/json
// @Tags user
// @Success 200 {string} string "ok"
// @Router /user/info [get]
func UserInfo(c *gin.Context) {
var u = make(map[string]string, 2)
func GetUserInfo(c *gin.Context) {
var u = make(map[string]string, 5)
u["user_name"] = config.UserInfo.UserName
u["head"] = config.UserInfo.Head
u["email"] = config.UserInfo.Email
u["description"] = config.UserInfo.Description
u["nick_name"] = config.UserInfo.NickName
c.JSON(http.StatusOK,
model.Result{
Success: oasis_err2.SUCCESS,
@@ -215,3 +288,14 @@ func UserInfo(c *gin.Context) {
Data: u,
})
}
// @Summary Get my shareId
// @Produce application/json
// @Accept application/json
// @Tags user
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /user/shareid [get]
func GetUserShareID(c *gin.Context) {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: config.ServerInfo.Token})
}

View File

@@ -1,475 +0,0 @@
package v1
import (
json2 "encoding/json"
"net/http"
"github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/pkg/config"
oasis_err2 "github.com/IceWhaleTech/CasaOS/pkg/utils/oasis_err"
"github.com/IceWhaleTech/CasaOS/service"
"github.com/gin-gonic/gin"
)
// @Summary 登录zerotier获取token
// @Produce application/json
// @Accept multipart/form-data
// @Tags zerotier
// @Param username formData string true "User name"
// @Param pwd formData string true "password"
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /zerotier/login [post]
func ZeroTierGetToken(c *gin.Context) {
username := c.PostForm("username")
pwd := c.PostForm("pwd")
if len(username) == 0 || len(pwd) == 0 {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
return
}
errInfo := service.MyService.ZeroTier().GetToken(username, pwd)
if len(errInfo) == 0 {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.ERROR, Message: oasis_err2.GetMsg(oasis_err2.GET_TOKEN_ERROR)})
} else {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
}
}
// @Summary 注册zerotier
// @Produce application/json
// @Accept multipart/form-data
// @Tags zerotier
// @Param firstName formData string true "first name"
// @Param pwd formData string true "password"
// @Param email formData string true "email"
// @Param lastName formData string true "last name"
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /zerotier/register [post]
func ZeroTierRegister(c *gin.Context) {
firstName := c.PostForm("firstName")
pwd := c.PostForm("pwd")
email := c.PostForm("email")
lastName := c.PostForm("lastName")
if len(firstName) == 0 || len(pwd) == 0 || len(email) == 0 || len(lastName) == 0 {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
return
}
errInfo := service.MyService.ZeroTier().ZeroTierRegister(email, lastName, firstName, pwd)
if len(errInfo) == 0 {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
} else {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.ERROR, Message: errInfo})
}
}
// @Summary 是否需要登录zerotier
// @Produce application/json
// @Accept application/json
// @Tags zerotier
// @Security ApiKeyAuth
// @Success 200 {string} string "false:需要登录true:不需要登录"
// @Router /zerotier/islogin [get]
func ZeroTierIsNeedLogin(c *gin.Context) {
if len(config.ZeroTierInfo.Token) == 0 {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: false})
} else {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: true})
}
}
// @Summary 获取zerotier网络列表
// @Produce application/json
// @Accept application/json
// @Tags zerotier
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /zerotier/list [get]
func ZeroTierGetNetworkList(c *gin.Context) {
jsonList, joined := service.MyService.ZeroTier().ZeroTierNetworkList(config.ZeroTierInfo.Token)
rdata := make(map[string]interface{})
rdata["network_list"] = jsonList
rdata["joined"] = joined
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: rdata})
}
// @Summary 获取zerotier网络详情
// @Produce application/json
// @Accept application/json
// @Tags zerotier
// @Security ApiKeyAuth
// @Param id path string true "network id"
// @Success 200 {string} string "ok"
// @Router /zerotier/info/{id} [get]
func ZeroTierGetNetworkGetInfo(c *gin.Context) {
id := c.Param("id")
if len(id) == 0 {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
return
}
info, joined := service.MyService.ZeroTier().ZeroTierGetInfo(config.ZeroTierInfo.Token, id)
rdata := make(map[string]interface{})
rdata["info"] = info
rdata["joined"] = joined
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: rdata})
}
//// @Summary 获取zerotier网络状态
//// @Produce application/json
//// @Accept application/json
//// @Tags zerotier
//// @Security ApiKeyAuth
//// @Success 200 {string} string "ok"
//// @Router /zerotier/status [get]
//func ZeroTierGetNetworkGetStatus(c *gin.Context) {
// status := service.MyService.ZeroTier().ZeroTierGetStatus(config.ZeroTierInfo.Token)
// c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: status})
//}
//// @Summary 修改网络类型
//// @Produce application/json
//// @Accept application/json
//// @Tags zerotier
//// @Security ApiKeyAuth
//// @Param id path string true "network id"
//// @Param type formData string true "Private true/false"
//// @Success 200 {string} string "ok"
//// @Router /zerotier/type/{id} [put]
//func ZeroTierEditType(c *gin.Context) {
// id := c.Param("id")
// t := c.PostForm("type")
// if len(id) == 0 || len(t) == 0 {
// c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
// return
// }
// postData := `{"config":{"private":` + t + `}}`
// info := service.MyService.ZeroTier().EditNetwork(config.ZeroTierInfo.Token, postData, id)
// c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: info})
//}
//// @Summary 修改名称
//// @Produce application/json
//// @Accept application/json
//// @Tags zerotier
//// @Security ApiKeyAuth
//// @Param id path string true "network id"
//// @Param name formData string true "需要过滤特殊字符串"
//// @Success 200 {string} string "ok"
//// @Router /zerotier/name/{id} [put]
//func ZeroTierEditName(c *gin.Context) {
// id := c.Param("id")
// name := c.PostForm("name")
// if len(id) == 0 || len(name) == 0 {
// c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
// return
// }
// postData := `{"config":{"name":"` + name + `"}}`
// info := service.MyService.ZeroTier().EditNetwork(config.ZeroTierInfo.Token, postData, id)
// c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: info})
//}
//// @Summary V6Assign (注意三个属性需要一起传过来,不传的会被zerotier设置成false)
//// @Produce application/json
//// @Accept application/json
//// @Tags zerotier
//// @Security ApiKeyAuth
//// @Param id path string true "network id"
//// @Param v6plan formData string false "true/false"
//// @Param rfc formData string false "true/false"
//// @Param auto formData string false "true/false"
//// @Success 200 {string} string "ok"
//// @Router /zerotier/v6assign/{id} [put]
//func ZeroTierEditV6Assign(c *gin.Context) {
// id := c.Param("id")
// v6plan := c.PostForm("v6plan")
// rfc := c.PostForm("rfc")
// auto := c.PostForm("auto")
// if len(id) == 0 {
// c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
// return
// }
// var spicing string
// if len(v6plan) > 0 {
// spicing = `"6plane":` + v6plan
// }
// if len(rfc) > 0 {
// if len(spicing) > 0 {
// spicing += ","
// }
// spicing += `"rfc4193":` + rfc
// }
//
// if len(auto) > 0 {
// if len(spicing) > 0 {
// spicing += ","
// }
// spicing += `"zt":` + auto
// }
// postData := `{"config":{"v6AssignMode":{` + spicing + `}}}`
// info := service.MyService.ZeroTier().EditNetwork(config.ZeroTierInfo.Token, postData, id)
// c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: info})
//}
//// @Summary Broadcast
//// @Produce application/json
//// @Accept application/json
//// @Tags zerotier
//// @Security ApiKeyAuth
//// @Param id path string true "network id"
//// @Param broadcast formData string true "true/false"
//// @Success 200 {string} string "ok"
//// @Router /zerotier/broadcast/{id} [put]
//func ZeroTierEditBroadcast(c *gin.Context) {
// id := c.Param("id")
// broadcast := c.PostForm("broadcast")
// if len(id) == 0 || len(broadcast) == 0 {
// c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
// return
// }
// postData := `{"config":{"enableBroadcast":` + broadcast + `}}`
// info := service.MyService.ZeroTier().EditNetwork(config.ZeroTierInfo.Token, postData, id)
// c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: info})
//}
// @Summary 网络列表
// @Produce application/json
// @Accept application/json
// @Tags zerotier
// @Security ApiKeyAuth
// @Param id path string true "network id"
// @Success 200 {string} string "ok"
// @Router /zerotier/member/{id} [get]
func ZeroTierMemberList(c *gin.Context) {
id := c.Param("id")
if len(id) == 0 {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
return
}
info := service.MyService.ZeroTier().MemberList(config.ZeroTierInfo.Token, id)
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: info})
}
// @Summary create new network
// @Produce application/json
// @Accept application/json
// @Tags zerotier
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /zerotier/create [post]
func ZeroTierCreateNetwork(c *gin.Context) {
info := service.MyService.ZeroTier().CreateNetwork(config.ZeroTierInfo.Token)
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: info})
}
//// @Summary 通过/拒绝客户端
//// @Produce application/json
//// @Accept application/json
//// @Tags zerotier
//// @Security ApiKeyAuth
//// @Param id path string true "network id"
//// @Param mId path string true "member_id"
//// @Param auth formData string true "true/false"
//// @Success 200 {string} string "ok"
//// @Router /zerotier/member/{id}/auth/{mId} [put]
//func ZeroTierMemberAuth(c *gin.Context) {
// id := c.Param("id")
// mId := c.Param("mId")
// auth := c.PostForm("auth")
// if len(id) == 0 || len(mId) == 0 || len(auth) == 0 {
// c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
// return
// }
// postData := `{"config":{"authorized":` + auth + `}}`
// info := service.MyService.ZeroTier().EditNetworkMember(config.ZeroTierInfo.Token, postData, id, mId)
// c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: info})
//}
//// @Summary 修改名字
//// @Produce application/json
//// @Accept application/json
//// @Tags zerotier
//// @Security ApiKeyAuth
//// @Param id path string true "network id"
//// @Param mId path string true "member_id"
//// @Param name formData string true "name"
//// @Success 200 {string} string "ok"
//// @Router /zerotier/member/{id}/name/{mId} [put]
//func ZeroTierMemberName(c *gin.Context) {
// id := c.Param("id")
// mId := c.Param("mId")
// name := c.PostForm("name")
// if len(id) == 0 || len(mId) == 0 || len(name) == 0 {
// c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
// return
// }
// postData := `{"name":"` + name + `"}`
// info := service.MyService.ZeroTier().EditNetworkMember(config.ZeroTierInfo.Token, postData, id, mId)
// c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: info})
//}
//// @Summary 修改桥接
//// @Produce application/json
//// @Accept application/json
//// @Tags zerotier
//// @Security ApiKeyAuth
//// @Param id path string true "network id"
//// @Param mId path string true "member_id"
//// @Param bridge formData string true "true/false"
//// @Success 200 {string} string "ok"
//// @Router /zerotier/member/{id}/bridge/{mId} [put]
//func ZeroTierMemberBridge(c *gin.Context) {
// id := c.Param("id")
// mId := c.Param("mId")
// bridge := c.PostForm("bridge")
// if len(id) == 0 || len(mId) == 0 || len(bridge) == 0 {
// c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
// return
// }
// postData := `{"config":{"activeBridge":` + bridge + `}}`
// info := service.MyService.ZeroTier().EditNetworkMember(config.ZeroTierInfo.Token, postData, id, mId)
// c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: info})
//}
// @Summary 修改网络
// @Produce application/json
// @Accept application/json
// @Tags zerotier
// @Security ApiKeyAuth
// @Param id path string true "network id"
// @Param json formData string true "json数据"
// @Success 200 {string} string "ok"
// @Router /zerotier/edit/{id} [put]
func ZeroTierEdit(c *gin.Context) {
id := c.Param("id")
json := c.PostForm("json")
if len(id) == 0 || len(json) == 0 {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
return
}
info := service.MyService.ZeroTier().EditNetwork(config.ZeroTierInfo.Token, json, id)
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: info})
}
// @Summary 获取已加入的网络
// @Produce application/json
// @Accept application/json
// @Tags zerotier
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /zerotier/joined/list [get]
func ZeroTierJoinedList(c *gin.Context) {
info := service.MyService.ZeroTier().GetJoinNetworks()
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: json2.RawMessage(info)})
}
// @Summary 修改网络用户信息
// @Produce application/json
// @Accept application/json
// @Tags zerotier
// @Security ApiKeyAuth
// @Param id path string true "network id"
// @Param mId path string true "mId"
// @Param json formData string true "json数据"
// @Success 200 {string} string "ok"
// @Router /zerotier/member/{id}/edit/{mId} [put]
func ZeroTierMemberEdit(c *gin.Context) {
id := c.Param("id")
mId := c.Param("mId")
json := c.PostForm("json")
if len(id) == 0 || len(json) == 0 || len(mId) == 0 {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
return
}
info := service.MyService.ZeroTier().EditNetworkMember(config.ZeroTierInfo.Token, json, id, mId)
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: info})
}
// @Summary 删除网络中的用户
// @Produce application/json
// @Accept application/json
// @Tags zerotier
// @Security ApiKeyAuth
// @Param id path string true "network id"
// @Param mId path string true "member_id"
// @Success 200 {string} string "ok"
// @Router /zerotier/member/{id}/del/{mId} [delete]
func ZeroTierMemberDelete(c *gin.Context) {
id := c.Param("id")
mId := c.Param("mId")
if len(id) == 0 || len(mId) == 0 {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
return
}
info := service.MyService.ZeroTier().DeleteMember(config.ZeroTierInfo.Token, id, mId)
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: info})
}
// @Summary 删除网络
// @Produce application/json
// @Accept application/json
// @Tags zerotier
// @Security ApiKeyAuth
// @Param id path string true "network id"
// @Success 200 {string} string "ok"
// @Router /zerotier/network/{id}/del [delete]
func ZeroTierDeleteNetwork(c *gin.Context) {
id := c.Param("id")
if len(id) == 0 {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
return
}
info := service.MyService.ZeroTier().DeleteNetwork(config.ZeroTierInfo.Token, id)
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: info})
}
// @Summary 加入网络
// @Produce application/json
// @Accept multipart/form-data
// @Tags zerotier
// @Param id path string true "network id"
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /zerotier/join/{id} [post]
func ZeroTierJoinNetwork(c *gin.Context) {
networkId := c.Param("id")
if len(networkId) != 16 {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
return
}
for _, v := range networkId {
if !service.MyService.ZeroTier().NetworkIdFilter(v) {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
return
}
}
service.MyService.ZeroTier().ZeroTierJoinNetwork(networkId)
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
}
// @Summary 获取zerotier网络列表
// @Produce application/json
// @Accept multipart/form-data
// @Tags zerotier
// @Param id path string true "network id"
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /zerotier/leave/{id} [post]
func ZeroTierLeaveNetwork(c *gin.Context) {
networkId := c.Param("id")
if len(networkId) != 16 {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
return
}
for _, v := range networkId {
if !service.MyService.ZeroTier().NetworkIdFilter(v) {
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.INVALID_PARAMS, Message: oasis_err2.GetMsg(oasis_err2.INVALID_PARAMS)})
return
}
}
service.MyService.ZeroTier().ZeroTierLeaveNetwork(networkId)
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
}

View File

@@ -1,3 +1,13 @@
/*
* @Author: LinkLeong link@icewhale.com
* @Date: 2021-09-30 18:18:14
* @LastEditors: LinkLeong
* @LastEditTime: 2022-05-27 18:07:13
* @FilePath: /CasaOS/route/v1/zima_info.go
* @Description:
* @Website: https://www.casaos.io
* Copyright (c) 2022 by icewhale, All Rights Reserved.
*/
package v1
import (
@@ -21,8 +31,8 @@ import (
// @Router /zima/getcpuinfo [get]
func CupInfo(c *gin.Context) {
//检查参数是否正确
cpu := service.MyService.ZiMa().GetCpuPercent()
num := service.MyService.ZiMa().GetCpuCoreNum()
cpu := service.MyService.System().GetCpuPercent()
num := service.MyService.System().GetCpuCoreNum()
data := make(map[string]interface{})
data["percent"] = cpu
data["num"] = num
@@ -40,7 +50,7 @@ func CupInfo(c *gin.Context) {
func MemInfo(c *gin.Context) {
//检查参数是否正确
mem := service.MyService.ZiMa().GetMemInfo()
mem := service.MyService.System().GetMemInfo()
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: mem})
}
@@ -65,15 +75,15 @@ func DiskInfo(c *gin.Context) {
// @Success 200 {string} string "ok"
// @Router /zima/getnetinfo [get]
func NetInfo(c *gin.Context) {
netList := service.MyService.ZiMa().GetNetInfo()
netList := service.MyService.System().GetNetInfo()
newNet := []model.IOCountersStat{}
for _, n := range netList {
for _, netCardName := range service.MyService.ZiMa().GetNet(true) {
for _, netCardName := range service.MyService.System().GetNet(true) {
if n.Name == netCardName {
item := *(*model.IOCountersStat)(unsafe.Pointer(&n))
item.State = strings.TrimSpace(service.MyService.ZiMa().GetNetState(n.Name))
item.DateTime = time.Now()
item.Time = time.Now().Unix()
newNet = append(newNet, item)
break
}

View File

@@ -3,9 +3,11 @@ package service
import (
"context"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"runtime"
"strconv"
"strings"
"sync"
"time"
@@ -13,159 +15,327 @@ import (
"github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/pkg/config"
"github.com/IceWhaleTech/CasaOS/pkg/utils/command"
loger2 "github.com/IceWhaleTech/CasaOS/pkg/utils/loger"
"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
CreateApplication(m model2.ApplicationModel) model2.ApplicationModel
GetApplicationList() (m []model2.ApplicationModel)
GetApplicationById(id string) (m model2.ApplicationModel)
UpdateApplicationOrderById(id string, order int)
GetMyList(index, size int, position bool) (*[]model2.MyAppList, *[]model2.MyAppList)
GetCasaOSCount() int
SaveContainer(m model2.AppListDBModel)
GetUninstallInfo(id string) model2.AppListDBModel
RemoveContainerById(id string)
DeleteApp(id string)
GetContainerInfo(name string) (types.Container, error)
GetAppDBInfo(id string) model2.AppListDBModel
UpdateApp(m model2.AppListDBModel)
GetSimpleContainerInfo(name string) (types.Container, error)
DelAppConfigDir(path string)
GetSystemAppList() *[]model2.MyAppList
GetSystemAppList() []types.Container
GetHardwareUsageSteam()
GetHardwareUsage() []model.DockerStatsModel
GetAppStats(id string) string
GetAllDBApps() []model2.AppListDBModel
ImportApplications(casaApp bool)
CheckNewImage()
}
type appStruct struct {
db *gorm.DB
log loger2.OLog
}
//获取我的应用列表
func (a *appStruct) GetMyList(index, size int, position bool) *[]model2.MyAppList {
//获取docker应用
func (a *appStruct) GetApplicationById(id string) (m model2.ApplicationModel) {
a.db.Where("id = ?", id).First(&m)
return
}
func (a *appStruct) UpdateApplicationOrderById(id string, order int) {
a.db.Model(&model2.ApplicationModel{}).Where("id = ?", id).Update("order", order)
}
func (a *appStruct) CreateApplication(m model2.ApplicationModel) model2.ApplicationModel {
a.db.Create(&m)
return m
}
func (a *appStruct) GetApplicationList() (m []model2.ApplicationModel) {
a.db.Find(&m)
return
}
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) GetCasaOSCount() int {
cli, err := client2.NewClientWithOpts(client2.FromEnv, client2.WithTimeout(time.Second*5))
if err != nil {
a.log.Error("初始化client失败", "app.getmylist", "line:36", err)
loger.Error("Failed to init client", zap.Any("err", err))
return 0
}
defer cli.Close()
fts := filters.NewArgs()
fts.Add("label", "origin")
containers, err := cli.ContainerList(context.Background(), types.ContainerListOptions{All: true, Filters: fts})
fts.Add("label", "casaos=casaos")
//fts.Add("label", "casaos:casaos")
containers, err := cli.ContainerList(context.Background(), types.ContainerListOptions{Filters: fts, Limit: 200})
if err != nil {
a.log.Error("获取docker容器失败", "app.getmylist", "line:42", err)
loger.Error("failed to get container_list", zap.Any("err", err))
return 0
}
systemApp := MyService.App().GetApplicationList()
return len(containers) + len(systemApp)
}
//获取我的应用列表
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))
}
//获取本地数据库应用
var lm []model2.AppListDBModel
a.db.Table(model2.CONTAINERTABLENAME).Select("title,icon,port_map,`index`,container_id,position,label,slogan,image").Find(&lm)
unTranslation := []model2.MyAppList{}
list := []model2.MyAppList{}
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
//}
systemApp := MyService.App().GetApplicationList()
for _, v := range systemApp {
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,
Name: v.Name,
Icon: v.Icon,
State: strconv.Itoa(v.State),
Id: strconv.Itoa(v.Id),
CustomId: strconv.Itoa(v.Id),
Port: "",
//Order: strconv.Itoa(v.Order),
Index: "/",
Image: "",
Type: v.Type,
Host: "",
Protocol: "",
NewVersion: false,
})
}
for _, m := range containers {
if m.Labels["casaos"] == "casaos" {
if m.Labels["origin"] == "system" {
continue
}
_, newVersion := NewVersionApp[m.ID]
list = append(list, model2.MyAppList{
Name: strings.ReplaceAll(m.Names[0], "/", ""),
Icon: m.Labels["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,
NewVersion: 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: "",
NewVersion: false,
Host: "",
Protocol: "",
Image: m.Image,
})
}
}
return &list
//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() *[]model2.MyAppList {
func (a *appStruct) GetSystemAppList() []types.Container {
//获取docker应用
cli, err := client2.NewClientWithOpts(client2.FromEnv)
if err != nil {
a.log.Error("初始化client失败", "app.getmylist", "line:36", err)
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 {
a.log.Error("获取docker容器失败", "app.sys", "line:123", err)
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("title,icon,port_map,`index`,container_id,position,label,slogan,image,volumes").Find(&lm)
list := []model2.MyAppList{}
lMap := make(map[string]interface{})
for _, dbModel := range lm {
lMap[dbModel.ContainerId] = dbModel
}
for _, container := range containers {
if lMap[container.ID] != nil {
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,
Volumes: m.Volumes,
//Rely: m.Rely,
})
}
}
return &list
a.db.Table(model2.CONTAINERTABLENAME).Select("custom_id,title,icon,container_id,label,slogan,image,port_map").Find(&lm)
return lm
}
//获取我的应用列表
@@ -173,13 +343,13 @@ func (a *appStruct) GetContainerInfo(name string) (types.Container, error) {
//获取docker应用
cli, err := client2.NewClientWithOpts(client2.FromEnv)
if err != nil {
a.log.Error("初始化client失败", "app.getmylist", "line:36", err)
loger.Error("Failed to init client", zap.Any("err", err))
}
filters := filters.NewArgs()
filters.Add("name", name)
containers, err := cli.ContainerList(context.Background(), types.ContainerListOptions{All: true, Filters: filters})
if err != nil {
a.log.Error("获取docker容器失败", "app.getcontainerinfo", "line:182", err)
loger.Error("Failed to get container_list", zap.Any("err", err))
}
if len(containers) > 0 {
@@ -236,7 +406,7 @@ func (a *appStruct) DelAppConfigDir(path string) {
command.OnlyExec("source " + config.AppInfo.ProjectPath + "/shell/helper.sh ;DelAppConfigDir " + path)
}
func (a *appStruct) RemoveContainerById(id string) {
func (a *appStruct) DeleteApp(id string) {
a.db.Table(model2.CONTAINERTABLENAME).Where("custom_id = ?", id).Delete(&model2.AppListDBModel{})
}
@@ -292,31 +462,29 @@ func (a *appStruct) GetHardwareUsageSteam() {
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
var lm []model2.AppListDBModel
a.db.Table(model2.CONTAINERTABLENAME).Select("label,title,icon,container_id").Where("origin != ?", "system").Find(&lm)
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, Filters: fts})
if err != nil {
loger.Error("Failed to get container_list", zap.Any("err", err))
}
for i := 0; i < 100; i++ {
if config.CasaOSGlobalVariables.AppChange {
lm = []model2.AppListDBModel{}
config.CasaOSGlobalVariables.AppChange = false
a.db.Table(model2.CONTAINERTABLENAME).Select("label,title,icon,container_id").Where("origin != ?", "system").Find(&lm)
dataApps := dataStats
dataStats.Range(func(key, value interface{}) bool {
dataStats.Delete(key)
return true
})
for _, v := range lm {
m, _ := dataApps.Load(v.ContainerId)
if m != nil {
dataStats.Store(v.ContainerId, m)
}
}
}
var wg sync.WaitGroup
for _, v := range lm {
for _, v := range containers {
wg.Add(1)
go func(v model2.AppListDBModel, i int) {
go func(v types.Container, i int) {
defer wg.Done()
stats, err := cli.ContainerStats(ctx, v.ContainerId, true)
stats, err := cli.ContainerStats(ctx, v.ID, true)
if err != nil {
return
}
@@ -325,19 +493,15 @@ func (a *appStruct) GetHardwareUsageSteam() {
if err := decode.Decode(&data); err == io.EOF {
return
}
m, _ := dataStats.Load(v.ContainerId)
m, _ := dataStats.Load(v.ID)
dockerStats := model.DockerStatsModel{}
if m != nil {
dockerStats.Pre = m.(model.DockerStatsModel).Data
}
dockerStats.Data = data
dockerStats.Icon = v.Icon
if len(v.Label) > 0 {
dockerStats.Title = v.Label
} else {
dockerStats.Title = v.Title
}
dataStats.Store(v.ContainerId, dockerStats)
dockerStats.Icon = v.Labels["icon"]
dockerStats.Title = strings.ReplaceAll(v.Names[0], "/", "")
dataStats.Store(v.ID, dockerStats)
if i == 99 {
stats.Body.Close()
}
@@ -351,6 +515,6 @@ func (a *appStruct) GetHardwareUsageSteam() {
cancel()
}
func NewAppService(db *gorm.DB, logger loger2.OLog) AppService {
return &appStruct{db: db, log: logger}
func NewAppService(db *gorm.DB) AppService {
return &appStruct{db: db}
}

9
service/app_test.go Normal file
View File

@@ -0,0 +1,9 @@
package service
import (
"testing"
)
func TestGetCasaOSCount(t *testing.T) {
}

View File

@@ -1,14 +1,17 @@
package service
import (
"encoding/json"
json2 "encoding/json"
"fmt"
"strconv"
"time"
"github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/pkg/config"
httper2 "github.com/IceWhaleTech/CasaOS/pkg/utils/httper"
model2 "github.com/IceWhaleTech/CasaOS/service/model"
"github.com/IceWhaleTech/CasaOS/types"
"github.com/tidwall/gjson"
)
@@ -18,6 +21,12 @@ type CasaService interface {
GetTaskList(size int) []model2.TaskDBModel
GetServerAppInfo(id, t string, language string) model.ServerAppList
ShareAppFile(body []byte) string
PushHeart(id, t string, language string)
PushAppAnalyse(uuid, t string, name, language string)
PushConnectionStatus(uuid, err string, from, to, event string)
PushUserInfo()
GetUserInfoByShareId(shareId string) model.UserInfo
GetPersonPublic() (list []model.FriendsModel)
}
type casaService struct {
@@ -70,24 +79,71 @@ func (o *casaService) GetServerList(index, size, tp, categoryId, key, language s
json2.Unmarshal([]byte(gjson.Get(listS, "data.community").String()), &community)
if len(list) > 0 {
Cache.SetDefault(keyName, listS)
Cache.Set(keyName, listS, time.Hour*24)
}
return
}
func (o *casaService) GetServerCategoryList() []model.ServerCategoryList {
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)
list := []model.ServerCategoryList{}
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.ServerCategoryList) {
// results := file.ReadFullFile(config.AppInfo.ProjectPath + "/conf/app_category.json")
// err := json2.Unmarshal(results, &list)
// if err != nil {
// loger.Error("marshal error", zap.Any("err", err), zap.Any("content", string(results)))
// }
// return list
// }
// func (o *casaService) AsyncGetServerCategoryList() {
// list := model.ServerCategoryList{}
// results := file.ReadFullFile(config.AppInfo.ProjectPath + "/conf/app_category.json")
// err := json2.Unmarshal(results, &list)
// if err != nil {
// loger.Error("marshal error", zap.Any("err", err), zap.Any("content", string(results)))
// }
// if list.Version == GetAppVersion() {
// return
// }
// item := []model.CategoryList{}
// 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()), &item)
// if len(item) > 0 {
// list.Version = GetAppVersion()
// list.Item = item
// by, err := json.Marshal(list)
// if err != nil {
// loger.Error("marshal error", zap.Any("err", err))
// }
// file.WriteToPath(by, config.AppInfo.ProjectPath+"/conf", "app_category.json")
// }
// }
func (o *casaService) GetServerAppInfo(id, t string, language string) model.ServerAppList {
head := make(map[string]string)
@@ -114,7 +170,6 @@ func GetToken() string {
}
go func() {
str := httper2.Get(config.ServerInfo.ServerApi+"/token", nil)
t <- gjson.Get(str, "data").String()
}()
auth = <-t
@@ -123,6 +178,106 @@ func GetToken() string {
return auth
}
func NewOasisService() CasaService {
func (o *casaService) PushHeart(id, t string, language string) {
m := model.CasaOSHeart{}
m.UuId = id
m.Type = t
b, _ := json.Marshal(m)
head := make(map[string]string)
head["Authorization"] = GetToken()
infoS := httper2.Post(config.ServerInfo.ServerApi+"/v1/analyse/heart", b, "application/json", head)
info := model.ServerAppList{}
json2.Unmarshal([]byte(gjson.Get(infoS, "data").String()), &info)
}
func (o *casaService) PushAppAnalyse(uuid, t string, name, language string) {
m := model.AppAnalyse{}
m.UUId = uuid
m.Type = t
m.Name = name
m.Language = language
m.Version = types.CURRENTVERSION
b, _ := json.Marshal(m)
head := make(map[string]string)
head["Authorization"] = GetToken()
infoS := httper2.Post(config.ServerInfo.ServerApi+"/v1/analyse/app", b, "application/json", head)
info := model.ServerAppList{}
json2.Unmarshal([]byte(gjson.Get(infoS, "data").String()), &info)
}
func (o *casaService) PushConnectionStatus(uuid, err string, from, to, event string) {
m := model.ConnectionStatus{}
m.UUId = uuid
m.Error = err
m.From = from
m.To = to
m.Event = event
b, _ := json.Marshal(m)
head := make(map[string]string)
head["Authorization"] = GetToken()
infoS := httper2.Post(config.ServerInfo.ServerApi+"/v1/analyse/connect", b, "application/json", head)
info := model.ServerAppList{}
json2.Unmarshal([]byte(gjson.Get(infoS, "data").String()), &info)
}
func (o *casaService) PushUserInfo() {
m := model.UserInfo{}
m.Desc = config.UserInfo.Description
m.Avatar = config.UserInfo.Avatar
m.NickName = config.UserInfo.NickName
m.ShareId = config.ServerInfo.Token
b, _ := json.Marshal(m)
head := make(map[string]string)
head["Authorization"] = GetToken()
infoS := httper2.Post(config.ServerInfo.ServerApi+"/v1/user/info", b, "application/json", head)
info := model.ServerAppList{}
json2.Unmarshal([]byte(gjson.Get(infoS, "data").String()), &info)
}
func (o *casaService) GetUserInfoByShareId(shareId string) model.UserInfo {
head := make(map[string]string)
head["Authorization"] = GetToken()
infoS := httper2.Get(config.ServerInfo.ServerApi+"/v1/user/info/"+shareId, head)
info := model.UserInfo{}
json2.Unmarshal([]byte(gjson.Get(infoS, "data").String()), &info)
return info
}
func (o *casaService) GetPersonPublic() (list []model.FriendsModel) {
head := make(map[string]string)
head["Authorization"] = GetToken()
listS := httper2.Get(config.ServerInfo.ServerApi+"/v1/person/public", head)
json2.Unmarshal([]byte(gjson.Get(listS, "data").String()), &list)
return list
}
func NewCasaService() CasaService {
return &casaService{}
}

View File

@@ -0,0 +1 @@
package service

View File

@@ -11,16 +11,17 @@ import (
"github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/pkg/config"
command2 "github.com/IceWhaleTech/CasaOS/pkg/utils/command"
loger2 "github.com/IceWhaleTech/CasaOS/pkg/utils/loger"
"github.com/IceWhaleTech/CasaOS/pkg/utils/loger"
model2 "github.com/IceWhaleTech/CasaOS/service/model"
"github.com/shirou/gopsutil/v3/disk"
"github.com/tidwall/gjson"
"go.uber.org/zap"
"gorm.io/gorm"
)
type DiskService interface {
GetPlugInDisk() []string
LSBLK() []model.LSBLKModel
LSBLK(isUseCache bool) []model.LSBLKModel
SmartCTL(path string) model.SmartctlA
FormatDisk(path, format string) []string
UmountPointAndRemoveDir(path string) []string
@@ -37,7 +38,6 @@ type DiskService interface {
RemoveLSBLKCache()
}
type diskService struct {
log loger2.OLog
db *gorm.DB
}
@@ -58,13 +58,13 @@ func (d *diskService) SmartCTL(path string) model.SmartctlA {
var m model.SmartctlA
str := command2.ExecSmartCTLByPath(path)
if str == nil {
d.log.Error("smartctl exec error,smartctl")
loger.Error("failed to exec shell ", zap.Any("err", "smartctl exec error"))
return m
}
err := json2.Unmarshal([]byte(str), &m)
if err != nil {
d.log.Error("json ummarshal error", err)
loger.Error("Failed to unmarshal json", zap.Any("err", err))
}
if !reflect.DeepEqual(m, model.SmartctlA{}) {
Cache.Add(key, m, time.Second*10)
@@ -119,11 +119,11 @@ func (d *diskService) GetDiskInfoByPath(path string) *disk.UsageStat {
}
//get disk details
func (d *diskService) LSBLK() []model.LSBLKModel {
func (d *diskService) LSBLK(isUseCache bool) []model.LSBLKModel {
key := "system_lsblk"
var n []model.LSBLKModel
if result, ok := Cache.Get(key); ok {
if result, ok := Cache.Get(key); ok && isUseCache {
res, ok := result.([]model.LSBLKModel)
if ok {
@@ -133,13 +133,13 @@ func (d *diskService) LSBLK() []model.LSBLKModel {
str := command2.ExecLSBLK()
if str == nil {
d.log.Error("lsblk exec error,lsblk")
loger.Error("Failed to exec shell", zap.Any("err", "lsblk exec error"))
return nil
}
var m []model.LSBLKModel
err := json2.Unmarshal([]byte(gjson.Get(string(str), "blockdevices").String()), &m)
if err != nil {
d.log.Error("json ummarshal error", err)
loger.Error("Failed to unmarshal json", zap.Any("err", err))
}
var c []model.LSBLKModel
@@ -171,7 +171,9 @@ func (d *diskService) LSBLK() []model.LSBLKModel {
i.Children = c
if fsused > 0 {
i.UsedPercent, err = strconv.ParseFloat(fmt.Sprintf("%.4f", float64(fsused)/float64(i.Size)), 64)
d.log.Fatal("diskservice_lsblk_fsused", err)
if err != nil {
loger.Error("Failed to parse float", zap.Any("err", err))
}
}
n = append(n, i)
health = true
@@ -188,15 +190,14 @@ func (d *diskService) LSBLK() []model.LSBLKModel {
func (d *diskService) GetDiskInfo(path string) model.LSBLKModel {
str := command2.ExecLSBLKByPath(path)
if str == nil {
d.log.Error("lsblk exec error,str")
loger.Error("Failed to exec shell", zap.Any("err", "lsblk exec error"))
return model.LSBLKModel{}
}
var ml []model.LSBLKModel
err := json2.Unmarshal([]byte(gjson.Get(string(str), "blockdevices").String()), &ml)
if err != nil {
d.log.Info(string(str))
d.log.Error("json ummarshal error", err)
loger.Error("Failed to unmarshal json", zap.Any("err", err))
return model.LSBLKModel{}
}
@@ -209,8 +210,7 @@ func (d *diskService) GetDiskInfo(path string) model.LSBLKModel {
chiArr := make(map[string]string)
chiList := command2.ExecResultStrArray("source " + config.AppInfo.ProjectPath + "/shell/helper.sh ;GetPartitionSectors " + m.Path)
if len(chiList) == 0 {
d.log.Error(m.Path, chiList)
d.log.Error("chiList length error")
loger.Error("chiList length error", zap.Any("err", "chiList length error"))
}
for i := 0; i < len(chiList); i++ {
tempArr := strings.Split(chiList[i], ",")
@@ -229,7 +229,7 @@ func (d *diskService) GetDiskInfo(path string) model.LSBLKModel {
diskEndSector := command2.ExecResultStrArray("source " + config.AppInfo.ProjectPath + "/shell/helper.sh ;GetDiskSizeAndSectors " + m.Path)
if len(diskEndSector) < 2 {
d.log.Error("diskEndSector length error")
loger.Error("diskEndSector length error", zap.Any("err", "diskEndSector length error"))
}
diskEndSectorInt, _ := strconv.ParseUint(diskEndSector[len(diskEndSector)-1], 10, 64)
if (diskEndSectorInt-maxSector)*m.MinIO/1024/1024 > 100 {
@@ -247,12 +247,12 @@ func (d *diskService) MountDisk(path, volume string) {
}
func (d *diskService) SaveMountPoint(m model2.SerialDisk) {
d.db.Where("serial = ?", m.Serial).Delete(&model2.SerialDisk{})
d.db.Where("uuid = ?", m.UUID).Delete(&model2.SerialDisk{})
d.db.Create(&m)
}
func (d *diskService) UpdateMountPoint(m model2.SerialDisk) {
d.db.Model(&model2.SerialDisk{}).Where("serial = ?", m.Serial).Update("mount_point", m.MountPoint)
d.db.Model(&model2.SerialDisk{}).Where("uui = ?", m.UUID).Update("mount_point", m.MountPoint)
}
func (d *diskService) DeleteMount(id string) {
@@ -273,6 +273,6 @@ func (d *diskService) GetSerialAll() []model2.SerialDisk {
return m
}
func NewDiskService(log loger2.OLog, db *gorm.DB) DiskService {
return &diskService{log: log, db: db}
func NewDiskService(db *gorm.DB) DiskService {
return &diskService{db: db}
}

View File

@@ -7,22 +7,22 @@ import (
"encoding/binary"
json2 "encoding/json"
"fmt"
"reflect"
"syscall"
model2 "github.com/IceWhaleTech/CasaOS/service/model"
types2 "github.com/IceWhaleTech/CasaOS/types"
"github.com/IceWhaleTech/CasaOS/model/notify"
"github.com/containerd/containerd"
"github.com/containerd/containerd/cio"
"github.com/containerd/containerd/namespaces"
"github.com/containerd/containerd/oci"
"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"
loger2 "github.com/IceWhaleTech/CasaOS/pkg/utils/loger"
"github.com/IceWhaleTech/CasaOS/pkg/utils/loger"
//"github.com/containerd/containerd/oci"
"io"
@@ -43,9 +43,10 @@ import (
)
type DockerService interface {
DockerPullImage(imageName string, m model2.AppNotify) error
DockerPullImage(imageName string, icon, name string) error
IsExistImage(imageName string) bool
DockerContainerCreate(imageName string, containerDbId string, m model.CustomizationPostData, net string) (containerId string, err error)
DockerContainerCreate(imageName string, m model.CustomizationPostData, net 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)
@@ -58,15 +59,28 @@ type DockerService interface {
DockerContainerUpdate(m model.CustomizationPostData, id string) (err error)
DockerContainerLog(name string) (string, error)
DockerContainerCommit(name string)
DockerContainerList() []types.Container
DockerNetworkModelList() []types.NetworkResource
DockerImageInfo(image string)
DockerImageInfo(image string) (types.ImageInspect, error)
GetNetWorkNameByNetWorkID(id string) (string, error)
ContainerExecShell(container_id string) string
}
type dockerService struct {
rootDir string
log loger2.OLog
}
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 {
@@ -119,10 +133,7 @@ func DockerPull() {
cli, _ := client2.NewClientWithOpts(client2.FromEnv)
defer cli.Close()
authConfig := types.AuthConfig{
Username: "cn-north-4@M4OW0IULZ3U6PCQPBUZC",
Password: "7390181a1565f90927bbd98038436b57d6ebc66a3828d7a11dfda42b9c19d91d",
}
authConfig := types.AuthConfig{}
encodedJSON, err := json2.Marshal(authConfig)
fmt.Println(err)
@@ -174,18 +185,16 @@ func DockerEx() {
//
//}
func (ds *dockerService) DockerImageInfo(image string) {
func (ds *dockerService) DockerImageInfo(image string) (types.ImageInspect, error) {
cli, err := client2.NewClientWithOpts(client2.FromEnv)
//but := bytes.Buffer{}
d, b, err := cli.ImageInspectWithRaw(context.Background(), image)
st, _ := json2.Marshal(d.Config)
fmt.Println(string(st))
fmt.Println("换行")
fmt.Println(string(b))
if err != nil {
fmt.Print(err)
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 {
@@ -308,7 +317,7 @@ func (ds *dockerService) IsExistImage(imageName string) bool {
}
//安装镜像
func (ds *dockerService) DockerPullImage(imageName string, m model2.AppNotify) error {
func (ds *dockerService) DockerPullImage(imageName string, icon, name string) error {
cli, err := client2.NewClientWithOpts(client2.FromEnv)
if err != nil {
return err
@@ -323,7 +332,8 @@ func (ds *dockerService) DockerPullImage(imageName string, m model2.AppNotify) e
return err
}
buf := make([]byte, 256)
//io.Copy()
buf := make([]byte, 2048*4)
for {
n, err := out.Read(buf)
if err != nil {
@@ -332,16 +342,33 @@ func (ds *dockerService) DockerPullImage(imageName string, m model2.AppNotify) e
}
break
}
if !reflect.DeepEqual(m, model2.AppNotify{}) {
m.Type = types2.NOTIFY_TYPE_INSTALL_LOG
m.State = 0
m.Message = string(buf[:n])
MyService.Notify().UpdateLog(m)
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
@@ -349,7 +376,7 @@ func (ds *dockerService) DockerPullImage(imageName string, m model2.AppNotify) e
//param mapPort 容器主端口映射到外部的端口
//param tcp 容器其他tcp端口
//param udp 容器其他udp端口
func (ds *dockerService) DockerContainerCreate(imageName string, containerDbId string, m model.CustomizationPostData, net string) (containerId string, err error) {
func (ds *dockerService) DockerContainerCreate(imageName string, m model.CustomizationPostData, net string) (containerId string, err error) {
if len(net) == 0 {
net = "bridge"
}
@@ -376,7 +403,7 @@ func (ds *dockerService) DockerContainerCreate(imageName string, containerDbId s
if tContainer > 0 {
ports[nat.Port(portMap.ContainerPort+"/tcp")] = struct{}{}
if net != "host" {
portMaps[nat.Port(portMap.ContainerPort+"/tcp")] = []nat.PortBinding{{HostIP: "0.0.0.0", HostPort: portMap.CommendPort}}
portMaps[nat.Port(portMap.ContainerPort+"/tcp")] = []nat.PortBinding{{HostPort: portMap.CommendPort}}
}
}
} else if portMap.Protocol == "both" {
@@ -385,7 +412,7 @@ func (ds *dockerService) DockerContainerCreate(imageName string, containerDbId s
if tContainer > 0 {
ports[nat.Port(portMap.ContainerPort+"/tcp")] = struct{}{}
if net != "host" {
portMaps[nat.Port(portMap.ContainerPort+"/tcp")] = []nat.PortBinding{{HostIP: "0.0.0.0", HostPort: portMap.CommendPort}}
portMaps[nat.Port(portMap.ContainerPort+"/tcp")] = []nat.PortBinding{{HostPort: portMap.CommendPort}}
}
}
@@ -393,7 +420,7 @@ func (ds *dockerService) DockerContainerCreate(imageName string, containerDbId s
if uContainer > 0 {
ports[nat.Port(portMap.ContainerPort+"/udp")] = struct{}{}
if net != "host" {
portMaps[nat.Port(portMap.ContainerPort+"/udp")] = []nat.PortBinding{{HostIP: "0.0.0.0", HostPort: portMap.CommendPort}}
portMaps[nat.Port(portMap.ContainerPort+"/udp")] = []nat.PortBinding{{HostPort: portMap.CommendPort}}
}
}
@@ -402,7 +429,7 @@ func (ds *dockerService) DockerContainerCreate(imageName string, containerDbId s
if uContainer > 0 {
ports[nat.Port(portMap.ContainerPort+"/udp")] = struct{}{}
if net != "host" {
portMaps[nat.Port(portMap.ContainerPort+"/udp")] = []nat.PortBinding{{HostIP: "0.0.0.0", HostPort: portMap.CommendPort}}
portMaps[nat.Port(portMap.ContainerPort+"/udp")] = []nat.PortBinding{{HostPort: portMap.CommendPort}}
}
}
}
@@ -410,12 +437,14 @@ func (ds *dockerService) DockerContainerCreate(imageName string, containerDbId s
}
var envArr []string
var showENV []string
for _, e := range m.Envs {
if strings.HasPrefix(e.Value, "$") {
envArr = append(envArr, e.Name+"="+env_helper.ReplaceDefaultENV(e.Value, MyService.System().GetTimeZone()))
continue
}
if len(e.Value) > 0 {
showENV = append(showENV, e.Name)
if e.Value == "port_map" {
envArr = append(envArr, e.Name+"="+m.PortMap)
continue
@@ -433,7 +462,7 @@ func (ds *dockerService) DockerContainerCreate(imageName string, containerDbId s
}
for _, p := range m.Devices {
if len(p.Path) > 0 {
res.Devices = append(res.Devices, container.DeviceMapping{PathOnHost: p.Path, PathInContainer: p.ContainerPath})
res.Devices = append(res.Devices, container.DeviceMapping{PathOnHost: p.Path, PathInContainer: p.ContainerPath, CgroupPermissions: "rwm"})
}
}
hostConfingBind := []string{}
@@ -442,18 +471,18 @@ func (ds *dockerService) DockerContainerCreate(imageName string, containerDbId s
for _, v := range m.Volumes {
path := v.Path
if len(path) == 0 {
path = docker.GetDir(containerDbId, v.Path)
path = docker.GetDir(m.Label, v.Path)
if len(path) == 0 {
continue
}
}
path = strings.ReplaceAll(path, "$AppID", containerDbId)
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 {
ds.log.Error("mkdir error", err)
loger.Error("Failed to create a folder", zap.Any("err", err))
continue
}
//}
@@ -490,13 +519,28 @@ func (ds *dockerService) DockerContainerCreate(imageName string, containerDbId s
// Retries: 1000,
// }
// fmt.Print(health)
if len(m.HostName) == 0 {
m.HostName = m.Label
}
config := &container.Config{
Image: imageName,
Labels: map[string]string{"origin": m.Origin, m.Origin: m.Origin},
Labels: map[string]string{"origin": m.Origin, m.Origin: m.Origin, "casaos": "casaos"},
Env: envArr,
// Healthcheck: health,
Hostname: m.HostName,
Cmd: m.Cmd,
}
hostConfig := &container.HostConfig{Resources: res, Mounts: volumes, RestartPolicy: rp, NetworkMode: container.NetworkMode(net)}
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["order"] = strconv.Itoa(MyService.App().GetCasaOSCount() + 1)
hostConfig := &container.HostConfig{Resources: res, Mounts: volumes, RestartPolicy: rp, NetworkMode: container.NetworkMode(net), Privileged: m.Privileged, CapAdd: m.CapAdd}
//if net != "host" {
config.ExposedPorts = ports
hostConfig.PortBindings = portMaps
@@ -507,7 +551,7 @@ func (ds *dockerService) DockerContainerCreate(imageName string, containerDbId s
hostConfig,
&network.NetworkingConfig{EndpointsConfig: map[string]*network.EndpointSettings{net: {NetworkID: "", Aliases: []string{}}}},
nil,
containerDbId)
m.Label)
if err != nil {
return "", err
}
@@ -690,6 +734,9 @@ func (ds *dockerService) DockerListByName(name string) (*types.Container, error)
if err != nil {
return &types.Container{}, err
}
if len(containers) == 0 {
return &types.Container{}, errors.New("not found")
}
return &containers[0], nil
}
@@ -754,7 +801,7 @@ func (ds *dockerService) DockerContainerUpdate(m model.CustomizationPostData, id
res.CPUShares = m.CpuShares
}
for _, p := range m.Devices {
res.Devices = append(res.Devices, container.DeviceMapping{PathOnHost: p.Path, PathInContainer: p.ContainerPath})
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 {
@@ -789,8 +836,8 @@ func (ds *dockerService) DockerNetworkModelList() []types.NetworkResource {
networks, _ := cli.NetworkList(context.Background(), types.NetworkListOptions{})
return networks
}
func NewDockerService(log loger2.OLog) DockerService {
return &dockerService{rootDir: command2.ExecResultStr(`source ./shell/helper.sh ;GetDockerRootDir`), log: log}
func NewDockerService() DockerService {
return &dockerService{rootDir: command2.ExecResultStr(`source ./shell/helper.sh ;GetDockerRootDir`)}
}
// ---------------------------------------test------------------------------------

33
service/down_record.go Normal file
View File

@@ -0,0 +1,33 @@
package service
import (
model2 "github.com/IceWhaleTech/CasaOS/service/model"
"gorm.io/gorm"
)
type DownRecordService interface {
AddDownRecord(m model2.PersonDownRecordDBModel)
GetDownloadListByFrom(id string) []model2.PersonDownRecordDBModel
GetDownloadListByPath(path string) (list []model2.PersonDownRecordDBModel)
}
type downRecordService struct {
db *gorm.DB
}
func (d *downRecordService) AddDownRecord(m model2.PersonDownRecordDBModel) {
d.db.Create(&m)
}
func (d *downRecordService) GetDownloadListByFrom(id string) []model2.PersonDownRecordDBModel {
var m []model2.PersonDownRecordDBModel
d.db.Model(m).Where("from = ?", id).Find(&m)
return m
}
func (d *downRecordService) GetDownloadListByPath(path string) (list []model2.PersonDownRecordDBModel) {
d.db.Where("path = ?", path).Find(&list)
return
}
func NewDownRecordService(db *gorm.DB) DownRecordService {
return &downRecordService{db: db}
}

66
service/download.go Normal file
View File

@@ -0,0 +1,66 @@
package service
import (
model2 "github.com/IceWhaleTech/CasaOS/service/model"
"gorm.io/gorm"
)
type DownloadService interface {
AddDownloadTask(m model2.PersonDownloadDBModel) //添加下载任务
EditDownloadState(m model2.PersonDownloadDBModel) //只修改状态
SaveDownload(m model2.PersonDownloadDBModel)
DelDownload(uuid string)
GetDownloadById(uuid string) model2.PersonDownloadDBModel
GetDownloadListByState(state string, t int) []model2.PersonDownloadDBModel
SetDownloadError(m model2.PersonDownloadDBModel)
GetDownloadListByPath(m model2.PersonDownloadDBModel) int
}
type downloadService struct {
db *gorm.DB
}
func (d *downloadService) GetDownloadListByPath(m model2.PersonDownloadDBModel) int {
var list []model2.PersonDownloadDBModel
d.db.Select("path").Where("path = ? AND `from` = ? AND state = 0", m.Path, m.From).Find(&list)
return len(list)
}
func (d *downloadService) AddDownloadTask(m model2.PersonDownloadDBModel) {
d.db.Create(&m)
}
func (d *downloadService) EditDownloadState(m model2.PersonDownloadDBModel) {
d.db.Model(&m).Where("uuid = ?", m.UUID).Update("state", m.State)
}
//failed during download
func (d *downloadService) SetDownloadError(m model2.PersonDownloadDBModel) {
d.db.Model(&m).Updates(m)
}
func (d *downloadService) DelDownload(uuid string) {
var m model2.PersonDownloadDBModel
d.db.Where("uuid = ?", uuid).Delete(&m)
}
func (d *downloadService) GetDownloadById(uuid string) model2.PersonDownloadDBModel {
var m model2.PersonDownloadDBModel
d.db.Model(m).Where("uuid = ?", uuid).First(&m)
return m
}
func (d *downloadService) GetDownloadListByState(state string, t int) (list []model2.PersonDownloadDBModel) {
if len(state) == 0 {
d.db.Where("type = ?", t).Find(&list)
} else {
d.db.Where("state = ? AND type= ?", state, t).Find(&list)
}
return
}
func (d *downloadService) SaveDownload(m model2.PersonDownloadDBModel) {
d.db.Save(&m)
}
func NewDownloadService(db *gorm.DB) DownloadService {
return &downloadService{db: db}
}

View File

@@ -1,38 +1,31 @@
/*
* @Author: LinkLeong link@icewhale.com
* @Date: 2021-12-20 14:15:46
* @LastEditors: LinkLeong
* @LastEditTime: 2022-06-08 15:46:36
* @FilePath: /CasaOS/service/file.go
* @Description:
* @Website: https://www.casaos.io
* Copyright (c) 2022 by icewhale, All Rights Reserved.
*/
package service
import (
"context"
"io"
"os"
"path/filepath"
"strings"
"sync"
"time"
"github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/pkg/utils/file"
)
// type InteruptReader struct {
// r io.Reader
// interupt chan int
// }
var FileQueue sync.Map
// func NewInteruptReader(r io.Reader) InteruptReader {
// return InteruptReader{
// r,
// make(chan int),
// }
// }
// func (r InteruptReader) Read(p []byte) (n int, err error) {
// if r.r == nil {
// return 0, io.EOF
// }
// select {
// case <-r.interupt:
// return r.r.Read(p)
// default:
// r.r = nil
// return 0, io.EOF
// }
// }
// func (r InteruptReader) Cancel() {
// r.interupt <- 0
// }
var OpStrArr []string
type reader struct {
ctx context.Context
@@ -83,3 +76,85 @@ func (w *writer) Write(p []byte) (n int, err error) {
return w.w.Write(p)
}
}
func FileOperate(k string) {
list, ok := FileQueue.Load(k)
if !ok {
return
}
temp := list.(model.FileOperate)
if temp.ProcessedSize > 0 {
return
}
for _, v := range temp.Item {
if temp.Type == "move" {
lastPath := v.From[strings.LastIndex(v.From, "/")+1:]
if !file.CheckNotExist(temp.To + "/" + lastPath) {
continue
}
err := os.Rename(v.From, temp.To+"/"+lastPath)
if err != nil {
continue
}
} else if temp.Type == "copy" {
err := file.CopyDir(v.From, temp.To)
if err != nil {
continue
}
} else {
continue
}
}
}
func ExecOpFile() {
len := len(OpStrArr)
if len == 0 {
return
}
if len > 1 {
len = 1
}
for i := 0; i < len; i++ {
go FileOperate(OpStrArr[i])
}
}
// file move or copy and send notify
func CheckFileStatus() {
for {
if len(OpStrArr) == 0 {
return
}
for _, v := range OpStrArr {
var total int64 = 0
item, ok := FileQueue.Load(v)
if !ok {
continue
}
temp := item.(model.FileOperate)
for i := 0; i < len(temp.Item); i++ {
if !temp.Item[i].Finished {
size, err := file.GetFileOrDirSize(temp.To + "/" + filepath.Base(temp.Item[i].From))
if err != nil {
continue
}
temp.Item[i].ProcessedSize = size
if size == temp.Item[i].Size {
temp.Item[i].Finished = true
}
total += size
} else {
total += temp.Item[i].ProcessedSize
}
}
temp.ProcessedSize = total
FileQueue.Store(v, temp)
}
time.Sleep(time.Second * 3)
}
}

155
service/friend.go Normal file
View File

@@ -0,0 +1,155 @@
package service
import (
"context"
"fmt"
"net"
"reflect"
"strconv"
"time"
"github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/pkg/config"
"github.com/IceWhaleTech/CasaOS/pkg/quic_helper"
model2 "github.com/IceWhaleTech/CasaOS/service/model"
"github.com/IceWhaleTech/CasaOS/types"
"github.com/lucas-clemente/quic-go"
uuid "github.com/satori/go.uuid"
"gorm.io/gorm"
)
type FriendService interface {
AddFriend(m model2.FriendModel)
DeleteFriend(m model2.FriendModel)
EditFriendMark(m model2.FriendModel)
EditFriendWrite(m model2.FriendModel)
EditFriendBlock(m model2.FriendModel)
GetFriendById(m model2.FriendModel) model2.FriendModel
GetFriendList() (list []model2.FriendModel)
GetFriendListRemote() (list []model2.FriendModel)
UpdateAddFriendType(m model2.FriendModel)
AgreeFrined(id string)
GetFriendByToken(token string) model2.FriendModel
UpdateOrCreate(m model2.FriendModel)
InternalInspection(ips []string, token string)
}
type friendService struct {
db *gorm.DB
}
func (p *friendService) AgreeFrined(id string) {
var m model2.FriendModel
p.db.Model(&m).Where("token = ?", id).Update("state", types.FRIENDSTATEDEFAULT)
}
func (p *friendService) AddFriend(m model2.FriendModel) {
p.db.Create(&m)
}
func (p *friendService) DeleteFriend(m model2.FriendModel) {
p.db.Where("token = ?", m.Token).Delete(&m)
}
func (p *friendService) EditFriendMark(m model2.FriendModel) {
p.db.Model(&m).Where("token = ?", m.Token).Update("mark", m.Mark)
}
func (p *friendService) EditFriendWrite(m model2.FriendModel) {
p.db.Model(&m).Where("token = ?", m.Token).Update("write", m.Write)
}
func (p *friendService) EditFriendBlock(m model2.FriendModel) {
p.db.Model(&m).Where("token = ?", m.Token).Update("block", m.Block)
}
func (p *friendService) GetFriendById(m model2.FriendModel) model2.FriendModel {
p.db.Model(m).Where("token = ?", m.Token).First(&m)
return m
}
func (p *friendService) GetFriendList() (list []model2.FriendModel) {
p.db.Select("nick_name", "avatar", "profile", "token", "state", "mark", "block", "version").Find(&list)
return list
}
func (p *friendService) GetFriendListRemote() (list []model2.FriendModel) {
p.db.Select("nick_name", "avatar", "profile", "token", "state", "mark", "block", "version").Where("internal_ip == '' OR internal_ip is null").Find(&list)
return list
}
func (p *friendService) GetFriendListInternal() (list []model2.FriendModel) {
p.db.Select("nick_name", "avatar", "profile", "token", "state", "mark", "block", "version").Where("internal_ip != ''").Find(&list)
return list
}
func (p *friendService) UpdateOrCreate(m model2.FriendModel) {
friend := model2.FriendModel{}
p.db.Where("token = ?", m.Token).First(&friend)
if reflect.DeepEqual(friend, model2.FriendModel{}) {
p.db.Create(&m)
} else {
p.db.Model(&m).Updates(m)
}
}
func (p *friendService) UpdateAddFriendType(m model2.FriendModel) {
p.db.Model(&m).Updates(m)
}
func (p *friendService) GetFriendByToken(token string) model2.FriendModel {
var m model2.FriendModel
p.db.Model(&m).Where("token = ?", token).First(&m)
return m
}
func (p *friendService) InternalInspection(ips []string, token string) {
for _, v := range ips {
fmt.Println("开始遍历 ip:", v)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
dstAddr, err := net.ResolveUDPAddr("udp", v)
if err != nil {
fmt.Println("1", err.Error())
continue
}
port, err := strconv.Atoi(config.ServerInfo.UDPPort)
if err != nil {
fmt.Println("2", err)
continue
}
srcAddr := &net.UDPAddr{
IP: net.IPv4zero, Port: port}
ticket := token
session, err := quic.DialContext(ctx, UDPConn, dstAddr, srcAddr.String(), quic_helper.GetClientTlsConfig(ticket), quic_helper.GetQUICConfig())
if err != nil {
fmt.Println("3", err, v)
continue
}
stream, err := session.OpenStreamSync(ctx)
if err != nil {
fmt.Println("4", err)
continue
}
uuid := uuid.NewV4().String()
SayHello(stream, token)
msg := model.MessageModel{
Type: types.PERSONPING,
Data: "",
From: config.ServerInfo.Token,
To: token,
UUId: uuid,
}
SendData(stream, msg)
go ReadContent(stream)
result := <-Message
fmt.Println("ping返回结果:", result, msg)
stream.Close()
if !reflect.DeepEqual(result, model.MessageModel{}) && result.Data.(string) == token && result.From == token {
fmt.Println("获取到正确的ip", v)
UDPAddressMap[result.From] = v
p.db.Model(&model2.FriendModel{}).Where("token = ?", token).Update("internal_ip", v)
return
}
}
}
func NewFriendService(db *gorm.DB) FriendService {
return &friendService{db: db}
}

View File

@@ -0,0 +1,20 @@
package model
import (
"time"
)
type ApplicationModel struct {
Id int `gorm:"column:id;primary_key" json:"id"`
Name string `json:"name"`
Icon string `json:"icon"`
State int `json:"state"`
Type string `json:"type"`
Order int `json:"order"`
CreatedAt time.Time `gorm:"<-:create" json:"created_at"`
UpdatedAt time.Time `gorm:"<-:create;<-:update" json:"updated_at"`
}
func (p *ApplicationModel) TableName() string {
return "o_application"
}

View File

@@ -1,11 +1,18 @@
/*
* @Author: LinkLeong link@icewhale.com
* @Date: 2022-05-13 18:15:46
* @LastEditors: LinkLeong
* @LastEditTime: 2022-05-30 17:33:21
* @FilePath: /CasaOS/service/model/o_container.go
* @Description:
* @Website: https://www.casaos.io
* Copyright (c) 2022 by icewhale, All Rights Reserved.
*/
package model
import (
"github.com/IceWhaleTech/CasaOS/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"`
@@ -41,6 +48,10 @@ type AppListDBModel struct {
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 {
@@ -48,15 +59,21 @@ func (p *AppListDBModel) TableName() string {
}
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"`
UpTime string `json:"up_time"`
Slogan string `json:"slogan"`
Rely model.MapStrings `json:"rely"` //[{"mysql":"id"},{"mysql":"id"}]
Type string `json:"type"`
//Rely model.MapStrings `json:"rely"` //[{"mysql":"id"},{"mysql":"id"}]
Image string `json:"image"`
Volumes string `json:"volumes"`
NewVersion bool `json:"new_version"`
Host string `json:"host"`
Protocol string `json:"protocol"`
}

View File

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

View File

@@ -0,0 +1,16 @@
package model
type PersonDownRecordDBModel struct {
UUID string `gorm:"column:uuid;primary_key" json:"uuid"`
Name string `json:"name"` //file name
Type int `json:"type"`
Size int64 `json:"size"` //file size
Downloader string `json:"downloader"` //Error message
Path string `json:"path"`
Created int64 `gorm:"autoCreateTime" json:"created"`
Updated int64 `gorm:"autoCreateTime;autoUpdateTime" json:"updated"`
}
func (p *PersonDownRecordDBModel) TableName() string {
return "o_person_down_record"
}

View File

@@ -0,0 +1,24 @@
package model
type PersonDownloadDBModel struct {
UUID string `gorm:"column:uuid;primary_key" json:"uuid"`
State int `json:"state"` //
Type int `json:"type"` //defult 0
Name string `json:"name"` //file name
Size int64 `json:"size"` //file size
BlockSize int `json:"block_size"` //Size of each file block
Length int `json:"length"` //slice length
Hash string `json:"hash"` //File hash value
Error string `json:"error"` //
From string `json:"from"` //Error message
Path string `json:"path"` //Full path to the file
Already int `json:"already" gorm:"-"` //Folder blocks that have been downloaded
LocalPath string `json:"local_path"` //The address where the file is saved after download
Duration int64 `json:"duration" gorm:"-"` //Length of time
Created int64 `gorm:"autoCreateTime" json:"created"`
Updated int64 `gorm:"autoCreateTime;autoUpdateTime" json:"updated"`
}
func (p *PersonDownloadDBModel) TableName() string {
return "o_person_download"
}

21
service/model/o_friend.go Normal file
View File

@@ -0,0 +1,21 @@
package model
type FriendModel struct {
State int `json:"state"`
CreatedAt int64 `gorm:"autoCreateTime" json:"created_at"`
UpdatedAt int64 `gorm:"autoCreateTime;autoUpdateTime" json:"updated_at"`
NickName string `json:"nick_name"`
Mark string `json:"mark"` //Remarks
Block bool `json:"block"` //Disable or not
Avatar string `json:"avatar"` //User avatar
Token string `gorm:"column:token;primary_key" json:"token"`
Profile string `json:"profile"` //Description
OnLine bool `json:"on_line" gorm:"-"`
Version int `json:"version"`
Write bool `json:"write"`
LocalIP string `json:"local_ip"`
}
func (p *FriendModel) TableName() string {
return "o_friend"
}

View File

@@ -6,9 +6,10 @@ type AppNotify struct {
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
Id string `json:"id"`
Type int `json:"type"` // 1:显示即为已读 2:info 3:warning 4:error 5:success
Type int `json:"type"`
Icon string `json:"icon"`
Name string `json:"name"`
Class int `json:"class"`
CustomId string `gorm:"column:custom_id;primary_key" json:"custom_id"`
}

View File

@@ -1,26 +1,336 @@
package service
import (
json2 "encoding/json"
"fmt"
"time"
model2 "github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/model/notify"
"github.com/IceWhaleTech/CasaOS/service/model"
"github.com/IceWhaleTech/CasaOS/types"
"github.com/ambelovsky/gosf"
socketio "github.com/googollee/go-socket.io"
"github.com/gorilla/websocket"
"github.com/shirou/gopsutil/v3/mem"
"gorm.io/gorm"
)
var NotifyMsg chan notify.Message
var ClientCount int = 0
type NotifyServer interface {
GetLog(id string) model.AppNotify
AddLog(log model.AppNotify)
UpdateLog(log model.AppNotify)
UpdateLogByCustomId(log model.AppNotify)
DelLog(id string)
GetList() (list []model.AppNotify)
GetList(c int) (list []model.AppNotify)
MarkRead(id string, state int)
// SendText(m model.AppNotify)
SendUninstallAppBySocket(app notify.Application)
SendNetInfoBySocket(netList []model2.IOCountersStat)
SendCPUInfoBySocket(cpu map[string]interface{})
SendMemInfoBySocket(mem *mem.VirtualMemoryStat)
SendUSBInfoBySocket(list []model2.DriveUSB)
SendDiskInfoBySocket(disk model2.Summary)
SendPersonStatusBySocket(status notify.Person)
SendFileOperateNotify(nowSend bool)
SendInstallAppBySocket(app notify.Application)
}
type notifyServer struct {
db *gorm.DB
}
func (i notifyServer) GetList() (list []model.AppNotify) {
i.db.Where("state=? or state=?", types.NOTIFY_DYNAMICE, types.NOTIFY_UNREAD).Find(&list)
// Send periodic broadcast messages
func (i *notifyServer) SendFileOperateNotify(nowSend bool) {
if nowSend {
len := 0
FileQueue.Range(func(k, v interface{}) bool {
len++
return true
})
model := notify.NotifyModel{}
listMsg := make(map[string]interface{})
if len == 0 {
model.Data = []string{}
listMsg["file_operate"] = model
msg := gosf.Message{}
msg.Success = true
msg.Body = listMsg
msg.Text = "file_operate"
notify := notify.Message{}
notify.Path = "file_operate"
notify.Msg = msg
NotifyMsg <- notify
return
}
model.State = "NORMAL"
list := []notify.File{}
OpStrArrbak := OpStrArr
for _, v := range OpStrArrbak {
tempItem, ok := FileQueue.Load(v)
temp := tempItem.(model2.FileOperate)
if !ok {
continue
}
task := notify.File{}
task.Id = v
task.ProcessedSize = temp.ProcessedSize
task.TotalSize = temp.TotalSize
task.To = temp.To
task.Type = temp.Type
if task.ProcessedSize == 0 {
task.Status = "STARTING"
} else {
task.Status = "PROCESSING"
}
if temp.ProcessedSize >= temp.TotalSize {
task.Finished = true
task.Status = "FINISHED"
FileQueue.Delete(v)
OpStrArr = OpStrArr[1:]
go ExecOpFile()
list = append(list, task)
continue
}
for _, v := range temp.Item {
if v.Size != v.ProcessedSize {
task.ProcessingPath = v.From
break
}
}
list = append(list, task)
}
model.Data = list
listMsg["file_operate"] = model
msg := gosf.Message{}
msg.Success = true
msg.Body = listMsg
msg.Text = "file_operate"
notify := notify.Message{}
notify.Path = "file_operate"
notify.Msg = msg
NotifyMsg <- notify
} else {
for {
len := 0
FileQueue.Range(func(k, v interface{}) bool {
len++
return true
})
if len == 0 {
return
}
listMsg := make(map[string]interface{})
model := notify.NotifyModel{}
model.State = "NORMAL"
list := []notify.File{}
OpStrArrbak := OpStrArr
for _, v := range OpStrArrbak {
tempItem, ok := FileQueue.Load(v)
temp := tempItem.(model2.FileOperate)
if !ok {
continue
}
task := notify.File{}
task.Id = v
task.ProcessedSize = temp.ProcessedSize
task.TotalSize = temp.TotalSize
task.To = temp.To
task.Type = temp.Type
if task.ProcessedSize == 0 {
task.Status = "STARTING"
} else {
task.Status = "PROCESSING"
}
if temp.ProcessedSize >= temp.TotalSize {
task.Finished = true
task.Status = "FINISHED"
FileQueue.Delete(v)
OpStrArr = OpStrArr[1:]
go ExecOpFile()
list = append(list, task)
continue
}
for _, v := range temp.Item {
if v.Size != v.ProcessedSize {
task.ProcessingPath = v.From
break
}
}
list = append(list, task)
}
model.Data = list
listMsg["file_operate"] = model
msg := gosf.Message{}
msg.Success = true
msg.Body = listMsg
msg.Text = "file_operate"
notify := notify.Message{}
notify.Path = "file_operate"
notify.Msg = msg
NotifyMsg <- notify
time.Sleep(time.Second * 3)
}
}
}
func (i *notifyServer) SendPersonStatusBySocket(status notify.Person) {
body := make(map[string]interface{})
body["data"] = status
msg := gosf.Message{}
msg.Body = body
msg.Success = true
msg.Text = "person_status"
notify := notify.Message{}
notify.Path = "person_status"
notify.Msg = msg
NotifyMsg <- notify
}
func (i *notifyServer) SendDiskInfoBySocket(disk model2.Summary) {
body := make(map[string]interface{})
body["data"] = disk
msg := gosf.Message{}
msg.Body = body
msg.Success = true
msg.Text = "sys_disk"
notify := notify.Message{}
notify.Path = "sys_disk"
notify.Msg = msg
NotifyMsg <- notify
}
func (i *notifyServer) SendUSBInfoBySocket(list []model2.DriveUSB) {
body := make(map[string]interface{})
body["data"] = list
msg := gosf.Message{}
msg.Body = body
msg.Success = true
msg.Text = "sys_usb"
notify := notify.Message{}
notify.Path = "sys_usb"
notify.Msg = msg
NotifyMsg <- notify
}
func (i *notifyServer) SendMemInfoBySocket(mem *mem.VirtualMemoryStat) {
body := make(map[string]interface{})
body["data"] = mem
msg := gosf.Message{}
msg.Body = body
msg.Success = true
msg.Text = "sys_mem"
notify := notify.Message{}
notify.Path = "sys_mem"
notify.Msg = msg
NotifyMsg <- notify
}
func (i *notifyServer) SendInstallAppBySocket(app notify.Application) {
body := make(map[string]interface{})
body["data"] = app
msg := gosf.Message{}
msg.Body = body
msg.Success = true
msg.Text = "app_install"
notify := notify.Message{}
notify.Path = "app_install"
notify.Msg = msg
NotifyMsg <- notify
}
func (i *notifyServer) SendCPUInfoBySocket(cpu map[string]interface{}) {
body := make(map[string]interface{})
body["data"] = cpu
msg := gosf.Message{}
msg.Body = body
msg.Success = true
msg.Text = "sys_cpu"
notify := notify.Message{}
notify.Path = "sys_cpu"
notify.Msg = msg
NotifyMsg <- notify
}
func (i *notifyServer) SendNetInfoBySocket(netList []model2.IOCountersStat) {
body := make(map[string]interface{})
body["data"] = netList
msg := gosf.Message{}
msg.Body = body
msg.Success = true
msg.Text = "sys_net"
notify := notify.Message{}
notify.Path = "sys_net"
notify.Msg = msg
NotifyMsg <- notify
}
func (i *notifyServer) SendUninstallAppBySocket(app notify.Application) {
body := make(map[string]interface{})
body["data"] = app
msg := gosf.Message{}
msg.Body = body
msg.Success = true
msg.Text = "app_uninstall"
notify := notify.Message{}
notify.Path = "app_uninstall"
notify.Msg = msg
NotifyMsg <- notify
}
func (i *notifyServer) SSR() {
server := socketio.NewServer(nil)
fmt.Println(server)
}
func (i notifyServer) GetList(c int) (list []model.AppNotify) {
i.db.Where("class = ?", c).Where(i.db.Where("state = ?", types.NOTIFY_DYNAMICE).Or("state = ?", types.NOTIFY_UNREAD)).Find(&list)
return
}
@@ -31,20 +341,92 @@ func (i *notifyServer) AddLog(log model.AppNotify) {
func (i *notifyServer) UpdateLog(log model.AppNotify) {
i.db.Save(&log)
}
func (i *notifyServer) UpdateLogByCustomId(log model.AppNotify) {
if len(log.CustomId) == 0 {
return
}
i.db.Model(&model.AppNotify{}).Select("*").Where("custom_id = ? ", log.CustomId).Updates(log)
}
func (i *notifyServer) GetLog(id string) model.AppNotify {
var log model.AppNotify
i.db.Where("custom_id = ? ", id).First(&log)
return log
}
func (i *notifyServer) MarkRead(id string, state int) {
i.db.Update("state=", state).Where("custom_id = ? ", id)
if id == "0" {
i.db.Model(&model.AppNotify{}).Where("1 = ?", 1).Update("state", state)
return
}
i.db.Model(&model.AppNotify{}).Where("id = ? ", id).Update("state", state)
}
func (i *notifyServer) DelLog(id string) {
var log model.AppNotify
i.db.Where("custom_id = ?", id).Delete(&log)
}
func SendMeg() {
// for {
// mt, message, err := ws.ReadMessage()
// if err != nil {
// break
// }
// notify := model.NotifyMssage{}
// json2.Unmarshal(message, &notify)
// if notify.Type == "read" {
// service.MyService.Notify().MarkRead(notify.Data, types.NOTIFY_READ)
// }
// if notify.Type == "app" {
// go func(ws *websocket.Conn) {
for {
list := MyService.Notify().GetList(types.NOTIFY_APP)
json, _ := json2.Marshal(list)
if len(list) > 0 {
var temp []*websocket.Conn
for _, v := range WebSocketConns {
err := v.WriteMessage(1, json)
if err == nil {
temp = append(temp, v)
}
}
WebSocketConns = temp
for _, v := range list {
MyService.Notify().MarkRead(v.Id, types.NOTIFY_READ)
}
}
if len(WebSocketConns) == 0 {
SocketRun = false
}
time.Sleep(time.Second * 2)
}
// }(ws)
// }
// }
}
// func (i notifyServer) SendText(m model.AppNotify) {
// list := []model.AppNotify{}
// list = append(list, m)
// json, _ := json2.Marshal(list)
// var temp []*websocket.Conn
// for _, v := range WebSocketConns {
// err := v.WriteMessage(1, json)
// if err == nil {
// temp = append(temp, v)
// }
// }
// WebSocketConns = temp
// if len(WebSocketConns) == 0 {
// SocketRun = false
// }
// }
func NewNotifyService(db *gorm.DB) NotifyServer {
return &notifyServer{db: db}
}

482
service/person.go Normal file
View File

@@ -0,0 +1,482 @@
package service
import (
"bufio"
"context"
"encoding/json"
"fmt"
"io"
"net"
"os"
"reflect"
"strconv"
"time"
"github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/pkg/config"
"github.com/IceWhaleTech/CasaOS/pkg/quic_helper"
"github.com/IceWhaleTech/CasaOS/pkg/utils"
"github.com/IceWhaleTech/CasaOS/pkg/utils/file"
httper2 "github.com/IceWhaleTech/CasaOS/pkg/utils/httper"
"github.com/IceWhaleTech/CasaOS/pkg/utils/ip_helper"
port2 "github.com/IceWhaleTech/CasaOS/pkg/utils/port"
model2 "github.com/IceWhaleTech/CasaOS/service/model"
"github.com/IceWhaleTech/CasaOS/types"
"github.com/lucas-clemente/quic-go"
"gorm.io/gorm"
)
type PersonService interface {
GetPersionInfo(token string) (m model.PersionModel, err error)
GetPersionNetWorkTypeDetection() string
}
type personService struct {
db *gorm.DB
}
var IpInfo model.PersionModel
var CancelList map[string]string
var InternalInspection map[string][]string
func PushIpInfo(token string) {
m := model.PersionModel{}
m.Ips = ip_helper.GetDeviceAllIP("")
m.Token = token
b, _ := json.Marshal(m)
if reflect.DeepEqual(IpInfo, m) {
return
}
head := make(map[string]string)
infoS := httper2.Post(config.ServerInfo.Handshake+"/v1/update", b, "application/json", head)
fmt.Println(infoS)
}
func (p *personService) GetPersionInfo(token string) (m model.PersionModel, err error) {
infoS := httper2.Get(config.ServerInfo.Handshake+"/v1/ips/"+token, nil)
err = json.Unmarshal([]byte(infoS), &m)
return
}
func (p *personService) GetPersionNetWorkTypeDetection() string {
data := make(chan string)
list := []string{"stun.l.google.com", "stun1.l.google.com", "stun2.l.google.com", "stun.sipgate.net"}
for _, v := range list {
go utils.GetNetWorkTypeDetection(data, v)
}
result := <-data
close(data)
return result
}
func NewPersonService(db *gorm.DB) PersonService {
return &personService{db: db}
}
//=======================================================================================================================================================================
var StreamList map[string]quic.Stream
var ServiceMessage chan model.MessageModel
func UDPService() {
port := 0
if len(config.ServerInfo.UDPPort) > 0 {
port, _ = strconv.Atoi(config.ServerInfo.UDPPort)
if port != 0 && !port2.IsPortAvailable(port, "udp") {
port = 0
}
}
srcAddr := &net.UDPAddr{
IP: net.IPv4zero, Port: port}
var err error
UDPConn, err = net.ListenUDP("udp", srcAddr)
if err != nil {
fmt.Println(err)
}
listener, err := quic.Listen(UDPConn, quic_helper.GetGenerateTLSConfig(config.ServerInfo.Token), quic_helper.GetQUICConfig())
if err != nil {
fmt.Println(err)
}
defer listener.Close()
ctx := context.Background()
acceptFailures := 0
const maxAcceptFailures = 10
if err != nil {
panic(err)
}
for {
select {
case <-ctx.Done():
fmt.Println(ctx.Err())
return
default:
}
session, err := listener.Accept(ctx)
if err != nil {
fmt.Println("Listen (BEP/quic): Accepting connection:", err)
acceptFailures++
if acceptFailures > maxAcceptFailures {
// Return to restart the listener, because something
// seems permanently damaged.
fmt.Println(err)
return
}
// Slightly increased delay for each failure.
time.Sleep(time.Duration(acceptFailures) * time.Second)
continue
}
acceptFailures = 0
streamCtx, cancel := context.WithTimeout(ctx, time.Second*10)
stream, err := session.AcceptStream(streamCtx)
cancel()
if err != nil {
fmt.Println("failed to accept stream from %s: %v", session.RemoteAddr(), err)
_ = session.CloseWithError(1, err.Error())
continue
}
// prefixByte := make([]byte, 4)
// c1, err := io.ReadFull(stream, prefixByte)
// fmt.Println(c1, err)
// prefixLength, err := strconv.Atoi(string(prefixByte))
// if err != nil {
// fmt.Println(err)
// }
// messageByte := make([]byte, prefixLength)
// t, err := io.ReadFull(stream, messageByte)
// fmt.Println(t, err)
// m := model.MessageModel{}
// err = json.Unmarshal(messageByte, &m)
// if err != nil {
// fmt.Println(err)
// }
go ProcessingContent(stream)
}
}
//处理内容
func ProcessingContent(stream quic.Stream) {
for {
prefixByte := make([]byte, 6)
_, err := io.ReadFull(stream, prefixByte)
if err != nil {
fmt.Println(err)
return
}
prefixLength, err := strconv.Atoi(string(prefixByte))
if err != nil {
fmt.Println(err)
}
messageByte := make([]byte, prefixLength)
_, err = io.ReadFull(stream, messageByte)
if err != nil {
return
}
m := model.MessageModel{}
err = json.Unmarshal(messageByte, &m)
if err != nil {
fmt.Println(err)
}
if m.Type == types.PERSONHELLO {
//nothing
continue
} else if m.Type == types.PERSONDIRECTORY {
friend := model2.FriendModel{}
friend.Token = m.From
var list []model.Path
rFriend := MyService.Friend().GetFriendById(friend)
if !reflect.DeepEqual(rFriend, model2.FriendModel{Token: m.From}) && !rFriend.Block {
if m.Data.(string) == "" || m.Data.(string) == "/" {
for _, v := range config.FileSettingInfo.ShareDir {
//tempList := MyService.ZiMa().GetDirPath(v)
temp := MyService.ZiMa().GetDirPathOne(v)
list = append(list, temp)
}
} else {
list = MyService.ZiMa().GetDirPath(m.Data.(string))
}
} else {
list = []model.Path{}
}
if rFriend.Write {
for i := 0; i < len(list); i++ {
list[i].Write = true
}
}
m.To = m.From
m.Data = list
m.From = config.ServerInfo.Token
SendData(stream, m)
break
} else if m.Type == types.PERSONDOWNLOAD {
SendFileData(stream, m.Data.(string), m.From, m.UUId, types.PERSONDOWNLOAD)
break
} else if m.Type == types.PERSONADDFRIEND {
friend := model2.FriendModel{}
dataModelByte, _ := json.Marshal(m.Data)
err := json.Unmarshal(dataModelByte, &friend)
if err != nil {
fmt.Println(err)
continue
}
go MyService.Friend().UpdateOrCreate(friend)
mi := model2.FriendModel{}
mi.Avatar = config.UserInfo.Avatar
mi.Profile = config.UserInfo.Description
mi.NickName = config.UserInfo.NickName
m.To = m.From
m.Data = mi
m.Type = types.PERSONADDFRIEND
m.From = config.ServerInfo.Token
SendData(stream, m)
break
} else if m.Type == types.PERSONCONNECTION {
if len(m.Data.(string)) > 0 {
fmt.Println("设置ip", m.Data.(string))
UDPAddressMap[m.From] = m.Data.(string)
} else {
delete(UDPAddressMap, m.From)
}
// mi := model2.FriendModel{}
// mi.Avatar = config.UserInfo.Avatar
// mi.Profile = config.UserInfo.Description
// mi.NickName = config.UserInfo.NickName
// mi.Token = config.ServerInfo.Token
user := MyService.Casa().GetUserInfoByShareId(m.From)
//好友申请 //不是好友
friend := model2.FriendModel{}
friend.Token = m.From
friend.Avatar = user.Avatar
friend.Block = false
friend.NickName = user.NickName
friend.Profile = user.Avatar
friend.Write = false
friend.Version = user.Version
if len(config.UserInfo.Public) > 0 {
friend.State = types.FRIENDSTATEREQUEST
}
MyService.Friend().AddFriend(friend)
msg := model.MessageModel{}
msg.Type = types.PERSONHELLO
msg.Data = ""
msg.To = m.From
msg.From = config.ServerInfo.Token
msg.UUId = m.UUId
Dial(msg, false)
//agree user
if len(config.UserInfo.Public) == 0 {
msg.Type = types.PERSONAGREEFRIEND
msg.Data = ""
msg.To = m.From
msg.From = config.ServerInfo.Token
msg.UUId = m.UUId
Dial(msg, true)
}
break
} else if m.Type == types.PERSONAGREEFRIEND {
MyService.Friend().AgreeFrined(m.From)
break
} else if m.Type == types.PERSONCANCEL {
CancelList[m.UUId] = "cancel"
break
} else if m.Type == types.PERSONSUMMARY {
Summary(m, "upload")
continue
} else if m.Type == types.PERSONUPLOAD {
//TODO:检查是否存在如果存在直接结束
task := model2.PersonDownloadDBModel{}
task.UUID = m.UUId
task.LocalPath = m.Data.(string)
MyService.Download().AddDownloadTask(task)
friend := MyService.Friend().GetFriendById(model2.FriendModel{Token: m.From})
if friend.Write {
continue
} else {
break
}
} else if m.Type == types.PERSONUPLOADDATA {
r := SaveFile(m, stream)
if r {
break
}
continue
} else if m.Type == types.PERSONINTERNALINSPECTION {
fmt.Println("内网测试")
var ips []string
dataModelByte, _ := json.Marshal(m.Data)
err := json.Unmarshal(dataModelByte, &ips)
if err != nil {
fmt.Println(err)
break
}
go MyService.Friend().InternalInspection(ips, m.From)
} else if m.Type == types.PERSONPING {
fmt.Println("来自", m.From, "的ping", m.Data)
msg := m
m.To = m.From
m.Data = config.ServerInfo.Token
m.From = config.ServerInfo.Token
SendData(stream, m)
var ips []string
dataModelByte, _ := json.Marshal(msg.Data)
err := json.Unmarshal(dataModelByte, &ips)
if err != nil {
fmt.Println(err)
break
}
backIP := false
if v, ok := UDPAddressMap[msg.From]; ok {
for _, ip := range ips {
if ip == v {
backIP = true
break
}
}
}
if !backIP {
fmt.Println("检测需要查询ip", msg.From)
go MyService.Friend().InternalInspection(ips, msg.From)
}
break
} else if m.Type == types.PERSONIMAGETHUMBNAIL {
m.To = m.From
if data, err := file.GetImage(m.Data.(string), 100, 0); err == nil {
m.Data = data
} else {
m.Data = ""
}
m.From = config.ServerInfo.Token
SendData(stream, m)
break
} else {
//不应有不做返回的数据
//ServiceMessage <- m
break
}
}
stream.Close()
}
//文件分片发送
func SendFileData(stream quic.Stream, filePath, to, uuid, t string) error {
summary := model.FileSummaryModel{}
msg := model.MessageModel{}
msg.Type = types.PERSONSUMMARY
msg.From = config.ServerInfo.Token
msg.To = to
msg.UUId = uuid
fStat, err := os.Stat(filePath)
if err != nil {
summary.Message = err.Error()
msg.Data = summary
summaryByte, _ := json.Marshal(msg)
summaryPrefixLength := file.PrefixLength(len(summaryByte))
summaryData := append(summaryPrefixLength, summaryByte...)
stream.Write(summaryData)
return err
}
blockSize, length := file.GetBlockInfo(fStat.Size())
f, err := os.Open(filePath)
if err != nil {
summary.Message = err.Error()
msg.Data = summary
summaryByte, _ := json.Marshal(msg)
summaryPrefixLength := file.PrefixLength(len(summaryByte))
summaryData := append(summaryPrefixLength, summaryByte...)
stream.Write(summaryData)
return err
}
//send file summary first
summary.BlockSize = blockSize
summary.Hash = file.GetHashByPath(filePath)
summary.Length = length
summary.Name = fStat.Name()
summary.Size = fStat.Size()
msg.Data = summary
summaryByte, _ := json.Marshal(msg)
summaryPrefixLength := file.PrefixLength(len(summaryByte))
summaryData := append(summaryPrefixLength, summaryByte...)
stream.Write(summaryData)
bufferedReader := bufio.NewReader(f)
buf := make([]byte, blockSize)
defer stream.Close()
for i := 0; i < length; i++ {
tran := model.TranFileModel{}
n, err := bufferedReader.Read(buf)
if err == io.EOF {
fmt.Println("读取完毕", err)
}
tran.Hash = file.GetHashByContent(buf[:n])
tran.Index = i
tran.Length = length
fileMsg := model.MessageModel{}
fileMsg.Type = t
fileMsg.Data = tran
fileMsg.From = config.ServerInfo.Token
fileMsg.To = to
fileMsg.UUId = uuid
b, _ := json.Marshal(fileMsg)
prefixLength := file.PrefixLength(len(b))
dataLength := file.DataLength(len(buf[:n]))
data := append(append(append(prefixLength, b...), dataLength...), buf[:n]...)
if _, ok := CancelList[uuid]; ok {
delete(CancelList, uuid)
return nil
}
stream.Write(data)
}
record := model2.PersonDownRecordDBModel{}
record.UUID = uuid
record.Name = f.Name()
record.Downloader = to
record.Path = filePath
record.Size = fStat.Size()
record.Type = types.PERSONFILEDOWNLOAD
if t == types.PERSONUPLOADDATA {
record.Type = types.PERSONFILEUPLOAD
}
MyService.DownRecord().AddDownRecord(record)
return nil
}

View File

@@ -1,7 +1,16 @@
/*
* @Author: LinkLeong link@icewhale.com
* @Date: 2021-09-30 18:18:14
* @LastEditors: LinkLeong
* @LastEditTime: 2022-06-02 18:00:57
* @FilePath: /CasaOS/service/rely.go
* @Description:
* @Website: https://www.casaos.io
* Copyright (c) 2022 by icewhale, All Rights Reserved.
*/
package service
import (
loger2 "github.com/IceWhaleTech/CasaOS/pkg/utils/loger"
model2 "github.com/IceWhaleTech/CasaOS/service/model"
"gorm.io/gorm"
)
@@ -14,7 +23,6 @@ type RelyService interface {
type relyService struct {
db *gorm.DB
log loger2.OLog
}
func (r *relyService) Create(rely model2.RelyDBModel) {
@@ -35,6 +43,6 @@ func (r *relyService) Delete(id string) {
r.db.Where("custom_id = ?", id).Delete(&c)
}
func NewRelyService(db *gorm.DB, log loger2.OLog) RelyService {
return &relyService{db: db, log: log}
func NewRelyService(db *gorm.DB) RelyService {
return &relyService{db: db}
}

View File

@@ -1,7 +1,7 @@
package service
import (
loger2 "github.com/IceWhaleTech/CasaOS/pkg/utils/loger"
"github.com/gorilla/websocket"
"github.com/patrickmn/go-cache"
"gorm.io/gorm"
)
@@ -10,15 +10,18 @@ var Cache *cache.Cache
var MyService Repository
var WebSocketConns []*websocket.Conn
var NewVersionApp map[string]string
var SocketRun bool
type Repository interface {
App() AppService
DDNS() DDNSService
User() UserService
Docker() DockerService
//Redis() RedisService
ZeroTier() ZeroTierService
ZiMa() ZiMaService
OAPI() CasaService
Casa() CasaService
Disk() DiskService
Notify() NotifyServer
ShareDirectory() ShareDirService
@@ -27,27 +30,33 @@ type Repository interface {
System() SystemService
Shortcuts() ShortcutsService
Search() SearchService
Person() PersonService
Friend() FriendService
Download() DownloadService
DownRecord() DownRecordService
}
func NewService(db *gorm.DB, log loger2.OLog) Repository {
func NewService(db *gorm.DB) Repository {
return &store{
app: NewAppService(db, log),
ddns: NewDDNSService(db, log),
app: NewAppService(db),
user: NewUserService(),
docker: NewDockerService(log),
docker: NewDockerService(),
//redis: NewRedisService(rp),
zerotier: NewZeroTierService(),
zima: NewZiMaService(),
oapi: NewOasisService(),
disk: NewDiskService(log, db),
casa: NewCasaService(),
disk: NewDiskService(db),
notify: NewNotifyService(db),
shareDirectory: NewShareDirService(db, log),
task: NewTaskService(db, log),
rely: NewRelyService(db, log),
system: NewSystemService(log),
shareDirectory: NewShareDirService(db),
task: NewTaskService(db),
rely: NewRelyService(db),
system: NewSystemService(),
shortcuts: NewShortcutsService(db),
search: NewSearchService(),
person: NewPersonService(db),
friend: NewFriendService(db),
download: NewDownloadService(db),
downrecord: NewDownRecordService(db),
}
}
@@ -57,9 +66,8 @@ type store struct {
ddns DDNSService
user UserService
docker DockerService
zerotier ZeroTierService
zima ZiMaService
oapi CasaService
casa CasaService
disk DiskService
notify NotifyServer
shareDirectory ShareDirService
@@ -68,14 +76,31 @@ type store struct {
system SystemService
shortcuts ShortcutsService
search SearchService
person PersonService
friend FriendService
download DownloadService
downrecord DownRecordService
}
func (c *store) DownRecord() DownRecordService {
return c.downrecord
}
func (c *store) Download() DownloadService {
return c.download
}
func (c *store) Friend() FriendService {
return c.friend
}
func (c *store) Rely() RelyService {
return c.rely
}
func (c *store) Shortcuts() ShortcutsService {
return c.shortcuts
}
func (c *store) Person() PersonService {
return c.person
}
func (c *store) System() SystemService {
return c.system
}
@@ -100,14 +125,11 @@ func (c *store) Docker() DockerService {
return c.docker
}
func (c *store) ZeroTier() ZeroTierService {
return c.zerotier
}
func (c *store) ZiMa() ZiMaService {
return c.zima
}
func (c *store) OAPI() CasaService {
return c.oapi
func (c *store) Casa() CasaService {
return c.casa
}
func (c *store) Disk() DiskService {

View File

@@ -1,13 +1,15 @@
package service
import (
"github.com/IceWhaleTech/CasaOS/pkg/config"
"github.com/IceWhaleTech/CasaOS/pkg/utils/command"
loger2 "github.com/IceWhaleTech/CasaOS/pkg/utils/loger"
"github.com/IceWhaleTech/CasaOS/service/model"
"gorm.io/gorm"
"os"
"strconv"
"github.com/IceWhaleTech/CasaOS/pkg/config"
"github.com/IceWhaleTech/CasaOS/pkg/utils/command"
"github.com/IceWhaleTech/CasaOS/pkg/utils/loger"
"github.com/IceWhaleTech/CasaOS/service/model"
"go.uber.org/zap"
"gorm.io/gorm"
)
type ShareDirService interface {
@@ -21,7 +23,6 @@ type ShareDirService interface {
type shareDirService struct {
db *gorm.DB
log loger2.OLog
}
func (s *shareDirService) List(desc bool) []model.ShareDirDBModel {
@@ -305,7 +306,7 @@ func (s *shareDirService) UpConfig() {
// /etc/samba/smb.conf
f, err := os.OpenFile("/etc/samba/smb.conf", os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644)
if err != nil {
s.log.Error("file create failed. err: " + err.Error())
loger.Error("Failed to create file", zap.Any("err", err))
} else {
defer f.Close()
f.WriteString(str)
@@ -318,6 +319,6 @@ func (s *shareDirService) Info(id string) model.ShareDirDBModel {
return m
}
func NewShareDirService(db *gorm.DB, log loger2.OLog) ShareDirService {
return &shareDirService{db: db, log: log}
func NewShareDirService(db *gorm.DB) ShareDirService {
return &shareDirService{db: db}
}

View File

@@ -1,12 +1,19 @@
package service
import (
"fmt"
"io/ioutil"
net2 "net"
"os"
"strconv"
"github.com/IceWhaleTech/CasaOS/pkg/config"
command2 "github.com/IceWhaleTech/CasaOS/pkg/utils/command"
"github.com/IceWhaleTech/CasaOS/pkg/utils/file"
"github.com/IceWhaleTech/CasaOS/pkg/utils/loger"
"github.com/shirou/gopsutil/v3/cpu"
"github.com/shirou/gopsutil/v3/mem"
"github.com/shirou/gopsutil/v3/net"
)
type SystemService interface {
@@ -17,11 +24,49 @@ type SystemService interface {
UpdateAssist()
UpSystemPort(port string)
GetTimeZone() string
UpdateUSBAutoMount(state string)
ExecUSBAutoMountShell(state string)
UpAppOrderFile(str string)
GetAppOrderFile() []byte
GetNet(physics bool) []string
GetNetInfo() []net.IOCountersStat
GetCpuCoreNum() int
GetCpuPercent() float64
GetMemInfo() *mem.VirtualMemoryStat
}
type systemService struct {
log loger.OLog
}
func (c *systemService) GetMemInfo() *mem.VirtualMemoryStat {
memInfo, _ := mem.VirtualMemory()
memInfo.UsedPercent, _ = strconv.ParseFloat(fmt.Sprintf("%.1f", memInfo.UsedPercent), 64)
return memInfo
}
func (c *systemService) GetCpuPercent() float64 {
percent, _ := cpu.Percent(0, false)
value, _ := strconv.ParseFloat(fmt.Sprintf("%.1f", percent[0]), 64)
return value
}
func (c *systemService) GetCpuCoreNum() int {
count, _ := cpu.Counts(false)
return count
}
func (c *systemService) GetNetInfo() []net.IOCountersStat {
parts, _ := net.IOCounters(true)
return parts
}
func (c *systemService) GetNet(physics bool) []string {
t := "1"
if physics {
t = "2"
}
return command2.ExecResultStrArray("source " + config.AppInfo.ProjectPath + "/shell/helper.sh ;GetNetCard " + t)
}
func (s *systemService) UpdateSystemVersion(version string) {
//command2.OnlyExec(config.AppInfo.ProjectPath + "/shell/tool.sh -r " + version)
//s.log.Error(config.AppInfo.ProjectPath + "/shell/tool.sh -r " + version)
@@ -36,6 +81,15 @@ func (s *systemService) GetTimeZone() string {
return command2.ExecResultStr("source " + config.AppInfo.ProjectPath + "/shell/helper.sh ;GetTimeZone")
}
func (s *systemService) ExecUSBAutoMountShell(state string) {
if state == "False" {
command2.OnlyExec("source " + config.AppInfo.ProjectPath + "/shell/helper.sh ;USB_Remove_File")
} else {
command2.OnlyExec("source " + config.AppInfo.ProjectPath + "/shell/helper.sh ;USB_Move_File")
}
}
func (s *systemService) GetSystemConfigDebug() []string {
return command2.ExecResultStrArray("source " + config.AppInfo.ProjectPath + "/shell/helper.sh ;GetSysInfo")
}
@@ -50,6 +104,17 @@ func (s *systemService) UpSystemConfig(str string, widget string) {
}
config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath)
}
func (s *systemService) UpAppOrderFile(str string) {
file.WriteToPath([]byte(str), config.AppInfo.ProjectPath+"/conf", "app_order.json")
}
func (s *systemService) GetAppOrderFile() []byte {
return file.ReadFullFile(config.AppInfo.ProjectPath + "/conf/app_order.json")
}
func (s *systemService) UpdateUSBAutoMount(state string) {
config.ServerInfo.USBAutoMount = state
config.Cfg.Section("server").Key("USBAutoMount").SetValue(state)
config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath)
}
func (s *systemService) UpSystemPort(port string) {
if len(port) > 0 && port != config.ServerInfo.HttpPort {
config.Cfg.Section("server").Key("HttpPort").SetValue(port)
@@ -71,6 +136,21 @@ func (s *systemService) GetCasaOSLogs(lineNumber int) string {
return string(content)
}
func NewSystemService(log loger.OLog) SystemService {
return &systemService{log: log}
func GetDeviceAllIP() []string {
var address []string
addrs, err := net2.InterfaceAddrs()
if err != nil {
return address
}
for _, a := range addrs {
if ipNet, ok := a.(*net2.IPNet); ok && !ipNet.IP.IsLoopback() {
if ipNet.IP.To16() != nil {
address = append(address, ipNet.IP.String())
}
}
}
return address
}
func NewSystemService() SystemService {
return &systemService{}
}

View File

@@ -6,7 +6,6 @@ import (
"github.com/IceWhaleTech/CasaOS/pkg/config"
httper2 "github.com/IceWhaleTech/CasaOS/pkg/utils/httper"
loger2 "github.com/IceWhaleTech/CasaOS/pkg/utils/loger"
"github.com/IceWhaleTech/CasaOS/service/model"
"github.com/IceWhaleTech/CasaOS/types"
"github.com/tidwall/gjson"
@@ -25,7 +24,6 @@ type TaskService interface {
type taskService struct {
db *gorm.DB
log loger2.OLog
}
func (s *taskService) List(desc bool) []model.TaskDBModel {
@@ -141,6 +139,6 @@ func SyncTask(db *gorm.DB) {
}
}(list)
}
func NewTaskService(db *gorm.DB, log loger2.OLog) TaskService {
return &taskService{db: db, log: log}
func NewTaskService(db *gorm.DB) TaskService {
return &taskService{db: db}
}

503
service/udpconn.go Normal file
View File

@@ -0,0 +1,503 @@
package service
import (
"context"
"crypto/md5"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net"
"os"
path2 "path"
"reflect"
"strconv"
"strings"
"time"
"github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/model/notify"
"github.com/IceWhaleTech/CasaOS/pkg/config"
"github.com/IceWhaleTech/CasaOS/pkg/quic_helper"
"github.com/IceWhaleTech/CasaOS/pkg/utils/file"
"github.com/IceWhaleTech/CasaOS/pkg/utils/ip_helper"
model2 "github.com/IceWhaleTech/CasaOS/service/model"
"github.com/IceWhaleTech/CasaOS/types"
"github.com/lucas-clemente/quic-go"
uuid "github.com/satori/go.uuid"
)
var UDPConn *net.UDPConn
var PeopleMap map[string]quic.Stream
var Message chan model.MessageModel
var UDPAddressMap map[string]string
func UDPSendData(msg model.MessageModel, localFilePath string) error {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
Message = make(chan model.MessageModel)
_, port, err := net.SplitHostPort(UDPConn.LocalAddr().String())
if config.ServerInfo.UDPPort != port {
config.ServerInfo.UDPPort = port
config.Cfg.Section("server").Key("UDPPort").SetValue(port)
config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath)
}
p, err := strconv.Atoi(port)
srcAddr := &net.UDPAddr{
IP: net.IPv4zero, Port: p} //注意端口必须固定
addr := UDPAddressMap[msg.To]
ticket := msg.To
dstAddr, err := net.ResolveUDPAddr("udp", addr)
session, err := quic.DialContext(ctx, UDPConn, dstAddr, srcAddr.String(), quic_helper.GetClientTlsConfig(ticket), quic_helper.GetQUICConfig())
if err != nil {
if msg.Type == types.PERSONDOWNLOAD {
task := MyService.Download().GetDownloadById(msg.UUId)
task.Error = err.Error()
task.State = types.DOWNLOADERROR
MyService.Download().SetDownloadError(task)
}
if config.SystemConfigInfo.Analyse != "False" {
go MyService.Casa().PushConnectionStatus(msg.UUId, err.Error(), msg.From, msg.To, msg.Type)
}
return err
}
stream, err := session.OpenStreamSync(ctx)
if err != nil {
if msg.Type == types.PERSONDOWNLOAD {
task := MyService.Download().GetDownloadById(msg.UUId)
task.Error = err.Error()
task.State = types.DOWNLOADERROR
MyService.Download().SetDownloadError(task)
}
if config.SystemConfigInfo.Analyse != "False" {
go MyService.Casa().PushConnectionStatus(msg.UUId, err.Error(), msg.From, msg.To, msg.Type)
}
session.CloseWithError(1, err.Error())
return err
}
SayHello(stream, msg.To)
//TODO:发送
SendData(stream, msg)
SendFileData(stream, localFilePath, msg.To, msg.UUId, types.PERSONUPLOADDATA)
stream.Close()
if config.SystemConfigInfo.Analyse != "False" {
go MyService.Casa().PushConnectionStatus(msg.UUId, "OK", msg.From, msg.To, msg.Type)
}
return nil
}
func Dial(msg model.MessageModel, server bool) (m model.MessageModel, err error) {
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second)
defer cancel()
Message = make(chan model.MessageModel)
_, port, err := net.SplitHostPort(UDPConn.LocalAddr().String())
if config.ServerInfo.UDPPort != port {
config.ServerInfo.UDPPort = port
config.Cfg.Section("server").Key("UDPPort").SetValue(port)
config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath)
}
p, err := strconv.Atoi(port)
srcAddr := &net.UDPAddr{
IP: net.IPv4zero, Port: p} //注意端口必须固定
addr := UDPAddressMap[msg.To]
ticket := msg.To
if server {
addr = config.ServerInfo.Handshake + ":9527"
ticket = "bench"
}
dstAddr, err := net.ResolveUDPAddr("udp", addr)
//DialTCP在网络协议net上连接本地地址laddr和远端地址raddr。net必须是"udp"、"udp4"、"udp6"如果laddr不是nil将使用它作为本地地址否则自动选择一个本地地址。
//(conn)UDPConn代表一个UDP网络连接实现了Conn和PacketConn接口
session, err := quic.DialContext(ctx, UDPConn, dstAddr, srcAddr.String(), quic_helper.GetClientTlsConfig(ticket), quic_helper.GetQUICConfig())
if err != nil {
if msg.Type == types.PERSONDOWNLOAD {
task := MyService.Download().GetDownloadById(msg.UUId)
task.Error = err.Error()
task.State = types.DOWNLOADERROR
MyService.Download().SetDownloadError(task)
}
if config.SystemConfigInfo.Analyse != "False" {
go MyService.Casa().PushConnectionStatus(msg.UUId, err.Error(), msg.From, msg.To, msg.Type)
}
return m, err
}
stream, err := session.OpenStreamSync(ctx)
if err != nil {
if msg.Type == types.PERSONDOWNLOAD {
task := MyService.Download().GetDownloadById(msg.UUId)
task.Error = err.Error()
task.State = types.DOWNLOADERROR
MyService.Download().SetDownloadError(task)
}
if config.SystemConfigInfo.Analyse != "False" {
go MyService.Casa().PushConnectionStatus(msg.UUId, err.Error(), msg.From, msg.To, msg.Type)
}
session.CloseWithError(1, err.Error())
return m, err
}
SayHello(stream, msg.To)
SendData(stream, msg)
go ReadContent(stream)
result := <-Message
stream.Close()
if config.SystemConfigInfo.Analyse != "False" {
go MyService.Casa().PushConnectionStatus(msg.UUId, "OK", msg.From, msg.To, msg.Type)
}
return result, nil
}
func SayHello(stream quic.Stream, to string) {
msg := model.MessageModel{}
msg.Type = types.PERSONHELLO
msg.Data = "hello"
msg.To = to
msg.From = config.ServerInfo.Token
msg.UUId = uuid.NewV4().String()
SendData(stream, msg)
}
//发送数据
func SendData(stream quic.Stream, m model.MessageModel) {
b, _ := json.Marshal(m)
prefixLength := file.PrefixLength(len(b))
data := append(prefixLength, b...)
stream.Write(data)
}
//读取数据
func ReadContent(stream quic.Stream) {
for {
prefixByte := make([]byte, 6)
_, err := io.ReadFull(stream, prefixByte)
if err != nil {
fmt.Println(err)
time.Sleep(time.Second * 1)
for k, v := range CancelList {
tempPath := config.AppInfo.RootPath + "/temp" + "/" + v
fmt.Println(file.RMDir(tempPath))
delete(CancelList, k)
}
break
}
prefixLength, err := strconv.Atoi(string(prefixByte))
if err != nil {
fmt.Println(err)
break
}
messageByte := make([]byte, prefixLength)
_, err = io.ReadFull(stream, messageByte)
if err != nil {
fmt.Println(err)
break
}
m := model.MessageModel{}
err = json.Unmarshal(messageByte, &m)
if err != nil {
fmt.Println(err)
break
}
if m.Type == types.PERSONDOWNLOAD {
r := SaveFile(m, stream)
if r {
break
}
} else if m.Type == types.PERSONSUMMARY {
Summary(m, "download")
} else if m.Type == types.PERSONCONNECTION {
if len(m.Data.(string)) > 0 {
UDPAddressMap[m.From] = m.Data.(string)
} else {
delete(UDPAddressMap, m.From)
}
// mi := model2.FriendModel{}
// mi.Avatar = config.UserInfo.Avatar
// mi.Profile = config.UserInfo.Description
// mi.NickName = config.UserInfo.NickName
// mi.Token = config.ServerInfo.Token
msg := model.MessageModel{}
msg.Type = types.PERSONHELLO
msg.Data = ""
msg.To = m.From
msg.From = config.ServerInfo.Token
msg.UUId = m.UUId
go Dial(msg, false)
Message <- m
break
} else if m.Type == types.PERSONGETIP {
notify := notify.Person{}
notify.ShareId = m.From
if len(m.Data.(string)) == 0 {
if _, ok := UDPAddressMap[m.From]; ok {
notify.Type = "OFFLINE"
go MyService.Notify().SendPersonStatusBySocket(notify)
}
delete(UDPAddressMap, m.From)
Message <- m
break
}
if _, ok := UDPAddressMap[m.From]; !ok {
notify.Type = "ONLINE"
go MyService.Notify().SendPersonStatusBySocket(notify)
}
UDPAddressMap[m.From] = m.Data.(string)
if config.ServerInfo.Token != m.From && strings.Split(m.Data.(string), ":")[0] == strings.Split(UDPAddressMap[config.ServerInfo.Token], ":")[0] {
msg := model.MessageModel{}
msg.Type = types.PERSONINTERNALINSPECTION
msg.Data = ip_helper.GetDeviceAllIP(config.ServerInfo.UDPPort)
msg.To = m.From
msg.From = config.ServerInfo.Token
msg.UUId = m.UUId
go Dial(msg, true)
}
Message <- m
break
} else if m.Type == types.PERSONINTERNALINSPECTION {
fmt.Println("接收到反验证")
var ips []string
dataModelByte, _ := json.Marshal(m.Data)
err := json.Unmarshal(dataModelByte, &ips)
if err != nil {
fmt.Println(err)
break
}
go MyService.Friend().InternalInspection(ips, m.From)
Message <- m
break
} else {
Message <- m
}
}
Message <- model.MessageModel{}
}
func SendIPToServer() {
msg := model.MessageModel{}
msg.Type = types.PERSONHELLO
msg.Data = ""
msg.From = config.ServerInfo.Token
msg.To = config.ServerInfo.Token
msg.UUId = uuid.NewV4().String()
Dial(msg, true)
}
func LoopFriend() {
list := MyService.Friend().GetFriendList()
msg := model.MessageModel{}
msg.Type = types.PERSONGETIP
msg.Data = ""
msg.From = config.ServerInfo.Token
msg.To = config.ServerInfo.Token
msg.UUId = uuid.NewV4().String()
Dial(msg, true)
for i := 0; i < len(list); i++ {
if _, ok := UDPAddressMap[list[i].Token]; !ok {
msg := model.MessageModel{}
msg.Type = types.PERSONGETIP
msg.Data = ""
msg.From = config.ServerInfo.Token
msg.To = list[i].Token
msg.UUId = uuid.NewV4().String()
Dial(msg, true)
}
msg.Type = types.PERSONPING
msg.Data = ""
msg.From = config.ServerInfo.Token
msg.To = list[i].Token
msg.UUId = uuid.NewV4().String()
if v, ok := UDPAddressMap[list[i].Token]; ok {
if ip_helper.HasLocalIP(net.ParseIP(strings.Split(v, ":")[0])) {
msg.Data = ip_helper.GetDeviceAllIP(config.ServerInfo.UDPPort)
}
oldIP := UDPAddressMap[list[i].Token]
data, err := Dial(msg, false)
if err != nil || reflect.DeepEqual(data, model.MessageModel{}) || len(data.Data.(string)) == 0 {
if oldIP == UDPAddressMap[list[i].Token] {
notify := notify.Person{}
notify.ShareId = data.From
notify.Type = "LEAVE"
go MyService.Notify().SendPersonStatusBySocket(notify)
delete(UDPAddressMap, list[i].Token)
msg := model.MessageModel{}
msg.Type = types.PERSONGETIP
msg.Data = ""
msg.From = config.ServerInfo.Token
msg.To = list[i].Token
msg.UUId = uuid.NewV4().String()
Dial(msg, true)
}
}
}
go func(shareId string) {
user := MyService.Casa().GetUserInfoByShareId(shareId)
m := model2.FriendModel{}
m.Token = shareId
friend := MyService.Friend().GetFriendById(m)
if friend.Version != user.Version {
friend.Avatar = user.Avatar
friend.NickName = user.NickName
friend.Profile = user.Desc
friend.Version = user.Version
MyService.Friend().UpdateOrCreate(friend)
}
}(list[i].Token)
}
}
//file summary
func Summary(m model.MessageModel, t string) {
dataModel := model.FileSummaryModel{}
dataModelByte, _ := json.Marshal(m.Data)
err := json.Unmarshal(dataModelByte, &dataModel)
if err != nil {
fmt.Println(err)
}
task := MyService.Download().GetDownloadById(m.UUId)
task.State = types.DOWNLOADING
fullPath := path2.Join(task.LocalPath, task.Name)
if len(dataModel.Message) > 0 {
task.State = types.DOWNLOADERROR
task.Error = dataModel.Message
}
//The file already exists and the file is the same, no need to download
if t != "upload" && file.Exists(fullPath) && file.GetHashByPath(fullPath) == dataModel.Hash {
task.State = types.DOWNLOADFINISHED
go func(from, uuid string) {
m := model.MessageModel{}
m.Data = ""
m.From = config.ServerInfo.Token
m.To = from
m.Type = types.PERSONCANCEL
m.UUId = uuid
CancelList[uuid] = uuid
Dial(m, false)
}(task.From, task.UUID)
}
task.UUID = m.UUId
task.Name = dataModel.Name
task.Length = dataModel.Length
task.Size = dataModel.Size
task.BlockSize = dataModel.BlockSize
task.Hash = dataModel.Hash
task.Type = types.PERSONFILEDOWNLOAD
task.From = m.From
if t == "upload" {
task.Type = types.PERSONFILERECEIVEUPLOAD
}
MyService.Download().SaveDownload(task)
}
//Save file fragment
func SaveFile(m model.MessageModel, stream quic.Stream) bool {
dataModelByte, _ := json.Marshal(m.Data)
dataModel := model.TranFileModel{}
err := json.Unmarshal(dataModelByte, &dataModel)
if err != nil {
fmt.Println(err)
return false
}
dataLengthByte := make([]byte, 8)
_, err = io.ReadFull(stream, dataLengthByte)
if err != nil {
fmt.Println(err)
return false
}
dataLength, err := strconv.Atoi(string(dataLengthByte))
if err != nil {
fmt.Println(err)
return false
}
dataByte := make([]byte, dataLength)
_, err = io.ReadFull(stream, dataByte)
if err != nil {
fmt.Println(err)
return false
}
sum := md5.Sum(dataByte)
hash := hex.EncodeToString(sum[:])
if dataModel.Hash != hash {
fmt.Println("hash不匹配", hash, dataModel.Hash)
return false
}
tempPath := config.AppInfo.RootPath + "/temp" + "/" + m.UUId
file.IsNotExistMkDir(tempPath)
filepath := tempPath + "/" + strconv.Itoa(dataModel.Index)
_, err = os.Stat(filepath)
if os.IsNotExist(err) {
err = ioutil.WriteFile(filepath, dataByte, 0644)
task := model2.PersonDownloadDBModel{}
task.UUID = m.UUId
if err != nil {
task.Error = err.Error()
task.State = types.DOWNLOADERROR
MyService.Download().SetDownloadError(task)
}
} else {
if file.GetHashByPath(filepath) != dataModel.Hash {
os.Remove(filepath)
err = ioutil.WriteFile(filepath, dataByte, 0644)
task := model2.PersonDownloadDBModel{}
task.UUID = m.UUId
if err != nil {
task.Error = err.Error()
task.State = types.DOWNLOADERROR
MyService.Download().SetDownloadError(task)
}
}
}
files, err := ioutil.ReadDir(tempPath)
if err != nil {
fmt.Println(err)
return false
}
if len(files) >= dataModel.Length {
summary := MyService.Download().GetDownloadById(m.UUId)
summary.State = types.DOWNLOADFINISH
MyService.Download().EditDownloadState(summary)
fullPath := file.GetNoDuplicateFileName(path2.Join(summary.LocalPath, summary.Name))
file.SpliceFiles(tempPath, fullPath, dataModel.Length, 0)
if file.GetHashByPath(fullPath) == summary.Hash {
file.RMDir(tempPath)
summary.State = types.DOWNLOADFINISHED
MyService.Download().EditDownloadState(summary)
} else {
os.Remove(config.FileSettingInfo.DownloadDir + "/" + summary.Name)
summary.State = types.DOWNLOADERROR
summary.Error = "hash mismatch"
MyService.Download().SetDownloadError(summary)
}
return true
}
return false
}

View File

@@ -9,7 +9,7 @@ import (
)
type UserService interface {
SetUser(username, pwd, token, email, desc string) error
SetUser(username, pwd, token, email, desc, nickName string) error
UpLoadFile(file multipart.File, name string) error
}
@@ -17,7 +17,7 @@ type user struct {
}
//设置用户名密码
func (c *user) SetUser(username, pwd, token, email, desc string) error {
func (c *user) SetUser(username, pwd, token, email, desc, nickName string) error {
if len(username) > 0 {
config.Cfg.Section("user").Key("UserName").SetValue(username)
config.UserInfo.UserName = username
@@ -28,10 +28,6 @@ func (c *user) SetUser(username, pwd, token, email, desc string) error {
config.Cfg.Section("user").Key("PWD").SetValue(pwd)
config.UserInfo.PWD = pwd
}
if len(token) > 0 {
config.Cfg.Section("user").Key("Token").SetValue(token)
config.UserInfo.Token = token
}
if len(email) > 0 {
config.Cfg.Section("user").Key("Email").SetValue(email)
config.UserInfo.Email = email
@@ -40,6 +36,10 @@ func (c *user) SetUser(username, pwd, token, email, desc string) error {
config.Cfg.Section("user").Key("Description").SetValue(desc)
config.UserInfo.Description = desc
}
if len(nickName) > 0 {
config.Cfg.Section("user").Key("NickName").SetValue(nickName)
config.UserInfo.NickName = nickName
}
config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath)
return nil
}

View File

@@ -1,353 +0,0 @@
package service
import (
"bytes"
"errors"
"fmt"
"io/ioutil"
"math/rand"
"net/http"
"strconv"
"strings"
"time"
"unicode"
"github.com/IceWhaleTech/CasaOS/pkg/config"
command2 "github.com/IceWhaleTech/CasaOS/pkg/utils/command"
httper2 "github.com/IceWhaleTech/CasaOS/pkg/utils/httper"
"github.com/IceWhaleTech/CasaOS/pkg/zerotier"
"github.com/PuerkitoBio/goquery"
"github.com/tidwall/gjson"
)
type ZeroTierService interface {
GetToken(username, pwd string) string
ZeroTierRegister(email, lastName, firstName, password string) string
ZeroTierNetworkList(token string) (interface{}, []string)
ZeroTierJoinNetwork(networkId string)
ZeroTierLeaveNetwork(networkId string)
ZeroTierGetInfo(token, id string) (interface{}, []string)
ZeroTierGetStatus(token string) interface{}
EditNetwork(token string, data string, id string) interface{}
CreateNetwork(token string) interface{}
MemberList(token string, id string) interface{}
EditNetworkMember(token string, data string, id, mId string) interface{}
DeleteMember(token string, id, mId string) interface{}
DeleteNetwork(token, id string) interface{}
GetJoinNetworks() string
NetworkIdFilter(letter rune) bool
}
type zerotierStruct struct {
}
var client http.Client
func (c *zerotierStruct) ZeroTierJoinNetwork(networkId string) {
command2.OnlyExec(`zerotier-cli join ` + networkId)
}
func (c *zerotierStruct) ZeroTierLeaveNetwork(networkId string) {
command2.OnlyExec(`zerotier-cli leave ` + networkId)
}
//登录并获取token
func (c *zerotierStruct) GetToken(username, pwd string) string {
if len(config.ZeroTierInfo.Token) > 0 {
return config.ZeroTierInfo.Token
} else {
return LoginGetToken(username, pwd)
}
}
func (c *zerotierStruct) ZeroTierRegister(email, lastName, firstName, password string) string {
url := "https://accounts.zerotier.com/auth/realms/zerotier/protocol/openid-connect/registrations?client_id=zt-central&redirect_uri=https%3A%2F%2Fmy.zerotier.com%2Fapi%2F_auth%2Foidc%2Fcallback&response_type=code&scope=openid+profile+email+offline_access&state=state"
action, cookies, _ := ZeroTierGet(url, nil, 4)
var buff bytes.Buffer
buff.WriteString("email=")
buff.WriteString(email)
buff.WriteString("&password=")
buff.WriteString(password)
buff.WriteString("&password-confirm=")
buff.WriteString(password)
buff.WriteString("&user.attributes.marketingOptIn=true")
buff.WriteString("&firstName")
buff.WriteString(firstName)
buff.WriteString("&lastName")
buff.WriteString(lastName)
action, errInfo, _ := ZeroTierPost(buff, action, cookies, false)
if len(errInfo) > 0 {
return errInfo
}
action, _, _ = ZeroTierGet(action, cookies, 5)
return ""
}
//固定请求head
func GetHead() map[string]string {
var head = make(map[string]string, 4)
head["Accept"] = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"
head["Accept-Language"] = "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3"
head["Connection"] = "keep-alive"
head["User-Agent"] = "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36"
return head
}
//登录并获取token会出现账号密码错误和邮箱未验证情况目前未出现其他情况
func LoginGetToken(username, pwd string) string {
//拿到登录的action
var loginUrl = "https://accounts.zerotier.com/auth/realms/zerotier/protocol/openid-connect/auth?client_id=zt-central&redirect_uri=https%3A%2F%2Fmy.zerotier.com%2Fapi%2F_auth%2Foidc%2Fcallback&response_type=code&scope=openid+profile+email+offline_access&state=states"
action, cookies, _ := ZeroTierGet(loginUrl, nil, 1)
if len(action) == 0 {
//没有拿到action页面结构变了
return ""
}
//登录
var str bytes.Buffer
str.WriteString("username=")
str.WriteString(username)
str.WriteString("&password=")
str.WriteString(pwd)
str.WriteString("&credentialId=&login=Log+In")
url, logingErrInfo, _ := ZeroTierPost(str, action, cookies, true)
action, cookies, isLoginOk := ZeroTierGet(url, cookies, 2)
if isLoginOk {
//登录成功可以继续调用api
randomTokenUrl := "https://my.zerotier.com/api/randomToken"
json, _, _ := ZeroTierGet(randomTokenUrl, cookies, 3)
//获取一个随机token
token := gjson.Get(json, "token")
userInfoUrl := "https://my.zerotier.com/api/status"
json, _, _ = ZeroTierGet(userInfoUrl, cookies, 3)
//拿到用户id
userId := gjson.Get(json, "user.id")
//设置新token
addTokenUrl := "https://my.zerotier.com/api/user/" + userId.String() + "/token"
data := make(map[string]string)
rand.Seed(time.Now().UnixNano())
data["tokenName"] = "oasis-token-" + strconv.Itoa(rand.Intn(1000))
data["token"] = token.String()
head := make(map[string]string)
head["Content-Type"] = "application/json"
_, statusCode := httper2.ZeroTierPost(addTokenUrl, data, head, cookies)
if statusCode == http.StatusOK {
config.Cfg.Section("zerotier").Key("Token").SetValue(token.String())
config.Cfg.SaveTo("conf/conf.ini")
config.ZeroTierInfo.Token = token.String()
}
} else {
//登录错误信息
if len(logingErrInfo) > 0 {
return logingErrInfo
} else {
//验证邮箱
action, _, _ = ZeroTierGet(url, cookies, 5)
return "You need to verify your email address to activate your account."
}
}
return ""
}
// t 1:获取action2登录成功后拿session可能需要验证有了或登录失败 3:随机生成token 4:注册页面拿action 5:注册成功后拿验证邮箱的地址
func ZeroTierGet(url string, cookies []*http.Cookie, t uint8) (action string, c []*http.Cookie, isExistSession bool) {
isExistSession = false
action = ""
c = []*http.Cookie{}
request, _ := http.NewRequest(http.MethodGet, url, nil)
for k, v := range GetHead() {
request.Header.Add(k, v)
}
for _, cookie := range cookies {
request.AddCookie(cookie)
}
resp, err := client.Do(request)
if err != nil {
return
}
defer resp.Body.Close()
c = resp.Cookies()
if t == 1 {
doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil {
return
}
action, _ = doc.Find("#kc-form-login").Attr("action")
return
} else if t == 2 {
for _, cookie := range resp.Cookies() {
if cookie.Name == "pgx-session" {
isExistSession = true
break
}
}
//判断是否登录成功,如果需要验证邮箱,则返回验证邮箱的地址。
if resp.StatusCode == http.StatusFound && len(resp.Header.Get("Location")) > 0 {
action = resp.Header.Get("Location")
}
return
} else if t == 3 {
//返回获取到的字符串
byteArr, _ := ioutil.ReadAll(resp.Body)
action = string(byteArr)
} else if t == 4 {
doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil {
return
}
action, _ = doc.Find("#kc-register-form").Attr("action")
return
} else if t == 5 {
doc, _ := goquery.NewDocumentFromReader(resp.Body)
fmt.Println(doc.Html())
action, _ = doc.Find("#kc-info-wrapper a").Attr("href")
return
}
return
}
//模拟提交表单
func ZeroTierPost(str bytes.Buffer, action string, cookies []*http.Cookie, isLogin bool) (url, errInfo string, err error) {
req, err := http.NewRequest(http.MethodPost, action, strings.NewReader(str.String()))
if err != nil {
return "", "", errors.New("newrequest error")
}
for k, v := range GetHead() {
req.Header.Set(k, v)
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
for _, cookie := range cookies {
req.AddCookie(cookie)
}
res, err := client.Do(req)
defer res.Body.Close()
if err != nil {
return "", "", errors.New("request error")
}
if !isLogin {
//注册成功
if res.StatusCode == http.StatusFound && len(res.Header.Get("Location")) > 0 {
return res.Header.Get("Location"), "", nil
} else {
register, _ := goquery.NewDocumentFromReader(res.Body)
firstErr := strings.TrimSpace(register.Find("#input-error-firstname").Text())
lastErr := strings.TrimSpace(register.Find("#input-error-lastname").Text())
emailErr := strings.TrimSpace(register.Find("#input-error-email").Text())
pwdErr := strings.TrimSpace(register.Find("#input-error-password").Text())
var errD strings.Builder
if len(firstErr) > 0 {
errD.WriteString(firstErr + ",")
}
if len(lastErr) > 0 {
errD.WriteString(lastErr + ",")
}
if len(emailErr) > 0 {
errD.WriteString(emailErr + ",")
}
if len(pwdErr) > 0 {
errD.WriteString(pwdErr + ",")
}
return "", errD.String(), nil
}
} else {
if res.StatusCode == http.StatusFound && len(res.Header.Get("Location")) > 0 {
return res.Header.Get("Location"), "", nil
}
doc, err := goquery.NewDocumentFromReader(res.Body)
if err != nil {
return "", "", errors.New("request error")
}
errDesc := doc.Find("#input-error").Text()
if len(errDesc) > 0 {
return "", strings.TrimSpace(errDesc), nil
}
}
return "", "", nil
}
//获取zerotile网络列表和本地用户已加入的网络
func (c *zerotierStruct) ZeroTierNetworkList(token string) (interface{}, []string) {
url := "https://my.zerotier.com/api/network"
return zerotier.GetData(url, token), command2.ExecResultStrArray(`zerotier-cli listnetworks | awk 'NR>1 {print $3} {line=$0}'`)
}
// get network info
func (c *zerotierStruct) ZeroTierGetInfo(token, id string) (interface{}, []string) {
url := "https://my.zerotier.com/api/network/" + id
info := zerotier.GetData(url, token)
return info, command2.ExecResultStrArray(`zerotier-cli listnetworks | awk 'NR>1 {print $3} {line=$0}'`)
}
//get status
func (c *zerotierStruct) ZeroTierGetStatus(token string) interface{} {
url := "https://my.zerotier.com/api/v1/status"
info := zerotier.GetData(url, token)
return info
}
func (c *zerotierStruct) EditNetwork(token string, data string, id string) interface{} {
url := "https://my.zerotier.com/api/v1/network/" + id
info := zerotier.PostData(url, token, data)
return info
}
func (c *zerotierStruct) EditNetworkMember(token string, data string, id, mId string) interface{} {
url := "https://my.zerotier.com/api/v1/network/" + id + "/member/" + mId
info := zerotier.PostData(url, token, data)
return info
}
func (c *zerotierStruct) MemberList(token string, id string) interface{} {
url := "https://my.zerotier.com/api/v1/network/" + id + "/member"
info := zerotier.GetData(url, token)
return info
}
func (c *zerotierStruct) DeleteMember(token string, id, mId string) interface{} {
url := "https://my.zerotier.com/api/v1/network/" + id + "/member/" + mId
info := zerotier.DeleteMember(url, token)
return info
}
func (c *zerotierStruct) DeleteNetwork(token, id string) interface{} {
url := "https://my.zerotier.com/api/v1/network/" + id
info := zerotier.DeleteMember(url, token)
return info
}
func (c *zerotierStruct) CreateNetwork(token string) interface{} {
url := "https://my.zerotier.com/api/v1/network"
info := zerotier.PostData(url, token, "{}")
return info
}
func (c *zerotierStruct) GetJoinNetworks() string {
json := command2.ExecResultStr("source " + config.AppInfo.ProjectPath + "/shell/helper.sh ;GetLocalJoinNetworks")
return json
}
func (c *zerotierStruct) NetworkIdFilter(letter rune) bool {
if unicode.IsNumber(letter) || unicode.IsLetter(letter) {
return true
} else {
return false
}
}
func NewZeroTierService() ZeroTierService {
//初始化client
client = http.Client{Timeout: 30 * time.Second, CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse //禁止重定向
},
}
return &zerotierStruct{}
}

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"runtime"
"strconv"
"strings"
@@ -17,56 +18,34 @@ import (
"github.com/shirou/gopsutil/v3/cpu"
"github.com/shirou/gopsutil/v3/disk"
"github.com/shirou/gopsutil/v3/host"
"github.com/shirou/gopsutil/v3/mem"
"github.com/shirou/gopsutil/v3/net"
)
//系统信息
type ZiMaService interface {
GetCpuPercent() float64
GetCpuCoreNum() int
GetMemInfo() *mem.VirtualMemoryStat
GetDiskInfo() *disk.UsageStat
GetNetInfo() []net.IOCountersStat
GetNet(physics bool) []string
GetNetState(name string) string
GetSysInfo() host.InfoStat
GetDirPath(path string) []model.Path
GetDirPathOne(path string) (m model.Path)
MkdirAll(path string) (int, error)
CreateFile(path string) (int, error)
RenameFile(oldF, newF string) (int, error)
GetCpuInfo() []cpu.InfoStat
GetDeviceTree() string
}
var NetArray [][]model.IOCountersStat
type zima struct {
}
//获取cpu占用率
func (c *zima) GetCpuPercent() float64 {
percent, _ := cpu.Percent(0, false)
value, _ := strconv.ParseFloat(fmt.Sprintf("%.1f", percent[0]), 64)
return value
}
//获取物理核心数
func (c *zima) GetCpuCoreNum() int {
count, _ := cpu.Counts(false)
return count
}
//cpu详情
func (c *zima) GetCpuInfo() []cpu.InfoStat {
info, _ := cpu.Info()
return info
}
//获取内存详情
func (c *zima) GetMemInfo() *mem.VirtualMemoryStat {
memInfo, _ := mem.VirtualMemory()
memInfo.UsedPercent, _ = strconv.ParseFloat(fmt.Sprintf("%.1f", memInfo.UsedPercent), 64)
return memInfo
}
//获取硬盘详情
func (c *zima) GetDiskInfo() *disk.UsageStat {
path := "/"
@@ -81,13 +60,32 @@ func (c *zima) GetDiskInfo() *disk.UsageStat {
//获取硬盘目录
func (c *zima) GetDirPath(path string) []model.Path {
if path == "/DATA" {
sysType := runtime.GOOS
if sysType == "windows" {
path = "C:\\CasaOS\\DATA"
}
if sysType == "darwin" {
path = "./CasaOS/DATA"
}
}
ls, _ := ioutil.ReadDir(path)
dirs := []model.Path{}
if strings.Count(path, "/") > 0 {
if len(path) > 0 {
for _, l := range ls {
dirs = append(dirs, model.Path{Name: l.Name(), Path: path + "/" + l.Name(), IsDir: l.IsDir(), Date: l.ModTime()})
filePath := filepath.Join(path, l.Name())
link, err := filepath.EvalSymlinks(filePath)
if err != nil {
link = filePath
}
temp := model.Path{Name: l.Name(), Path: filePath, IsDir: l.IsDir(), Date: l.ModTime(), Size: l.Size()}
if filePath != link {
file, _ := os.Stat(link)
temp.IsDir = file.IsDir()
}
dirs = append(dirs, temp)
}
} else {
dirs = append(dirs, model.Path{Name: "DATA", Path: "/DATA/", IsDir: true, Date: time.Now()})
@@ -95,19 +93,29 @@ func (c *zima) GetDirPath(path string) []model.Path {
return dirs
}
func (c *zima) GetDirPathOne(path string) (m model.Path) {
f, err := os.Stat(path)
if err != nil {
return
}
m.IsDir = f.IsDir()
m.Name = f.Name()
m.Path = path
m.Size = f.Size()
m.Date = f.ModTime()
return
}
//获取系统信息
func (c *zima) GetSysInfo() host.InfoStat {
info, _ := host.Info()
return *info
}
//shell脚本参数 {1:虚拟网卡 2:物理网卡}
func (c *zima) GetNet(physics bool) []string {
t := "1"
if physics {
t = "2"
}
return command2.ExecResultStrArray("source " + config.AppInfo.ProjectPath + "/shell/helper.sh ;GetNetCard " + t)
func (c *zima) GetDeviceTree() string {
return command2.ExecResultStr("source " + config.AppInfo.ProjectPath + "/shell/helper.sh ;GetDeviceTree")
}
//shell脚本参数 { 网卡名称 }
@@ -115,13 +123,6 @@ func (c *zima) GetNetState(name string) string {
return command2.ExecResultStr("source " + config.AppInfo.ProjectPath + "/shell/helper.sh ;CatNetCardState " + name)
}
//网络信息
func (c *zima) GetNetInfo() []net.IOCountersStat {
parts, _ := net.IOCounters(true)
//fmt.Println(net.ConntrackStatsWithContext(true))
return parts
}
//mkdir
func (c *zima) MkdirAll(path string) (int, error) {
_, err := os.Stat(path)
@@ -174,3 +175,41 @@ func (c *zima) RenameFile(oldF, newF string) (int, error) {
func NewZiMaService() ZiMaService {
return &zima{}
}
// func LoopNet() {
// netList := MyService.ZiMa().GetNetInfo()
// nets := MyService.ZiMa().GetNet(true)
// num := 0
// for i := 0; i < len(netList); i++ {
// for _, netCardName := range nets {
// if netList[i].Name == netCardName {
// var netArray []model.IOCountersStat
// if len(NetArray) < (num + 1) {
// netArray = []model.IOCountersStat{}
// } else {
// netArray = NetArray[num]
// }
// item := *(*model.IOCountersStat)(unsafe.Pointer(&netList[i]))
// item.State = strings.TrimSpace(MyService.ZiMa().GetNetState(netList[i].Name))
// item.Time = time.Now().Unix()
// if len(netArray) >= 60 {
// netArray = netArray[1:]
// }
// netArray = append(netArray, item)
// if len(NetArray) < (num + 1) {
// NetArray = append(NetArray, []model.IOCountersStat{})
// }
// NetArray[num] = netArray
// num++
// break
// }
// }
// }
// }

Some files were not shown because too many files have changed in this diff Show More