Compare commits

..
212 Commits
Author SHA1 Message Date
ssongliu f4587d0ae5 fix: Fix panic issue caused by 1pctl command (#11453)
Refs  #11449
2025-12-25 04:17:02 +00:00
CityFun 41bae1e636 perf: improve display of IP certificate editing interface (#11451)
Refs https://github.com/1Panel-dev/1Panel/issues/11449
2025-12-25 03:17:02 +00:00
CityFun 5295fd08d4 feat: update Let's Encrypt to official endpoint (#11444) 2025-12-24 11:24:17 +08:00
CityFun 36fdf05d2b feat: support apply ip Certificate (#11437) 2025-12-23 13:35:38 +08:00
ssongliu 5794dbc0a6 Merge pull request #11435 from 1Panel-dev/pr@dev-v2@fix_compose_log
fix: Fix compose log download failure issue
2025-12-23 13:35:14 +08:00
ssongliu c10474db69 fix: Fix typo (#11436) 2025-12-23 05:35:06 +00:00
wanghe d68309fd44 Merge pull request #11432 from HynoR/fix/firewall
feat: Add UDP port 443 accept rule to Pre-firewall config
2025-12-22 19:14:21 +08:00
CityFun 1c634756a0 fix: resolve issue with quick navigation failure in applications (#11434) 2025-12-22 18:50:22 +08:00
CityFun ae536550c3 style: adjust node card display (#11431) 2025-12-22 18:49:58 +08:00
ssongliu 63d5d70314 feat: Retain historical settings when toggling watermark (#11429)
Refs #11392
2025-12-22 10:23:39 +00:00
KOMATA 0be73600e5 feat: Implement Punycode utility functions and integrate into domain dispay (#11433)
* feat: Implement Punycode utility functions and integrate into domain display

* chore: Add punycode package and update import path in utility functions
2025-12-22 18:22:29 +08:00
ssongliu c9cc064f51 fix: Fix container orchestration creation failure issue (#11430) 2025-12-22 18:21:51 +08:00
HynoR dc47eb5ae4 feat: Add UDP port 443 acceptance rule to firewall and iptables configuration 2025-12-22 15:55:47 +08:00
ssongliu 955d9db699 style: Modify terminal connection style issues (#11425)
Refs  #11413
2025-12-22 06:33:29 +00:00
CityFun 3c1549e288 fix: resolve limitation of fetching only 100 redirects for websites (#11424) 2025-12-22 14:31:40 +08:00
CityFun 9fd030b6c3 feat: improve scrolling effect of left-side on website configuration page (#11423)
Refs https://github.com/1Panel-dev/1Panel/issues/11412
2025-12-22 06:01:30 +00:00
CityFun 63a10691b3 feat: limit website name length in configuration (#11422) 2025-12-22 05:59:30 +00:00
ssongliu 5674673c5d feat: Support display and operations for multi-YAML compose startup (#11421)
Refs #11405
2025-12-22 05:57:30 +00:00
ssongliu 74f29e3dc3 chore: Adjust image dependency warning messages (#11407) 2025-12-19 10:08:32 +00:00
ssongliu 1dac8593ae feat: Add database transaction monitoring logs (#11406) 2025-12-19 10:06:32 +00:00
ssongliu 71fd123be1 fix: Fix snapshot application checkbox abnormality issue (#11404) 2025-12-19 10:04:33 +00:00
CityFun d4d66cad2e feat: improve prompt messages when synchronizing WAF IP groups (#11402) 2025-12-19 07:41:02 +00:00
CityFun c78a6ec047 style: change table tooltip style (#11399) 2025-12-19 15:33:57 +08:00
CityFun 1ae3198b10 fix: clear stream configuration when closing website creation page (#11400) 2025-12-19 07:27:03 +00:00
CityFun a6c172b1ab feat: remove Cache-Control: no-cache for create proxy website (#11398) 2025-12-19 07:25:03 +00:00
CityFun 9e9d68f841 style: adjust status column styling in websites list page (#11397) 2025-12-19 07:23:02 +00:00
CityFun 123e6b90a3 feat: update MySQL parameter input validation rules (#11396) 2025-12-19 05:42:11 +00:00
CityFun 00b4a6caf5 feat: update MySQL parameter input validation rules (#11395)
https://github.com/1Panel-dev/1Panel/issues/11394
2025-12-19 03:41:27 +00:00
ssongliu 8642e20d19 perf: Optimize container task log output (#11393) 2025-12-19 03:39:40 +00:00
ssongliu 2b81f7ef7a feat: Output last incomplete line in shell cronjob (#11391) 2025-12-18 21:58:24 +08:00
ssongliu c08e83e5d4 fix: Fix firewalld range port forwarding failure issue (#11390) 2025-12-18 09:21:13 +00:00
CityFun 863ee5c172 feat: Adjust tooltip positions on some pages (#11389) 2025-12-18 09:01:14 +00:00
ssongliu a91f372fbd chore: Revert container command input style (#11387)
revert for #10724

https://bbs.fit2cloud.com/t/topic/16496
2025-12-18 08:59:14 +00:00
ssongliu 825acd5b46 feat: Optimize snapshot backup items (#11385) 2025-12-18 07:43:13 +00:00
CityFun 70e64e648f feat: Adjust prompt for creating stream website (#11384) 2025-12-18 07:41:13 +00:00
ssongliu 7855e89ca9 chore: Modify orchestration environment variable style (#11381) 2025-12-17 14:20:12 +00:00
CityFun 75273abda7 fix: Resolve issue where website names are not displayed in logs for stream-type websites (#11379) 2025-12-17 08:50:11 +00:00
ssongliu 02b278ecff feat: Add version printing after rollback (#11378)
Refs #11265
2025-12-17 08:48:10 +00:00
CityFun e43d7b7a59 fix: Fix issue where backing up stream websites fails (#11377) 2025-12-17 16:46:51 +08:00
CityFun 61f321a04d fix: Fix issue where disabling logs for stream websites fails (#11376) 2025-12-17 16:46:41 +08:00
CityFun 7885b00213 feat: Optimize display issues after creating TCP/UDP proxies (#11375) 2025-12-17 08:46:11 +00:00
CityFun 2ad882b7c2 fix: Fixed issue where deleting symbolic link files fails (#11373)
Refs https://github.com/1Panel-dev/1Panel/issues/11324
2025-12-17 08:44:10 +00:00
ssongliu 2546809013 chore: Adjust task log output format (#11372) 2025-12-17 06:24:09 +00:00
CityFun 4fcf3e11cd fix: Resolve issue where bulk certificate setup creates duplicate server certificates (#11371) 2025-12-17 05:52:10 +00:00
CityFun 1f6cd6f4e9 feat: increase timeout duration for some API interfaces (#11370)
Refs https://github.com/1Panel-dev/1Panel/issues/11348
2025-12-17 05:50:09 +00:00
ssongliu 5c1ce232db chore: Modify i18n initialization method (#11368) 2025-12-17 13:46:32 +08:00
CityFun 172e9e212b feat: Adjust the display position of PHP runtime environment prompts (#11366) 2025-12-17 05:46:09 +00:00
CityFun 279a16c256 feat: Optimize HTTP to HTTPS redirection rules for non-443 port websites (#11364)
Refs https://github.com/1Panel-dev/1Panel/issues/11360
2025-12-17 05:44:09 +00:00
ssongliu e772cdff3c fix: Fix task log printing garbled characters issue (#11363) 2025-12-17 13:42:23 +08:00
CityFun 8881827f9d feat: Optimize the structure of installed apps in the App Store (#11361) 2025-12-17 13:42:13 +08:00
ssongliu d9579901fe fix: Fix incomplete log printing for compose save errors (#11357) 2025-12-16 08:41:58 +00:00
蘭 682d78698f ref: Optimize the button layout on the file upload page (#11354) 2025-12-16 08:39:58 +00:00
CityFun 2cc8567c2f feat: relocate PHP extension installation prompts (#11352)
https://github.com/1Panel-dev/1Panel/issues/11293
2025-12-16 08:38:00 +00:00
ssongliu 184a214bff feat: Add hint messages for compose environment variables (#11351) 2025-12-16 08:35:59 +00:00
CityFun c6376541ed feat: add OpenResty version restriction for TCP/UDP proxy (#11350) 2025-12-16 08:33:59 +00:00
ssongliu 6aca579854 fix: Fix ufw IPv6 rule configuration issue (#11349)
Refs #11211
2025-12-16 08:31:59 +00:00
CityFun 15bc6cf884 feat: add prompt for TCP/UDP proxy (#11343) 2025-12-15 18:56:07 +08:00
ssongliu 895dde74dd fix: Fix local application backup failure in snapshots (#11341) 2025-12-15 18:42:05 +08:00
CityFun c477d20a05 feat: support batch SSL configuration for websites (#11339)
Refs https://github.com/1Panel-dev/1Panel/issues/8250
2025-12-15 10:41:38 +00:00
ssongliu 2fdb1700e5 feat: Database restore supports timeout settings (#11338) 2025-12-15 18:40:37 +08:00
KOMATA 11764e1ba5 feat: improve website create function and interface (#11331)
* feat: improve website create function and interface

* feat: update website create tag

* feat: update i18n files

* feat: update valid domain regex

* feat: reorder website types to improve structure

* feat: update domain labels for consistency in the domain creation form

* feat: update i18n
2025-12-15 16:01:21 +08:00
ssongliu 3212e23f43 feat: Terminal connection supports reset (#11336) 2025-12-15 07:19:21 +00:00
CityFun 84a58f2bb1 fix: resolve issue where crossVersionUpdate setting not taking effect (#11333)
Refs https://github.com/1Panel-dev/1Panel/issues/11328
2025-12-15 07:17:21 +00:00
KOMATA 191396c78a fix: Validate StreamPorts in StreamConfig to prevent empty values (#11332) 2025-12-15 10:11:09 +08:00
CityFun 956a64c692 feat: Support managing application upgrades for all nodes on a single page (#11325) 2025-12-12 10:49:10 +00:00
ssongliu 6f9a8d5afe fix: Fix version upgrade failure in some scenarios 2025-12-12 18:22:49 +08:00
ssongliu 9424d061fa fix: Fix version upgrade failure in some scenarios (#11322) 2025-12-12 18:13:35 +08:00
CityFun 3db8e47679 feat: optimize prompt for syncing WAF IP groups (#11317) 2025-12-12 08:11:11 +00:00
CityFun 5602741550 feat: improve container deletion prompt (#11316) 2025-12-12 08:09:11 +00:00
CityFun 1ef3f540c2 feat: Task center supports switching nodes (#11315) 2025-12-12 08:07:12 +00:00
ssongliu 51846895d5 fix: Fix lock issue during monitoring input insertion (#11312) 2025-12-12 08:05:11 +00:00
ssongliu 551542c99c perf: Optimize GPU monitoring style (#11303) 2025-12-11 14:11:05 +00:00
CityFun ab13ddec48 feat: The website supports TCP/UDP proxying. (#11302)
Refs https://github.com/1Panel-dev/1Panel/issues/5135
Refs https://github.com/1Panel-dev/1Panel/issues/6976
2025-12-11 10:33:03 +00:00
CityFun 2d1792c6de fix: Fix the issue where the website app deployment fails (#11301) 2025-12-11 09:03:03 +00:00
KOMATA 3aedb7e82d chore: Add trusted dependencies for vue-office packages to support bun toolkit (#11291) 2025-12-11 17:00:34 +08:00
ssongliu a9c9f0272a fix: Fix cronjob manual stop not working (#11300) 2025-12-11 17:00:22 +08:00
Kadxy 8606427e8c feat(frontend): add autocomplete attributes for MFA input (#11297) 2025-12-11 16:59:58 +08:00
ssongliu 3d3d22dc06 fix: Fix local PostgreSQL database backup failure (#11295) 2025-12-10 19:02:24 +08:00
ssongliu 23a3a5cf74 Fix typo (#11287) 2025-12-10 14:31:19 +08:00
ssongliu 0f5410d225 fix: Fix focus loss issue in file selection component (#11281)
Refs #11272
2025-12-09 10:38:56 +00:00
ssongliu 1b7be836ef fix: Fix container orchestration format validation issues (#11280)
Refs #11227
2025-12-09 10:36:57 +00:00
CityFun 66e7223db9 feat: Support quick installation of PHP lz4 extension (#11279) 2025-12-09 09:42:56 +00:00
ssongliu d801b811d4 style: Optimize container-associated resource styles (#11278)
Refs #11268
2025-12-09 09:40:57 +00:00
ssongliu 307029272e fix: Fix port forwarding initialization status judgment issue (#11276)
Refs #11267
2025-12-09 09:38:57 +00:00
ssongliu 9aa3caab16 feat: Container compose supports .env file operations (#11274) 2025-12-09 09:36:58 +00:00
KOMATA b47a4525dc feat: Enhance browser cache management and localization support (#11277)
* feat: Enhance browser cache management and localization support

- Updated browser cache handling in WebsiteService to include a 'noModify' option.
- Introduced new method AddBroswerNoCache in Location to manage no-cache directives.
- Added localization for 'noModify' in multiple languages.
- Updated frontend to support the new browser cache options in the proxy configuration.

* fix: Update cache display logic in proxy configuration

- Adjusted the rendering of cache tags in the proxy configuration to conditionally display based on cache time values.
- Improved user interface by adding margin classes for better spacing between cache tags.
2025-12-09 17:32:23 +08:00
KOMATA 38ab0d9ca0 feat: Implement process management and network data handling (#11270)
- Introduced ProcessStore for managing process and network data via WebSocket.
- Enhanced TableSearch component to synchronize search parameters with props.
- Updated network and process views to utilize ProcessStore for data fetching and state management.
- Improved data filtering and sorting in network and process views.
- Added WebSocket connection management with polling for real-time updates.
2025-12-09 17:31:23 +08:00
KOMATA 38985671c6 perf: improve getNetConnections function and Websocket handling (#11269)
* feat: Enhance WebSocket client functionality and improve data processing

- Reduced message queue size in WebSocket client from 100 to 32.
- Introduced atomic boolean to track client closure state.
- Added SendPayload method to handle message sending with queue management.
- Updated ProcessData function to utilize SendPayload for sending responses.
- Expanded netTypes to include both IPv4 and IPv6 protocols in network connection retrieval.
- Improved net connection processing by using a map for process names, enhancing efficiency.

* feat: Enhance WebSocket client and process data handling

- Added synchronization with sync.Once for safe closure of WebSocket client.
- Updated message queue size to a constant for better maintainability.
- Implemented context timeouts for process data retrieval to prevent blocking.
- Improved network connection handling by utilizing a more efficient method for retrieving connections.
- Introduced a new function to determine connection types based on protocol family.

* feat: Enhance network connection retrieval and process name mapping

- Updated getNetConnections function to improve efficiency by using maps for process names and connections.
- Introduced a new helper function to retrieve process names from the filesystem or process context.
- Enhanced filtering logic for network connections based on process ID, name, and port.
- Increased initial capacity for connection results to optimize performance.

* refactor: Rename SendPayload method to Send in WebSocket client

- Updated the SendPayload method to be more succinctly named Send for clarity.
- Ensured the method continues to handle message sending while maintaining existing functionality.

* refactor: Update ProcessData and getNetConnections for improved clarity and efficiency

- Replaced SendPayload method calls with Send for consistency in WebSocket message handling.
- Enhanced getNetConnections function by refining process name retrieval and filtering logic.
- Improved error handling in getProcessNameWithContext for better robustness.

* refactor: Simplify WebSocket client closure and reading logic

- Removed unnecessary synchronization for closing the WebSocket client.
- Updated the Read method to handle message reading directly without a separate Close method.
- Ensured the Socket is closed properly after reading messages to prevent resource leaks.
2025-12-09 17:30:12 +08:00
ssongliu d1c2a69820 pref: Optimize backup account code structure (#11261) 2025-12-08 18:22:59 +08:00
KOMATA 3a1b9fedd1 perf: Simplify process data handling and improve performance (#11236)
* refactor: Simplify process data handling and improve performance

- Replaced goroutine-based processing with a direct loop for handling process data.
- Introduced context support for process and connection retrieval.
- Enhanced error handling and data structuring for process information.
- Improved SSH session retrieval by mapping users by host for better efficiency.

* chore: go fmt
2025-12-08 17:34:52 +08:00
蘭 8119ba75fc ref: Adjust Swagger format (#11207)
* ref: Adjust Swagger format

* ref: Adjust Swagger format
2025-12-08 17:34:24 +08:00
ssongliu be4c22012e fix: Fix HTTP/HTTPS docker proxy issues (#11258)
Refs #11254
2025-12-08 09:20:38 +00:00
ssongliu 60fef48cf0 chore: Remove system upgrade temporary files (#11255)
Refs #11241
2025-12-08 09:18:37 +00:00
KOMATA bb72ea0fc0 fix: Improve SSH session retrieval by grouping users by host in process_data.go (#11210) 2025-12-08 16:39:08 +08:00
KOMATA af48fed3c2 feat: Add file preview functionality and interface (#11200)
* feat: Add file content preview functionality

- Implemented a new API endpoint for previewing file content.
- Added PreviewContent method in BaseApi to handle requests.
- Introduced GetPreviewContent method in FileService to retrieve file previews.
- Updated frontend to include a TextPreview component for displaying file previews.
- Added localization support for preview-related messages in multiple languages.
- Enhanced file management view to support previewing large files.

* feat: Update file preview functionality and interface

- Added PreviewContentReq interface to define request parameters for file preview.
- Updated getPreviewContent function to use the new PreviewContentReq type.
- Modified text-preview component to align with updated API, removing unnecessary parameters.
2025-12-08 16:37:50 +08:00
endymx ce3fa94708 feat(iptables): range forward (#11188) 2025-12-08 16:35:01 +08:00
ssongliu 7f9f4ae740 fix: Fix PostgreSQL backup failure with $ character (#11252) 2025-12-08 15:24:08 +08:00
CityFun 8bec6f2b28 fix: Fixed network search returning empty results with no page update (#11251) 2025-12-08 07:10:37 +00:00
CityFun b397363c9d fix: Fixed process search returning empty results with no page update (#11246) 2025-12-08 05:54:36 +00:00
KOMATA acb800b505 fix: Update compose operation handling to ensure correct path usage and detail loading after operations (#11245) 2025-12-08 13:41:55 +08:00
CityFun ec37af2f18 fix: Fixed missing redirect after installing runtime environment (#11243) 2025-12-08 04:44:36 +00:00
CityFun d2b030e698 feat: Added support for auto-filling remote MySQL root password in App Store (#11240) 2025-12-08 11:39:08 +08:00
ssongliu c6f496f2a5 fix: Fix the issue of container compose log cleanup failure (#11238) 2025-12-08 11:38:05 +08:00
CityFun 722e1929f1 fix: Fixed file comparison highlighting loss issue (#11237) 2025-12-08 03:28:35 +00:00
王贺 f9a20ec443 Fix typo (#11229) 2025-12-07 11:36:20 +08:00
ssongliu f5568d4874 fix: Fix style issues caused by element upgrade (#11224) 2025-12-06 14:16:34 +00:00
ssongliu 87bbe4ee12 fix: Fix the issue of abnormal compose directory redirection (#11223) 2025-12-06 14:14:28 +00:00
ssongliu a034b03c1d fix: Task framework adds timeout exit parameter (#11206) 2025-12-05 18:42:23 +08:00
CityFun 470f4e4db7 style: Updated layout page save button styling (#11198) 2025-12-05 03:24:23 +00:00
CityFun 9428efd8c0 fix: Fixed CAPTCHA loading issue in certain scenarios (#11186) 2025-12-04 10:12:22 +00:00
CityFun bf4d91b68b fix: Fixed login CAPTCHA bypass vulnerability (#11185) 2025-12-04 10:10:23 +00:00
KOMATA 781155b029 perf: remove useless stat checking logic (#11178)
* feat: Enhance proxy initialization and error handling

* Add a timeout to the dialer for Unix socket connections
* Improve error response by including the error message in the "Bad Gateway" response

* refactor: Change sockPath variable to constant in proxy initialization

* Update sockPath to a constant SockPath for improved clarity and consistency
* Ensure the new constant is used in the dialer function for Unix socket connections
2025-12-04 18:07:47 +08:00
KOMATA 3caf0ac529 fix: Ensure proper client closure in volume and device clean operations (#11177) 2025-12-04 18:07:16 +08:00
CityFun 7d25228e55 perf: Optimized tensorrtLLM display performanceperf (#11184) 2025-12-04 17:57:19 +08:00
ssongliu 4fb099912e fix: Fix the issue of remote database connection failure (#11181)
Refs #11173
2025-12-04 09:56:23 +00:00
ssongliu b3350b54b3 fix: Add iptables execution timeout (#11176) 2025-12-04 11:22:25 +08:00
CityFun cd4e0374d0 refactor: Refactored agent core.db loading logic (#11175) 2025-12-04 11:15:40 +08:00
KOMATA 5d2084fda4 feat: improve linux distribution information get logic (#11171)
* feat: Add PrettyDistro field to dashboard information and update related services and frontend components

* fix: Trim whitespace and parentheses from detected Linux distribution name in GetDistro method

* fix: Correctly trim parentheses from detected Linux distribution name in GetDistro method

* refactor: Simplify Linux distribution detection by removing unnecessary checks and consolidating logic
2025-12-03 14:14:35 +08:00
ssongliu 8c32fa3e6c fix: Fix the issue of abnormal clam scan status judgment (#11172) 2025-12-03 14:13:22 +08:00
KOMATA fef13211a0 feat: Enhance CPU metrics by adding CPU detailed information (#11169)
* fix: Update CPU usage retrieval to include detailed percentage information

* feat: Enhance CPU metrics by adding CPU frequency and detailed usage percentages

* feat: Add CPU frequency metric to dashboard base information
2025-12-03 13:37:57 +08:00
CityFun 5ff5b0a5ad feat: revised some of the translations (#11168) 2025-12-03 03:58:14 +00:00
CityFun 8ac9d022b6 fix: fix missing version issue during mssql installation (#11167) 2025-12-03 03:56:15 +00:00
CityFun 2c02dc07e9 fix: resolve i18n file loading failure issue (#11164) 2025-12-03 10:08:20 +08:00
ssongliu ba999c516a chore: Modify the default runtime value on the overview page (#11159) 2025-12-02 10:21:42 +00:00
AirboZH 28303ffa82 feat: Add support for AWS Route 53 DNS provider (#11158) 2025-12-02 18:20:35 +08:00
ssongliu 1f9a3aa51a feat: Add refresh after resource release in container overview (#11157) 2025-12-02 09:29:41 +00:00
ssongliu 656dda234c perf: Optimize GPU monitoring display (#11156) 2025-12-02 09:21:41 +00:00
KOMATA 63b62f4074 fix: Enhance error handling in file saving process (#11152)
* fix: Enhance error handling in file saving process

* f

* f
2025-12-02 17:19:22 +08:00
KOMATA 158180a052 fix: Enhance error handling for additional HTTP status codes in RequestHttp class (#11151) 2025-12-02 17:11:55 +08:00
ssongliu dbe479e920 fix: Fix agent packaging failure issue (#11150) 2025-12-02 02:57:40 +00:00
蘭 7aefa34871 fix: Fix the issue of software link creation errors (#11149) 2025-12-02 02:53:41 +00:00
ssongliu 7d92436b92 perf: Change container overview page disk usage to asynchronous requests (#11148) 2025-12-02 02:29:39 +00:00
AirboZH 66a070c239 feat: Add support for AliESA DNS provider (#11147) 2025-12-02 10:11:46 +08:00
ssongliu bb9d9042f0 chore: Modify iptables initialization logic (#11145) 2025-12-01 22:20:17 +08:00
ssongliu a5d86d4c00 fix: Fix the issue where container details cannot be fully selected and copied (#11144) 2025-12-01 22:06:59 +08:00
KOMATA e867deb547 feat: Add copy function for raw JSON in container and network detail views (#11141)
* feat: Add copy button for raw JSON in container and network detail views

* refactor: Change CodemirrorPro component from disabled to readonly in container and network detail views

* feat: Add readonly prop to CodemirrorPro component for enhanced editing control
2025-12-01 18:13:52 +08:00
ssongliu af15659d99 fix: Fix display issues caused by default table height (#11143) 2025-12-01 10:05:37 +00:00
ssongliu f63658db9e fix: Fix the issue of extremely slow container list retrieval (#11142)
Refs #11129
2025-12-01 10:03:37 +00:00
ssongliu d4edf2f874 fix: Fix the issue of abnormal time filtering in GPU monitoring (#11139) 2025-12-01 08:43:38 +00:00
ssongliu 94f7d78cc9 fix: Fix iptables state persistence issue (#11137)
Refs #11126
2025-12-01 06:33:38 +00:00
ssongliu 5cede39e5c fix: Fix log display issue when backup account is unavailable during … (#11135)
Refs #11123
2025-12-01 02:15:37 +00:00
KOMATA 508ac2ae84 fix: Improve error handling in file download API (#11127) 2025-12-01 09:40:48 +08:00
KOMATA 997b820a42 perf: use gzipped data for improved load speed in large response (#11128)
* perf: use gzipped data for improved load speed in big files

* perf: switch to gzipped response for GetFileTree to enhance performance

* perf: switch to gzipped response for SearchApp to improve performance

* perf: update file size conditions for gzipped response to enhance performance
2025-12-01 09:40:32 +08:00
KOMATA 5de83985e3 feat: Add path protection mechanism to prevent deletion of system dir (#11130)
* feat: Add path protection mechanism to prevent deletion of critical system directories

* feat: Enhance recycle bin service with path protection for deletion requests
2025-12-01 09:40:00 +08:00
ssongliu c0220dbed0 fix: Disable caching for recommended apps on the overview page (#11117) 2025-11-28 10:19:27 +00:00
ssongliu 2e9305b9b1 fix: Fix some overview page caching issues (#11114) 2025-11-28 09:05:29 +00:00
王贺 b18bdea1c9 chore: limit gitee sync workflow to main repo (#11112) 2025-11-28 16:47:37 +08:00
ssongliu 25fb431163 fix: Fix the upgrade service file location issue (#11109) 2025-11-28 11:07:49 +08:00
CityFun ac43f00273 perf: optimize login API logic (#11104) 2025-11-28 03:05:27 +00:00
ssongliu 8ccf1fcbf3 fix: Fix the issue of abnormal runtime refresh on the overview page (#11106) 2025-11-27 10:37:24 +00:00
ssongliu b2803f574b chore: Remove some unused code (#11105) 2025-11-27 17:59:48 +08:00
ssongliu bbc14bb723 feat: Add GPU monitoring settings redirection (#11103) 2025-11-27 09:57:25 +00:00
KOMATA e89311d5d4 feat: Implement dashboard caching and refresh functionality (#11100)
* feat: Implement dashboard caching and refresh functionality

* feat: Enhance dashboard and launcher with hover refresh functionality and improve caching strategy

* feat: Introduce dashboard caching utility functions for improved data management
2025-11-27 17:54:54 +08:00
CityFun b5e56c6b65 feat: optimize IP whitelist validation logic (#11102) 2025-11-27 16:29:31 +08:00
ssongliu 3d2023858c feat: Compatible with XPU monitoring (#11099) 2025-11-27 05:57:24 +00:00
KOMATA 74b48a18be fix: Improve app list loading by switching to JSON decoder from file stream (#11095) 2025-11-27 13:47:03 +08:00
CityFun 42f61f60fb fix: Fixed issue with edit runtime port failed (#11096)
Refs https://github.com/1Panel-dev/1Panel/issues/11091
2025-11-27 03:51:24 +00:00
KOMATA e32a11910e fix: adjust WebSocket buffer sizes in terminal and host API (#11094) 2025-11-27 11:22:33 +08:00
CityFun dc9fdf897b feat: add more fields to process detail view (#11093) 2025-11-27 11:21:44 +08:00
CityFun 35c946a064 feat: support creating databases with custom charset and collation during app installation (#11092) 2025-11-27 03:21:23 +00:00
KOMATAand王贺 747a0c45ee feat: async load data of top CPU and memory processes data (#11081)
* feat: async load data of top CPU and memory processes data

* refactor: Update CPU and memory top toggle functionality and state management

---------

Co-authored-by: 王贺 <wanghe@fit2cloud.com>
2025-11-26 23:02:09 +08:00
ssongliu 8e03b24b0c feat: Compatible with XPU monitoring (#11088) 2025-11-26 22:30:15 +08:00
ssongliu 18c65c3096 fix: Fix the issue of snapshots not automatically restarting after restoration (#11087) 2025-11-26 22:29:59 +08:00
ssongliu 3b306f990a chore: Optimize iptables initial state loading speed (#11085) 2025-11-26 22:29:48 +08:00
CityFun 92fa6552b1 style: change file management terminal style (#11084) 2025-11-26 10:35:21 +00:00
ssongliu a39dc33770 fix: Handle empty character set in MariaDB compatibility (#11082) 2025-11-26 09:21:21 +00:00
KOMATA 0a42d4942c perf: replace gopsutil sampling with cached metrics and optimize system info retrieval (#11079)
* refactor: change psutil method
2025-11-26 16:33:47 +08:00
ssongliu 8086d5cecb fix: Fix uppercase exclusion in container compose (#11080) 2025-11-26 07:53:24 +00:00
ssongliu a6875614f6 style: Optimize container compose page style (#11076) 2025-11-26 04:39:20 +00:00
CityFun 6b061fa327 perf: change db connect params (#11075) 2025-11-26 11:11:11 +08:00
KOMATA b416e6b6f1 refactor: Optimize log file reading logic (#11064)
* refactor: Optimize log file reading with buffered reader pool and improve pagination logic

* refactor: Enhance file reading logic with improved buffer management and constant usage

* refactor: Adjust maximum read file size and enhance line reading functionality
2025-11-25 22:42:29 +08:00
ssongliu ac1f49f8dd fix: Fix the issue of abnormal image list retrieval in snapshots (#11074)
Refs #11030
2025-11-25 14:25:18 +00:00
ssongliu 6ddee2649d feat: Add default values for curl scheduled tasks (#11073) 2025-11-25 14:17:18 +00:00
ssongliu fd14f53cb9 style: Adjust container network details style (#11072) 2025-11-25 10:51:18 +00:00
CityFun 029e2210f5 perf: change db connect params (#11071) 2025-11-25 18:48:47 +08:00
ssongliu 69769e6203 fix: Fix the issue of database connections not being released (#11070) 2025-11-25 10:07:19 +00:00
ssongliu 2a21f194d3 feat: Optimize orchestration creation logs (#11069)
Refs  #11068
2025-11-25 09:21:18 +00:00
ssongliu e46d8c5812 feat: Support MySQL data synchronization collation (#11067) 2025-11-25 15:02:37 +08:00
CityFun 938605a169 fix: Resolve application list display error caused by docker-compose.yml modifications (#11065) 2025-11-25 06:27:18 +00:00
ssongliu c3cc26a136 fix: Fix the issue of abnormal iptables persistence loading (#11066) 2025-11-25 14:21:24 +08:00
KOMATA dda83b6307 feat: Implement Gzip compression for successful log API responses (#11063)
* feat: Implement Gzip compression for successful API responses

* refactor: Optimize Gzip compression handling in API responses using sync.Pool
2025-11-25 13:39:08 +08:00
CityFun 48e2e01a57 feat: Add MySQL character set and collation configuration during application installation (#11062) 2025-11-25 03:51:17 +00:00
ssongliu d6b00967ca fix: Fix the issue of database backup failure (#11060) 2025-11-25 03:19:18 +00:00
ssongliu 6d4c1f8e79 fix: Fix the issue of abnormal container list sorting (#11059) 2025-11-25 11:17:34 +08:00
ssongliu e88fe0ac2f fix: Fix the issue of database creation failure (#11057) 2025-11-24 14:35:16 +00:00
ssongliu 496c0b50b4 fix: Fix the issue of abnormal iptables rule persistence (#11056)
Refs #11027
2025-11-24 14:33:15 +00:00
wanghe-fit2cloud 9d73d74c63 chore: remove UPX compression 2025-11-24 18:27:54 +08:00
CityFun 7e4e12107a perf: change db connect params (#11055) 2025-11-24 10:03:15 +00:00
CityFun 5b15de91e6 fix: Fixed issue with build failed (#11054) 2025-11-24 16:29:48 +08:00
KOMATA 98b3dc9d7d refactor: update Swagger serving and processing method (#11052)
* feat: Add Swagger UI support and update Go module dependencies
2025-11-24 16:14:06 +08:00
ssongliu c05e610b13 feat: Support more MySQL character sets and collations (#11053) 2025-11-24 08:07:14 +00:00
ssongliu 63f9368e26 feat: GPU monitoring data supports persistence (#11051)
Refs #9496
2025-11-24 07:59:15 +00:00
ssongliu 3f47a6e701 feat: Support multiple URL access for cronjob (#11050)
Refs #10279
2025-11-24 07:57:18 +00:00
ssongliu d7c9b3b192 feat: Support favorite operations for containers and images (#11049)
Refs #10035 #3372
2025-11-24 07:55:14 +00:00
CityFun 063ed23acb perf: Optimize disk management formatting process (#11047)
https://github.com/1Panel-dev/1Panel/issues/11031
2025-11-24 07:51:15 +00:00
ssongliu 7c33dd9026 feat: PG remote databases version 18.x support backup and restore (#11048)
Refs #10917
2025-11-24 07:47:14 +00:00
CityFun a853d8c869 perf: change base64Captcha (#11046) 2025-11-24 14:47:58 +08:00
KOMATA 4ddeb81972 refactor: Update database connection settings to limit concurrency and idle connections (#11045) 2025-11-24 14:04:54 +08:00
CityFun 4dd9f00637 perf: Increase supervisor API timeout duration (#11043)
Refs https://github.com/1Panel-dev/1Panel/issues/11041
2025-11-24 03:59:14 +00:00
ssongliu 49f3779fd2 fix: Fix the issue of abnormal WebDAV connection (#11038) 2025-11-24 11:36:58 +08:00
KOMATAandssongliu 7389aad122 feat: Add ComposeDetail view for enhanced container management (#10981)
* feat: Add ComposeDetail view for enhanced container management

* feat: Update container log management with new table layout and improved stats display

* feat: Update log search mode to 'all' for comprehensive log retrieval

* cleanup

* feat: Enhance log container and compose management with new UI components and improved functionality

* refactor: Remove unused ComposeDetail route and enhance UI components in container management

* refactor: Improve UI styling and interaction for compose list items in container management

* refactor: Update styling and hover effects for compose list items in container management

* refactor: Enhance container compose header with additional context and improve port handling in inspect view

* refactor: Update .env label to use translation for improved localization in container compose view

* refactor: Adjust height and placeholder text for YAML editor in container compose view

* refactor: Adjust docker compose layout

---------

Co-authored-by: ssongliu <songlius11@163.com>
2025-11-21 18:20:19 +08:00
KOMATA bf095c677c feat: Migrate AWS SDK to v2 (#10983)
* refactor: Migrate S3 client to AWS SDK v2

* refactor: Migrate S3 client to AWS SDK v2 and update dependencies

* refactor: Simplify endpoint handling in S3 client initialization

* refactor: Update S3 client method receivers to pointer types for consistency
2025-11-21 16:18:38 +08:00
ssongliu fb929687d8 fix: Fix the issue of abnormal sshd service auto-start status (#11022)
Refs #11017
2025-11-21 08:13:06 +00:00
CityFun bddcf76fc3 fix: Fixed issue with uninstall extension failed (#11021) 2025-11-21 07:59:08 +00:00
KOMATA 9370bdb5ac fix: Update Cache-Control headers for static resources in router (#11020)
* fix: Update Cache-Control headers for static resources in router

* chore: Remove Cache-Control header from static resource response in router
2025-11-21 15:47:48 +08:00
KOMATA ef6d8bb17b fix: improve logic and fix bug in i18n module (#11019)
* feat: Implement language caching and improve language handling in i18n module

* refactor: Optimize i18n initialization with sync.Once for thread safety

* fix: Replace logging with fmt.Println for language file loading errors in i18n module

* fix: Correct format string in error logging for language file loading in i18n module

* fix: Update language files and improve error handling in i18n module

* fix: Update Malay language file extension from .yml to .yaml and add new translations in i18n module

* fix: Improve error messages for language file loading in i18n module

* fix: Ensure cached database language is set correctly during i18n initialization

* fix: Enhance language detection in i18n module by using Accept-Language header
2025-11-21 15:37:01 +08:00
CityFun 99c2eb04c9 feat: Add memory requirement field display for local applications (#11018)
Refs https://github.com/1Panel-dev/1Panel/issues/11015
2025-11-21 04:05:05 +00:00
CityFun d83b673c90 perf: significantly improve app store homepage loading speed (#11013) 2025-11-20 18:57:49 +08:00
ssongliu 7498f3f66b feat: Container networks support multi-NIC operations (#11011)
Refs #11007
2025-11-20 08:35:03 +00:00
ssongliu 147eda529c fix: Fix the issue with PostgreSQL configuration file abnormalities (#11010)
Refs #11004
2025-11-20 08:33:02 +00:00
CityFun c1f0da4248 refactor: remove debug cmd for agent and core (#11009) 2025-11-20 08:31:03 +00:00
KOMATA 8656dada06 fix: correct suitable log queue size to improve performance (#11008)
* fix: Update log queue size to improve performance and ensure proper handling of log entries

* fix: Adjust log queue size to maintain consistency with queue size limit
2025-11-20 16:28:03 +08:00
390 changed files with 16097 additions and 9749 deletions
+1
View File
@@ -6,6 +6,7 @@ permissions:
jobs:
generic_handler:
name: Add Labels to PR
if: github.repository == '1Panel-dev/1Panel'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
+1 -1
View File
@@ -21,7 +21,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.23'
go-version: '1.24'
- name: Build Release
uses: goreleaser/goreleaser-action@v6
with:
+1 -1
View File
@@ -21,7 +21,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.23'
go-version: '1.24'
- name: Build Release
uses: goreleaser/goreleaser-action@v6
with:
+1
View File
@@ -8,6 +8,7 @@ on:
jobs:
sonarcloud:
name: SonarCloud
if: github.repository == '1Panel-dev/1Panel'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
+1
View File
@@ -2,6 +2,7 @@ name: Synchronize to Gitee
on: [push]
jobs:
repo-sync:
if: github.repository == '1Panel-dev/1Panel'
runs-on: ubuntu-latest
steps:
- name: Mirror the Github organization repos to Gitee.
-6
View File
@@ -33,9 +33,6 @@ builds:
- ppc64le
- s390x
- riscv64
hooks:
post:
- ./ci/script.sh compress_binary {{ .Path }} {{ .Arch }}
- id: core
dir: core
@@ -59,9 +56,6 @@ builds:
- ppc64le
- s390x
- riscv64
hooks:
post:
- ./ci/script.sh compress_binary {{ .Path }} {{ .Arch }}
archives:
- formats: [ 'tar.gz' ]
+1 -1
View File
@@ -46,4 +46,4 @@ build_agent_on_darwin:
build_all: build_frontend build_core_on_linux build_agent_on_linux
build_on_local: clean_assets build_frontend build_core_on_darwin build_agent_on_darwin upx_bin
build_on_local: clean_assets build_frontend build_core_on_darwin build_agent_on_darwin
+29 -1
View File
@@ -1,6 +1,9 @@
package v2
import (
"net/http"
"time"
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
@@ -26,7 +29,7 @@ func (b *BaseApi) SearchApp(c *gin.Context) {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, list)
helper.SuccessWithDataGzipped(c, list)
}
// @Tags App
@@ -192,3 +195,28 @@ func (b *BaseApi) GetAppListUpdate(c *gin.Context) {
}
helper.SuccessWithData(c, res)
}
// @Tags App
// @Summary Get app icon by app_id
// @Accept json
// @Param appId path integer true "app id"
// @Success 200 {file} file "app icon"
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /apps/icon/:appId [get]
func (b *BaseApi) GetAppIcon(c *gin.Context) {
appID, err := helper.GetIntParamByKey(c, "appID")
if err != nil {
helper.BadRequest(c, err)
return
}
iconBytes, err := appService.GetAppIcon(appID)
if err != nil {
helper.InternalServer(c, err)
return
}
c.Header("Content-Type", "image/png")
c.Header("Cache-Control", "public, max-age=31536000, immutable")
c.Header("Last-Modified", time.Now().UTC().Format(http.TimeFormat))
c.Data(http.StatusOK, "image/png", iconBytes)
}
+17
View File
@@ -25,6 +25,23 @@ func (b *BaseApi) CheckBackupUsed(c *gin.Context) {
helper.Success(c)
}
// @Tags Backup Account
// @Summary Check backup account
// @Accept json
// @Param request body dto.BackupOperate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /backups/check [post]
func (b *BaseApi) CheckBackup(c *gin.Context) {
var req dto.BackupOperate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
helper.SuccessWithData(c, backupService.CheckConn(req))
}
// @Tags Backup Account
// @Summary Create backup account
// @Accept json
+44 -1
View File
@@ -263,6 +263,27 @@ func (b *BaseApi) ContainerListStats(c *gin.Context) {
helper.SuccessWithData(c, data)
}
// @Summary Load container stats size
// @Accept json
// @Param request body dto.OperationWithName true "request"
// @Success 200 {object} dto.ContainerItemStats
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /containers/item/stats [post]
func (b *BaseApi) ContainerItemStats(c *gin.Context) {
var req dto.OperationWithName
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
data, err := containerService.ContainerItemStats(req)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, data)
}
// @Tags Container
// @Summary Create container
// @Accept json
@@ -372,6 +393,28 @@ func (b *BaseApi) CleanContainerLog(c *gin.Context) {
helper.Success(c)
}
// @Tags Container
// @Summary Clean compose log
// @Accept json
// @Param request body dto.ComposeLogClean true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /containers/compose/clean/log [post]
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"清理容器编排 [name] 日志","formatEN":"clean compose [name] logs"}
func (b *BaseApi) CleanComposeLog(c *gin.Context) {
var req dto.ComposeLogClean
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := containerService.ComposeLogClean(req); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags Container
// @Summary Rename Container
// @Accept json
@@ -481,7 +524,6 @@ func (b *BaseApi) Inspect(c *gin.Context) {
helper.SuccessWithData(c, result)
}
// @Router /containers/download/log [post]
func (b *BaseApi) DownloadContainerLogs(c *gin.Context) {
var req dto.ContainerLog
if err := helper.CheckBindAndValidate(&req, c); err != nil {
@@ -695,6 +737,7 @@ func (b *BaseApi) ComposeUpdate(c *gin.Context) {
// @Param since query string false "时间筛选"
// @Param follow query string false "是否追踪"
// @Param tail query string false "显示行号"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /containers/search/log [get]
+26 -4
View File
@@ -27,7 +27,7 @@ func (b *BaseApi) LoadDashboardOsInfo(c *gin.Context) {
// @Tags Dashboard
// @Summary Load app launcher
// @Accept json
// @Success 200 {Array} dto.AppLauncher
// @Success 200 {array} dto.AppLauncher
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /dashboard/app/launcher [get]
@@ -37,14 +37,14 @@ func (b *BaseApi) LoadAppLauncher(c *gin.Context) {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, data)
helper.SuccessWithDataGzipped(c, data)
}
// @Tags Dashboard
// @Summary Load app launcher options
// @Accept json
// @Param request body dto.SearchByFilter true "request"
// @Success 200 {Array} dto.LauncherOption
// @Success 200 {array} dto.LauncherOption
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /dashboard/app/launcher/option [post]
@@ -85,7 +85,7 @@ func (b *BaseApi) UpdateAppLauncher(c *gin.Context) {
// @Tags Dashboard
// @Summary Load quick jump options
// @Success 200 {Array} dto.QuickJump
// @Success 200 {array} dto.QuickJump
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /dashboard/quick/option [get]
@@ -179,6 +179,28 @@ func (b *BaseApi) LoadDashboardCurrentInfo(c *gin.Context) {
helper.SuccessWithData(c, data)
}
// @Tags Dashboard
// @Summary Load top cpu processes
// @Success 200 {array} dto.Process
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /dashboard/current/top/cpu [get]
func (b *BaseApi) LoadDashboardTopCPU(c *gin.Context) {
data := dashboardService.LoadTopCPU()
helper.SuccessWithData(c, data)
}
// @Tags Dashboard
// @Summary Load top memory processes
// @Success 200 {array} dto.Process
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /dashboard/current/top/mem [get]
func (b *BaseApi) LoadDashboardTopMem(c *gin.Context) {
data := dashboardService.LoadTopMem()
helper.SuccessWithData(c, data)
}
// @Tags Dashboard
// @Summary System restart
// @Accept json
+9 -10
View File
@@ -195,27 +195,26 @@ func (b *BaseApi) SearchMysql(c *gin.Context) {
}
// @Tags Database Mysql
// @Summary List mysql database names
// @Summary List mysql database format collation options
// @Accept json
// @Param request body dto.PageInfo true "request"
// @Success 200 {array} dto.MysqlOption
// @Param request body dto.OperationWithName true "request"
// @Success 200 {array} dto.MysqlFormatCollationOption
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /databases/options [get]
func (b *BaseApi) ListDBName(c *gin.Context) {
list, err := mysqlService.ListDBOption()
if err != nil {
helper.InternalServer(c, err)
// @Router /databases/format/options [post]
func (b *BaseApi) ListDBFormatCollationOptions(c *gin.Context) {
var req dto.OperationWithName
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
helper.SuccessWithData(c, list)
helper.SuccessWithData(c, mysqlService.LoadFormatOption(req))
}
// @Tags Database Mysql
// @Summary Load mysql database from remote
// @Accept json
// @Param request body dto.MysqlLoadDB true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /databases/load [post]
+1
View File
@@ -167,6 +167,7 @@ func (b *BaseApi) SearchPostgresql(c *gin.Context) {
// @Summary Load postgresql database from remote
// @Accept json
// @Param request body dto.PostgresqlLoadDB true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /databases/pg/:database/load [post]
+1 -1
View File
@@ -27,7 +27,7 @@ func (b *BaseApi) LoadDeviceBaseInfo(c *gin.Context) {
// @Tags Device
// @Summary list time zone options
// @Accept json
// @Success 200 {Array} string
// @Success 200 {array} string
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /toolbox/device/zone/options [get]
+4 -2
View File
@@ -28,7 +28,7 @@ func (b *BaseApi) LoadFail2BanBaseInfo(c *gin.Context) {
// @Summary Page fail2ban ip list
// @Accept json
// @Param request body dto.Fail2BanSearch true "request"
// @Success 200 {Array} string
// @Success 200 {array} string
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /toolbox/fail2ban/search [post]
@@ -51,6 +51,7 @@ func (b *BaseApi) SearchFail2Ban(c *gin.Context) {
// @Summary Operate fail2ban
// @Accept json
// @Param request body dto.Operate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /toolbox/fail2ban/operate [post]
@@ -72,7 +73,8 @@ func (b *BaseApi) OperateFail2Ban(c *gin.Context) {
// @Tags Fail2ban
// @Summary Operate sshd of fail2ban
// @Accept json
// @Param request body dto.Operate true "request"
// @Param request body dto.Fail2BanSet true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /toolbox/fail2ban/operate/sshd [post]
+39 -3
View File
@@ -90,7 +90,7 @@ func (b *BaseApi) GetFileTree(c *gin.Context) {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, tree)
helper.SuccessWithDataGzipped(c, tree)
}
// @Tags File
@@ -265,6 +265,33 @@ func (b *BaseApi) GetContent(c *gin.Context) {
helper.InternalServer(c, err)
return
}
if info.Size > 2*1024 && info.Size < 5*1024*1024 {
helper.SuccessWithDataGzipped(c, info)
} else {
helper.SuccessWithData(c, info)
}
}
// @Tags File
// @Summary Preview file content
// @Accept json
// @Param request body request.FileContentReq true "request"
// @Success 200 {object} response.FileInfo
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /files/preview [post]
// @x-panel-log {"bodyKeys":["path"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"预览文件内容 [path]","formatEN":"Preview file content [path]"}
func (b *BaseApi) PreviewContent(c *gin.Context) {
var req request.FileContentReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
info, err := fileService.GetPreviewContent(req)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, info)
}
@@ -521,9 +548,14 @@ func (b *BaseApi) Download(c *gin.Context) {
file, err := os.Open(filePath)
if err != nil {
helper.InternalServer(c, err)
return
}
defer file.Close()
info, _ := file.Stat()
info, err := file.Stat()
if err != nil {
helper.InternalServer(c, err)
return
}
c.Header("Content-Length", strconv.FormatInt(info.Size(), 10))
c.Header("Content-Disposition", "attachment; filename*=utf-8''"+url.PathEscape(info.Name()))
http.ServeContent(c.Writer, c.Request, info.Name(), info.ModTime(), file)
@@ -841,7 +873,11 @@ func (b *BaseApi) ReadFileByLine(c *gin.Context) {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, res)
if res.TotalLines > 100 {
helper.SuccessWithDataGzipped(c, res)
} else {
helper.SuccessWithData(c, res)
}
}
// @Tags File
+1 -1
View File
@@ -269,7 +269,7 @@ func (b *BaseApi) OperateFilterRule(c *gin.Context) {
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := iptablesService.OperateRule(req); err != nil {
if err := iptablesService.OperateRule(req, true); err != nil {
helper.InternalServer(c, err)
return
}
+1
View File
@@ -54,6 +54,7 @@ func (b *BaseApi) LoadFtpLogInfo(c *gin.Context) {
// @Summary Operate FTP
// @Accept json
// @Param request body dto.Operate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /toolbox/ftp/operate [post]
+40
View File
@@ -1,10 +1,14 @@
package helper
import (
"compress/gzip"
"context"
"encoding/json"
"fmt"
"net/http"
"strconv"
"strings"
"sync"
"github.com/1Panel-dev/1Panel/agent/global"
"gorm.io/gorm"
@@ -46,6 +50,42 @@ func SuccessWithData(ctx *gin.Context, data interface{}) {
ctx.Abort()
}
var gzipWriterPool = sync.Pool{
New: func() interface{} {
return gzip.NewWriter(nil)
},
}
func SuccessWithDataGzipped(ctx *gin.Context, data interface{}) {
if !strings.Contains(ctx.GetHeader("Accept-Encoding"), "gzip") {
SuccessWithData(ctx, data)
return
}
if data == nil {
data = gin.H{}
}
res := dto.Response{
Code: http.StatusOK,
Data: data,
}
jsonBytes, err := json.Marshal(res)
if err != nil {
ErrorWithDetail(ctx, http.StatusInternalServerError, "ErrInternalServer", err)
return
}
ctx.Header("Content-Encoding", "gzip")
ctx.Header("Content-Type", "application/json; charset=utf-8")
ctx.Status(http.StatusOK)
gz := gzipWriterPool.Get().(*gzip.Writer)
gz.Reset(ctx.Writer)
_, _ = gz.Write(jsonBytes)
_ = gz.Close()
gzipWriterPool.Put(gz)
ctx.Abort()
}
func Success(ctx *gin.Context) {
res := dto.Response{
Code: http.StatusOK,
+25
View File
@@ -28,6 +28,27 @@ func (b *BaseApi) LoadMonitor(c *gin.Context) {
helper.InternalServer(c, err)
return
}
helper.SuccessWithDataGzipped(c, data)
}
// @Tags Monitor
// @Summary Load monitor data
// @Param request body dto.MonitorGPUSearch true "request"
// @Success 200 {object} dto.MonitorGPUData
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /hosts/monitor/gpu/search [post]
func (b *BaseApi) LoadGPUMonitor(c *gin.Context) {
var req dto.MonitorGPUSearch
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
data, err := monitorService.LoadGPUMonitorData(req)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, data)
}
@@ -105,3 +126,7 @@ func (b *BaseApi) GetIOOptions(c *gin.Context) {
sort.Strings(options)
helper.SuccessWithData(c, options)
}
func (b *BaseApi) GetCPUOptions(c *gin.Context) {
helper.SuccessWithData(c, monitorService.LoadGPUOptions())
}
+44 -1
View File
@@ -84,9 +84,31 @@ func (b *BaseApi) CheckLocalConn(c *gin.Context) {
helper.SuccessWithData(c, err == nil)
}
// @Tags System Setting
// @Summary Update local is conn
// @Accept json
// @Param request body dto.SSHDefaultConn true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /settings/ssh/conn/default [post]
// @x-panel-log {"bodyKeys":["defaultConn"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"本地终端默认连接 [defaultConn]","formatEN":"update system default conn [defaultConn]"}
func (b *BaseApi) SetDefaultIsConn(c *gin.Context) {
var req dto.SSHDefaultConn
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := settingService.SetDefaultIsConn(req); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags System Setting
// @Summary Check local conn info
// @Success 200 {bool} isOk
// @Success 200 {boolean} isOk
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /settings/ssh/check/info [post]
@@ -155,3 +177,24 @@ func (b *BaseApi) GetSettingByKey(c *gin.Context) {
value := settingService.GetSettingByKey(key)
helper.SuccessWithData(c, value)
}
// @Tags System Setting
// @Summary Save common description
// @Accept json
// @Param request body dto.CommonDescription true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /settings/description/save [post]
func (b *BaseApi) SaveDescription(c *gin.Context) {
var req dto.CommonDescription
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := settingService.SaveDescription(req); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
+1 -1
View File
@@ -9,7 +9,7 @@ import (
// @Tags Host
// @Summary Check if a system component exists
// @Accept json
// @Param name query string true "Component name to check (e.g., rsync, docker)"
// @Param name path string true "Component name to check (e.g., rsync, docker)"
// @Success 200 {object} response.ComponentInfo
// @Security ApiKeyAuth
// @Security Timestamp
+2 -2
View File
@@ -228,8 +228,8 @@ func wshandleError(ws *websocket.Conn, err error) bool {
}
var upGrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024 * 1024 * 10,
ReadBufferSize: 4096,
WriteBufferSize: 16384,
CheckOrigin: func(r *http.Request) bool {
return true
},
+41 -1
View File
@@ -73,7 +73,7 @@ func (b *BaseApi) GetWebsiteOptions(c *gin.Context) {
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /websites [post]
// @x-panel-log {"bodyKeys":["primaryDomain"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"创建网站 [primaryDomain]","formatEN":"Create website [primaryDomain]"}
// @x-panel-log {"bodyKeys":["alias"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"创建网站 [alias]","formatEN":"Create website [alias]"}
func (b *BaseApi) CreateWebsite(c *gin.Context) {
var req request.WebsiteCreate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
@@ -1223,3 +1223,43 @@ func (b *BaseApi) UpdateCORSConfig(c *gin.Context) {
}
helper.Success(c)
}
// @Tags Website
// @Summary Update Stream Config
// @Accept json
// @Param request body request.StreamUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /websites/stream/update [post]
func (b *BaseApi) UpdateStreamConfig(c *gin.Context) {
var req request.StreamUpdate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := websiteService.UpdateStream(req); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags Website
// @Summary Batch set HTTPS for websites
// @Accept json
// @Param request body request.BatchWebsiteHttps true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /websites/batch/https [post]
func (b *BaseApi) BatchSetHttps(c *gin.Context) {
var req request.BatchWebsiteHttps
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := websiteService.BatchSetHttps(c, req); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
+4 -3
View File
@@ -1,7 +1,6 @@
package v2
import (
"github.com/1Panel-dev/1Panel/agent/app/model"
"io"
"mime/multipart"
"net/http"
@@ -9,6 +8,8 @@ import (
"reflect"
"strconv"
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
@@ -34,7 +35,7 @@ func (b *BaseApi) PageWebsiteSSL(c *gin.Context) {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, dto.PageResult{
helper.SuccessWithDataGzipped(c, dto.PageResult{
Total: total,
Items: accounts,
})
@@ -44,7 +45,7 @@ func (b *BaseApi) PageWebsiteSSL(c *gin.Context) {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, list)
helper.SuccessWithDataGzipped(c, list)
}
}
+7 -5
View File
@@ -12,6 +12,8 @@ type AppDatabase struct {
DbUser string `json:"PANEL_DB_USER"`
Password string `json:"PANEL_DB_USER_PASSWORD"`
DatabaseName string `json:"DATABASE_NAME"`
Format string `json:"format"`
Collation string `json:"collation"`
}
type AuthParam struct {
@@ -90,15 +92,15 @@ type AppProperty struct {
Description Locale `json:"description"`
Key string `json:"key"`
Required []string `json:"Required"`
CrossVersionUpdate bool `json:"crossVersionUpdate"`
Limit int `json:"limit"`
Recommend int `json:"recommend"`
CrossVersionUpdate bool `json:"crossVersionUpdate" yaml:"crossVersionUpdate"`
Limit int `json:"limit" yaml:"limit"`
Recommend int `json:"recommend" yaml:"recommend"`
Website string `json:"website"`
Github string `json:"github"`
Document string `json:"document"`
Architectures []string `json:"architectures"`
MemoryRequired int `json:"memoryRequired"`
GpuSupport bool `json:"gpuSupport"`
MemoryRequired int `json:"memoryRequired" yaml:"memoryRequired"`
GpuSupport bool `json:"gpuSupport" yaml:"gpuSupport"`
Version float64 `json:"version"`
Deprecated float64 `json:"deprecated"`
}
+7
View File
@@ -33,6 +33,12 @@ type BackupInfo struct {
RememberAuth bool `json:"rememberAuth"`
}
type BackupCheckRes struct {
IsOk bool `json:"isOk"`
Msg string `json:"msg"`
Token string `json:"token"`
}
type ForBuckets struct {
Type string `json:"type" validate:"required"`
AccessKey string `json:"accessKey"`
@@ -77,6 +83,7 @@ type CommonRecover struct {
Secret string `json:"secret"`
TaskID string `json:"taskID"`
BackupRecordID uint `json:"backupRecordID"`
Timeout int `json:"timeout"`
}
type RecordSearch struct {
+59 -42
View File
@@ -15,8 +15,9 @@ type PageContainer struct {
}
type InspectReq struct {
ID string `json:"id" validate:"required"`
Type string `json:"type" validate:"required"`
ID string `json:"id" validate:"required"`
Type string `json:"type" validate:"required"`
Detail string `json:"detail"`
}
type ContainerInfo struct {
@@ -31,15 +32,15 @@ type ContainerInfo struct {
Network []string `json:"network"`
Ports []string `json:"ports"`
SizeRw int64 `json:"sizeRw"`
SizeRootFs int64 `json:"sizeRootFs"`
IsFromApp bool `json:"isFromApp"`
IsFromCompose bool `json:"isFromCompose"`
AppName string `json:"appName"`
AppInstallName string `json:"appInstallName"`
Websites []string `json:"websites"`
IsPinned bool `json:"isPinned"`
Description string `json:"description"`
}
type ContainerOptions struct {
@@ -63,15 +64,6 @@ type ContainerStatus struct {
NetworkCount int `json:"networkCount"`
VolumeCount int `json:"volumeCount"`
RepoCount int `json:"repoCount"`
ContainerUsage int64 `json:"containerUsage"`
ContainerReclaimable int64 `json:"containerReclaimable"`
ImageUsage int64 `json:"imageUsage"`
ImageReclaimable int64 `json:"imageReclaimable"`
VolumeUsage int64 `json:"volumeUsage"`
VolumeReclaimable int64 `json:"volumeReclaimable"`
BuildCacheUsage int64 `json:"buildCacheUsage"`
BuildCacheReclaimable int64 `json:"buildCacheReclaimable"`
}
type ResourceLimit struct {
CPU int `json:"cpu"`
@@ -79,17 +71,16 @@ type ResourceLimit struct {
}
type ContainerOperate struct {
TaskID string `json:"taskID"`
ForcePull bool `json:"forcePull"`
Name string `json:"name" validate:"required"`
Image string `json:"image" validate:"required"`
Network string `json:"network"`
Hostname string `json:"hostname"`
DomainName string `json:"domainName"`
MacAddr string `json:"macAddr"`
DNS []string `json:"dns"`
Ipv4 string `json:"ipv4"`
Ipv6 string `json:"ipv6"`
TaskID string `json:"taskID"`
ForcePull bool `json:"forcePull"`
Name string `json:"name" validate:"required"`
Image string `json:"image" validate:"required"`
Hostname string `json:"hostname"`
DomainName string `json:"domainName"`
DNS []string `json:"dns"`
Networks []ContainerNetwork `json:"networks"`
PublishAllPorts bool `json:"publishAllPorts"`
ExposedPorts []PortHelper `json:"exposedPorts"`
Tty bool `json:"tty"`
@@ -108,6 +99,12 @@ type ContainerOperate struct {
Env []string `json:"env"`
RestartPolicy string `json:"restartPolicy"`
}
type ContainerNetwork struct {
Network string `json:"network"`
Ipv4 string `json:"ipv4"`
Ipv6 string `json:"ipv6"`
MacAddr string `json:"macAddr"`
}
type ContainerCreateByCommand struct {
TaskID string `json:"taskID"`
@@ -121,6 +118,19 @@ type ContainerUpgrade struct {
ForcePull bool `json:"forcePull"`
}
type ContainerItemStats struct {
SizeRw int64 `json:"sizeRw"`
SizeRootFs int64 `json:"sizeRootFs"`
ContainerUsage int64 `json:"containerUsage"`
ContainerReclaimable int64 `json:"containerReclaimable"`
ImageUsage int64 `json:"imageUsage"`
ImageReclaimable int64 `json:"imageReclaimable"`
VolumeUsage int64 `json:"volumeUsage"`
VolumeReclaimable int64 `json:"volumeReclaimable"`
BuildCacheUsage int64 `json:"buildCacheUsage"`
BuildCacheReclaimable int64 `json:"buildCacheReclaimable"`
}
type ContainerListStats struct {
ContainerID string `json:"containerID"`
@@ -252,35 +262,42 @@ type ComposeInfo struct {
Workdir string `json:"workdir"`
Path string `json:"path"`
Containers []ComposeContainer `json:"containers"`
Env []string `json:"env"`
Env string `json:"env"`
}
type ComposeContainer struct {
ContainerID string `json:"containerID"`
Name string `json:"name"`
CreateTime string `json:"createTime"`
State string `json:"state"`
ContainerID string `json:"containerID"`
Name string `json:"name"`
CreateTime string `json:"createTime"`
State string `json:"state"`
Ports []string `json:"ports"`
}
type ComposeCreate struct {
TaskID string `json:"taskID"`
Name string `json:"name"`
From string `json:"from" validate:"required,oneof=edit path template"`
File string `json:"file"`
Path string `json:"path"`
Template uint `json:"template"`
Env []string `json:"env"`
TaskID string `json:"taskID"`
Name string `json:"name"`
From string `json:"from" validate:"required,oneof=edit path template"`
File string `json:"file"`
Path string `json:"path"`
Template uint `json:"template"`
Env string `json:"env"`
}
type ComposeOperation struct {
Name string `json:"name" validate:"required"`
Path string `json:"path"`
Operation string `json:"operation" validate:"required,oneof=up start restart stop down delete"`
WithFile bool `json:"withFile"`
Force bool `josn:"force"`
Force bool `json:"force"`
}
type ComposeUpdate struct {
Name string `json:"name" validate:"required"`
Path string `json:"path" validate:"required"`
Content string `json:"content" validate:"required"`
Env []string `json:"env"`
Name string `json:"name" validate:"required"`
Path string `json:"path" validate:"required"`
DetailPath string `json:"detailPath"`
Content string `json:"content" validate:"required"`
Env string `json:"env"`
}
type ComposeLogClean struct {
Name string `json:"name" validate:"required"`
Path string `json:"path" validate:"required"`
DetailPath string `json:"detailPath"`
}
type ContainerLog struct {
+15 -10
View File
@@ -13,15 +13,17 @@ type DashboardBase struct {
Platform string `json:"platform"`
PlatformFamily string `json:"platformFamily"`
PlatformVersion string `json:"platformVersion"`
PrettyDistro string `json:"prettyDistro"`
KernelArch string `json:"kernelArch"`
KernelVersion string `json:"kernelVersion"`
VirtualizationSystem string `json:"virtualizationSystem"`
IpV4Addr string `json:"ipV4Addr"`
SystemProxy string `json:"systemProxy"`
CPUCores int `json:"cpuCores"`
CPULogicalCores int `json:"cpuLogicalCores"`
CPUModelName string `json:"cpuModelName"`
CPUCores int `json:"cpuCores"`
CPULogicalCores int `json:"cpuLogicalCores"`
CPUModelName string `json:"cpuModelName"`
CPUMhz float64 `json:"cpuMhz"`
QuickJumps []QuickJump `json:"quickJump"`
CurrentInfo DashboardCurrent `json:"currentInfo"`
@@ -48,6 +50,7 @@ type OsInfo struct {
PlatformFamily string `json:"platformFamily"`
KernelArch string `json:"kernelArch"`
KernelVersion string `json:"kernelVersion"`
PrettyDistro string `json:"prettyDistro"`
DiskSize int64 `json:"diskSize"`
}
@@ -58,9 +61,10 @@ type NodeCurrent struct {
Load15 float64 `json:"load15"`
LoadUsagePercent float64 `json:"loadUsagePercent"`
CPUUsedPercent float64 `json:"cpuUsedPercent"`
CPUUsed float64 `json:"cpuUsed"`
CPUTotal int `json:"cpuTotal"`
CPUUsedPercent float64 `json:"cpuUsedPercent"`
CPUUsed float64 `json:"cpuUsed"`
CPUTotal int `json:"cpuTotal"`
CPUDetailedPercent []float64 `json:"cpuDetailedPercent"`
MemoryTotal uint64 `json:"memoryTotal"`
MemoryAvailable uint64 `json:"memoryAvailable"`
@@ -84,10 +88,11 @@ type DashboardCurrent struct {
Load15 float64 `json:"load15"`
LoadUsagePercent float64 `json:"loadUsagePercent"`
CPUPercent []float64 `json:"cpuPercent"`
CPUUsedPercent float64 `json:"cpuUsedPercent"`
CPUUsed float64 `json:"cpuUsed"`
CPUTotal int `json:"cpuTotal"`
CPUPercent []float64 `json:"cpuPercent"`
CPUUsedPercent float64 `json:"cpuUsedPercent"`
CPUUsed float64 `json:"cpuUsed"`
CPUTotal int `json:"cpuTotal"`
CPUDetailedPercent []float64 `json:"cpuDetailedPercent"`
MemoryTotal uint64 `json:"memoryTotal"`
MemoryUsed uint64 `json:"memoryUsed"`
+17 -8
View File
@@ -38,6 +38,7 @@ type MysqlDBInfo struct {
From string `json:"from"`
MysqlName string `json:"mysqlName"`
Format string `json:"format"`
Collation string `json:"collation"`
Username string `json:"username"`
Password string `json:"password"`
Permission string `json:"permission"`
@@ -57,13 +58,19 @@ type MysqlDBCreate struct {
Name string `json:"name" validate:"required"`
From string `json:"from" validate:"required,oneof=local remote"`
Database string `json:"database" validate:"required"`
Format string `json:"format" validate:"required,oneof=utf8mb4 utf8 gbk big5"`
Format string `json:"format" validate:"required"`
Collation string `json:"collation"`
Username string `json:"username" validate:"required"`
Password string `json:"password" validate:"required"`
Permission string `json:"permission" validate:"required"`
Description string `json:"description"`
}
type MysqlFormatCollationOption struct {
Format string `json:"format"`
Collations []string `json:"collations"`
}
type BindUser struct {
Database string `json:"database" validate:"required"`
DB string `json:"db" validate:"required"`
@@ -251,6 +258,7 @@ type DatabaseInfo struct {
Version string `json:"version"`
Address string `json:"address"`
Port uint `json:"port"`
InitialDB string `json:"initialDB"`
Username string `json:"username"`
Password string `json:"password"`
@@ -302,13 +310,14 @@ type DatabaseCreate struct {
}
type DatabaseUpdate struct {
ID uint `json:"id"`
Type string `json:"type" validate:"required"`
Version string `json:"version" validate:"required"`
Address string `json:"address"`
Port uint `json:"port"`
Username string `json:"username" validate:"required"`
Password string `json:"password"`
ID uint `json:"id"`
Type string `json:"type" validate:"required"`
Version string `json:"version" validate:"required"`
Address string `json:"address"`
Port uint `json:"port"`
InitialDB string `json:"initialDB"`
Username string `json:"username" validate:"required"`
Password string `json:"password"`
SSL bool `json:"ssl"`
RootCert string `json:"rootCert"`
+3
View File
@@ -15,6 +15,9 @@ type ImageInfo struct {
IsUsed bool `json:"isUsed"`
Tags []string `json:"tags"`
Size int64 `json:"size"`
IsPinned bool `json:"isPinned"`
Description string `json:"description"`
}
type ImageLoad struct {
+43 -1
View File
@@ -11,7 +11,7 @@ type MonitorSearch struct {
}
type MonitorData struct {
Param string `json:"param" validate:"required,oneof=cpu memory load io network"`
Param string `json:"param"`
Date []time.Time `json:"date"`
Value []interface{} `json:"value"`
}
@@ -37,3 +37,45 @@ type MonitorSettingUpdate struct {
Key string `json:"key" validate:"required,oneof=MonitorStatus MonitorStoreDays MonitorInterval DefaultNetwork DefaultIO"`
Value string `json:"value"`
}
type MonitorGPUOptions struct {
GPUType string `json:"gpuType"`
ChartHide []GPUChartHide `json:"chartHide"`
Options []string `json:"options"`
}
type GPUChartHide struct {
ProductName string `json:"productName"`
Process bool `json:"process"`
GPU bool `json:"gpu"`
Memory bool `json:"memory"`
Power bool `json:"power"`
Temperature bool `json:"temperature"`
Speed bool `json:"speed"`
}
type MonitorGPUSearch struct {
ProductName string `json:"productName"`
StartTime time.Time `json:"startTime"`
EndTime time.Time `json:"endTime"`
}
type MonitorGPUData struct {
Date []time.Time `json:"date"`
GPUValue []float64 `json:"gpuValue"`
TemperatureValue []float64 `json:"temperatureValue"`
PowerTotal []float64 `json:"powerTotal"`
PowerUsed []float64 `json:"powerUsed"`
PowerPercent []float64 `json:"powerPercent"`
MemoryTotal []float64 `json:"memoryTotal"`
MemoryUsed []float64 `json:"memoryUsed"`
MemoryPercent []float64 `json:"memoryPercent"`
SpeedValue []int `json:"speedValue"`
ProcessCount []int `json:"processCount"`
GPUProcesses [][]GPUProcess `json:"gpuProcesses"`
}
type GPUProcess struct {
Pid string `json:"pid"`
Type string `json:"type"`
ProcessName string `json:"processName"`
UsedMemory string `json:"usedMemory"`
}
-1
View File
@@ -13,7 +13,6 @@ type FileContentReq struct {
Path string `json:"path" validate:"required"`
IsDetail bool `json:"isDetail"`
}
type SearchUploadWithPage struct {
dto.PageInfo
Path string `json:"path" validate:"required"`
+34
View File
@@ -38,6 +38,21 @@ type WebsiteCreate struct {
FtpConfig
DataBaseConfig
SSLConfig
StreamConfig
}
type StreamConfig struct {
StreamPorts string `json:"streamPorts"`
Name string `json:"name"`
Algorithm string `json:"algorithm"`
Servers []dto.NginxUpstreamServer `json:"servers"`
}
type StreamUpdate struct {
WebsiteID uint `json:"websiteID" validate:"required"`
StreamConfig
}
type WebsiteOptionReq struct {
@@ -114,6 +129,25 @@ type BatchWebsiteGroup struct {
GroupID uint `json:"groupID" validate:"required"`
}
type BatchWebsiteHttps struct {
IDs []uint `json:"ids" validate:"required"`
TaskID string `json:"taskID" validate:"required"`
WebsiteSSLID uint `json:"websiteSSLId"`
Type string `json:"type" validate:"oneof=existed auto manual"`
PrivateKey string `json:"privateKey"`
Certificate string `json:"certificate"`
PrivateKeyPath string `json:"privateKeyPath"`
CertificatePath string `json:"certificatePath"`
ImportType string `json:"importType"`
HttpConfig string `json:"httpConfig" validate:"oneof=HTTPSOnly HTTPAlso HTTPToHTTPS"`
SSLProtocol []string `json:"SSLProtocol"`
Algorithm string `json:"algorithm"`
Hsts bool `json:"hsts"`
HstsIncludeSubDomains bool `json:"hstsIncludeSubDomains"`
HttpsPorts []int `json:"httpsPorts"`
Http3 bool `json:"http3"`
}
type WebsiteRedirectUpdate struct {
WebsiteID uint `json:"websiteId" validate:"required"`
Key string `json:"key" validate:"required"`
+1
View File
@@ -31,6 +31,7 @@ type WebsiteSSLCreate struct {
Shell string `json:"shell"`
PushNode bool `json:"pushNode"`
Nodes string `json:"nodes"`
IsIp bool `json:"isIp"`
}
type WebsiteDNSReq struct {
+2 -7
View File
@@ -33,18 +33,13 @@ type AppItem struct {
Key string `json:"key"`
ID uint `json:"id"`
Description string `json:"description"`
Icon string `json:"icon"`
Type string `json:"type"`
Status string `json:"status"`
Resource string `json:"resource"`
Installed bool `json:"installed"`
Versions []string `json:"versions"`
Limit int `json:"limit"`
Tags []TagDTO `json:"tags"`
Github string `json:"github"`
Website string `json:"website"`
Tags []string `json:"tags"`
GpuSupport bool `json:"gpuSupport"`
Recommend int `json:"recommend"`
Type string `json:"type"`
}
type TagDTO struct {
+4
View File
@@ -3,6 +3,7 @@ package response
import (
"time"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/model"
)
@@ -16,6 +17,9 @@ type WebsiteDTO struct {
RuntimeType string `json:"runtimeType"`
SiteDir string `json:"siteDir"`
OpenBaseDir bool `json:"openBaseDir"`
Algorithm string `json:"algorithm"`
Servers []dto.NginxUpstreamServer `json:"servers"`
}
type WebsiteRes struct {
+12
View File
@@ -62,6 +62,10 @@ type Clean struct {
Size uint64 `json:"size"`
}
type SSHDefaultConn struct {
WithReset bool `json:"withReset"`
DefaultConn string `json:"defaultConn"`
}
type SSHConnData struct {
Addr string `json:"addr" validate:"required"`
Port uint `json:"port" validate:"required,number,max=65535,min=1"`
@@ -81,3 +85,11 @@ type SystemProxy struct {
User string `json:"user"`
Password string `json:"password"`
}
type CommonDescription struct {
ID string `json:"id" validate:"required"`
Type string `json:"type" validate:"required"`
DetailType string `json:"detailType"`
IsPinned bool `json:"isPinned"`
Description string `json:"description"`
}
+1
View File
@@ -52,6 +52,7 @@ type DataTree struct {
Label string `json:"label"`
Key string `json:"key"`
Name string `json:"name"`
IsLocal bool `json:"isLocal"`
Size uint64 `json:"size"`
IsCheck bool `json:"isCheck"`
IsDisable bool `json:"isDisable"`
+1
View File
@@ -6,6 +6,7 @@ type DatabaseMysql struct {
From string `json:"from" gorm:"not null;default:local"`
MysqlName string `json:"mysqlName" gorm:"not null"`
Format string `json:"format" gorm:"not null"`
Collation string `json:"collation"`
Username string `json:"username" gorm:"not null"`
Password string `json:"password" gorm:"not null"`
Permission string `json:"permission" gorm:"not null"`
+13
View File
@@ -31,3 +31,16 @@ type MonitorNetwork struct {
Up float64 `json:"up"`
Down float64 `json:"down"`
}
type MonitorGPU struct {
BaseModel
ProductName string `json:"productName"`
GPUUtil float64 `json:"gpuUtil"`
Temperature float64 `json:"temperature"`
PowerDraw float64 `json:"powerDraw"`
MaxPowerLimit float64 `json:"maxPowerLimit"`
MemUsed float64 `json:"memUsed"`
MemTotal float64 `json:"memTotal"`
FanSpeed int `json:"fanSpeed"`
Processes string `json:"processes"`
}
+8
View File
@@ -7,6 +7,14 @@ type Setting struct {
About string `json:"about"`
}
type CommonDescription struct {
ID string `json:"id"`
Type string `json:"type"`
DetailType string `json:"detailType"`
IsPinned bool `json:"isPinned"`
Description string `json:"description"`
}
type NodeInfo struct {
Scope string `json:"scope"`
BaseDir string `json:"baseDir"`
+2
View File
@@ -37,6 +37,8 @@ type Website struct {
Favorite bool `json:"favorite"`
StreamPorts string `json:"streamPorts"`
Domains []WebsiteDomain `json:"domains" gorm:"-:migration"`
WebsiteSSL WebsiteSSL `json:"webSiteSSL" gorm:"-:migration"`
}
+1
View File
@@ -41,6 +41,7 @@ type WebsiteSSL struct {
PushNode bool `json:"pushNode"`
PrivateKeyPath string `json:"privateKeyPath"`
CertPath string `json:"certPath"`
IsIp bool `json:"isIP"`
AcmeAccount WebsiteAcmeAccount `json:"acmeAccount" gorm:"-:migration"`
DnsAccount WebsiteDnsAccount `json:"dnsAccount" gorm:"-:migration"`
+2 -2
View File
@@ -33,7 +33,7 @@ type IAppRepo interface {
DeleteByIDs(ctx context.Context, ids []uint) error
DeleteBy(opts ...DBOption) error
GetTopRecomment() ([]string, error)
GetTopRecommend() ([]string, error)
}
func NewIAppRepo() IAppRepo {
@@ -124,7 +124,7 @@ func (a AppRepo) GetBy(opts ...DBOption) ([]model.App, error) {
return apps, nil
}
func (a AppRepo) GetTopRecomment() ([]string, error) {
func (a AppRepo) GetTopRecommend() ([]string, error) {
var (
apps []model.App
names []string
+5
View File
@@ -77,6 +77,11 @@ func WithByType(tp string) DBOption {
return g.Where("`type` = ?", tp)
}
}
func WithByDetailType(tp string) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("`detail_type` = ?", tp)
}
}
func WithTypes(types []string) DBOption {
return func(db *gorm.DB) *gorm.DB {
+46 -4
View File
@@ -5,21 +5,27 @@ import (
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/global"
"gorm.io/gorm"
)
type MonitorRepo struct{}
type IMonitorRepo interface {
GetBase(opts ...DBOption) ([]model.MonitorBase, error)
GetGPU(opts ...DBOption) ([]model.MonitorGPU, error)
GetIO(opts ...DBOption) ([]model.MonitorIO, error)
GetNetwork(opts ...DBOption) ([]model.MonitorNetwork, error)
CreateMonitorBase(model model.MonitorBase) error
BatchCreateMonitorGPU(list []model.MonitorGPU) error
BatchCreateMonitorIO(ioList []model.MonitorIO) error
BatchCreateMonitorNet(ioList []model.MonitorNetwork) error
DelMonitorBase(timeForDelete time.Time) error
DelMonitorGPU(timeForDelete time.Time) error
DelMonitorIO(timeForDelete time.Time) error
DelMonitorNet(timeForDelete time.Time) error
WithByProductName(name string) DBOption
}
func NewIMonitorRepo() IMonitorRepo {
@@ -53,15 +59,42 @@ func (u *MonitorRepo) GetNetwork(opts ...DBOption) ([]model.MonitorNetwork, erro
err := db.Find(&data).Error
return data, err
}
func (u *MonitorRepo) GetGPU(opts ...DBOption) ([]model.MonitorGPU, error) {
var data []model.MonitorGPU
db := global.GPUMonitorDB
for _, opt := range opts {
db = opt(db)
}
err := db.Find(&data).Error
return data, err
}
func (u *MonitorRepo) CreateMonitorBase(model model.MonitorBase) error {
return global.MonitorDB.Create(&model).Error
}
func (u *MonitorRepo) BatchCreateMonitorIO(ioList []model.MonitorIO) error {
return global.MonitorDB.CreateInBatches(ioList, len(ioList)).Error
func (s *MonitorRepo) BatchCreateMonitorGPU(list []model.MonitorGPU) error {
for _, item := range list {
if err := global.GPUMonitorDB.Create(&item).Error; err != nil {
global.LOG.Errorf("create Monitor GPU record failed, err: %v", err)
}
}
return nil
}
func (u *MonitorRepo) BatchCreateMonitorNet(ioList []model.MonitorNetwork) error {
return global.MonitorDB.CreateInBatches(ioList, len(ioList)).Error
func (u *MonitorRepo) BatchCreateMonitorIO(list []model.MonitorIO) error {
for _, item := range list {
if err := global.MonitorDB.Create(&item).Error; err != nil {
global.LOG.Errorf("create Monitor IO record failed, err: %v", err)
}
}
return nil
}
func (u *MonitorRepo) BatchCreateMonitorNet(list []model.MonitorNetwork) error {
for _, item := range list {
if err := global.MonitorDB.Create(&item).Error; err != nil {
global.LOG.Errorf("create Monitor Network record failed, err: %v", err)
}
}
return nil
}
func (u *MonitorRepo) DelMonitorBase(timeForDelete time.Time) error {
return global.MonitorDB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorBase{}).Error
@@ -72,3 +105,12 @@ func (u *MonitorRepo) DelMonitorIO(timeForDelete time.Time) error {
func (u *MonitorRepo) DelMonitorNet(timeForDelete time.Time) error {
return global.MonitorDB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorNetwork{}).Error
}
func (s *MonitorRepo) DelMonitorGPU(timeForDelete time.Time) error {
return global.GPUMonitorDB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorGPU{}).Error
}
func (s *MonitorRepo) WithByProductName(name string) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("product_name = ?", name)
}
}
+40 -26
View File
@@ -2,7 +2,6 @@ package repo
import (
"errors"
"time"
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/global"
@@ -19,13 +18,14 @@ type ISettingRepo interface {
Update(key, value string) error
WithByKey(key string) DBOption
CreateMonitorBase(model model.MonitorBase) error
BatchCreateMonitorIO(ioList []model.MonitorIO) error
BatchCreateMonitorNet(ioList []model.MonitorNetwork) error
DelMonitorBase(timeForDelete time.Time) error
DelMonitorIO(timeForDelete time.Time) error
DelMonitorNet(timeForDelete time.Time) error
UpdateOrCreate(key, value string) error
GetDescription(opts ...DBOption) (model.CommonDescription, error)
GetDescriptionList(opts ...DBOption) ([]model.CommonDescription, error)
CreateDescription(data *model.CommonDescription) error
UpdateDescription(id string, val map[string]interface{}) error
DelDescription(id string) error
WithByDescriptionID(id string) DBOption
}
func NewISettingRepo() ISettingRepo {
@@ -78,25 +78,6 @@ func (s *SettingRepo) Update(key, value string) error {
return global.DB.Model(&model.Setting{}).Where("key = ?", key).Updates(map[string]interface{}{"value": value}).Error
}
func (s *SettingRepo) CreateMonitorBase(model model.MonitorBase) error {
return global.MonitorDB.Create(&model).Error
}
func (s *SettingRepo) BatchCreateMonitorIO(ioList []model.MonitorIO) error {
return global.MonitorDB.CreateInBatches(ioList, len(ioList)).Error
}
func (s *SettingRepo) BatchCreateMonitorNet(ioList []model.MonitorNetwork) error {
return global.MonitorDB.CreateInBatches(ioList, len(ioList)).Error
}
func (s *SettingRepo) DelMonitorBase(timeForDelete time.Time) error {
return global.MonitorDB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorBase{}).Error
}
func (s *SettingRepo) DelMonitorIO(timeForDelete time.Time) error {
return global.MonitorDB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorIO{}).Error
}
func (s *SettingRepo) DelMonitorNet(timeForDelete time.Time) error {
return global.MonitorDB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorNetwork{}).Error
}
func (s *SettingRepo) UpdateOrCreate(key, value string) error {
var setting model.Setting
result := global.DB.Where("key = ?", key).First(&setting)
@@ -108,3 +89,36 @@ func (s *SettingRepo) UpdateOrCreate(key, value string) error {
}
return global.DB.Model(&setting).UpdateColumn("value", value).Error
}
func (s *SettingRepo) GetDescriptionList(opts ...DBOption) ([]model.CommonDescription, error) {
var lists []model.CommonDescription
db := global.DB.Model(&model.CommonDescription{})
for _, opt := range opts {
db = opt(db)
}
err := db.Find(&lists).Error
return lists, err
}
func (s *SettingRepo) GetDescription(opts ...DBOption) (model.CommonDescription, error) {
var data model.CommonDescription
db := global.DB.Model(&model.CommonDescription{})
for _, opt := range opts {
db = opt(db)
}
err := db.First(&data).Error
return data, err
}
func (s *SettingRepo) CreateDescription(data *model.CommonDescription) error {
return global.DB.Create(data).Error
}
func (s *SettingRepo) UpdateDescription(id string, val map[string]interface{}) error {
return global.DB.Model(&model.CommonDescription{}).Where("id = ?", id).Updates(val).Error
}
func (s *SettingRepo) DelDescription(id string) error {
return global.DB.Where("id = ?", id).Delete(&model.CommonDescription{}).Error
}
func (s *SettingRepo) WithByDescriptionID(id string) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("id = ?", id)
}
}
+3 -2
View File
@@ -9,6 +9,7 @@ import (
"github.com/1Panel-dev/1Panel/agent/global"
alertUtil "github.com/1Panel-dev/1Panel/agent/utils/alert"
"github.com/1Panel-dev/1Panel/agent/utils/common"
"github.com/1Panel-dev/1Panel/agent/utils/psutil"
versionUtil "github.com/1Panel-dev/1Panel/agent/utils/version"
"github.com/1Panel-dev/1Panel/agent/utils/xpack"
"github.com/shirou/gopsutil/v4/cpu"
@@ -352,7 +353,7 @@ func loadLoadInfo(alert dto.AlertDTO) {
return
}
var loadValue float64
CPUTotal, _ := cpu.Counts(true)
CPUTotal, _ := psutil.CPUInfo.GetLogicalCores(false)
switch alert.Cycle {
case 1:
loadValue = avgStat.Load1 / (float64(CPUTotal*2) * 0.75) * 100
@@ -839,7 +840,7 @@ func processSingleDisk(alert dto.AlertDTO) error {
}
func checkAndCreateDiskAlert(alert dto.AlertDTO, path string) (bool, error) {
usageStat, err := disk.Usage(path)
usageStat, err := psutil.DISK.GetUsage(path, false)
if err != nil {
global.LOG.Errorf("error getting disk usage for %s, err: %v", path, err)
return false, err
+26 -30
View File
@@ -37,7 +37,7 @@ type AppService struct {
}
type IAppService interface {
PageApp(ctx *gin.Context, req request.AppSearch) (interface{}, error)
PageApp(ctx *gin.Context, req request.AppSearch) (*response.AppRes, error)
GetAppTags(ctx *gin.Context) ([]response.TagDTO, error)
GetApp(ctx *gin.Context, key string) (*response.AppDTO, error)
GetAppDetail(appId uint, version, appType string) (response.AppDetailDTO, error)
@@ -46,13 +46,14 @@ type IAppService interface {
GetAppUpdate() (*response.AppUpdateRes, error)
GetAppDetailByID(id uint) (*response.AppDetailDTO, error)
SyncAppListFromLocal(taskID string)
GetAppIcon(appID uint) ([]byte, error)
}
func NewIAppService() IAppService {
return &AppService{}
}
func (a AppService) PageApp(ctx *gin.Context, req request.AppSearch) (interface{}, error) {
func (a AppService) PageApp(ctx *gin.Context, req request.AppSearch) (*response.AppRes, error) {
var opts []repo.DBOption
opts = append(opts, appRepo.OrderByRecommend())
if req.Name != "" {
@@ -98,7 +99,7 @@ func (a AppService) PageApp(ctx *gin.Context, req request.AppSearch) (interface{
}
opts = append(opts, repo.WithByIDs(appIds))
}
var res response.AppRes
res := &response.AppRes{}
total, apps, err := appRepo.Page(req.Page, req.PageSize, opts...)
if err != nil {
@@ -120,22 +121,20 @@ func (a AppService) PageApp(ctx *gin.Context, req request.AppSearch) (interface{
ID: ap.ID,
Name: ap.Name,
Key: ap.Key,
Type: ap.Type,
Icon: ap.Icon,
Resource: ap.Resource,
Limit: ap.Limit,
Website: ap.Website,
Github: ap.Github,
GpuSupport: ap.GpuSupport,
Recommend: ap.Recommend,
Description: ap.GetDescription(ctx),
Type: ap.Type,
}
appDTOs = append(appDTOs, appDTO)
tags, err := getAppTags(ap.ID, lang)
if err != nil {
continue
}
appDTO.Tags = tags
for _, tag := range tags {
appDTO.Tags = append(appDTO.Tags, tag.Name)
}
if ap.Type == constant.RuntimePHP || ap.Type == constant.RuntimeGo || ap.Type == constant.RuntimeNode || ap.Type == constant.RuntimePython || ap.Type == constant.RuntimeJava || ap.Type == constant.RuntimeDotNet {
details, _ := appDetailRepo.GetBy(appDetailRepo.WithAppId(ap.ID))
var ids []uint
@@ -195,24 +194,7 @@ func (a AppService) GetApp(ctx *gin.Context, key string) (*response.AppDTO, erro
if err != nil {
return nil, err
}
var versionsRaw []string
hasLatest := false
latestVersion := ""
for _, detail := range details {
if strings.Contains(detail.Version, "latest") {
hasLatest = true
latestVersion = detail.Version
continue
}
if key == "openresty" && !common.CompareAppVersion(detail.Version, "1.27") {
continue
}
versionsRaw = append(versionsRaw, detail.Version)
}
appDTO.Versions = common.GetSortedVersions(versionsRaw)
if hasLatest {
appDTO.Versions = append([]string{latestVersion}, appDTO.Versions...)
}
appDTO.Versions = getAppVersions(key, details)
tags, err := getAppTags(app.ID, strings.ToLower(common.GetLang(ctx)))
if err != nil {
return nil, err
@@ -864,13 +846,15 @@ func getAppList() (*dto.AppList, error) {
return nil, err
}
listFile := filepath.Join(global.Dir.ResourceDir, "1panel.json")
content, err := os.ReadFile(listFile)
file, err := os.Open(listFile)
if err != nil {
return nil, err
}
if err = json.Unmarshal(content, list); err != nil {
defer file.Close()
if err = json.NewDecoder(file).Decode(list); err != nil {
return nil, err
}
return list, nil
}
@@ -1153,7 +1137,7 @@ func (a AppService) SyncAppListFromRemote(taskID string) (err error) {
}, nil)
go func() {
if err = syncTask.Execute(); err != nil {
if err := syncTask.Execute(); err != nil {
_ = NewISettingService().Update("AppStoreLastModified", "0")
_ = NewISettingService().Update("AppStoreSyncStatus", constant.StatusError)
}
@@ -1161,3 +1145,15 @@ func (a AppService) SyncAppListFromRemote(taskID string) (err error) {
return nil
}
func (a AppService) GetAppIcon(appID uint) ([]byte, error) {
app, err := appRepo.GetFirst(repo.WithByID(appID))
if err != nil {
return nil, err
}
iconBytes, err := base64.StdEncoding.DecodeString(app.Icon)
if err != nil {
return nil, err
}
return iconBytes, nil
}
+1
View File
@@ -515,6 +515,7 @@ func (a *AppInstallService) GetServices(key string) ([]response.AppService, erro
} else {
service.From = constant.AppResourceRemote
service.Status = constant.StatusRunning
service.Config = map[string]string{"PANEL_DB_ROOT_PASSWORD": db.Password, "PANEL_DB_ROOT_USER": db.Username}
}
res = append(res, service)
}
+42 -6
View File
@@ -6,7 +6,6 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"log"
"maps"
"math"
"net/http"
@@ -45,6 +44,7 @@ import (
"github.com/compose-spec/compose-go/v2/types"
"github.com/docker/docker/api/types/container"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/subosito/gotenv"
"gopkg.in/yaml.v3"
)
@@ -300,10 +300,11 @@ func createLink(ctx context.Context, installTask *task.Task, app model.App, appI
createMysql.Name = dbConfig.DbName
createMysql.Username = dbConfig.DbUser
createMysql.Database = database.Name
createMysql.Format = "utf8mb4"
createMysql.Format = dbConfig.Format
createMysql.Permission = "%"
createMysql.Password = dbConfig.Password
createMysql.From = database.From
createMysql.Collation = dbConfig.Collation
mysqldb, err := NewIMysqlService().Create(ctx, createMysql)
if err != nil {
return err
@@ -540,8 +541,18 @@ func handleUpgradeCompose(install model.AppInstall, detail model.AppDetail) (map
if !ok || oldValue == nil {
return nil, buserr.New("ErrFileParse")
}
oldValueMap := oldValue.(map[string]interface{})
oldServiceValue := oldValueMap[install.ServiceName].(map[string]interface{})
oldValueMap, ok := oldValue.(map[string]interface{})
if !ok {
return nil, buserr.New("ErrFileParse")
}
oldServiceValueInterface, ok := oldValueMap[install.ServiceName]
if !ok || oldServiceValueInterface == nil {
return nil, buserr.New("ErrFileParse")
}
oldServiceValue, ok := oldServiceValueInterface.(map[string]interface{})
if !ok {
return nil, buserr.New("ErrFileParse")
}
if oldServiceValue["deploy"] != nil {
serviceValue["deploy"] = oldServiceValue["deploy"]
}
@@ -948,7 +959,7 @@ func handleMap(params map[string]interface{}, envParams map[string]string) {
}
}
func downloadApp(app model.App, appDetail model.AppDetail, appInstall *model.AppInstall, logger *log.Logger) (err error) {
func downloadApp(app model.App, appDetail model.AppDetail, appInstall *model.AppInstall, logger *logrus.Logger) (err error) {
if app.IsLocalApp() || app.IsCustomApp() {
return nil
}
@@ -1414,6 +1425,7 @@ func handleLocalApp(appDir string) (app *model.App, err error) {
app.Key = "local" + appDefine.Key
app.Architectures = strings.Join(appDefine.Architectures, ",")
app.GpuSupport = appDefine.GpuSupport
app.MemoryRequired = appDefine.MemoryRequired
app.Resource = constant.AppResourceLocal
app.Status = constant.AppNormal
@@ -1555,7 +1567,6 @@ func handleInstalled(appInstallList []model.AppInstall, updated bool, sync bool)
Message: installed.Message,
HttpPort: installed.HttpPort,
HttpsPort: installed.HttpsPort,
Icon: installed.App.Icon,
AppName: installed.App.Name,
AppKey: installed.App.Key,
AppType: installed.App.Type,
@@ -2163,3 +2174,28 @@ func isEditCompose(installed model.AppInstall) bool {
}
return false
}
func getAppVersions(key string, details []model.AppDetail) []string {
var (
versionsRaw []string
versions []string
)
hasLatest := false
latestVersion := ""
for _, detail := range details {
if key != "mssql" && strings.Contains(detail.Version, "latest") {
hasLatest = true
latestVersion = detail.Version
continue
}
if key == "openresty" && !common.CompareAppVersion(detail.Version, "1.27") {
continue
}
versionsRaw = append(versionsRaw, detail.Version)
}
versions = common.GetSortedVersions(versionsRaw)
if hasLatest {
versions = append([]string{latestVersion}, versions...)
}
return versions
}
+50 -37
View File
@@ -34,6 +34,7 @@ type IBackupService interface {
LoadBackupOptions() ([]dto.BackupOption, error)
SearchWithPage(search dto.SearchPageWithType) (int64, interface{}, error)
Create(backupDto dto.BackupOperate) error
CheckConn(req dto.BackupOperate) dto.BackupCheckRes
GetBuckets(backupDto dto.ForBuckets) ([]interface{}, error)
Update(req dto.BackupOperate) error
Delete(id uint) error
@@ -123,6 +124,43 @@ func (u *BackupService) SearchWithPage(req dto.SearchPageWithType) (int64, inter
return count, data, nil
}
func (u *BackupService) CheckConn(req dto.BackupOperate) dto.BackupCheckRes {
var res dto.BackupCheckRes
var backup model.BackupAccount
if err := copier.Copy(&backup, &req); err != nil {
res.Msg = i18n.GetMsgWithDetail("ErrStructTransform", err.Error())
return res
}
itemAccessKey, err := base64.StdEncoding.DecodeString(backup.AccessKey)
if err != nil {
res.Msg = err.Error()
return res
}
backup.AccessKey = string(itemAccessKey)
itemCredential, err := base64.StdEncoding.DecodeString(backup.Credential)
if err != nil {
res.Msg = err.Error()
return res
}
backup.Credential = string(itemCredential)
if req.Type == constant.OneDrive || req.Type == constant.GoogleDrive {
refreshToken, err := loadRefreshTokenByCode(&backup)
if err != nil {
res.Msg = err.Error()
return res
}
res.Token = base64.StdEncoding.EncodeToString([]byte(refreshToken))
}
isOk, err := u.checkBackupConn(&backup)
if err != nil {
res.Msg = err.Error()
return res
}
res.IsOk = isOk
return res
}
func (u *BackupService) Create(req dto.BackupOperate) error {
if req.Type == constant.Local {
return buserr.New("ErrBackupLocalCreate")
@@ -147,19 +185,6 @@ func (u *BackupService) Create(req dto.BackupOperate) error {
return err
}
backup.Credential = string(itemCredential)
if req.Type == constant.OneDrive || req.Type == constant.GoogleDrive || req.Type == constant.ALIYUN {
if err := loadRefreshTokenByCode(&backup); err != nil {
return err
}
}
if req.Type != constant.Local {
isOk, err := u.checkBackupConn(&backup)
if err != nil || !isOk {
return buserr.WithMap("ErrBackupCheck", map[string]interface{}{"err": err.Error()}, err)
}
}
backup.AccessKey, err = encrypt.StringEncrypt(backup.AccessKey)
if err != nil {
return err
@@ -248,16 +273,7 @@ func (u *BackupService) Update(req dto.BackupOperate) error {
global.Dir.LocalBackupDir = newBackup.BackupPath
}
if newBackup.Type == constant.OneDrive || newBackup.Type == constant.GoogleDrive || newBackup.Type == constant.ALIYUN {
if err := loadRefreshTokenByCode(&newBackup); err != nil {
return err
}
}
if backup.Type != constant.Local {
isOk, err := u.checkBackupConn(&newBackup)
if err != nil || !isOk {
return buserr.WithMap("ErrBackupCheck", map[string]interface{}{"err": err.Error()}, err)
}
newBackup.AccessKey, err = encrypt.StringEncrypt(newBackup.AccessKey)
if err != nil {
return err
@@ -514,37 +530,34 @@ func newClient(account *model.BackupAccount, isEncrypt bool) (cloud_storage.Clou
return client, nil
}
func loadRefreshTokenByCode(backup *model.BackupAccount) error {
func loadRefreshTokenByCode(backup *model.BackupAccount) (string, error) {
varMap := make(map[string]interface{})
if err := json.Unmarshal([]byte(backup.Vars), &varMap); err != nil {
return fmt.Errorf("unmarshal backup vars failed, err: %v", err)
return "", fmt.Errorf("unmarshal backup vars failed, err: %v", err)
}
if _, ok := varMap["refresh_token"]; ok {
return "", nil
}
refreshToken := ""
var err error
if backup.Type == constant.GoogleDrive {
switch backup.Type {
case constant.GoogleDrive:
refreshToken, err = client.RefreshGoogleToken("authorization_code", "refreshToken", varMap)
if err != nil {
return err
return "", err
}
}
if backup.Type == constant.OneDrive {
case constant.OneDrive:
refreshToken, err = client.RefreshToken("authorization_code", "refreshToken", varMap)
if err != nil {
return err
return "", err
}
}
if backup.Type != constant.ALIYUN {
delete(varMap, "code")
varMap["refresh_token"] = refreshToken
}
varMap["refresh_status"] = constant.StatusSuccess
varMap["refresh_time"] = time.Now().Format(constant.DateTimeLayout)
itemVars, err := json.Marshal(varMap)
if err != nil {
return fmt.Errorf("json marshal var map failed, err: %v", err)
}
itemVars, _ := json.Marshal(varMap)
backup.Vars = string(itemVars)
return nil
return refreshToken, nil
}
func loadBackupNamesByID(accountIDs string, downloadID uint) ([]string, string, error) {
+17 -11
View File
@@ -8,17 +8,14 @@ import (
"strings"
"time"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/i18n"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/app/task"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/i18n"
"github.com/1Panel-dev/1Panel/agent/utils/common"
"github.com/1Panel-dev/1Panel/agent/utils/files"
"github.com/1Panel-dev/1Panel/agent/utils/mysql/client"
@@ -60,11 +57,11 @@ func (u *BackupService) MysqlRecover(req dto.CommonRecover) error {
}
func (u *BackupService) MysqlRecoverByUpload(req dto.CommonRecover) error {
recoveFile, err := loadSqlFile(req.File)
recoverFile, err := loadSqlFile(req.File)
if err != nil {
return err
}
req.File = recoveFile
req.File = recoverFile
if err := handleMysqlRecover(req, nil, false); err != nil {
return err
@@ -94,7 +91,7 @@ func handleMysqlBackup(db DatabaseHelper, parentTask *task.Task, recordID uint,
if parentTask != nil {
return itemHandler()
}
backupTask.AddSubTaskWithOps(task.GetTaskName(itemName, task.TaskBackup, task.TaskScopeBackup), func(t *task.Task) error { return itemHandler() }, nil, 3, time.Hour)
backupTask.AddSubTaskWithOps(task.GetTaskName(itemName, task.TaskBackup, task.TaskScopeBackup), func(t *task.Task) error { return itemHandler() }, nil, 0, 3*time.Hour)
go func() {
if err := backupTask.Execute(); err != nil {
backupRepo.UpdateRecordByMap(recordID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()})
@@ -196,7 +193,16 @@ func handleMysqlRecover(req dto.CommonRecover, parentTask *task.Task, isRollback
return recoverDatabase(parentTask)
}
itemTask.AddSubTaskWithOps(i18n.GetMsgByKey("TaskRecover"), recoverDatabase, nil, 3, time.Hour)
var timeout time.Duration
switch req.Timeout {
case -1:
timeout = 0
case 0:
timeout = 3 * time.Hour
default:
timeout = time.Duration(req.Timeout) * time.Second
}
itemTask.AddSubTaskWithOps(i18n.GetMsgByKey("TaskRecover"), recoverDatabase, nil, 0, timeout)
go func() {
_ = itemTask.Execute()
}()
+24 -8
View File
@@ -17,7 +17,6 @@ import (
"github.com/1Panel-dev/1Panel/agent/utils/common"
"github.com/1Panel-dev/1Panel/agent/utils/files"
"github.com/1Panel-dev/1Panel/agent/utils/postgresql/client"
pgclient "github.com/1Panel-dev/1Panel/agent/utils/postgresql/client"
)
func (u *BackupService) PostgresqlBackup(req dto.CommonBackup) error {
@@ -56,11 +55,11 @@ func (u *BackupService) PostgresqlRecover(req dto.CommonRecover) error {
}
func (u *BackupService) PostgresqlRecoverByUpload(req dto.CommonRecover) error {
recoveFile, err := loadSqlFile(req.File)
recoverFile, err := loadSqlFile(req.File)
if err != nil {
return err
}
req.File = recoveFile
req.File = recoverFile
if err := handlePostgresqlRecover(req, nil, false); err != nil {
return err
}
@@ -82,11 +81,11 @@ func handlePostgresqlBackup(db DatabaseHelper, parentTask *task.Task, recordID u
}
}
itemHandler := func() error { return doPostgresqlgBackup(db, targetDir, fileName, secret) }
itemHandler := func() error { return doPostgresqlBackup(db, targetDir, fileName, secret, backupTask) }
if parentTask != nil {
return itemHandler()
}
backupTask.AddSubTaskWithOps(task.GetTaskName(itemName, task.TaskBackup, task.TaskScopeBackup), func(t *task.Task) error { return itemHandler() }, nil, 3, time.Hour)
backupTask.AddSubTaskWithOps(task.GetTaskName(itemName, task.TaskBackup, task.TaskScopeBackup), func(t *task.Task) error { return itemHandler() }, nil, 0, 3*time.Hour)
go func() {
if err := backupTask.Execute(); err != nil {
backupRepo.UpdateRecordByMap(recordID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()})
@@ -130,10 +129,12 @@ func handlePostgresqlRecover(req dto.CommonRecover, parentTask *task.Task, isRol
if !isRollback {
rollbackFile := path.Join(global.Dir.TmpDir, fmt.Sprintf("database/%s/%s_%s.sql.gz", req.Type, req.DetailName, time.Now().Format(constant.DateTimeSlimLayout)))
if err := cli.Backup(client.BackupInfo{
Database: req.Name,
Name: req.DetailName,
TargetDir: path.Dir(rollbackFile),
FileName: path.Base(rollbackFile),
Task: t,
Timeout: 300,
}); err != nil {
return fmt.Errorf("backup postgresql db %s for rollback before recover failed, err: %v", req.DetailName, err)
@@ -142,9 +143,11 @@ func handlePostgresqlRecover(req dto.CommonRecover, parentTask *task.Task, isRol
if !isOk {
global.LOG.Info("recover failed, start to rollback now")
if err := cli.Recover(client.RecoverInfo{
Database: req.Name,
Name: req.DetailName,
SourceFile: rollbackFile,
Task: t,
Timeout: 300,
}); err != nil {
global.LOG.Errorf("rollback postgresql db %s from %s failed, err: %v", req.DetailName, rollbackFile, err)
@@ -167,9 +170,11 @@ func handlePostgresqlRecover(req dto.CommonRecover, parentTask *task.Task, isRol
t.LogWithStatus(i18n.GetMsgByKey("Decrypt"), err)
}
if err := cli.Recover(client.RecoverInfo{
Database: req.Name,
Name: req.DetailName,
SourceFile: req.File,
Username: dbInfo.Username,
Task: t,
Timeout: 300,
}); err != nil {
global.LOG.Errorf("recover postgresql db %s from %s failed, err: %v", req.DetailName, req.File, err)
@@ -182,24 +187,35 @@ func handlePostgresqlRecover(req dto.CommonRecover, parentTask *task.Task, isRol
return recoverDatabase(parentTask)
}
itemTask.AddSubTaskWithOps(i18n.GetMsgByKey("TaskRecover"), recoverDatabase, nil, 3, time.Hour)
var timeout time.Duration
switch req.Timeout {
case -1:
timeout = 0
case 0:
timeout = 3 * time.Hour
default:
timeout = time.Duration(req.Timeout) * time.Second
}
itemTask.AddSubTaskWithOps(i18n.GetMsgByKey("TaskRecover"), recoverDatabase, nil, 0, timeout)
go func() {
_ = itemTask.Execute()
}()
return nil
}
func doPostgresqlgBackup(db DatabaseHelper, targetDir, fileName, secret string) error {
func doPostgresqlBackup(db DatabaseHelper, targetDir, fileName, secret string, task *task.Task) error {
cli, err := LoadPostgresqlClientByFrom(db.Database)
if err != nil {
return err
}
defer cli.Close()
backupInfo := pgclient.BackupInfo{
backupInfo := client.BackupInfo{
Database: db.Database,
Name: db.Name,
TargetDir: targetDir,
FileName: fileName,
Task: task,
Timeout: 300,
}
if err := cli.Backup(backupInfo); err != nil {
+2 -2
View File
@@ -131,7 +131,7 @@ func handleWebsiteRecover(website *model.Website, parentTask *task.Task, recover
if err != nil {
return err
}
if err = fileOp.CopyFile(fmt.Sprintf("%s/%s.conf", tmpPath, website.Alias), GetOpenrestyDir(SiteConfDir)); err != nil {
if err = fileOp.CopyFile(fmt.Sprintf("%s/%s.conf", tmpPath, website.Alias), GetConfDir(*website)); err != nil {
return err
}
@@ -259,7 +259,7 @@ func doWebsiteBackup(website *model.Website, parentTask *task.Task, backupDir, f
if err := fileOp.SaveFile(tmpDir+"/website.json", string(remarkInfo), fs.ModePerm); err != nil {
return err
}
nginxConfFile := GetSitePath(*website, SiteConf)
nginxConfFile := GetWebsiteConfigPath(*website)
if err := fileOp.CopyFile(nginxConfFile, tmpDir); err != nil {
return err
}
+224 -179
View File
@@ -19,12 +19,8 @@ import (
"syscall"
"time"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/gin-gonic/gin"
"github.com/pkg/errors"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/app/task"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/constant"
@@ -45,7 +41,9 @@ import (
"github.com/docker/docker/api/types/volume"
"github.com/docker/docker/client"
"github.com/docker/go-connections/nat"
"github.com/gin-gonic/gin"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/shirou/gopsutil/v4/cpu"
"github.com/shirou/gopsutil/v4/mem"
)
@@ -70,6 +68,7 @@ type IContainerService interface {
ContainerUpgrade(req dto.ContainerUpgrade) error
ContainerInfo(req dto.OperationWithName) (*dto.ContainerOperate, error)
ContainerListStats() ([]dto.ContainerListStats, error)
ContainerItemStats(req dto.OperationWithName) (dto.ContainerItemStats, error)
LoadResourceLimit() (*dto.ResourceLimit, error)
ContainerRename(req dto.ContainerRename) error
ContainerCommit(req dto.ContainerCommit) error
@@ -84,6 +83,7 @@ type IContainerService interface {
CreateVolume(req dto.VolumeCreate) error
TestCompose(req dto.ComposeCreate) (bool, error)
ComposeUpdate(req dto.ComposeUpdate) error
ComposeLogClean(req dto.ComposeLogClean) error
Prune(req dto.ContainerPrune) error
LoadUsers(req dto.OperationWithName) []string
@@ -96,19 +96,12 @@ func NewIContainerService() IContainerService {
}
func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, error) {
var (
records []container.Summary
list []container.Summary
)
client, err := docker.NewDockerClient()
if err != nil {
return 0, nil, err
}
defer client.Close()
options := container.ListOptions{
All: true,
Size: true,
}
options := container.ListOptions{All: true}
if len(req.Filters) != 0 {
options.Filters = filters.NewArgs()
options.Filters.Add("label", req.Filters)
@@ -117,114 +110,32 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
if err != nil {
return 0, nil, err
}
if req.ExcludeAppStore {
for _, item := range containers {
if created, ok := item.Labels[composeCreatedBy]; ok && created == "Apps" {
continue
}
list = append(list, item)
}
} else {
list = containers
}
records := searchWithFilter(req, containers)
if len(req.Name) != 0 {
length, count := len(list), 0
for count < length {
if !strings.Contains(list[count].Names[0][1:], req.Name) && !strings.Contains(list[count].Image, req.Name) {
list = append(list[:count], list[(count+1):]...)
length--
} else {
count++
}
}
}
if req.State != "all" {
length, count := len(list), 0
for count < length {
if list[count].State != req.State {
list = append(list[:count], list[(count+1):]...)
length--
} else {
count++
}
}
}
switch req.OrderBy {
case "name":
sort.Slice(list, func(i, j int) bool {
if req.Order == constant.OrderAsc {
return list[i].Names[0][1:] < list[j].Names[0][1:]
}
return list[i].Names[0][1:] > list[j].Names[0][1:]
})
default:
sort.Slice(list, func(i, j int) bool {
if req.Order == constant.OrderAsc {
return list[i].Created < list[j].Created
}
return list[i].Created > list[j].Created
})
}
total, start, end := len(list), (req.Page-1)*req.PageSize, req.Page*req.PageSize
var backData []dto.ContainerInfo
total, start, end := len(records), (req.Page-1)*req.PageSize, req.Page*req.PageSize
if start > total {
records = make([]container.Summary, 0)
backData = make([]dto.ContainerInfo, 0)
} else {
if end >= total {
end = total
}
records = list[start:end]
backData = records[start:end]
}
backDatas := make([]dto.ContainerInfo, len(records))
for i := 0; i < len(records); i++ {
item := records[i]
IsFromCompose := false
if _, ok := item.Labels[composeProjectLabel]; ok {
IsFromCompose = true
}
IsFromApp := false
if created, ok := item.Labels[composeCreatedBy]; ok && created == "Apps" {
IsFromApp = true
}
exposePorts := transPortToStr(records[i].Ports)
info := dto.ContainerInfo{
ContainerID: item.ID,
CreateTime: time.Unix(item.Created, 0).Format(constant.DateTimeLayout),
Name: item.Names[0][1:],
ImageId: strings.Split(item.ImageID, ":")[1],
ImageName: item.Image,
State: item.State,
RunTime: item.Status,
Ports: exposePorts,
IsFromApp: IsFromApp,
IsFromCompose: IsFromCompose,
SizeRw: item.SizeRw,
SizeRootFs: item.SizeRootFs,
}
install, _ := appInstallRepo.GetFirst(appInstallRepo.WithContainerName(info.Name))
for i := 0; i < len(backData); i++ {
install, _ := appInstallRepo.GetFirst(appInstallRepo.WithContainerName(backData[i].Name))
if install.ID > 0 {
info.AppInstallName = install.Name
info.AppName = install.App.Name
backData[i].AppInstallName = install.Name
backData[i].AppName = install.App.Name
websites, _ := websiteRepo.GetBy(websiteRepo.WithAppInstallId(install.ID))
for _, website := range websites {
info.Websites = append(info.Websites, website.PrimaryDomain)
backData[i].Websites = append(backData[i].Websites, website.PrimaryDomain)
}
}
backDatas[i] = info
if item.NetworkSettings != nil && len(item.NetworkSettings.Networks) > 0 {
networks := make([]string, 0, len(item.NetworkSettings.Networks))
for key := range item.NetworkSettings.Networks {
networks = append(networks, item.NetworkSettings.Networks[key].IPAddress)
}
sort.Strings(networks)
backDatas[i].Network = networks
}
}
return int64(total), backDatas, nil
return int64(total), backData, nil
}
func (u *ContainerService) List() []dto.ContainerOptions {
@@ -287,13 +198,60 @@ func (u *ContainerService) LoadStatus() (dto.ContainerStatus, error) {
defer client.Close()
c := context.Background()
usage, err := client.DiskUsage(c, types.DiskUsageOptions{})
images, _ := client.ImageList(c, image.ListOptions{All: true})
data.ImageCount = len(images)
repo, _ := imageRepoRepo.List()
data.RepoCount = len(repo)
templates, _ := composeRepo.List()
data.ComposeTemplateCount = len(templates)
networks, _ := client.NetworkList(c, network.ListOptions{})
data.NetworkCount = len(networks)
volumes, _ := client.VolumeList(c, volume.ListOptions{})
data.VolumeCount = len(volumes.Volumes)
data.ComposeCount = loadComposeCount(client)
containers, _ := client.ContainerList(c, container.ListOptions{All: true})
data.ContainerCount = len(containers)
for _, item := range containers {
switch item.State {
case "created":
data.Created++
case "running":
data.Running++
case "paused":
data.Paused++
case "restarting":
data.Restarting++
case "dead":
data.Dead++
case "exited":
data.Exited++
case "removing":
data.Removing++
}
}
return data, nil
}
func (u *ContainerService) ContainerItemStats(req dto.OperationWithName) (dto.ContainerItemStats, error) {
var data dto.ContainerItemStats
client, err := docker.NewDockerClient()
if err != nil {
return data, err
}
if req.Name != "system" {
defer client.Close()
containerInfo, _, err := client.ContainerInspectWithRaw(context.Background(), req.Name, true)
if err != nil {
return data, err
}
data.SizeRw = *containerInfo.SizeRw
data.SizeRootFs = *containerInfo.SizeRootFs
return data, nil
}
data.ImageCount = len(usage.Images)
data.VolumeCount = len(usage.Volumes)
usage, err := client.DiskUsage(context.Background(), types.DiskUsageOptions{})
if err != nil {
return data, err
}
for _, item := range usage.Images {
data.ImageUsage += item.Size
if item.Containers < 1 {
@@ -315,34 +273,6 @@ func (u *ContainerService) LoadStatus() (dto.ContainerStatus, error) {
for _, item := range usage.BuildCache {
data.BuildCacheUsage += item.Size
}
repo, _ := imageRepoRepo.List()
data.RepoCount = len(repo)
templates, _ := composeRepo.List()
data.ComposeTemplateCount = len(templates)
networks, _ := client.NetworkList(c, network.ListOptions{})
data.NetworkCount = len(networks)
volumes, _ := client.VolumeList(c, volume.ListOptions{})
data.VolumeCount = len(volumes.Volumes)
data.ComposeCount = loadComposeCount(client)
data.ContainerCount = len(usage.Containers)
for _, item := range usage.Containers {
switch item.State {
case "created":
data.Created++
case "running":
data.Running++
case "paused":
data.Paused++
case "restarting":
data.Restarting++
case "dead":
data.Dead++
case "exited":
data.Exited++
case "removing":
data.Removing++
}
}
return data, nil
}
func (u *ContainerService) ContainerListStats() ([]dto.ContainerListStats, error) {
@@ -430,6 +360,9 @@ func (u *ContainerService) Inspect(req dto.InspectReq) (string, error) {
}
for _, container := range containers {
config := container.Labels[composeConfigLabel]
if len(req.Detail) != 0 && strings.Contains(config, req.Detail) {
config = req.Detail
}
workdir := container.Labels[composeWorkdirLabel]
if len(config) != 0 && len(workdir) != 0 && strings.Contains(config, workdir) {
filePath = config
@@ -667,27 +600,27 @@ func (u *ContainerService) ContainerInfo(req dto.OperationWithName) (*dto.Contai
data.Name = strings.ReplaceAll(oldContainer.Name, "/", "")
data.Image = oldContainer.Config.Image
if oldContainer.NetworkSettings != nil {
for network := range oldContainer.NetworkSettings.Networks {
data.Network = network
break
for net, val := range oldContainer.NetworkSettings.Networks {
netItem := dto.ContainerNetwork{
Network: net,
MacAddr: val.MacAddress,
}
if val.IPAMConfig != nil {
if netItem.Network != "bridge" {
netItem.Ipv4 = val.IPAMConfig.IPv4Address
netItem.Ipv6 = val.IPAMConfig.IPv6Address
}
} else {
if netItem.Network != "bridge" {
netItem.Ipv4 = val.IPAddress
}
}
data.Networks = append(data.Networks, netItem)
}
}
exposePorts, _ := loadPortByInspect(oldContainer.ID, client)
data.ExposedPorts = loadContainerPortForInfo(exposePorts)
networkSettings := oldContainer.NetworkSettings
bridgeNetworkSettings := networkSettings.Networks[data.Network]
if bridgeNetworkSettings.IPAMConfig != nil {
data.MacAddr = bridgeNetworkSettings.MacAddress
if data.Network != "bridge" {
data.Ipv4 = bridgeNetworkSettings.IPAMConfig.IPv4Address
data.Ipv6 = bridgeNetworkSettings.IPAMConfig.IPv6Address
}
} else {
if data.Network != "bridge" {
data.Ipv4 = bridgeNetworkSettings.IPAddress
}
}
data.Hostname = oldContainer.Config.Hostname
data.DNS = oldContainer.HostConfig.DNS
data.DomainName = oldContainer.Config.Domainname
@@ -1037,12 +970,18 @@ func collectLogs(done <-chan struct{}, params dto.StreamLog, messageChan chan<-
var dockerCmd *exec.Cmd
if params.Type == "compose" {
dockerComposCmd := common.GetDockerComposeCommand()
if dockerComposCmd == "docker-compose" {
newCmdArgs := append([]string{"-f", params.Compose}, cmdArgs...)
dockerCmd = exec.Command(dockerComposCmd, newCmdArgs...)
dockerComposeCmd := common.GetDockerComposeCommand()
var yamlFiles []string
for _, item := range strings.Split(params.Compose, ",") {
if len(item) != 0 {
yamlFiles = append(yamlFiles, "-f", item)
}
}
if dockerComposeCmd == "docker-compose" {
newCmdArgs := append(yamlFiles, cmdArgs...)
dockerCmd = exec.Command(dockerComposeCmd, newCmdArgs...)
} else {
newCmdArgs := append([]string{"compose", "-f", params.Compose}, cmdArgs...)
newCmdArgs := append(append([]string{"compose"}, yamlFiles...), cmdArgs...)
dockerCmd = exec.Command("docker", newCmdArgs...)
}
} else {
@@ -1122,10 +1061,16 @@ func (u *ContainerService) DownloadContainerLogs(containerType, container, since
commandArg := []string{"logs", container}
dockerCommand := global.CONF.DockerConfig.Command
if containerType == "compose" {
var yamlFiles []string
for _, item := range strings.Split(container, ",") {
if len(item) != 0 {
yamlFiles = append(yamlFiles, "-f", item)
}
}
if dockerCommand == "docker-compose" {
commandArg = []string{"-f", container, "logs"}
commandArg = append(yamlFiles, "logs")
} else {
commandArg = []string{"compose", "-f", container, "logs"}
commandArg = append(append([]string{"compose"}, yamlFiles...), "logs")
}
}
@@ -1212,10 +1157,8 @@ func (u *ContainerService) ContainerStats(id string) (*dto.ContainerStats, error
body, err := io.ReadAll(res.Body)
if err != nil {
res.Body.Close()
return nil, err
}
res.Body.Close()
var stats *container.StatsResponse
if err := json.Unmarshal(body, &stats); err != nil {
return nil, err
@@ -1505,25 +1448,25 @@ func loadConfigInfo(isCreate bool, req dto.ContainerOperate, oldContainer *conta
config.User = req.User
config.WorkingDir = req.WorkingDir
if len(req.Network) != 0 {
switch req.Network {
case "host", "none", "bridge":
hostConf.NetworkMode = container.NetworkMode(req.Network)
}
if req.Ipv4 != "" || req.Ipv6 != "" {
networkConf.EndpointsConfig = map[string]*network.EndpointSettings{req.Network: {
IPAMConfig: &network.EndpointIPAMConfig{
IPv4Address: req.Ipv4,
IPv6Address: req.Ipv6,
}, MacAddress: req.MacAddr}}
} else {
networkConf.EndpointsConfig = map[string]*network.EndpointSettings{req.Network: {}}
if len(req.Networks) != 0 {
networkConf.EndpointsConfig = make(map[string]*network.EndpointSettings)
for _, item := range req.Networks {
switch item.Network {
case "host", "none", "bridge":
hostConf.NetworkMode = container.NetworkMode(item.Network)
}
if item.Ipv4 != "" || item.Ipv6 != "" {
networkConf.EndpointsConfig[item.Network] = &network.EndpointSettings{
IPAMConfig: &network.EndpointIPAMConfig{
IPv4Address: item.Ipv4,
IPv6Address: item.Ipv6,
}, MacAddress: item.MacAddr}
} else {
networkConf.EndpointsConfig[item.Network] = &network.EndpointSettings{}
}
}
} else {
if req.Ipv4 != "" || req.Ipv6 != "" {
return nil, nil, nil, fmt.Errorf("please set up the network")
}
networkConf = network.NetworkingConfig{}
return nil, nil, nil, fmt.Errorf("please set up the network")
}
hostConf.Privileged = req.Privileged
@@ -1771,3 +1714,105 @@ func loadContainerPortForInfo(itemPorts []container.Port) []dto.PortHelper {
}
return exposedPorts
}
func searchWithFilter(req dto.PageContainer, containers []container.Summary) []dto.ContainerInfo {
var (
records []dto.ContainerInfo
list []container.Summary
)
if req.ExcludeAppStore {
for _, item := range containers {
if created, ok := item.Labels[composeCreatedBy]; ok && created == "Apps" {
continue
}
list = append(list, item)
}
} else {
list = containers
}
if len(req.Name) != 0 {
length, count := len(list), 0
for count < length {
if !strings.Contains(list[count].Names[0][1:], req.Name) && !strings.Contains(list[count].Image, req.Name) {
list = append(list[:count], list[(count+1):]...)
length--
} else {
count++
}
}
}
if req.State != "all" {
length, count := len(list), 0
for count < length {
if list[count].State != req.State {
list = append(list[:count], list[(count+1):]...)
length--
} else {
count++
}
}
}
switch req.OrderBy {
case "name":
sort.Slice(list, func(i, j int) bool {
if req.Order == constant.OrderAsc {
return list[i].Names[0][1:] < list[j].Names[0][1:]
}
return list[i].Names[0][1:] > list[j].Names[0][1:]
})
default:
sort.Slice(list, func(i, j int) bool {
if req.Order == constant.OrderAsc {
return list[i].Created < list[j].Created
}
return list[i].Created > list[j].Created
})
}
for _, item := range list {
IsFromCompose := false
if _, ok := item.Labels[composeProjectLabel]; ok {
IsFromCompose = true
}
IsFromApp := false
if created, ok := item.Labels[composeCreatedBy]; ok && created == "Apps" {
IsFromApp = true
}
exposePorts := transPortToStr(item.Ports)
info := dto.ContainerInfo{
ContainerID: item.ID,
CreateTime: time.Unix(item.Created, 0).Format(constant.DateTimeLayout),
Name: item.Names[0][1:],
Ports: exposePorts,
ImageId: strings.Split(item.ImageID, ":")[1],
ImageName: item.Image,
State: item.State,
RunTime: item.Status,
IsFromApp: IsFromApp,
IsFromCompose: IsFromCompose,
}
if item.NetworkSettings != nil && len(item.NetworkSettings.Networks) > 0 {
networks := make([]string, 0, len(item.NetworkSettings.Networks))
for key := range item.NetworkSettings.Networks {
networks = append(networks, item.NetworkSettings.Networks[key].IPAddress)
}
sort.Strings(networks)
info.Network = networks
}
records = append(records, info)
}
descriptions, _ := settingRepo.GetDescriptionList(repo.WithByType("container"))
for i := 0; i < len(records); i++ {
for _, desc := range descriptions {
if desc.ID == records[i].ContainerID {
records[i].Description = desc.Description
records[i].IsPinned = desc.IsPinned
}
}
}
sort.Slice(records, func(i, j int) bool {
return records[i].IsPinned && !records[j].IsPinned
})
return records
}
+86 -45
View File
@@ -2,10 +2,12 @@ package service
import (
"bufio"
"context"
"errors"
"fmt"
"os"
"path"
"path/filepath"
"sort"
"strings"
"time"
@@ -23,7 +25,6 @@ import (
"github.com/1Panel-dev/1Panel/agent/utils/docker"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
"golang.org/x/net/context"
)
const composeProjectLabel = "com.docker.compose.project"
@@ -73,6 +74,7 @@ func (u *ContainerService) PageCompose(req dto.SearchWithPage) (int64, interface
Name: container.Names[0][1:],
State: container.State,
CreateTime: time.Unix(container.Created, 0).Format(constant.DateTimeLayout),
Ports: transPortToStr(container.Ports),
}
if compose, has := composeMap[name]; has {
compose.ContainerCount++
@@ -179,7 +181,7 @@ func (u *ContainerService) TestCompose(req dto.ComposeCreate) (bool, error) {
cmd := getComposeCmd(req.Path, "config")
stdout, err := cmd.CombinedOutput()
if err != nil {
return false, errors.New(string(stdout))
return false, fmt.Errorf("docker-compose config failed, std: %s, err: %v", string(stdout), err)
}
return true, nil
}
@@ -203,14 +205,13 @@ func (u *ContainerService) CreateCompose(req dto.ComposeCreate) error {
}
go func() {
taskItem.AddSubTask(i18n.GetMsgByKey("ComposeCreate"), func(t *task.Task) error {
cmd := getComposeCmd(req.Path, "up")
out, err := cmd.CombinedOutput()
taskItem.Log(i18n.GetWithName("ComposeCreateRes", string(out)))
err := compose.UpWithTask(req.Path, t)
t.LogWithStatus(i18n.GetMsgByKey("ComposeCreate"), err)
if err != nil {
_, _ = compose.Down(req.Path)
return err
}
_ = composeRepo.CreateRecord(&model.Compose{Name: req.Name, Path: req.Path})
_ = composeRepo.CreateRecord(&model.Compose{Name: strings.ToLower(req.Name), Path: req.Path})
return nil
}, nil)
_ = taskItem.Execute()
@@ -232,24 +233,24 @@ func (u *ContainerService) ComposeOperation(req dto.ComposeOperation) error {
return err
}
if req.WithFile {
_ = os.RemoveAll(path.Dir(req.Path))
for _, item := range strings.Split(req.Path, ",") {
if len(item) != 0 {
_ = os.RemoveAll(path.Dir(item))
}
}
}
_ = composeRepo.DeleteRecord(repo.WithByName(req.Name))
return nil
}
if _, err := os.Stat(req.Path); err != nil {
return fmt.Errorf("load file with path %s failed, %v", req.Path, err)
}
if req.Operation == "up" {
if stdout, err := compose.Up(req.Path); err != nil {
return errors.New(string(stdout))
return fmt.Errorf("docker-compose up failed, std: %s, err: %v", stdout, err)
}
} else {
if stdout, err := compose.Operate(req.Path, req.Operation); err != nil {
return errors.New(string(stdout))
return fmt.Errorf("docker-compose %s failed, std: %s, err: %v", req.Operation, stdout, err)
}
}
global.LOG.Infof("docker-compose %s %s successful", req.Operation, req.Name)
return nil
}
@@ -257,11 +258,11 @@ func (u *ContainerService) ComposeUpdate(req dto.ComposeUpdate) error {
if cmd.CheckIllegal(req.Name, req.Path) {
return buserr.New("ErrCmdIllegal")
}
oldFile, err := os.ReadFile(req.Path)
oldFile, err := os.ReadFile(req.DetailPath)
if err != nil {
return fmt.Errorf("load file with path %s failed, %v", req.Path, err)
return fmt.Errorf("load file with path %s failed, %v", req.DetailPath, err)
}
file, err := os.OpenFile(req.Path, os.O_WRONLY|os.O_TRUNC, 0640)
file, err := os.OpenFile(req.DetailPath, os.O_WRONLY|os.O_TRUNC, 0640)
if err != nil {
return err
}
@@ -270,21 +271,73 @@ func (u *ContainerService) ComposeUpdate(req dto.ComposeUpdate) error {
_, _ = write.WriteString(req.Content)
write.Flush()
global.LOG.Infof("docker-compose.yml %s has been replaced, now start to docker-compose restart", req.Path)
if err := newComposeEnv(req.Path, req.Env); err != nil {
global.LOG.Infof("docker-compose.yml %s has been replaced, now start to docker-compose restart", req.DetailPath)
if err := newComposeEnv(req.DetailPath, req.Env); err != nil {
return err
}
if stdout, err := compose.Up(req.Path); err != nil {
global.LOG.Errorf("update failed when handle compose up, std: %s, err: %s, now try to recreate the old compose file", stdout, err)
if err := recreateCompose(string(oldFile), req.Path); err != nil {
return fmt.Errorf("update failed when handle compose up, err: %s, recreate failed: %v", string(stdout), err)
return fmt.Errorf("update failed and recreate old compose file also failed, err: %v", err)
}
return fmt.Errorf("update failed when handle compose up, err: %s", string(stdout))
return fmt.Errorf("update failed when handle compose up, std: %v, err: %s", stdout, err)
}
return nil
}
func (u *ContainerService) ComposeLogClean(req dto.ComposeLogClean) error {
client, err := docker.NewDockerClient()
if err != nil {
return err
}
defer client.Close()
options := container.ListOptions{All: true}
options.Filters = filters.NewArgs()
options.Filters.Add("label", composeProjectLabel)
list, err := client.ContainerList(context.Background(), options)
if err != nil {
return err
}
ctx := context.Background()
for _, item := range list {
if name, ok := item.Labels[composeProjectLabel]; ok {
if name != req.Name {
continue
}
containerItem, err := client.ContainerInspect(ctx, item.ID)
if err != nil {
return err
}
if err := client.ContainerStop(ctx, containerItem.ID, container.StopOptions{}); err != nil {
return err
}
file, err := os.OpenFile(containerItem.LogPath, os.O_RDWR|os.O_CREATE, constant.FilePerm)
if err != nil {
return err
}
defer file.Close()
if err = file.Truncate(0); err != nil {
return err
}
_, _ = file.Seek(0, 0)
files, _ := filepath.Glob(fmt.Sprintf("%s.*", containerItem.LogPath))
for _, file := range files {
_ = os.Remove(file)
}
}
}
return u.ComposeOperation(dto.ComposeOperation{
Name: req.Name,
Path: req.Path,
Operation: "restart",
})
}
func (u *ContainerService) loadPath(req *dto.ComposeCreate) error {
if req.From == "template" || req.From == "edit" {
dir := fmt.Sprintf("%s/docker/compose/%s", global.Dir.DataDir, req.Name)
@@ -309,11 +362,8 @@ func (u *ContainerService) loadPath(req *dto.ComposeCreate) error {
}
func removeContainerForCompose(composeName, composePath string) error {
if _, err := os.Stat(composePath); err == nil {
if stdout, err := compose.Operate(composePath, "down"); err != nil {
return errors.New(stdout)
}
return nil
if stdout, err := compose.Operate(composePath, "down"); err != nil {
return errors.New(stdout)
}
var options container.ListOptions
options.All = true
@@ -353,40 +403,31 @@ func recreateCompose(content, path string) error {
func loadEnv(list []dto.ComposeInfo) []dto.ComposeInfo {
for i := 0; i < len(list); i++ {
envFilePath := path.Join(path.Dir(list[i].Path), "1panel.env")
tmpPath := list[i].Path
if strings.Contains(list[i].Path, ",") {
tmpPath = strings.Split(list[i].Path, ",")[0]
}
envFilePath := path.Join(path.Dir(tmpPath), ".env")
file, err := os.ReadFile(envFilePath)
if err != nil {
continue
}
lines := strings.Split(string(file), "\n")
for _, line := range lines {
lineItem := strings.TrimSpace(line)
if len(lineItem) != 0 && !strings.HasPrefix(lineItem, "#") {
list[i].Env = append(list[i].Env, lineItem)
}
}
list[i].Env = string(file)
}
return list
}
func newComposeEnv(pathItem string, env []string) error {
if len(env) == 0 {
return nil
}
envFilePath := path.Join(path.Dir(pathItem), "1panel.env")
func newComposeEnv(pathItem string, env string) error {
envFilePath := path.Join(path.Dir(pathItem), ".env")
file, err := os.OpenFile(envFilePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, constant.FilePerm)
if err != nil {
global.LOG.Errorf("failed to create env file: %v", err)
return err
}
defer file.Close()
for _, env := range env {
envItem := strings.TrimSpace(env)
if _, err := file.WriteString(fmt.Sprintf("%s\n", envItem)); err != nil {
global.LOG.Errorf("failed to write env to file: %v", err)
return err
}
if _, err := file.WriteString(env); err != nil {
global.LOG.Errorf("failed to write env to file: %v", err)
return err
}
global.LOG.Infof("1panel.env file successfully created or updated with env variables in %s", envFilePath)
return nil
}
+3 -6
View File
@@ -117,10 +117,8 @@ func (u *ContainerService) CreateNetwork(req dto.NetworkCreate) error {
return err
}
defer client.Close()
var (
ipams []network.IPAMConfig
enableV6 bool
)
var ipams []network.IPAMConfig
if req.Ipv4 {
var itemIpam network.IPAMConfig
if len(req.AuxAddress) != 0 {
@@ -141,7 +139,6 @@ func (u *ContainerService) CreateNetwork(req dto.NetworkCreate) error {
ipams = append(ipams, itemIpam)
}
if req.Ipv6 {
enableV6 = true
var itemIpam network.IPAMConfig
if len(req.AuxAddress) != 0 {
itemIpam.AuxAddress = make(map[string]string)
@@ -162,7 +159,7 @@ func (u *ContainerService) CreateNetwork(req dto.NetworkCreate) error {
}
options := network.CreateOptions{
EnableIPv6: &enableV6,
EnableIPv6: &req.Ipv6,
Driver: req.Driver,
Options: stringsToMap(req.Options),
Labels: stringsToMap(req.Labels),
+2 -15
View File
@@ -5,7 +5,6 @@ import (
"sort"
"strings"
"time"
"unicode"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/buserr"
@@ -20,6 +19,7 @@ func (u *ContainerService) PageVolume(req dto.SearchWithPage) (int64, interface{
if err != nil {
return 0, nil, err
}
defer client.Close()
list, err := client.VolumeList(context.TODO(), volume.ListOptions{})
if err != nil {
return 0, nil, err
@@ -57,7 +57,7 @@ func (u *ContainerService) PageVolume(req dto.SearchWithPage) (int64, interface{
var volume dto.Volume
volume.Driver = item.Driver
volume.Mountpoint = item.Mountpoint
volume.Name = simplifyVolumeName(item.Name)
volume.Name = item.Name
for key, val := range item.Labels {
volume.Labels = append(volume.Labels, dto.VolumeOption{Key: key, Value: val})
}
@@ -143,16 +143,3 @@ func (u *ContainerService) CreateVolume(req dto.VolumeCreate) error {
}
return nil
}
func simplifyVolumeName(name string) string {
if len(name) != 64 {
return name
}
for _, char := range name {
if !unicode.Is(unicode.ASCII_Hex_Digit, char) {
return name
}
}
return name[:12]
}
+7 -7
View File
@@ -447,7 +447,7 @@ func (u *CronjobService) SearchRecords(search dto.SearchRecord) (int64, interfac
func (u *CronjobService) LoadNextHandle(specStr string) ([]string, error) {
spec := cron.NewParser(cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow)
now := time.Now()
var nexts [5]string
var next [5]string
if strings.HasPrefix(specStr, "@every ") {
duration := time.Minute
if strings.HasSuffix(specStr, "s") {
@@ -462,10 +462,10 @@ func (u *CronjobService) LoadNextHandle(specStr string) ([]string, error) {
}
for i := 0; i < 5; i++ {
nextTime := now.Add(time.Duration(durationItem) * duration)
nexts[i] = nextTime.Format(constant.DateTimeLayout)
next[i] = nextTime.Format(constant.DateTimeLayout)
now = nextTime
}
return nexts[:], nil
return next[:], nil
}
sched, err := spec.Parse(specStr)
if err != nil {
@@ -473,10 +473,10 @@ func (u *CronjobService) LoadNextHandle(specStr string) ([]string, error) {
}
for i := 0; i < 5; i++ {
nextTime := sched.Next(now)
nexts[i] = nextTime.Format(constant.DateTimeLayout)
next[i] = nextTime.Format(constant.DateTimeLayout)
now = nextTime
}
return nexts[:], nil
return next[:], nil
}
func (u *CronjobService) LoadRecordLog(req dto.OperateByID) string {
@@ -633,8 +633,8 @@ func (u *CronjobService) HandleStop(id uint) error {
if len(record.TaskID) == 0 {
return nil
}
if cancle, ok := global.TaskCtxMap[record.TaskID]; ok {
cancle()
if cancel, ok := global.TaskCtxMap[record.TaskID]; ok {
cancel()
}
return nil
}
+13 -13
View File
@@ -13,6 +13,7 @@ import (
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/app/task"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/i18n"
"github.com/1Panel-dev/1Panel/agent/app/dto"
@@ -21,7 +22,6 @@ import (
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/common"
"github.com/1Panel-dev/1Panel/agent/utils/files"
"github.com/pkg/errors"
)
func (u *CronjobService) handleApp(cronjob model.Cronjob, startTime time.Time, taskItem *task.Task) error {
@@ -32,7 +32,7 @@ func (u *CronjobService) handleApp(cronjob model.Cronjob, startTime time.Time, t
}
accountMap := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
if !accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].isOk {
return errors.New(i18n.GetMsgWithDetail("LoadBackupFailed", accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].message))
return buserr.New(i18n.GetMsgWithDetail("LoadBackupFailed", accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].message))
}
for _, app := range apps {
retry := 0
@@ -90,7 +90,7 @@ func (u *CronjobService) handleWebsite(cronjob model.Cronjob, startTime time.Tim
}
accountMap := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
if !accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].isOk {
return errors.New(i18n.GetMsgWithDetail("LoadBackupFailed", accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].message))
return buserr.New(i18n.GetMsgWithDetail("LoadBackupFailed", accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].message))
}
for _, web := range webs {
retry := 0
@@ -149,7 +149,7 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, startTime time.Ti
}
accountMap := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
if !accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].isOk {
return errors.New(i18n.GetMsgWithDetail("LoadBackupFailed", accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].message))
return buserr.New(i18n.GetMsgWithDetail("LoadBackupFailed", accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].message))
}
for _, dbInfo := range dbs {
retry := 0
@@ -178,7 +178,7 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, startTime time.Ti
}
}
} else {
if err := doPostgresqlgBackup(dbInfo, backupDir, record.FileName, cronjob.Secret); err != nil {
if err := doPostgresqlBackup(dbInfo, backupDir, record.FileName, cronjob.Secret, taskItem); err != nil {
if retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {
retry++
return err
@@ -217,7 +217,7 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, startTime time.Ti
func (u *CronjobService) handleDirectory(cronjob model.Cronjob, startTime time.Time, taskItem *task.Task) error {
accountMap := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
if !accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].isOk {
return errors.New(i18n.GetMsgWithDetail("LoadBackupFailed", accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].message))
return buserr.New(i18n.GetMsgWithDetail("LoadBackupFailed", accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].message))
}
taskItem.AddSubTaskWithOps(task.GetTaskName(cronjob.SourceDir, task.TaskBackup, task.TaskScopeCronjob), func(task *task.Task) error {
fileName := fmt.Sprintf("%s.tar.gz", startTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(2))
@@ -267,7 +267,7 @@ func (u *CronjobService) handleDirectory(cronjob model.Cronjob, startTime time.T
func (u *CronjobService) handleSystemLog(cronjob model.Cronjob, startTime time.Time, taskItem *task.Task) error {
accountMap := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
if !accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].isOk {
return errors.New(i18n.GetMsgWithDetail("LoadBackupFailed", accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].message))
return buserr.New(i18n.GetMsgWithDetail("LoadBackupFailed", accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].message))
}
taskItem.AddSubTaskWithOps(task.GetTaskName(i18n.GetMsgByKey("SystemLog"), task.TaskBackup, task.TaskScopeCronjob), func(task *task.Task) error {
nameItem := startTime.Format(constant.DateTimeSlimLayout) + common.RandStrAndNum(5)
@@ -303,7 +303,7 @@ func (u *CronjobService) handleSystemLog(cronjob model.Cronjob, startTime time.T
func (u *CronjobService) handleSnapshot(cronjob model.Cronjob, jobRecord model.JobRecords, taskItem *task.Task) error {
accountMap := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
if !accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].isOk {
return errors.New(i18n.GetMsgWithDetail("LoadBackupFailed", accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].message))
return buserr.New(i18n.GetMsgWithDetail("LoadBackupFailed", accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].message))
}
var record model.BackupRecord
record.Status = constant.StatusSuccess
@@ -440,10 +440,10 @@ func loadDbsForJob(cronjob model.Cronjob) []DatabaseHelper {
}
func loadWebsForJob(cronjob model.Cronjob) []model.Website {
var weblist []model.Website
var list []model.Website
if cronjob.Website == "all" {
weblist, _ = websiteRepo.List()
return weblist
list, _ = websiteRepo.List()
return list
}
websites := strings.Split(cronjob.Website, ",")
var idItems []uint
@@ -451,8 +451,8 @@ func loadWebsForJob(cronjob model.Cronjob) []model.Website {
itemID, _ := strconv.Atoi(websites[i])
idItems = append(idItems, uint(itemID))
}
weblist, _ = websiteRepo.GetBy(repo.WithByIDs(idItems))
return weblist
list, _ = websiteRepo.GetBy(repo.WithByIDs(idItems))
return list
}
func handleBackupLogs(taskItem *task.Task, targetDir, fileName string, secret string) error {
+24 -14
View File
@@ -8,14 +8,13 @@ import (
"io"
"net/http"
"os"
"path"
pathUtils "path"
"path/filepath"
"strings"
"time"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/utils/alert_push"
"github.com/pkg/errors"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/model"
@@ -65,7 +64,7 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
return
}
if err = u.loadTask(cronjob, &record, taskItem); err != nil {
global.LOG.Debugf("preper to handle cron job [%s] %s failed, err: %v", cronjob.Type, cronjob.Name, err)
global.LOG.Debugf("prepare to handle cron job [%s] %s failed, err: %v", cronjob.Type, cronjob.Name, err)
item, _ := taskRepo.GetFirst(taskRepo.WithByID(record.TaskID))
if len(item.ID) == 0 {
record.TaskID = ""
@@ -147,7 +146,7 @@ func (u *CronjobService) handleShell(cronjob model.Cronjob, taskItem *task.Task)
if len(cronjob.ContainerName) != 0 {
scriptItem := cronjob.Script
if cronjob.ScriptMode == "select" {
scriptItem = path.Join("/tmp", path.Base(cronjob.Script))
scriptItem = pathUtils.Join("/tmp", pathUtils.Base(cronjob.Script))
if err := cmdMgr.Run("docker", "cp", cronjob.Script, cronjob.ContainerName+":"+scriptItem); err != nil {
return err
}
@@ -195,10 +194,17 @@ func (u *CronjobService) handleShell(cronjob model.Cronjob, taskItem *task.Task)
}
func (u *CronjobService) handleCurl(cronjob model.Cronjob, taskItem *task.Task) {
taskItem.AddSubTaskWithOps(i18n.GetWithName("HandleShell", cronjob.Name), func(t *task.Task) error {
cmdMgr := cmd.NewCommandMgr(cmd.WithTask(*taskItem))
return cmdMgr.Run("curl", cronjob.URL)
}, nil, int(cronjob.RetryTimes), time.Duration(cronjob.Timeout)*time.Second)
urls := strings.Split(cronjob.URL, ",")
for _, url := range urls {
if len(strings.TrimSpace(url)) == 0 {
continue
}
taskItem.AddSubTaskWithOps(i18n.GetWithName("HandleCurl", url), func(t *task.Task) error {
taskItem.LogStart(i18n.GetWithName("HandleCurl", url))
cmdMgr := cmd.NewCommandMgr(cmd.WithTask(*taskItem))
return cmdMgr.Run("curl", url)
}, nil, int(cronjob.RetryTimes), time.Duration(cronjob.Timeout)*time.Second)
}
}
func (u *CronjobService) handleNtpSync(cronjob model.Cronjob, taskItem *task.Task) {
@@ -245,11 +251,11 @@ func (u *CronjobService) handleCleanLog(cronjob model.Cronjob, taskItem *task.Ta
if appInstall.ID > 0 {
curStr := i18n.GetWithName("CleanLogByName", "OpenResty")
t.LogStart(curStr)
accessLogPath := path.Join(appInstall.GetPath(), "log", "access.log")
accessLogPath := pathUtils.Join(appInstall.GetPath(), "log", "access.log")
if err := os.Truncate(accessLogPath, 0); err != nil {
t.LogFailedWithErr(curStr, err)
}
errLogPath := path.Join(appInstall.GetPath(), "log", "error.log")
errLogPath := pathUtils.Join(appInstall.GetPath(), "log", "error.log")
if err := os.Truncate(errLogPath, 0); err != nil {
t.LogFailedWithErr(curStr, err)
}
@@ -267,8 +273,12 @@ func (u *CronjobService) handleSyncIpGroup(cronjob model.Cronjob, taskItem *task
if err != nil {
return err
}
ipGroupDir := path.Join(appInstall.GetPath(), "1pwaf", "data", "rules", "ip_group")
urlDir := path.Join(ipGroupDir, "ip_group_url")
ipGroupDir := pathUtils.Join(appInstall.GetPath(), "1pwaf", "data", "rules", "ip_group")
urlDir := pathUtils.Join(ipGroupDir, "ip_group_url")
_, err = os.Stat(urlDir)
if err != nil && os.IsNotExist(err) {
return buserr.New("WafIpGroupNotFound")
}
urlsFiles, err := os.ReadDir(urlDir)
if err != nil {
@@ -332,7 +342,7 @@ func (u *CronjobService) handleSyncIpGroup(cronjob model.Cronjob, taskItem *task
func (u *CronjobService) handleCutWebsiteLog(cronjob *model.Cronjob, startTime time.Time, taskItem *task.Task) error {
clientMap := NewBackupClientMap([]string{fmt.Sprintf("%v", cronjob.DownloadAccountID)})
if !clientMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].isOk {
return errors.New(i18n.GetMsgWithDetail("LoadBackupFailed", clientMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].message))
return buserr.New(i18n.GetMsgWithDetail("LoadBackupFailed", clientMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].message))
}
taskItem.AddSubTaskWithOps(i18n.GetWithName("CutWebsiteLog", cronjob.Name), func(t *task.Task) error {
websites := loadWebsForJob(*cronjob)
@@ -464,7 +474,7 @@ func (u *CronjobService) removeExpiredLog(cronjob model.Cronjob) {
}
_ = cronjobRepo.DeleteRecord(repo.WithByID(records[i].ID))
_ = taskRepo.Delete(taskRepo.WithByID(records[i].TaskID))
_ = os.Remove(path.Join(global.CONF.Base.InstallDir, "1panel/log/task/Cronjob", records[i].TaskID+".log"))
_ = os.Remove(pathUtils.Join(global.CONF.Base.InstallDir, "1panel/log/task/Cronjob", records[i].TaskID+".log"))
}
}
+86 -56
View File
@@ -1,6 +1,7 @@
package service
import (
"cmp"
"context"
"encoding/json"
"fmt"
@@ -23,10 +24,9 @@ import (
"github.com/1Panel-dev/1Panel/agent/utils/common"
"github.com/1Panel-dev/1Panel/agent/utils/controller"
"github.com/1Panel-dev/1Panel/agent/utils/copier"
"github.com/1Panel-dev/1Panel/agent/utils/psutil"
"github.com/gin-gonic/gin"
"github.com/shirou/gopsutil/v4/cpu"
"github.com/shirou/gopsutil/v4/disk"
"github.com/shirou/gopsutil/v4/host"
"github.com/shirou/gopsutil/v4/load"
"github.com/shirou/gopsutil/v4/mem"
"github.com/shirou/gopsutil/v4/net"
@@ -39,6 +39,8 @@ type IDashboardService interface {
LoadBaseInfo(ioOption string, netOption string) (*dto.DashboardBase, error)
LoadCurrentInfoForNode() *dto.NodeCurrent
LoadCurrentInfo(ioOption string, netOption string) *dto.DashboardCurrent
LoadTopCPU() []dto.Process
LoadTopMem() []dto.Process
LoadQuickOptions() []dto.QuickJump
ChangeQuick(req dto.ChangeQuicks) error
@@ -77,7 +79,7 @@ func (u *DashboardService) Restart(operation string) error {
func (u *DashboardService) LoadOsInfo() (*dto.OsInfo, error) {
var baseInfo dto.OsInfo
hostInfo, err := host.Info()
hostInfo, err := psutil.HOST.GetHostInfo(false)
if err != nil {
return nil, err
}
@@ -86,8 +88,9 @@ func (u *DashboardService) LoadOsInfo() (*dto.OsInfo, error) {
baseInfo.PlatformFamily = hostInfo.PlatformFamily
baseInfo.KernelArch = hostInfo.KernelArch
baseInfo.KernelVersion = hostInfo.KernelVersion
baseInfo.PrettyDistro = psutil.HOST.GetDistro()
diskInfo, err := disk.Usage(global.Dir.BaseDir)
diskInfo, err := psutil.DISK.GetUsage(global.Dir.BaseDir, false)
if err == nil {
baseInfo.DiskSize = int64(diskInfo.Free)
}
@@ -104,12 +107,17 @@ func (u *DashboardService) LoadOsInfo() (*dto.OsInfo, error) {
func (u *DashboardService) LoadCurrentInfoForNode() *dto.NodeCurrent {
var currentInfo dto.NodeCurrent
currentInfo.CPUTotal, _ = cpu.Counts(true)
totalPercent, _ := cpu.Percent(100*time.Millisecond, false)
if len(totalPercent) == 1 {
currentInfo.CPUUsedPercent = totalPercent[0]
currentInfo.CPUUsed = currentInfo.CPUUsedPercent * 0.01 * float64(currentInfo.CPUTotal)
currentInfo.CPUTotal, _ = psutil.CPUInfo.GetLogicalCores(false)
cpuUsedPercent, perCore, cpuDetailedPercent := psutil.CPU.GetCPUUsage()
if len(perCore) == 0 {
currentInfo.CPUTotal = psutil.CPU.NumCPU()
} else {
currentInfo.CPUTotal = len(perCore)
}
currentInfo.CPUUsedPercent = cpuUsedPercent
currentInfo.CPUUsed = cpuUsedPercent * 0.01 * float64(currentInfo.CPUTotal)
currentInfo.CPUDetailedPercent = cpuDetailedPercent
loadInfo, _ := load.Avg()
currentInfo.Load1 = loadInfo.Load1
@@ -134,38 +142,39 @@ func (u *DashboardService) LoadCurrentInfoForNode() *dto.NodeCurrent {
func (u *DashboardService) LoadBaseInfo(ioOption string, netOption string) (*dto.DashboardBase, error) {
var baseInfo dto.DashboardBase
hostInfo, err := host.Info()
hostInfo, err := psutil.HOST.GetHostInfo(false)
if err != nil {
return nil, err
}
baseInfo.Hostname = hostInfo.Hostname
baseInfo.OS = hostInfo.OS
baseInfo.Platform = hostInfo.Platform
baseInfo.PlatformFamily = hostInfo.PlatformFamily
baseInfo.PlatformVersion = hostInfo.PlatformVersion
baseInfo.KernelArch = hostInfo.KernelArch
baseInfo.KernelVersion = hostInfo.KernelVersion
ss, _ := json.Marshal(hostInfo)
baseInfo.VirtualizationSystem = string(ss)
baseInfo.IpV4Addr = loadOutboundIP()
httpProxy := os.Getenv("http_proxy")
if httpProxy == "" {
httpProxy = os.Getenv("HTTP_PROXY")
baseInfo = dto.DashboardBase{
Hostname: hostInfo.Hostname,
OS: hostInfo.OS,
Platform: hostInfo.Platform,
PlatformFamily: hostInfo.PlatformFamily,
PlatformVersion: hostInfo.PlatformVersion,
PrettyDistro: psutil.HOST.GetDistro(),
KernelArch: hostInfo.KernelArch,
KernelVersion: hostInfo.KernelVersion,
VirtualizationSystem: string(ss),
IpV4Addr: loadOutboundIP(),
SystemProxy: "noProxy",
}
if httpProxy != "" {
baseInfo.SystemProxy = httpProxy
if proxy := cmp.Or(os.Getenv("http_proxy"), os.Getenv("HTTP_PROXY")); proxy != "" {
baseInfo.SystemProxy = proxy
}
baseInfo.SystemProxy = "noProxy"
loadQuickJump(&baseInfo)
cpuInfo, err := cpu.Info()
if err == nil {
cpuInfo, err := psutil.CPUInfo.GetCPUInfo(false)
if err == nil && len(cpuInfo) > 0 {
baseInfo.CPUModelName = cpuInfo[0].ModelName
}
baseInfo.CPUCores, _ = cpu.Counts(false)
baseInfo.CPULogicalCores, _ = cpu.Counts(true)
baseInfo.CPUCores, _ = psutil.CPUInfo.GetPhysicalCores(false)
baseInfo.CPULogicalCores, _ = psutil.CPUInfo.GetLogicalCores(false)
baseInfo.CPUMhz = cpuInfo[0].Mhz
baseInfo.CurrentInfo = *u.LoadCurrentInfo(ioOption, netOption)
return &baseInfo, nil
@@ -173,18 +182,22 @@ func (u *DashboardService) LoadBaseInfo(ioOption string, netOption string) (*dto
func (u *DashboardService) LoadCurrentInfo(ioOption string, netOption string) *dto.DashboardCurrent {
var currentInfo dto.DashboardCurrent
hostInfo, _ := host.Info()
hostInfo, _ := psutil.HOST.GetHostInfo(false)
currentInfo.Uptime = hostInfo.Uptime
currentInfo.TimeSinceUptime = time.Now().Add(-time.Duration(hostInfo.Uptime) * time.Second).Format(constant.DateTimeLayout)
currentInfo.TimeSinceUptime = time.Unix(int64(hostInfo.BootTime), 0).Format(constant.DateTimeLayout)
currentInfo.Procs = hostInfo.Procs
currentInfo.CPUTotal, _ = psutil.CPUInfo.GetLogicalCores(false)
currentInfo.CPUTotal, _ = cpu.Counts(true)
totalPercent, _ := cpu.Percent(100*time.Millisecond, false)
if len(totalPercent) == 1 {
currentInfo.CPUUsedPercent = totalPercent[0]
currentInfo.CPUUsed = currentInfo.CPUUsedPercent * 0.01 * float64(currentInfo.CPUTotal)
cpuUsedPercent, perCore, cpuDetailedPercent := psutil.CPU.GetCPUUsage()
if len(perCore) == 0 {
currentInfo.CPUTotal = psutil.CPU.NumCPU()
} else {
currentInfo.CPUTotal = len(perCore)
}
currentInfo.CPUPercent, _ = cpu.Percent(100*time.Millisecond, true)
currentInfo.CPUPercent = perCore
currentInfo.CPUUsedPercent = cpuUsedPercent
currentInfo.CPUUsed = cpuUsedPercent * 0.01 * float64(currentInfo.CPUTotal)
currentInfo.CPUDetailedPercent = cpuDetailedPercent
loadInfo, _ := load.Avg()
currentInfo.Load1 = loadInfo.Load1
@@ -211,9 +224,6 @@ func (u *DashboardService) LoadCurrentInfo(ioOption string, netOption string) *d
currentInfo.GPUData = loadGPUInfo()
currentInfo.XPUData = loadXpuInfo()
currentInfo.TopCPUItems = loadTopCPU()
currentInfo.TopMemItems = loadTopMem()
if ioOption == "all" {
diskInfo, _ := disk.IOCounters()
for _, state := range diskInfo {
@@ -246,6 +256,7 @@ func (u *DashboardService) LoadCurrentInfo(ioOption string, netOption string) *d
if state.Name == netOption {
currentInfo.NetBytesSent = state.BytesSent
currentInfo.NetBytesRecv = state.BytesRecv
break
}
}
}
@@ -254,6 +265,14 @@ func (u *DashboardService) LoadCurrentInfo(ioOption string, netOption string) *d
return &currentInfo
}
func (u *DashboardService) LoadTopCPU() []dto.Process {
return loadTopCPU()
}
func (u *DashboardService) LoadTopMem() []dto.Process {
return loadTopMem()
}
func (u *DashboardService) LoadAppLauncher(ctx *gin.Context) ([]dto.AppLauncher, error) {
var data []dto.AppLauncher
appInstalls, err := appInstallRepo.ListBy(context.Background())
@@ -266,7 +285,7 @@ func (u *DashboardService) LoadAppLauncher(ctx *gin.Context) ([]dto.AppLauncher,
}
showList, err := launcherRepo.ListName()
defaultList, err := appRepo.GetTopRecomment()
defaultList, err := appRepo.GetTopRecommend()
if err != nil {
return data, nil
}
@@ -455,41 +474,52 @@ func loadDiskInfo() []dto.DiskInfo {
)
wg.Add(len(mounts))
for i := 0; i < len(mounts); i++ {
go func(timeoutCh <-chan time.Time, mount diskInfo) {
go func(mount diskInfo) {
defer wg.Done()
var itemData dto.DiskInfo
itemData.Path = mount.Mount
itemData.Type = mount.Type
itemData.Device = mount.Device
type diskResult struct {
state *disk.UsageStat
err error
}
resultCh := make(chan diskResult, 1)
go func() {
state, err := psutil.DISK.GetUsage(mount.Mount, false)
resultCh <- diskResult{state: state, err: err}
}()
select {
case <-timeoutCh:
case <-time.After(5 * time.Second):
mu.Lock()
datas = append(datas, itemData)
mu.Unlock()
global.LOG.Errorf("load disk info from %s failed, err: timeout", mount.Mount)
default:
state, err := disk.Usage(mount.Mount)
if err != nil {
case result := <-resultCh:
if result.err != nil {
mu.Lock()
datas = append(datas, itemData)
mu.Unlock()
global.LOG.Errorf("load disk info from %s failed, err: %v", mount.Mount, err)
global.LOG.Errorf("load disk info from %s failed, err: %v", mount.Mount, result.err)
return
}
itemData.Total = state.Total
itemData.Free = state.Free
itemData.Used = state.Used
itemData.UsedPercent = state.UsedPercent
itemData.InodesTotal = state.InodesTotal
itemData.InodesUsed = state.InodesUsed
itemData.InodesFree = state.InodesFree
itemData.InodesUsedPercent = state.InodesUsedPercent
itemData.Total = result.state.Total
itemData.Free = result.state.Free
itemData.Used = result.state.Used
itemData.UsedPercent = result.state.UsedPercent
itemData.InodesTotal = result.state.InodesTotal
itemData.InodesUsed = result.state.InodesUsed
itemData.InodesFree = result.state.InodesFree
itemData.InodesUsedPercent = result.state.InodesUsedPercent
mu.Lock()
datas = append(datas, itemData)
mu.Unlock()
}
}(time.After(5*time.Second), mounts[i])
}(mounts[i])
}
wg.Wait()
+24 -19
View File
@@ -119,9 +119,10 @@ func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool {
if req.Timeout == 0 {
req.Timeout = 30
}
var err error
switch req.Type {
case constant.AppPostgresql:
_, err := postgresql.NewPostgresqlClient(pgclient.DBInfo{
_, err = postgresql.NewPostgresqlClient(pgclient.DBInfo{
From: "remote",
Address: req.Address,
Port: req.Port,
@@ -130,17 +131,15 @@ func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool {
Password: req.Password,
Timeout: req.Timeout,
})
return err == nil
case constant.AppRedis:
_, err := redisclient.NewRedisClient(redisclient.DBInfo{
_, err = redisclient.NewRedisClient(redisclient.DBInfo{
Address: req.Address,
Port: req.Port,
Password: req.Password,
Timeout: req.Timeout,
})
return err == nil
case "mysql", "mariadb":
_, err := mysql.NewMysqlClient(client.DBInfo{
_, err = mysql.NewMysqlClient(client.DBInfo{
From: "remote",
Address: req.Address,
Port: req.Port,
@@ -154,10 +153,13 @@ func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool {
SkipVerify: req.SkipVerify,
Timeout: req.Timeout,
})
return err == nil
}
if err != nil {
global.LOG.Errorf("check database connection failed, err: %v", err)
return false
}
return false
return true
}
func (u *DatabaseService) Create(req dto.DatabaseCreate) error {
@@ -174,12 +176,13 @@ func (u *DatabaseService) Create(req dto.DatabaseCreate) error {
switch req.Type {
case constant.AppPostgresql:
if _, err := postgresql.NewPostgresqlClient(pgclient.DBInfo{
From: "remote",
Address: req.Address,
Port: req.Port,
Username: req.Username,
Password: req.Password,
Timeout: req.Timeout,
From: "remote",
Address: req.Address,
Port: req.Port,
InitialDB: req.InitialDB,
Username: req.Username,
Password: req.Password,
Timeout: req.Timeout,
}); err != nil {
return err
}
@@ -275,12 +278,13 @@ func (u *DatabaseService) Update(req dto.DatabaseUpdate) error {
switch req.Type {
case constant.AppPostgresql:
if _, err := postgresql.NewPostgresqlClient(pgclient.DBInfo{
From: "remote",
Address: req.Address,
Port: req.Port,
Username: req.Username,
Password: req.Password,
Timeout: req.Timeout,
From: "remote",
Address: req.Address,
Port: req.Port,
InitialDB: req.InitialDB,
Username: req.Username,
Password: req.Password,
Timeout: req.Timeout,
}); err != nil {
return err
}
@@ -326,6 +330,7 @@ func (u *DatabaseService) Update(req dto.DatabaseUpdate) error {
upMap["port"] = req.Port
upMap["username"] = req.Username
upMap["password"] = pass
upMap["initial_db"] = req.InitialDB
upMap["timeout"] = req.Timeout
upMap["description"] = req.Description
upMap["ssl"] = req.SSL
+15 -1
View File
@@ -5,8 +5,10 @@ import (
"fmt"
"os"
"path"
"strings"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
@@ -51,7 +53,14 @@ func (u *DBCommonService) LoadDatabaseFile(req dto.OperationWithNameAndType) (st
case "mariadb-conf":
filePath = path.Join(global.Dir.DataDir, fmt.Sprintf("apps/mariadb/%s/conf/my.cnf", req.Name))
case "postgresql-conf":
database, err := databaseRepo.Get(repo.WithByName(req.Name))
if err != nil {
return "", err
}
filePath = path.Join(global.Dir.DataDir, fmt.Sprintf("apps/postgresql/%s/data/postgresql.conf", req.Name))
if strings.HasPrefix(database.Version, "18.") {
filePath = path.Join(global.Dir.DataDir, fmt.Sprintf("apps/postgresql/%s/data/18/docker/postgresql.conf", req.Name))
}
case "redis-conf":
filePath = path.Join(global.Dir.DataDir, fmt.Sprintf("apps/redis/%s/conf/redis.conf", req.Name))
case "redis-cluster-conf":
@@ -76,8 +85,13 @@ func (u *DBCommonService) UpdateConfByFile(req dto.DBConfUpdateByFile) error {
switch req.Type {
case constant.AppMariaDB, constant.AppMysql, constant.AppMysqlCluster:
path = fmt.Sprintf("%s/%s/%s/conf/my.cnf", global.Dir.AppInstallDir, req.Type, app.Name)
case constant.AppPostgresql, constant.AppPostgresqlCluster:
case constant.AppPostgresqlCluster:
path = fmt.Sprintf("%s/%s/%s/data/postgresql.conf", global.Dir.AppInstallDir, req.Type, app.Name)
case constant.AppPostgresql:
path = fmt.Sprintf("%s/%s/%s/data/postgresql.conf", global.Dir.AppInstallDir, req.Type, app.Name)
if strings.HasPrefix(app.Version, "18.") {
path = fmt.Sprintf("%s/%s/%s/data/18/docker/postgresql.conf", global.Dir.AppInstallDir, req.Type, app.Name)
}
case constant.AppRedis, constant.AppRedisCluster:
path = fmt.Sprintf("%s/%s/%s/conf/redis.conf", global.Dir.AppInstallDir, req.Type, app.Name)
}
+18 -3
View File
@@ -23,8 +23,8 @@ import (
"github.com/1Panel-dev/1Panel/agent/utils/compose"
"github.com/1Panel-dev/1Panel/agent/utils/encrypt"
"github.com/1Panel-dev/1Panel/agent/utils/mysql"
"github.com/1Panel-dev/1Panel/agent/utils/re"
"github.com/1Panel-dev/1Panel/agent/utils/mysql/client"
"github.com/1Panel-dev/1Panel/agent/utils/re"
_ "github.com/go-sql-driver/mysql"
"github.com/jinzhu/copier"
"github.com/pkg/errors"
@@ -45,6 +45,7 @@ type IMysqlService interface {
DeleteCheck(req dto.MysqlDBDeleteCheck) ([]dto.DBResource, error)
Delete(ctx context.Context, req dto.MysqlDBDelete) error
LoadFormatOption(req dto.OperationWithName) []dto.MysqlFormatCollationOption
LoadStatus(req dto.OperationWithNameAndType) (*dto.MysqlStatus, error)
LoadVariables(req dto.OperationWithNameAndType) (*dto.MysqlVariables, error)
LoadRemoteAccess(req dto.OperationWithNameAndType) (bool, error)
@@ -99,7 +100,7 @@ func (u *MysqlService) ListDBOption() ([]dto.MysqlOption, error) {
}
func (u *MysqlService) Create(ctx context.Context, req dto.MysqlDBCreate) (*model.DatabaseMysql, error) {
if cmd.CheckIllegal(req.Name, req.Username, req.Password, req.Format, req.Permission) {
if cmd.CheckIllegal(req.Name, req.Username, req.Password, req.Format, req.Collation, req.Permission) {
return nil, buserr.New("ErrCmdIllegal")
}
@@ -114,7 +115,7 @@ func (u *MysqlService) Create(ctx context.Context, req dto.MysqlDBCreate) (*mode
}
if req.From == "local" && req.Username == "root" {
return nil, errors.New("Cannot set root as user name")
return nil, errors.New("cannot set root as user name")
}
cli, version, err := LoadMysqlClientByFrom(req.Database)
@@ -126,6 +127,7 @@ func (u *MysqlService) Create(ctx context.Context, req dto.MysqlDBCreate) (*mode
if err := cli.Create(client.CreateInfo{
Name: req.Name,
Format: req.Format,
Collation: req.Collation,
Username: req.Username,
Password: req.Password,
Permission: req.Permission,
@@ -584,6 +586,19 @@ func (u *MysqlService) LoadStatus(req dto.OperationWithNameAndType) (*dto.MysqlS
return &info, nil
}
func (u *MysqlService) LoadFormatOption(req dto.OperationWithName) []dto.MysqlFormatCollationOption {
defaultList := []dto.MysqlFormatCollationOption{{Format: "utf8mb4"}, {Format: "utf8mb3"}, {Format: "gbk"}, {Format: "big5"}}
client, _, err := LoadMysqlClientByFrom(req.Name)
if err != nil {
return defaultList
}
options, err := client.LoadFormatCollation(3)
if err != nil {
return defaultList
}
return options
}
func executeSqlForMaps(containerName, dbType, password, command string) (map[string]string, error) {
if dbType == "mysql-cluster" {
dbType = "mysql"
+1 -5
View File
@@ -21,7 +21,6 @@ import (
"github.com/1Panel-dev/1Panel/agent/utils/postgresql/client"
_ "github.com/jackc/pgx/v5/stdlib"
"github.com/jinzhu/copier"
"github.com/pkg/errors"
)
type PostgresqlService struct{}
@@ -138,10 +137,6 @@ func (u *PostgresqlService) Create(ctx context.Context, req dto.PostgresqlDBCrea
return nil, buserr.WithDetail("ErrStructTransform", err.Error(), nil)
}
if req.From == "local" && req.Username == "root" {
return nil, errors.New("Cannot set root as user name")
}
cli, err := LoadPostgresqlClientByFrom(req.Database)
if err != nil {
return nil, err
@@ -185,6 +180,7 @@ func LoadPostgresqlClientByFrom(database string) (postgresql.PostgresqlClient, e
dbInfo.Port = databaseItem.Port
dbInfo.Username = databaseItem.Username
dbInfo.Password = databaseItem.Password
dbInfo.InitialDB = databaseItem.InitialDB
} else {
app, err := appInstallRepo.LoadBaseInfo(databaseItem.Type, database)
if err != nil {
+3 -3
View File
@@ -80,9 +80,9 @@ func (u *RedisService) CheckHasCli() bool {
func (u *RedisService) InstallCli() error {
item := dto.ContainerOperate{
Name: "1Panel-redis-cli-tools",
Image: "redis:7.4.4",
Network: "1panel-network",
Name: "1Panel-redis-cli-tools",
Image: "redis:7.4.4",
Networks: []dto.ContainerNetwork{{Network: "1panel-network"}},
}
return NewIContainerService().ContainerCreate(item, false)
}
+2
View File
@@ -20,6 +20,7 @@ import (
"github.com/1Panel-dev/1Panel/agent/utils/common"
"github.com/1Panel-dev/1Panel/agent/utils/controller"
"github.com/1Panel-dev/1Panel/agent/utils/ntp"
"github.com/1Panel-dev/1Panel/agent/utils/psutil"
"github.com/shirou/gopsutil/v4/mem"
)
@@ -134,6 +135,7 @@ func (u *DeviceService) Update(key, value string) error {
if err := cmd.RunDefaultBashCf("%s hostnamectl set-hostname %s", cmd.SudoHandleCmd(), value); err != nil {
return err
}
_, _ = psutil.HOST.GetHostInfo(true)
case "Ntp", "LocalTime":
if cmd.CheckIllegal(value) {
return buserr.New("ErrCmdIllegal")
+10 -3
View File
@@ -78,9 +78,9 @@ func (u *DeviceService) Scan() dto.CleanData {
return common.CompareVersion(upgradeTree.Children[i].Label, upgradeTree.Children[j].Label)
})
if global.IsMaster {
var copiesSeeting model.Setting
_ = global.CoreDB.Where("key = ?", "UpgradeBackupCopies").First(&copiesSeeting).Error
copies, _ := strconv.Atoi(copiesSeeting.Value)
var copiesSetting model.Setting
_ = global.CoreDB.Where("key = ?", "UpgradeBackupCopies").First(&copiesSetting).Error
copies, _ := strconv.Atoi(copiesSetting.Value)
if copies == 0 || copies > len(upgradeTree.Children) {
copies = len(upgradeTree.Children)
}
@@ -413,6 +413,7 @@ func loadContainerTree() []dto.CleanTree {
if err != nil {
return treeData
}
defer client.Close()
diskUsage, err := client.DiskUsage(context.Background(), types.DiskUsageOptions{})
if err != nil {
return treeData
@@ -552,6 +553,7 @@ func dropBuildCache() (int, int) {
global.LOG.Errorf("do not get docker client")
return 0, 0
}
defer client.Close()
opts := build.CachePruneOptions{}
opts.All = true
res, err := client.BuildCachePrune(context.Background(), opts)
@@ -568,6 +570,7 @@ func dropImages() (int, int) {
global.LOG.Errorf("do not get docker client")
return 0, 0
}
defer client.Close()
pruneFilters := filters.NewArgs()
pruneFilters.Add("dangling", "false")
res, err := client.ImagesPrune(context.Background(), pruneFilters)
@@ -584,6 +587,7 @@ func dropContainers() (int, int) {
global.LOG.Errorf("do not get docker client")
return 0, 0
}
defer client.Close()
pruneFilters := filters.NewArgs()
res, err := client.ContainersPrune(context.Background(), pruneFilters)
if err != nil {
@@ -599,6 +603,7 @@ func dropVolumes() (int, int) {
global.LOG.Errorf("do not get docker client")
return 0, 0
}
defer client.Close()
pruneFilters := filters.NewArgs()
versions, err := client.ServerVersion(context.Background())
if err != nil {
@@ -647,11 +652,13 @@ func dropTaskLog(logDir string) {
if err == nil {
_ = xpackDB.Table("tampers").Where("task_id != ?", "").Select("task_id").Find(&usedTasks).Error
}
defer common.CloseDB(xpackDB)
case "System":
xpackDB, err := common.LoadDBConnByPathWithErr(path.Join(global.CONF.Base.InstallDir, "1panel/db/xpack.db"), "xpack.db")
if err == nil {
_ = xpackDB.Model("nodes").Where("task_id != ?", "").Select("task_id").Find(&usedTasks).Error
}
defer common.CloseDB(xpackDB)
default:
dropFileOrDir(logDir)
_ = taskRepo.Delete(repo.WithByType(taskType))
+11 -12
View File
@@ -32,20 +32,19 @@ func (s *DiskService) GetCompleteDiskInfo() (*response.CompleteDiskInfo, error)
output, err := cmd.RunDefaultWithStdoutBashC("lsblk -J -o NAME,SIZE,TYPE,MOUNTPOINT,FSTYPE,MODEL,SERIAL,TRAN,ROTA")
if err == nil {
diskInfos, err = parseLsblkJsonOutput(output)
if err != nil {
return nil, fmt.Errorf("failed to parse lsblk output: %v", err)
}
} else {
output, err = cmd.RunDefaultWithStdoutBashC("lsblk -P -o NAME,SIZE,TYPE,MOUNTPOINT,FSTYPE,MODEL,SERIAL,TRAN,ROTA")
if err != nil {
return nil, fmt.Errorf("failed to run lsblk command: %v", err)
}
diskInfos, err = parseLsblkOutput(output)
if err != nil {
return nil, fmt.Errorf("failed to parse lsblk output: %v", err)
if err == nil {
result := organizeDiskInfo(diskInfos)
return &result, nil
}
}
output, err = cmd.RunDefaultWithStdoutBashC("lsblk -P -o NAME,SIZE,TYPE,MOUNTPOINT,FSTYPE,MODEL,SERIAL,TRAN,ROTA")
if err != nil {
return nil, fmt.Errorf("failed to run lsblk command: %v", err)
}
diskInfos, err = parseLsblkOutput(output)
if err != nil {
return nil, fmt.Errorf("failed to parse lsblk output: %v", err)
}
result := organizeDiskInfo(diskInfos)
return &result, nil
}
+90 -6
View File
@@ -5,10 +5,6 @@ import (
"context"
"encoding/json"
"fmt"
"github.com/1Panel-dev/1Panel/agent/app/task"
"github.com/1Panel-dev/1Panel/agent/i18n"
"github.com/1Panel-dev/1Panel/agent/utils/convert"
"github.com/1Panel-dev/1Panel/agent/utils/ini_conf"
"io"
"io/fs"
"os"
@@ -21,6 +17,11 @@ import (
"time"
"unicode/utf8"
"github.com/1Panel-dev/1Panel/agent/app/task"
"github.com/1Panel-dev/1Panel/agent/i18n"
"github.com/1Panel-dev/1Panel/agent/utils/convert"
"github.com/1Panel-dev/1Panel/agent/utils/ini_conf"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/jinzhu/copier"
"golang.org/x/text/encoding"
@@ -55,6 +56,7 @@ type IFileService interface {
Compress(c request.FileCompress) error
DeCompress(c request.FileDeCompress) error
GetContent(op request.FileContentReq) (response.FileInfo, error)
GetPreviewContent(op request.FileContentReq) (response.FileInfo, error)
SaveContent(edit request.FileEdit) error
FileDownload(d request.FileDownload) (string, error)
DirSize(req request.DirSizeReq) (response.DirSizeRes, error)
@@ -170,7 +172,6 @@ func shouldFilterPath(path string) bool {
return false
}
// 递归构建文件树(只取当前目录以及当前目录下的第一层子节点)
func (f *FileService) buildFileTree(node *response.FileTree, items []*files.FileInfo, op request.FileOption, level int) error {
for _, v := range items {
if shouldFilterPath(v.Path) {
@@ -241,6 +242,8 @@ func (f *FileService) Create(op request.FileCreate) error {
if err := fo.LinkFile(op.LinkPath, op.Path, op.IsSymlink); err != nil {
return err
}
handleDefaultOwn(op.Path)
return nil
}
if err := fo.CreateFileWithMode(op.Path, fs.FileMode(mode)); err != nil {
return err
@@ -268,6 +271,11 @@ func (f *FileService) Delete(op request.FileDelete) error {
return fo.DeleteFile(op.Path)
}
}
info, _ := fo.Fs.Stat(op.Path)
if info == nil || files.IsSymlink(info.Mode()) {
return os.Remove(op.Path)
}
if err := NewIRecycleBinService().Create(request.RecycleBinCreate{SourcePath: op.Path}); err != nil {
return err
}
@@ -371,6 +379,82 @@ func (f *FileService) GetContent(op request.FileContentReq) (response.FileInfo,
return response.FileInfo{FileInfo: *info}, nil
}
func (f *FileService) GetPreviewContent(op request.FileContentReq) (response.FileInfo, error) {
info, err := files.NewFileInfo(files.FileOption{
Path: op.Path,
Expand: false,
IsDetail: op.IsDetail,
})
if err != nil {
return response.FileInfo{}, err
}
if files.IsBlockDevice(info.FileMode) {
return response.FileInfo{FileInfo: *info}, nil
}
file, err := os.Open(op.Path)
if err != nil {
return response.FileInfo{}, err
}
defer file.Close()
headBuf := make([]byte, 1024)
n, err := file.Read(headBuf)
if err != nil && err != io.EOF {
return response.FileInfo{}, err
}
headBuf = headBuf[:n]
if len(headBuf) > 0 && files.DetectBinary(headBuf) {
return response.FileInfo{FileInfo: *info}, nil
}
const maxSize = 10 * 1024 * 1024
if info.Size <= maxSize {
if _, err := file.Seek(0, 0); err != nil {
return response.FileInfo{}, err
}
content, err := io.ReadAll(file)
if err != nil {
return response.FileInfo{}, err
}
info.Content = string(content)
} else {
lines, err := files.TailFromEnd(op.Path, 300)
if err != nil {
return response.FileInfo{}, err
}
info.Content = strings.Join(lines, "\n")
}
content := []byte(info.Content)
if len(content) > 1024 {
content = content[:1024]
}
if !utf8.Valid(content) {
_, decodeName, _ := charset.DetermineEncoding(content, "")
decoder := files.GetDecoderByName(decodeName)
if decoder != nil {
reader := strings.NewReader(info.Content)
var dec *encoding.Decoder
if decodeName == "windows-1252" {
dec = simplifiedchinese.GBK.NewDecoder()
} else {
dec = decoder.NewDecoder()
}
decodedReader := transform.NewReader(reader, dec)
contents, err := io.ReadAll(decodedReader)
if err != nil {
return response.FileInfo{}, err
}
info.Content = string(contents)
}
}
return response.FileInfo{FileInfo: *info}, nil
}
func (f *FileService) SaveContent(edit request.FileEdit) error {
info, err := files.NewFileInfo(files.FileOption{
Path: edit.Path,
@@ -591,7 +675,7 @@ func (f *FileService) ReadLogByLine(req request.FileReadByLineReq) (*response.Fi
scope string
logFileRes *dto.LogFileRes
)
if stat.Size() > 500*1024*1024 {
if stat.Size() > files.MaxReadFileSize {
lines, err = files.TailFromEnd(logFilePath, req.PageSize)
isEndOfFile = true
scope = "tail"
+4 -94
View File
@@ -68,7 +68,7 @@ func (u *FirewallService) LoadBaseInfo(tab string) (dto.FirewallBaseInfo, error)
go func() {
defer wg.Done()
baseInfo.IsActive, _ = client.Status()
baseInfo.IsInit, baseInfo.IsBind = loadInitStatus(baseInfo.Name, tab)
baseInfo.IsInit, baseInfo.IsBind = iptables.LoadInitStatus(baseInfo.Name, tab)
}()
wg.Wait()
return baseInfo, nil
@@ -711,6 +711,9 @@ func (u *FirewallService) addPortsBeforeStart(client firewall.FirewallClient) er
if err := client.Port(fireClient.FireInfo{Port: "443", Protocol: "tcp", Strategy: "accept"}, "add"); err != nil {
return err
}
if err := client.Port(fireClient.FireInfo{Port: "443", Protocol: "udp", Strategy: "accept"}, "add"); err != nil {
return err
}
return client.Reload()
}
@@ -826,96 +829,3 @@ func checkPortUsed(ports, proto string, apps []portOfApp) string {
}
return ""
}
func loadInitStatus(clientName, tab string) (bool, bool) {
if clientName == "firewalld" {
return true, true
}
if clientName == "ufw" && tab != "forward" {
return true, true
}
switch tab {
case "base":
if isExist, _ := iptables.CheckChainExist(iptables.FilterTab, iptables.Chain1PanelBasicBefore); !isExist {
return false, false
}
if exist := iptables.CheckRuleExist(iptables.FilterTab, iptables.Chain1PanelBasicBefore, iptables.IoRuleIn); !exist {
return false, false
}
if exist := iptables.CheckRuleExist(iptables.FilterTab, iptables.Chain1PanelBasicBefore, iptables.EstablishedRule); !exist {
return false, false
}
if exist, _ := iptables.CheckChainExist(iptables.FilterTab, iptables.Chain1PanelBasic); !exist {
return false, false
}
if exist, _ := iptables.CheckChainExist(iptables.FilterTab, iptables.Chain1PanelBasicAfter); !exist {
return false, false
}
if exist := iptables.CheckRuleExist(iptables.FilterTab, iptables.Chain1PanelBasicAfter, iptables.DropAllTcp); !exist {
return false, false
}
if exist := iptables.CheckRuleExist(iptables.FilterTab, iptables.Chain1PanelBasicAfter, iptables.DropAllUdp); !exist {
return false, false
}
if bind, _ := iptables.CheckChainBind(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasicBefore); !bind {
return true, false
}
if bind, _ := iptables.CheckChainBind(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasic); !bind {
return true, false
}
if bind, _ := iptables.CheckChainBind(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasicAfter); !bind {
return true, false
}
return true, true
case "advance":
isExist, _ := iptables.CheckChainExist(iptables.FilterTab, iptables.Chain1PanelInput)
if !isExist {
return false, false
}
isExist, _ = iptables.CheckChainExist(iptables.FilterTab, iptables.Chain1PanelOutput)
if !isExist {
return false, false
}
isBind, _ := iptables.CheckChainBind(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelInput)
if !isBind {
return true, false
}
isBind, _ = iptables.CheckChainBind(iptables.FilterTab, iptables.ChainOutput, iptables.Chain1PanelOutput)
return true, isBind
case "forward":
stdout, err := cmd.RunDefaultWithStdoutBashC("cat /proc/sys/net/ipv4/ip_forward")
if err != nil {
global.LOG.Errorf("check /proc/sys/net/ipv4/ip_forward failed, err: %v", err)
return false, false
}
if strings.TrimSpace(stdout) == "0" {
return false, false
}
exist, _ := iptables.CheckChainExist(iptables.NatTab, iptables.Chain1PanelPreRouting)
if !exist {
return false, false
}
exist, _ = iptables.CheckChainExist(iptables.NatTab, iptables.Chain1PanelPostRouting)
if !exist {
return false, false
}
exist, _ = iptables.CheckChainExist(iptables.FilterTab, iptables.Chain1PanelForward)
if !exist {
return false, false
}
isBind, _ := iptables.CheckChainBind(iptables.NatTab, "PREROUTING", iptables.Chain1PanelPreRouting)
if !isBind {
return false, false
}
isBind, _ = iptables.CheckChainBind(iptables.NatTab, "POSTROUTING", iptables.Chain1PanelPostRouting)
if !isBind {
return false, false
}
isBind, _ = iptables.CheckChainBind(iptables.FilterTab, "FORWARD", iptables.Chain1PanelForward)
return true, isBind
default:
return false, false
}
}
+12
View File
@@ -128,6 +128,18 @@ func (u *ImageService) Page(req dto.PageImage) (int64, interface{}, error) {
})
}
imageDescriptions, _ := settingRepo.GetDescriptionList(repo.WithByType("image"))
for i := 0; i < len(list); i++ {
for _, desc := range imageDescriptions {
if "sha256:"+desc.ID == records[i].ID {
records[i].Description = desc.Description
records[i].IsPinned = desc.IsPinned
}
}
}
sort.Slice(records, func(i, j int) bool {
return records[i].IsPinned && !records[j].IsPinned
})
total, start, end := len(records), (req.Page-1)*req.PageSize, req.Page*req.PageSize
if start > total {
backDatas = make([]dto.ImageInfo, 0)
+11 -3
View File
@@ -263,7 +263,15 @@ func stopBeforeUpdateRepo() error {
if err := validateDockerConfig(); err != nil {
return err
}
if err := controller.HandleRestart("docker"); err != nil {
dockerService, _ := controller.LoadServiceName("docker")
if len(dockerService) == 0 {
return errors.New("docker service not found")
}
client, err := controller.New()
if err != nil {
return err
}
if err := client.Operate("restart", dockerService); err != nil {
return fmt.Errorf("failed to restart Docker: %v", err)
}
ticker := time.NewTicker(3 * time.Second)
@@ -276,8 +284,8 @@ func stopBeforeUpdateRepo() error {
cancel()
return errors.New("the docker service cannot be restarted")
default:
active, err := controller.CheckActive("docker")
if active && err != nil {
active, _ := client.IsActive(dockerService)
if active {
global.LOG.Info("docker restart with new conf successful!")
return nil
}
+64 -14
View File
@@ -8,6 +8,7 @@ import (
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
"github.com/1Panel-dev/1Panel/agent/utils/firewall/client"
@@ -16,7 +17,7 @@ import (
type IIptablesService interface {
Search(req dto.SearchPageWithType) (int64, interface{}, error)
OperateRule(req dto.IptablesRuleOp) error
OperateRule(req dto.IptablesRuleOp, withSave bool) error
BatchOperate(req dto.IptablesBatchOperate) error
LoadChainStatus(req dto.OperationWithName) dto.IptablesChainStatus
@@ -63,7 +64,7 @@ func (s *IptablesService) Search(req dto.SearchPageWithType) (int64, interface{}
return int64(total), records, nil
}
func (s *IptablesService) OperateRule(req dto.IptablesRuleOp) error {
func (s *IptablesService) OperateRule(req dto.IptablesRuleOp, withSave bool) error {
action := "ACCEPT"
if req.Strategy == "drop" {
action = "DROP"
@@ -122,6 +123,9 @@ func (s *IptablesService) OperateRule(req dto.IptablesRuleOp) error {
}
}
if !withSave {
return nil
}
if err := iptables.SaveRulesToFile(iptables.FilterTab, req.Chain, name); err != nil {
global.LOG.Errorf("persistence for %s failed, err: %v", iptables.Chain1PanelBasic, err)
}
@@ -129,11 +133,23 @@ func (s *IptablesService) OperateRule(req dto.IptablesRuleOp) error {
}
func (s *IptablesService) BatchOperate(req dto.IptablesBatchOperate) error {
if len(req.Rules) == 0 {
return errors.New("no rules to operate")
}
for _, rule := range req.Rules {
if err := s.OperateRule(rule); err != nil {
if err := s.OperateRule(rule, false); err != nil {
return err
}
}
chain := iptables.Chain1PanelInput
fileName := iptables.InputFileName
if req.Rules[0].Chain == iptables.Chain1PanelOutput {
chain = iptables.Chain1PanelOutput
fileName = iptables.OutputFileName
}
if err := iptables.SaveRulesToFile(iptables.FilterTab, chain, fileName); err != nil {
global.LOG.Errorf("persistence for %s failed, err: %v", iptables.Chain1PanelBasic, err)
}
return nil
}
@@ -168,9 +184,20 @@ func (s *IptablesService) Operate(req dto.IptablesOp) error {
if err := iptables.BindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasicAfter, 3); err != nil {
return err
}
if err := iptables.SaveRulesToFile(iptables.FilterTab, iptables.Chain1PanelBasicBefore, iptables.BasicBeforeFileName); err != nil {
return err
}
if err := iptables.SaveRulesToFile(iptables.FilterTab, iptables.Chain1PanelBasicAfter, iptables.BasicAfterFileName); err != nil {
return err
}
_ = settingRepo.Update("IptablesStatus", constant.StatusEnable)
return nil
case "init-forward":
return client.EnableIptablesForward()
if err := client.EnableIptablesForward(); err != nil {
return err
}
_ = settingRepo.Update("IptablesForwardStatus", constant.StatusEnable)
return nil
case "init-advance":
if err := iptables.AddChain(iptables.FilterTab, iptables.Chain1PanelInput); err != nil {
return err
@@ -185,6 +212,8 @@ func (s *IptablesService) Operate(req dto.IptablesOp) error {
if err := iptables.BindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelInput, number); err != nil {
return err
}
_ = settingRepo.Update("IptablesInputStatus", constant.StatusEnable)
_ = settingRepo.Update("IptablesOutputStatus", constant.StatusEnable)
return nil
case "bind-base":
if err := initPreRules(); err != nil {
@@ -199,6 +228,7 @@ func (s *IptablesService) Operate(req dto.IptablesOp) error {
if err := iptables.BindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasicAfter, 3); err != nil {
return err
}
_ = settingRepo.Update("IptablesStatus", constant.StatusEnable)
return nil
case "unbind-base":
if err := iptables.UnbindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasicAfter); err != nil {
@@ -210,16 +240,29 @@ func (s *IptablesService) Operate(req dto.IptablesOp) error {
if err := iptables.UnbindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasic); err != nil {
return err
}
_ = settingRepo.Update("IptablesStatus", constant.StatusDisable)
return nil
case "bind":
if err := iptables.BindChain(iptables.FilterTab, targetChain, req.Name, loadBindNumber(req.Name)); err != nil {
return err
}
if req.Name == iptables.Chain1PanelInput {
_ = settingRepo.Update("IptablesInputStatus", constant.StatusEnable)
}
if req.Name == iptables.Chain1PanelOutput {
_ = settingRepo.Update("IptablesOutputStatus", constant.StatusEnable)
}
return nil
case "unbind":
if err := iptables.UnbindChain(iptables.FilterTab, targetChain, req.Name); err != nil {
return err
}
if req.Name == iptables.Chain1PanelInput {
_ = settingRepo.Update("IptablesInputStatus", constant.StatusDisable)
}
if req.Name == iptables.Chain1PanelOutput {
_ = settingRepo.Update("IptablesOutputStatus", constant.StatusDisable)
}
return nil
}
return nil
@@ -310,16 +353,7 @@ func initPreRules() error {
if err := iptables.AddRule(iptables.FilterTab, iptables.Chain1PanelBasicBefore, iptables.EstablishedRule); err != nil {
return err
}
panelPort := ""
if !global.IsMaster {
panelPort = global.CONF.Base.Port
} else {
var portSetting model.Setting
_ = global.CoreDB.Where("key = ?", "ServerPort").First(&portSetting).Error
if len(portSetting.Value) != 0 {
panelPort = portSetting.Value
}
}
panelPort := LoadPanelPort()
if len(panelPort) == 0 {
return errors.New("find 1panel service port failed")
}
@@ -329,6 +363,9 @@ func initPreRules() error {
return err
}
}
if err := iptables.AddRule(iptables.FilterTab, iptables.Chain1PanelBasicAfter, "-p udp -m udp --dport 443 -j ACCEPT"); err != nil {
return err
}
if err := iptables.AddRule(iptables.FilterTab, iptables.Chain1PanelBasicAfter, iptables.DropAllTcp); err != nil {
return err
}
@@ -337,3 +374,16 @@ func initPreRules() error {
}
return nil
}
func LoadPanelPort() string {
if !global.IsMaster {
return global.CONF.Base.Port
} else {
var portSetting model.Setting
_ = global.CoreDB.Where("key = ?", "ServerPort").First(&portSetting).Error
if len(portSetting.Value) != 0 {
return portSetting.Value
}
}
return ""
}
+214 -17
View File
@@ -6,6 +6,7 @@ import (
"fmt"
"sort"
"strconv"
"strings"
"time"
"github.com/1Panel-dev/1Panel/agent/app/repo"
@@ -15,7 +16,10 @@ import (
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/gpu"
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/xpu"
"github.com/1Panel-dev/1Panel/agent/utils/common"
"github.com/1Panel-dev/1Panel/agent/utils/psutil"
"github.com/robfig/cron/v3"
"github.com/shirou/gopsutil/v4/cpu"
"github.com/shirou/gopsutil/v4/disk"
@@ -35,6 +39,8 @@ var monitorCancel context.CancelFunc
type IMonitorService interface {
Run()
LoadMonitorData(req dto.MonitorSearch) ([]dto.MonitorData, error)
LoadGPUOptions() dto.MonitorGPUOptions
LoadGPUMonitorData(req dto.MonitorGPUSearch) (dto.MonitorGPUData, error)
LoadSetting() (*dto.MonitorSetting, error)
UpdateSetting(key, value string) error
CleanData() error
@@ -113,6 +119,109 @@ func (m *MonitorService) LoadMonitorData(req dto.MonitorSearch) ([]dto.MonitorDa
return data, nil
}
func (m *MonitorService) LoadGPUOptions() dto.MonitorGPUOptions {
var data dto.MonitorGPUOptions
gpuExist, gpuClient := gpu.New()
xpuExist, xpuClient := xpu.New()
if !gpuExist && !xpuExist {
return data
}
if gpuExist {
data.GPUType = "gpu"
gpuInfo, err := gpuClient.LoadGpuInfo()
if err != nil || len(gpuInfo.GPUs) == 0 {
global.LOG.Error("Load GPU info failed or no GPU found, err: ", err)
return data
}
sort.Slice(gpuInfo.GPUs, func(i, j int) bool {
return gpuInfo.GPUs[i].Index < gpuInfo.GPUs[j].Index
})
for _, item := range gpuInfo.GPUs {
var chartHide dto.GPUChartHide
chartHide.ProductName = fmt.Sprintf("%d - %s", item.Index, item.ProductName)
chartHide.GPU = item.GPUUtil == "" || item.GPUUtil == "N/A"
if (item.MemTotal == "" || item.MemTotal == "N/A") && (item.MemUsed == "" || item.MemUsed == "N/A") {
chartHide.Memory = true
}
if (item.MaxPowerLimit == "" || item.MaxPowerLimit == "N/A") && (item.PowerDraw == "" || item.PowerDraw == "N/A") {
chartHide.Power = true
}
chartHide.Temperature = item.Temperature == "" || item.Temperature == "N/A"
chartHide.Speed = item.FanSpeed == "" || item.FanSpeed == "N/A"
data.ChartHide = append(data.ChartHide, chartHide)
data.Options = append(data.Options, fmt.Sprintf("%d - %s", item.Index, item.ProductName))
}
return data
} else {
data.GPUType = "xpu"
xpu, err := xpuClient.LoadGpuInfo()
if err != nil || len(xpu.Xpu) == 0 {
global.LOG.Error("Load XPU info failed or no XPU found, err: ", err)
}
sort.Slice(xpu.Xpu, func(i, j int) bool {
return xpu.Xpu[i].Basic.DeviceID < xpu.Xpu[j].Basic.DeviceID
})
for _, item := range xpu.Xpu {
var chartHide dto.GPUChartHide
chartHide.GPU = true
chartHide.Speed = true
chartHide.ProductName = fmt.Sprintf("%d - %s", item.Basic.DeviceID, item.Basic.DeviceName)
if (item.Stats.MemoryUsed == "" || item.Stats.MemoryUsed == "N/A") && (item.Basic.Memory == "" || item.Basic.FreeMemory == "N/A") {
chartHide.Memory = true
}
if item.Stats.Power == "" || item.Stats.Power == "N/A" {
chartHide.Power = true
}
chartHide.Temperature = item.Stats.Temperature == "" || item.Stats.Temperature == "N/A"
data.ChartHide = append(data.ChartHide, chartHide)
data.Options = append(data.Options, fmt.Sprintf("%d - %s", item.Basic.DeviceID, item.Basic.DeviceName))
}
return data
}
}
func (m *MonitorService) LoadGPUMonitorData(req dto.MonitorGPUSearch) (dto.MonitorGPUData, error) {
loc, _ := time.LoadLocation(common.LoadTimeZoneByCmd())
req.StartTime = req.StartTime.In(loc)
req.EndTime = req.EndTime.In(loc)
var data dto.MonitorGPUData
gpuList, err := monitorRepo.GetGPU(repo.WithByCreatedAt(req.StartTime, req.EndTime), monitorRepo.WithByProductName(req.ProductName))
if err != nil {
return data, err
}
for _, gpu := range gpuList {
data.Date = append(data.Date, gpu.CreatedAt)
data.GPUValue = append(data.GPUValue, gpu.GPUUtil)
data.TemperatureValue = append(data.TemperatureValue, gpu.Temperature)
data.PowerUsed = append(data.PowerUsed, gpu.PowerDraw)
data.PowerTotal = append(data.PowerTotal, gpu.MaxPowerLimit)
if gpu.MaxPowerLimit != 0 {
data.PowerPercent = append(data.PowerPercent, gpu.PowerDraw/gpu.MaxPowerLimit*100)
} else {
data.PowerPercent = append(data.PowerPercent, float64(0))
}
data.MemoryTotal = append(data.MemoryTotal, gpu.MemTotal)
data.MemoryUsed = append(data.MemoryUsed, gpu.MemUsed)
if gpu.MemTotal != 0 {
data.MemoryPercent = append(data.MemoryPercent, gpu.MemUsed/gpu.MemTotal*100)
} else {
data.MemoryPercent = append(data.MemoryPercent, float64(0))
}
var process []dto.GPUProcess
if err := json.Unmarshal([]byte(gpu.Processes), &process); err == nil {
data.ProcessCount = append(data.ProcessCount, len(process))
data.GPUProcesses = append(data.GPUProcesses, process)
} else {
data.ProcessCount = append(data.ProcessCount, 0)
data.GPUProcesses = append(data.GPUProcesses, []dto.GPUProcess{})
}
data.SpeedValue = append(data.SpeedValue, gpu.FanSpeed)
}
return data, nil
}
func (m *MonitorService) LoadSetting() (*dto.MonitorSetting, error) {
setting, err := settingRepo.GetList()
if err != nil {
@@ -174,10 +283,13 @@ func (m *MonitorService) CleanData() error {
if err := global.MonitorDB.Exec("DELETE FROM monitor_networks").Error; err != nil {
return err
}
_ = global.GPUMonitorDB.Exec("DELETE FROM monitor_gpus").Error
return nil
}
func (m *MonitorService) Run() {
saveGPUDataToDB()
saveXPUDataToDB()
var itemModel model.MonitorBase
totalPercent, _ := cpu.Percent(3*time.Second, false)
if len(totalPercent) == 1 {
@@ -190,7 +302,7 @@ func (m *MonitorService) Run() {
itemModel.TopCPU = string(topItemCPU)
}
}
cpuCount, _ := cpu.Counts(false)
cpuCount, _ := psutil.CPUInfo.GetPhysicalCores(false)
loadInfo, _ := load.Avg()
itemModel.CpuLoad1 = loadInfo.Load1
itemModel.CpuLoad5 = loadInfo.Load5
@@ -207,7 +319,7 @@ func (m *MonitorService) Run() {
}
}
if err := settingRepo.CreateMonitorBase(itemModel); err != nil {
if err := monitorRepo.CreateMonitorBase(itemModel); err != nil {
global.LOG.Errorf("Insert basic monitoring data failed, err: %v", err)
}
@@ -220,9 +332,9 @@ func (m *MonitorService) Run() {
}
storeDays, _ := strconv.Atoi(MonitorStoreDays.Value)
timeForDelete := time.Now().AddDate(0, 0, -storeDays)
_ = settingRepo.DelMonitorBase(timeForDelete)
_ = settingRepo.DelMonitorIO(timeForDelete)
_ = settingRepo.DelMonitorNet(timeForDelete)
_ = monitorRepo.DelMonitorBase(timeForDelete)
_ = monitorRepo.DelMonitorIO(timeForDelete)
_ = monitorRepo.DelMonitorNet(timeForDelete)
}
func (m *MonitorService) loadDiskIO() {
@@ -302,9 +414,7 @@ func (m *MonitorService) saveIODataToDB(ctx context.Context, interval float64) {
}
}
}
if err := settingRepo.BatchCreateMonitorIO(ioList); err != nil {
global.LOG.Errorf("Insert io monitoring data failed, err: %v", err)
}
_ = monitorRepo.BatchCreateMonitorIO(ioList)
m.DiskIO <- ioStat2
}
}
@@ -341,9 +451,7 @@ func (m *MonitorService) saveNetDataToDB(ctx context.Context, interval float64)
}
}
if err := settingRepo.BatchCreateMonitorNet(netList); err != nil {
global.LOG.Errorf("Insert network monitoring data failed, err: %v", err)
}
_ = monitorRepo.BatchCreateMonitorNet(netList)
m.NetIO <- netStat2
}
}
@@ -377,15 +485,15 @@ func loadTopCPU() []dto.Process {
}
name, err := p.Name()
if err != nil {
name = "undifine"
name = "undefined"
}
cmd, err := p.Cmdline()
if err != nil {
cmd = "undifine"
cmd = "undefined"
}
user, err := p.Username()
if err != nil {
user = "undifine"
user = "undefined"
}
if len(top5) == 5 {
top5[minIndex] = dto.Process{Percent: percent, Pid: p.Pid, User: user, Name: name, Cmd: cmd}
@@ -428,15 +536,15 @@ func loadTopMem() []dto.Process {
}
name, err := p.Name()
if err != nil {
name = "undifine"
name = "undefined"
}
cmd, err := p.Cmdline()
if err != nil {
cmd = "undifine"
cmd = "undefined"
}
user, err := p.Username()
if err != nil {
user = "undifine"
user = "undefined"
}
percent, _ := p.MemoryPercent()
if len(top5) == 5 {
@@ -482,3 +590,92 @@ func StartMonitor(removeBefore bool, interval string) error {
return nil
}
func saveGPUDataToDB() {
exist, client := gpu.New()
if !exist {
return
}
gpuInfo, err := client.LoadGpuInfo()
if err != nil {
return
}
var list []model.MonitorGPU
for _, gpuItem := range gpuInfo.GPUs {
item := model.MonitorGPU{
ProductName: fmt.Sprintf("%d - %s", gpuItem.Index, gpuItem.ProductName),
GPUUtil: loadGPUInfoFloat(gpuItem.GPUUtil),
Temperature: loadGPUInfoFloat(gpuItem.Temperature),
PowerDraw: loadGPUInfoFloat(gpuItem.PowerDraw),
MaxPowerLimit: loadGPUInfoFloat(gpuItem.MaxPowerLimit),
MemUsed: loadGPUInfoFloat(gpuItem.MemUsed),
MemTotal: loadGPUInfoFloat(gpuItem.MemTotal),
FanSpeed: loadGPUInfoInt(gpuItem.FanSpeed),
}
process, _ := json.Marshal(gpuItem.Processes)
if len(process) != 0 {
item.Processes = string(process)
}
list = append(list, item)
}
if err := repo.NewIMonitorRepo().BatchCreateMonitorGPU(list); err != nil {
global.LOG.Errorf("batch create gpu monitor data failed, err: %v", err)
return
}
}
func saveXPUDataToDB() {
exist, client := xpu.New()
if !exist {
return
}
xpuInfo, err := client.LoadGpuInfo()
if err != nil {
return
}
var list []model.MonitorGPU
for _, xpuItem := range xpuInfo.Xpu {
item := model.MonitorGPU{
ProductName: fmt.Sprintf("%d - %s", xpuItem.Basic.DeviceID, xpuItem.Basic.DeviceName),
Temperature: loadGPUInfoFloat(xpuItem.Stats.Temperature),
PowerDraw: loadGPUInfoFloat(xpuItem.Stats.Power),
MemUsed: loadGPUInfoFloat(xpuItem.Stats.MemoryUsed),
MemTotal: loadGPUInfoFloat(xpuItem.Basic.Memory),
}
if len(xpuItem.Processes) != 0 {
var processItem []dto.GPUProcess
for _, ps := range xpuItem.Processes {
processItem = append(processItem, dto.GPUProcess{
Pid: fmt.Sprintf("%v", ps.PID),
Type: ps.SHR,
ProcessName: ps.Command,
UsedMemory: ps.Memory,
})
}
process, _ := json.Marshal(processItem)
if len(process) != 0 {
item.Processes = string(process)
}
}
list = append(list, item)
}
if err := repo.NewIMonitorRepo().BatchCreateMonitorGPU(list); err != nil {
global.LOG.Errorf("batch create gpu monitor data failed, err: %v", err)
return
}
}
func loadGPUInfoInt(val string) int {
val = strings.TrimSuffix(val, "%")
val = strings.TrimSpace(val)
data, _ := strconv.Atoi(val)
return data
}
func loadGPUInfoFloat(val string) float64 {
val = strings.TrimSpace(val)
suffixes := []string{"W", "MB", "MiB", "°C", "C", "%"}
for _, suffix := range suffixes {
val = strings.TrimSuffix(val, suffix)
}
val = strings.TrimSpace(val)
data, _ := strconv.ParseFloat(val, 64)
return data
}
+1 -1
View File
@@ -51,7 +51,7 @@ func getNginxFull(website *model.Website) (dto.NginxFull, error) {
if website != nil {
nginxFull.Website = *website
var siteNginxConfig dto.NginxConfig
siteConfigPath := GetSitePath(*website, SiteConf)
siteConfigPath := GetWebsiteConfigPath(*website)
siteNginxConfig.FilePath = siteConfigPath
siteNginxContent, err := os.ReadFile(siteConfigPath)
if err != nil {
+167 -18
View File
@@ -1,11 +1,15 @@
package service
import (
"bufio"
"fmt"
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
"github.com/1Panel-dev/1Panel/agent/utils/common"
"github.com/1Panel-dev/1Panel/agent/utils/websocket"
"github.com/shirou/gopsutil/v4/process"
"os"
"strconv"
"strings"
"time"
)
@@ -103,24 +107,20 @@ func (ps *ProcessService) GetProcessInfoByPID(pid int32) (*websocket.PsProcessDa
data.CmdLine = cmdline
}
if memInfo, err := p.MemoryInfo(); err == nil {
data.Rss = common.FormatBytes(memInfo.RSS)
data.VMS = common.FormatBytes(memInfo.VMS)
data.HWM = common.FormatBytes(memInfo.HWM)
data.Data = common.FormatBytes(memInfo.Data)
data.Stack = common.FormatBytes(memInfo.Stack)
data.Locked = common.FormatBytes(memInfo.Locked)
data.Swap = common.FormatBytes(memInfo.Swap)
data.RssValue = memInfo.RSS
} else {
data.Rss = "--"
data.Data = "--"
data.VMS = "--"
data.HWM = "--"
data.Stack = "--"
data.Locked = "--"
data.Swap = "--"
data.RssValue = 0
if memDetail, err := getMemoryDetail(p.Pid); err == nil {
data.Rss = common.FormatBytes(memDetail.RSS)
data.VMS = common.FormatBytes(memDetail.VMS)
data.HWM = common.FormatBytes(memDetail.HWM)
data.Data = common.FormatBytes(memDetail.Data)
data.Stack = common.FormatBytes(memDetail.Stack)
data.Locked = common.FormatBytes(memDetail.Locked)
data.Swap = common.FormatBytes(memDetail.Swap)
data.Dirty = common.FormatBytes(memDetail.Dirty)
data.RssValue = memDetail.RSS
data.PSS = common.FormatBytes(memDetail.PSS)
data.USS = common.FormatBytes(memDetail.USS)
data.Shared = common.FormatBytes(memDetail.Shared)
data.Text = common.FormatBytes(memDetail.Text)
}
if envs, err := p.Environ(); err == nil {
@@ -133,3 +133,152 @@ func (ps *ProcessService) GetProcessInfoByPID(pid int32) (*websocket.PsProcessDa
return data, nil
}
type MemoryDetail struct {
RSS uint64
VMS uint64
HWM uint64
Data uint64
Stack uint64
Locked uint64
Swap uint64
PSS uint64
USS uint64
Shared uint64
Text uint64
Dirty uint64
}
func getMemoryDetail(pid int32) (*MemoryDetail, error) {
mem := &MemoryDetail{}
if err := readStatus(pid, mem); err != nil {
return nil, err
}
if err := readSmapsRollup(pid, mem); err != nil {
if err := readSmaps(pid, mem); err != nil {
return nil, err
}
}
return mem, nil
}
func readStatus(pid int32, mem *MemoryDetail) error {
filePath := fmt.Sprintf("/proc/%d/status", pid)
file, err := os.Open(filePath)
if err != nil {
return err
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
fields := strings.Fields(line)
if len(fields) < 2 {
continue
}
key := strings.TrimSuffix(fields[0], ":")
value, _ := strconv.ParseUint(fields[1], 10, 64)
value *= 1024
switch key {
case "VmRSS":
mem.RSS = value
case "VmSize":
mem.VMS = value
case "VmData":
mem.Data = value
case "VmSwap":
mem.Swap = value
case "VmExe":
mem.Text = value
case "RssShmem":
mem.Shared = value
case "VmHWM":
mem.HWM = value
case "VmStk":
mem.Stack = value
case "VmLck":
mem.Locked = value
}
}
return scanner.Err()
}
func readSmapsRollup(pid int32, mem *MemoryDetail) error {
filePath := fmt.Sprintf("/proc/%d/smaps_rollup", pid)
file, err := os.Open(filePath)
if err != nil {
return err
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
fields := strings.Fields(line)
if len(fields) < 2 {
continue
}
key := strings.TrimSuffix(fields[0], ":")
value, _ := strconv.ParseUint(fields[1], 10, 64)
value *= 1024
switch key {
case "Pss":
mem.PSS = value
case "Private_Clean", "Private_Dirty":
mem.USS += value
case "Shared_Clean", "Shared_Dirty":
if mem.Shared == 0 {
mem.Shared = value
}
}
}
return scanner.Err()
}
func readSmaps(pid int32, mem *MemoryDetail) error {
filePath := fmt.Sprintf("/proc/%d/smaps", pid)
file, err := os.Open(filePath)
if err != nil {
return err
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
fields := strings.Fields(line)
if len(fields) < 2 {
continue
}
key := strings.TrimSuffix(fields[0], ":")
value, _ := strconv.ParseUint(fields[1], 10, 64)
value *= 1024
switch key {
case "Pss":
mem.PSS += value
case "Private_Clean", "Private_Dirty":
mem.USS += value
case "Shared_Clean", "Shared_Dirty":
if mem.Shared == 0 {
mem.Shared += value
}
}
}
return scanner.Err()
}
+3
View File
@@ -76,6 +76,9 @@ func (r RecycleBinService) Page(search dto.PageInfo) (int64, []response.RecycleB
}
func (r RecycleBinService) Create(create request.RecycleBinCreate) error {
if files.IsProtected(create.SourcePath) {
return buserr.New("ErrPathNotDelete")
}
op := files.NewFileOp()
if !op.Stat(create.SourcePath) {
return buserr.New("ErrLinkPathNotFound")
+1 -1
View File
@@ -722,7 +722,7 @@ func unInstallPHPExtension(runtime *model.Runtime, delExtensions []string) error
if ext.Name == del {
exist = true
delMap[ext.Check] = struct{}{}
delPHPExtensions(dir, phpExtensionDir, ext.Check, ext.Name)
delPHPExtensions(dir, phpExtensionDir, ext.File, ext.Name)
break
}
}
+36
View File
@@ -7,7 +7,9 @@ import (
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/utils/encrypt"
"github.com/1Panel-dev/1Panel/agent/utils/ssh"
"github.com/jinzhu/copier"
@@ -21,9 +23,12 @@ type ISettingService interface {
TestConnByInfo(req dto.SSHConnData) bool
SaveConnInfo(req dto.SSHConnData) error
SetDefaultIsConn(req dto.SSHDefaultConn) error
GetSystemProxy() (*dto.SystemProxy, error)
GetLocalConn() dto.SSHConnData
GetSettingByKey(key string) string
SaveDescription(req dto.CommonDescription) error
}
func NewISettingService() ISettingService {
@@ -122,6 +127,15 @@ func (u *SettingService) SaveConnInfo(req dto.SSHConnData) error {
return nil
}
func (u *SettingService) SetDefaultIsConn(req dto.SSHDefaultConn) error {
if req.DefaultConn == constant.StatusDisable && req.WithReset {
if err := settingRepo.Update("LocalSSHConn", ""); err != nil {
return err
}
}
return settingRepo.Update("LocalSSHConnShow", req.DefaultConn)
}
func (u *SettingService) GetSystemProxy() (*dto.SystemProxy, error) {
systemProxy := dto.SystemProxy{}
systemProxy.Type, _ = settingRepo.GetValueByKey("ProxyType")
@@ -135,6 +149,7 @@ func (u *SettingService) GetSystemProxy() (*dto.SystemProxy, error) {
func (u *SettingService) GetLocalConn() dto.SSHConnData {
var data dto.SSHConnData
data.LocalSSHConnShow, _ = settingRepo.GetValueByKey("LocalSSHConnShow")
connItem, _ := settingRepo.GetValueByKey("LocalSSHConn")
if len(connItem) == 0 {
return data
@@ -170,3 +185,24 @@ func (u *SettingService) GetSettingByKey(key string) string {
return value
}
}
func (u *SettingService) SaveDescription(req dto.CommonDescription) error {
if len(req.Description) == 0 && !req.IsPinned {
_ = settingRepo.DelDescription(req.ID)
return nil
}
data, _ := settingRepo.GetDescription(settingRepo.WithByDescriptionID(req.ID), repo.WithByType(req.Type), repo.WithByDetailType(req.DetailType))
if data.ID == "" {
if err := copier.Copy(&data, &req); err != nil {
return err
}
return settingRepo.CreateDescription(&data)
}
valMap := make(map[string]interface{})
valMap["type"] = req.Type
valMap["detail_type"] = req.DetailType
valMap["is_pinned"] = req.IsPinned
valMap["description"] = req.Description
return settingRepo.UpdateDescription(data.ID, valMap)
}
+30 -7
View File
@@ -224,12 +224,16 @@ func loadApps(fileOp fileUtils.FileOp) ([]dto.DataTree, error) {
appTreeMap := make(map[string]dto.DataTree)
for _, app := range apps {
itemApp := dto.DataTree{
ID: uuid.NewString(),
Label: fmt.Sprintf("%s - %s", app.App.Name, app.Name),
Key: app.App.Key,
Name: app.Name,
ID: uuid.NewString(),
Label: fmt.Sprintf("%s - %s", app.App.Name, app.Name),
Key: app.App.Key,
Name: app.Name,
IsLocal: app.App.Resource == "local",
}
appPath := path.Join(global.Dir.DataDir, "apps", app.App.Key, app.Name)
if itemApp.IsLocal {
appPath = path.Join(global.Dir.AppDir, "local", strings.TrimPrefix(app.App.Key, "local"), app.Name)
}
itemAppData := dto.DataTree{ID: uuid.NewString(), Label: "appData", Key: app.App.Key, Name: app.Name, IsCheck: true, Path: appPath}
if app.App.Key == constant.AppOpenresty && len(websites) != 0 {
itemAppData.IsDisable = true
@@ -295,7 +299,11 @@ func loadAppImage(list []dto.DataTree) []dto.DataTree {
for i := 0; i < len(list); i++ {
itemAppImage := dto.DataTree{ID: uuid.NewString(), Label: "appImage"}
stdout, err := cmd.RunDefaultWithStdoutBashCf("cat %s | grep image: ", path.Join(global.Dir.AppDir, list[i].Key, list[i].Name, "docker-compose.yml"))
appPath := path.Join(global.Dir.DataDir, "apps", list[i].Key, list[i].Name)
if list[i].IsLocal {
appPath = path.Join(global.Dir.AppDir, "local", strings.TrimPrefix(list[i].Key, "local"), list[i].Name)
}
stdout, err := cmd.RunDefaultWithStdoutBashCf("cat %s | grep image: ", path.Join(appPath, "docker-compose.yml"))
if err != nil {
list[i].Children = append(list[i].Children, itemAppImage)
continue
@@ -328,11 +336,26 @@ func loadPanelFile(fileOp fileUtils.FileOp) ([]dto.DataTree, error) {
Path: path.Join(global.Dir.DataDir, fileItem.Name()),
}
switch itemData.Label {
case "agent", "runtime", "docker", "task", "geo", "secret", "uploads":
case "runtime", "docker", "task", "geo", "secret", "uploads":
itemData.IsDisable = true
case "clamav", "download":
panelPath := path.Join(global.Dir.DataDir, itemData.Label)
itemData.Children, _ = loadFile(panelPath, 3, fileOp)
case "agent":
panelPath := path.Join(global.Dir.DataDir, itemData.Label)
itemData.Children, _ = loadFile(panelPath, 2, fileOp)
itemData.IsCheck = false
for i := 0; i < len(itemData.Children); i++ {
if itemData.Children[i].Label != "package" {
itemData.Children[i].IsDisable = true
} else {
itemData.Size = -itemData.Children[i].Size
itemData.Children[i].IsCheck = false
for j := 0; j < len(itemData.Children[i].Children); j++ {
itemData.Children[i].Children[j].IsCheck = false
}
}
}
case "apps", "backup", "log", "db", "tmp":
continue
}
@@ -341,7 +364,7 @@ func loadPanelFile(fileOp fileUtils.FileOp) ([]dto.DataTree, error) {
if err != nil {
continue
}
itemData.Size = uint64(sizeItem)
itemData.Size = uint64(int64(sizeItem) + int64(itemData.Size))
} else {
fileInfo, err := fileItem.Info()
if err != nil {
+22 -3
View File
@@ -12,6 +12,8 @@ import (
"time"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/docker/docker/api/types/image"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/model"
@@ -23,6 +25,7 @@ import (
"github.com/1Panel-dev/1Panel/agent/utils/common"
"github.com/1Panel-dev/1Panel/agent/utils/controller"
"github.com/1Panel-dev/1Panel/agent/utils/copier"
"github.com/1Panel-dev/1Panel/agent/utils/docker"
"github.com/1Panel-dev/1Panel/agent/utils/files"
"gorm.io/gorm"
)
@@ -259,11 +262,12 @@ func loadDbConn(snap *snapHelper, targetDir string, req dto.SnapshotCreate) erro
return err
}
} else {
taskDB, err := common.LoadDBConnByPathWithErr(path.Join(targetDir, "db/task.db"), "core.db")
taskDB, err := common.LoadDBConnByPathWithErr(path.Join(targetDir, "db/task.db"), "task.db")
snap.Task.LogWithStatus(i18n.GetWithName("SnapNewDB", "task"), err)
if err != nil {
return err
}
defer common.CloseDB(taskDB)
_ = taskDB.Where("id = ?", req.TaskID).Delete(&model.Task{}).Error
}
if !req.WithOperationLog && global.IsMaster {
@@ -368,9 +372,21 @@ func snapAppImage(snap snapHelper, req dto.SnapshotCreate, targetDir string) err
}
}
client, err := docker.NewDockerClient()
if err != nil {
snap.Task.Log("load docker client failed, skip save app images")
return nil
}
images, err := client.ImageList(context.Background(), image.ListOptions{})
if err != nil {
snap.Task.Log("list docker images failed, skip save app images")
return nil
}
var existImages []string
for _, img := range images {
existImages = append(existImages, img.RepoTags...)
}
var imageList []string
existStr, _ := cmd.RunDefaultWithStdoutBashC("docker images | awk '{print $1\":\"$2}' | grep -v REPOSITORY:TAG")
existImages := strings.Split(existStr, "\n")
for _, app := range req.AppData {
for _, item := range app.Children {
if item.Label == "appImage" && item.IsCheck {
@@ -546,5 +562,8 @@ func snapUpload(snap snapHelper, accounts string, downloadID, retry uint, file s
src := path.Join(global.Dir.LocalBackupDir, "tmp/system", path.Base(file))
dst := path.Join("system_snapshot", path.Base(file))
accountMap := NewBackupClientMap(strings.Split(accounts, ","))
if !accountMap[fmt.Sprintf("%d", downloadID)].isOk {
return buserr.New(i18n.GetMsgWithDetail("LoadBackupFailed", accountMap[fmt.Sprintf("%d", downloadID)].message))
}
return uploadWithMap(snap.Task, accountMap, src, dst, accounts, downloadID, retry)
}
+16 -22
View File
@@ -88,31 +88,25 @@ func (t TensorRTLLMService) Page(req request.TensorRTLLMSearch) response.TensorR
}
composeByte, err := files.NewFileOp().GetContent(path.Join(global.Dir.TensorRTLLMDir, item.Name, "docker-compose.yml"))
if err != nil {
continue
}
serverDTO.Environments, err = getDockerComposeEnvironments(composeByte)
if err != nil {
continue
if err == nil {
serverDTO.Environments, _ = getDockerComposeEnvironments(composeByte)
}
volumes, err := getDockerComposeVolumes(composeByte)
if err != nil {
continue
}
var defaultVolumes = map[string]string{
"${MODEL_PATH}": "${MODEL_PATH}",
}
for _, volume := range volumes {
exist := false
for key, value := range defaultVolumes {
if key == volume.Source && value == volume.Target {
exist = true
break
}
if err == nil {
var defaultVolumes = map[string]string{
"${MODEL_PATH}": "${MODEL_PATH}",
}
if !exist {
serverDTO.Volumes = append(serverDTO.Volumes, volume)
for _, volume := range volumes {
exist := false
for key, value := range defaultVolumes {
if key == volume.Source && value == volume.Target {
exist = true
break
}
}
if !exist {
serverDTO.Volumes = append(serverDTO.Volumes, volume)
}
}
}
items = append(items, serverDTO)
+202 -1362
View File
File diff suppressed because it is too large Load Diff
+312
View File
@@ -0,0 +1,312 @@
package service
import (
"bufio"
"fmt"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
"github.com/1Panel-dev/1Panel/agent/app/dto/response"
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/cmd/server/nginx_conf"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/utils/files"
"github.com/1Panel-dev/1Panel/agent/utils/nginx"
"github.com/1Panel-dev/1Panel/agent/utils/nginx/components"
"github.com/1Panel-dev/1Panel/agent/utils/nginx/parser"
"golang.org/x/crypto/bcrypt"
"os"
"path"
"strings"
)
func (w WebsiteService) GetAuthBasics(req request.NginxAuthReq) (res response.NginxAuthRes, err error) {
var (
website model.Website
authContent []byte
nginxParams []response.NginxParam
)
website, err = websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))
if err != nil {
return
}
absoluteAuthPath := GetSitePath(website, SiteRootAuthBasicPath)
fileOp := files.NewFileOp()
if !fileOp.Stat(absoluteAuthPath) {
return
}
nginxParams, err = getNginxParamsByKeys(constant.NginxScopeServer, []string{"auth_basic"}, &website)
if err != nil {
return
}
res.Enable = len(nginxParams[0].Params) > 0
authContent, err = fileOp.GetContent(absoluteAuthPath)
authArray := strings.Split(string(authContent), "\n")
for _, line := range authArray {
if line == "" {
continue
}
params := strings.Split(line, ":")
auth := dto.NginxAuth{
Username: params[0],
}
if len(params) == 3 {
auth.Remark = params[2]
}
res.Items = append(res.Items, auth)
}
return
}
func (w WebsiteService) UpdateAuthBasic(req request.NginxAuthUpdate) (err error) {
var (
website model.Website
params []dto.NginxParam
authContent []byte
authArray []string
)
website, err = websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))
if err != nil {
return err
}
authPath := fmt.Sprintf("/www/sites/%s/auth_basic/auth.pass", website.Alias)
absoluteAuthPath := GetSitePath(website, SiteRootAuthBasicPath)
fileOp := files.NewFileOp()
if !fileOp.Stat(path.Dir(absoluteAuthPath)) {
_ = fileOp.CreateDir(path.Dir(absoluteAuthPath), constant.DirPerm)
}
if !fileOp.Stat(absoluteAuthPath) {
_ = fileOp.CreateFile(absoluteAuthPath)
}
params = append(params, dto.NginxParam{Name: "auth_basic", Params: []string{`"Authentication"`}})
params = append(params, dto.NginxParam{Name: "auth_basic_user_file", Params: []string{authPath}})
authContent, err = fileOp.GetContent(absoluteAuthPath)
if err != nil {
return
}
if len(authContent) > 0 {
authArray = strings.Split(string(authContent), "\n")
}
switch req.Operate {
case "disable":
return deleteNginxConfig(constant.NginxScopeServer, params, &website)
case "enable":
return updateNginxConfig(constant.NginxScopeServer, params, &website)
case "create":
for _, line := range authArray {
authParams := strings.Split(line, ":")
username := authParams[0]
if username == req.Username {
err = buserr.New("ErrUsernameIsExist")
return
}
}
var passwdHash []byte
passwdHash, err = bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
if err != nil {
return
}
line := fmt.Sprintf("%s:%s\n", req.Username, passwdHash)
if req.Remark != "" {
line = fmt.Sprintf("%s:%s:%s\n", req.Username, passwdHash, req.Remark)
}
authArray = append(authArray, line)
case "edit":
userExist := false
for index, line := range authArray {
authParams := strings.Split(line, ":")
username := authParams[0]
if username == req.Username {
userExist = true
var passwdHash []byte
passwdHash, err = bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
if err != nil {
return
}
userPasswd := fmt.Sprintf("%s:%s\n", req.Username, passwdHash)
if req.Remark != "" {
userPasswd = fmt.Sprintf("%s:%s:%s\n", req.Username, passwdHash, req.Remark)
}
authArray[index] = userPasswd
}
}
if !userExist {
err = buserr.New("ErrUsernameIsNotExist")
return
}
case "delete":
deleteIndex := -1
for index, line := range authArray {
authParams := strings.Split(line, ":")
username := authParams[0]
if username == req.Username {
deleteIndex = index
}
}
if deleteIndex < 0 {
return
}
authArray = append(authArray[:deleteIndex], authArray[deleteIndex+1:]...)
}
var passFile *os.File
passFile, err = os.Create(absoluteAuthPath)
if err != nil {
return
}
defer passFile.Close()
writer := bufio.NewWriter(passFile)
for _, line := range authArray {
if line == "" {
continue
}
_, err = writer.WriteString(line + "\n")
if err != nil {
return
}
}
err = writer.Flush()
if err != nil {
return
}
authContent, err = fileOp.GetContent(absoluteAuthPath)
if err != nil {
return
}
if len(authContent) == 0 {
if err = deleteNginxConfig(constant.NginxScopeServer, params, &website); err != nil {
return
}
}
return
}
func (w WebsiteService) GetPathAuthBasics(req request.NginxAuthReq) (res []response.NginxPathAuthRes, err error) {
var (
website model.Website
authContent []byte
)
website, err = websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))
if err != nil {
return
}
fileOp := files.NewFileOp()
absoluteAuthDir := GetSitePath(website, SitePathAuthBasicDir)
passDir := path.Join(absoluteAuthDir, "pass")
if !fileOp.Stat(absoluteAuthDir) || !fileOp.Stat(passDir) {
return
}
entries, err := os.ReadDir(absoluteAuthDir)
if err != nil {
return nil, err
}
for _, entry := range entries {
if !entry.IsDir() {
name := strings.TrimSuffix(entry.Name(), ".conf")
pathAuth := dto.NginxPathAuth{
Name: name,
}
configPath := path.Join(absoluteAuthDir, entry.Name())
content, err := fileOp.GetContent(configPath)
if err != nil {
return nil, err
}
config, err := parser.NewStringParser(string(content)).Parse()
if err != nil {
return nil, err
}
directives := config.Directives
location, _ := directives[0].(*components.Location)
pathAuth.Path = strings.TrimPrefix(location.Match, "^")
passPath := path.Join(passDir, fmt.Sprintf("%s.pass", name))
authContent, err = fileOp.GetContent(passPath)
if err != nil {
return nil, err
}
authArray := strings.Split(string(authContent), "\n")
for _, line := range authArray {
if line == "" {
continue
}
params := strings.Split(line, ":")
pathAuth.Username = params[0]
if len(params) == 3 {
pathAuth.Remark = params[2]
}
}
res = append(res, response.NginxPathAuthRes{
NginxPathAuth: pathAuth,
})
}
}
return
}
func (w WebsiteService) UpdatePathAuthBasic(req request.NginxPathAuthUpdate) error {
website, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))
if err != nil {
return err
}
fileOp := files.NewFileOp()
authDir := GetSitePath(website, SitePathAuthBasicDir)
if !fileOp.Stat(authDir) {
_ = fileOp.CreateDir(authDir, constant.DirPerm)
}
passDir := path.Join(authDir, "pass")
if !fileOp.Stat(passDir) {
_ = fileOp.CreateDir(passDir, constant.DirPerm)
}
confPath := path.Join(authDir, fmt.Sprintf("%s.conf", req.Name))
passPath := path.Join(passDir, fmt.Sprintf("%s.pass", req.Name))
var config *components.Config
switch req.Operate {
case "delete":
_ = fileOp.DeleteFile(confPath)
_ = fileOp.DeleteFile(passPath)
return updateNginxConfig(constant.NginxScopeServer, nil, &website)
case "create":
config, err = parser.NewStringParser(string(nginx_conf.PathAuth)).Parse()
if err != nil {
return err
}
if fileOp.Stat(confPath) || fileOp.Stat(passPath) {
return buserr.New("ErrNameIsExist")
}
case "edit":
par, err := parser.NewParser(confPath)
if err != nil {
return err
}
config, err = par.Parse()
if err != nil {
return err
}
}
config.FilePath = confPath
directives := config.Directives
location, _ := directives[0].(*components.Location)
location.UpdateDirective("auth_basic_user_file", []string{fmt.Sprintf("/www/sites/%s/path_auth/pass/%s", website.Alias, fmt.Sprintf("%s.pass", req.Name))})
location.ChangePath("~*", fmt.Sprintf("^%s", req.Path))
var passwdHash []byte
passwdHash, err = bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
if err != nil {
return err
}
line := fmt.Sprintf("%s:%s\n", req.Username, passwdHash)
if req.Remark != "" {
line = fmt.Sprintf("%s:%s:%s\n", req.Username, passwdHash, req.Remark)
}
_ = fileOp.SaveFile(passPath, line, constant.DirPerm)
if err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
return buserr.WithErr("ErrUpdateBuWebsite", err)
}
nginxInclude := fmt.Sprintf("/www/sites/%s/path_auth/*.conf", website.Alias)
if err = updateNginxConfig(constant.NginxScopeServer, []dto.NginxParam{{Name: "include", Params: []string{nginxInclude}}}, &website); err != nil {
return nil
}
return nil
}
+185
View File
@@ -0,0 +1,185 @@
package service
import (
"context"
"encoding/json"
"fmt"
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/utils/files"
"path"
"strconv"
)
func (w WebsiteService) CreateWebsiteDomain(create request.WebsiteDomainCreate) ([]model.WebsiteDomain, error) {
var (
domainModels []model.WebsiteDomain
addPorts []int
)
httpPort, httpsPort, err := getAppInstallPort(constant.AppOpenresty)
if err != nil {
return nil, err
}
website, err := websiteRepo.GetFirst(repo.WithByID(create.WebsiteID))
if err != nil {
return nil, err
}
domainModels, addPorts, _, err = getWebsiteDomains(create.Domains, httpPort, httpsPort, create.WebsiteID)
if err != nil {
return nil, err
}
go func() {
_ = OperateFirewallPort(nil, addPorts)
}()
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
if err != nil {
return nil, err
}
wafDataPath := path.Join(nginxInstall.GetPath(), "1pwaf", "data")
fileOp := files.NewFileOp()
if fileOp.Stat(wafDataPath) {
websitesConfigPath := path.Join(wafDataPath, "conf", "sites.json")
content, err := fileOp.GetContent(websitesConfigPath)
if err != nil {
return nil, err
}
var websitesArray []request.WafWebsite
if content != nil {
if err := json.Unmarshal(content, &websitesArray); err != nil {
return nil, err
}
}
for index, wafWebsite := range websitesArray {
if wafWebsite.Key == website.Alias {
wafSite := request.WafWebsite{
Key: website.Alias,
Domains: wafWebsite.Domains,
Host: wafWebsite.Host,
}
for _, domain := range domainModels {
wafSite.Domains = append(wafSite.Domains, domain.Domain)
wafSite.Host = append(wafSite.Host, domain.Domain+":"+strconv.Itoa(domain.Port))
}
if len(wafSite.Host) == 0 {
wafSite.Host = []string{}
}
websitesArray[index] = wafSite
break
}
}
websitesContent, err := json.Marshal(websitesArray)
if err != nil {
return nil, err
}
if err := fileOp.SaveFileWithByte(websitesConfigPath, websitesContent, constant.DirPerm); err != nil {
return nil, err
}
}
if err = addListenAndServerName(website, domainModels); err != nil {
return nil, err
}
return domainModels, websiteDomainRepo.BatchCreate(context.TODO(), domainModels)
}
func (w WebsiteService) GetWebsiteDomain(websiteId uint) ([]model.WebsiteDomain, error) {
return websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteId(websiteId))
}
func (w WebsiteService) DeleteWebsiteDomain(domainId uint) error {
webSiteDomain, err := websiteDomainRepo.GetFirst(repo.WithByID(domainId))
if err != nil {
return err
}
if websiteDomains, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteId(webSiteDomain.WebsiteID)); len(websiteDomains) == 1 {
return fmt.Errorf("can not delete last domain")
}
website, err := websiteRepo.GetFirst(repo.WithByID(webSiteDomain.WebsiteID))
if err != nil {
return err
}
var ports []int
if oldDomains, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteId(webSiteDomain.WebsiteID), websiteDomainRepo.WithPort(webSiteDomain.Port)); len(oldDomains) == 1 {
ports = append(ports, webSiteDomain.Port)
}
var domains []string
if oldDomains, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteId(webSiteDomain.WebsiteID), websiteDomainRepo.WithDomain(webSiteDomain.Domain)); len(oldDomains) == 1 {
domains = append(domains, webSiteDomain.Domain)
}
if len(ports) > 0 || len(domains) > 0 {
stringBinds := make([]string, len(ports))
for i := 0; i < len(ports); i++ {
stringBinds[i] = strconv.Itoa(ports[i])
}
if err := deleteListenAndServerName(website, stringBinds, domains); err != nil {
return err
}
}
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
if err != nil {
return err
}
wafDataPath := path.Join(nginxInstall.GetPath(), "1pwaf", "data")
fileOp := files.NewFileOp()
if fileOp.Stat(wafDataPath) {
websitesConfigPath := path.Join(wafDataPath, "conf", "sites.json")
content, err := fileOp.GetContent(websitesConfigPath)
if err != nil {
return err
}
var websitesArray []request.WafWebsite
var newWebsitesArray []request.WafWebsite
if content != nil {
if err := json.Unmarshal(content, &websitesArray); err != nil {
return err
}
}
for _, wafWebsite := range websitesArray {
if wafWebsite.Key == website.Alias {
wafSite := wafWebsite
oldDomains := wafSite.Domains
var newDomains []string
for _, domain := range oldDomains {
if domain == webSiteDomain.Domain {
continue
}
newDomains = append(newDomains, domain)
}
wafSite.Domains = newDomains
oldHostArray := wafSite.Host
var newHostArray []string
for _, host := range oldHostArray {
if host == webSiteDomain.Domain+":"+strconv.Itoa(webSiteDomain.Port) {
continue
}
newHostArray = append(newHostArray, host)
}
wafSite.Host = newHostArray
if len(wafSite.Host) == 0 {
wafSite.Host = []string{}
}
newWebsitesArray = append(newWebsitesArray, wafSite)
} else {
newWebsitesArray = append(newWebsitesArray, wafWebsite)
}
}
websitesContent, err := json.Marshal(newWebsitesArray)
if err != nil {
return err
}
if err = fileOp.SaveFileWithByte(websitesConfigPath, websitesContent, constant.DirPerm); err != nil {
return err
}
}
return websiteDomainRepo.DeleteBy(context.TODO(), repo.WithByID(domainId))
}
+229
View File
@@ -0,0 +1,229 @@
package service
import (
"bytes"
"fmt"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/cmd/server/nginx_conf"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/utils/files"
"github.com/1Panel-dev/1Panel/agent/utils/nginx"
"github.com/1Panel-dev/1Panel/agent/utils/nginx/components"
"github.com/1Panel-dev/1Panel/agent/utils/nginx/parser"
"os"
"path"
"strings"
)
func (w WebsiteService) GetLoadBalances(id uint) ([]dto.NginxUpstream, error) {
website, err := websiteRepo.GetFirst(repo.WithByID(id))
if err != nil {
return nil, err
}
includeDir := GetSitePath(website, SiteUpstreamDir)
fileOp := files.NewFileOp()
if !fileOp.Stat(includeDir) {
return nil, nil
}
entries, err := os.ReadDir(includeDir)
if err != nil {
return nil, err
}
var res []dto.NginxUpstream
for _, entry := range entries {
if entry.IsDir() {
continue
}
name := entry.Name()
if !strings.HasSuffix(name, ".conf") {
continue
}
upstreamName := strings.TrimSuffix(name, ".conf")
upstream := dto.NginxUpstream{
Name: upstreamName,
}
upstreamPath := path.Join(includeDir, name)
content, err := fileOp.GetContent(upstreamPath)
if err != nil {
return nil, err
}
upstream.Content = string(content)
nginxParser, err := parser.NewParser(upstreamPath)
if err != nil {
return nil, err
}
config, err := nginxParser.Parse()
if err != nil {
return nil, err
}
upstreams := config.FindUpstreams()
for _, up := range upstreams {
if up.UpstreamName == upstreamName {
directives := up.GetDirectives()
for _, d := range directives {
dName := d.GetName()
if _, ok := dto.LBAlgorithms[dName]; ok {
upstream.Algorithm = dName
}
}
upstream.Servers = getNginxUpstreamServers(up.UpstreamServers)
}
}
res = append(res, upstream)
}
return res, nil
}
func (w WebsiteService) CreateLoadBalance(req request.WebsiteLBCreate) error {
website, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))
if err != nil {
return err
}
includeDir := GetSitePath(website, SiteUpstreamDir)
fileOp := files.NewFileOp()
if !fileOp.Stat(includeDir) {
_ = fileOp.CreateDir(includeDir, constant.DirPerm)
}
filePath := path.Join(includeDir, fmt.Sprintf("%s.conf", req.Name))
if fileOp.Stat(filePath) {
return buserr.New("ErrNameIsExist")
}
config, err := parser.NewStringParser(string(nginx_conf.Upstream)).Parse()
if err != nil {
return err
}
config.Block = &components.Block{}
config.FilePath = filePath
upstream := components.Upstream{
UpstreamName: req.Name,
}
if req.Algorithm != "default" {
upstream.UpdateDirective(req.Algorithm, []string{})
}
upstream.UpstreamServers = parseUpstreamServers(req.Servers)
config.Block.Directives = append(config.Block.Directives, &upstream)
defer func() {
if err != nil {
_ = fileOp.DeleteFile(filePath)
}
}()
if err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
return buserr.WithErr("ErrUpdateBuWebsite", err)
}
nginxInclude := fmt.Sprintf("/www/sites/%s/upstream/*.conf", website.Alias)
if err = updateNginxConfig("", []dto.NginxParam{{Name: "include", Params: []string{nginxInclude}}}, &website); err != nil {
return err
}
return nil
}
func (w WebsiteService) UpdateLoadBalance(req request.WebsiteLBUpdate) error {
website, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))
if err != nil {
return err
}
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
if err != nil {
return err
}
includeDir := GetSitePath(website, SiteUpstreamDir)
fileOp := files.NewFileOp()
filePath := path.Join(includeDir, fmt.Sprintf("%s.conf", req.Name))
if !fileOp.Stat(filePath) {
return nil
}
oldContent, err := fileOp.GetContent(filePath)
if err != nil {
return err
}
parser, err := parser.NewParser(filePath)
if err != nil {
return err
}
config, err := parser.Parse()
if err != nil {
return err
}
upstreams := config.FindUpstreams()
for _, up := range upstreams {
if up.UpstreamName == req.Name {
directives := up.GetDirectives()
for _, d := range directives {
dName := d.GetName()
if _, ok := dto.LBAlgorithms[dName]; ok {
up.RemoveDirective(dName, nil)
}
}
if req.Algorithm != "default" {
up.UpdateDirective(req.Algorithm, []string{})
}
up.UpstreamServers = parseUpstreamServers(req.Servers)
}
}
if err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
return buserr.WithErr("ErrUpdateBuWebsite", err)
}
return nginxCheckAndReload(string(oldContent), filePath, nginxInstall.ContainerName)
}
func (w WebsiteService) DeleteLoadBalance(req request.WebsiteLBDelete) error {
website, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))
if err != nil {
return err
}
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
if err != nil {
return err
}
proxies, _ := w.GetProxies(website.ID)
if len(proxies) > 0 {
for _, proxy := range proxies {
if strings.HasSuffix(proxy.ProxyPass, fmt.Sprintf("://%s", req.Name)) {
return buserr.New("ErrProxyIsUsed")
}
}
}
includeDir := GetSitePath(website, SiteUpstreamDir)
fileOp := files.NewFileOp()
filePath := path.Join(includeDir, fmt.Sprintf("%s.conf", req.Name))
if !fileOp.Stat(filePath) {
return nil
}
if err = fileOp.DeleteFile(filePath); err != nil {
return err
}
return opNginx(nginxInstall.ContainerName, constant.NginxReload)
}
func (w WebsiteService) UpdateLoadBalanceFile(req request.WebsiteLBUpdateFile) error {
website, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))
if err != nil {
return err
}
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
if err != nil {
return err
}
includeDir := GetSitePath(website, SiteUpstreamDir)
filePath := path.Join(includeDir, fmt.Sprintf("%s.conf", req.Name))
fileOp := files.NewFileOp()
oldContent, err := fileOp.GetContent(filePath)
if err != nil {
return err
}
if err = fileOp.WriteFile(filePath, strings.NewReader(req.Content), constant.DirPerm); err != nil {
return err
}
defer func() {
if err != nil {
_ = fileOp.WriteFile(filePath, bytes.NewReader(oldContent), constant.DirPerm)
}
}()
return opNginx(nginxInstall.ContainerName, constant.NginxReload)
}
+126
View File
@@ -0,0 +1,126 @@
package service
import (
"context"
"fmt"
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/app/task"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/i18n"
"sort"
)
func (w WebsiteService) BatchOpWebsite(req request.BatchWebsiteOp) error {
websites, _ := websiteRepo.List(repo.WithByIDs(req.IDs))
opTask, err := task.NewTaskWithOps(i18n.GetMsgByKey("Status"), task.TaskBatch, task.TaskScopeWebsite, req.TaskID, 0)
if err != nil {
return err
}
sort.SliceStable(websites, func(i, j int) bool {
if websites[i].Type == constant.Subsite && websites[j].Type != constant.Subsite {
return true
}
if websites[i].Type != constant.Subsite && websites[j].Type == constant.Subsite {
return false
}
return false
})
opWebsiteTask := func(t *task.Task) error {
for _, web := range websites {
msg := fmt.Sprintf("%s %s", i18n.GetMsgByKey(req.Operate), web.PrimaryDomain)
switch req.Operate {
case constant.StopWeb, constant.StartWeb:
if err := opWebsite(&web, req.Operate); err != nil {
t.LogFailedWithErr(msg, err)
continue
}
_ = websiteRepo.Save(context.Background(), &web)
case "delete":
if err := w.DeleteWebsite(request.WebsiteDelete{
ID: web.ID,
}); err != nil {
t.LogFailedWithErr(msg, err)
continue
}
}
t.LogSuccess(msg)
}
return nil
}
opTask.AddSubTask("", opWebsiteTask, nil)
go func() {
_ = opTask.Execute()
}()
return nil
}
func (w WebsiteService) BatchSetGroup(req request.BatchWebsiteGroup) error {
websites, _ := websiteRepo.List(repo.WithByIDs(req.IDs))
for _, web := range websites {
web.WebsiteGroupID = req.GroupID
if err := websiteRepo.Save(context.Background(), &web); err != nil {
return err
}
}
return nil
}
func (w WebsiteService) BatchSetHttps(ctx context.Context, req request.BatchWebsiteHttps) error {
websites, _ := websiteRepo.List(repo.WithByIDs(req.IDs))
opTask, err := task.NewTaskWithOps(i18n.GetMsgByKey("SSL"), task.TaskBatch, task.TaskScopeWebsite, req.TaskID, 0)
if err != nil {
return err
}
websiteHttpsOp := request.WebsiteHTTPSOp{
Enable: true,
WebsiteSSLID: req.WebsiteSSLID,
Type: req.Type,
PrivateKey: req.PrivateKey,
Certificate: req.Certificate,
PrivateKeyPath: req.PrivateKeyPath,
CertificatePath: req.CertificatePath,
ImportType: req.ImportType,
HttpConfig: req.HttpConfig,
SSLProtocol: req.SSLProtocol,
Algorithm: req.Algorithm,
Hsts: req.Hsts,
HstsIncludeSubDomains: req.HstsIncludeSubDomains,
HttpsPorts: req.HttpsPorts,
Http3: req.Http3,
}
if req.Type == constant.SSLManual {
websiteSSL, err := getManualWebsiteSSL(websiteHttpsOp)
if err != nil {
return err
}
if err = websiteSSLRepo.Create(ctx, &websiteSSL); err != nil {
return err
}
websiteHttpsOp.Type = constant.SSLExisted
websiteHttpsOp.WebsiteSSLID = websiteSSL.ID
}
opWebsiteTask := func(t *task.Task) error {
for _, web := range websites {
if web.Type == constant.Stream {
continue
}
websiteHttpsOp.WebsiteID = web.ID
msg := fmt.Sprintf("%s [%s] %s", i18n.GetMsgByKey("Set"), web.PrimaryDomain, i18n.GetMsgByKey("SSL"))
if _, err := w.OpWebsiteHTTPS(ctx, websiteHttpsOp); err != nil {
t.LogFailedWithErr(msg, err)
continue
}
t.LogSuccess(msg)
}
return nil
}
opTask.AddSubTask("", opWebsiteTask, nil)
go func() {
_ = opTask.Execute()
}()
return nil
}
+408
View File
@@ -0,0 +1,408 @@
package service
import (
"bytes"
"errors"
"fmt"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
"github.com/1Panel-dev/1Panel/agent/app/dto/response"
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/cmd/server/nginx_conf"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/utils/files"
"github.com/1Panel-dev/1Panel/agent/utils/nginx"
"github.com/1Panel-dev/1Panel/agent/utils/nginx/components"
"github.com/1Panel-dev/1Panel/agent/utils/nginx/parser"
"github.com/1Panel-dev/1Panel/agent/utils/re"
"path"
"strconv"
"strings"
)
func (w WebsiteService) OperateProxy(req request.WebsiteProxyConfig) (err error) {
var (
website model.Website
par *parser.Parser
oldContent []byte
)
website, err = websiteRepo.GetFirst(repo.WithByID(req.ID))
if err != nil {
return
}
fileOp := files.NewFileOp()
includeDir := GetSitePath(website, SiteProxyDir)
if !fileOp.Stat(includeDir) {
_ = fileOp.CreateDir(includeDir, constant.DirPerm)
}
fileName := fmt.Sprintf("%s.conf", req.Name)
includePath := path.Join(includeDir, fileName)
backName := fmt.Sprintf("%s.bak", req.Name)
backPath := path.Join(includeDir, backName)
if req.Operate == "create" && (fileOp.Stat(includePath) || fileOp.Stat(backPath)) {
err = buserr.New("ErrNameIsExist")
return
}
defer func() {
if err != nil {
switch req.Operate {
case "create":
_ = fileOp.DeleteFile(includePath)
case "edit":
_ = fileOp.WriteFile(includePath, bytes.NewReader(oldContent), constant.DirPerm)
}
}
}()
var config *components.Config
switch req.Operate {
case "create":
config, err = parser.NewStringParser(string(nginx_conf.GetWebsiteFile("proxy.conf"))).Parse()
if err != nil {
return
}
case "edit":
par, err = parser.NewParser(includePath)
if err != nil {
return
}
config, err = par.Parse()
if err != nil {
return
}
oldContent, err = fileOp.GetContent(includePath)
if err != nil {
return
}
case "delete":
_ = fileOp.DeleteFile(includePath)
_ = fileOp.DeleteFile(backPath)
return updateNginxConfig(constant.NginxScopeServer, nil, &website)
case "disable":
_ = fileOp.Rename(includePath, backPath)
return updateNginxConfig(constant.NginxScopeServer, nil, &website)
case "enable":
_ = fileOp.Rename(backPath, includePath)
return updateNginxConfig(constant.NginxScopeServer, nil, &website)
}
config.FilePath = includePath
directives := config.Directives
var location *components.Location
for _, directive := range directives {
if loc, ok := directive.(*components.Location); ok {
location = loc
break
}
}
if location == nil {
err = errors.New("invalid proxy config, no location found")
return
}
location.UpdateDirective("proxy_pass", []string{req.ProxyPass})
location.UpdateDirective("proxy_set_header", []string{"Host", req.ProxyHost})
location.ChangePath(req.Modifier, req.Match)
// Server Cache Settings
if req.Cache {
if err = openProxyCache(website); err != nil {
return
}
location.AddServerCache(fmt.Sprintf("proxy_cache_zone_of_%s", website.Alias), req.ServerCacheTime, req.ServerCacheUnit)
} else {
location.RemoveServerCache(fmt.Sprintf("proxy_cache_zone_of_%s", website.Alias))
}
// Browser Cache Settings
if req.CacheTime != 0 {
location.AddBrowserCache(req.CacheTime, req.CacheUnit)
} else {
location.RemoveBrowserCache()
}
// Content Replace Settings
if len(req.Replaces) > 0 {
location.AddSubFilter(req.Replaces)
} else {
location.RemoveSubFilter()
}
// SSL Settings
if req.SNI {
location.UpdateDirective("proxy_ssl_server_name", []string{"on"})
if req.ProxySSLName != "" {
location.UpdateDirective("proxy_ssl_name", []string{req.ProxySSLName})
}
} else {
location.UpdateDirective("proxy_ssl_server_name", []string{"off"})
}
// CORS Settings
if req.Cors {
location.UpdateDirective("add_header", []string{"Access-Control-Allow-Origin", req.AllowOrigins, "always"})
if req.AllowMethods != "" {
location.UpdateDirective("add_header", []string{"Access-Control-Allow-Methods", req.AllowMethods, "always"})
} else {
location.RemoveDirective("add_header", []string{"Access-Control-Allow-Methods"})
}
if req.AllowHeaders != "" {
location.UpdateDirective("add_header", []string{"Access-Control-Allow-Headers", req.AllowHeaders, "always"})
} else {
location.RemoveDirective("add_header", []string{"Access-Control-Allow-Headers"})
}
if req.AllowCredentials {
location.UpdateDirective("add_header", []string{"Access-Control-Allow-Credentials", "true", "always"})
} else {
location.RemoveDirective("add_header", []string{"Access-Control-Allow-Credentials"})
}
if req.Preflight {
location.AddCorsOption()
} else {
location.RemoveCorsOption()
}
} else {
location.RemoveDirective("add_header", []string{"Access-Control-Allow-Origin"})
location.RemoveDirective("add_header", []string{"Access-Control-Allow-Methods"})
location.RemoveDirective("add_header", []string{"Access-Control-Allow-Headers"})
location.RemoveDirective("add_header", []string{"Access-Control-Allow-Credentials"})
location.RemoveDirectiveByFullParams("if", []string{"(", "$request_method", "=", "'OPTIONS'", ")"})
}
if err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
return buserr.WithErr("ErrUpdateBuWebsite", err)
}
nginxInclude := fmt.Sprintf("/www/sites/%s/proxy/*.conf", website.Alias)
return updateNginxConfig(constant.NginxScopeServer, []dto.NginxParam{{Name: "include", Params: []string{nginxInclude}}}, &website)
}
func (w WebsiteService) UpdateProxyCache(req request.NginxProxyCacheUpdate) (err error) {
website, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))
if err != nil {
return
}
cacheDir := GetSitePath(website, SiteCacheDir)
fileOp := files.NewFileOp()
if !fileOp.Stat(cacheDir) {
_ = fileOp.CreateDir(cacheDir, constant.DirPerm)
}
if req.Open {
proxyCachePath := fmt.Sprintf("/www/sites/%s/cache levels=1:2 keys_zone=proxy_cache_zone_of_%s:%d%s max_size=%d%s inactive=%d%s", website.Alias, website.Alias, req.ShareCache, req.ShareCacheUnit, req.CacheLimit, req.CacheLimitUnit, req.CacheExpire, req.CacheExpireUnit)
return updateNginxConfig("", []dto.NginxParam{{Name: "proxy_cache_path", Params: []string{proxyCachePath}}}, &website)
}
return deleteNginxConfig("", []dto.NginxParam{{Name: "proxy_cache_path"}}, &website)
}
func (w WebsiteService) GetProxyCache(id uint) (res response.NginxProxyCache, err error) {
var (
website model.Website
)
website, err = websiteRepo.GetFirst(repo.WithByID(id))
if err != nil {
return
}
parser, err := parser.NewParser(GetSitePath(website, SiteConf))
if err != nil {
return
}
config, err := parser.Parse()
if err != nil {
return
}
var params []string
for _, d := range config.GetDirectives() {
if d.GetName() == "proxy_cache_path" {
params = d.GetParameters()
}
}
if len(params) == 0 {
return
}
for _, param := range params {
if re.GetRegex(re.ProxyCacheZonePattern).MatchString(param) {
matches := re.GetRegex(re.ProxyCacheZonePattern).FindStringSubmatch(param)
if len(matches) > 0 {
res.ShareCache, _ = strconv.Atoi(matches[1])
res.ShareCacheUnit = matches[2]
}
}
if re.GetRegex(re.ProxyCacheMaxSizeValidationPattern).MatchString(param) {
matches := re.GetRegex(re.ProxyCacheMaxSizePattern).FindStringSubmatch(param)
if len(matches) > 0 {
res.CacheLimit, _ = strconv.ParseFloat(matches[1], 64)
res.CacheLimitUnit = matches[2]
}
}
if re.GetRegex(re.ProxyCacheInactivePattern).MatchString(param) {
matches := re.GetRegex(re.ProxyCacheInactivePattern).FindStringSubmatch(param)
if len(matches) > 0 {
res.CacheExpire, _ = strconv.Atoi(matches[1])
res.CacheExpireUnit = matches[2]
}
}
}
res.Open = true
return
}
func (w WebsiteService) GetProxies(id uint) (res []request.WebsiteProxyConfig, err error) {
var (
website model.Website
fileList response.FileInfo
)
website, err = websiteRepo.GetFirst(repo.WithByID(id))
if err != nil {
return
}
includeDir := GetSitePath(website, SiteProxyDir)
fileOp := files.NewFileOp()
if !fileOp.Stat(includeDir) {
return
}
fileList, err = NewIFileService().GetFileList(request.FileOption{FileOption: files.FileOption{Path: includeDir, Expand: true, Page: 1, PageSize: 100}})
if len(fileList.Items) == 0 {
return
}
var (
content []byte
config *components.Config
)
for _, configFile := range fileList.Items {
proxyConfig := request.WebsiteProxyConfig{
ID: website.ID,
}
parts := strings.Split(configFile.Name, ".")
proxyConfig.Name = parts[0]
if parts[1] == "conf" {
proxyConfig.Enable = true
} else {
proxyConfig.Enable = false
}
proxyConfig.FilePath = configFile.Path
content, err = fileOp.GetContent(configFile.Path)
if err != nil {
return
}
proxyConfig.Content = string(content)
config, err = parser.NewStringParser(string(content)).Parse()
if err != nil {
return nil, err
}
directives := config.GetDirectives()
var location *components.Location
for _, directive := range directives {
if loc, ok := directive.(*components.Location); ok {
location = loc
break
}
}
if location == nil {
err = errors.New("invalid proxy config, no location found")
return
}
proxyConfig.ProxyPass = location.ProxyPass
proxyConfig.Cache = location.Cache
if location.CacheTime > 0 {
proxyConfig.CacheTime = location.CacheTime
proxyConfig.CacheUnit = location.CacheUint
}
if location.ServerCacheTime > 0 {
proxyConfig.ServerCacheTime = location.ServerCacheTime
proxyConfig.ServerCacheUnit = location.ServerCacheUint
}
proxyConfig.Match = location.Match
proxyConfig.Modifier = location.Modifier
proxyConfig.ProxyHost = location.Host
proxyConfig.Replaces = location.Replaces
for _, directive := range location.Directives {
if directive.GetName() == "proxy_ssl_server_name" {
proxyConfig.SNI = directive.GetParameters()[0] == "on"
}
if directive.GetName() == "proxy_ssl_name" && len(directive.GetParameters()) > 0 {
proxyConfig.ProxySSLName = directive.GetParameters()[0]
}
}
proxyConfig.Cors = location.Cors
proxyConfig.AllowCredentials = location.AllowCredentials
proxyConfig.AllowHeaders = location.AllowHeaders
proxyConfig.AllowOrigins = location.AllowOrigins
proxyConfig.AllowMethods = location.AllowMethods
proxyConfig.Preflight = location.Preflight
res = append(res, proxyConfig)
}
return
}
func (w WebsiteService) UpdateProxyFile(req request.NginxProxyUpdate) (err error) {
var (
website model.Website
oldRewriteContent []byte
)
website, err = websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))
if err != nil {
return err
}
absolutePath := fmt.Sprintf("%s/%s.conf", GetSitePath(website, SiteProxyDir), req.Name)
fileOp := files.NewFileOp()
oldRewriteContent, err = fileOp.GetContent(absolutePath)
if err != nil {
return err
}
if err = fileOp.WriteFile(absolutePath, strings.NewReader(req.Content), constant.DirPerm); err != nil {
return err
}
defer func() {
if err != nil {
_ = fileOp.WriteFile(absolutePath, bytes.NewReader(oldRewriteContent), constant.DirPerm)
}
}()
return updateNginxConfig(constant.NginxScopeServer, nil, &website)
}
func (w WebsiteService) ClearProxyCache(req request.NginxCommonReq) error {
website, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))
if err != nil {
return err
}
cacheDir := GetSitePath(website, SiteCacheDir)
fileOp := files.NewFileOp()
if fileOp.Stat(cacheDir) {
if err = fileOp.CleanDir(cacheDir); err != nil {
return err
}
}
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
if err != nil {
return err
}
if err = opNginx(nginxInstall.ContainerName, constant.NginxReload); err != nil {
return err
}
return nil
}
func (w WebsiteService) DeleteProxy(req request.WebsiteProxyDel) (err error) {
fileOp := files.NewFileOp()
website, err := websiteRepo.GetFirst(repo.WithByID(req.ID))
if err != nil {
return
}
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
if err != nil {
return
}
includeDir := path.Join(nginxInstall.GetPath(), "www", "sites", website.Alias, "proxy")
if !fileOp.Stat(includeDir) {
_ = fileOp.CreateDir(includeDir, 0755)
}
fileName := fmt.Sprintf("%s.conf", req.Name)
includePath := path.Join(includeDir, fileName)
backName := fmt.Sprintf("%s.bak", req.Name)
backPath := path.Join(includeDir, backName)
_ = fileOp.DeleteFile(includePath)
_ = fileOp.DeleteFile(backPath)
return updateNginxConfig(constant.NginxScopeServer, nil, &website)
}
+124
View File
@@ -0,0 +1,124 @@
package service
import (
"bytes"
"context"
"fmt"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
"github.com/1Panel-dev/1Panel/agent/app/dto/response"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/cmd/server/nginx_conf"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/utils/files"
"os"
"path"
"strings"
)
func (w WebsiteService) UpdateRewriteConfig(req request.NginxRewriteUpdate) error {
website, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))
if err != nil {
return err
}
includePath := fmt.Sprintf("/www/sites/%s/rewrite/%s.conf", website.Alias, website.Alias)
absolutePath := GetSitePath(website, SiteReWritePath)
fileOp := files.NewFileOp()
var oldRewriteContent []byte
if !fileOp.Stat(path.Dir(absolutePath)) {
if err := fileOp.CreateDir(path.Dir(absolutePath), constant.DirPerm); err != nil {
return err
}
}
if !fileOp.Stat(absolutePath) {
if err := fileOp.CreateFile(absolutePath); err != nil {
return err
}
} else {
oldRewriteContent, err = fileOp.GetContent(absolutePath)
if err != nil {
return err
}
}
if err := fileOp.WriteFile(absolutePath, strings.NewReader(req.Content), constant.DirPerm); err != nil {
return err
}
if err := updateNginxConfig(constant.NginxScopeServer, []dto.NginxParam{{Name: "include", Params: []string{includePath}}}, &website); err != nil {
_ = fileOp.WriteFile(absolutePath, bytes.NewReader(oldRewriteContent), constant.DirPerm)
return err
}
website.Rewrite = req.Name
return websiteRepo.Save(context.Background(), &website)
}
func (w WebsiteService) GetRewriteConfig(req request.NginxRewriteReq) (*response.NginxRewriteRes, error) {
website, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))
if err != nil {
return nil, err
}
var contentByte []byte
if req.Name == "current" {
rewriteConfPath := GetSitePath(website, SiteReWritePath)
fileOp := files.NewFileOp()
if fileOp.Stat(rewriteConfPath) {
contentByte, err = fileOp.GetContent(rewriteConfPath)
if err != nil {
return nil, err
}
}
} else {
rewriteFile := fmt.Sprintf("rewrite/%s.conf", strings.ToLower(req.Name))
contentByte, _ = nginx_conf.Rewrites.ReadFile(rewriteFile)
if contentByte == nil {
customRewriteDir := GetOpenrestyDir(DefaultRewriteDir)
customRewriteFile := path.Join(customRewriteDir, fmt.Sprintf("%s.conf", strings.ToLower(req.Name)))
contentByte, err = files.NewFileOp().GetContent(customRewriteFile)
}
}
return &response.NginxRewriteRes{
Content: string(contentByte),
}, err
}
func (w WebsiteService) OperateCustomRewrite(req request.CustomRewriteOperate) error {
rewriteDir := GetOpenrestyDir(DefaultRewriteDir)
fileOp := files.NewFileOp()
if !fileOp.Stat(rewriteDir) {
if err := fileOp.CreateDir(rewriteDir, constant.DirPerm); err != nil {
return err
}
}
rewriteFile := path.Join(rewriteDir, fmt.Sprintf("%s.conf", req.Name))
switch req.Operate {
case "create":
if fileOp.Stat(rewriteFile) {
return buserr.New("ErrNameIsExist")
}
return fileOp.WriteFile(rewriteFile, strings.NewReader(req.Content), constant.DirPerm)
case "delete":
return fileOp.DeleteFile(rewriteFile)
}
return nil
}
func (w WebsiteService) ListCustomRewrite() ([]string, error) {
rewriteDir := GetOpenrestyDir(DefaultRewriteDir)
fileOp := files.NewFileOp()
if !fileOp.Stat(rewriteDir) {
return nil, nil
}
entries, err := os.ReadDir(rewriteDir)
if err != nil {
return nil, err
}
var res []string
for _, entry := range entries {
if entry.IsDir() {
continue
}
res = append(res, strings.TrimSuffix(entry.Name(), ".conf"))
}
return res, nil
}
+6 -1
View File
@@ -141,6 +141,7 @@ func (w WebsiteSSLService) Create(create request.WebsiteSSLCreate) (request.Webs
SkipDNS: create.SkipDNS,
DisableCNAME: create.DisableCNAME,
ExecShell: create.ExecShell,
IsIp: create.IsIp,
}
if create.ExecShell {
websiteSSL.Shell = create.Shell
@@ -322,7 +323,11 @@ func (w WebsiteSSLService) ObtainSSL(apply request.WebsiteSSLApply) error {
handleError(websiteSSL, err)
return
}
resource, err = client.ObtainSSL(domains, privateKey)
if websiteSSL.IsIp {
resource, err = client.ObtainIPSSL(domains[0], privateKey)
} else {
resource, err = client.ObtainSSL(domains, privateKey)
}
if err != nil {
handleError(websiteSSL, err)
return
+263 -104
View File
@@ -2,7 +2,9 @@ package service
import (
"context"
"crypto/x509"
"encoding/json"
"encoding/pem"
"fmt"
"log"
"net"
@@ -14,10 +16,10 @@ import (
"syscall"
"time"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/utils/xpack"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/global"
@@ -144,11 +146,13 @@ func createWebsiteFolder(website *model.Website, runtime *model.Runtime) error {
if err := fileOp.CreateFile(path.Join(siteFolder, "log", "error.log")); err != nil {
return err
}
if err := fileOp.CreateDir(path.Join(siteFolder, "index"), constant.DirPerm); err != nil {
return err
}
if err := fileOp.CreateDir(path.Join(siteFolder, "ssl"), constant.DirPerm); err != nil {
return err
if website.Type != constant.Stream {
if err := fileOp.CreateDir(path.Join(siteFolder, "index"), constant.DirPerm); err != nil {
return err
}
if err := fileOp.CreateDir(path.Join(siteFolder, "ssl"), constant.DirPerm); err != nil {
return err
}
}
if website.Type == constant.Runtime {
if runtime.Type == constant.RuntimePHP && runtime.Resource == constant.ResourceLocal {
@@ -175,7 +179,7 @@ func createWebsiteFolder(website *model.Website, runtime *model.Runtime) error {
return nil
}
func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, appInstall *model.AppInstall, runtime *model.Runtime) error {
func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, appInstall *model.AppInstall, runtime *model.Runtime, streamConfig request.StreamConfig) error {
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
if err != nil {
return err
@@ -183,72 +187,86 @@ func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, a
if err = createWebsiteFolder(website, runtime); err != nil {
return err
}
configPath := GetSitePath(*website, SiteConf)
nginxContent := nginx_conf.GetWebsiteFile("website_default.conf")
config, err := parser.NewStringParser(string(nginxContent)).Parse()
if err != nil {
return err
}
servers := config.FindServers()
if len(servers) == 0 {
return errors.New("nginx config is not valid")
}
server := servers[0]
server.DeleteListen("80")
var serverNames []string
for _, domain := range domains {
serverNames = append(serverNames, domain.Domain)
setListen(server, strconv.Itoa(domain.Port), website.IPV6, false, website.DefaultServer, false)
}
server.UpdateServerName(serverNames)
siteFolder := path.Join("/www", "sites", website.Alias)
server.UpdateDirective("access_log", []string{path.Join(siteFolder, "log", "access.log"), "main"})
server.UpdateDirective("error_log", []string{path.Join(siteFolder, "log", "error.log")})
rootIndex := path.Join("/www/sites", website.Alias, "index")
switch website.Type {
case constant.Deployment:
proxy := fmt.Sprintf("http://127.0.0.1:%d", appInstall.HttpPort)
server.UpdateRootProxy([]string{proxy})
case constant.Static:
server.UpdateRoot(rootIndex)
server.UpdateDirective("error_page", []string{"404", "/404.html"})
case constant.Proxy:
nginxInclude := fmt.Sprintf("/www/sites/%s/proxy/*.conf", website.Alias)
server.UpdateDirective("include", []string{nginxInclude})
server.UpdateRoot(rootIndex)
case constant.Runtime:
switch runtime.Type {
case constant.RuntimePHP:
server.UpdateDirective("error_page", []string{"404", "/404.html"})
if runtime.Resource == constant.ResourceLocal {
server.UpdateRoot(rootIndex)
localPath := path.Join(rootIndex, "index.php")
server.UpdatePHPProxy([]string{website.Proxy}, localPath)
} else {
server.UpdateRoot(rootIndex)
server.UpdatePHPProxy([]string{website.Proxy}, "")
}
case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython, constant.RuntimeDotNet:
server.UpdateRootProxy([]string{fmt.Sprintf("http://%s", website.Proxy)})
var (
configPath string
config *components.Config
)
if website.Type == constant.Stream {
if streamConfig.StreamPorts == "" {
return buserr.New("ErrTypePortRange")
}
case constant.Subsite:
parentWebsite, err := websiteRepo.GetFirst(repo.WithByID(website.ParentWebsiteID))
nginxContent := nginx_conf.GetWebsiteFile("stream_default.conf")
config, err = parser.NewStringParser(string(nginxContent)).Parse()
if err != nil {
return err
}
website.Proxy = parentWebsite.Proxy
rootIndex = path.Join("/www/sites", parentWebsite.Alias, "index", website.SiteDir)
server.UpdateDirective("error_page", []string{"404", "/404.html"})
if parentWebsite.Type == constant.Runtime {
parentRuntime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(parentWebsite.RuntimeID))
if err != nil {
return err
servers := config.FindServers()
if len(servers) == 0 {
return errors.New("nginx config is not valid")
}
server := servers[0]
ports := strings.Split(streamConfig.StreamPorts, ",")
for _, port := range ports {
server.UpdateListen(port, false)
if website.IPV6 {
server.UpdateListen("[::]:"+port, false)
}
website.RuntimeID = parentRuntime.ID
if parentRuntime.Type == constant.RuntimePHP {
if parentRuntime.Resource == constant.ResourceLocal {
}
siteFolder := path.Join("/www", "sites", website.Alias)
server.UpdateDirective("access_log", []string{path.Join(siteFolder, "log", "access.log"), "streamlog"})
server.UpdateDirective("error_log", []string{path.Join(siteFolder, "log", "error.log")})
server.UpdateDirective("proxy_pass", []string{website.Alias})
upstream := components.Upstream{
UpstreamName: website.Alias,
}
if streamConfig.Algorithm != "default" {
upstream.UpdateDirective(streamConfig.Algorithm, []string{})
}
upstream.UpstreamServers = parseUpstreamServers(streamConfig.Servers)
config.Block.Directives = append(config.Block.Directives, &upstream)
configPath = GetSitePath(*website, StreamConf)
} else {
configPath = GetSitePath(*website, SiteConf)
nginxContent := nginx_conf.GetWebsiteFile("website_default.conf")
config, err = parser.NewStringParser(string(nginxContent)).Parse()
if err != nil {
return err
}
servers := config.FindServers()
if len(servers) == 0 {
return errors.New("nginx config is not valid")
}
server := servers[0]
server.DeleteListen("80")
var serverNames []string
for _, domain := range domains {
serverNames = append(serverNames, domain.Domain)
setListen(server, strconv.Itoa(domain.Port), website.IPV6, false, website.DefaultServer, false)
}
server.UpdateServerName(serverNames)
siteFolder := path.Join("/www", "sites", website.Alias)
server.UpdateDirective("access_log", []string{path.Join(siteFolder, "log", "access.log"), "main"})
server.UpdateDirective("error_log", []string{path.Join(siteFolder, "log", "error.log")})
rootIndex := path.Join("/www/sites", website.Alias, "index")
switch website.Type {
case constant.Deployment:
proxy := fmt.Sprintf("http://127.0.0.1:%d", appInstall.HttpPort)
server.UpdateRootProxy([]string{proxy})
case constant.Static:
server.UpdateRoot(rootIndex)
server.UpdateDirective("error_page", []string{"404", "/404.html"})
case constant.Proxy:
nginxInclude := fmt.Sprintf("/www/sites/%s/proxy/*.conf", website.Alias)
server.UpdateDirective("include", []string{nginxInclude})
server.UpdateRoot(rootIndex)
case constant.Runtime:
switch runtime.Type {
case constant.RuntimePHP:
server.UpdateDirective("error_page", []string{"404", "/404.html"})
if runtime.Resource == constant.ResourceLocal {
server.UpdateRoot(rootIndex)
localPath := path.Join(rootIndex, "index.php")
server.UpdatePHPProxy([]string{website.Proxy}, localPath)
@@ -256,13 +274,39 @@ func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, a
server.UpdateRoot(rootIndex)
server.UpdatePHPProxy([]string{website.Proxy}, "")
}
case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython, constant.RuntimeDotNet:
server.UpdateRootProxy([]string{fmt.Sprintf("http://%s", website.Proxy)})
}
case constant.Subsite:
parentWebsite, err := websiteRepo.GetFirst(repo.WithByID(website.ParentWebsiteID))
if err != nil {
return err
}
website.Proxy = parentWebsite.Proxy
rootIndex = path.Join("/www/sites", parentWebsite.Alias, "index", website.SiteDir)
server.UpdateDirective("error_page", []string{"404", "/404.html"})
if parentWebsite.Type == constant.Runtime {
parentRuntime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(parentWebsite.RuntimeID))
if err != nil {
return err
}
website.RuntimeID = parentRuntime.ID
if parentRuntime.Type == constant.RuntimePHP {
if parentRuntime.Resource == constant.ResourceLocal {
server.UpdateRoot(rootIndex)
localPath := path.Join(rootIndex, "index.php")
server.UpdatePHPProxy([]string{website.Proxy}, localPath)
} else {
server.UpdateRoot(rootIndex)
server.UpdatePHPProxy([]string{website.Proxy}, "")
}
}
}
if parentWebsite.Type == constant.Static {
server.UpdateRoot(rootIndex)
}
}
if parentWebsite.Type == constant.Static {
server.UpdateRoot(rootIndex)
}
}
config.FilePath = configPath
if err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
return err
@@ -432,14 +476,20 @@ func createWafConfig(website *model.Website, domains []model.WebsiteDomain) erro
}
func delNginxConfig(website model.Website, force bool) error {
configPath := GetSitePath(website, SiteConf)
fileOp := files.NewFileOp()
var (
configPath string
)
if website.Type == constant.Stream {
configPath = GetSitePath(website, StreamConf)
if !fileOp.Stat(configPath) {
return nil
} else {
configPath = GetSitePath(website, SiteConf)
}
if err := fileOp.DeleteFile(configPath); err != nil {
return err
if fileOp.Stat(configPath) {
if err := fileOp.DeleteFile(configPath); err != nil {
return err
}
}
sitePath := GteSiteDir(website.Alias)
if fileOp.Stat(sitePath) {
@@ -694,6 +744,7 @@ func applySSL(website *model.Website, websiteSSL model.WebsiteSSL, req request.W
}
httpPorts := make(map[int]struct{})
httpsPorts := make(map[int]struct{})
sslPort := 0
hasDefaultPort := false
for _, domain := range domains {
@@ -721,6 +772,7 @@ func applySSL(website *model.Website, websiteSSL model.WebsiteSSL, req request.W
defaultHttpPortIPV6 := "[::]:" + defaultHttpPort
for port := range httpsPorts {
sslPort = port
portStr := strconv.Itoa(port)
server.RemoveListenByBind(portStr)
server.RemoveListenByBind("[::]:" + portStr)
@@ -741,7 +793,7 @@ func applySSL(website *model.Website, websiteSSL model.WebsiteSSL, req request.W
server.UpdateListen(defaultHttpPortIPV6, website.DefaultServer)
}
}
server.AddHTTP2HTTPS()
server.AddHTTP2HTTPS(sslPort)
case constant.HTTPAlso:
if hasDefaultPort {
server.UpdateListen(defaultHttpPort, website.DefaultServer)
@@ -880,9 +932,16 @@ func deleteWebsiteFolder(website *model.Website) error {
if fileOp.Stat(siteFolder) {
_ = fileOp.DeleteDir(siteFolder)
}
nginxFilePath := GetSitePath(*website, SiteConf)
if fileOp.Stat(nginxFilePath) {
_ = fileOp.DeleteFile(nginxFilePath)
if website.Type == constant.Stream {
steamFilePath := GetSitePath(*website, StreamConf)
if fileOp.Stat(steamFilePath) {
_ = fileOp.DeleteFile(steamFilePath)
}
} else {
nginxFilePath := GetSitePath(*website, SiteConf)
if fileOp.Stat(nginxFilePath) {
_ = fileOp.DeleteFile(nginxFilePath)
}
}
return nil
}
@@ -1052,7 +1111,7 @@ func getWebsiteDomains(domains []request.WebsiteDomain, defaultHTTPPort, default
if domain.Domain == "" {
continue
}
if !common.IsValidDomain(domain.Domain) {
if !common.IsValidNginxServerName(domain.Domain) {
err = buserr.WithName("ErrDomainFormat", domain.Domain)
return
}
@@ -1089,27 +1148,8 @@ func getWebsiteDomains(domains []request.WebsiteDomain, defaultHTTPPort, default
addPorts = append(addPorts, port)
continue
}
if existPorts, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithPort(port)); len(existPorts) == 0 {
errMap := make(map[string]interface{})
errMap["port"] = port
appInstall, _ := appInstallRepo.GetFirst(appInstallRepo.WithPort(port))
if appInstall.ID > 0 {
errMap["type"] = i18n.GetMsgByKey("TYPE_APP")
errMap["name"] = appInstall.Name
err = buserr.WithMap("ErrPortExist", errMap, nil)
return
}
runtime, _ := runtimeRepo.GetFirst(context.Background(), runtimeRepo.WithPort(port))
if runtime != nil {
errMap["type"] = i18n.GetMsgByKey("TYPE_RUNTIME")
errMap["name"] = runtime.Name
err = buserr.WithMap("ErrPortExist", errMap, nil)
return
}
if port != defaultHTTPsPort && common.ScanPort(port) {
err = buserr.WithDetail("ErrPortInUsed", port, nil)
return
}
if err = checkWebsitePort(defaultHTTPsPort, port, ""); err != nil {
return
}
if existPorts, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteId(websiteID), websiteDomainRepo.WithPort(port)); len(existPorts) == 0 {
addPorts = append(addPorts, port)
@@ -1119,6 +1159,43 @@ func getWebsiteDomains(domains []request.WebsiteDomain, defaultHTTPPort, default
return
}
func checkWebsitePort(defaultHTTPsPort, port int, websiteType string) error {
if existPorts, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithPort(port)); len(existPorts) > 0 {
return nil
}
if websiteType == constant.Stream {
websites, _ := websiteRepo.List(websiteRepo.WithType(constant.Stream))
for _, website := range websites {
ports := strings.Split(website.StreamPorts, ",")
for _, p := range ports {
pInt, _ := strconv.Atoi(p)
if pInt == port {
return nil
}
}
}
}
errMap := make(map[string]interface{})
errMap["port"] = port
appInstall, _ := appInstallRepo.GetFirst(appInstallRepo.WithPort(port))
if appInstall.ID > 0 {
errMap["type"] = i18n.GetMsgByKey("TYPE_APP")
errMap["name"] = appInstall.Name
return buserr.WithMap("ErrPortExist", errMap, nil)
}
runtime, _ := runtimeRepo.GetFirst(context.Background(), runtimeRepo.WithPort(port))
if runtime != nil {
errMap["type"] = i18n.GetMsgByKey("TYPE_RUNTIME")
errMap["name"] = runtime.Name
return buserr.WithMap("ErrPortExist", errMap, nil)
}
if port != defaultHTTPsPort && common.ScanPort(port) {
return buserr.WithDetail("ErrPortInUsed", port, nil)
}
return nil
}
func saveCertificateFile(websiteSSL *model.WebsiteSSL, logger *log.Logger) {
if websiteSSL.PushDir {
fileOp := files.NewFileOp()
@@ -1304,12 +1381,23 @@ const (
SitePathAuthBasicDir = "SitePathAuthBasicDir"
SiteUpstreamDir = "SiteUpstreamDir"
SiteCorsPath = "SiteCorsPath"
StreamDir = "StreamDir"
StreamConf = "StreamConf"
)
func GetWebsiteConfigPath(website model.Website) string {
if website.Type != constant.Stream {
return GetSitePath(website, SiteConf)
}
return GetSitePath(website, StreamConf)
}
func GetSitePath(website model.Website, confType string) string {
switch confType {
case SiteConf:
return path.Join(GetWebSiteRootDir(), "conf.d", website.Alias+".conf")
case StreamConf:
return path.Join(GetWebSiteRootDir(), "stream.d", website.Alias+".conf")
case SiteAccessLog:
return path.Join(GteSiteDir(website.Alias), "log", "access.log")
case SiteErrorLog:
@@ -1340,12 +1428,21 @@ func GetSitePath(website model.Website, confType string) string {
return ""
}
func GetConfDir(website model.Website) string {
if website.Type != constant.Stream {
return GetOpenrestyDir(SiteConf)
}
return GetOpenrestyDir(StreamDir)
}
func GetOpenrestyDir(confType string) string {
switch confType {
case WebsiteRootDir:
return GetWebSiteRootDir()
case SiteConfDir:
return path.Join(GetWebSiteRootDir(), "conf.d")
case StreamDir:
return path.Join(GetWebSiteRootDir(), "stream.d")
case SitesRootDir:
return path.Join(GetWebSiteRootDir(), "sites")
case DefaultDir:
@@ -1619,3 +1716,65 @@ func getNginxUpstreamServers(upstreamServers []*components.UpstreamServer) []dto
}
return servers
}
func getManualWebsiteSSL(req request.WebsiteHTTPSOp) (model.WebsiteSSL, error) {
var websiteSSL model.WebsiteSSL
var (
certificate string
privateKey string
)
switch req.ImportType {
case "paste":
certificate = req.Certificate
privateKey = req.PrivateKey
case "local":
fileOp := files.NewFileOp()
if !fileOp.Stat(req.PrivateKeyPath) {
return websiteSSL, buserr.New("ErrSSLKeyNotFound")
}
if !fileOp.Stat(req.CertificatePath) {
return websiteSSL, buserr.New("ErrSSLCertificateNotFound")
}
if content, err := fileOp.GetContent(req.PrivateKeyPath); err != nil {
return websiteSSL, err
} else {
privateKey = string(content)
}
if content, err := fileOp.GetContent(req.CertificatePath); err != nil {
return websiteSSL, err
} else {
certificate = string(content)
}
}
privateKeyCertBlock, _ := pem.Decode([]byte(privateKey))
if privateKeyCertBlock == nil {
return websiteSSL, buserr.New("ErrSSLKeyFormat")
}
certBlock, _ := pem.Decode([]byte(certificate))
if certBlock == nil {
return websiteSSL, buserr.New("ErrSSLCertificateFormat")
}
cert, err := x509.ParseCertificate(certBlock.Bytes)
if err != nil {
return websiteSSL, err
}
websiteSSL.ExpireDate = cert.NotAfter
websiteSSL.StartDate = cert.NotBefore
websiteSSL.Type = cert.Issuer.CommonName
if len(cert.Issuer.Organization) > 0 {
websiteSSL.Organization = cert.Issuer.Organization[0]
} else {
websiteSSL.Organization = cert.Issuer.CommonName
}
if len(cert.DNSNames) > 0 {
websiteSSL.PrimaryDomain = cert.DNSNames[0]
websiteSSL.Domains = strings.Join(cert.DNSNames, ",")
}
websiteSSL.Provider = constant.Manual
websiteSSL.PrivateKey = privateKey
websiteSSL.Pem = certificate
websiteSSL.Status = constant.SSLReady
return websiteSSL, nil
}
+28 -16
View File
@@ -1,11 +1,9 @@
package task
import (
"bufio"
"context"
"errors"
"fmt"
"log"
"os"
"path"
"strconv"
@@ -13,6 +11,7 @@ import (
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/sirupsen/logrus"
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/app/repo"
@@ -29,8 +28,7 @@ type Task struct {
Name string
TaskID string
Logger *log.Logger
Writer *bufio.Writer
Logger *logrus.Logger
SubTasks []*SubTask
Rollbacks []RollbackFunc
logFile *os.File
@@ -138,12 +136,13 @@ func NewTask(name, operate, taskScope, taskID string, resourceID uint) (*Task, e
}
}
logPath := path.Join(global.Dir.TaskDir, taskScope, taskID+".log")
file, err := os.OpenFile(logPath, os.O_TRUNC|os.O_CREATE|os.O_WRONLY, constant.FilePerm)
logger := logrus.New()
logger.SetFormatter(&SimpleFormatter{})
logFile, err := os.OpenFile(logPath, os.O_TRUNC|os.O_CREATE|os.O_WRONLY, constant.FilePerm)
if err != nil {
return nil, fmt.Errorf("failed to open log file: %w", err)
}
writer := bufio.NewWriter(file)
logger := log.New(file, "", log.LstdFlags)
logger.SetOutput(logFile)
taskModel := &model.Task{
ID: taskID,
Name: name,
@@ -154,9 +153,9 @@ func NewTask(name, operate, taskScope, taskID string, resourceID uint) (*Task, e
Operate: operate,
}
taskRepo := repo.NewITaskRepo()
ctx, cancle := context.WithCancel(context.Background())
global.TaskCtxMap[taskID] = cancle
task := &Task{TaskCtx: ctx, Name: name, logFile: file, Logger: logger, taskRepo: taskRepo, Task: taskModel, Writer: writer}
ctx, cancel := context.WithCancel(context.Background())
global.TaskCtxMap[taskID] = cancel
task := &Task{TaskCtx: ctx, Name: name, logFile: logFile, Logger: logger, taskRepo: taskRepo, Task: taskModel}
return task, nil
}
@@ -175,16 +174,18 @@ func ReNewTask(name, operate, taskScope, taskID string, resourceID uint) (*Task,
return nil, fmt.Errorf("failed to create log directory: %w", err)
}
}
logPath := path.Join(global.Dir.TaskDir, taskScope, taskID+".log")
file, err := os.OpenFile(logPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, constant.FilePerm)
logger := logrus.New()
logger.SetFormatter(&SimpleFormatter{})
logFile, err := os.OpenFile(logPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, constant.FilePerm)
if err != nil {
return nil, fmt.Errorf("failed to open log file: %w", err)
}
writer := bufio.NewWriter(file)
logger := log.New(file, "", log.LstdFlags)
logger.SetOutput(logFile)
logger.Print("\n --------------------------------------------------- \n")
taskItem.Status = constant.StatusExecuting
task := &Task{Name: name, logFile: file, Logger: logger, taskRepo: taskRepo, Task: &taskItem, Writer: writer}
task := &Task{Name: name, logFile: logFile, Logger: logger, taskRepo: taskRepo, Task: &taskItem}
task.updateTask(&taskItem)
return task, nil
}
@@ -226,6 +227,9 @@ func (s *SubTask) Execute() error {
s.RootTask.Log(i18n.GetWithName("TaskRetry", strconv.Itoa(i)))
}
ctx, cancel := context.WithTimeout(context.Background(), s.Timeout)
if s.Timeout == 0 {
ctx, cancel = context.WithCancel(context.Background())
}
defer cancel()
done := make(chan error)
@@ -325,7 +329,7 @@ func (t *Task) LogFailed(msg string) {
}
func (t *Task) LogFailedWithErr(msg string, err error) {
t.Logger.Printf("%s %s : %s\n", msg, i18n.GetMsgByKey("Failed"), err.Error())
t.Logger.Printf("%s %s : %s", msg, i18n.GetMsgByKey("Failed"), err.Error())
}
func (t *Task) LogSuccess(msg string) {
@@ -336,7 +340,7 @@ func (t *Task) LogSuccessF(format string, v ...any) {
}
func (t *Task) LogStart(msg string) {
t.Logger.Printf("%s%s\n", i18n.GetMsgByKey("Start"), msg)
t.Logger.Printf("%s%s", i18n.GetMsgByKey("Start"), msg)
}
func (t *Task) LogWithOps(operate, msg string) {
@@ -350,3 +354,11 @@ func (t *Task) LogSuccessWithOps(operate, msg string) {
func (t *Task) LogFailedWithOps(operate, msg string, err error) {
t.Logger.Printf("%s%s%s : %s ", i18n.GetMsgByKey(operate), msg, i18n.GetMsgByKey("Failed"), err.Error())
}
type SimpleFormatter struct{}
func (f *SimpleFormatter) Format(entry *logrus.Entry) ([]byte, error) {
timestamp := entry.Time.Format("2006/01/02 15:04:05")
message := fmt.Sprintf("%s %s\n", timestamp, entry.Message)
return []byte(message), nil
}
-169
View File
@@ -1,169 +0,0 @@
package cmd
import (
"fmt"
"os"
"runtime"
"runtime/pprof"
"time"
"github.com/spf13/cobra"
)
var (
cpuProfile string
memProfile string
duration time.Duration
showMemStats bool
showGoroutine bool
)
func init() {
RootCmd.AddCommand(debugCmd)
debugCmd.Flags().StringVar(&cpuProfile, "cpuprofile", "", "write cpu profile to file")
debugCmd.Flags().StringVar(&memProfile, "memprofile", "", "write memory profile to file")
debugCmd.Flags().DurationVar(&duration, "duration", 30*time.Second, "duration for cpu profiling")
debugCmd.Flags().BoolVar(&showMemStats, "memstats", false, "show memory statistics")
debugCmd.Flags().BoolVar(&showGoroutine, "goroutine", false, "show goroutine profile")
}
var debugCmd = &cobra.Command{
Use: "debug",
Short: "Performance debugging with pprof",
Long: `Debug command provides CPU and memory profiling capabilities using pprof.
Examples:
# CPU profiling for 30 seconds
1panel-agent debug --cpuprofile=cpu.prof --duration=30s
# Memory profiling
1panel-agent debug --memprofile=mem.prof
# Show memory statistics
1panel-agent debug --memstats
# Show goroutine information
1panel-agent debug --goroutine
# Combined profiling
1panel-agent debug --cpuprofile=cpu.prof --memprofile=mem.prof --memstats`,
RunE: func(cmd *cobra.Command, args []string) error {
if cpuProfile != "" {
if err := startCPUProfile(cpuProfile); err != nil {
return fmt.Errorf("failed to start CPU profile: %v", err)
}
defer stopCPUProfile()
time.Sleep(duration)
fmt.Printf("CPU profile saved to: %s\n", cpuProfile)
}
if memProfile != "" {
if err := writeMemProfile(memProfile); err != nil {
return fmt.Errorf("failed to write memory profile: %v", err)
}
fmt.Printf("Memory profile saved to: %s\n", memProfile)
}
if showMemStats {
printMemStats()
}
if showGoroutine {
printGoroutineInfo()
}
if cpuProfile == "" && memProfile == "" && !showMemStats && !showGoroutine {
printBasicInfo()
}
return nil
},
}
func startCPUProfile(filename string) error {
f, err := os.Create(filename)
if err != nil {
return err
}
if err := pprof.StartCPUProfile(f); err != nil {
f.Close()
return err
}
return nil
}
func stopCPUProfile() {
pprof.StopCPUProfile()
}
func writeMemProfile(filename string) error {
f, err := os.Create(filename)
if err != nil {
return err
}
defer f.Close()
runtime.GC()
if err := pprof.WriteHeapProfile(f); err != nil {
return err
}
return nil
}
func printMemStats() {
var m runtime.MemStats
runtime.ReadMemStats(&m)
fmt.Println("\n=== Memory Statistics ===")
fmt.Printf("Allocated memory: %d KB (%d MB)\n", m.Alloc/1024, m.Alloc/1024/1024)
fmt.Printf("Total allocated: %d KB (%d MB)\n", m.TotalAlloc/1024, m.TotalAlloc/1024/1024)
fmt.Printf("System memory: %d KB (%d MB)\n", m.Sys/1024, m.Sys/1024/1024)
fmt.Printf("Heap allocated: %d KB (%d MB)\n", m.HeapAlloc/1024, m.HeapAlloc/1024/1024)
fmt.Printf("Heap system: %d KB (%d MB)\n", m.HeapSys/1024, m.HeapSys/1024/1024)
fmt.Printf("Heap idle: %d KB (%d MB)\n", m.HeapIdle/1024, m.HeapIdle/1024/1024)
fmt.Printf("Heap in use: %d KB (%d MB)\n", m.HeapInuse/1024, m.HeapInuse/1024/1024)
fmt.Printf("Heap released: %d KB (%d MB)\n", m.HeapReleased/1024, m.HeapReleased/1024/1024)
fmt.Printf("Heap objects: %d\n", m.HeapObjects)
fmt.Printf("GC runs: %d\n", m.NumGC)
fmt.Printf("GC pause total: %d ns\n", m.PauseTotalNs)
fmt.Printf("Next GC: %d KB (%d MB)\n", m.NextGC/1024, m.NextGC/1024/1024)
fmt.Printf("Last GC: %s\n", time.Unix(0, int64(m.LastGC)).Format("2006-01-02 15:04:05"))
}
func printGoroutineInfo() {
fmt.Println("\n=== Goroutine Information ===")
fmt.Printf("Number of goroutines: %d\n", runtime.NumGoroutine())
fmt.Printf("Number of CPUs: %d\n", runtime.NumCPU())
fmt.Printf("GOMAXPROCS: %d\n", runtime.GOMAXPROCS(0))
buf := make([]byte, 1024*1024)
stackSize := runtime.Stack(buf, true)
fmt.Printf("\nGoroutine stack trace (first 2048 bytes):\n")
if stackSize > 2048 {
fmt.Printf("%s...\n", buf[:2048])
fmt.Printf("(truncated, total size: %d bytes)\n", stackSize)
} else {
fmt.Printf("%s\n", buf[:stackSize])
}
}
func printBasicInfo() {
fmt.Println("=== Performance Debug Info ===")
fmt.Printf("Go version: %s\n", runtime.Version())
fmt.Printf("OS/Arch: %s/%s\n", runtime.GOOS, runtime.GOARCH)
fmt.Printf("CPUs: %d\n", runtime.NumCPU())
fmt.Printf("GOMAXPROCS: %d\n", runtime.GOMAXPROCS(0))
fmt.Printf("Goroutines: %d\n", runtime.NumGoroutine())
var m runtime.MemStats
runtime.ReadMemStats(&m)
fmt.Printf("Memory allocated: %d KB\n", m.Alloc/1024)
fmt.Printf("Total allocations: %d KB\n", m.TotalAlloc/1024)
fmt.Printf("GC runs: %d\n", m.NumGC)
fmt.Println("\nUse --help to see available profiling options.")
}
+62 -55
View File
@@ -3,21 +3,21 @@
"name": "amqp",
"check": "amqp",
"file": "amqp.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "apcu",
"check": "apcu",
"file": "apcu.so",
"versions": ["56", "70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56", "70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "bcmath",
"check": "bcmath",
"file": "bcmath.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
@@ -31,7 +31,7 @@
"name": "opcache",
"check": "Zend OPcache",
"file": "opcache.so",
"versions": ["56", "70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56", "70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
@@ -45,21 +45,21 @@
"name": "memcached",
"check": "memcached",
"file": "memcached.so",
"versions": ["56", "70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56", "70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "redis",
"check": "redis",
"file": "redis.so",
"versions": [ "56", "70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": [ "56", "70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "mcrypt",
"check": "mcrypt",
"file": "mcrypt.so",
"versions": ["70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
@@ -67,35 +67,35 @@
"name": "imagick",
"check": "imagick",
"file": "imagick.so",
"versions": ["56", "70", "71", "72", "73", "74", "80", "81", "82","83","84"],
"versions": ["56", "70", "71", "72", "73", "74", "80", "81", "82","83","84","85"],
"installed": false
},
{
"name": "xdebug",
"check": "xdebug",
"file": "xdebug.so",
"versions": ["56", "70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56", "70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "imap",
"check": "imap",
"file": "imap.so",
"versions": ["56", "70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56", "70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "exif",
"check": "exif",
"file": "exif.so",
"versions": [ "56", "70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": [ "56", "70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "intl",
"check": "intl",
"file": "intl.so",
"versions": ["56", "70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56", "70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
@@ -109,42 +109,42 @@
"name": "swoole",
"check": "swoole",
"file": "swoole.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "zstd",
"check": "zstd",
"file": "zstd.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "xlswriter",
"check": "xlswriter",
"file": "xlswriter.so",
"versions": ["70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "oci8",
"check": "oci8",
"file": "oci8.so",
"versions": ["70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "pdo_oci",
"check": "pdo_oci",
"file": "pdo_oci.so",
"versions": ["70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "pdo_sqlsrv",
"check": "pdo_sqlsrv",
"file": "pdo_sqlsrv.so",
"versions": ["70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
@@ -158,77 +158,77 @@
"name": "yaf",
"check": "yaf",
"file": "yaf.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "mongodb",
"check": "mongodb",
"file": "mongodb.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "yac",
"check": "yac",
"file": "yac.so",
"versions": ["70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "pgsql",
"check": "pgsql",
"file": "pgsql.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "ssh2",
"check": "ssh2",
"file": "ssh2.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "grpc",
"check": "grpc",
"file": "grpc.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "xhprof",
"check": "xhprof",
"file": "xhprof.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "protobuf",
"check": "protobuf",
"file": "protobuf.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "pdo_pgsql",
"check": "pdo_pgsql",
"file": "pdo_pgsql.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "snmp",
"check": "snmp",
"file": "snmp.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "ldap",
"check": "ldap",
"file": "ldap.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
@@ -242,42 +242,42 @@
"name": "enchant",
"check": "enchant",
"file": "enchant.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "pspell",
"check": "pspell",
"file": "pspell.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "bz2",
"check": "bz2",
"file": "bz2.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "sysvshm",
"check": "sysvshm",
"file": "sysvshm.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "calendar",
"check": "calendar",
"file": "calendar.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "gmp",
"check": "gmp",
"file": "gmp.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
@@ -291,91 +291,91 @@
"name": "sysvmsg",
"check": "sysvmsg",
"file": "sysvmsg.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "igbinary",
"check": "igbinary",
"file": "igbinary.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "zmq",
"check": "zmq",
"file": "zmq.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "smbclient",
"check": "smbclient",
"file": "smbclient.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "event",
"check": "event",
"file": "event.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "mailparse",
"check": "mailparse",
"file": "mailparse.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "yaml",
"check": "yaml",
"file": "yaml.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "sg16",
"check": "SourceGuardian",
"file": "sourceguardian.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "mysqli",
"check": "mysqli",
"file": "mysqli.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "pdo_mysql",
"check": "pdo_mysql",
"file": "pdo_mysql.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "zip",
"check": "zip",
"file": "zip.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "shmop",
"check": "shmop",
"file": "shmop.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "gd",
"check": "gd",
"file": "gd.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
@@ -383,49 +383,56 @@
"name": "pcntl",
"check": "pcntl",
"file": "pcntl.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "sodium",
"check": "sodium",
"file": "sodium.so",
"versions": ["70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "gettext",
"check": "gettext",
"file": "gettext.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "soap",
"check": "soap",
"file": "soap.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "sysvsem",
"check": "sysvsem",
"file": "sysvsem.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "sockets",
"check": "sockets",
"file": "sockets.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "xmlrpc",
"check": "xmlrpc",
"file": "xmlrpc.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
},
{
"name": "lz4",
"check": "lz4",
"file": "lz4.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"],
"installed": false
}
]
-1
View File
@@ -11,7 +11,6 @@ location ^~ /test {
proxy_http_version 1.1;
add_header X-Cache $upstream_cache_status;
add_header Cache-Control no-cache;
proxy_ssl_server_name off;
proxy_ssl_name $proxy_host;
}
@@ -0,0 +1,5 @@
server {
proxy_pass mysql_cluster;
access_log /www/sites/domain/log/access.log streamlog;
error_log /www/sites/domain/log/error.log;
}

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