Merge pull request #179 from IceWhaleTech/dev

Dev
This commit is contained in:
link 2022-05-13 18:21:44 +08:00 committed by GitHub
commit df2477a12f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
94 changed files with 11417 additions and 9447 deletions

359
CHANGELOG.md Normal file
View file

@ -0,0 +1,359 @@
# 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.1-pre] - 2022-05-13
### 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
### Security
### 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

2
UI

@ -1 +1 @@
Subproject commit 4a8f15711ee5520f6bb644107f1eb25f252689b1
Subproject commit 2f6deb2253df1d0247d5329cb0ea1aa983053a2e

View file

@ -16,33 +16,27 @@ RootPath = /casaOS
HttpPort = 8089
UDPPort =
RunMode = release
ServerApi = https://api.casaos.zimaboard.com
ServerApi = https://api.casaos.io
Handshake = socket.casaos.io
Token =
USBAutoMount = true
USBAutoMount =
[user]
UserName = admin
PWD = zimaboard
UserName =
PWD =
Email = user@gmail.com
Description = description
Initialized = false
Avatar =
NickName =
[redis]
Host = 127.0.0.1:6379
Password =
MaxIdle = 30
MaxActive = 30
IdleTimeout = 200
PublicKey =
[system]
ConfigStr =
WidgetList =
Analyse =
[file]
ShareDir =
DownloadDir =

Binary file not shown.

Binary file not shown.

5
go.mod
View file

@ -3,16 +3,19 @@ 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/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
github.com/gin-gonic/gin v1.7.2

32
go.sum
View file

@ -56,6 +56,8 @@ github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZ
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
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=
@ -82,8 +84,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=
@ -99,8 +99,6 @@ 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/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=
@ -269,6 +267,8 @@ 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=
@ -289,6 +289,18 @@ 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/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=
@ -330,6 +342,9 @@ 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=
@ -400,6 +415,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=
@ -516,6 +534,7 @@ github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH
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=
@ -935,6 +954,8 @@ 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=
@ -957,7 +978,6 @@ 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=
@ -991,6 +1011,7 @@ 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=
@ -1342,6 +1363,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=

51
main.go
View file

@ -4,7 +4,6 @@ import (
"flag"
"fmt"
"net/http"
"runtime"
"time"
"github.com/IceWhaleTech/CasaOS/pkg/cache"
@ -13,6 +12,8 @@ import (
loger2 "github.com/IceWhaleTech/CasaOS/pkg/utils/loger"
"github.com/IceWhaleTech/CasaOS/route"
"github.com/IceWhaleTech/CasaOS/service"
model2 "github.com/IceWhaleTech/CasaOS/service/model"
"github.com/IceWhaleTech/CasaOS/types"
"github.com/robfig/cron"
"gorm.io/gorm"
@ -21,7 +22,7 @@ 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() {
@ -29,25 +30,10 @@ func init() {
config.InitSetup(*configFlag)
config.UpdateSetup()
loger2.LogSetup()
sysType := runtime.GOOS
if sysType == "windows" {
config.AppInfo.ProjectPath = "C:\\CasaOS\\service"
config.Cfg.Section("app").Key("ProjectPath").SetValue("C:\\CasaOS\\service")
config.AppInfo.RootPath = "C:\\CasaOS"
config.Cfg.Section("app").Key("RootPath").SetValue("C:\\CasaOS")
config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath)
if len(*dbFlag) == 0 {
*dbFlag = config.AppInfo.ProjectPath + "/db"
}
if sysType == "darwin" {
config.AppInfo.ProjectPath = "./CasaOS/service"
config.Cfg.Section("app").Key("ProjectPath").SetValue("./CasaOS/service")
config.AppInfo.RootPath = "./CasaOS"
config.Cfg.Section("app").Key("RootPath").SetValue("./CasaOS")
config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath)
}
sqliteDB = sqlite.GetDb(config.AppInfo.ProjectPath)
sqliteDB = sqlite.GetDb(*dbFlag)
//gredis.GetRedisConn(config.RedisInfo),
service.MyService = service.NewService(sqliteDB, loger2.NewOLoger())
service.Cache = cache.Init()
@ -58,10 +44,13 @@ func init() {
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.LoopFriend()
// go service.MyService.App().CheckNewImage()
}
@ -97,20 +86,22 @@ func main() {
service.SendIPToServer()
service.LoopFriend()
service.MyService.App().CheckNewImage()
})
if err != nil {
fmt.Println(err)
}
err = cron2.AddFunc("0/1 * * * * *", func() {
notify := model2.AppNotify{}
notify.CustomId = ""
notify.Type = types.NOTIFY_TYPE_HEALTH_CHECK
go service.MyService.Notify().SendText(notify)
})
if err != nil {
fmt.Println(err)
}
// err = cron2.AddFunc("0/1 * * * * *", func() {
// //service.SendIPToServer()
// //service.LoopNet()
// })
// if err != nil {
// fmt.Println(err)
// }
cron2.Start()
defer cron2.Stop()
s := &http.Server{

View file

@ -1,3 +1,13 @@
/*
* @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 {
@ -5,6 +15,7 @@ type AppAnalyse struct {
Type string `json:"type"`
UUId string `json:"uuid"`
Language string `json:"language"`
Version string `json:"version"`
}
type ConnectionStatus struct {

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"`

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

@ -0,0 +1 @@
package receive

View file

@ -18,6 +18,7 @@ type UserModel struct {
Initialized bool
Avatar string
NickName string
Public string
}
//服务配置

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

@ -10,4 +10,5 @@ type Path struct {
Size int64 `json:"size"` //File Size
Type string `json:"type,omitempty"`
Label string `json:"label,omitempty"`
Write bool `json:"write"`
}

View file

@ -22,8 +22,7 @@ var UserInfo = &model.UserModel{}
//用户相关
var AppInfo = &model.APPModel{}
//redis相关配置
var RedisInfo = &model.RedisModel{}
//var RedisInfo = &model.RedisModel{}
//server相关
var ServerInfo = &model.ServerModel{}
@ -53,7 +52,7 @@ func InitSetup(config string) {
mapTo("user", UserInfo)
mapTo("app", AppInfo)
mapTo("redis", RedisInfo)
//mapTo("redis", RedisInfo)
mapTo("server", ServerInfo)
mapTo("system", SystemConfigInfo)
mapTo("file", FileSettingInfo)

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{}, model2.PersonDownloadDBModel{}, model2.FriendModel{})
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

@ -283,7 +283,6 @@ func CopyDir(src string, dst string) error {
return nil
}
//文件写入临时目录
func WriteToPath(data []byte, path, name string) error {
fullPath := path
if strings.HasSuffix(path, "/") {

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,6 @@ package httper
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
@ -23,6 +22,42 @@ func Get(url string, head map[string]string) (response string) {
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 ""
}
@ -78,88 +113,6 @@ func Post(url string, data []byte, contentType string, head map[string]string) (
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,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

@ -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

@ -13,10 +13,11 @@ const (
PWD_INVALID_OLD = 10003
ACCOUNT_LOCK = 10004
//system
DIR_ALREADY_EXISTS = 20001
FILE_ALREADY_EXISTS = 20002
FILE_OR_DIR_EXISTS = 20003
PORT_IS_OCCUPIED = 20004
DIR_ALREADY_EXISTS = 20001
FILE_ALREADY_EXISTS = 20002
FILE_OR_DIR_EXISTS = 20003
PORT_IS_OCCUPIED = 20004
COMMAND_ERROR_INVALID_OPERATION = 20005
//zerotier
GET_TOKEN_ERROR = 30001
@ -29,9 +30,10 @@ const (
FORMAT_ERROR = 40005
//app
UNINSTALL_APP_ERROR = 50001
PULL_IMAGE_ERROR = 50002
DEVICE_NOT_EXIST = 50003
UNINSTALL_APP_ERROR = 50001
PULL_IMAGE_ERROR = 50002
DEVICE_NOT_EXIST = 50003
ERROR_APP_NAME_EXIST = 50004
//file
FILE_DOES_NOT_EXIST = 60001
@ -73,9 +75,10 @@ var MsgFlags = map[int]string{
GET_TOKEN_ERROR: "Get token error,Please log in to zerotier's official website to confirm whether the account is available",
//app
UNINSTALL_APP_ERROR: "Error uninstalling app",
PULL_IMAGE_ERROR: "Error pulling image",
DEVICE_NOT_EXIST: "Device does not exist",
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",
@ -93,12 +96,13 @@ var MsgFlags = map[int]string{
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",
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

@ -37,6 +37,31 @@ func IsNeedUpdate() (bool, model.Version) {
return false, version
}
func IsClientNeedUpdate() (bool, model.Version) {
var version model.Version
v := httper.OasisGet(config.ServerInfo.ServerApi + "/v1/sys/client/version")
data := gjson.Get(v, "data")
json2.Unmarshal([]byte(data.String()), &version)
v1 := strings.Split(version.Version, ".")
v2 := strings.Split(types.CURRENTVERSION, ".")
for len(v1) < len(v2) {
v1 = append(v1, "0")
}
for len(v2) < len(v1) {
v2 = append(v2, "0")
}
for i := 0; i < len(v1); i++ {
a, _ := strconv.Atoi(v1[i])
b, _ := strconv.Atoi(v2[i])
if a > b {
return true, version
}
}
return false, version
}
//a版本大于b版本
func VersionCompared(a string, b string) bool {
v1 := strings.Split(a, ".")

View file

@ -1,17 +1,16 @@
package route
import (
"encoding/json"
"encoding/xml"
"fmt"
"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"
@ -27,6 +26,9 @@ func InitFunction() {
CheckSerialDiskMount()
CheckToken2_11()
ImportApplications()
ChangeAPIUrl()
InitSystemApplication()
}
@ -102,8 +104,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
@ -111,83 +114,46 @@ 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 {
break
}
}
for _, i := range info.HostConfig.Mounts {
if i.Target == "/config" {
path = i.Source
break
}
}
content := file.ReadFullFile(path)
content := file.ReadFullFile(path + "config.xml")
syncConfig := &system_app.SyncConfig{}
xml.Unmarshal(content, &syncConfig)
config.SystemConfigInfo.SyncKey = syncConfig.Key
break
}
}
if !syncIsExistence {
@ -261,7 +227,7 @@ func CheckToken2_11() {
downloadPath = "C:\\CasaOS\\DATA\\Downloads"
}
if sysType == "darwin" {
downloadPath = "~/CasaOS/DATA/Downloads"
downloadPath = "./CasaOS/DATA/Downloads"
}
config.Cfg.Section("file").Key("DownloadDir").SetValue(downloadPath)
config.FileSettingInfo.DownloadDir = downloadPath
@ -280,7 +246,10 @@ func CheckToken2_11() {
config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath)
}
service.MyService.System().ExecUSBAutoMountShell(config.ServerInfo.USBAutoMount)
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")
@ -291,3 +260,39 @@ func CheckToken2_11() {
// 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)
}
}

View file

@ -41,7 +41,8 @@ func InitRouter() *gin.Engine {
r.POST("/v1/user/setusernamepwd", v1.Set_Name_Pwd)
//get user info
r.GET("/v1/user/info", v1.GetUserInfo)
//get user info
r.GET("/v1/person/shareid", v1.GetPersonShareId)
v1Group := r.Group("/v1")
v1Group.Use(jwt2.JWT(swagHandler))
@ -102,7 +103,7 @@ func InitRouter() *gin.Engine {
v1AppGroup.Use()
{
//获取我的已安装的列表
v1AppGroup.GET("/mylist", v1.MyAppList)
v1AppGroup.GET("/my/list", v1.MyAppList)
//
v1AppGroup.GET("/usage", v1.AppUsageList)
//app详情
@ -117,8 +118,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容器日志
@ -139,17 +140,17 @@ 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.GET("/client/version", v1.GetClientVersion)
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)
@ -161,13 +162,12 @@ func InitRouter() *gin.Engine {
v1SysGroup.POST("/kill", v1.PostKillCasaOS)
v1SysGroup.GET("/info", v1.Info)
v1SysGroup.PUT("/usb/off", v1.PutSystemOffUSBAutoMount)
v1SysGroup.GET("/usb/on", v1.PutSystemOnUSBAutoMount)
v1SysGroup.PUT("/usb/on", v1.PutSystemOnUSBAutoMount)
v1SysGroup.GET("/usb", v1.GetSystemUSBAutoMount)
}
v1FileGroup := v1Group.Group("/file")
v1FileGroup.Use()
{
//修改文件名称/目录名称
v1FileGroup.PUT("/rename", v1.RenamePath)
v1FileGroup.GET("/read", v1.GetFilerContent)
v1FileGroup.POST("/upload", v1.PostFileUpload)
@ -182,6 +182,7 @@ func InitRouter() *gin.Engine {
v1FileGroup.POST("/operate", v1.PostOperateFileOrDir)
v1FileGroup.DELETE("/delete", v1.DeleteFile)
v1FileGroup.PUT("/update", v1.PutFileContent)
v1FileGroup.GET("/image", v1.GetFileImage)
//v1FileGroup.GET("/download", v1.UserFileDownloadCommonService)
}
@ -233,29 +234,18 @@ func InitRouter() *gin.Engine {
v1TaskGroup.PUT("/completion/:id", v1.PutTaskMarkerCompletion)
}
v1ShortcutsGroup := v1Group.Group("/shortcuts")
v1ShortcutsGroup.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)
}
v1PersonGroup := v1Group.Group("/person")
v1PersonGroup.Use()
{
v1PersonGroup.GET("/test", v1.PersonTest)
v1PersonGroup.GET("/detection", v1.GetPersonDetection)
v1PersonGroup.GET("/users", v1.GetPersonFriend)
v1PersonGroup.POST("/user/:shareids", v1.PostAddPersonFriend)
v1PersonGroup.DELETE("/user/:shareid", v1.DeletePersonFriend)
@ -267,11 +257,15 @@ func InitRouter() *gin.Engine {
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)
}
v1AnalyseGroup := v1Group.Group("/analyse")

View file

@ -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

View file

@ -4,8 +4,8 @@ import (
"bytes"
"encoding/json"
json2 "encoding/json"
"fmt"
"net/http"
"reflect"
"strconv"
"strings"
"time"
@ -16,6 +16,7 @@ import (
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"
@ -27,7 +28,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"
)
@ -154,6 +154,27 @@ func InstallApp(c *gin.Context) {
const CUSTOM = "custom"
var dockerImage string
var dockerImageVersion string
//check app name is exist
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
}
}
//检查端口
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)})
@ -248,12 +269,12 @@ 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.CustomId = m.Label
installLog.Message = "installing rely"
installLog.Class = types.NOTIFY_APP
installLog.Type = types.NOTIFY_TYPE_UNIMPORTANT
@ -281,7 +302,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
//结构体转换
@ -333,7 +354,7 @@ func InstallApp(c *gin.Context) {
// service.MyService.Notify().UpdateLog(installLog)
// return
// }
containerId, err := service.MyService.Docker().DockerContainerCreate(dockerImage+":"+dockerImageVersion, id, m, appInfo.NetworkModel)
containerId, err := service.MyService.Docker().DockerContainerCreate(dockerImage+":"+dockerImageVersion, m, appInfo.NetworkModel)
installLog.Name = appInfo.Title
installLog.Icon = appInfo.Icon
if err != nil {
@ -352,7 +373,7 @@ func InstallApp(c *gin.Context) {
// 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
@ -418,7 +439,7 @@ func InstallApp(c *gin.Context) {
}
//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
@ -449,7 +470,7 @@ func InstallApp(c *gin.Context) {
capAdd, _ := json.Marshal(m.CapAdd)
//step: 保存数据到数据库
md := model2.AppListDBModel{
CustomId: id,
CustomId: "",
Title: appInfo.Title,
//ScreenshotLink: appInfo.ScreenshotLink,
Slogan: appInfo.Tagline,
@ -485,12 +506,13 @@ func InstallApp(c *gin.Context) {
//if appInfo.NetworkModel == "host" {
// m.PortMap = m.Port
//}
service.MyService.App().SaveContainer(md)
fmt.Println(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})
}
@ -691,10 +713,16 @@ 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
@ -709,17 +737,16 @@ func UnInstallApp(c *gin.Context) {
//存在镜像正在使用的情况
// step删除镜像
service.MyService.Docker().DockerImageRemove(info.Image + ":" + info.Version)
service.MyService.Docker().DockerImageRemove(info.Image)
//step: 删除本地数据
service.MyService.App().RemoveContainerById(appId)
if info.Origin != "custom" {
//service.MyService.App().RemoveContainerById(appId)
if info.Config.Labels["origin"] != "custom" {
fmt.Println(info.HostConfig.Mounts)
//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.HostConfig.Mounts {
if strings.Contains(v.Source, info.Name) {
service.MyService.App().DelAppConfigDir(v.Source)
}
}
@ -793,7 +820,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 {
@ -889,19 +916,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
}
@ -911,77 +942,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)
envsStr, _ := json2.Marshal(m.Envs)
volumesStr, _ := json2.Marshal(m.Volumes)
devicesStr, _ := json2.Marshal(m.Devices)
capAddStr, _ := json2.Marshal(m.CapAdd)
cmdStr, _ := json.Marshal(m.Cmd)
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 || m.HostName != appInfo.HostName || !reflect.DeepEqual(string(cmdStr), appInfo.Cmd) || !reflect.DeepEqual(string(capAddStr), appInfo.CapAdd) || m.Privileged != appInfo.Privileged {
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)
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)
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" >>
//step启动容器
err = service.MyService.Docker().DockerContainerStart(containerId)
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 {
@ -1047,31 +1055,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.Cmd = string(cmdStr)
appInfo.Privileged = m.Privileged
appInfo.CapAdd = string(capAddStr)
appInfo.HostName = m.HostName
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, model2.AppNotify{})
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
@ -1160,7 +1233,7 @@ 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 需要自定义错误
@ -1168,50 +1241,81 @@ func ContainerUpdateInfo(c *gin.Context) {
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 {
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.HostConfig.Mounts); i++ {
temp := model.PathMap{
Path: strings.ReplaceAll(info.HostConfig.Mounts[i].Source, "$AppID", info.Name),
ContainerPath: info.HostConfig.Mounts[i].Target,
}
vol = append(vol, temp)
}
var driver model.PathArray
var cmd []string
json2.Unmarshal([]byte(appInfo.Cmd), &cmd)
var capAdd []string
json2.Unmarshal([]byte(appInfo.CapAdd), &capAdd)
//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.CapAdd = capAdd
m.Cmd = cmd
m.HostName = appInfo.HostName
m.Privileged = appInfo.Privileged
m.EnableUPNP = false
m.Index = info.Config.Labels["index"]
m.Position = false
m.CustomId = info.Config.Labels["custom_id"]
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
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS), Data: m})
}

View file

@ -14,6 +14,7 @@ import (
"strings"
"github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/pkg/config"
"github.com/IceWhaleTech/CasaOS/pkg/utils/file"
oasis_err2 "github.com/IceWhaleTech/CasaOS/pkg/utils/oasis_err"
"github.com/IceWhaleTech/CasaOS/service"
@ -224,10 +225,10 @@ func DirPath(c *gin.Context) {
path := c.DefaultQuery("path", "")
info := service.MyService.ZiMa().GetDirPath(path)
if path == "/DATA/AppData" {
list := service.MyService.App().GetAllDBApps()
list := service.MyService.Docker().DockerContainerList()
apps := make(map[string]string, len(list))
for _, v := range list {
apps[v.CustomId] = v.Label
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 {
@ -342,7 +343,7 @@ func GetFileUpload(c *gin.Context) {
path := c.Query("path")
dirPath := ""
hash := file.GetHashByContent([]byte(fileName))
tempDir := "/casaOS/temp/" + hash + strconv.Itoa(totalChunks) + "/"
tempDir := config.AppInfo.RootPath + "/temp/" + hash + strconv.Itoa(totalChunks) + "/"
if fileName != relative {
dirPath = strings.TrimSuffix(relative, fileName)
tempDir += dirPath
@ -381,7 +382,7 @@ func PostFileUpload(c *gin.Context) {
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) + "/"
tempDir := config.AppInfo.RootPath + "/temp/" + hash + strconv.Itoa(totalChunks) + "/"
if fileName != relative {
dirPath = strings.TrimSuffix(relative, fileName)
@ -504,7 +505,7 @@ func PutFileContent(c *gin.Context) {
path := c.PostForm("path")
content := c.PostForm("content")
if !file.Exists(path) {
c.JSON(oasis_err2.FILE_ALREADY_EXISTS, model.Result{Success: oasis_err2.FILE_ALREADY_EXISTS, Message: oasis_err2.GetMsg(oasis_err2.FILE_ALREADY_EXISTS)})
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)
@ -522,3 +523,42 @@ func PutFileContent(c *gin.Context) {
}
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))
}

View file

@ -1,18 +1,24 @@
package v1
import (
"bytes"
"encoding/base64"
"encoding/gob"
"encoding/json"
"fmt"
"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"
@ -21,33 +27,6 @@ import (
uuid "github.com/satori/go.uuid"
)
func PersonTest(c *gin.Context) {
token := c.Query("token")
_, err := uuid.FromString(token)
fmt.Println(err)
//service.MyService.Person().GetPersionInfo("fb2333a1-72b2-4cb4-9e31-61ccaffa55b9")
msg := model.MessageModel{}
msg.Type = types.PERSONHELLO
msg.Data = ""
msg.From = config.ServerInfo.Token
msg.To = token
msg.UUId = uuid.NewV4().String()
dd, err := service.Dial(msg, true)
if err == nil {
fmt.Println(err)
}
fmt.Println(dd)
user := service.MyService.Casa().GetUserInfoByShareId(token)
if reflect.DeepEqual(user, model.UserInfo{}) {
fmt.Println("空数据")
}
fmt.Println(user)
c.JSON(http.StatusOK, model.Result{Success: oasis_err2.SUCCESS, Message: oasis_err2.GetMsg(oasis_err2.SUCCESS)})
}
// @Summary Retry the file that failed to download
// @Produce application/json
// @Accept application/json
@ -135,7 +114,7 @@ func GetPersonFile(c *gin.Context) {
task.Size = 0
task.State = types.DOWNLOADAWAIT
task.Created = time.Now().Unix()
task.Type = 0
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)})
@ -197,7 +176,7 @@ func DeletePersonDownloadFile(c *gin.Context) {
// @Router /person/list [get]
func GetPersonDownloadList(c *gin.Context) {
state := c.DefaultQuery("state", "")
list := service.MyService.Download().GetDownloadListByState(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 {
@ -235,6 +214,79 @@ func PutPersonRemarks(c *gin.Context) {
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
@ -247,11 +299,39 @@ func GetPersonFriend(c *gin.Context) {
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
@ -296,10 +376,20 @@ func PostAddPersonFriend(c *gin.Context) {
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
@ -439,6 +529,17 @@ 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
@ -489,8 +590,214 @@ func DeletePersonFriend(c *gin.Context) {
// @Tags person
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /person/public [delete]
// @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,7 +23,7 @@ import (
"github.com/gin-gonic/gin"
)
// @Summary 系统信息
// @Summary check version
// @Produce application/json
// @Accept application/json
// @Tags sys
@ -50,6 +50,32 @@ func CheckVersion(c *gin.Context) {
return
}
// @Summary check client version
// @Produce application/json
// @Accept application/json
// @Tags sys
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /sys/client/version [get]
func GetClientVersion(c *gin.Context) {
need, version := version.IsClientNeedUpdate()
if need {
installLog := model2.AppNotify{}
installLog.State = 0
installLog.Message = "New version " + version.Version + " is ready, ready to upgrade"
installLog.Type = types.NOTIFY_TYPE_NEED_CONFIRM
installLog.CreatedAt = strconv.FormatInt(time.Now().Unix(), 10)
installLog.UpdatedAt = strconv.FormatInt(time.Now().Unix(), 10)
installLog.Name = "CasaOS System"
service.MyService.Notify().AddLog(installLog)
}
data := make(map[string]interface{}, 1)
data["is_need"] = need
data["version"] = version
data["current_version"] = types.CURRENTVERSION
c.JSON(http.StatusOK, model.Result{Success: oasis_err.SUCCESS, Message: oasis_err.GetMsg(oasis_err.SUCCESS), Data: data})
}
// @Summary 系统信息
// @Produce application/json
// @Accept application/json
@ -115,7 +141,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 +162,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 +178,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,
@ -257,7 +278,7 @@ func PostKillCasaOS(c *gin.Context) {
// @Tags sys
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /sys/usg/off [put]
// @Router /sys/usb/off [put]
func PutSystemOffUSBAutoMount(c *gin.Context) {
service.MyService.System().UpdateUSBAutoMount("False")
service.MyService.System().ExecUSBAutoMountShell("False")
@ -280,6 +301,7 @@ func GetSystemUSBAutoMount(c *gin.Context) {
if config.ServerInfo.USBAutoMount == "False" {
state = "False"
}
c.JSON(http.StatusOK,
model.Result{
Success: oasis_err.SUCCESS,
@ -288,6 +310,25 @@ func GetSystemUSBAutoMount(c *gin.Context) {
})
}
// @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

View file

@ -3,9 +3,11 @@ package service
import (
"context"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"runtime"
"strconv"
"strings"
"sync"
"time"
@ -19,24 +21,32 @@ import (
"github.com/docker/docker/api/types/filters"
client2 "github.com/docker/docker/client"
"github.com/pkg/errors"
uuid "github.com/satori/go.uuid"
"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 {
@ -44,73 +54,253 @@ type appStruct struct {
log loger2.OLog
}
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 {
//获取docker应用
cli, err := client2.NewClientWithOpts(client2.FromEnv, client2.WithTimeout(time.Second*5))
if err != nil {
a.log.Error("初始化client失败", "app.getmylist", "line:36", err)
return 0
}
defer cli.Close()
fts := filters.NewArgs()
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)
return 0
}
systemApp := MyService.App().GetApplicationList()
return len(containers) + len(systemApp)
}
//获取我的应用列表
func (a *appStruct) GetMyList(index, size int, position bool) *[]model2.MyAppList {
func (a *appStruct) GetMyList(index, size int, position bool) (*[]model2.MyAppList, *[]model2.MyAppList) {
//获取docker应用
cli, err := client2.NewClientWithOpts(client2.FromEnv, client2.WithTimeout(time.Second*5))
if err != nil {
a.log.Error("初始化client失败", "app.getmylist", "line:36", err)
}
defer cli.Close()
fts := filters.NewArgs()
fts.Add("label", "origin")
containers, err := cli.ContainerList(context.Background(), types.ContainerListOptions{All: true, Filters: fts})
// 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 {
a.log.Error("获取docker容器失败", "app.getmylist", "line:42", 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
}
systemApp := MyService.App().GetApplicationList()
for _, v := range systemApp {
list = append(list, model2.MyAppList{
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,
NewVersion: false,
})
}
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
for _, m := range containers {
if m.Labels["casaos"] == "casaos" {
if m.Labels["origin"] == "system" {
continue
}
// info, err := cli.ContainerInspect(context.Background(), container.ID)
// var tm string
// if err != nil {
// tm = time.Now().String()
// } else {
// tm = info.State.StartedAt
//}
_, newVersion := NewVersionApp[m.ID]
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,
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,
})
} 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,
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 {
@ -126,51 +316,21 @@ func (a *appStruct) GetSystemAppList() *[]model2.MyAppList {
//获取本地数据库应用
var lm []model2.AppListDBModel
a.db.Table(model2.CONTAINERTABLENAME).Select("title,icon,port_map,`index`,container_id,position,label,slogan,image,volumes").Find(&lm)
// 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 {
//list := []model2.MyAppList{}
//lMap := make(map[string]interface{})
// for _, dbModel := range lm {
// lMap[dbModel.ContainerId] = dbModel
// }
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
return containers
}
func (a *appStruct) GetAllDBApps() []model2.AppListDBModel {
var lm []model2.AppListDBModel
a.db.Table(model2.CONTAINERTABLENAME).Select("custom_id,title,icon,container_id,label,slogan,image").Find(&lm)
a.db.Table(model2.CONTAINERTABLENAME).Select("custom_id,title,icon,container_id,label,slogan,image,port_map").Find(&lm)
return lm
}
@ -242,7 +402,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{})
}
@ -298,31 +458,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 {
a.log.Error("获取docker容器失败", "app.getmylist", "line:42", 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
}
@ -331,19 +489,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()
}

9
service/app_test.go Normal file
View file

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

View file

@ -10,6 +10,7 @@ import (
"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"
)
@ -154,6 +155,7 @@ func (o *casaService) PushAppAnalyse(uuid, t string, name, language string) {
m.Type = t
m.Name = name
m.Language = language
m.Version = types.CURRENTVERSION
b, _ := json.Marshal(m)
head := make(map[string]string)

View file

@ -137,8 +137,42 @@ func (d *diskService) LSBLK(isUseCache bool) []model.LSBLKModel {
return nil
}
var m []model.LSBLKModel
// strStr := `{
// "blockdevices": [
// {"name":"loop0", "kname":"loop0", "path":"/dev/loop0", "maj:min":"7:0", "fsavail":"0", "fssize":"62M", "fstype":"squashfs", "fsused":"62M", "fsuse%":"100%", "fsver":"4.0", "mountpoint":"/snap/core20/1405", "label":null, "uuid":null, "ptuuid":null, "pttype":null, "parttype":null, "parttypename":null, "partlabel":null, "partuuid":null, "partflags":null, "ra":128, "ro":true, "rm":false, "hotplug":false, "model":null, "serial":null, "size":619, "state":null, "owner":"root", "group":"disk", "mode":"brw-rw----", "alignment":0, "min-io":512, "opt-io":0, "phy-sec":512, "log-sec":512, "rota":false, "sched":"mq-deadline", "rq-size":256, "type":"loop", "disc-aln":0, "disc-gran":"4K", "disc-max":"4G", "disc-zero":false, "wsame":"0B", "wwn":null, "rand":false, "pkname":null, "hctl":null, "tran":null, "subsystems":"block", "rev":null, "vendor":null, "zoned":"none", "dax":false},
// {"name":"loop1", "kname":"loop1", "path":"/dev/loop1", "maj:min":"7:1", "fsavail":"0", "fssize":"55.6M", "fstype":"squashfs", "fsused":"55.6M", "fsuse%":"100%", "fsver":"4.0", "mountpoint":"/snap/core18/2344", "label":null, "uuid":null, "ptuuid":null, "pttype":null, "parttype":null, "parttypename":null, "partlabel":null, "partuuid":null, "partflags":null, "ra":128, "ro":true, "rm":false, "hotplug":false, "model":null, "serial":null, "size":55, "state":null, "owner":"root", "group":"disk", "mode":"brw-rw----", "alignment":0, "min-io":512, "opt-io":0, "phy-sec":512, "log-sec":512, "rota":false, "sched":"mq-deadline", "rq-size":256, "type":"loop", "disc-aln":0, "disc-gran":"4K", "disc-max":"4G", "disc-zero":false, "wsame":"0B", "wwn":null, "rand":false, "pkname":null, "hctl":null, "tran":null, "subsystems":"block", "rev":null, "vendor":null, "zoned":"none", "dax":false},
// {"name":"loop2", "kname":"loop2", "path":"/dev/loop2", "maj:min":"7:2", "fsavail":"0", "fssize":"44.8M", "fstype":"squashfs", "fsused":"44.8M", "fsuse%":"100%", "fsver":"4.0", "mountpoint":"/snap/snapd/15314", "label":null, "uuid":null, "ptuuid":null, "pttype":null, "parttype":null, "parttypename":null, "partlabel":null, "partuuid":null, "partflags":null, "ra":128, "ro":true, "rm":false, "hotplug":false, "model":null, "serial":null, "size":446, "state":null, "owner":"root", "group":"disk", "mode":"brw-rw----", "alignment":0, "min-io":512, "opt-io":0, "phy-sec":512, "log-sec":512, "rota":false, "sched":"mq-deadline", "rq-size":256, "type":"loop", "disc-aln":0, "disc-gran":"4K", "disc-max":"4G", "disc-zero":false, "wsame":"0B", "wwn":null, "rand":false, "pkname":null, "hctl":null, "tran":null, "subsystems":"block", "rev":null, "vendor":null, "zoned":"none", "dax":false},
// {"name":"loop3", "kname":"loop3", "path":"/dev/loop3", "maj:min":"7:3", "fsavail":"0", "fssize":"78.9M", "fstype":"squashfs", "fsused":"78.9M", "fsuse%":"100%", "fsver":"4.0", "mountpoint":"/snap/lxd/22754", "label":null, "uuid":null, "ptuuid":null, "pttype":null, "parttype":null, "parttypename":null, "partlabel":null, "partuuid":null, "partflags":null, "ra":128, "ro":true, "rm":false, "hotplug":false, "model":null, "serial":null, "size":788, "state":null, "owner":"root", "group":"disk", "mode":"brw-rw----", "alignment":0, "min-io":512, "opt-io":0, "phy-sec":512, "log-sec":512, "rota":false, "sched":"mq-deadline", "rq-size":256, "type":"loop", "disc-aln":0, "disc-gran":"4K", "disc-max":"4G", "disc-zero":false, "wsame":"0B", "wwn":null, "rand":false, "pkname":null, "hctl":null, "tran":null, "subsystems":"block", "rev":null, "vendor":null, "zoned":"none", "dax":false},
// {"name":"loop4", "kname":"loop4", "path":"/dev/loop4", "maj:min":"7:4", "fsavail":"0", "fssize":"43.8M", "fstype":"squashfs", "fsused":"43.8M", "fsuse%":"100%", "fsver":"4.0", "mountpoint":"/snap/snapd/15177", "label":null, "uuid":null, "ptuuid":null, "pttype":null, "parttype":null, "parttypename":null, "partlabel":null, "partuuid":null, "partflags":null, "ra":128, "ro":true, "rm":false, "hotplug":false, "model":null, "serial":null, "size":436, "state":null, "owner":"root", "group":"disk", "mode":"brw-rw----", "alignment":0, "min-io":512, "opt-io":0, "phy-sec":512, "log-sec":512, "rota":false, "sched":"mq-deadline", "rq-size":256, "type":"loop", "disc-aln":0, "disc-gran":"4K", "disc-max":"4G", "disc-zero":false, "wsame":"0B", "wwn":null, "rand":false, "pkname":null, "hctl":null, "tran":null, "subsystems":"block", "rev":null, "vendor":null, "zoned":"none", "dax":false},
// {"name":"loop5", "kname":"loop5", "path":"/dev/loop5", "maj:min":"7:5", "fsavail":"0", "fssize":"55.5M", "fstype":"squashfs", "fsused":"55.5M", "fsuse%":"100%", "fsver":"4.0", "mountpoint":"/snap/core18/1997", "label":null, "uuid":null, "ptuuid":null, "pttype":null, "parttype":null, "parttypename":null, "partlabel":null, "partuuid":null, "partflags":null, "ra":128, "ro":true, "rm":false, "hotplug":false, "model":null, "serial":null, "size":554, "state":null, "owner":"root", "group":"disk", "mode":"brw-rw----", "alignment":0, "min-io":512, "opt-io":0, "phy-sec":512, "log-sec":512, "rota":false, "sched":"mq-deadline", "rq-size":256, "type":"loop", "disc-aln":0, "disc-gran":"4K", "disc-max":"4G", "disc-zero":false, "wsame":"0B", "wwn":null, "rand":false, "pkname":null, "hctl":null, "tran":null, "subsystems":"block", "rev":null, "vendor":null, "zoned":"none", "dax":false},
// {"name":"loop6", "kname":"loop6", "path":"/dev/loop6", "maj:min":"7:6", "fsavail":"0", "fssize":"80M", "fstype":"squashfs", "fsused":"80M", "fsuse%":"100%", "fsver":"4.0", "mountpoint":"/snap/lxd/22826", "label":null, "uuid":null, "ptuuid":null, "pttype":null, "parttype":null, "parttypename":null, "partlabel":null, "partuuid":null, "partflags":null, "ra":128, "ro":true, "rm":false, "hotplug":false, "model":null, "serial":null, "size":799, "state":null, "owner":"root", "group":"disk", "mode":"brw-rw----", "alignment":0, "min-io":512, "opt-io":0, "phy-sec":512, "log-sec":512, "rota":false, "sched":"mq-deadline", "rq-size":256, "type":"loop", "disc-aln":0, "disc-gran":"4K", "disc-max":"4G", "disc-zero":false, "wsame":"0B", "wwn":null, "rand":false, "pkname":null, "hctl":null, "tran":null, "subsystems":"block", "rev":null, "vendor":null, "zoned":"none", "dax":false},
// {"name":"sda", "kname":"sda", "path":"/dev/sda", "maj:min":"8:0", "fsavail":null, "fssize":null, "fstype":null, "fsused":null, "fsuse%":null, "fsver":null, "mountpoint":null, "label":null, "uuid":null, "ptuuid":"1596101a-e20d-4296-96e2-0870efce554a", "pttype":"gpt", "parttype":null, "parttypename":null, "partlabel":null, "partuuid":null, "partflags":null, "ra":128, "ro":false, "rm":false, "hotplug":false, "model":"ST1000DM003-1ER1", "serial":"Z4YCS1B6", "size":9315, "state":"running", "owner":"root", "group":"disk", "mode":"brw-rw----", "alignment":0, "min-io":4096, "opt-io":0, "phy-sec":4096, "log-sec":512, "rota":true, "sched":"mq-deadline", "rq-size":64, "type":"disk", "disc-aln":0, "disc-gran":"0B", "disc-max":"0B", "disc-zero":false, "wsame":"0B", "wwn":"0x5000c50090db103a", "rand":true, "pkname":null, "hctl":"0:0:0:0", "tran":"sata", "subsystems":"block:scsi:pci", "rev":"CC61", "vendor":"ATA ", "zoned":"none", "dax":false,
// "children": [
// {"name":"sda1", "kname":"sda1", "path":"/dev/sda1", "maj:min":"8:1", "fsavail":null, "fssize":null, "fstype":null, "fsused":null, "fsuse%":null, "fsver":null, "mountpoint":null, "label":null, "uuid":null, "ptuuid":"1596101a-e20d-4296-96e2-0870efce554a", "pttype":"gpt", "parttype":null, "parttypename":null, "partlabel":null, "partuuid":null, "partflags":null, "ra":128, "ro":false, "rm":false, "hotplug":false, "model":null, "serial":null, "size":9315, "state":null, "owner":"root", "group":"disk", "mode":"brw-rw----", "alignment":3072, "min-io":4096, "opt-io":0, "phy-sec":4096, "log-sec":512, "rota":true, "sched":"mq-deadline", "rq-size":64, "type":"part", "disc-aln":0, "disc-gran":"0B", "disc-max":"0B", "disc-zero":false, "wsame":"0B", "wwn":"0x5000c50090db103a", "rand":true, "pkname":"sda", "hctl":null, "tran":null, "subsystems":"block:scsi:pci", "rev":null, "vendor":null, "zoned":"none", "dax":false}
// ]
// },
// {"name":"sdb", "kname":"sdb", "path":"/dev/sdb", "maj:min":"8:16", "fsavail":null, "fssize":null, "fstype":null, "fsused":null, "fsuse%":null, "fsver":null, "mountpoint":null, "label":null, "uuid":null, "ptuuid":"baed02d0-e92d-4a00-9609-f94f31271a0e", "pttype":"gpt", "parttype":null, "parttypename":null, "partlabel":null, "partuuid":null, "partflags":null, "ra":128, "ro":false, "rm":false, "hotplug":false, "model":"ST1000DM003-1ER1", "serial":"W4Y51MFH", "size":9315, "state":"running", "owner":"root", "group":"disk", "mode":"brw-rw----", "alignment":0, "min-io":4096, "opt-io":0, "phy-sec":4096, "log-sec":512, "rota":true, "sched":"mq-deadline", "rq-size":64, "type":"disk", "disc-aln":0, "disc-gran":"0B", "disc-max":"0B", "disc-zero":false, "wsame":"0B", "wwn":"0x5000c5008acd2f00", "rand":true, "pkname":null, "hctl":"1:0:0:0", "tran":"sata", "subsystems":"block:scsi:pci", "rev":"CC46", "vendor":"ATA ", "zoned":"none", "dax":false,
// "children": [
// {"name":"sdb1", "kname":"sdb1", "path":"/dev/sdb1", "maj:min":"8:17", "fsavail":null, "fssize":null, "fstype":"zfs_member", "fsused":null, "fsuse%":null, "fsver":"5000", "mountpoint":null, "label":null, "uuid":null, "ptuuid":"baed02d0-e92d-4a00-9609-f94f31271a0e", "pttype":"gpt", "parttype":"0fc63daf-8483-4772-8e79-3d69d8477de4", "parttypename":"Linux filesystem", "partlabel":"primary", "partuuid":"57880cc0-2695-41c3-bf14-7161693e5bff", "partflags":null, "ra":128, "ro":false, "rm":false, "hotplug":false, "model":null, "serial":null, "size":9315, "state":null, "owner":"root", "group":"disk", "mode":"brw-rw----", "alignment":3072, "min-io":4096, "opt-io":0, "phy-sec":4096, "log-sec":512, "rota":true, "sched":"mq-deadline", "rq-size":64, "type":"part", "disc-aln":0, "disc-gran":"0B", "disc-max":"0B", "disc-zero":false, "wsame":"0B", "wwn":"0x5000c5008acd2f00", "rand":true, "pkname":"sdb", "hctl":null, "tran":null, "subsystems":"block:scsi:pci", "rev":null, "vendor":null, "zoned":"none", "dax":false}
// ]
// },
// {"name":"nvme0n1", "kname":"nvme0n1", "path":"/dev/nvme0n1", "maj:min":"259:0", "fsavail":null, "fssize":null, "fstype":null, "fsused":null, "fsuse%":null, "fsver":null, "mountpoint":null, "label":null, "uuid":null, "ptuuid":"338abc31-a3d4-4af2-9342-b53268d9e5ac", "pttype":"gpt", "parttype":null, "parttypename":null, "partlabel":null, "partuuid":null, "partflags":null, "ra":128, "ro":false, "rm":false, "hotplug":false, "model":"LITEON CL1-8D128-HP", "serial":"UJDJA01PJDH3UI", "size":1192, "state":"live", "owner":"root", "group":"disk", "mode":"brw-rw----", "alignment":0, "min-io":512, "opt-io":0, "phy-sec":512, "log-sec":512, "rota":false, "sched":"none", "rq-size":255, "type":"disk", "disc-aln":0, "disc-gran":"512B", "disc-max":"2T", "disc-zero":false, "wsame":"0B", "wwn":"eui.0023035630392fe7", "rand":false, "pkname":null, "hctl":null, "tran":"nvme", "subsystems":"block:nvme:pci", "rev":null, "vendor":null, "zoned":"none", "dax":false,
// "children": [
// {"name":"nvme0n1p1", "kname":"nvme0n1p1", "path":"/dev/nvme0n1p1", "maj:min":"259:1", "fsavail":null, "fssize":null, "fstype":null, "fsused":null, "fsuse%":null, "fsver":null, "mountpoint":null, "label":null, "uuid":null, "ptuuid":"338abc31-a3d4-4af2-9342-b53268d9e5ac", "pttype":"gpt", "parttype":"21686148-6449-6e6f-744e-656564454649", "parttypename":"BIOS boot", "partlabel":null, "partuuid":"b2bac638-9468-449f-9669-79be44e3c80d", "partflags":null, "ra":128, "ro":false, "rm":false, "hotplug":false, "model":null, "serial":null, "size":1, "state":null, "owner":"root", "group":"disk", "mode":"brw-rw----", "alignment":0, "min-io":512, "opt-io":0, "phy-sec":512, "log-sec":512, "rota":false, "sched":"none", "rq-size":255, "type":"part", "disc-aln":0, "disc-gran":"512B", "disc-max":"2T", "disc-zero":false, "wsame":"0B", "wwn":"eui.0023035630392fe7", "rand":false, "pkname":"nvme0n1", "hctl":null, "tran":"nvme", "subsystems":"block:nvme:pci", "rev":null, "vendor":null, "zoned":"none", "dax":false},
// {"name":"nvme0n1p2", "kname":"nvme0n1p2", "path":"/dev/nvme0n1p2", "maj:min":"259:2", "fsavail":"1.2G", "fssize":"1.4G", "fstype":"ext4", "fsused":"131.5M", "fsuse%":"9%", "fsver":"1.0", "mountpoint":"/boot", "label":null, "uuid":"cd8164e9-bf7a-4684-8a3b-1d9f209b4930", "ptuuid":"338abc31-a3d4-4af2-9342-b53268d9e5ac", "pttype":"gpt", "parttype":"0fc63daf-8483-4772-8e79-3d69d8477de4", "parttypename":"Linux filesystem", "partlabel":null, "partuuid":"42ed9ed6-1221-4bea-901a-bc2f7b7cb9e1", "partflags":null, "ra":128, "ro":false, "rm":false, "hotplug":false, "model":null, "serial":null, "size":15, "state":null, "owner":"root", "group":"disk", "mode":"brw-rw----", "alignment":0, "min-io":512, "opt-io":0, "phy-sec":512, "log-sec":512, "rota":false, "sched":"none", "rq-size":255, "type":"part", "disc-aln":0, "disc-gran":"512B", "disc-max":"2T", "disc-zero":false, "wsame":"0B", "wwn":"eui.0023035630392fe7", "rand":false, "pkname":"nvme0n1", "hctl":null, "tran":"nvme", "subsystems":"block:nvme:pci", "rev":null, "vendor":null, "zoned":"none", "dax":false},
// {"name":"nvme0n1p3", "kname":"nvme0n1p3", "path":"/dev/nvme0n1p3", "maj:min":"259:3", "fsavail":null, "fssize":null, "fstype":"LVM2_member", "fsused":null, "fsuse%":null, "fsver":"LVM2 001", "mountpoint":null, "label":null, "uuid":"0G7ryL-p2Ks-i9HS-wvEO-lXHs-oyZX-1KIlZO", "ptuuid":"338abc31-a3d4-4af2-9342-b53268d9e5ac", "pttype":"gpt", "parttype":"0fc63daf-8483-4772-8e79-3d69d8477de4", "parttypename":"Linux filesystem", "partlabel":null, "partuuid":"f6ae2e8c-14ae-4d94-89f1-2c154e909843", "partflags":null, "ra":128, "ro":false, "rm":false, "hotplug":false, "model":null, "serial":null, "size":1177, "state":null, "owner":"root", "group":"disk", "mode":"brw-rw----", "alignment":0, "min-io":512, "opt-io":0, "phy-sec":512, "log-sec":512, "rota":false, "sched":"none", "rq-size":255, "type":"part", "disc-aln":0, "disc-gran":"512B", "disc-max":"2T", "disc-zero":false, "wsame":"0B", "wwn":"eui.0023035630392fe7", "rand":false, "pkname":"nvme0n1", "hctl":null, "tran":"nvme", "subsystems":"block:nvme:pci", "rev":null, "vendor":null, "zoned":"none", "dax":false,
// "children": [
// {"name":"ubuntu--vg-ubuntu--lv", "kname":"dm-0", "path":"/dev/mapper/ubuntu--vg-ubuntu--lv", "maj:min":"253:0", "fsavail":"78.6G", "fssize":"115.4G", "fstype":"ext4", "fsused":"30.9G", "fsuse%":"27%", "fsver":"1.0", "mountpoint":"/", "label":null, "uuid":"e8a9082f-3643-4820-a5e5-05817d7738c6", "ptuuid":null, "pttype":null, "parttype":null, "parttypename":null, "partlabel":null, "partuuid":null, "partflags":null, "ra":128, "ro":false, "rm":false, "hotplug":false, "model":null, "serial":null, "size":1177, "state":"running", "owner":"root", "group":"disk", "mode":"brw-rw----", "alignment":0, "min-io":512, "opt-io":0, "phy-sec":512, "log-sec":512, "rota":false, "sched":null, "rq-size":128, "type":"lvm", "disc-aln":0, "disc-gran":"512B", "disc-max":"2T", "disc-zero":false, "wsame":"0B", "wwn":null, "rand":false, "pkname":"nvme0n1p3", "hctl":null, "tran":null, "subsystems":"block", "rev":null, "vendor":null, "zoned":"none", "dax":false}
// ]
// }
// ]
// }
// ]
// }`
// fmt.Println(gjson.Get(strStr, "blockdevices").String())
err := json2.Unmarshal([]byte(gjson.Get(string(str), "blockdevices").String()), &m)
if err != nil {
fmt.Println(err)
d.log.Error("json ummarshal error", err)
}

View file

@ -16,6 +16,7 @@ import (
"github.com/containerd/containerd/cio"
"github.com/containerd/containerd/namespaces"
"github.com/containerd/containerd/oci"
"github.com/pkg/errors"
"github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/pkg/docker"
@ -45,7 +46,8 @@ import (
type DockerService interface {
DockerPullImage(imageName string, m model2.AppNotify) 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,8 +60,9 @@ 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
}
@ -69,6 +72,19 @@ type dockerService struct {
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 {
cli, _ := client2.NewClientWithOpts(client2.FromEnv)
exec, err := cli.ContainerExecCreate(context.Background(), container_id, types.ExecConfig{
@ -119,10 +135,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 +187,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 {
@ -342,6 +353,18 @@ func (ds *dockerService) DockerPullImage(imageName string, m model2.AppNotify) e
}
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 +372,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"
}
@ -442,12 +465,12 @@ 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 {
@ -495,12 +518,19 @@ func (ds *dockerService) DockerContainerCreate(imageName string, containerDbId s
}
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,
}
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["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
@ -512,7 +542,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
}
@ -695,6 +725,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
}

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}
}

View file

@ -11,7 +11,7 @@ type DownloadService interface {
SaveDownload(m model2.PersonDownloadDBModel)
DelDownload(uuid string)
GetDownloadById(uuid string) model2.PersonDownloadDBModel
GetDownloadListByState(state string) []model2.PersonDownloadDBModel
GetDownloadListByState(state string, t int) []model2.PersonDownloadDBModel
SetDownloadError(m model2.PersonDownloadDBModel)
GetDownloadListByPath(m model2.PersonDownloadDBModel) int
}
@ -48,11 +48,11 @@ func (d *downloadService) GetDownloadById(uuid string) model2.PersonDownloadDBMo
d.db.Model(m).Where("uuid = ?", uuid).First(&m)
return m
}
func (d *downloadService) GetDownloadListByState(state string) (list []model2.PersonDownloadDBModel) {
func (d *downloadService) GetDownloadListByState(state string, t int) (list []model2.PersonDownloadDBModel) {
if len(state) == 0 {
d.db.Find(&list)
d.db.Where("type = ?", t).Find(&list)
} else {
d.db.Where("state = ?", state).Find(&list)
d.db.Where("state = ? AND type= ?", state, t).Find(&list)
}
return

View file

@ -1,9 +1,20 @@
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"
)
@ -11,17 +22,26 @@ 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)
}
@ -31,6 +51,9 @@ func (p *friendService) DeleteFriend(m model2.FriendModel) {
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)
}
@ -43,7 +66,14 @@ 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)
@ -59,6 +89,67 @@ 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,8 @@
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"`
@ -52,15 +49,19 @@ func (p *AppListDBModel) TableName() string {
}
type MyAppList struct {
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"`
Port string `json:"port"`
UpTime string `json:"up_time"`
Slogan string `json:"slogan"`
Rely model.MapStrings `json:"rely"` //[{"mysql":"id"},{"mysql":"id"}]
Image string `json:"image"`
Volumes string `json:"volumes"`
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"`
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"`
}

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

@ -3,7 +3,7 @@ package model
type PersonDownloadDBModel struct {
UUID string `gorm:"column:uuid;primary_key" json:"uuid"`
State int `json:"state"` //
Type int `json:"type"` //defult 1
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

View file

@ -1,7 +1,7 @@
package model
type FriendModel struct {
State int `json:"state"` //Reserved
State int `json:"state"`
CreatedAt int64 `gorm:"autoCreateTime" json:"created_at"`
UpdatedAt int64 `gorm:"autoCreateTime;autoUpdateTime" json:"updated_at"`
NickName string `json:"nick_name"`
@ -12,6 +12,8 @@ type FriendModel struct {
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 {

View file

@ -15,8 +15,10 @@ import (
"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"
@ -26,6 +28,7 @@ import (
type PersonService interface {
GetPersionInfo(token string) (m model.PersionModel, err error)
GetPersionNetWorkTypeDetection() string
}
type personService struct {
@ -34,11 +37,12 @@ type personService struct {
var IpInfo model.PersionModel
var CancelList map[string]string
var InternalInspection map[string][]string
func PushIpInfo(token string) {
m := model.PersionModel{}
m.Ips = GetDeviceAllIP()
m.Ips = ip_helper.GetDeviceAllIP("")
m.Token = token
b, _ := json.Marshal(m)
@ -54,6 +58,16 @@ func (p *personService) GetPersionInfo(token string) (m model.PersionModel, err
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}
@ -193,6 +207,11 @@ func ProcessingContent(stream quic.Stream) {
} 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
@ -200,10 +219,9 @@ func ProcessingContent(stream quic.Stream) {
break
} else if m.Type == types.PERSONDOWNLOAD {
SendFileData(stream, m.Data.(string), m.From, m.UUId)
SendFileData(stream, m.Data.(string), m.From, m.UUId, types.PERSONDOWNLOAD)
break
} else if m.Type == types.PERSONADDFRIEND {
fmt.Println("有用户来请求加好友", m)
friend := model2.FriendModel{}
dataModelByte, _ := json.Marshal(m.Data)
err := json.Unmarshal(dataModelByte, &friend)
@ -225,6 +243,7 @@ func ProcessingContent(stream quic.Stream) {
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)
@ -236,14 +255,18 @@ func ProcessingContent(stream quic.Stream) {
// 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{}
@ -254,10 +277,96 @@ func ProcessingContent(stream quic.Stream) {
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
@ -269,7 +378,7 @@ func ProcessingContent(stream quic.Stream) {
}
//文件分片发送
func SendFileData(stream quic.Stream, filePath, to, uuid string) error {
func SendFileData(stream quic.Stream, filePath, to, uuid, t string) error {
summary := model.FileSummaryModel{}
msg := model.MessageModel{}
@ -341,7 +450,7 @@ func SendFileData(stream quic.Stream, filePath, to, uuid string) error {
tran.Length = length
fileMsg := model.MessageModel{}
fileMsg.Type = types.PERSONDOWNLOAD
fileMsg.Type = t
fileMsg.Data = tran
fileMsg.From = config.ServerInfo.Token
fileMsg.To = to
@ -356,5 +465,18 @@ func SendFileData(stream quic.Stream, filePath, to, uuid string) error {
}
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

@ -12,7 +12,7 @@ var Cache *cache.Cache
var MyService Repository
var WebSocketConns []*websocket.Conn
var NewVersionApp map[string]string
var SocketRun bool
type Repository interface {
@ -34,6 +34,7 @@ type Repository interface {
Person() PersonService
Friend() FriendService
Download() DownloadService
DownRecord() DownRecordService
}
func NewService(db *gorm.DB, log loger2.OLog) Repository {
@ -57,6 +58,7 @@ func NewService(db *gorm.DB, log loger2.OLog) Repository {
person: NewPersonService(db),
friend: NewFriendService(db),
download: NewDownloadService(db),
downrecord: NewDownRecordService(db),
}
}
@ -79,6 +81,11 @@ type store struct {
person PersonService
friend FriendService
download DownloadService
downrecord DownRecordService
}
func (c *store) DownRecord() DownRecordService {
return c.downrecord
}
func (c *store) Download() DownloadService {

View file

@ -7,6 +7,7 @@ import (
"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"
)
@ -20,6 +21,8 @@ type SystemService interface {
GetTimeZone() string
UpdateUSBAutoMount(state string)
ExecUSBAutoMountShell(state string)
UpAppOrderFile(str string)
GetAppOrderFile() []byte
}
type systemService struct {
log loger.OLog
@ -62,9 +65,15 @@ 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("system").Key("USBAutoMount").SetValue(state)
config.Cfg.Section("server").Key("USBAutoMount").SetValue(state)
config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath)
}
func (s *systemService) UpSystemPort(port string) {

View file

@ -11,13 +11,16 @@ import (
"net"
"os"
path2 "path"
"reflect"
"strconv"
"strings"
"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/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"
@ -29,6 +32,65 @@ 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(), 10*time.Second)
defer cancel()
@ -142,139 +204,17 @@ func ReadContent(stream quic.Stream) {
}
m := model.MessageModel{}
err = json.Unmarshal(messageByte, &m)
fmt.Println("客户端", m)
if err != nil {
fmt.Println(err)
break
}
if m.Type == types.PERSONDOWNLOAD {
dataModelByte, _ := json.Marshal(m.Data)
dataModel := model.TranFileModel{}
err := json.Unmarshal(dataModelByte, &dataModel)
if err != nil {
fmt.Println(err)
continue
}
dataLengthByte := make([]byte, 8)
_, err = io.ReadFull(stream, dataLengthByte)
if err != nil {
fmt.Println(err)
continue
}
dataLength, err := strconv.Atoi(string(dataLengthByte))
if err != nil {
fmt.Println(err)
continue
}
dataByte := make([]byte, dataLength)
_, err = io.ReadFull(stream, dataByte)
if err != nil {
fmt.Println(err)
continue
}
sum := md5.Sum(dataByte)
hash := hex.EncodeToString(sum[:])
if dataModel.Hash != hash {
fmt.Println("hash不匹配", hash, dataModel.Hash)
continue
}
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)
continue
}
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)
}
r := SaveFile(m, stream)
if r {
break
}
} else if m.Type == types.PERSONSUMMARY {
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)
fullPath := path2.Join(task.LocalPath, task.Name)
task.State = types.DOWNLOADING
if len(dataModel.Message) > 0 {
task.State = types.DOWNLOADERROR
task.Error = dataModel.Message
}
if 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 = 0
task.From = m.From
MyService.Download().SaveDownload(task)
Summary(m, "download")
} else if m.Type == types.PERSONCONNECTION {
if len(m.Data.(string)) > 0 {
UDPAddressMap[m.From] = m.Data.(string)
@ -295,7 +235,7 @@ func ReadContent(stream quic.Stream) {
go Dial(msg, false)
Message <- m
break
} else if m.Type == "get_ip" {
} else if m.Type == types.PERSONGETIP {
notify := model2.AppNotify{}
notify.CustomId = m.From
if len(m.Data.(string)) == 0 {
@ -312,9 +252,32 @@ func ReadContent(stream quic.Stream) {
go MyService.Notify().SendText(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
}
}
@ -334,23 +297,55 @@ func SendIPToServer() {
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++ {
msg := model.MessageModel{}
msg.Type = "get_ip"
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()
Dial(msg, true)
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 := model2.AppNotify{}
notify.CustomId = data.From
notify.Type = types.NOTIFY_TYPE_PERSION_FIRNED_LEAVE
go MyService.Notify().SendText(notify)
msg.Type = types.PERSONHELLO
msg.Data = ""
msg.From = config.ServerInfo.Token
msg.To = list[i].Token
msg.UUId = uuid.NewV4().String()
if _, ok := UDPAddressMap[list[i].Token]; ok {
go Dial(msg, false)
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)
@ -368,3 +363,140 @@ func LoopFriend() {
}
}
//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

@ -39,6 +39,7 @@ type ZiMaService interface {
CreateFile(path string) (int, error)
RenameFile(oldF, newF string) (int, error)
GetCpuInfo() []cpu.InfoStat
GetDeviceTree() string
}
var NetArray [][]model.IOCountersStat
@ -86,6 +87,16 @@ 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{}
@ -139,6 +150,10 @@ func (c *zima) GetNet(physics bool) []string {
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脚本参数 { 网卡名称 }
func (c *zima) GetNetState(name string) string {
return command2.ExecResultStr("source " + config.AppInfo.ProjectPath + "/shell/helper.sh ;CatNetCardState " + name)

View file

@ -32,7 +32,7 @@ GetNetCard() {
GetTimeZone(){
timedatectl | grep "Time zone" | awk '{print $3}'
timedatectl | grep "Time zone" | awk '{printf $3}'
}
#查看网卡状态
@ -341,4 +341,8 @@ USB_Remove_File() {
((EUID)) && sudo_cmd="sudo"
$sudo_cmd rm -fr /etc/udev/rules.d/11-usb-mount.rules
$sudo_cmd rm -fr /etc/systemd/system/usb-mount@.service
}
}
GetDeviceTree(){
cat /proc/device-tree/model
}

View file

@ -1 +0,0 @@
package types

7
types/friend.go Normal file
View file

@ -0,0 +1,7 @@
package types
const (
FRIENDSTATEDEFAULT = iota
FRIENDSTATEWAIT
FRIENDSTATEREQUEST
)

View file

@ -12,6 +12,7 @@ const (
NOTIFY_TYPE_INSTALL_LOG
NOTIFY_TYPE_PERSION_FIRNED_LEAVE
NOTIFY_TYPE_PERSION_FIRNED_LIVE
NOTIFY_TYPE_HEALTH_CHECK
)
const (

View file

@ -1,9 +1,24 @@
package types
const PERSONADDFRIEND = "add_user"
const PERSONAGREEFRIEND = "agree_user"
const PERSONDOWNLOAD = "file_data"
const PERSONSUMMARY = "summary"
const PERSONGETIP = "get_ip"
const PERSONCONNECTION = "connection"
const PERSONDIRECTORY = "directory"
const PERSONHELLO = "hello"
const PERSONSHAREID = "share_id"
const PERSONUPLOAD = "upload"
const PERSONUPLOADDATA = "upload_data"
const PERSONINTERNALINSPECTION = "internal_inspection"
const PERSONPING = "ping"
const PERSONIMAGETHUMBNAIL = "image_thumbnail"
const PERSONCANCEL = "cancel" // Cancel Download
const (
PERSONFILEDOWNLOAD = iota //default state
PERSONFILEUPLOAD
PERSONFILERECEIVEUPLOAD //receive upload file
)

View file

@ -1,5 +1,15 @@
/*
* @Author: LinkLeong link@icewhale.com
* @Date: 2022-02-17 18:53:22
* @LastEditors: LinkLeong
* @LastEditTime: 2022-05-13 18:08:25
* @FilePath: /CasaOS/types/system.go
* @Description:
* @Website: https://www.casaos.io
* Copyright (c) 2022 by icewhale, All Rights Reserved.
*/
package types
const CURRENTVERSION = "0.3.0"
const CURRENTVERSION = "0.3.1"
const BODY = "<li>Add CasaConnect function, now you can share private files peer-to-peer with your friends.</li><li>Add a widget for network traffic monitoring</li><li>Updated the initial directory of Files to the Root directory</li><li>Fix the application ipv6 opening problem</li>"
const BODY = ""

View file

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/ui/img/icon/mstile-150x150.png"/>
<TileColor>#da532c</TileColor>
</tile>
</msapplication>
</browserconfig>
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/ui/img/icon/mstile-150x150.png"/>
<TileColor>#da532c</TileColor>
</tile>
</msapplication>
</browserconfig>

View file

@ -1,14 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.2.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<style type="text/css">
.st0{fill:none;stroke:#363636;stroke-width:2;stroke-linejoin:round;stroke-miterlimit:2;}
@media ( prefers-color-scheme: dark ) {
.st0{fill:none;stroke:#FFFFFF;stroke-width:2;stroke-linejoin:round;stroke-miterlimit:2;}
}
</style>
<path class="st0" d="M12,22c5.5,0,10-4.5,10-10S17.5,2,12,2S2,6.5,2,12S6.5,22,12,22z"/>
<path class="st0" d="M12,22c3.9,0,7-3.1,7-7s-3.1-7-7-7s-7,3.1-7,7S8.1,22,12,22z"/>
<path class="st0" d="M12,22c2.2,0,4-1.8,4-4s-1.8-4-4-4s-4,1.8-4,4S9.8,22,12,22z"/>
</svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.2.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<style type="text/css">
.st0{fill:none;stroke:#363636;stroke-width:2;stroke-linejoin:round;stroke-miterlimit:2;}
@media ( prefers-color-scheme: dark ) {
.st0{fill:none;stroke:#FFFFFF;stroke-width:2;stroke-linejoin:round;stroke-miterlimit:2;}
}
</style>
<path class="st0" d="M12,22c5.5,0,10-4.5,10-10S17.5,2,12,2S2,6.5,2,12S6.5,22,12,22z"/>
<path class="st0" d="M12,22c3.9,0,7-3.1,7-7s-3.1-7-7-7s-7,3.1-7,7S8.1,22,12,22z"/>
<path class="st0" d="M12,22c2.2,0,4-1.8,4-4s-1.8-4-4-4s-4,1.8-4,4S9.8,22,12,22z"/>
</svg>

Before

Width:  |  Height:  |  Size: 863 B

After

Width:  |  Height:  |  Size: 877 B

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

67
web/img/Files.svg Normal file
View file

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="64" height="64" version="1.1" viewBox="0 0 16.933 16.933" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<style type="text/css"/>
<linearGradient id="linearGradient1951" x1="100" x2="133.19" y1="17.453" y2="51.606" gradientTransform="matrix(.26458 0 0 .24792 -20.108 .79576)" gradientUnits="userSpaceOnUse">
<stop stop-color="#26abe7" offset="0"/>
<stop stop-color="#0669bc" offset="1"/>
</linearGradient>
<linearGradient id="linearGradient11110" x1=".52917" x2="16.404" y1="5.3815" y2="5.3815" gradientTransform="matrix(1 0 0 .93702 6.5e-7 .92263)" gradientUnits="userSpaceOnUse">
<stop stop-color="#fff" offset="0"/>
<stop stop-color="#fff" stop-opacity="0" offset="1"/>
</linearGradient>
<linearGradient id="linearGradient1911" x1="25.085" x2="25.085" y1="24.031" y2="26.412" gradientTransform="translate(-17.897,-21.369)" gradientUnits="userSpaceOnUse">
<stop stop-color="#0271ca" offset="0"/>
<stop stop-color="#0768ba" offset="1"/>
</linearGradient>
<linearGradient id="linearGradient9169" x1="7.4082" x2="7.4082" y1="4.2333" y2="5.5561" gradientUnits="userSpaceOnUse">
<stop stop-color="#fff" offset="0"/>
<stop stop-color="#e3e4e5" offset="1"/>
</linearGradient>
<linearGradient id="linearGradient23227" x1=".52916" x2="16.404" y1="14.196" y2="14.196" gradientTransform="translate(-18.282 -.015458)" gradientUnits="userSpaceOnUse">
<stop stop-color="#0a5ba8" stop-opacity=".99608" offset="0"/>
<stop stop-color="#104a8c" stop-opacity=".99608" offset="1"/>
</linearGradient>
</defs>
<metadata>
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
</cc:Work>
</rdf:RDF>
</metadata>
<path d="m1.1977 2.2644c-0.3653 0-0.66145 0.32575-0.66145 0.72759v2.9636c-0.0026 0.02629-0.0072 0.052-0.0072 0.07906v7.9373c0 0.40184 0.29614 0.72759 0.66144 0.72759h14.552c0.36531 0 0.66144-0.32575 0.66144-0.72759v-9.393c0-0.40184-0.29614-0.72759-0.66144-0.72759v5.16e-4h-8.1993l-0.89864-1.1095s-0.36607-0.478-1.0583-0.478h-1.3229z" color="#000000" color-rendering="auto" dominant-baseline="auto" fill="url(#linearGradient1911)" image-rendering="auto" shape-rendering="auto" solid-color="#000000" stop-color="#000000" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;font-variation-settings:normal;inline-size:0;isolation:auto;mix-blend-mode:normal;shape-margin:0;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal"/>
<g fill="#5e4aa6" stroke-width=".26458">
<circle cx="-330.35" cy="-328.38" r="0"/>
<circle cx="-312.11" cy="-326.25" r="0"/>
<circle cx="-306.02" cy="-333.07" r="0"/>
<circle cx="-308.84" cy="-326.01" r="0"/>
<circle cx="-328.8" cy="-330.45" r="0"/>
<g transform="translate(24.108 -4.592)">
<circle cx="-330.35" cy="-328.38" r="0"/>
<circle cx="-312.11" cy="-326.25" r="0"/>
<circle cx="-306.02" cy="-333.07" r="0"/>
<circle cx="-308.84" cy="-326.01" r="0"/>
<circle cx="-328.8" cy="-330.45" r="0"/>
</g>
<g transform="translate(2.3479 -14.944)">
<circle cx="-330.35" cy="-328.38" r="0"/>
<circle cx="-312.11" cy="-326.25" r="0"/>
<circle cx="-306.02" cy="-333.07" r="0"/>
<circle cx="-308.84" cy="-326.01" r="0"/>
<circle cx="-328.8" cy="-330.45" r="0"/>
</g>
<g transform="translate(.000295 -.00036978)">
<circle cx="-330.35" cy="-328.38" r="0"/>
<circle cx="-312.11" cy="-326.25" r="0"/>
<circle cx="-306.02" cy="-333.07" r="0"/>
<circle cx="-308.84" cy="-326.01" r="0"/>
<circle cx="-328.8" cy="-330.45" r="0"/>
</g>
</g>
<path d="m0.52916 13.691v0.28215c0 0.20092 0.07357 0.3825 0.19327 0.51417s0.28553 0.21342 0.46818 0.21342h14.552c0.18265 0 0.34796-0.08175 0.46766-0.21342s0.19378-0.31325 0.19378-0.51417v-0.28215c0 0.20092-0.07409 0.3825-0.19378 0.51417-0.1197 0.13167-0.28501 0.21342-0.46766 0.21342h-14.552c-0.18265 0-0.34848-0.08176-0.46818-0.21342s-0.19327-0.31325-0.19327-0.51417z" fill="url(#linearGradient23227)" stroke-width=".26458"/>
<rect x=".79373" y="4.2333" width="15.346" height="8.9957" ry=".79373" fill="url(#linearGradient9169)" stroke-linecap="round" stroke-linejoin="round" stroke-width=".1077"/>
<path d="m7.542 4.7624s-0.35919 0.44853-1.1575 0.74229c-0.03271 0.0055-0.06509 0.010846-0.10077 0.015978h-5.4967c-0.21986 0-0.41824 0.082975-0.56171 0.21741-0.13937 0.13058-0.22042 0.31234-0.2253 0.51133v0.48421c0 0.02369-0.0021296 0.045593 0 0.073482l0.52916 6.9301c0.014339 0.18779 0.07357 0.35841 0.19327 0.48179s0.28553 0.19998 0.46818 0.19998h14.552c0.18265 0 0.34796-0.0766 0.46766-0.19998s0.18179-0.29386 0.19378-0.48179l0.52916-8.2935c0.011991-0.18793-0.07408-0.35889-0.19378-0.48227-0.11945-0.1231-0.28449-0.19866-0.46665-0.19901h-8.0112z" fill="url(#linearGradient1951)"/>
<path d="m7.542 4.7624s-0.35919 0.44853-1.1575 0.74229c-0.03271 0.0055-0.06509 0.010846-0.10077 0.015978h-5.49c-0.21986 0-0.41824 0.082975-0.56171 0.21741-0.13937 0.13058-0.22041 0.31234-0.2253 0.51132l-0.0067094 0.36074c0.0049-0.19899 0.092639-0.38123 0.23201-0.51181 0.14347-0.13443 0.34185-0.21741 0.56171-0.21741h5.49c0.03568-0.00513 0.06806-0.01048 0.10077-0.015978 0.75911-0.24646 1.2883-0.74229 1.2883-0.74229h8.5998c0.18216 3.471e-4 0.34718 0.075914 0.46663 0.19901 0.1197 0.12338 0.19378 0.29401 0.19378 0.48227v-0.36025c0-0.18827-0.07408-0.35889-0.19378-0.48227-0.11945-0.1231-0.28447-0.19866-0.46663-0.19901h-8.0112z" fill="url(#linearGradient11110)" opacity=".3"/>
</svg>

After

Width:  |  Height:  |  Size: 5.8 KiB

View file

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

Before

Width:  |  Height:  |  Size: 752 B

After

Width:  |  Height:  |  Size: 763 B

View file

@ -1,25 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 64 64" enable-background="new 0 0 64 64" xml:space="preserve">
<g>
<g>
<g>
<path fill="#A4C639" d="M32,0c17.7,0,32,14.3,32,32S49.7,64,32,64S0,49.7,0,32S14.3,0,32,0z"/>
</g>
</g>
<g>
<g>
<path fill="#FFFFFF" d="M16.6,24.6c-1.4,0-2.6,1.2-2.6,2.6L14,38c0,1.4,1.2,2.6,2.6,2.6c1.5,0,2.6-1.2,2.6-2.6l0-10.9
C19.3,25.7,18.1,24.6,16.6,24.6 M37.8,14.8l1.8-3.3c0.1-0.2,0-0.4-0.1-0.5c-0.2-0.1-0.4,0-0.5,0.1l-1.9,3.3
c-1.6-0.7-3.3-1.1-5.1-1.1c-1.8,0-3.6,0.4-5.1,1.1L25,11.2C24.9,11,24.7,11,24.5,11c-0.2,0.1-0.2,0.3-0.1,0.5l1.8,3.3
c-3.6,1.8-6,5.3-6,9.3l23.6,0C43.8,20.2,41.4,16.7,37.8,14.8 M26.6,19.9c-0.5,0-1-0.4-1-1c0-0.5,0.4-1,1-1c0.5,0,1,0.4,1,1
C27.6,19.5,27.2,19.9,26.6,19.9 M37.4,19.9c-0.5,0-1-0.4-1-1c0-0.5,0.4-1,1-1c0.5,0,1,0.4,1,1C38.4,19.5,37.9,19.9,37.4,19.9
M20.3,25.1l0,16.8c0,1.5,1.3,2.8,2.8,2.8l1.9,0l0,5.7c0,1.4,1.2,2.6,2.6,2.6c1.5,0,2.6-1.2,2.6-2.6l0-5.7l3.5,0l0,5.7
c0,1.4,1.2,2.6,2.6,2.6c1.5,0,2.6-1.2,2.6-2.6l0-5.7l1.9,0c1.5,0,2.8-1.2,2.8-2.8l0-16.8L20.3,25.1z M50,27.2
c0-1.4-1.2-2.6-2.6-2.6c-1.4,0-2.6,1.2-2.6,2.6l0,10.9c0,1.4,1.2,2.6,2.6,2.6c1.4,0,2.6-1.2,2.6-2.6L50,27.2z"/>
</g>
</g>
</g>
</svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 64 64" enable-background="new 0 0 64 64" xml:space="preserve">
<g>
<g>
<g>
<path fill="#A4C639" d="M32,0c17.7,0,32,14.3,32,32S49.7,64,32,64S0,49.7,0,32S14.3,0,32,0z"/>
</g>
</g>
<g>
<g>
<path fill="#FFFFFF" d="M16.6,24.6c-1.4,0-2.6,1.2-2.6,2.6L14,38c0,1.4,1.2,2.6,2.6,2.6c1.5,0,2.6-1.2,2.6-2.6l0-10.9
C19.3,25.7,18.1,24.6,16.6,24.6 M37.8,14.8l1.8-3.3c0.1-0.2,0-0.4-0.1-0.5c-0.2-0.1-0.4,0-0.5,0.1l-1.9,3.3
c-1.6-0.7-3.3-1.1-5.1-1.1c-1.8,0-3.6,0.4-5.1,1.1L25,11.2C24.9,11,24.7,11,24.5,11c-0.2,0.1-0.2,0.3-0.1,0.5l1.8,3.3
c-3.6,1.8-6,5.3-6,9.3l23.6,0C43.8,20.2,41.4,16.7,37.8,14.8 M26.6,19.9c-0.5,0-1-0.4-1-1c0-0.5,0.4-1,1-1c0.5,0,1,0.4,1,1
C27.6,19.5,27.2,19.9,26.6,19.9 M37.4,19.9c-0.5,0-1-0.4-1-1c0-0.5,0.4-1,1-1c0.5,0,1,0.4,1,1C38.4,19.5,37.9,19.9,37.4,19.9
M20.3,25.1l0,16.8c0,1.5,1.3,2.8,2.8,2.8l1.9,0l0,5.7c0,1.4,1.2,2.6,2.6,2.6c1.5,0,2.6-1.2,2.6-2.6l0-5.7l3.5,0l0,5.7
c0,1.4,1.2,2.6,2.6,2.6c1.5,0,2.6-1.2,2.6-2.6l0-5.7l1.9,0c1.5,0,2.8-1.2,2.8-2.8l0-16.8L20.3,25.1z M50,27.2
c0-1.4-1.2-2.6-2.6-2.6c-1.4,0-2.6,1.2-2.6,2.6l0,10.9c0,1.4,1.2,2.6,2.6,2.6c1.4,0,2.6-1.2,2.6-2.6L50,27.2z"/>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -1,28 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 64 64" style="enable-background:new 0 0 64 64;" xml:space="preserve">
<style type="text/css">
.st0{fill:#F4F4F4;}
.st1{opacity:0.1;fill-rule:evenodd;clip-rule:evenodd;fill:url(#SVGID_1_);enable-background:new ;}
.st2{fill:#999999;}
</style>
<g id="图层_1">
<g transform="scale(.26458)">
<path class="st0" d="M49.1,15.1h143.6c10.4,0,18.9,8.5,18.9,18.9v173.9c0,10.4-8.5,18.9-18.9,18.9H49.1
c-10.4,0-18.9-8.5-18.9-18.9V34C30.2,23.6,38.7,15.1,49.1,15.1z"/>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="-957.3084" y1="2609.5137" x2="-955.1654" y2="2607.3706" gradientTransform="matrix(2.3333 0 0 -2.3333 2411.3325 6281.5693)">
<stop offset="0" style="stop-color:#000000"/>
<stop offset="1" style="stop-color:#000000;stop-opacity:0"/>
</linearGradient>
<path class="st1" d="M211.7,173.9l-52.9,52.9h34c10.5,0,18.9-8.4,18.9-18.9V173.9z"/>
</g>
</g>
<g id="图层_2">
<path class="st2" d="M41.1,31.4c0-4.2,3.4-6.2,3.6-6.3c-2-2.9-5-3.3-6.1-3.3c-2.6-0.3-5.1,1.5-6.4,1.5c-1.3,0-3.3-1.5-5.5-1.4
c-2.8,0-5.4,1.6-6.9,4.2c-2.9,5.1-0.8,12.6,2.1,16.8c1.4,2,3.1,4.3,5.3,4.2c2.1-0.1,2.9-1.4,5.4-1.4c2.5,0,3.3,1.4,5.5,1.3
c2.3,0,3.7-2.1,5.1-4.1c1.6-2.3,2.3-4.6,2.3-4.7C45.5,38.1,41.1,36.4,41.1,31.4L41.1,31.4z M36.9,19c1.2-1.4,1.9-3.4,1.7-5.3
c-1.7,0.1-3.7,1.1-4.9,2.5c-1.1,1.2-2,3.2-1.8,5.1C33.8,21.5,35.7,20.5,36.9,19L36.9,19z"/>
</g>
</svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 64 64" style="enable-background:new 0 0 64 64;" xml:space="preserve">
<style type="text/css">
.st0{fill:#F4F4F4;}
.st1{opacity:0.1;fill-rule:evenodd;clip-rule:evenodd;fill:url(#SVGID_1_);enable-background:new ;}
.st2{fill:#999999;}
</style>
<g id="图层_1">
<g transform="scale(.26458)">
<path class="st0" d="M49.1,15.1h143.6c10.4,0,18.9,8.5,18.9,18.9v173.9c0,10.4-8.5,18.9-18.9,18.9H49.1
c-10.4,0-18.9-8.5-18.9-18.9V34C30.2,23.6,38.7,15.1,49.1,15.1z"/>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="-957.3084" y1="2609.5137" x2="-955.1654" y2="2607.3706" gradientTransform="matrix(2.3333 0 0 -2.3333 2411.3325 6281.5693)">
<stop offset="0" style="stop-color:#000000"/>
<stop offset="1" style="stop-color:#000000;stop-opacity:0"/>
</linearGradient>
<path class="st1" d="M211.7,173.9l-52.9,52.9h34c10.5,0,18.9-8.4,18.9-18.9V173.9z"/>
</g>
</g>
<g id="图层_2">
<path class="st2" d="M41.1,31.4c0-4.2,3.4-6.2,3.6-6.3c-2-2.9-5-3.3-6.1-3.3c-2.6-0.3-5.1,1.5-6.4,1.5c-1.3,0-3.3-1.5-5.5-1.4
c-2.8,0-5.4,1.6-6.9,4.2c-2.9,5.1-0.8,12.6,2.1,16.8c1.4,2,3.1,4.3,5.3,4.2c2.1-0.1,2.9-1.4,5.4-1.4c2.5,0,3.3,1.4,5.5,1.3
c2.3,0,3.7-2.1,5.1-4.1c1.6-2.3,2.3-4.6,2.3-4.7C45.5,38.1,41.1,36.4,41.1,31.4L41.1,31.4z M36.9,19c1.2-1.4,1.9-3.4,1.7-5.3
c-1.7,0.1-3.7,1.1-4.9,2.5c-1.1,1.2-2,3.2-1.8,5.1C33.8,21.5,35.7,20.5,36.9,19L36.9,19z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

View file

@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.2.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<style type="text/css">
.st0{fill:none;stroke:#FFFFFF;stroke-width:2;stroke-linejoin:round;stroke-miterlimit:2;}
</style>
<path class="st0" d="M12,22c5.5,0,10-4.5,10-10S17.5,2,12,2S2,6.5,2,12S6.5,22,12,22z"/>
<path class="st0" d="M12,22c3.9,0,7-3.1,7-7s-3.1-7-7-7s-7,3.1-7,7S8.1,22,12,22z"/>
<path class="st0" d="M12,22c2.2,0,4-1.8,4-4s-1.8-4-4-4s-4,1.8-4,4S9.8,22,12,22z"/>
</svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.2.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<style type="text/css">
.st0{fill:none;stroke:#FFFFFF;stroke-width:2;stroke-linejoin:round;stroke-miterlimit:2;}
</style>
<path class="st0" d="M12,22c5.5,0,10-4.5,10-10S17.5,2,12,2S2,6.5,2,12S6.5,22,12,22z"/>
<path class="st0" d="M12,22c3.9,0,7-3.1,7-7s-3.1-7-7-7s-7,3.1-7,7S8.1,22,12,22z"/>
<path class="st0" d="M12,22c2.2,0,4-1.8,4-4s-1.8-4-4-4s-4,1.8-4,4S9.8,22,12,22z"/>
</svg>

Before

Width:  |  Height:  |  Size: 729 B

After

Width:  |  Height:  |  Size: 740 B

View file

@ -1,39 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 120 120" style="enable-background:new 0 0 120 120;" xml:space="preserve">
<style type="text/css">
.st0{fill:url(#SVGID_1_);}
.st1{fill:url(#SVGID_2_);}
.st2{fill:url(#SVGID_3_);}
</style>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="60" y1="122" x2="60" y2="2" gradientTransform="matrix(1 0 0 -1 0 122)">
<stop offset="0" style="stop-color:#5A9CFF"/>
<stop offset="0.879" style="stop-color:#2A23D5"/>
<stop offset="1" style="stop-color:#4A3CEC"/>
</linearGradient>
<path class="st0" d="M0,60C0,26.9,26.9,0,60,0l0,0c33.1,0,60,26.9,60,60l0,0c0,33.1-26.9,60-60,60l0,0C26.9,120,0,93.1,0,60L0,60z"
/>
<g>
<g>
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="59.9999" y1="82" x2="59.9997" y2="29" gradientTransform="matrix(1 0 0 -1 0 122)">
<stop offset="3.509020e-02" style="stop-color:#FFFFFF"/>
<stop offset="0.5256" style="stop-color:#B0D4FF"/>
<stop offset="0.8371" style="stop-color:#DEEDFF"/>
<stop offset="1" style="stop-color:#FEFEFF"/>
</linearGradient>
<path class="st1" d="M75.1,42.1c0,8.4-6.8,15.1-15.1,15.1c-8.4,0-15.1-6.8-15.1-15.1S51.6,27,60,27C68.4,27,75.1,33.8,75.1,42.1z"
/>
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="60" y1="82" x2="59.9998" y2="29" gradientTransform="matrix(1 0 0 -1 0 122)">
<stop offset="3.509020e-02" style="stop-color:#FFFFFF"/>
<stop offset="0.5256" style="stop-color:#B0D4FF"/>
<stop offset="0.8371" style="stop-color:#DEEDFF"/>
<stop offset="1" style="stop-color:#FEFEFF"/>
</linearGradient>
<path class="st2" d="M34.4,71.7c6.9-5.6,15.8-9,25.6-9c9.8,0,18.7,3.4,25.6,9c3.9,3.2,3.9,9.1,0,12.3c-6.9,5.6-15.8,9-25.6,9
c-9.8,0-18.7-3.4-25.6-9C30.5,80.8,30.5,74.9,34.4,71.7z"/>
</g>
</g>
</svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 120 120" style="enable-background:new 0 0 120 120;" xml:space="preserve">
<style type="text/css">
.st0{fill:url(#SVGID_1_);}
.st1{fill:url(#SVGID_2_);}
.st2{fill:url(#SVGID_3_);}
</style>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="60" y1="122" x2="60" y2="2" gradientTransform="matrix(1 0 0 -1 0 122)">
<stop offset="0" style="stop-color:#5A9CFF"/>
<stop offset="0.879" style="stop-color:#2A23D5"/>
<stop offset="1" style="stop-color:#4A3CEC"/>
</linearGradient>
<path class="st0" d="M0,60C0,26.9,26.9,0,60,0l0,0c33.1,0,60,26.9,60,60l0,0c0,33.1-26.9,60-60,60l0,0C26.9,120,0,93.1,0,60L0,60z"
/>
<g>
<g>
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="59.9999" y1="82" x2="59.9997" y2="29" gradientTransform="matrix(1 0 0 -1 0 122)">
<stop offset="3.509020e-02" style="stop-color:#FFFFFF"/>
<stop offset="0.5256" style="stop-color:#B0D4FF"/>
<stop offset="0.8371" style="stop-color:#DEEDFF"/>
<stop offset="1" style="stop-color:#FEFEFF"/>
</linearGradient>
<path class="st1" d="M75.1,42.1c0,8.4-6.8,15.1-15.1,15.1c-8.4,0-15.1-6.8-15.1-15.1S51.6,27,60,27C68.4,27,75.1,33.8,75.1,42.1z"
/>
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="60" y1="82" x2="59.9998" y2="29" gradientTransform="matrix(1 0 0 -1 0 122)">
<stop offset="3.509020e-02" style="stop-color:#FFFFFF"/>
<stop offset="0.5256" style="stop-color:#B0D4FF"/>
<stop offset="0.8371" style="stop-color:#DEEDFF"/>
<stop offset="1" style="stop-color:#FEFEFF"/>
</linearGradient>
<path class="st2" d="M34.4,71.7c6.9-5.6,15.8-9,25.6-9c9.8,0,18.7,3.4,25.6,9c3.9,3.2,3.9,9.1,0,12.3c-6.9,5.6-15.8,9-25.6,9
c-9.8,0-18.7-3.4-25.6-9C30.5,80.8,30.5,74.9,34.4,71.7z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2 KiB

After

Width:  |  Height:  |  Size: 2 KiB

View file

@ -1,121 +1,121 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1"
id="图层_1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 64 64"
style="enable-background:new 0 0 64 64;" xml:space="preserve">
<style type="text/css">
.st0{fill:#5E4AA6;}
.st1{fill:url(#SVGID_1_);}
.st2{opacity:1.000000e-03;fill:#1E5992;enable-background:new ;}
.st3{fill:url(#SVGID_2_);}
.st4{opacity:0.3;fill:url(#SVGID_3_);enable-background:new ;}
.st5{fill:url(#SVGID_4_);}
.st6{fill:url(#SVGID_5_);}
.st7{fill:url(#SVGID_6_);}
</style>
<g>
<rect x="-1248.6" y="-1241.1" class="st0" width="0" height="0"/>
<rect x="-1179.7" y="-1233.1" class="st0" width="0" height="0"/>
<rect x="-1156.6" y="-1258.9" class="st0" width="0" height="0"/>
<rect x="-1167.3" y="-1232.2" class="st0" width="0" height="0"/>
<g transform="translate(.000295 -.00032978)">
<rect x="-1248.6" y="-1241.1" class="st0" width="0" height="0"/>
<rect x="-1179.7" y="-1233.1" class="st0" width="0" height="0"/>
<rect x="-1156.6" y="-1258.9" class="st0" width="0" height="0"/>
<rect x="-1167.3" y="-1232.2" class="st0" width="0" height="0"/>
<rect x="-1242.7" y="-1249" class="st0" width="0" height="0"/>
</g>
<rect x="-1248.6" y="-1241.1" class="st0" width="0" height="0"/>
<rect x="-1179.7" y="-1233.1" class="st0" width="0" height="0"/>
<rect x="-1156.6" y="-1258.9" class="st0" width="0" height="0"/>
<rect x="-1167.3" y="-1232.2" class="st0" width="0" height="0"/>
<rect x="-1242.7" y="-1249" class="st0" width="0" height="0"/>
<g transform="translate(2.95e-4,-3.2978e-4)">
<rect x="-1248.6" y="-1241.1" class="st0" width="0" height="0"/>
<rect x="-1179.7" y="-1233.1" class="st0" width="0" height="0"/>
<rect x="-1156.6" y="-1258.9" class="st0" width="0" height="0"/>
<rect x="-1167.3" y="-1232.2" class="st0" width="0" height="0"/>
<rect x="-1242.7" y="-1249" class="st0" width="0" height="0"/>
</g>
</g>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="-363.3979" y1="913.4439" x2="-363.3979" y2="911.0629" gradientTransform="matrix(3.7796 0 0 -3.7796 1405.5416 3462.4551)">
<stop offset="0" style="stop-color:#0272CB"/>
<stop offset="1" style="stop-color:#0768BA"/>
</linearGradient>
<path image-rendering="auto" shape-rendering="auto" color-rendering="auto" class="st1" d="M4.6,8.5c-1.4,0-2.5,1.2-2.5,2.8v11.2
c0,0.1,0,0.2,0,0.3v30c0,1.5,1.1,2.8,2.5,2.8h55c1.4,0,2.5-1.2,2.5-2.8V17.2c0-1.5-1.1-2.8-2.5-2.8v0h-31l-3.4-4.2
c0,0-1.4-1.8-4-1.8h-5L4.6,8.5z"/>
<path class="st2" d="M31.6,14.4c-0.4,0-1.2-0.1-2,0.4c-2.8,1.4-2.9,2.2-5.9,2.7H5c-0.8,0-1.6,0.3-2.1,0.9c-0.5,0.5-0.8,1.3-0.9,2.1
v1c0-0.8,0.3-1.5,0.9-2.1c0.5-0.5,1.3-0.9,2.1-0.9h18.7c3-0.5,3.1-1.3,5.9-2.7c0.8-0.4,1.5-0.4,2-0.4h27.9c0.7,0,1.3,0.3,1.8,0.8
c0.5,0.5,0.7,1.2,0.7,1.9v-1c0-0.8-0.3-1.4-0.7-1.9c-0.5-0.5-1.1-0.8-1.8-0.8h-4.4H31.6z M2.1,22.4c0,0.1,0,0.2,0,0.3v1
c0-0.1,0-0.2,0-0.3V22.4z"/>
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="95.1452" y1="43.801" x2="128.3352" y2="9.648" gradientTransform="matrix(1 0 0 -1 -75.9965 65.4973)">
<stop offset="0" style="stop-color:#27AEE9"/>
<stop offset="1" style="stop-color:#0669BC"/>
</linearGradient>
<path class="st3" d="M28.5,15.5c0,0-1.4,1.8-4.4,3c-0.1,0-0.2,0-0.4,0.1H5c-0.8,0-1.6,0.3-2.1,0.9c-0.5,0.5-0.8,1.3-0.9,2.1v2
c0,0.1,0,0.2,0,0.3v28c0,0.8,0.3,1.4,0.7,1.9c0.5,0.5,1.1,0.8,1.8,0.8h55c0.7,0,1.3-0.3,1.8-0.8c0.5-0.5,0.7-1.2,0.7-1.9V18.2
c0-0.8-0.3-1.4-0.7-1.9c-0.5-0.5-1.1-0.8-1.8-0.8H31.3L28.5,15.5z"/>
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="-389.233" y1="932.0934" x2="-373.3582" y2="932.0934" gradientTransform="matrix(3.7796 0 0 -3.7796 1473.188 3543.282)">
<stop offset="0" style="stop-color:#FFFFFF"/>
<stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0"/>
</linearGradient>
<path class="st4" d="M28.5,15.5c0,0-1.4,1.8-4.4,3c-0.1,0-0.2,0-0.4,0.1H5c-0.8,0-1.6,0.3-2.1,0.9c-0.5,0.5-0.8,1.3-0.9,2.1v1.5
c0-0.8,0.3-1.5,0.9-2.1C3.5,20.3,4.2,20,5,20h18.7c0.1,0,0.3,0,0.4-0.1c2.9-1,4.9-3,4.9-3h30.5c0.7,0,1.3,0.3,1.8,0.8
c0.5,0.5,0.7,1.2,0.7,1.9v-1.5c0-0.8-0.3-1.4-0.7-1.9c-0.5-0.5-1.1-0.8-1.8-0.8H31.3H28.5z M2.1,23.4c0,0.1,0,0.2,0,0.3v1.5
c0-0.1,0-0.2,0-0.3V23.4z"/>
<linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="-389.2331" y1="923.2966" x2="-373.3582" y2="923.2966" gradientTransform="matrix(3.7796 0 0 -3.7796 1473.188 3543.2234)">
<stop offset="0" style="stop-color:#0A5BA8;stop-opacity:0.9961"/>
<stop offset="1" style="stop-color:#104A8C;stop-opacity:0.9961"/>
</linearGradient>
<path image-rendering="auto" shape-rendering="auto" color-rendering="auto" class="st5" d="M2,51.6v1.1c0,0.8,0.3,1.4,0.7,1.9
c0.5,0.5,1.1,0.8,1.8,0.8h55c0.7,0,1.3-0.3,1.8-0.8c0.5-0.5,0.7-1.2,0.7-1.9v-1.1c0,0.8-0.3,1.4-0.7,1.9c-0.5,0.5-1.1,0.8-1.8,0.8
h-55c-0.7,0-1.3-0.3-1.8-0.8S2,52.4,2,51.6z"/>
<g>
<image style="overflow:visible;opacity:0.25;" width="34" height="44" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACIAAAAsCAYAAAAATWqyAAAACXBIWXMAAAsSAAALEgHS3X78AAAA
GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA/JJREFUeNrMmFtv00AQhX2LyYUk
pS0VIEA88P9/ES8IqZTShtI0JXHsmE31TXW6dRzb7UMtHSVO1rvHM2cuu0HwQq6wwX/hM61Vep97
iYQgAqGgKwHDBpQ+IX9yWzx26IGE+y5kbMHCIXdYg0JI3V2JRypi8b7D0GHkMOC3uCORgsX/OSwc
bh2W/HZvGZ9IDImJw7HDEd/7kGljFVtkzcLXDpcOF/yvbronotYYQeKLwyeHE4exwyvGBHWi84hu
F1o5zB3OHX7weyEuuiOcePowIkeQ+OrwwWEKkdATnopORR7K2C2Rv1g2gNQNbsog88AiIffmmreQ
+Ohw4JB6Ps+BEkmAairDooEQ6jPunnCVRnoIdIIlpnyPxd9m8gwy9lKpRN4rEX6JJSa7xJ9U5I9Y
Jk1FpDmKv+LNrrlXIkN5gQPcbC63+SrTQbIjmemggtC7RfFnCO83vl4xbmuB17h0K/B3iH5oOtgx
fyURX/m5xP8Fqv/u8JNQXHgWMaG/FzEeY5W8rmQkDRLRDdbYkvgGkXN8nnliTXHZgmftGnrh2prI
ku8ziGxxymK56EldaBFlgt2K9RAXdiayYuIZmrjEQgVuGLNQAOk51rth7BRXpURaKyKlR2RDpFxh
8g2mfgemkiPOwIqx9twYCxVVlbfOIsY+A5oJI6LjhOx7yDMznpnLcwvRWCpVt7FrAknjVsJtgphJ
zTVjxi4lY0Ze+S+0wLUlEkpvkgg2kpBKWWgFLO1rcoylBrUiEsmbJ2TLiYSlpfuFRJeJ+RayZrXX
aCoSQo2IaKq30v8G9WcsOpBK+hdcQOYfz45I85bqN3WdXl1mjSUPmAZ6fNrbhpLA5rgmwQpHpPtD
7ld1Xd4+In2vYk6xSgyWEt5Grsfin8BbXBM+hUjK29hbHkvqzrHGBncE/DcktD9DxGpNJ4somQGT
j6VeWJT0JULe8H2CW6z6jrBi0iVqNHwTyQ+aW+ztB5BY8oyR1n6k7Bq+fhhbYxMKmVx+n3AfisAH
kneW+7r/fUQCb9cXSQmIvIa79PpW08N6nzWaEvHzSyATR+KyoOK/TdN9UBsiVaTKHZmy9Qa+K5Gg
7UJBAzG+iCvZs2/deGh7+c+XTTs0XdyOETLZMkQdiNhGbC19zaPeJKkgkUurZ9U1kF1cWyIZc8yZ
cyWEyl1ECmkNZ/SfATUl7Xg+kkHijDlvZPP9iEgp3ZZtK0+lKR7tqxU1RHIsMWPOK9mqVh7UqDV+
MeCSqps+8cTI5v3D5kytUmsRK/NX0i4+5QwtZ+Fb6eofWCSsaZbTpo1vAzK6G8iqOvuw5jTAP958
6hlr1fFmuS9FP/dhb6ND3xdx/RdgADBglifWgXLhAAAAAElFTkSuQmCC" transform="matrix(1 0 0 1 15 15)">
</image>
<g>
<g>
<g>
<linearGradient id="SVGID_5_" gradientUnits="userSpaceOnUse" x1="32" y1="21.5142" x2="32" y2="51.1873">
<stop offset="0" style="stop-color:#FBFDFE"/>
<stop offset="0.9716" style="stop-color:#C6E8F5"/>
</linearGradient>
<path class="st6" d="M22.9,51.9h18.2c1,0,1.8-0.8,1.8-1.8V22.7c0-1-0.8-1.8-1.8-1.8H22.9c-1,0-1.8,0.8-1.8,1.8v27.4
C21.1,51.1,21.9,51.9,22.9,51.9z M39.8,49.7c-0.6,0-1.1-0.5-1.1-1.1c0-0.6,0.5-1.1,1.1-1.1c0.6,0,1.1,0.5,1.1,1.1
C40.9,49.3,40.4,49.7,39.8,49.7z M39.8,23.4c0.6,0,1.1,0.5,1.1,1.1s-0.5,1.1-1.1,1.1c-0.6,0-1.1-0.5-1.1-1.1
S39.2,23.4,39.8,23.4z M32,24.2c4.6,0,8.4,3.7,8.4,8.4s-3.7,8.4-8.4,8.4c-0.7,0-1.4-0.1-2.1-0.3l1.6-2.7
c0.5-0.8-0.4-1.7-1.2-1.2l-3.6,2.1c0,0,0,0,0,0c-1.8-1.5-2.9-3.8-2.9-6.4C23.6,27.9,27.4,24.2,32,24.2z M24.4,23.4
c0.6,0,1.1,0.5,1.1,1.1s-0.5,1.1-1.1,1.1s-1.1-0.5-1.1-1.1S23.8,23.4,24.4,23.4z M24.4,47.6c0.6,0,1.1,0.5,1.1,1.1
c0,0.6-0.5,1.1-1.1,1.1s-1.1-0.5-1.1-1.1C23.3,48.1,23.8,47.6,24.4,47.6z"/>
<linearGradient id="SVGID_6_" gradientUnits="userSpaceOnUse" x1="31.9994" y1="32.4788" x2="32.0716" y2="36.9457">
<stop offset="0" style="stop-color:#FBFDFE"/>
<stop offset="1" style="stop-color:#C6E8F5"/>
</linearGradient>
<circle class="st7" cx="32" cy="32.5" r="2.2"/>
</g>
</g>
</g>
</g>
</svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1"
id="图层_1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 64 64"
style="enable-background:new 0 0 64 64;" xml:space="preserve">
<style type="text/css">
.st0{fill:#5E4AA6;}
.st1{fill:url(#SVGID_1_);}
.st2{opacity:1.000000e-03;fill:#1E5992;enable-background:new ;}
.st3{fill:url(#SVGID_2_);}
.st4{opacity:0.3;fill:url(#SVGID_3_);enable-background:new ;}
.st5{fill:url(#SVGID_4_);}
.st6{fill:url(#SVGID_5_);}
.st7{fill:url(#SVGID_6_);}
</style>
<g>
<rect x="-1248.6" y="-1241.1" class="st0" width="0" height="0"/>
<rect x="-1179.7" y="-1233.1" class="st0" width="0" height="0"/>
<rect x="-1156.6" y="-1258.9" class="st0" width="0" height="0"/>
<rect x="-1167.3" y="-1232.2" class="st0" width="0" height="0"/>
<g transform="translate(.000295 -.00032978)">
<rect x="-1248.6" y="-1241.1" class="st0" width="0" height="0"/>
<rect x="-1179.7" y="-1233.1" class="st0" width="0" height="0"/>
<rect x="-1156.6" y="-1258.9" class="st0" width="0" height="0"/>
<rect x="-1167.3" y="-1232.2" class="st0" width="0" height="0"/>
<rect x="-1242.7" y="-1249" class="st0" width="0" height="0"/>
</g>
<rect x="-1248.6" y="-1241.1" class="st0" width="0" height="0"/>
<rect x="-1179.7" y="-1233.1" class="st0" width="0" height="0"/>
<rect x="-1156.6" y="-1258.9" class="st0" width="0" height="0"/>
<rect x="-1167.3" y="-1232.2" class="st0" width="0" height="0"/>
<rect x="-1242.7" y="-1249" class="st0" width="0" height="0"/>
<g transform="translate(2.95e-4,-3.2978e-4)">
<rect x="-1248.6" y="-1241.1" class="st0" width="0" height="0"/>
<rect x="-1179.7" y="-1233.1" class="st0" width="0" height="0"/>
<rect x="-1156.6" y="-1258.9" class="st0" width="0" height="0"/>
<rect x="-1167.3" y="-1232.2" class="st0" width="0" height="0"/>
<rect x="-1242.7" y="-1249" class="st0" width="0" height="0"/>
</g>
</g>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="-363.3979" y1="913.4439" x2="-363.3979" y2="911.0629" gradientTransform="matrix(3.7796 0 0 -3.7796 1405.5416 3462.4551)">
<stop offset="0" style="stop-color:#0272CB"/>
<stop offset="1" style="stop-color:#0768BA"/>
</linearGradient>
<path image-rendering="auto" shape-rendering="auto" color-rendering="auto" class="st1" d="M4.6,8.5c-1.4,0-2.5,1.2-2.5,2.8v11.2
c0,0.1,0,0.2,0,0.3v30c0,1.5,1.1,2.8,2.5,2.8h55c1.4,0,2.5-1.2,2.5-2.8V17.2c0-1.5-1.1-2.8-2.5-2.8v0h-31l-3.4-4.2
c0,0-1.4-1.8-4-1.8h-5L4.6,8.5z"/>
<path class="st2" d="M31.6,14.4c-0.4,0-1.2-0.1-2,0.4c-2.8,1.4-2.9,2.2-5.9,2.7H5c-0.8,0-1.6,0.3-2.1,0.9c-0.5,0.5-0.8,1.3-0.9,2.1
v1c0-0.8,0.3-1.5,0.9-2.1c0.5-0.5,1.3-0.9,2.1-0.9h18.7c3-0.5,3.1-1.3,5.9-2.7c0.8-0.4,1.5-0.4,2-0.4h27.9c0.7,0,1.3,0.3,1.8,0.8
c0.5,0.5,0.7,1.2,0.7,1.9v-1c0-0.8-0.3-1.4-0.7-1.9c-0.5-0.5-1.1-0.8-1.8-0.8h-4.4H31.6z M2.1,22.4c0,0.1,0,0.2,0,0.3v1
c0-0.1,0-0.2,0-0.3V22.4z"/>
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="95.1452" y1="43.801" x2="128.3352" y2="9.648" gradientTransform="matrix(1 0 0 -1 -75.9965 65.4973)">
<stop offset="0" style="stop-color:#27AEE9"/>
<stop offset="1" style="stop-color:#0669BC"/>
</linearGradient>
<path class="st3" d="M28.5,15.5c0,0-1.4,1.8-4.4,3c-0.1,0-0.2,0-0.4,0.1H5c-0.8,0-1.6,0.3-2.1,0.9c-0.5,0.5-0.8,1.3-0.9,2.1v2
c0,0.1,0,0.2,0,0.3v28c0,0.8,0.3,1.4,0.7,1.9c0.5,0.5,1.1,0.8,1.8,0.8h55c0.7,0,1.3-0.3,1.8-0.8c0.5-0.5,0.7-1.2,0.7-1.9V18.2
c0-0.8-0.3-1.4-0.7-1.9c-0.5-0.5-1.1-0.8-1.8-0.8H31.3L28.5,15.5z"/>
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="-389.233" y1="932.0934" x2="-373.3582" y2="932.0934" gradientTransform="matrix(3.7796 0 0 -3.7796 1473.188 3543.282)">
<stop offset="0" style="stop-color:#FFFFFF"/>
<stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0"/>
</linearGradient>
<path class="st4" d="M28.5,15.5c0,0-1.4,1.8-4.4,3c-0.1,0-0.2,0-0.4,0.1H5c-0.8,0-1.6,0.3-2.1,0.9c-0.5,0.5-0.8,1.3-0.9,2.1v1.5
c0-0.8,0.3-1.5,0.9-2.1C3.5,20.3,4.2,20,5,20h18.7c0.1,0,0.3,0,0.4-0.1c2.9-1,4.9-3,4.9-3h30.5c0.7,0,1.3,0.3,1.8,0.8
c0.5,0.5,0.7,1.2,0.7,1.9v-1.5c0-0.8-0.3-1.4-0.7-1.9c-0.5-0.5-1.1-0.8-1.8-0.8H31.3H28.5z M2.1,23.4c0,0.1,0,0.2,0,0.3v1.5
c0-0.1,0-0.2,0-0.3V23.4z"/>
<linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="-389.2331" y1="923.2966" x2="-373.3582" y2="923.2966" gradientTransform="matrix(3.7796 0 0 -3.7796 1473.188 3543.2234)">
<stop offset="0" style="stop-color:#0A5BA8;stop-opacity:0.9961"/>
<stop offset="1" style="stop-color:#104A8C;stop-opacity:0.9961"/>
</linearGradient>
<path image-rendering="auto" shape-rendering="auto" color-rendering="auto" class="st5" d="M2,51.6v1.1c0,0.8,0.3,1.4,0.7,1.9
c0.5,0.5,1.1,0.8,1.8,0.8h55c0.7,0,1.3-0.3,1.8-0.8c0.5-0.5,0.7-1.2,0.7-1.9v-1.1c0,0.8-0.3,1.4-0.7,1.9c-0.5,0.5-1.1,0.8-1.8,0.8
h-55c-0.7,0-1.3-0.3-1.8-0.8S2,52.4,2,51.6z"/>
<g>
<image style="overflow:visible;opacity:0.25;" width="34" height="44" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACIAAAAsCAYAAAAATWqyAAAACXBIWXMAAAsSAAALEgHS3X78AAAA
GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA/JJREFUeNrMmFtv00AQhX2LyYUk
pS0VIEA88P9/ES8IqZTShtI0JXHsmE31TXW6dRzb7UMtHSVO1rvHM2cuu0HwQq6wwX/hM61Vep97
iYQgAqGgKwHDBpQ+IX9yWzx26IGE+y5kbMHCIXdYg0JI3V2JRypi8b7D0GHkMOC3uCORgsX/OSwc
bh2W/HZvGZ9IDImJw7HDEd/7kGljFVtkzcLXDpcOF/yvbronotYYQeKLwyeHE4exwyvGBHWi84hu
F1o5zB3OHX7weyEuuiOcePowIkeQ+OrwwWEKkdATnopORR7K2C2Rv1g2gNQNbsog88AiIffmmreQ
+Ohw4JB6Ps+BEkmAairDooEQ6jPunnCVRnoIdIIlpnyPxd9m8gwy9lKpRN4rEX6JJSa7xJ9U5I9Y
Jk1FpDmKv+LNrrlXIkN5gQPcbC63+SrTQbIjmemggtC7RfFnCO83vl4xbmuB17h0K/B3iH5oOtgx
fyURX/m5xP8Fqv/u8JNQXHgWMaG/FzEeY5W8rmQkDRLRDdbYkvgGkXN8nnliTXHZgmftGnrh2prI
ku8ziGxxymK56EldaBFlgt2K9RAXdiayYuIZmrjEQgVuGLNQAOk51rth7BRXpURaKyKlR2RDpFxh
8g2mfgemkiPOwIqx9twYCxVVlbfOIsY+A5oJI6LjhOx7yDMznpnLcwvRWCpVt7FrAknjVsJtgphJ
zTVjxi4lY0Ze+S+0wLUlEkpvkgg2kpBKWWgFLO1rcoylBrUiEsmbJ2TLiYSlpfuFRJeJ+RayZrXX
aCoSQo2IaKq30v8G9WcsOpBK+hdcQOYfz45I85bqN3WdXl1mjSUPmAZ6fNrbhpLA5rgmwQpHpPtD
7ld1Xd4+In2vYk6xSgyWEt5Grsfin8BbXBM+hUjK29hbHkvqzrHGBncE/DcktD9DxGpNJ4somQGT
j6VeWJT0JULe8H2CW6z6jrBi0iVqNHwTyQ+aW+ztB5BY8oyR1n6k7Bq+fhhbYxMKmVx+n3AfisAH
kneW+7r/fUQCb9cXSQmIvIa79PpW08N6nzWaEvHzSyATR+KyoOK/TdN9UBsiVaTKHZmy9Qa+K5Gg
7UJBAzG+iCvZs2/deGh7+c+XTTs0XdyOETLZMkQdiNhGbC19zaPeJKkgkUurZ9U1kF1cWyIZc8yZ
cyWEyl1ECmkNZ/SfATUl7Xg+kkHijDlvZPP9iEgp3ZZtK0+lKR7tqxU1RHIsMWPOK9mqVh7UqDV+
MeCSqps+8cTI5v3D5kytUmsRK/NX0i4+5QwtZ+Fb6eofWCSsaZbTpo1vAzK6G8iqOvuw5jTAP958
6hlr1fFmuS9FP/dhb6ND3xdx/RdgADBglifWgXLhAAAAAElFTkSuQmCC" transform="matrix(1 0 0 1 15 15)">
</image>
<g>
<g>
<g>
<linearGradient id="SVGID_5_" gradientUnits="userSpaceOnUse" x1="32" y1="21.5142" x2="32" y2="51.1873">
<stop offset="0" style="stop-color:#FBFDFE"/>
<stop offset="0.9716" style="stop-color:#C6E8F5"/>
</linearGradient>
<path class="st6" d="M22.9,51.9h18.2c1,0,1.8-0.8,1.8-1.8V22.7c0-1-0.8-1.8-1.8-1.8H22.9c-1,0-1.8,0.8-1.8,1.8v27.4
C21.1,51.1,21.9,51.9,22.9,51.9z M39.8,49.7c-0.6,0-1.1-0.5-1.1-1.1c0-0.6,0.5-1.1,1.1-1.1c0.6,0,1.1,0.5,1.1,1.1
C40.9,49.3,40.4,49.7,39.8,49.7z M39.8,23.4c0.6,0,1.1,0.5,1.1,1.1s-0.5,1.1-1.1,1.1c-0.6,0-1.1-0.5-1.1-1.1
S39.2,23.4,39.8,23.4z M32,24.2c4.6,0,8.4,3.7,8.4,8.4s-3.7,8.4-8.4,8.4c-0.7,0-1.4-0.1-2.1-0.3l1.6-2.7
c0.5-0.8-0.4-1.7-1.2-1.2l-3.6,2.1c0,0,0,0,0,0c-1.8-1.5-2.9-3.8-2.9-6.4C23.6,27.9,27.4,24.2,32,24.2z M24.4,23.4
c0.6,0,1.1,0.5,1.1,1.1s-0.5,1.1-1.1,1.1s-1.1-0.5-1.1-1.1S23.8,23.4,24.4,23.4z M24.4,47.6c0.6,0,1.1,0.5,1.1,1.1
c0,0.6-0.5,1.1-1.1,1.1s-1.1-0.5-1.1-1.1C23.3,48.1,23.8,47.6,24.4,47.6z"/>
<linearGradient id="SVGID_6_" gradientUnits="userSpaceOnUse" x1="31.9994" y1="32.4788" x2="32.0716" y2="36.9457">
<stop offset="0" style="stop-color:#FBFDFE"/>
<stop offset="1" style="stop-color:#C6E8F5"/>
</linearGradient>
<circle class="st7" cx="32" cy="32.5" r="2.2"/>
</g>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

View file

@ -1,113 +1,113 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1"
id="图层_1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 64 64"
style="enable-background:new 0 0 64 64;" xml:space="preserve">
<style type="text/css">
.st0{fill:#5E4AA6;}
.st1{fill:url(#SVGID_1_);}
.st2{opacity:1.000000e-03;fill:#1E5992;enable-background:new ;}
.st3{fill:url(#SVGID_2_);}
.st4{opacity:0.3;fill:url(#SVGID_3_);enable-background:new ;}
.st5{fill:url(#SVGID_4_);}
.st6{fill:url(#SVGID_5_);}
</style>
<g>
<polygon class="st0" points="-1248.6,-1241.1 -1248.6,-1241.1 -1248.6,-1241.1 "/>
<polygon class="st0" points="-1179.7,-1233.1 -1179.7,-1233.1 -1179.7,-1233.1 "/>
<polygon class="st0" points="-1156.6,-1258.9 -1156.6,-1258.9 -1156.6,-1258.9 "/>
<polygon class="st0" points="-1167.3,-1232.2 -1167.3,-1232.2 -1167.3,-1232.2 "/>
<g transform="translate(.000295 -.00032978)">
<polygon class="st0" points="-1248.6,-1241.1 -1248.6,-1241.1 -1248.6,-1241.1 "/>
<polygon class="st0" points="-1179.7,-1233.1 -1179.7,-1233.1 -1179.7,-1233.1 "/>
<polygon class="st0" points="-1156.6,-1258.9 -1156.6,-1258.9 -1156.6,-1258.9 "/>
<polygon class="st0" points="-1167.3,-1232.2 -1167.3,-1232.2 -1167.3,-1232.2 "/>
<polygon class="st0" points="-1242.7,-1249 -1242.7,-1249 -1242.7,-1249 "/>
</g>
<polygon class="st0" points="-1248.6,-1241.1 -1248.6,-1241.1 -1248.6,-1241.1 "/>
<polygon class="st0" points="-1179.7,-1233.1 -1179.7,-1233.1 -1179.7,-1233.1 "/>
<polygon class="st0" points="-1156.6,-1258.9 -1156.6,-1258.9 -1156.6,-1258.9 "/>
<polygon class="st0" points="-1167.3,-1232.2 -1167.3,-1232.2 -1167.3,-1232.2 "/>
<polygon class="st0" points="-1242.7,-1249 -1242.7,-1249 -1242.7,-1249 "/>
<g transform="translate(2.95e-4,-3.2978e-4)">
<polygon class="st0" points="-1248.6,-1241.1 -1248.6,-1241.1 -1248.6,-1241.1 "/>
<polygon class="st0" points="-1179.7,-1233.1 -1179.7,-1233.1 -1179.7,-1233.1 "/>
<polygon class="st0" points="-1156.6,-1258.9 -1156.6,-1258.9 -1156.6,-1258.9 "/>
<polygon class="st0" points="-1167.3,-1232.2 -1167.3,-1232.2 -1167.3,-1232.2 "/>
<polygon class="st0" points="-1242.7,-1249 -1242.7,-1249 -1242.7,-1249 "/>
</g>
</g>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="-753.1828" y1="651.0814" x2="-753.1828" y2="653.4623" gradientTransform="matrix(3.7796 0 0 3.7796 2878.7295 -2450.8245)">
<stop offset="0" style="stop-color:#0272CB"/>
<stop offset="1" style="stop-color:#0768BA"/>
</linearGradient>
<path image-rendering="auto" shape-rendering="auto" color-rendering="auto" class="st1" d="M4.5,8.5C3.1,8.5,2,9.7,2,11.2v11.2
c0,0.1,0,0.2,0,0.3v30c0,1.5,1.1,2.8,2.5,2.8h55c1.4,0,2.5-1.2,2.5-2.8V17.2c0-1.5-1.1-2.8-2.5-2.8l0,0h-31l-3.4-4.2
c0,0-1.4-1.8-4-1.8h-5L4.5,8.5z"/>
<path class="st2" d="M31.6,14.4c-0.4,0-1.2-0.1-2,0.4c-2.8,1.4-2.9,2.2-5.9,2.7H5c-0.8,0-1.6,0.3-2.1,0.9c-0.6,0.5-0.9,1.2-0.9,2v1
c0-0.8,0.3-1.5,0.9-2.1c0.5-0.5,1.3-0.9,2.1-0.9h18.7c3-0.5,3.1-1.3,5.9-2.7c0.8-0.4,1.5-0.4,2-0.4h27.9c0.7,0,1.3,0.3,1.8,0.8
S62,17.3,62,18v-1c0-0.8-0.3-1.4-0.7-1.9c-0.5-0.5-1.1-0.8-1.8-0.8h-4.4L31.6,14.4L31.6,14.4z M2,22.4c0,0.1,0,0.2,0,0.3v1
c0-0.1,0-0.2,0-0.3V22.4z"/>
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="95.0853" y1="2392.217" x2="128.2754" y2="2426.3699" gradientTransform="matrix(1 0 0 1 -75.9965 -2370.5027)">
<stop offset="0" style="stop-color:#27AEE9"/>
<stop offset="1" style="stop-color:#0669BC"/>
</linearGradient>
<path class="st3" d="M28.5,15.5c0,0-1.4,1.8-4.4,3c-0.1,0-0.2,0-0.4,0.1H5c-0.8,0-1.6,0.3-2.1,0.9C2.4,20,2,20.7,2,21.5v2
c0,0.1,0,0.2,0,0.3v28c0,0.8,0.3,1.4,0.7,1.9c0.5,0.5,1.1,0.8,1.8,0.8h55c0.7,0,1.3-0.3,1.8-0.8s0.7-1.2,0.7-1.9V18.2
c0-0.8-0.3-1.4-0.7-1.9c-0.5-0.5-1.1-0.8-1.8-0.8H31.2L28.5,15.5z"/>
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="-779.0179" y1="632.4208" x2="-763.1432" y2="632.4208" gradientTransform="matrix(3.7796 0 0 3.7796 2946.376 -2369.9976)">
<stop offset="0" style="stop-color:#FFFFFF"/>
<stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0"/>
</linearGradient>
<path class="st4" d="M28.5,15.5c0,0-1.4,1.8-4.4,3c-0.1,0-0.2,0-0.4,0.1H5c-0.8,0-1.6,0.3-2.1,0.9C2.4,20,2,20.7,2,21.5V23
c0-0.8,0.3-1.5,0.9-2.1C3.4,20.3,4.2,20,5,20h18.7c0.1,0,0.3,0,0.4-0.1c2.9-1,4.9-3,4.9-3h30.5c0.7,0,1.3,0.3,1.8,0.8
s0.7,1.2,0.7,1.9v-1.5c0-0.8-0.3-1.4-0.7-1.9c-0.5-0.5-1.1-0.8-1.8-0.8H31.2h-2.7V15.5z M2,23.4c0,0.1,0,0.2,0,0.3v1.5
c0-0.1,0-0.2,0-0.3V23.4z"/>
<linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="-779.0179" y1="641.2203" x2="-763.1432" y2="641.2203" gradientTransform="matrix(3.7796 0 0 3.7796 2946.376 -2370.0562)">
<stop offset="0" style="stop-color:#0A5BA8;stop-opacity:0.9961"/>
<stop offset="1" style="stop-color:#104A8C;stop-opacity:0.9961"/>
</linearGradient>
<path image-rendering="auto" shape-rendering="auto" color-rendering="auto" class="st5" d="M2,51.6v1.1c0,0.8,0.3,1.4,0.7,1.9
c0.5,0.5,1.1,0.8,1.8,0.8h55c0.7,0,1.3-0.3,1.8-0.8s0.7-1.2,0.7-1.9v-1.1c0,0.8-0.3,1.4-0.7,1.9c-0.5,0.5-1.1,0.8-1.8,0.8h-55
c-0.7,0-1.3-0.3-1.8-0.8S2,52.4,2,51.6z"/>
<g>
<image style="overflow:visible;opacity:0.25;" width="33" height="45" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAtCAYAAAAgJgIUAAAACXBIWXMAAAsSAAALEgHS3X78AAAA
GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABD9JREFUeNrMmAlv20YQhcXLoi6f
cRzkQAv0//+m1i6ay67pIxYjiuR2CXwDvNCURSpOUgMPNKnl7tuZN8dyNPof/AXP/K77WSQCELbe
bwjUXN2PJBGCyCMGIYuXoOK+7jtpNJBwMz7xmHgsPPY95tzHYpFB1ogGEAghMPU49HjhceZxAqFE
XDLIEvFAK4zZ/SuPtx4vPVKPrx6X/B4IiXUfi8QDhNjsdOZx7PHG4w+ujWVyjwPGNbooRB9bXRMP
cNseJBrzv/b4zeMdz3KssPJ48PiCddYSMd9FImRciitOscAbyMxYMIDArUcGkRUWedIacU9XmB4W
uOOE6yHumOCCDJ188LiGVCEidbuSsKhIWXBBWE55ZmKcStgusFCKS0aSPx5ZJX4igYVCwBBjFYuA
SsQXoBsjc4BWAq4rCD1KZtGGnVtSGjPpTHZ5zAK205DJl+jggf8riapENvcomUUdBGIWt4UPxcTm
/5RdW4IqIJCLBiyxTZkvEsupa74hoVGwQHwvJSvuM1ncyh1OLOEgNxcBH7KhAIKFuMWpJnTSKRO8
IzO+gFhFKFZMdA/MbWsINJY6Yt5m3A2bcvKORU3VFqbpYE4u+B2cSSK6Jg9Y/K8gNuI+lSjaw+RT
rhlzp2LNUZclzB0HuOItCWmMv1PI3kiErEWcY+Yzizp2a+GctPqQ5uriDaU6FVEecb9k5xYBgSxi
FlFdpTKm7hJkWxObOqdQmhfLEboTs8QaclY1E8ZGPDct5KIF15WsnEy64oVcdlgIylbJrhlXSNKy
zTQ6+uTxGTcupbB9Q8KSRykmz5hgn+e5KHvFu04WM1IrFgp5ryHwj8dHSOTt6hq1UrUmLEtGewzO
iYwv7DhBwBYJIxmfYPJm/HuPvyByKe/XmzKmamEPWEjV4oqQfHCMpSwrTiSBPeCCc48LLHErucZt
6zG1ek5YIBELWaN7KnXExFiy2yt2f0Fpz7r00KfRNdckYpVZK3zn/G7izFj0AqgbVl2d1iYSrker
f8DVMmNj/n9Z9BwYgfsuN/SxhGvV/Uiq6xwyE+kXrhDhnyLE5tkdv5eb+s34icULdqcnrrG4opax
X1nsipC8JLy3EniqvXNSLR+EwJzyfMpVBWaJroBUu5Ma3Og6qYwFu1lK/ahFtBFuURdZoep1Uo+3
aGLUKj6VRE0kYTsVsWoCi/ocuoccAwMJ16QVujUkjiRsUzm1V9vywNDPCO2GNRDNpNJ/xh3fMHa2
hBOhrhGd6SNnjJXzcpcPJX1JlCx+TxheYvIQ/5eEZCZhud6UnIaScC0St+SBfUlSM37P+tSJXS1R
Sz+RIcYRteCzNLJ3EPgbqyy3Jamh0WHNyr1o4IbUPGaRJc+upXkp+7gjGBAZejqbyElMT2G5CFaz
5bN9vetqfjUzVhIhg6Jk6CdEPbG3v2Vq1R0Uos/9RXfnr7q//O8/AQYAxhWve3m9fqQAAAAASUVO
RK5CYII=" transform="matrix(1 0 0 1 16 15)">
</image>
<g>
<g>
<linearGradient id="SVGID_5_" gradientUnits="userSpaceOnUse" x1="32.3842" y1="22.1772" x2="32.3842" y2="51.1664">
<stop offset="0" style="stop-color:#FBFDFE"/>
<stop offset="0.9716" style="stop-color:#C6E8F5"/>
</linearGradient>
<path class="st6" d="M28.6,34.1c0-1.7-1.4-3.2-3.1-3.2c-1.8,0-3.3,1.4-3.3,3.2c0,1.4,0.9,2.6,2.2,3v1.2c0,0.7,0.4,1.5,1,1.9
l6.2,4.7c-1.6,0.5-2.7,1.9-2.7,3.6c0,2.1,1.7,3.8,3.8,3.8s3.8-1.7,3.8-3.8c0-1.7-1.2-3.2-2.8-3.7v-1.8l5.7-3.9c0.6-0.4,1-1.2,1-2
v-1.7h0.6c0.9,0,1.7-0.8,1.7-1.7V31c0-0.9-0.8-1.7-1.7-1.7h-3.3c-0.9,0-1.7,0.8-1.7,1.7v2.8c0,0.9,0.8,1.7,1.7,1.7h0.6v1.9
l-4.7,3.3V28.2h1.7c1.2,0,1.9-1.3,1.3-2.3L34,21.7c-0.7-1.1-2.2-1.1-2.9,0l-2.6,4.2c-0.6,1,0.1,2.3,1.3,2.3h1.8v14.3l-5.2-3.9
v-1.4C27.6,36.7,28.6,35.5,28.6,34.1z"/>
</g>
</g>
</g>
</svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1"
id="图层_1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 64 64"
style="enable-background:new 0 0 64 64;" xml:space="preserve">
<style type="text/css">
.st0{fill:#5E4AA6;}
.st1{fill:url(#SVGID_1_);}
.st2{opacity:1.000000e-03;fill:#1E5992;enable-background:new ;}
.st3{fill:url(#SVGID_2_);}
.st4{opacity:0.3;fill:url(#SVGID_3_);enable-background:new ;}
.st5{fill:url(#SVGID_4_);}
.st6{fill:url(#SVGID_5_);}
</style>
<g>
<polygon class="st0" points="-1248.6,-1241.1 -1248.6,-1241.1 -1248.6,-1241.1 "/>
<polygon class="st0" points="-1179.7,-1233.1 -1179.7,-1233.1 -1179.7,-1233.1 "/>
<polygon class="st0" points="-1156.6,-1258.9 -1156.6,-1258.9 -1156.6,-1258.9 "/>
<polygon class="st0" points="-1167.3,-1232.2 -1167.3,-1232.2 -1167.3,-1232.2 "/>
<g transform="translate(.000295 -.00032978)">
<polygon class="st0" points="-1248.6,-1241.1 -1248.6,-1241.1 -1248.6,-1241.1 "/>
<polygon class="st0" points="-1179.7,-1233.1 -1179.7,-1233.1 -1179.7,-1233.1 "/>
<polygon class="st0" points="-1156.6,-1258.9 -1156.6,-1258.9 -1156.6,-1258.9 "/>
<polygon class="st0" points="-1167.3,-1232.2 -1167.3,-1232.2 -1167.3,-1232.2 "/>
<polygon class="st0" points="-1242.7,-1249 -1242.7,-1249 -1242.7,-1249 "/>
</g>
<polygon class="st0" points="-1248.6,-1241.1 -1248.6,-1241.1 -1248.6,-1241.1 "/>
<polygon class="st0" points="-1179.7,-1233.1 -1179.7,-1233.1 -1179.7,-1233.1 "/>
<polygon class="st0" points="-1156.6,-1258.9 -1156.6,-1258.9 -1156.6,-1258.9 "/>
<polygon class="st0" points="-1167.3,-1232.2 -1167.3,-1232.2 -1167.3,-1232.2 "/>
<polygon class="st0" points="-1242.7,-1249 -1242.7,-1249 -1242.7,-1249 "/>
<g transform="translate(2.95e-4,-3.2978e-4)">
<polygon class="st0" points="-1248.6,-1241.1 -1248.6,-1241.1 -1248.6,-1241.1 "/>
<polygon class="st0" points="-1179.7,-1233.1 -1179.7,-1233.1 -1179.7,-1233.1 "/>
<polygon class="st0" points="-1156.6,-1258.9 -1156.6,-1258.9 -1156.6,-1258.9 "/>
<polygon class="st0" points="-1167.3,-1232.2 -1167.3,-1232.2 -1167.3,-1232.2 "/>
<polygon class="st0" points="-1242.7,-1249 -1242.7,-1249 -1242.7,-1249 "/>
</g>
</g>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="-753.1828" y1="651.0814" x2="-753.1828" y2="653.4623" gradientTransform="matrix(3.7796 0 0 3.7796 2878.7295 -2450.8245)">
<stop offset="0" style="stop-color:#0272CB"/>
<stop offset="1" style="stop-color:#0768BA"/>
</linearGradient>
<path image-rendering="auto" shape-rendering="auto" color-rendering="auto" class="st1" d="M4.5,8.5C3.1,8.5,2,9.7,2,11.2v11.2
c0,0.1,0,0.2,0,0.3v30c0,1.5,1.1,2.8,2.5,2.8h55c1.4,0,2.5-1.2,2.5-2.8V17.2c0-1.5-1.1-2.8-2.5-2.8l0,0h-31l-3.4-4.2
c0,0-1.4-1.8-4-1.8h-5L4.5,8.5z"/>
<path class="st2" d="M31.6,14.4c-0.4,0-1.2-0.1-2,0.4c-2.8,1.4-2.9,2.2-5.9,2.7H5c-0.8,0-1.6,0.3-2.1,0.9c-0.6,0.5-0.9,1.2-0.9,2v1
c0-0.8,0.3-1.5,0.9-2.1c0.5-0.5,1.3-0.9,2.1-0.9h18.7c3-0.5,3.1-1.3,5.9-2.7c0.8-0.4,1.5-0.4,2-0.4h27.9c0.7,0,1.3,0.3,1.8,0.8
S62,17.3,62,18v-1c0-0.8-0.3-1.4-0.7-1.9c-0.5-0.5-1.1-0.8-1.8-0.8h-4.4L31.6,14.4L31.6,14.4z M2,22.4c0,0.1,0,0.2,0,0.3v1
c0-0.1,0-0.2,0-0.3V22.4z"/>
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="95.0853" y1="2392.217" x2="128.2754" y2="2426.3699" gradientTransform="matrix(1 0 0 1 -75.9965 -2370.5027)">
<stop offset="0" style="stop-color:#27AEE9"/>
<stop offset="1" style="stop-color:#0669BC"/>
</linearGradient>
<path class="st3" d="M28.5,15.5c0,0-1.4,1.8-4.4,3c-0.1,0-0.2,0-0.4,0.1H5c-0.8,0-1.6,0.3-2.1,0.9C2.4,20,2,20.7,2,21.5v2
c0,0.1,0,0.2,0,0.3v28c0,0.8,0.3,1.4,0.7,1.9c0.5,0.5,1.1,0.8,1.8,0.8h55c0.7,0,1.3-0.3,1.8-0.8s0.7-1.2,0.7-1.9V18.2
c0-0.8-0.3-1.4-0.7-1.9c-0.5-0.5-1.1-0.8-1.8-0.8H31.2L28.5,15.5z"/>
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="-779.0179" y1="632.4208" x2="-763.1432" y2="632.4208" gradientTransform="matrix(3.7796 0 0 3.7796 2946.376 -2369.9976)">
<stop offset="0" style="stop-color:#FFFFFF"/>
<stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0"/>
</linearGradient>
<path class="st4" d="M28.5,15.5c0,0-1.4,1.8-4.4,3c-0.1,0-0.2,0-0.4,0.1H5c-0.8,0-1.6,0.3-2.1,0.9C2.4,20,2,20.7,2,21.5V23
c0-0.8,0.3-1.5,0.9-2.1C3.4,20.3,4.2,20,5,20h18.7c0.1,0,0.3,0,0.4-0.1c2.9-1,4.9-3,4.9-3h30.5c0.7,0,1.3,0.3,1.8,0.8
s0.7,1.2,0.7,1.9v-1.5c0-0.8-0.3-1.4-0.7-1.9c-0.5-0.5-1.1-0.8-1.8-0.8H31.2h-2.7V15.5z M2,23.4c0,0.1,0,0.2,0,0.3v1.5
c0-0.1,0-0.2,0-0.3V23.4z"/>
<linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="-779.0179" y1="641.2203" x2="-763.1432" y2="641.2203" gradientTransform="matrix(3.7796 0 0 3.7796 2946.376 -2370.0562)">
<stop offset="0" style="stop-color:#0A5BA8;stop-opacity:0.9961"/>
<stop offset="1" style="stop-color:#104A8C;stop-opacity:0.9961"/>
</linearGradient>
<path image-rendering="auto" shape-rendering="auto" color-rendering="auto" class="st5" d="M2,51.6v1.1c0,0.8,0.3,1.4,0.7,1.9
c0.5,0.5,1.1,0.8,1.8,0.8h55c0.7,0,1.3-0.3,1.8-0.8s0.7-1.2,0.7-1.9v-1.1c0,0.8-0.3,1.4-0.7,1.9c-0.5,0.5-1.1,0.8-1.8,0.8h-55
c-0.7,0-1.3-0.3-1.8-0.8S2,52.4,2,51.6z"/>
<g>
<image style="overflow:visible;opacity:0.25;" width="33" height="45" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAtCAYAAAAgJgIUAAAACXBIWXMAAAsSAAALEgHS3X78AAAA
GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABD9JREFUeNrMmAlv20YQhcXLoi6f
cRzkQAv0//+m1i6ay67pIxYjiuR2CXwDvNCURSpOUgMPNKnl7tuZN8dyNPof/AXP/K77WSQCELbe
bwjUXN2PJBGCyCMGIYuXoOK+7jtpNJBwMz7xmHgsPPY95tzHYpFB1ogGEAghMPU49HjhceZxAqFE
XDLIEvFAK4zZ/SuPtx4vPVKPrx6X/B4IiXUfi8QDhNjsdOZx7PHG4w+ujWVyjwPGNbooRB9bXRMP
cNseJBrzv/b4zeMdz3KssPJ48PiCddYSMd9FImRciitOscAbyMxYMIDArUcGkRUWedIacU9XmB4W
uOOE6yHumOCCDJ188LiGVCEidbuSsKhIWXBBWE55ZmKcStgusFCKS0aSPx5ZJX4igYVCwBBjFYuA
SsQXoBsjc4BWAq4rCD1KZtGGnVtSGjPpTHZ5zAK205DJl+jggf8riapENvcomUUdBGIWt4UPxcTm
/5RdW4IqIJCLBiyxTZkvEsupa74hoVGwQHwvJSvuM1ncyh1OLOEgNxcBH7KhAIKFuMWpJnTSKRO8
IzO+gFhFKFZMdA/MbWsINJY6Yt5m3A2bcvKORU3VFqbpYE4u+B2cSSK6Jg9Y/K8gNuI+lSjaw+RT
rhlzp2LNUZclzB0HuOItCWmMv1PI3kiErEWcY+Yzizp2a+GctPqQ5uriDaU6FVEecb9k5xYBgSxi
FlFdpTKm7hJkWxObOqdQmhfLEboTs8QaclY1E8ZGPDct5KIF15WsnEy64oVcdlgIylbJrhlXSNKy
zTQ6+uTxGTcupbB9Q8KSRykmz5hgn+e5KHvFu04WM1IrFgp5ryHwj8dHSOTt6hq1UrUmLEtGewzO
iYwv7DhBwBYJIxmfYPJm/HuPvyByKe/XmzKmamEPWEjV4oqQfHCMpSwrTiSBPeCCc48LLHErucZt
6zG1ek5YIBELWaN7KnXExFiy2yt2f0Fpz7r00KfRNdckYpVZK3zn/G7izFj0AqgbVl2d1iYSrker
f8DVMmNj/n9Z9BwYgfsuN/SxhGvV/Uiq6xwyE+kXrhDhnyLE5tkdv5eb+s34icULdqcnrrG4opax
X1nsipC8JLy3EniqvXNSLR+EwJzyfMpVBWaJroBUu5Ma3Og6qYwFu1lK/ahFtBFuURdZoep1Uo+3
aGLUKj6VRE0kYTsVsWoCi/ocuoccAwMJ16QVujUkjiRsUzm1V9vywNDPCO2GNRDNpNJ/xh3fMHa2
hBOhrhGd6SNnjJXzcpcPJX1JlCx+TxheYvIQ/5eEZCZhud6UnIaScC0St+SBfUlSM37P+tSJXS1R
Sz+RIcYRteCzNLJ3EPgbqyy3Jamh0WHNyr1o4IbUPGaRJc+upXkp+7gjGBAZejqbyElMT2G5CFaz
5bN9vetqfjUzVhIhg6Jk6CdEPbG3v2Vq1R0Uos/9RXfnr7q//O8/AQYAxhWve3m9fqQAAAAASUVO
RK5CYII=" transform="matrix(1 0 0 1 16 15)">
</image>
<g>
<g>
<linearGradient id="SVGID_5_" gradientUnits="userSpaceOnUse" x1="32.3842" y1="22.1772" x2="32.3842" y2="51.1664">
<stop offset="0" style="stop-color:#FBFDFE"/>
<stop offset="0.9716" style="stop-color:#C6E8F5"/>
</linearGradient>
<path class="st6" d="M28.6,34.1c0-1.7-1.4-3.2-3.1-3.2c-1.8,0-3.3,1.4-3.3,3.2c0,1.4,0.9,2.6,2.2,3v1.2c0,0.7,0.4,1.5,1,1.9
l6.2,4.7c-1.6,0.5-2.7,1.9-2.7,3.6c0,2.1,1.7,3.8,3.8,3.8s3.8-1.7,3.8-3.8c0-1.7-1.2-3.2-2.8-3.7v-1.8l5.7-3.9c0.6-0.4,1-1.2,1-2
v-1.7h0.6c0.9,0,1.7-0.8,1.7-1.7V31c0-0.9-0.8-1.7-1.7-1.7h-3.3c-0.9,0-1.7,0.8-1.7,1.7v2.8c0,0.9,0.8,1.7,1.7,1.7h0.6v1.9
l-4.7,3.3V28.2h1.7c1.2,0,1.9-1.3,1.3-2.3L34,21.7c-0.7-1.1-2.2-1.1-2.9,0l-2.6,4.2c-0.6,1,0.1,2.3,1.3,2.3h1.8v14.3l-5.2-3.9
v-1.4C27.6,36.7,28.6,35.5,28.6,34.1z"/>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View file

@ -1,25 +1,25 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="200.000000pt" height="200.000000pt" viewBox="0 0 200.000000 200.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.14, written by Peter Selinger 2001-2017
</metadata>
<g transform="translate(0.000000,200.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M875 1894 c-11 -2 -51 -11 -88 -20 -341 -78 -610 -364 -673 -714 -82
-459 197 -902 647 -1030 94 -27 277 -37 378 -21 303 47 575 261 690 540 55
133 66 192 65 361 -1 136 -4 165 -27 235 -36 116 -62 170 -123 261 -123 186
-347 336 -566 379 -42 8 -276 15 -303 9z m250 -168 c11 -2 42 -9 70 -16 131
-30 288 -135 387 -260 160 -201 198 -506 93 -745 l-22 -50 2 80 c3 180 -64
351 -184 476 -209 216 -544 260 -807 105 -201 -117 -326 -347 -319 -587 l2
-74 -19 44 c-63 140 -80 332 -44 476 24 94 87 219 147 292 109 133 290 238
448 259 25 3 47 7 49 9 5 4 173 -3 197 -9z m8 -501 c33 -8 85 -31 116 -50 227
-137 305 -418 183 -651 l-21 -39 -1 35 c-9 251 -245 439 -490 389 -186 -38
-323 -200 -330 -389 l-1 -35 -23 45 c-101 194 -61 429 99 578 130 122 292 162
468 117z m-27 -499 c182 -85 183 -345 1 -438 -53 -27 -161 -26 -215 1 -146 75
-180 267 -69 390 63 69 190 90 283 47z"/>
</g>
</svg>
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="200.000000pt" height="200.000000pt" viewBox="0 0 200.000000 200.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.14, written by Peter Selinger 2001-2017
</metadata>
<g transform="translate(0.000000,200.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M875 1894 c-11 -2 -51 -11 -88 -20 -341 -78 -610 -364 -673 -714 -82
-459 197 -902 647 -1030 94 -27 277 -37 378 -21 303 47 575 261 690 540 55
133 66 192 65 361 -1 136 -4 165 -27 235 -36 116 -62 170 -123 261 -123 186
-347 336 -566 379 -42 8 -276 15 -303 9z m250 -168 c11 -2 42 -9 70 -16 131
-30 288 -135 387 -260 160 -201 198 -506 93 -745 l-22 -50 2 80 c3 180 -64
351 -184 476 -209 216 -544 260 -807 105 -201 -117 -326 -347 -319 -587 l2
-74 -19 44 c-63 140 -80 332 -44 476 24 94 87 219 147 292 109 133 290 238
448 259 25 3 47 7 49 9 5 4 173 -3 197 -9z m8 -501 c33 -8 85 -31 116 -50 227
-137 305 -418 183 -651 l-21 -39 -1 35 c-9 251 -245 439 -490 389 -186 -38
-323 -200 -330 -389 l-1 -35 -23 45 c-101 194 -61 429 99 578 130 122 292 162
468 117z m-27 -499 c182 -85 183 -345 1 -438 -53 -27 -161 -26 -215 1 -146 75
-180 267 -69 390 63 69 190 90 283 47z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -1,159 +1,159 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
version="1.1"
id="Capa_1"
x="0px"
y="0px"
viewBox="0 0 407 407"
style="enable-background:new 0 0 407 407;"
xml:space="preserve"><metadata
id="metadata36"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs34"><linearGradient
xlink:href="#SVGID_1_"
id="linearGradient46"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1,0,0,-1,0,407.2074)"
x1="203.7169"
y1="7.7039"
x2="203.7169"
y2="402.0255" /><linearGradient
xlink:href="#SVGID_1_"
id="linearGradient48"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1,0,0,-1,0,407.2074)"
x1="203.7169"
y1="7.7039"
x2="203.7169"
y2="402.0255" />
<linearGradient
gradientTransform="matrix(1,0,0,-1,0,407.2074)"
y2="402.02551"
x2="203.7169"
y1="7.7038999"
x1="203.7169"
gradientUnits="userSpaceOnUse"
id="linearGradient120"><stop
id="stop6-9"
style="stop-color:#F2F2F2"
offset="0" /><stop
id="stop8-1"
style="stop-color:#F7F7F7"
offset="0.5963" /><stop
id="stop10-7"
style="stop-color:#FEFEFE"
offset="1" /></linearGradient><filter
style="color-interpolation-filters:sRGB"
id="filter1002"
x="-0.013201674"
width="1.0264033"
y="-0.013198327"
height="1.0263967"><feGaussianBlur
stdDeviation="2.168925"
id="feGaussianBlur1004" /></filter></defs>
<style
type="text/css"
id="style2">
.st0{fill:url(#SVGID_1_);}
.st1{fill:#515151;}
</style>
<g
transform="matrix(1,0,0,0.98477354,0,6.0829719)"
id="g15-0"
style="fill:#4d4d4d;filter:url(#filter1002)"><linearGradient
gradientTransform="matrix(1,0,0,-1,0,407.2074)"
y2="402.02551"
x2="203.7169"
y1="7.7038999"
x1="203.7169"
gradientUnits="userSpaceOnUse"
id="linearGradient91"><stop
id="stop85"
style="stop-color:#F2F2F2"
offset="0" /><stop
id="stop87"
style="stop-color:#F7F7F7"
offset="0.5963" /><stop
id="stop89"
style="stop-color:#FEFEFE"
offset="1" /></linearGradient><path
style="fill:#4d4d4d"
id="path13-7"
d="M 400.9,202.3 C 400.9,92.3 313.8,5.1 203.7,5.1 93.7,5.2 6.6,92.3 6.6,202.3 c 0,110 87.1,197.2 197.2,197.2 110.1,0 197.1,-87.1 197.1,-197.2 z"
class="st0" /><path
style="fill:#4d4d4d"
class="st0"
d="M 400.9,202.3 C 400.9,92.3 313.8,5.1 203.7,5.1 93.7,5.2 6.6,92.3 6.6,202.3 c 0,110 87.1,197.2 197.2,197.2 110.1,0 197.1,-87.1 197.1,-197.2 z"
id="path40-7" /></g><g
id="g15">
<linearGradient
gradientTransform="matrix(1,0,0,-1,0,407.2074)"
y2="402.02551"
x2="203.7169"
y1="7.7038999"
x1="203.7169"
gradientUnits="userSpaceOnUse"
id="SVGID_1_">
<stop
id="stop6"
style="stop-color:#F2F2F2"
offset="0" />
<stop
id="stop8"
style="stop-color:#F7F7F7"
offset="0.5963" />
<stop
id="stop10"
style="stop-color:#FEFEFE"
offset="1" />
</linearGradient>
<path
style="fill:url(#linearGradient46)"
id="path13"
d="M 400.9,202.3 C 400.9,92.3 313.8,5.1 203.7,5.1 93.7,5.2 6.6,92.3 6.6,202.3 c 0,110 87.1,197.2 197.2,197.2 110.1,0 197.1,-87.1 197.1,-197.2 z"
class="st0" />
<path
style="fill:url(#linearGradient48)"
class="st0"
d="M 400.9,202.3 C 400.9,92.3 313.8,5.1 203.7,5.1 93.7,5.2 6.6,92.3 6.6,202.3 c 0,110 87.1,197.2 197.2,197.2 110.1,0 197.1,-87.1 197.1,-197.2 z"
id="path40" /></g><g
id="g27">
<path
style="fill:#515151"
id="path17"
d="m 223.7,137.8 h 0.2 v 6.9 h 7.9 v -28.5 c 0,-2.1 -0.4,-3.9 -1.2,-5.6 -0.8,-1.7 -1.9,-3.1 -3.4,-4.3 -1.5,-1.2 -3.2,-2.1 -5.3,-2.7 -2.1,-0.6 -4.3,-1 -6.9,-1 -2.3,0 -4.5,0.3 -6.5,1 -2,0.6 -3.7,1.5 -5.2,2.7 -1.5,1.2 -2.6,2.5 -3.5,4.1 -0.9,1.6 -1.3,3.3 -1.4,5.1 h 7.8 c 0.2,-0.9 0.5,-1.7 1,-2.4 0.5,-0.7 1.1,-1.4 1.8,-1.9 0.7,-0.5 1.6,-0.9 2.6,-1.2 1,-0.3 2,-0.4 3.1,-0.4 2.8,0 4.9,0.6 6.5,1.9 1.5,1.2 2.3,3.1 2.3,5.5 v 3.2 l -11.2,0.6 c -5.2,0.3 -9.1,1.5 -11.8,3.6 -2.7,2.1 -4.1,4.9 -4.1,8.6 0,1.9 0.3,3.6 1,5.1 0.7,1.5 1.7,2.8 2.9,3.9 1.2,1.1 2.7,1.9 4.4,2.5 1.7,0.6 3.6,0.9 5.7,0.9 1.4,0 2.8,-0.2 4.1,-0.5 1.3,-0.3 2.6,-0.8 3.7,-1.5 1.1,-0.6 2.1,-1.4 3.1,-2.3 1,-1.2 1.8,-2.2 2.4,-3.3 z m -3.2,-2 c -1,0.8 -2.1,1.5 -3.4,2 -1.3,0.5 -2.8,0.7 -4.3,0.7 -2.4,0 -4.3,-0.5 -5.7,-1.6 -1.4,-1.1 -2.1,-2.5 -2.1,-4.3 0,-1.8 0.7,-3.3 2.2,-4.3 1.4,-1 3.6,-1.6 6.5,-1.8 l 10.1,-0.7 v 3.2 c 0,1.4 -0.3,2.6 -0.8,3.8 -0.8,1.2 -1.5,2.2 -2.5,3 z"
class="st1" />
<path
style="fill:#515151"
id="path19"
d="m 278.5,228.7 -13.7,-3.4 c -16.7,-4.2 -23.6,-9.7 -23.6,-18.9 0,-11.7 10.9,-19.8 26.7,-19.8 15.8,0 26.5,8 27.7,21 h 17.9 c -0.7,-21.6 -19,-36.7 -45.2,-36.7 -26.8,0 -45.7,15 -45.7,36.5 0,17.5 10.6,28.2 34.1,34 l 16.3,4 c 16.7,4.2 23.9,10.4 23.9,20.6 0,11.8 -11.9,20.4 -28.4,20.4 -17.2,0 -29.8,-8.6 -31.2,-21.7 h -18 c 1.3,22.8 20.2,37.3 47.9,37.3 29.7,0 48.4,-14.7 48.4,-38.3 -0.3,-18.4 -11,-28.7 -37.1,-35 z"
class="st1" />
<path
style="fill:#515151"
id="path21"
d="m 187.7,144.7 v -28.3 c 0,-2.1 -0.3,-4 -1,-5.6 -0.7,-1.6 -1.5,-3.1 -2.7,-4.3 -1.2,-1.2 -2.6,-2.1 -4.3,-2.8 -1.7,-0.7 -3.5,-1 -5.6,-1 -1.5,0 -2.9,0.2 -4.2,0.6 -1.3,0.4 -2.6,0.9 -3.7,1.6 -1.1,0.7 -2.1,1.6 -3,2.5 -0.9,0.9 -1.6,2.1 -2.1,3.4 h -0.2 c -0.8,-2.6 -2.3,-4.6 -4.3,-6 -2,-1.4 -4.5,-2.1 -7.4,-2.1 -1.3,0 -2.6,0.2 -3.9,0.5 -1.2,0.3 -2.3,0.8 -3.4,1.5 -1,0.7 -1.9,1.4 -2.7,2.4 -0.8,0.9 -1.4,2 -1.9,3.1 h -0.2 v -7 h -7.9 v 41.6 h 7.8 v -25.5 c 0,-1.3 0.7,-2.6 1.1,-3.7 0.4,-1.1 1,-2.1 1.8,-3 0.8,-0.8 1.7,-1.5 2.8,-2 1.1,-0.5 2.3,-0.7 3.5,-0.7 1.2,0 2.3,0.2 3.3,0.6 1,0.4 1.8,0.9 2.5,1.6 0.7,0.7 1.2,1.5 1.6,2.5 0.4,1 0.6,2.1 0.6,3.2 v 27 h 8.2 V 119 c 0,-1.3 0.2,-2.6 0.7,-3.7 0.4,-1.1 1,-2.1 1.8,-2.9 0.8,-0.8 1.7,-1.4 2.7,-1.9 1.1,-0.4 2.2,-0.7 3.5,-0.7 2.6,0 4.6,0.8 6,2.2 1.4,1.5 2.1,3.6 2.1,6.3 v 26.3 z"
class="st1" />
<path
style="fill:#515151"
id="path23"
d="m 147.4,170.9 c -36.3,0 -59.1,25.3 -59.1,65.6 0,40.3 22.8,65.6 59.1,65.6 36.3,0 59.1,-25.3 59.1,-65.6 -0.1,-40.3 -22.8,-65.6 -59.1,-65.6 z m 0,115 c -24.9,0 -40.5,-19.1 -40.5,-49.4 0,-30.4 15.6,-49.5 40.5,-49.5 24.8,0 40.5,19.1 40.5,49.5 -0.1,30.3 -15.7,49.4 -40.5,49.4 z"
class="st1" />
<path
style="fill:#515151"
id="path25"
d="m 252.1,113.5 c 1,-1.2 2.1,-2.2 3.5,-2.9 1.4,-0.7 2.9,-1 4.6,-1 1.4,0 2.7,0.2 3.8,0.6 1.1,0.4 2.1,0.9 3,1.6 0.8,0.7 1.5,1.5 2,2.5 0.5,1 0.9,2 1.1,3.1 h 7.9 c -0.2,-2 -0.7,-3.9 -1.6,-5.7 -0.9,-1.8 -2.1,-3.3 -3.6,-4.7 -1.5,-1.3 -3.4,-2.4 -5.5,-3.2 -2.1,-0.8 -4.5,-1.2 -7.2,-1.2 -2.9,0 -5.6,0.5 -8,1.5 -2.4,1 -4.4,2.4 -6.1,4.3 -1.7,1.9 -3,4.1 -3.9,6.7 -0.9,2.6 -1.4,5.5 -1.4,8.8 0,3.3 0.5,6.3 1.4,8.9 0.9,2.6 2.3,4.9 4,6.7 1.7,1.8 3.8,3.2 6.2,4.2 2.4,1 5.1,1.5 8,1.5 2.5,0 4.8,-0.4 6.9,-1 2.1,-0.6 3.9,-1.7 5.5,-2.9 1.5,-1.3 2.8,-2.8 3.8,-4.6 1,-1.8 1.6,-3.8 1.8,-5.9 h -7.9 c -0.5,2.5 -1.6,4.3 -3.3,5.6 -1.7,1.3 -3.9,1.9 -6.6,1.9 -1.7,0 -3.2,-0.3 -4.6,-1 -1.4,-0.6 -2.6,-1.6 -3.5,-2.8 -1,-1.2 -1.7,-2.7 -2.3,-4.5 -0.5,-1.8 -0.8,-3.8 -0.8,-6 0,-2.2 0.3,-4.1 0.8,-5.9 0.3,-1.9 1,-3.4 2,-4.6 z"
class="st1" />
</g>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
version="1.1"
id="Capa_1"
x="0px"
y="0px"
viewBox="0 0 407 407"
style="enable-background:new 0 0 407 407;"
xml:space="preserve"><metadata
id="metadata36"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs34"><linearGradient
xlink:href="#SVGID_1_"
id="linearGradient46"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1,0,0,-1,0,407.2074)"
x1="203.7169"
y1="7.7039"
x2="203.7169"
y2="402.0255" /><linearGradient
xlink:href="#SVGID_1_"
id="linearGradient48"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1,0,0,-1,0,407.2074)"
x1="203.7169"
y1="7.7039"
x2="203.7169"
y2="402.0255" />
<linearGradient
gradientTransform="matrix(1,0,0,-1,0,407.2074)"
y2="402.02551"
x2="203.7169"
y1="7.7038999"
x1="203.7169"
gradientUnits="userSpaceOnUse"
id="linearGradient120"><stop
id="stop6-9"
style="stop-color:#F2F2F2"
offset="0" /><stop
id="stop8-1"
style="stop-color:#F7F7F7"
offset="0.5963" /><stop
id="stop10-7"
style="stop-color:#FEFEFE"
offset="1" /></linearGradient><filter
style="color-interpolation-filters:sRGB"
id="filter1002"
x="-0.013201674"
width="1.0264033"
y="-0.013198327"
height="1.0263967"><feGaussianBlur
stdDeviation="2.168925"
id="feGaussianBlur1004" /></filter></defs>
<style
type="text/css"
id="style2">
.st0{fill:url(#SVGID_1_);}
.st1{fill:#515151;}
</style>
<g
transform="matrix(1,0,0,0.98477354,0,6.0829719)"
id="g15-0"
style="fill:#4d4d4d;filter:url(#filter1002)"><linearGradient
gradientTransform="matrix(1,0,0,-1,0,407.2074)"
y2="402.02551"
x2="203.7169"
y1="7.7038999"
x1="203.7169"
gradientUnits="userSpaceOnUse"
id="linearGradient91"><stop
id="stop85"
style="stop-color:#F2F2F2"
offset="0" /><stop
id="stop87"
style="stop-color:#F7F7F7"
offset="0.5963" /><stop
id="stop89"
style="stop-color:#FEFEFE"
offset="1" /></linearGradient><path
style="fill:#4d4d4d"
id="path13-7"
d="M 400.9,202.3 C 400.9,92.3 313.8,5.1 203.7,5.1 93.7,5.2 6.6,92.3 6.6,202.3 c 0,110 87.1,197.2 197.2,197.2 110.1,0 197.1,-87.1 197.1,-197.2 z"
class="st0" /><path
style="fill:#4d4d4d"
class="st0"
d="M 400.9,202.3 C 400.9,92.3 313.8,5.1 203.7,5.1 93.7,5.2 6.6,92.3 6.6,202.3 c 0,110 87.1,197.2 197.2,197.2 110.1,0 197.1,-87.1 197.1,-197.2 z"
id="path40-7" /></g><g
id="g15">
<linearGradient
gradientTransform="matrix(1,0,0,-1,0,407.2074)"
y2="402.02551"
x2="203.7169"
y1="7.7038999"
x1="203.7169"
gradientUnits="userSpaceOnUse"
id="SVGID_1_">
<stop
id="stop6"
style="stop-color:#F2F2F2"
offset="0" />
<stop
id="stop8"
style="stop-color:#F7F7F7"
offset="0.5963" />
<stop
id="stop10"
style="stop-color:#FEFEFE"
offset="1" />
</linearGradient>
<path
style="fill:url(#linearGradient46)"
id="path13"
d="M 400.9,202.3 C 400.9,92.3 313.8,5.1 203.7,5.1 93.7,5.2 6.6,92.3 6.6,202.3 c 0,110 87.1,197.2 197.2,197.2 110.1,0 197.1,-87.1 197.1,-197.2 z"
class="st0" />
<path
style="fill:url(#linearGradient48)"
class="st0"
d="M 400.9,202.3 C 400.9,92.3 313.8,5.1 203.7,5.1 93.7,5.2 6.6,92.3 6.6,202.3 c 0,110 87.1,197.2 197.2,197.2 110.1,0 197.1,-87.1 197.1,-197.2 z"
id="path40" /></g><g
id="g27">
<path
style="fill:#515151"
id="path17"
d="m 223.7,137.8 h 0.2 v 6.9 h 7.9 v -28.5 c 0,-2.1 -0.4,-3.9 -1.2,-5.6 -0.8,-1.7 -1.9,-3.1 -3.4,-4.3 -1.5,-1.2 -3.2,-2.1 -5.3,-2.7 -2.1,-0.6 -4.3,-1 -6.9,-1 -2.3,0 -4.5,0.3 -6.5,1 -2,0.6 -3.7,1.5 -5.2,2.7 -1.5,1.2 -2.6,2.5 -3.5,4.1 -0.9,1.6 -1.3,3.3 -1.4,5.1 h 7.8 c 0.2,-0.9 0.5,-1.7 1,-2.4 0.5,-0.7 1.1,-1.4 1.8,-1.9 0.7,-0.5 1.6,-0.9 2.6,-1.2 1,-0.3 2,-0.4 3.1,-0.4 2.8,0 4.9,0.6 6.5,1.9 1.5,1.2 2.3,3.1 2.3,5.5 v 3.2 l -11.2,0.6 c -5.2,0.3 -9.1,1.5 -11.8,3.6 -2.7,2.1 -4.1,4.9 -4.1,8.6 0,1.9 0.3,3.6 1,5.1 0.7,1.5 1.7,2.8 2.9,3.9 1.2,1.1 2.7,1.9 4.4,2.5 1.7,0.6 3.6,0.9 5.7,0.9 1.4,0 2.8,-0.2 4.1,-0.5 1.3,-0.3 2.6,-0.8 3.7,-1.5 1.1,-0.6 2.1,-1.4 3.1,-2.3 1,-1.2 1.8,-2.2 2.4,-3.3 z m -3.2,-2 c -1,0.8 -2.1,1.5 -3.4,2 -1.3,0.5 -2.8,0.7 -4.3,0.7 -2.4,0 -4.3,-0.5 -5.7,-1.6 -1.4,-1.1 -2.1,-2.5 -2.1,-4.3 0,-1.8 0.7,-3.3 2.2,-4.3 1.4,-1 3.6,-1.6 6.5,-1.8 l 10.1,-0.7 v 3.2 c 0,1.4 -0.3,2.6 -0.8,3.8 -0.8,1.2 -1.5,2.2 -2.5,3 z"
class="st1" />
<path
style="fill:#515151"
id="path19"
d="m 278.5,228.7 -13.7,-3.4 c -16.7,-4.2 -23.6,-9.7 -23.6,-18.9 0,-11.7 10.9,-19.8 26.7,-19.8 15.8,0 26.5,8 27.7,21 h 17.9 c -0.7,-21.6 -19,-36.7 -45.2,-36.7 -26.8,0 -45.7,15 -45.7,36.5 0,17.5 10.6,28.2 34.1,34 l 16.3,4 c 16.7,4.2 23.9,10.4 23.9,20.6 0,11.8 -11.9,20.4 -28.4,20.4 -17.2,0 -29.8,-8.6 -31.2,-21.7 h -18 c 1.3,22.8 20.2,37.3 47.9,37.3 29.7,0 48.4,-14.7 48.4,-38.3 -0.3,-18.4 -11,-28.7 -37.1,-35 z"
class="st1" />
<path
style="fill:#515151"
id="path21"
d="m 187.7,144.7 v -28.3 c 0,-2.1 -0.3,-4 -1,-5.6 -0.7,-1.6 -1.5,-3.1 -2.7,-4.3 -1.2,-1.2 -2.6,-2.1 -4.3,-2.8 -1.7,-0.7 -3.5,-1 -5.6,-1 -1.5,0 -2.9,0.2 -4.2,0.6 -1.3,0.4 -2.6,0.9 -3.7,1.6 -1.1,0.7 -2.1,1.6 -3,2.5 -0.9,0.9 -1.6,2.1 -2.1,3.4 h -0.2 c -0.8,-2.6 -2.3,-4.6 -4.3,-6 -2,-1.4 -4.5,-2.1 -7.4,-2.1 -1.3,0 -2.6,0.2 -3.9,0.5 -1.2,0.3 -2.3,0.8 -3.4,1.5 -1,0.7 -1.9,1.4 -2.7,2.4 -0.8,0.9 -1.4,2 -1.9,3.1 h -0.2 v -7 h -7.9 v 41.6 h 7.8 v -25.5 c 0,-1.3 0.7,-2.6 1.1,-3.7 0.4,-1.1 1,-2.1 1.8,-3 0.8,-0.8 1.7,-1.5 2.8,-2 1.1,-0.5 2.3,-0.7 3.5,-0.7 1.2,0 2.3,0.2 3.3,0.6 1,0.4 1.8,0.9 2.5,1.6 0.7,0.7 1.2,1.5 1.6,2.5 0.4,1 0.6,2.1 0.6,3.2 v 27 h 8.2 V 119 c 0,-1.3 0.2,-2.6 0.7,-3.7 0.4,-1.1 1,-2.1 1.8,-2.9 0.8,-0.8 1.7,-1.4 2.7,-1.9 1.1,-0.4 2.2,-0.7 3.5,-0.7 2.6,0 4.6,0.8 6,2.2 1.4,1.5 2.1,3.6 2.1,6.3 v 26.3 z"
class="st1" />
<path
style="fill:#515151"
id="path23"
d="m 147.4,170.9 c -36.3,0 -59.1,25.3 -59.1,65.6 0,40.3 22.8,65.6 59.1,65.6 36.3,0 59.1,-25.3 59.1,-65.6 -0.1,-40.3 -22.8,-65.6 -59.1,-65.6 z m 0,115 c -24.9,0 -40.5,-19.1 -40.5,-49.4 0,-30.4 15.6,-49.5 40.5,-49.5 24.8,0 40.5,19.1 40.5,49.5 -0.1,30.3 -15.7,49.4 -40.5,49.4 z"
class="st1" />
<path
style="fill:#515151"
id="path25"
d="m 252.1,113.5 c 1,-1.2 2.1,-2.2 3.5,-2.9 1.4,-0.7 2.9,-1 4.6,-1 1.4,0 2.7,0.2 3.8,0.6 1.1,0.4 2.1,0.9 3,1.6 0.8,0.7 1.5,1.5 2,2.5 0.5,1 0.9,2 1.1,3.1 h 7.9 c -0.2,-2 -0.7,-3.9 -1.6,-5.7 -0.9,-1.8 -2.1,-3.3 -3.6,-4.7 -1.5,-1.3 -3.4,-2.4 -5.5,-3.2 -2.1,-0.8 -4.5,-1.2 -7.2,-1.2 -2.9,0 -5.6,0.5 -8,1.5 -2.4,1 -4.4,2.4 -6.1,4.3 -1.7,1.9 -3,4.1 -3.9,6.7 -0.9,2.6 -1.4,5.5 -1.4,8.8 0,3.3 0.5,6.3 1.4,8.9 0.9,2.6 2.3,4.9 4,6.7 1.7,1.8 3.8,3.2 6.2,4.2 2.4,1 5.1,1.5 8,1.5 2.5,0 4.8,-0.4 6.9,-1 2.1,-0.6 3.9,-1.7 5.5,-2.9 1.5,-1.3 2.8,-2.8 3.8,-4.6 1,-1.8 1.6,-3.8 1.8,-5.9 h -7.9 c -0.5,2.5 -1.6,4.3 -3.3,5.6 -1.7,1.3 -3.9,1.9 -6.6,1.9 -1.7,0 -3.2,-0.3 -4.6,-1 -1.4,-0.6 -2.6,-1.6 -3.5,-2.8 -1,-1.2 -1.7,-2.7 -2.3,-4.5 -0.5,-1.8 -0.8,-3.8 -0.8,-6 0,-2.2 0.3,-4.1 0.8,-5.9 0.3,-1.9 1,-3.4 2,-4.6 z"
class="st1" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

View file

@ -1,54 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 429 117.3" enable-background="new 0 0 429 117.3" xml:space="preserve">
<g>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="58.666" y1="117.332" x2="58.666" y2="-9.094947e-13">
<stop offset="0" style="stop-color:#0882C8"/>
<stop offset="1" style="stop-color:#26B6DB"/>
</linearGradient>
<circle fill="url(#SVGID_1_)" cx="58.7" cy="58.7" r="58.7"/>
<g>
<circle fill="none" stroke="#FFFFFF" stroke-width="6" stroke-miterlimit="10" cx="58.7" cy="58.5" r="43.7"/>
<g>
<path fill="#FFFFFF" d="M94.7,47.8c4.7,1.6,9.8-0.9,11.4-5.6c1.6-4.7-0.9-9.8-5.6-11.4c-4.7-1.6-9.8,0.9-11.4,5.6
C87.5,41.1,90,46.2,94.7,47.8z"/>
<line fill="none" stroke="#FFFFFF" stroke-width="6" stroke-miterlimit="10" x1="97.6" y1="39.4" x2="67.5" y2="64.4"/>
</g>
<g>
<path fill="#FFFFFF" d="M77.6,91c-0.4,4.9,3.2,9.3,8.2,9.8c5,0.4,9.3-3.2,9.8-8.2c0.4-4.9-3.2-9.3-8.2-9.8
C82.4,82.4,78,86,77.6,91z"/>
<line fill="none" stroke="#FFFFFF" stroke-width="6" stroke-miterlimit="10" x1="86.5" y1="91.8" x2="67.5" y2="64.4"/>
</g>
<path fill="#FFFFFF" d="M60,69.3c2.7,4.2,8.3,5.4,12.4,2.7c4.2-2.7,5.4-8.3,2.7-12.4c-2.7-4.2-8.3-5.4-12.4-2.7
C58.5,59.5,57.3,65.1,60,69.3z"/>
<g>
<path fill="#FFFFFF" d="M21.2,61.4c-4.3-2.5-9.8-1.1-12.3,3.1c-2.5,4.3-1.1,9.8,3.1,12.3c4.3,2.5,9.8,1.1,12.3-3.1
C26.8,69.5,25.4,64,21.2,61.4z"/>
<line fill="none" stroke="#FFFFFF" stroke-width="6" stroke-miterlimit="10" x1="16.6" y1="69.1" x2="67.5" y2="64.4"/>
</g>
</g>
</g>
<g>
<path fill="#0891D1" d="M163.8,50.2c-0.6-0.7-6.3-4.1-11.4-4.1c-3.4,0-5.2,1.2-5.2,3.5c0,2.9,3.2,3.7,8.9,5.2
c8.2,2.2,13.3,5,13.3,12.9c0,9.7-7.8,13-16,13c-6.2,0-13.1-2-18.2-5.3l4.3-8.6c0.8,0.8,7.5,5,14,5c3.5,0,5.2-1.1,5.2-3.2
c0-3.2-4.4-4-10.3-5.8c-7.9-2.4-11.5-5.3-11.5-11.8c0-9,7.2-13.9,15.7-13.9c6.1,0,11.6,2.5,15.4,4.7L163.8,50.2z"/>
<path fill="#0891D1" d="M175,85.1c1.7,0.5,3.3,0.8,4.4,0.8c2,0,3.3-1.5,4.2-5.5l-11.9-31.5h9.8l7.4,23.3l6.3-23.3h8.9l-12.1,36.6
c-1.7,5.3-6.2,8.7-11.8,8.8c-1.7,0-3.5-0.2-5.3-0.9V85.1z"/>
<path fill="#0891D1" d="M239.3,80.3h-9.6V62.6c0-4.1-1.7-5.9-4.3-5.9c-2.6,0-5.8,2.3-7,5.6v18.1h-9.6V48.8h8.6v5.3
c2.3-3.7,6.8-5.9,12.2-5.9c8.2,0,9.5,6.7,9.5,11.9V80.3z"/>
<path fill="#0891D1" d="M261.6,48.2c7.2,0,12.3,3.4,14.8,8.3l-9.4,2.8c-1.2-1.9-3.1-3-5.5-3c-4,0-7,3.2-7,8.2c0,5,3.1,8.3,7,8.3
c2.4,0,4.6-1.3,5.5-3.1l9.4,2.9c-2.3,4.9-7.6,8.3-14.8,8.3c-10.6,0-16.9-7.7-16.9-16.4S250.9,48.2,261.6,48.2z"/>
<path fill="#0891D1" d="M302.1,78.7c-2.6,1.1-6.2,2.3-9.7,2.3c-4.7,0-8.8-2.3-8.8-8.4V56.1h-4v-7.3h4v-10h9.6v10h6.4v7.3h-6.4v13.1
c0,2.1,1.2,2.9,2.8,2.9c1.4,0,3-0.6,4.2-1.1L302.1,78.7z"/>
<path fill="#0891D1" d="M337.2,80.3h-9.6V62.6c0-4.1-1.8-5.9-4.6-5.9c-2.3,0-5.5,2.2-6.7,5.6v18.1h-9.6V36.5h9.6v17.6
c2.3-3.7,6.3-5.9,10.9-5.9c8.5,0,9.9,6.5,9.9,11.9V80.3z"/>
<path fill="#0891D1" d="M343.4,45.2v-8.7h9.6v8.7H343.4z M343.4,80.3V48.8h9.6v31.5H343.4z"/>
<path fill="#0891D1" d="M389.9,80.3h-9.6V62.6c0-4.1-1.7-5.9-4.3-5.9c-2.6,0-5.8,2.3-7,5.6v18.1h-9.6V48.8h8.6v5.3
c2.3-3.7,6.8-5.9,12.2-5.9c8.2,0,9.5,6.7,9.5,11.9V80.3z"/>
<path fill="#0891D1" d="M395.5,64.6c0-9.2,6-16.3,14.6-16.3c4.7,0,8.4,2.2,10.6,5.8v-5.2h8.3v29.3c0,9.6-7.5,15.5-18.2,15.5
c-6.8,0-11.5-2.3-15-6.3l5.1-5.2c2.3,2.6,6,4.3,9.9,4.3c4.6,0,8.6-2.4,8.6-8.3v-3.1c-1.9,3.5-5.9,5.3-10,5.3
C401.1,80.5,395.5,73.3,395.5,64.6z M419.4,68.5v-6.6c-1.3-3.3-4.2-5.5-7.1-5.5c-4.1,0-7,4-7,8.4c0,4.6,3.2,8,7.5,8
C415.7,72.8,418.1,71,419.4,68.5z"/>
</g>
</svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 429 117.3" enable-background="new 0 0 429 117.3" xml:space="preserve">
<g>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="58.666" y1="117.332" x2="58.666" y2="-9.094947e-13">
<stop offset="0" style="stop-color:#0882C8"/>
<stop offset="1" style="stop-color:#26B6DB"/>
</linearGradient>
<circle fill="url(#SVGID_1_)" cx="58.7" cy="58.7" r="58.7"/>
<g>
<circle fill="none" stroke="#FFFFFF" stroke-width="6" stroke-miterlimit="10" cx="58.7" cy="58.5" r="43.7"/>
<g>
<path fill="#FFFFFF" d="M94.7,47.8c4.7,1.6,9.8-0.9,11.4-5.6c1.6-4.7-0.9-9.8-5.6-11.4c-4.7-1.6-9.8,0.9-11.4,5.6
C87.5,41.1,90,46.2,94.7,47.8z"/>
<line fill="none" stroke="#FFFFFF" stroke-width="6" stroke-miterlimit="10" x1="97.6" y1="39.4" x2="67.5" y2="64.4"/>
</g>
<g>
<path fill="#FFFFFF" d="M77.6,91c-0.4,4.9,3.2,9.3,8.2,9.8c5,0.4,9.3-3.2,9.8-8.2c0.4-4.9-3.2-9.3-8.2-9.8
C82.4,82.4,78,86,77.6,91z"/>
<line fill="none" stroke="#FFFFFF" stroke-width="6" stroke-miterlimit="10" x1="86.5" y1="91.8" x2="67.5" y2="64.4"/>
</g>
<path fill="#FFFFFF" d="M60,69.3c2.7,4.2,8.3,5.4,12.4,2.7c4.2-2.7,5.4-8.3,2.7-12.4c-2.7-4.2-8.3-5.4-12.4-2.7
C58.5,59.5,57.3,65.1,60,69.3z"/>
<g>
<path fill="#FFFFFF" d="M21.2,61.4c-4.3-2.5-9.8-1.1-12.3,3.1c-2.5,4.3-1.1,9.8,3.1,12.3c4.3,2.5,9.8,1.1,12.3-3.1
C26.8,69.5,25.4,64,21.2,61.4z"/>
<line fill="none" stroke="#FFFFFF" stroke-width="6" stroke-miterlimit="10" x1="16.6" y1="69.1" x2="67.5" y2="64.4"/>
</g>
</g>
</g>
<g>
<path fill="#0891D1" d="M163.8,50.2c-0.6-0.7-6.3-4.1-11.4-4.1c-3.4,0-5.2,1.2-5.2,3.5c0,2.9,3.2,3.7,8.9,5.2
c8.2,2.2,13.3,5,13.3,12.9c0,9.7-7.8,13-16,13c-6.2,0-13.1-2-18.2-5.3l4.3-8.6c0.8,0.8,7.5,5,14,5c3.5,0,5.2-1.1,5.2-3.2
c0-3.2-4.4-4-10.3-5.8c-7.9-2.4-11.5-5.3-11.5-11.8c0-9,7.2-13.9,15.7-13.9c6.1,0,11.6,2.5,15.4,4.7L163.8,50.2z"/>
<path fill="#0891D1" d="M175,85.1c1.7,0.5,3.3,0.8,4.4,0.8c2,0,3.3-1.5,4.2-5.5l-11.9-31.5h9.8l7.4,23.3l6.3-23.3h8.9l-12.1,36.6
c-1.7,5.3-6.2,8.7-11.8,8.8c-1.7,0-3.5-0.2-5.3-0.9V85.1z"/>
<path fill="#0891D1" d="M239.3,80.3h-9.6V62.6c0-4.1-1.7-5.9-4.3-5.9c-2.6,0-5.8,2.3-7,5.6v18.1h-9.6V48.8h8.6v5.3
c2.3-3.7,6.8-5.9,12.2-5.9c8.2,0,9.5,6.7,9.5,11.9V80.3z"/>
<path fill="#0891D1" d="M261.6,48.2c7.2,0,12.3,3.4,14.8,8.3l-9.4,2.8c-1.2-1.9-3.1-3-5.5-3c-4,0-7,3.2-7,8.2c0,5,3.1,8.3,7,8.3
c2.4,0,4.6-1.3,5.5-3.1l9.4,2.9c-2.3,4.9-7.6,8.3-14.8,8.3c-10.6,0-16.9-7.7-16.9-16.4S250.9,48.2,261.6,48.2z"/>
<path fill="#0891D1" d="M302.1,78.7c-2.6,1.1-6.2,2.3-9.7,2.3c-4.7,0-8.8-2.3-8.8-8.4V56.1h-4v-7.3h4v-10h9.6v10h6.4v7.3h-6.4v13.1
c0,2.1,1.2,2.9,2.8,2.9c1.4,0,3-0.6,4.2-1.1L302.1,78.7z"/>
<path fill="#0891D1" d="M337.2,80.3h-9.6V62.6c0-4.1-1.8-5.9-4.6-5.9c-2.3,0-5.5,2.2-6.7,5.6v18.1h-9.6V36.5h9.6v17.6
c2.3-3.7,6.3-5.9,10.9-5.9c8.5,0,9.9,6.5,9.9,11.9V80.3z"/>
<path fill="#0891D1" d="M343.4,45.2v-8.7h9.6v8.7H343.4z M343.4,80.3V48.8h9.6v31.5H343.4z"/>
<path fill="#0891D1" d="M389.9,80.3h-9.6V62.6c0-4.1-1.7-5.9-4.3-5.9c-2.6,0-5.8,2.3-7,5.6v18.1h-9.6V48.8h8.6v5.3
c2.3-3.7,6.8-5.9,12.2-5.9c8.2,0,9.5,6.7,9.5,11.9V80.3z"/>
<path fill="#0891D1" d="M395.5,64.6c0-9.2,6-16.3,14.6-16.3c4.7,0,8.4,2.2,10.6,5.8v-5.2h8.3v29.3c0,9.6-7.5,15.5-18.2,15.5
c-6.8,0-11.5-2.3-15-6.3l5.1-5.2c2.3,2.6,6,4.3,9.9,4.3c4.6,0,8.6-2.4,8.6-8.3v-3.1c-1.9,3.5-5.9,5.3-10,5.3
C401.1,80.5,395.5,73.3,395.5,64.6z M419.4,68.5v-6.6c-1.3-3.3-4.2-5.5-7.1-5.5c-4.1,0-7,4-7,8.4c0,4.6,3.2,8,7.5,8
C415.7,72.8,418.1,71,419.4,68.5z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View file

@ -1,34 +1,34 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="robots" content="noindex">
<script>
if (document.URL.indexOf("ui") === -1) {
window.location.replace("ui")
}
</script>
<link rel="apple-touch-icon" sizes="180x180" href="/ui/img/icon/apple-touch-icon.png">
<link rel="manifest" href="/ui/site.webmanifest">
<meta name="msapplication-TileColor" content="#da532c">
<meta name="theme-color" content="#ffffff">
<link rel="icon" href="/ui/favicon.svg" type="image/svg+xml">
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/@mdi/font@6.5.95/css/materialdesignicons.min.css">
<title>
CasaOS
</title>
<link href="/ui/js/0.js" rel="prefetch"><link href="/ui/js/1.js" rel="prefetch"><link href="/ui/js/2.js" rel="prefetch"><link href="/ui/js/3.js" rel="prefetch"><link href="/ui/js/4.js" rel="prefetch"><link href="/ui/js/5.js" rel="prefetch"><link href="/ui/js/6.js" rel="prefetch"><link href="/ui/js/7.js" rel="prefetch"><link href="/ui/js/app.js" rel="preload" as="script"><link href="/ui/js/chunk-vendors.js" rel="preload" as="script"></head>
<body>
<noscript>
<strong>We're sorry but CasaOS doesn't work properly without JavaScript enabled.
Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
<script type="text/javascript" src="/ui/js/chunk-vendors.js"></script><script type="text/javascript" src="/ui/js/app.js"></script></body>
</html>
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="robots" content="noindex">
<script>
if (document.URL.indexOf("ui") === -1) {
window.location.replace("ui")
}
</script>
<link rel="apple-touch-icon" sizes="180x180" href="/ui/img/icon/apple-touch-icon.png">
<link rel="manifest" href="/ui/site.webmanifest">
<meta name="msapplication-TileColor" content="#da532c">
<meta name="theme-color" content="#ffffff">
<link rel="icon" href="/ui/favicon.svg" type="image/svg+xml">
<title>
CasaOS
</title>
<link href="/ui/js/0.js" rel="prefetch"><link href="/ui/js/1.js" rel="prefetch"><link href="/ui/js/2.js" rel="prefetch"><link href="/ui/js/3.js" rel="prefetch"><link href="/ui/js/4.js" rel="prefetch"><link href="/ui/js/5.js" rel="prefetch"><link href="/ui/js/6.js" rel="prefetch"><link href="/ui/js/7.js" rel="prefetch"><link href="/ui/js/app.js" rel="preload" as="script"><link href="/ui/js/chunk-vendors.js" rel="preload" as="script"></head>
<body>
<noscript>
<strong>We're sorry but CasaOS doesn't work properly without JavaScript enabled.
Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
<script type="text/javascript" src="/ui/js/chunk-vendors.js"></script><script type="text/javascript" src="/ui/js/app.js"></script></body>
</html>

View file

@ -168,7 +168,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) *
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _Users_liangjianli_go_CasaOSNew_CasaOS_UI_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./node_modules/@babel/runtime/helpers/esm/objectSpread2 */ \"./node_modules/@babel/runtime/helpers/esm/objectSpread2.js\");\n/* harmony import */ var vee_validate_dist_rules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! vee-validate/dist/rules */ \"./node_modules/vee-validate/dist/rules.js\");\n/* harmony import */ var vee_validate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! vee-validate */ \"./node_modules/vee-validate/dist/vee-validate.esm.js\");\n/* harmony import */ var is_valid_hostname__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! is-valid-hostname */ \"./node_modules/is-valid-hostname/index.js\");\n/* harmony import */ var is_valid_hostname__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(is_valid_hostname__WEBPACK_IMPORTED_MODULE_3__);\n/* harmony import */ var uuid_validate__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! uuid-validate */ \"./node_modules/uuid-validate/index.js\");\n/* harmony import */ var uuid_validate__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(uuid_validate__WEBPACK_IMPORTED_MODULE_4__);\n\n\n\n\n\nObject(vee_validate__WEBPACK_IMPORTED_MODULE_2__[\"extend\"])(\"required\", Object(_Users_liangjianli_go_CasaOSNew_CasaOS_UI_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Object(_Users_liangjianli_go_CasaOSNew_CasaOS_UI_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_0__[\"default\"])({}, vee_validate_dist_rules__WEBPACK_IMPORTED_MODULE_1__[\"required\"]), {}, {\n message: \"This field is required\"\n}));\nObject(vee_validate__WEBPACK_IMPORTED_MODULE_2__[\"extend\"])(\"email\", Object(_Users_liangjianli_go_CasaOSNew_CasaOS_UI_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Object(_Users_liangjianli_go_CasaOSNew_CasaOS_UI_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_0__[\"default\"])({}, vee_validate_dist_rules__WEBPACK_IMPORTED_MODULE_1__[\"email\"]), {}, {\n message: \"This field must be a valid email\"\n}));\nObject(vee_validate__WEBPACK_IMPORTED_MODULE_2__[\"extend\"])(\"confirmed\", Object(_Users_liangjianli_go_CasaOSNew_CasaOS_UI_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Object(_Users_liangjianli_go_CasaOSNew_CasaOS_UI_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_0__[\"default\"])({}, vee_validate_dist_rules__WEBPACK_IMPORTED_MODULE_1__[\"confirmed\"]), {}, {\n message: \"This field confirmation does not match\"\n}));\nObject(vee_validate__WEBPACK_IMPORTED_MODULE_2__[\"extend\"])(\"length\", Object(_Users_liangjianli_go_CasaOSNew_CasaOS_UI_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Object(_Users_liangjianli_go_CasaOSNew_CasaOS_UI_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_0__[\"default\"])({}, vee_validate_dist_rules__WEBPACK_IMPORTED_MODULE_1__[\"length\"]), {}, {\n message: \"This field must have 2 options\"\n}));\nObject(vee_validate__WEBPACK_IMPORTED_MODULE_2__[\"extend\"])(\"min\", Object(_Users_liangjianli_go_CasaOSNew_CasaOS_UI_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Object(_Users_liangjianli_go_CasaOSNew_CasaOS_UI_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_0__[\"default\"])({}, vee_validate_dist_rules__WEBPACK_IMPORTED_MODULE_1__[\"min\"]), {}, {\n message: \"This field must have more than {length} characters\"\n}));\nObject(vee_validate__WEBPACK_IMPORTED_MODULE_2__[\"extend\"])('rfc1123', {\n validate: function validate(value) {\n return is_valid_hostname__WEBPACK_IMPORTED_MODULE_3___default()(value);\n },\n message: 'You entered an invalid RFC1123 hostname'\n});\nObject(vee_validate__WEBPACK_IMPORTED_MODULE_2__[\"extend\"])('uuid', {\n validate: function validate(value) {\n return uuid_validate__WEBPACK_IMPORTED_MODULE_4___default()(value);\n },\n message: 'You entered an invalid share ID'\n});\n\n//# sourceURL=webpack:///./src/plugins/vee-validate.js?");
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var E_Company_CasaOS_UI_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./node_modules/@babel/runtime/helpers/esm/objectSpread2 */ \"./node_modules/@babel/runtime/helpers/esm/objectSpread2.js\");\n/* harmony import */ var vee_validate_dist_rules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! vee-validate/dist/rules */ \"./node_modules/vee-validate/dist/rules.js\");\n/* harmony import */ var vee_validate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! vee-validate */ \"./node_modules/vee-validate/dist/vee-validate.esm.js\");\n/* harmony import */ var is_valid_hostname__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! is-valid-hostname */ \"./node_modules/is-valid-hostname/index.js\");\n/* harmony import */ var is_valid_hostname__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(is_valid_hostname__WEBPACK_IMPORTED_MODULE_3__);\n/* harmony import */ var uuid_validate__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! uuid-validate */ \"./node_modules/uuid-validate/index.js\");\n/* harmony import */ var uuid_validate__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(uuid_validate__WEBPACK_IMPORTED_MODULE_4__);\n\n\n\n\n\nObject(vee_validate__WEBPACK_IMPORTED_MODULE_2__[\"extend\"])(\"required\", Object(E_Company_CasaOS_UI_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Object(E_Company_CasaOS_UI_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_0__[\"default\"])({}, vee_validate_dist_rules__WEBPACK_IMPORTED_MODULE_1__[\"required\"]), {}, {\n message: \"This field is required\"\n}));\nObject(vee_validate__WEBPACK_IMPORTED_MODULE_2__[\"extend\"])(\"email\", Object(E_Company_CasaOS_UI_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Object(E_Company_CasaOS_UI_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_0__[\"default\"])({}, vee_validate_dist_rules__WEBPACK_IMPORTED_MODULE_1__[\"email\"]), {}, {\n message: \"This field must be a valid email\"\n}));\nObject(vee_validate__WEBPACK_IMPORTED_MODULE_2__[\"extend\"])(\"confirmed\", Object(E_Company_CasaOS_UI_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Object(E_Company_CasaOS_UI_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_0__[\"default\"])({}, vee_validate_dist_rules__WEBPACK_IMPORTED_MODULE_1__[\"confirmed\"]), {}, {\n message: \"This field confirmation does not match\"\n}));\nObject(vee_validate__WEBPACK_IMPORTED_MODULE_2__[\"extend\"])(\"length\", Object(E_Company_CasaOS_UI_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Object(E_Company_CasaOS_UI_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_0__[\"default\"])({}, vee_validate_dist_rules__WEBPACK_IMPORTED_MODULE_1__[\"length\"]), {}, {\n message: \"This field must have 2 options\"\n}));\nObject(vee_validate__WEBPACK_IMPORTED_MODULE_2__[\"extend\"])(\"min\", Object(E_Company_CasaOS_UI_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Object(E_Company_CasaOS_UI_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_0__[\"default\"])({}, vee_validate_dist_rules__WEBPACK_IMPORTED_MODULE_1__[\"min\"]), {}, {\n message: \"This field must have more than {length} characters\"\n}));\nObject(vee_validate__WEBPACK_IMPORTED_MODULE_2__[\"extend\"])('rfc1123', {\n validate: function validate(value) {\n return is_valid_hostname__WEBPACK_IMPORTED_MODULE_3___default()(value);\n },\n message: 'You entered an invalid RFC1123 hostname'\n});\nObject(vee_validate__WEBPACK_IMPORTED_MODULE_2__[\"extend\"])('uuid', {\n validate: function validate(value) {\n return uuid_validate__WEBPACK_IMPORTED_MODULE_4___default()(value);\n },\n message: 'You entered an invalid share ID'\n});\n\n//# sourceURL=webpack:///./src/plugins/vee-validate.js?");
/***/ })

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,2 +1,2 @@
User-agent: *
User-agent: *
Disallow: /

View file

@ -1,14 +1,14 @@
{
"name": "",
"short_name": "",
"icons": [
{
"src": "/ui/img/icon/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}
{
"name": "",
"short_name": "",
"icons": [
{
"src": "/ui/img/icon/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}

View file

@ -1,6 +1,6 @@
package web
import "embed"
//go:embed index.html favicon.svg img js browserconfig.xml site.webmanifest robots.txt
var Static embed.FS
package web
import "embed"
//go:embed index.html favicon.svg browserconfig.xml site.webmanifest robots.txt img js fonts
var Static embed.FS