Compare commits

...
320 Commits
Author SHA1 Message Date
Mr Chen 869d7795a6 Merge pull request #321 from midoks/dev
0.12.1
2023-01-04 20:58:27 +08:00
midoks 0a3d976c6a 0.12.1
* mysql/mariadb/mysql-apt/mysql-yum增加新的同步方式[同步账户]。
* pureftp安装时自动放行端口。
* 面板ssl,添加【申请Lets证书】功能。
* 站点子域名绑定优化。
* 修复添加数据库名字判断。
* 增加PHP52的隐藏和显示指令。
* 各种细节优化。
2023-01-04 20:57:11 +08:00
midoks c4d39ef9fb Update intl.sh 2023-01-04 20:50:18 +08:00
midoks 8e5b499601 Update install.sh 2023-01-04 20:41:26 +08:00
midoks e6dff6094c Update tools.py 2023-01-04 20:21:52 +08:00
midoks 47dec45a27 Update tools.py 2023-01-04 20:20:47 +08:00
midoks 4706af7423 Update tools.py 2023-01-04 20:18:50 +08:00
midoks 1bc458eb8f 增加PHP52的隐藏和显示指令 2023-01-04 20:17:20 +08:00
midoks dc3f872592 Update config_api.py 2023-01-04 19:13:08 +08:00
midoks 3d48289329 up 2023-01-04 18:52:24 +08:00
midoks 6126539929 Update config_api.py 2023-01-04 18:00:49 +08:00
midoks cff5df6d13 Update requirements.txt 2023-01-04 17:57:55 +08:00
midoks 23a0b74f9d Update config.js 2023-01-04 17:49:58 +08:00
midoks 5927a3abab 面板ssl申请 2023-01-04 17:48:07 +08:00
midoks 4b176ad702 修复子目录绑定ssl申请问题 2023-01-03 23:45:51 +08:00
midoks 577103506d Update site_api.py 2023-01-03 23:39:40 +08:00
midoks 18f8d2b905 Update files.js 2023-01-03 18:56:56 +08:00
midoks 3a6b5339d2 Update config_api.py 2023-01-03 18:50:07 +08:00
midoks c25d4305f7 Update files.js 2023-01-03 18:49:58 +08:00
midoks b6ef87e97b up 2023-01-03 18:43:25 +08:00
midoks 4335668cd4 Update config_api.py 2023-01-03 18:29:20 +08:00
midoks 0fc2ab011d Update files.js 2023-01-03 18:26:20 +08:00
midoks 683035e6b7 up 2023-01-03 18:06:28 +08:00
midoks f2dfa7ad48 Update index.py 2023-01-03 14:55:39 +08:00
midoks 2ae2c188e7 Update index.py 2023-01-03 14:54:57 +08:00
midoks b5cd0564cb 安装,自动放行端口 2023-01-03 14:43:34 +08:00
midoks 2f274ce202 Update pure-ftpd.conf 2023-01-03 14:14:36 +08:00
midoks 0a67932473 域名添加重复bug优化 2023-01-02 22:56:19 +08:00
midoks 7b7cb215c4 Update config_api.py 2023-01-02 20:47:05 +08:00
midoks f6e98ae286 站点配置保持优化 2023-01-02 20:46:09 +08:00
midoks 5958f6e067 Update info.json 2023-01-02 12:01:05 +08:00
midoks 6dadfb1019 Update mysql.sql 2023-01-02 11:58:41 +08:00
midoks f5aeb5664b Update index.py 2023-01-02 11:58:08 +08:00
midoks ca1080937d Update index.py 2023-01-02 11:52:57 +08:00
midoks 1dd934ea3f mysql-yum加入主从同步 2023-01-02 11:47:35 +08:00
midoks a34512d057 Update ubuntu.sh 2023-01-02 01:06:54 +08:00
midoks 575c1e48ae Update info.json 2023-01-02 01:02:13 +08:00
midoks 9f3ee4997b up 2023-01-02 00:59:57 +08:00
midoks cfaa2d9f25 up 2023-01-02 00:30:44 +08:00
midoks aded1fc731 Update index.py 2023-01-02 00:16:12 +08:00
midoks 4a6e847743 Update index.py 2023-01-02 00:13:20 +08:00
midoks 24fac51538 Update index.py 2023-01-02 00:12:49 +08:00
midoks 148b32fed3 Update mariadb.js 2023-01-02 00:11:00 +08:00
midoks fff19d2c4a Update my.cnf 2023-01-02 00:04:43 +08:00
midoks a5e0332179 Update index.py 2023-01-01 23:42:14 +08:00
midoks 1349655570 up 2023-01-01 23:37:43 +08:00
midoks b9e617bf0f Update index.py 2023-01-01 21:58:26 +08:00
midoks f65d10e1e4 up 2023-01-01 21:56:45 +08:00
midoks 6d9ec087ce Update index.py 2023-01-01 21:18:55 +08:00
midoks bfb9da0471 up 2023-01-01 20:55:32 +08:00
midoks 8a95a68c81 Update update_dev.sh 2023-01-01 20:55:09 +08:00
midoks d9d81ded3d mysql-apt添加主从同步功能 2023-01-01 20:43:00 +08:00
midoks 403660c980 Update config_api.py 2023-01-01 20:26:36 +08:00
midoks ac06cfc83c mariadb添加新的同步方式 2023-01-01 20:22:06 +08:00
midoks c50012b9b5 Update index.py 2023-01-01 17:17:05 +08:00
midoks baeb0ddc8f up 2023-01-01 17:00:47 +08:00
midoks 83f8c99275 Update update.sh 2022-12-31 21:06:55 +08:00
midoks 2d2b454a31 Update update_dev.sh 2022-12-31 21:05:36 +08:00
midoks aad41ce14b Update index.py 2022-12-31 20:57:56 +08:00
midoks ebf8c82d33 导入优化 2022-12-31 20:30:02 +08:00
midoks d25100fec3 Update index.py 2022-12-31 20:01:20 +08:00
midoks fe658d9009 Update config_api.py 2022-12-31 17:10:58 +08:00
midoks f37c2c9bc6 Update index.py 2022-12-31 16:46:28 +08:00
midoks a1550765aa up 2022-12-31 14:52:39 +08:00
midoks b020f23161 Update info.json 2022-12-31 13:37:11 +08:00
midoks 4afe2061e2 Update mysql.js 2022-12-31 13:32:30 +08:00
midoks ab64c01981 up 2022-12-31 13:14:30 +08:00
midoks 8a97734700 Update index.py 2022-12-31 13:08:03 +08:00
midoks 16b4c64aa5 Update index.py 2022-12-31 13:07:11 +08:00
midoks df2a5919af Update my5.7.cnf 2022-12-30 20:41:51 +08:00
midoks 40a81b794c Update my5.7.cnf 2022-12-30 20:38:40 +08:00
midoks 8fc7ab3abf up 2022-12-30 20:14:28 +08:00
midoks ca4205c89a up 2022-12-30 20:13:35 +08:00
midoks c89b15a645 Update mysql.js 2022-12-30 20:08:00 +08:00
midoks 7a863c4544 修复添加数据库名字判断 2022-12-30 20:07:31 +08:00
midoks 5d81406fdf 优化mysql主从同步UI 2022-12-30 15:21:33 +08:00
midoks cce59c0521 up 2022-12-30 14:56:31 +08:00
Mr Chen 0a5c5aae12 Merge pull request #320 from midoks/midoks-patch-1
Create requirements.txt
2022-12-30 14:25:03 +08:00
Mr Chen 7478426206 Create requirements.txt 2022-12-30 14:24:27 +08:00
midoks c0ed736edb Update index.py 2022-12-30 12:18:35 +08:00
midoks 313eb25ccd Update mysql.js 2022-12-30 12:10:11 +08:00
midoks f58595755b Update index.py 2022-12-30 12:01:02 +08:00
midoks 18a892a576 Update update_dev.sh 2022-12-30 00:32:51 +08:00
midoks 03d558092f Update update_dev.sh 2022-12-30 00:32:20 +08:00
midoks 3cffa9223a Update my5.7.cnf 2022-12-30 00:28:50 +08:00
midoks c0d5aff2f5 mysql配置优化 2022-12-30 00:06:25 +08:00
midoks 3fa4c7804b Update mysql.sql 2022-12-29 23:32:20 +08:00
midoks 22acce8bc0 mysql主从加入新的同步方式 2022-12-29 23:32:14 +08:00
midoks e53e5cd672 Update mw.py 2022-12-28 23:41:02 +08:00
midoks a7d861de22 up 2022-12-28 23:38:37 +08:00
midoks ddeef3c17e up 2022-12-28 23:21:07 +08:00
midoks af733ac28a Merge branch 'master' into dev 2022-12-28 22:34:24 +08:00
Mr Chen 148e2021dc Merge pull request #318 from midoks/dependabot/pip/wheel-0.38.1
Bump wheel from 0.33.6 to 0.38.1
2022-12-28 22:33:23 +08:00
midoks 50c3fe58f4 Update rhel.sh 2022-12-28 21:54:29 +08:00
midoks 5ed3728d4f Update rhel.sh 2022-12-28 21:47:54 +08:00
midoks 9bf06c9d21 Update rhel.sh 2022-12-28 21:47:41 +08:00
midoks 08b3738cfd up 2022-12-28 21:44:18 +08:00
midoks a04f59a3aa Update rhel.sh 2022-12-28 21:31:55 +08:00
midoks 155b8d2b4c Update rhel.sh 2022-12-28 17:26:56 +08:00
dependabot[bot] 039d2794ab Bump wheel from 0.33.6 to 0.38.1
Bumps [wheel](https://github.com/pypa/wheel) from 0.33.6 to 0.38.1.
- [Release notes](https://github.com/pypa/wheel/releases)
- [Changelog](https://github.com/pypa/wheel/blob/main/docs/news.rst)
- [Commits](https://github.com/pypa/wheel/compare/0.33.6...0.38.1)

---
updated-dependencies:
- dependency-name: wheel
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-26 20:58:18 +00:00
midoks 0d2721523c up 2022-12-25 22:43:34 +08:00
midoks f042665c05 Update install.sh 2022-12-25 18:27:27 +08:00
midoks 0118548c24 up 2022-12-24 22:32:14 +08:00
midoks 1ba6625525 Update libsodium.sh 2022-12-24 22:29:33 +08:00
midoks 293f8ab0af up 2022-12-24 22:21:45 +08:00
midoks dc68868883 Update sodium.sh 2022-12-24 21:57:40 +08:00
midoks 30f835b6b8 Update sodium.sh 2022-12-24 21:53:10 +08:00
midoks 4d90fdfe4f Update install.sh 2022-12-21 22:28:40 +08:00
midoks bb4067aa6b Update install.sh 2022-12-21 22:25:36 +08:00
midoks 5dbf89b872 Update README.md 2022-12-21 22:11:54 +08:00
midoks cc244a8ea5 Update README.md 2022-12-21 21:39:57 +08:00
Mr Chen 975cb988f7 Merge pull request #310 from midoks/dev
0.12.0
2022-12-21 21:38:48 +08:00
midoks 5a11d1bff9 Update README.md 2022-12-21 21:36:03 +08:00
midoks bab3501722 0.12.0 2022-12-21 21:26:40 +08:00
midoks 872d68e0f1 Update config_api.py 2022-12-21 21:15:16 +08:00
midoks 9a5878a90c #309 先注释 2022-12-21 21:01:04 +08:00
midoks e5e226b57a Update rhel.sh 2022-12-21 14:28:20 +08:00
midoks 5d12218a3e Update rhel.sh 2022-12-20 13:40:56 +08:00
midoks 7b3bbc8bdd Update install_dev.sh 2022-12-20 13:33:59 +08:00
midoks 67030e91f6 Update rhel.sh 2022-12-20 13:14:35 +08:00
midoks c03eb0aa40 Update rhel.sh 2022-12-20 13:10:18 +08:00
midoks 2b5aaba0b7 Update install_dev.sh 2022-12-20 13:04:55 +08:00
midoks c7a01b8c52 Update rhel.sh 2022-12-20 13:02:45 +08:00
midoks 9bfb72ada5 Update rhel.sh 2022-12-20 12:59:47 +08:00
midoks 5ab8936f6b Update phplib.conf 2022-12-20 12:01:43 +08:00
midoks 43c5161085 Update sodium.sh 2022-12-20 11:53:38 +08:00
midoks 7d90a6e99f Update libsodium.sh 2022-12-20 11:35:23 +08:00
midoks 9ba8028944 up 2022-12-20 11:26:49 +08:00
midoks 36fa3d7630 Update libsodium.sh 2022-12-20 11:20:31 +08:00
midoks b6a2d7e3e8 up 2022-12-20 11:18:21 +08:00
midoks 99e73d1bda libsodium 2022-12-20 00:04:24 +08:00
midoks 1083b46078 Update rhel.sh 2022-12-19 23:59:04 +08:00
midoks 8bd081804d Update index.py 2022-12-19 23:54:28 +08:00
midoks 2536a79481 update 2022-12-19 20:28:12 +08:00
midoks eab11680e5 up 2022-12-19 19:54:09 +08:00
midoks 651b7d6d1b Update index.py 2022-12-19 18:29:42 +08:00
midoks 75e181116e #307 2022-12-19 18:10:57 +08:00
midoks c0a981b5a1 Update crontab.js 2022-12-19 14:38:20 +08:00
midoks 61dc601d48 Update crontab_api.py 2022-12-19 14:30:25 +08:00
midoks 415a2a1f8f #307 2022-12-19 14:27:29 +08:00
midoks 7c06a85ac7 Update crontab_api.py 2022-12-19 14:17:08 +08:00
midoks 7c8329bb78 Update crontab_api.py 2022-12-19 14:04:27 +08:00
midoks 5c8a50429f Update crontab_api.py 2022-12-19 13:50:04 +08:00
midoks 300bdc1eb1 up 2022-12-19 13:47:37 +08:00
midoks a8a9e9033e 文件备份OK 2022-12-18 22:44:59 +08:00
midoks 77425dfc48 up 2022-12-18 21:11:15 +08:00
midoks 52eae4625f Update site_api.py 2022-12-18 15:48:31 +08:00
midoks dafcf60633 Update site_api.py 2022-12-18 15:47:38 +08:00
midoks 79c1e81d2c up 2022-12-18 15:46:12 +08:00
midoks b779a18de6 up 2022-12-18 03:15:39 +08:00
midoks 45f22d239a Update install.sh 2022-12-18 01:15:59 +08:00
midoks f7b688d6bf 站点分类问题 #300 2022-12-18 00:54:26 +08:00
midoks 6f069c715a php-apt 8.2 2022-12-18 00:10:37 +08:00
midoks 98ed286b50 up 2022-12-17 23:57:05 +08:00
midoks e931fa526f up 2022-12-17 23:50:21 +08:00
midoks ff17610732 Update crontab_api.py 2022-12-17 23:23:00 +08:00
midoks 13bbe07ac5 Update site_api.py 2022-12-17 22:28:22 +08:00
midoks 5380cd805c up 2022-12-17 22:19:59 +08:00
midoks c0b056c18d Update index.py 2022-12-17 22:10:01 +08:00
midoks e287086b36 Update index.py 2022-12-17 21:58:07 +08:00
midoks 8647300e16 Update index.py 2022-12-17 21:48:36 +08:00
midoks 636f7af8ef up 2022-12-17 21:34:16 +08:00
midoks 87cf208dfc mysql 2022-12-17 21:28:31 +08:00
midoks 579a84c6f1 Update index.py 2022-12-17 21:14:34 +08:00
midoks 54b90f4484 Update index.py 2022-12-17 21:11:20 +08:00
midoks ebdaf5d395 Update index.py 2022-12-17 21:06:12 +08:00
midoks 1e5ecabbd1 up 2022-12-17 20:52:35 +08:00
midoks 0596dc243e Update index.py 2022-12-17 20:49:18 +08:00
midoks 7fa6ec7cd3 up 2022-12-17 20:46:02 +08:00
midoks c21d8eed8b Update index.py 2022-12-17 20:38:31 +08:00
midoks 326f9f0e6b Update index.py 2022-12-17 20:33:53 +08:00
midoks 60c94f120e Update index.py 2022-12-17 20:32:50 +08:00
midoks 55c08a8916 Update index.py 2022-12-17 20:11:21 +08:00
midoks cf944fb976 Update index.py 2022-12-17 20:02:27 +08:00
midoks cda1ea93a8 Update config_api.py 2022-12-17 20:01:39 +08:00
midoks 46f02cae0d Update crontab.js 2022-12-17 20:00:10 +08:00
midoks 034d00a262 Update config_api.py 2022-12-17 18:59:28 +08:00
midoks fe1a68208b Update crontab.js 2022-12-17 18:59:17 +08:00
midoks f364cc4e2d Update config_api.py 2022-12-17 18:56:42 +08:00
midoks 5b35b5f38c Update crontab.js 2022-12-17 18:56:21 +08:00
midoks 2151b2c597 Update index.py 2022-12-17 18:48:29 +08:00
midoks 4f8e95210f Update index.py 2022-12-17 18:02:20 +08:00
midoks b31297b47f Update crontab_api.py 2022-12-17 17:57:10 +08:00
midoks e492a87160 Update crontab_api.py 2022-12-17 17:42:30 +08:00
midoks 47d6eb7993 Update crontab.js 2022-12-17 17:21:04 +08:00
midoks 3ddccea897 Update config_api.py 2022-12-17 17:17:35 +08:00
midoks 069d594e49 up 2022-12-17 17:16:57 +08:00
midoks 7c49609a2b Update config_api.py 2022-12-17 17:10:01 +08:00
midoks 70d260dd5a update 2022-12-17 17:00:19 +08:00
midoks 58503722c0 Update crontab_api.py 2022-12-17 16:02:51 +08:00
midoks 0a98b4dc09 Update crontab_api.py 2022-12-17 15:45:04 +08:00
midoks 5ab0f493e4 Update crontab_api.py 2022-12-17 14:06:51 +08:00
midoks 30580874fd Update config_api.py 2022-12-17 14:04:01 +08:00
midoks 26327d958c Update crontab_api.py 2022-12-17 14:02:36 +08:00
midoks 522eddd393 Update crontab.js 2022-12-17 13:30:54 +08:00
midoks 935df8d4d1 Update config_api.py 2022-12-17 11:36:13 +08:00
midoks 15b4ba9cbb up 2022-12-17 11:34:37 +08:00
midoks c005e1f56d Update debian.sh 2022-12-17 09:58:56 +08:00
midoks edbca430ee Update debian.sh 2022-12-17 09:55:48 +08:00
midoks f8fedb8285 Update crontab.js 2022-12-16 01:09:59 +08:00
midoks d9472709fd up 2022-12-16 01:02:05 +08:00
midoks 79bf1eae07 Update config_api.py 2022-12-16 00:23:24 +08:00
midoks fab6df7097 up 2022-12-16 00:12:53 +08:00
midoks b9416d58f9 Update info.json 2022-12-16 00:03:29 +08:00
midoks 96d86ce49e Update info.json 2022-12-16 00:01:41 +08:00
midoks 46f2d9f029 Update plugins_api.py 2022-12-15 23:47:06 +08:00
midoks 9d5fb77123 Update plugins_api.py 2022-12-15 23:41:56 +08:00
midoks d0c903bf11 up 2022-12-15 20:03:00 +08:00
midoks 3c1b0e6708 Update crontab_api.py 2022-12-15 20:02:49 +08:00
midoks 340b72591a Merge branch 'dev' of github.com:midoks/mdserver-web into dev 2022-12-15 18:10:49 +08:00
midoks 8f6a194a02 Update debug.sh 2022-12-15 18:06:11 +08:00
Mr Chen 7e65993978 Merge pull request #304 from hiCasper/dev
Revert changes
2022-12-15 18:03:27 +08:00
hiCasper ca27a2d49d Revert changes 2022-12-15 17:53:17 +08:00
Mr Chen b941c9c71b Merge pull request #302 from hiCasper/dev
Dev
2022-12-15 17:05:19 +08:00
hiCasper e68c7b0346 Change log path 2022-12-14 14:17:44 +08:00
midoks c7c3d8fdb9 Create jquery.qrcode.js 2022-12-13 16:15:06 +08:00
hiCasper de383e0584 Fix 2022-12-13 15:12:44 +08:00
midoks 048463cf9d 修改名字 2022-12-13 14:45:29 +08:00
hiCasper 0c118613d5 Add more install logs 2022-12-13 14:39:04 +08:00
hiCasper aa8e9f173b Improve OS detection 2022-12-13 14:34:10 +08:00
hiCasper c21c54198c Better way installing deps 2022-12-13 13:42:42 +08:00
Mr Chen e8ad9995d7 Merge pull request #299 from hiCasper/dev
Fix problems
2022-12-13 13:31:38 +08:00
hiCasper 7a0b6be401 Merge branch 'dev' of https://github.com/hiCasper/mdserver-web into dev 2022-12-13 13:09:57 +08:00
hiCasper 8b2a127ceb Add dev install hint 2022-12-13 13:09:15 +08:00
hiCasper 8a23fb18ff Rollback 2022-12-13 13:08:48 +08:00
hiCasper 234eca3729 Merge branch 'dev' of https://github.com/midoks/mdserver-web into dev 2022-12-13 13:05:22 +08:00
hiCasper 9c74eb6a77 Add dev version hint 2022-12-13 12:59:43 +08:00
hiCasper 0093af2661 Sync upstream 2022-12-13 12:45:38 +08:00
hiCasper 482d9bdd86 Fix locale error 2022-12-13 02:28:40 +08:00
hiCasper d8f9b179ad Add hint 2022-12-13 01:49:51 +08:00
hiCasper d179328976 Revert and fix deps issue 2022-12-13 01:36:25 +08:00
hiCasper 9aaa5e2a65 Log installation to file 2022-12-13 01:36:25 +08:00
hiCasper 821a9ce2d1 Fix acme.sh deps 2022-12-13 00:50:38 +08:00
midoks 7b39b28dcd debian系列回退 2022-12-13 00:08:15 +08:00
midoks c881ad7d18 Update site_api.py 2022-12-12 23:30:57 +08:00
midoks 82da5c2649 Create sub.conf.tpl 2022-12-10 19:54:17 +08:00
Mr Chen 315ba990ec Merge pull request #292 from hiCasper/dev
Optimize install scripts
2022-12-10 15:23:55 +08:00
hiCasper 992fcb7e6f Optimize OS detection 2022-12-10 14:51:19 +08:00
hiCasper d7337239f5 Optimize acme.sh install 2022-12-10 14:40:06 +08:00
hiCasper 224b91fe0b Fix install scripts 2022-12-10 14:02:05 +08:00
Mr Chen 589dfe6da2 Merge pull request #291 from hiCasper/dev
安装脚本等优化
2022-12-10 11:58:21 +08:00
hiCasper b1b47c3c38 Combine install scripts 2022-12-10 03:01:31 +08:00
hiCasper 106f5d897d Skip missing packages 2022-12-10 02:59:16 +08:00
hiCasper c856d09b3b Speed up install 2022-12-10 02:59:16 +08:00
hiCasper 6bf8792bb6 Fix pip location 2022-12-10 02:59:15 +08:00
hiCasper 75edb074b2 Change pip source 2022-12-10 02:37:33 +08:00
hiCasper 4a5df99f06 Fix dnf dependencies 2022-12-10 02:37:33 +08:00
hiCasper 2c7536a138 Fix typo 2022-12-10 02:09:28 +08:00
hiCasper a758fc2264 Fix pip source 2022-12-10 02:09:28 +08:00
hiCasper 0d94fe2d26 Fix yum error 2022-12-10 02:09:28 +08:00
hiCasper 630bdd5f96 Fix error 2022-12-10 02:09:27 +08:00
hiCasper 411fc670fc Combine install scripts 2022-12-10 02:09:27 +08:00
hiCasper cab35c7c28 Fix iptables detection 2022-12-10 02:09:27 +08:00
hiCasper b3c8a6365f Using whitenoise to serving static files 2022-12-10 02:09:27 +08:00
Mr Chen b64f02ff2f Merge pull request #290 from midoks/dev
0.11.4
2022-12-10 01:52:51 +08:00
midoks 7e9df9640e PHP82正式释出。 2022-12-10 01:48:52 +08:00
midoks 6c478be4df Update fileinfo.sh 2022-12-09 22:38:53 +08:00
midoks e919fac1d5 Update fileinfo.sh 2022-12-09 22:38:21 +08:00
midoks 4854142f9a Update fileinfo.sh 2022-12-09 22:24:56 +08:00
midoks f37de275ba Update fileinfo.sh 2022-12-09 22:19:17 +08:00
midoks 0c8f777452 Update phplib.conf 2022-12-09 20:23:52 +08:00
midoks 9235affadf Update phalcon.sh 2022-12-09 20:06:43 +08:00
midoks 2001a3b5dc Update install.sh 2022-12-09 19:49:24 +08:00
midoks 6ba1e4c19c Update yaf.sh 2022-12-09 19:44:58 +08:00
midoks 231048a4a3 Update phalcon.sh 2022-12-09 19:42:08 +08:00
midoks 6b9aab3759 Update fileinfo.sh 2022-12-09 19:22:32 +08:00
midoks e8a152b1a3 Update install.sh 2022-12-09 19:15:58 +08:00
midoks 1cf8e4ecb7 Update fileinfo.sh 2022-12-09 19:11:55 +08:00
midoks aa04235446 up 2022-12-09 19:03:16 +08:00
midoks 3acbc36297 Update tools.py 2022-12-09 18:34:36 +08:00
midoks 85aba4f1ce Update tools.py 2022-12-09 18:32:32 +08:00
midoks f33f80cf34 Update mw.tpl 2022-12-09 18:04:33 +08:00
midoks 1868b3197d Update tools.py 2022-12-09 18:04:07 +08:00
midoks b8e1fb2ca2 Update install.sh 2022-12-09 15:05:41 +08:00
midoks 9aa9c4f799 删除任务时删除日志 2022-12-09 12:16:32 +08:00
midoks 344603f1b2 Update redis.sh 2022-12-08 19:06:49 +08:00
midoks 71b43e9f2f Update redis.sh 2022-12-08 18:53:46 +08:00
midoks adba50bfc1 Update redis.sh 2022-12-08 18:52:52 +08:00
Mr Chen d917fa76f6 Merge pull request #286 from midoks/dev
0.11.3
2022-12-08 00:07:48 +08:00
midoks 32971ccead 0.11.3 2022-12-08 00:06:50 +08:00
midoks c7a3481b95 #285
观察细致入微。
2022-12-07 22:56:18 +08:00
midoks 8915b672e5 Update config_api.py 2022-12-07 19:35:35 +08:00
midoks e472d104d1 Update nginx_panel.conf 2022-12-07 16:33:44 +08:00
midoks 48ee5574c7 Update mw.py 2022-12-07 15:31:47 +08:00
midoks 706afbd681 Update mw.py 2022-12-07 15:31:32 +08:00
midoks 70ca6d0459 Update mw.py 2022-12-07 15:12:18 +08:00
midoks f344a32fa7 Update mw.py 2022-12-07 15:09:43 +08:00
midoks e6123000ac Update nginx_panel.conf 2022-12-07 12:56:30 +08:00
midoks b94361c59f Update config_api.py 2022-12-07 00:37:28 +08:00
midoks 4a7efb67d4 Update config_api.py 2022-12-07 00:22:02 +08:00
midoks 536b7ac997 Update config_api.py 2022-12-07 00:20:31 +08:00
midoks 0f5b055aeb 面板ssl 2022-12-07 00:14:22 +08:00
midoks 7d81a59c8f Update config_api.py 2022-12-06 23:42:23 +08:00
midoks 2540cd1308 Update config_api.py 2022-12-06 23:40:41 +08:00
midoks 5358cd2d38 up 2022-12-06 23:38:09 +08:00
midoks c4bb20ed54 Update files_api.py 2022-12-06 20:37:07 +08:00
midoks 7f2f307131 Update files_api.py 2022-12-06 20:30:02 +08:00
midoks 7a8ef9fe00 Update files_api.py 2022-12-06 20:19:06 +08:00
midoks e1abc66179 Update setting.py 2022-12-06 18:00:28 +08:00
midoks 8bda20c3d0 Update config_api.py 2022-12-06 17:50:49 +08:00
midoks 3c01838df1 Update .gitignore 2022-12-06 17:38:42 +08:00
midoks 3b459e0349 panel ssl 测试中 2022-12-06 17:35:23 +08:00
midoks d3e7684f04 ssl update 2022-12-06 16:27:24 +08:00
midoks b2e6e7f256 Update firewall_api.py 2022-12-06 01:25:53 +08:00
midoks ca6812561a Update mw.py 2022-12-06 01:03:25 +08:00
midoks 44e8079db6 Update README.md 2022-12-05 15:08:39 +08:00
midoks 9bd063cc1d Update phalcon.sh 2022-12-04 16:10:26 +08:00
midoks 1b0044e28a Update index.py 2022-12-04 15:58:04 +08:00
midoks 9b752cf24f up 2022-12-04 15:57:33 +08:00
midoks cf5f98958c Update phplib.conf 2022-12-04 12:19:11 +08:00
midoks c46872dab5 Update phplib.conf 2022-12-04 12:13:14 +08:00
midoks b57edb7b87 加入phalcon 2022-12-04 12:12:26 +08:00
midoks 595cc30c6f Update swoole.sh 2022-12-04 11:42:41 +08:00
midoks 4b6f5a5f4e Update swoole.sh 2022-12-04 11:29:53 +08:00
midoks 741da10906 Update swoole.sh 2022-12-04 11:29:26 +08:00
midoks 9965786976 Update README.md 2022-12-04 11:24:15 +08:00
midoks f083696131 Update install.sh 2022-12-04 11:24:09 +08:00
midoks dd2105d9ab 文件搜索优化 2022-12-04 00:57:03 +08:00
midoks c1574f3430 up 2022-12-03 15:56:58 +08:00
midoks 6394ead5b8 解除域名绑定成功 2022-12-03 13:27:24 +08:00
127 changed files with 5082 additions and 1206 deletions
+1 -2
View File
@@ -11,8 +11,7 @@ Debian x, Centos x, Centos x stream, Ubuntu x?
Ex:
```bash
................start mw success
bash: line 149: ./scripts/init.d/mw: No such file or directory
一个issue,一个问题。多余问题,视情况而答。
```
## 错误截图 ?
+4 -1
View File
@@ -106,6 +106,7 @@ venv.bak/
.idea/*.iml
tmp/*
*.swp
*.pem
*.zip
logs/*
data/*.db
@@ -116,13 +117,15 @@ data/json/config.json
data/site.pl
data/admin_path.pl
data/close.pl
ssl/input.pl
data/datadir.pl
data/502Task.pl
data/default.pl
data/backup.pl
data/debug.pl
data/default_site.pl
ssl/input.pl
lib/python*
bin
+17 -11
View File
@@ -2,6 +2,7 @@
<img alt="logo" src="https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/route/static/logo.png" height="140" />
<h3 align="center">mdserver-web</h3>
<p align="center">一款简单Linux面板服务</p>
<p align="center">强烈推荐系统:debian</p>
</p>
### 简介
@@ -10,10 +11,11 @@
复制了后台管理界面,按照自己想要的方式写了一版。
![CentOS](https://img.shields.io/badge/LINUX-CentOS-blue?style=for-the-badge&logo=CentOS)
![Ubuntu](https://img.shields.io/badge/LINUX-Ubuntu-blue?style=for-the-badge&logo=Ubuntu)
![Debian](https://img.shields.io/badge/LINUX-Debian-blue?style=for-the-badge&logo=Debian)
![Ubuntu](https://img.shields.io/badge/LINUX-Ubuntu-blue?style=for-the-badge&logo=Ubuntu)
![Fedora](https://img.shields.io/badge/LINUX-Fedora-blue?style=for-the-badge&logo=Fedora)
![CentOS](https://img.shields.io/badge/LINUX-CentOS-blue?style=for-the-badge&logo=CentOS)
[![Wiki](https://img.shields.io/badge/MW-Wiki-red?style=for-the-badge&logo=wiki)](https://github.com/midoks/mdserver-web/wiki)
[![](https://data.jsdelivr.com/v1/package/gh/midoks/mdserver-web/badge?style=for-the-badge)](https://www.jsdelivr.com/package/gh/midoks/mdserver-web)
@@ -90,15 +92,15 @@ docker run -itd --name mw-server --privileged=true -p 7200:7200 -p 80:80 -p 443:
```
### 版本更新 0.11.2
### 版本更新 0.12.1
* redis安全强化。
* 添加php命令行设置。
* 域名绑定。
* Gitea优化。
* 网站统计优化。
* centos9下php-gd安装优化。
* php82rc7测试版。
* mysql/mariadb/mysql-apt/mysql-yum增加新的同步方式[同步账户]。
* pureftp安装时自动放行端口。
* 面板ssl,添加【申请Lets证书】功能。
* 站点子域名绑定优化。
* 修复添加数据库名字判断。
* 增加PHP52的隐藏和显示指令。
* 各种细节优化。
### JSDelivr安装地址
@@ -160,7 +162,9 @@ curl -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/up
wget -O uninstall.sh https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/uninstall.sh && bash uninstall.sh
curl -fsSL https://gitee.com/midoks/mdserver-web/raw/master/scripts/install_dev.sh | bash
curl -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/quick/debug.sh | bash
curl -fsSL https://gitee.com/midoks/mdserver-web/raw/dev/scripts/install_dev.sh | bash
curl -fsSL https://gitee.com/midoks/mdserver-web/raw/dev/scripts/update_dev.sh | bash
```
@@ -168,6 +172,8 @@ curl -fsSL https://gitee.com/midoks/mdserver-web/raw/dev/scripts/update_dev.sh |
TVbNgrpeGBGZVm5gTLa21ADP7RpnPFhjya
日行一善,以后必定大富大贵
### 微信赞助
+1 -1
View File
@@ -825,7 +825,7 @@ fi
ret_title, json.dumps(a_auth)))
break
if number_of_checks == self._max_check_num:
if number_of_checks == self.__max_check_num:
raise StopIteration(
"错误:已尝试验证{0}次. 最大验证次数为{1}. 验证时间间隔为{2}秒.".format(
number_of_checks,
+1 -1
View File
@@ -736,7 +736,7 @@ class cert_request:
ret_title, json.dumps(a_auth)))
break
if number_of_checks == self._max_check_num:
if number_of_checks == self.__max_check_num:
raise StopIteration(
"错误:已尝试验证{0}次. 最大验证次数为{1}. 验证时间间隔为{2}秒.".format(
number_of_checks,
+195 -56
View File
@@ -27,7 +27,7 @@ from flask import request
class config_api:
__version = '0.11.2'
__version = '0.12.1'
__api_addr = 'data/api.json'
def __init__(self):
@@ -89,21 +89,6 @@ class config_api:
return mw.returnJson(True, '修改成功!')
return mw.returnJson(False, '修改失败!')
def setPanelDomainApi(self):
domain = request.form.get('domain', '')
cfg_domain = 'data/bind_domain.pl'
if domain == '':
os.remove(cfg_domain)
return mw.returnJson(True, '清空域名成功!')
reg = r"^([\w\-\*]{1,100}\.){1,4}(\w{1,10}|\w{1,10}\.\w{1,10})$"
if not re.match(reg, domain):
return mw.returnJson(False, '主域名格式不正确')
mw.writeFile(cfg_domain, domain)
return mw.returnJson(True, '设置域名成功!')
def syncDateApi(self):
if mw.isAppleSystem():
return mw.returnJson(True, '开发系统不必同步时间!')
@@ -150,7 +135,8 @@ class config_api:
import system_api
import firewall_api
if os.path.exists("/lib/systemd/system/firewalld.service"):
sysCfgDir = mw.systemdCfgDir()
if os.path.exists(sysCfgDir + "/firewalld.service"):
if not firewall_api.firewall_api().getFwStatus():
return mw.returnJson(False, 'firewalld必须先启动!')
@@ -257,7 +243,8 @@ class config_api:
import system_api
import firewall_api
if os.path.exists("/lib/systemd/system/firewalld.service"):
sysCfgDir = mw.systemdCfgDir()
if os.path.exists(sysCfgDir + "/firewalld.service"):
if not firewall_api.firewall_api().getFwStatus():
return mw.returnJson(False, 'firewalld必须先启动!')
@@ -353,21 +340,36 @@ class config_api:
# 获取面板证书
def getPanelSslApi(self):
cert = {}
cert['privateKey'] = mw.readFile('ssl/privateKey.pem')
cert['certPem'] = mw.readFile('ssl/certificate.pem')
cert['rep'] = os.path.exists('ssl/input.pl')
cert = self.getPanelSslData()
return mw.getJson(cert)
def getPanelSslData(self):
cert = {}
keyPath = 'ssl/private.pem'
certPath = 'ssl/cert.pem'
if not os.path.exists(certPath):
mw.createSSL()
cert['privateKey'] = mw.readFile(keyPath)
cert['certPem'] = mw.readFile(certPath)
cert['rep'] = os.path.exists('ssl/input.pl')
cert['info'] = mw.getCertName(certPath)
return cert
# 保存面板证书
def savePanelSslApi(self):
keyPath = 'ssl/privateKey.pem'
certPath = 'ssl/certificate.pem'
keyPath = 'ssl/private.pem'
certPath = 'ssl/cert.pem'
checkCert = '/tmp/cert.pl'
certPem = request.form.get('certPem', '').strip()
privateKey = request.form.get('privateKey', '').strip()
if(privateKey.find('KEY') == -1):
return mw.returnJson(False, '秘钥错误,请检查!')
if(certPem.find('CERTIFICATE') == -1):
return mw.returnJson(False, '证书错误,请检查!')
mw.writeFile(checkCert, certPem)
if privateKey:
mw.writeFile(keyPath, privateKey)
@@ -378,22 +380,176 @@ class config_api:
mw.writeFile('ssl/input.pl', 'True')
return mw.returnJson(True, '证书已保存!')
# 申请面板let证书
def applyPanelLetSslApi(self):
# check domain is bind?
bind_domain = 'data/bind_domain.pl'
if not os.path.exists(bind_domain):
return mw.returnJson(False, '未绑定域名!')
siteName = mw.readFile(bind_domain).strip()
auth_to = mw.getRunDir() + "/tmp"
to_args = {
'domains': [siteName],
'auth_type': 'http',
'auth_to': auth_to,
}
src_letpath = mw.getServerDir() + '/web_conf/letsencrypt/' + siteName
src_csrpath = src_letpath + "/fullchain.pem" # 生成证书路径
src_keypath = src_letpath + "/privkey.pem" # 密钥文件路径
dst_letpath = mw.getRunDir() + '/ssl'
dst_csrpath = dst_letpath + '/cert.pem'
dst_keypath = dst_letpath + '/private.pem'
if not os.path.exists(src_letpath):
import cert_api
data = cert_api.cert_api().applyCertApi(to_args)
if not data['status']:
msg = data['msg']
if type(data['msg']) != str:
msg = data['msg'][0]
emsg = data['msg'][1]['challenges'][0]['error']
msg = msg + '<p><span>响应状态:</span>' + str(emsg['status']) + '</p><p><span>错误类型:</span>' + emsg[
'type'] + '</p><p><span>错误代码:</span>' + emsg['detail'] + '</p>'
return mw.returnJson(data['status'], msg, data['msg'])
mw.buildSoftLink(src_csrpath, dst_csrpath, True)
mw.buildSoftLink(src_keypath, dst_keypath, True)
mw.execShell('echo "lets" > "' + dst_letpath + '/README"')
data = self.getPanelSslData()
tmp_well_know = auth_to + '/.well-known'
if os.path.exists(tmp_well_know):
mw.execShell('rm -rf ' + tmp_well_know)
return mw.returnJson(True, '申请成功!', data)
def setPanelDomainApi(self):
domain = request.form.get('domain', '')
panel_tpl = mw.getRunDir() + "/data/tpl/nginx_panel.conf"
dst_panel_path = mw.getServerDir() + "/web_conf/nginx/vhost/panel.conf"
cfg_domain = 'data/bind_domain.pl'
if domain == '':
os.remove(cfg_domain)
os.remove(dst_panel_path)
mw.restartWeb()
return mw.returnJson(True, '清空域名成功!')
reg = r"^([\w\-\*]{1,100}\.){1,4}(\w{1,10}|\w{1,10}\.\w{1,10})$"
if not re.match(reg, domain):
return mw.returnJson(False, '主域名格式不正确')
op_dir = mw.getServerDir() + "/openresty"
if not os.path.exists(op_dir):
return mw.returnJson(False, '依赖OpenResty,先安装启动它!')
content = mw.readFile(panel_tpl)
content = content.replace("{$PORT}", "80")
content = content.replace("{$SERVER_NAME}", domain)
content = content.replace("{$PANAL_PORT}", mw.readFile('data/port.pl'))
content = content.replace("{$LOGPATH}", mw.getRunDir() + '/logs')
content = content.replace("{$PANAL_ADDR}", mw.getRunDir())
mw.writeFile(dst_panel_path, content)
mw.restartWeb()
mw.writeFile(cfg_domain, domain)
return mw.returnJson(True, '设置域名成功!')
# 设置面板SSL
def setPanelSslApi(self):
sslConf = mw.getRunDir() + '/data/ssl.pl'
panel_tpl = mw.getRunDir() + "/data/tpl/nginx_panel.conf"
dst_panel_path = mw.getServerDir() + "/web_conf/nginx/vhost/panel.conf"
if os.path.exists(sslConf):
os.system('rm -f ' + sslConf)
conf = mw.readFile(dst_panel_path)
if conf:
rep = "\s+ssl_certificate\s+.+;\s+ssl_certificate_key\s+.+;"
conf = re.sub(rep, '', conf)
rep = "\s+ssl_protocols\s+.+;\n"
conf = re.sub(rep, '', conf)
rep = "\s+ssl_ciphers\s+.+;\n"
conf = re.sub(rep, '', conf)
rep = "\s+ssl_prefer_server_ciphers\s+.+;\n"
conf = re.sub(rep, '', conf)
rep = "\s+ssl_session_cache\s+.+;\n"
conf = re.sub(rep, '', conf)
rep = "\s+ssl_session_timeout\s+.+;\n"
conf = re.sub(rep, '', conf)
rep = "\s+ssl_ecdh_curve\s+.+;\n"
conf = re.sub(rep, '', conf)
rep = "\s+ssl_session_tickets\s+.+;\n"
conf = re.sub(rep, '', conf)
rep = "\s+ssl_stapling\s+.+;\n"
conf = re.sub(rep, '', conf)
rep = "\s+ssl_stapling_verify\s+.+;\n"
conf = re.sub(rep, '', conf)
rep = "\s+ssl\s+on;"
conf = re.sub(rep, '', conf)
rep = "\s+error_page\s497.+;"
conf = re.sub(rep, '', conf)
rep = "\s+if.+server_port.+\n.+\n\s+\s*}"
conf = re.sub(rep, '', conf)
rep = "\s+listen\s+443.*;"
conf = re.sub(rep, '', conf)
rep = "\s+listen\s+\[\:\:\]\:443.*;"
conf = re.sub(rep, '', conf)
mw.writeFile(dst_panel_path, conf)
mw.writeLog('面板配置', '面板SSL关闭成功!')
mw.restartWeb()
return mw.returnJson(True, 'SSL已关闭,请使用http协议访问面板!')
else:
os.system('pip install cffi==1.10')
os.system('pip install cryptography==2.1')
os.system('pip install pyOpenSSL==16.2')
try:
if not self.createSSL():
return mw.returnJson(False, '开启失败,无法自动安装pyOpenSSL组件!<p>请尝试手动安装: pip install pyOpenSSL</p>')
if not os.path.exists('ssl/input.ssl'):
mw.createSSL()
mw.writeFile(sslConf, 'True')
keyPath = mw.getRunDir() + '/ssl/private.pem'
certPath = mw.getRunDir() + '/ssl/cert.pem'
conf = mw.readFile(dst_panel_path)
if conf:
if conf.find('ssl_certificate') == -1:
sslStr = """#error_page 404/404.html;
ssl_certificate %s;
ssl_certificate_key %s;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
error_page 497 https://$host$request_uri;""" % (certPath, keyPath)
if(conf.find('ssl_certificate') != -1):
return mw.returnJson(True, 'SSL开启成功!')
conf = conf.replace('#error_page 404/404.html;', sslStr)
rep = "listen\s+([0-9]+)\s*[default_server]*;"
tmp = re.findall(rep, conf)
if not mw.inArray(tmp, '443'):
listen = re.search(rep, conf).group()
http_ssl = "\n\tlisten 443 ssl http2;"
http_ssl = http_ssl + "\n\tlisten [::]:443 ssl http2;"
conf = conf.replace(listen, listen + http_ssl)
mw.backFile(dst_panel_path)
mw.writeFile(dst_panel_path, conf)
isError = mw.checkWebConfig()
if(isError != True):
mw.restoreFile(dst_panel_path)
return mw.returnJson(False, '证书错误: <br><a style="color:red;">' + isError.replace("\n", '<br>') + '</a>')
except Exception as ex:
return mw.returnJson(False, '开启失败,无法自动安装pyOpenSSL组件!<p>请尝试手动安装: pip install pyOpenSSL</p>')
return mw.returnJson(False, '开启失败:' + str(ex))
mw.restartWeb()
return mw.returnJson(True, '开启成功,请使用https协议访问面板!')
def getApi(self):
@@ -401,32 +557,6 @@ class config_api:
return mw.getJson(data)
##### ----- end ----- ###
# 自签证书
def createSSL(self):
if os.path.exists('ssl/input.pl'):
return True
import OpenSSL
key = OpenSSL.crypto.PKey()
key.generate_key(OpenSSL.crypto.TYPE_RSA, 2048)
cert = OpenSSL.crypto.X509()
cert.set_serial_number(0)
cert.get_subject().CN = mw.getLocalIp()
cert.set_issuer(cert.get_subject())
cert.gmtime_adj_notBefore(0)
cert.gmtime_adj_notAfter(86400 * 3650)
cert.set_pubkey(key)
cert.sign(key, 'md5')
cert_ca = OpenSSL.crypto.dump_certificate(
OpenSSL.crypto.FILETYPE_PEM, cert)
private_key = OpenSSL.crypto.dump_privatekey(
OpenSSL.crypto.FILETYPE_PEM, key)
if len(cert_ca) > 100 and len(private_key) > 100:
mw.writeFile('ssl/certificate.pem', cert_ca)
mw.writeFile('ssl/privateKey.pem', private_key)
print(cert_ca, private_key)
return True
return False
# 获取临时登录列表
def getTempLoginApi(self):
if 'tmp_login_expire' in session:
@@ -650,4 +780,13 @@ class config_api:
data['username'] = mw.M('users').where(
"id=?", (1,)).getField('username')
# databases hook 获取
database_file = 'data/hook_database.json'
if os.path.exists(database_file):
df = mw.readFile(database_file)
df = json.loads(df)
data['hook_database'] = df
else:
data['hook_database'] = []
return data
+47 -22
View File
@@ -94,7 +94,7 @@ class crontab_api:
rdata['list'] = mw.getPage(_page)
rdata['p'] = p
# backup hock
# backup hook
bh_file = mw.getPanelDataDir() + "/hook_backup.json"
if os.path.exists(bh_file):
hb_data = mw.readFile(bh_file)
@@ -131,7 +131,7 @@ class crontab_api:
# 参数校验
def cronCheck(self, params):
if params['stype'] == 'site' or params['stype'] == 'database' or params['stype'] == 'logs':
if params['stype'] == 'site' or params['stype'] == 'database' or params['stype'].find('database_') > -1 or params['stype'] == 'logs' or params['stype'] == 'path':
if params['save'] == '':
return False, '保留份数不能为空!'
@@ -230,8 +230,8 @@ class crontab_api:
cronInfo['sbody'] = get['sbody']
cronInfo['urladdress'] = get['urladdress']
addData = mw.M('crontab').where('id=?', (sid,)).save('name,type,where1,where_hour,where_minute,save,backup_to,sbody,urladdress', (get[
'name'], field_type, get['where1'], get['hour'], get['minute'], get['save'], get['backup_to'], get['sbody'], get['urladdress']))
addData = mw.M('crontab').where('id=?', (sid,)).save('name,type,where1,where_hour,where_minute,save,backup_to, sname, sbody,urladdress',
(iname, field_type, get['where1'], get['hour'], get['minute'], get['save'], get['backup_to'], sname, get['sbody'], get['urladdress']))
self.removeForCrond(cronInfo['echo'])
self.syncToCrond(cronInfo)
mw.writeLog('计划任务', '修改计划任务[' + cronInfo['name'] + ']成功')
@@ -377,28 +377,46 @@ class crontab_api:
stype = request.form.get('type', '')
bak_data = []
if stype == 'sites' or stype == 'databases':
if stype == 'site' or stype == 'sites' or stype == 'database' or stype.find('database_') > -1 or stype == 'path':
hookPath = mw.getPanelDataDir() + "/hook_backup.json"
if os.path.exists(hookPath):
t = mw.readFile(hookPath)
bak_data = json.loads(t)
if stype == 'databases':
if stype == 'database' or stype.find('database_') > -1:
sqlite3_name = 'mysql'
path = mw.getServerDir() + '/mysql'
if stype != 'database':
soft_name = stype.replace('database_', '')
path = mw.getServerDir() + '/' + soft_name
if soft_name == 'postgresql':
sqlite3_name = 'pgsql'
db_list = {}
db_list['orderOpt'] = bak_data
path = mw.getServerDir() + '/mysql'
if not os.path.exists(path + '/mysql.db'):
if not os.path.exists(path + '/' + sqlite3_name + '.db'):
db_list['data'] = []
else:
db_list['data'] = mw.M('databases').dbPos(
path, 'mysql').field('name,ps').select()
path, sqlite3_name).field('name,ps').select()
return mw.getJson(db_list)
if stype == 'path':
db_list = {}
db_list['data'] = [{"name": mw.getWwwDir(), "ps": "www"}]
db_list['orderOpt'] = bak_data
return mw.getJson(db_list)
data = {}
data['orderOpt'] = bak_data
data['data'] = mw.M(stype).field('name,ps').select()
default_db = 'sites'
# if stype == 'site' or stype == 'logs':
# stype == 'sites'
data['data'] = mw.M(default_db).field('name,ps').select()
return mw.getJson(data)
##### ----- start ----- ###
@@ -495,13 +513,19 @@ export LANG=en_US.UTF-8
MW_PATH=%s/bin/activate
if [ -f $MW_PATH ];then
source $MW_PATH
fi
''' % (mw.getRunDir(),)
fi''' % (mw.getRunDir(),)
head = head + source_bin_activate + "\n"
log = '.log'
script_dir = mw.getServerDir() + "/mdserver-web/scripts"
script_dir = mw.getRunDir() + "/scripts"
source_stype = 'database'
if stype.find('database_') > -1:
plugin_name = stype.replace('database_', '')
script_dir = mw.getRunDir() + "/plugins/" + plugin_name + "/scripts"
source_stype = stype
stype = 'database'
wheres = {
'path': head + "python3 " + script_dir + "/backup.py path " + param['sname'] + " " + str(param['save']),
@@ -513,13 +537,14 @@ fi
if param['backup_to'] != 'localhost':
cfile = mw.getPluginDir() + "/" + \
param['backup_to'] + "/index.py"
wheres = {
'path': head + "python3 " + cfile + " path " + param['sname'] + " " + str(param['save']),
'site': head + "python3 " + cfile + " site " + param['sname'] + " " + str(param['save']),
'database': head + "python3 " + cfile + " database " + param['sname'] + " " + str(param['save']),
'logs': head + "python3 " + script_dir + "/logs_backup.py " + param['sname'] + log + " " + str(param['save']),
'rememory': head + "/bin/bash " + script_dir + '/rememory.sh'
}
wheres['path'] = head + "python3 " + cfile + \
" path " + param['sname'] + " " + str(param['save'])
wheres['site'] = head + "python3 " + cfile + \
" site " + param['sname'] + " " + str(param['save'])
wheres['database'] = head + "python3 " + cfile + " " + \
source_stype + " " + \
param['sname'] + " " + str(param['save'])
try:
shell = wheres[stype]
except:
@@ -583,9 +608,9 @@ echo "--------------------------------------------------------------------------
# 重载配置
def crondReload(self):
if mw.isAppleSystem():
# mw.execShell('/usr/sbin/cron restart')
if os.path.exists('/etc/crontab'):
pass
# mw.execShell('/usr/sbin/cron restart')
else:
if os.path.exists('/etc/init.d/crond'):
mw.execShell('/etc/init.d/crond reload')
+1
View File
@@ -117,6 +117,7 @@ class Sql():
sql = "SELECT " + self.__OPT_FIELD + " FROM " + self.__DB_TABLE + \
self.__OPT_WHERE + self.__OPT_GROUP + self.__OPT_ORDER + self.__OPT_LIMIT
# print(sql)
# print(self.__OPT_PARAM)
result = self.__DB_CONN.execute(sql, self.__OPT_PARAM)
data = result.fetchall()
# 构造字曲系列
+92 -27
View File
@@ -97,11 +97,11 @@ class files_api:
import shutil
try:
shutil.move(sfile, dfile)
msg = mw.getInfo('移动文件或目录[{1}]到[{2}]成功!', (sfile, dfile,))
msg = mw.getInfo('移动或重名命文件[{1}]到[{2}]成功!', (sfile, dfile,))
mw.writeLog('文件管理', msg)
return mw.returnJson(True, '移动文件或目录成功!')
return mw.returnJson(True, '移动或重名命文件成功!')
except:
return mw.returnJson(False, '移动文件或目录失败!')
return mw.returnJson(False, '移动或重名命文件失败!')
def deleteApi(self):
path = request.form.get('path', '')
@@ -148,12 +148,16 @@ class files_api:
if not os.path.exists(path):
path = mw.getRootDir() + "/wwwroot"
search = request.args.get('search', '').strip().lower()
search_all = request.args.get('all', '').strip().lower()
page = request.args.get('p', '1').strip().lower()
row = request.args.get('showRow', '10')
disk = request.form.get('disk', '')
if disk == 'True':
row = 1000
# return self.getAllDir(path, int(page), int(row), "wp-inlcude")
if search_all == 'yes' and search != '':
return self.getAllDir(path, int(page), int(row), search)
return self.getDir(path, int(page), int(row), search)
def createFileApi(self):
@@ -236,6 +240,11 @@ class files_api:
system_api.system_api().restartTask()
except:
system_api.system_api().restartTask()
# 删除日志
task_log = mw.getRunDir() + "/tmp/panelTask.pl"
if os.path.exists(task_log):
os.remove(task_log)
return mw.returnJson(True, '任务已删除!')
# 上传文件
@@ -830,11 +839,59 @@ class files_api:
if search:
if name.lower().find(search) == -1:
continue
# if name[0:1] == '.':
# continue
if name == '.':
continue
i += 1
return i
def getAllDir(self, path, page=1, page_size=10, search=None):
# print("search:", search)
data = {}
dirnames = []
filenames = []
count = 0
max_limit = 3000
for d_list in os.walk(path):
if count >= max_limit:
break
for d in d_list[1]:
if count >= max_limit:
break
if d.lower().find(search) != -1:
filename = d_list[0] + '/' + d
if not os.path.exists(filename):
continue
dirnames.append(self.__get_stats(filename, path))
count += 1
for f in d_list[2]:
if count >= max_limit:
break
if f.lower().find(search) != -1:
filename = d_list[0] + '/' + f
if not os.path.exists(filename):
continue
filenames.append(self.__get_stats(filename, path))
count += 1
data['DIR'] = sorted(dirnames)
data['FILES'] = sorted(filenames)
data['PATH'] = path.replace('//', '/')
info = {}
info['count'] = len(dirnames) + len(filenames)
info['row'] = page_size
info['p'] = page
info['tojs'] = 'getFiles'
pageObj = mw.getPageObject(info, '1,2,3,4,5,6,7,8')
data['PAGE'] = pageObj[0]
return mw.getJson(data)
def getDir(self, path, page=1, page_size=10, search=None):
data = {}
dirnames = []
@@ -861,31 +918,14 @@ class files_api:
continue
try:
filePath = path + '/' + filename
link = ''
if os.path.islink(filePath):
filePath = os.readlink(filePath)
link = ' -> ' + filePath
if not os.path.exists(filePath):
filePath = path + '/' + filePath
if not os.path.exists(filePath):
continue
if not os.path.exists(filePath):
continue
stat = os.stat(filePath)
accept = str(oct(stat.st_mode)[-3:])
mtime = str(int(stat.st_mtime))
user = ''
try:
user = pwd.getpwuid(stat.st_uid).pw_name
except Exception as ee:
user = str(stat.st_uid)
size = str(stat.st_size)
file_stats = self.__get_stats(filePath, path)
if os.path.isdir(filePath):
dirnames.append(filename + ';' + size + ';' +
mtime + ';' + accept + ';' + user + ';' + link)
dirnames.append(file_stats)
else:
filenames.append(filename + ';' + size + ';' +
mtime + ';' + accept + ';' + user + ';' + link)
filenames.append(file_stats)
n += 1
except Exception as e:
continue
@@ -894,3 +934,28 @@ class files_api:
data['PATH'] = path.replace('//', '/')
return mw.getJson(data)
def __get_stats(self, filename, path=None):
filename = filename.replace('//', '/')
try:
stat = os.stat(filename)
accept = str(oct(stat.st_mode)[-3:])
mtime = str(int(stat.st_mtime))
user = ''
try:
user = str(pwd.getpwuid(stat.st_uid).pw_name)
except:
user = str(stat.st_uid)
size = str(stat.st_size)
link = ''
if os.path.islink(filename):
link = ' -> ' + os.readlink(filename)
if path:
tmp_path = (path + '/').replace('//', '/')
filename = filename.replace(tmp_path, '', 1)
return filename + ';' + size + ';' + mtime + ';' + accept + ';' + user + ';' + link
except Exception as e:
# print(e)
return ';;;;;'
+29 -9
View File
@@ -342,6 +342,28 @@ class firewall_api:
status = request.form.get('status', '1')
return mw.getJson(self.setFw(status))
def setFwIptables(self, status):
# iptables特殊处理
if status == '1':
mw.execShell('service iptables save')
mw.execShell('service iptables stop')
else:
# 重新导入数据
_list = mw.M('firewall').field('id,port,ps,addtime').limit(
'0,1000').order('id desc').select()
for x in _list:
port = x['port']
if mw.isIpAddr(port):
cmd = 'iptables -I INPUT -s ' + port + ' -j DROP'
mw.execShell(cmd)
else:
self.addAcceptPort(port)
mw.execShell('service iptables save')
mw.execShell('service iptables start')
def setFw(self, status):
if status == '1':
if self.__isUfw:
@@ -352,19 +374,17 @@ class firewall_api:
elif self.__isMac:
pass
else:
mw.execShell('/etc/init.d/iptables save')
mw.execShell('/etc/init.d/iptables stop')
self.setFwIptables(status)
else:
if self.__isUfw:
mw.execShell("echo 'y'|sudo ufw enable")
mw.execShell("echo 'y'| ufw enable")
if self.__isFirewalld:
mw.execShell('systemctl start firewalld.service')
mw.execShell('systemctl enable firewalld.service')
elif self.__isMac:
pass
else:
mw.execShell('/etc/init.d/iptables save')
mw.execShell('/etc/init.d/iptables restart')
self.setFwIptables(status)
return mw.returnData(True, '设置成功!')
@@ -437,8 +457,8 @@ class firewall_api:
elif self.__isMac:
pass
else:
mw.execShell('/etc/init.d/iptables save')
mw.execShell('/etc/init.d/iptables restart')
mw.execShell('service iptables save')
mw.execShell('service iptables restart')
def getFwStatus(self):
if self.__isUfw:
@@ -456,8 +476,8 @@ class firewall_api:
elif self.__isMac:
return False
else:
cmd = "ps -ef|grep iptables |grep -v grep | awk '{print $2}'"
cmd = "systemctl status iptables | grep 'inactive'"
data = mw.execShell(cmd)
if data[0] == '':
if data[0] != '':
return False
return True
+149 -4
View File
@@ -219,6 +219,15 @@ def isInstalledWeb():
return False
def isIpAddr(ip):
check_ip = re.compile(
'^(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|[1-9])\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)$')
if check_ip.match(ip):
return True
else:
return False
def restartWeb():
return opWeb("reload")
@@ -490,6 +499,18 @@ def backFile(file, act=None):
execShell("cp -p {0} {1}".format(file, file + file_type))
def removeBackFile(file, act=None):
"""
@name 删除备份配置文件
@param file 需要删除备份文件
@param act 如果存在,则还原默认配置
"""
file_type = "_bak"
if act:
file_type = "_def"
execShell("rm -rf {0}".format(file + file_type))
def restoreFile(file, act=None):
"""
@name 还原配置文件
@@ -920,13 +941,22 @@ def checkDomainPanel():
tmp = getHost()
domain = readFile('data/bind_domain.pl')
port = readFile('data/port.pl').strip()
npid = getServerDir() + "/openresty/nginx/logs/nginx.pid"
if not os.path.exists(npid):
return False
nconf = getServerDir() + "/web_conf/nginx/vhost/panel.conf"
if os.path.exists(nconf):
port = "80"
if domain:
client_ip = getClientIp()
if client_ip in ['127.0.0.1', 'localhost', '::1']:
return False
if tmp.strip().lower() != domain.strip().lower():
from flask import Flask, redirect, request, url_for
to = "http://" + domain + ":" + port
to = "http://" + domain + ":" + str(port)
return redirect(to, code=302)
return False
@@ -1051,7 +1081,7 @@ def makeConf():
file = getRunDir() + '/data/json/config.json'
if not os.path.exists(file):
c = {}
c['title'] = '猫王面板'
c['title'] = '大圣面板'
c['home'] = 'http://github/midoks/mdserver-web'
c['recycle_bin'] = True
c['template'] = 'default'
@@ -1282,13 +1312,107 @@ def get_string_arr(t):
t_arr.append(str(i) + str(j))
return t_arr
# 转换时间
def strfDate(sdate):
return time.strftime('%Y-%m-%d', time.strptime(sdate, '%Y%m%d%H%M%S'))
# 获取证书名称
def getCertName(certPath):
if not os.path.exists(certPath):
return None
try:
import OpenSSL
result = {}
x509 = OpenSSL.crypto.load_certificate(
OpenSSL.crypto.FILETYPE_PEM, readFile(certPath))
# 取产品名称
issuer = x509.get_issuer()
result['issuer'] = ''
if hasattr(issuer, 'CN'):
result['issuer'] = issuer.CN
if not result['issuer']:
is_key = [b'0', '0']
issue_comp = issuer.get_components()
if len(issue_comp) == 1:
is_key = [b'CN', 'CN']
for iss in issue_comp:
if iss[0] in is_key:
result['issuer'] = iss[1].decode()
break
if not result['issuer']:
if hasattr(issuer, 'O'):
result['issuer'] = issuer.O
# 取到期时间
result['notAfter'] = strfDate(
bytes.decode(x509.get_notAfter())[:-1])
# 取申请时间
result['notBefore'] = strfDate(
bytes.decode(x509.get_notBefore())[:-1])
# 取可选名称
result['dns'] = []
for i in range(x509.get_extension_count()):
s_name = x509.get_extension(i)
if s_name.get_short_name() in [b'subjectAltName', 'subjectAltName']:
s_dns = str(s_name).split(',')
for d in s_dns:
result['dns'].append(d.split(':')[1])
subject = x509.get_subject().get_components()
# 取主要认证名称
if len(subject) == 1:
result['subject'] = subject[0][1].decode()
else:
if not result['dns']:
for sub in subject:
if sub[0] == b'CN':
result['subject'] = sub[1].decode()
break
if 'subject' in result:
result['dns'].append(result['subject'])
else:
result['subject'] = result['dns'][0]
result['endtime'] = int(int(time.mktime(time.strptime(
result['notAfter'], "%Y-%m-%d")) - time.time()) / 86400)
return result
except Exception as e:
# print(getTracebackInfo())
return None
def createSSL():
# 自签证书
if os.path.exists('ssl/input.pl'):
return True
import OpenSSL
key = OpenSSL.crypto.PKey()
key.generate_key(OpenSSL.crypto.TYPE_RSA, 2048)
cert = OpenSSL.crypto.X509()
cert.set_serial_number(0)
cert.get_subject().CN = getLocalIp()
cert.set_issuer(cert.get_subject())
cert.gmtime_adj_notBefore(0)
cert.gmtime_adj_notAfter(86400 * 3650)
cert.set_pubkey(key)
cert.sign(key, 'md5')
cert_ca = OpenSSL.crypto.dump_certificate(
OpenSSL.crypto.FILETYPE_PEM, cert)
private_key = OpenSSL.crypto.dump_privatekey(
OpenSSL.crypto.FILETYPE_PEM, key)
if len(cert_ca) > 100 and len(private_key) > 100:
writeFile('ssl/cert.pem', cert_ca, 'wb+')
writeFile('ssl/private.pem', private_key, 'wb+')
return True
return False
def getSSHPort():
try:
file = '/etc/ssh/sshd_config'
conf = readFile(file)
rep = "#*Port\s+([0-9]+)\s*\n"
port = re.search(rep, conf).groups(0)[0]
rep = "(#*)?Port\s+([0-9]+)\s*\n"
port = re.search(rep, conf).groups(0)[1]
return int(port)
except:
return 22
@@ -1350,3 +1474,24 @@ def getMyORMDb():
import ormDb
o = ormDb.ORM()
return o
# ---------------------------------------------------------------------------------
# 打印相关
# ---------------------------------------------------------------------------------
def echoStart(tag):
print("=" * 90)
print("★开始{}[{}]".format(tag, formatDate()))
print("=" * 90)
def echoEnd(tag):
print("=" * 90)
print("☆{}完成[{}]".format(tag, formatDate()))
print("=" * 90)
print("\n")
def echoInfo(msg):
print("|-{}".format(msg))
+2 -2
View File
@@ -236,7 +236,7 @@ class plugins_api:
if 'hook' in info:
hooks = info['hook']
for x in hooks:
if x in ['backup']:
if x in ['backup', 'database']:
self.hookInstallFile(x, info)
return True
return False
@@ -245,7 +245,7 @@ class plugins_api:
if 'hook' in info:
hooks = info['hook']
for x in hooks:
if x in ['backup']:
if x in ['backup', 'database']:
self.hookUninstallFile(x, info)
return True
return False
+74 -102
View File
@@ -76,16 +76,16 @@ class site_api:
def listApi(self):
limit = request.form.get('limit', '10')
p = request.form.get('p', '1')
type_id = request.form.get('type_id', '')
type_id = request.form.get('type_id', '0').strip()
start = (int(p) - 1) * (int(limit))
siteM = mw.M('sites')
if type_id != '' and type_id == '-1' and type_id == '0':
siteM.where('type_id=?', (type_id))
siteM = mw.M('sites').field('id,name,path,status,ps,addtime,edate')
if type_id != '' and int(type_id) >= 0:
siteM.where('type_id=?', (type_id,))
_list = siteM.field('id,name,path,status,ps,addtime,edate').limit(
(str(start)) + ',' + limit).order('id desc').select()
_list = siteM.limit((str(start)) + ',' +
limit).order('id desc').select()
for i in range(len(_list)):
_list[i]['backup_count'] = mw.M('backup').where(
@@ -140,6 +140,10 @@ class site_api:
return mw.getJson(data)
def getCliPhpVersionApi(self):
php_dir = mw.getServerDir() + '/php'
if not os.path.exists(php_dir):
return mw.returnJson(False, '未安装PHP,无法设置')
php_bin = '/usr/bin/php'
php_versions = self.getPhpVersion()
php_versions = php_versions[1:]
@@ -589,7 +593,7 @@ class site_api:
key = mw.readFile(key_path)
csr = mw.readFile(csr_path)
cert_data = self.getCertName(csr_path)
cert_data = mw.getCertName(csr_path)
data = {
'status': status,
'domain': domains,
@@ -813,9 +817,9 @@ class site_api:
if not len(domains):
return mw.returnJson(False, '请选择域名')
file = self.getHostConf(siteName)
if os.path.exists(file):
siteConf = mw.readFile(file)
host_conf_file = self.getHostConf(siteName)
if os.path.exists(host_conf_file):
siteConf = mw.readFile(host_conf_file)
if siteConf.find('301-END') != -1:
return mw.returnJson(False, '检测到您的站点做了301重定向设置,请先关闭重定向!')
@@ -833,25 +837,21 @@ class site_api:
if os.path.exists(proxy_dir_file):
return mw.returnJson(False, '检测到您的站点做了反向代理设置,请先关闭反向代理!')
auth_to = self.getSitePath(siteName)
# fix binddir domain ssl apply question
mw.backFile(host_conf_file)
auth_to = self.getSitePath(siteName)
rep = "\s*root\s*(.+);"
replace_root = "\n\troot " + auth_to + ";"
siteConf = re.sub(rep, replace_root, siteConf)
mw.writeFile(host_conf_file, siteConf)
mw.restartWeb()
to_args = {
'domains': domains,
'auth_type': 'http',
'auth_to': auth_to,
}
import cert_api
data = cert_api.cert_api().applyCertApi(to_args)
if not data['status']:
msg = data['msg']
if type(data['msg']) != str:
msg = data['msg'][0]
emsg = data['msg'][1]['challenges'][0]['error']
msg = msg + '<p><span>响应状态:</span>' + str(emsg['status']) + '</p><p><span>错误类型:</span>' + emsg[
'type'] + '</p><p><span>错误代码:</span>' + emsg['detail'] + '</p>'
return mw.returnJson(data['status'], msg, data['msg'])
src_letpath = mw.getServerDir() + '/web_conf/letsencrypt/' + siteName
src_csrpath = src_letpath + "/fullchain.pem" # 生成证书路径
src_keypath = src_letpath + "/privkey.pem" # 密钥文件路径
@@ -860,11 +860,23 @@ class site_api:
dst_csrpath = dst_letpath + '/fullchain.pem'
dst_keypath = dst_letpath + '/privkey.pem'
if not os.path.exists(dst_letpath):
mw.execShell('mkdir -p ' + dst_letpath)
mw.buildSoftLink(src_csrpath, dst_csrpath, True)
mw.buildSoftLink(src_keypath, dst_keypath, True)
mw.execShell('echo "lets" > "' + dst_letpath + '/README"')
if not os.path.exists(src_letpath):
import cert_api
data = cert_api.cert_api().applyCertApi(to_args)
mw.restoreFile(host_conf_file)
if not data['status']:
msg = data['msg']
if type(data['msg']) != str:
msg = data['msg'][0]
emsg = data['msg'][1]['challenges'][0]['error']
msg = msg + '<p><span>响应状态:</span>' + str(emsg['status']) + '</p><p><span>错误类型:</span>' + emsg[
'type'] + '</p><p><span>错误代码:</span>' + emsg['detail'] + '</p>'
return mw.returnJson(data['status'], msg, data['msg'])
mw.execShell('mkdir -p ' + dst_letpath)
mw.buildSoftLink(src_csrpath, dst_csrpath, True)
mw.buildSoftLink(src_keypath, dst_keypath, True)
mw.execShell('echo "lets" > "' + dst_letpath + '/README"')
# 写入配置文件
result = self.setSslConf(siteName)
@@ -873,6 +885,8 @@ class site_api:
result['csr'] = mw.readFile(src_csrpath)
result['key'] = mw.readFile(src_keypath)
mw.restartWeb()
return mw.returnJson(data['status'], data['msg'], result)
def getAcmeLogsApi(self):
@@ -1115,6 +1129,26 @@ class site_api:
host = self.getHostConf(siteName)
return mw.getJson({'host': host})
def saveHostConfApi(self):
path = request.form.get('path', '')
data = request.form.get('data', '')
encoding = request.form.get('encoding', '')
import files_api
mw.backFile(path)
save_ret_data = files_api.files_api().saveBody(path, data, encoding)
rdata = json.loads(save_ret_data)
if rdata['status']:
isError = mw.checkWebConfig()
if isError != True:
mw.restoreFile(path)
return mw.returnJson(False, 'ERROR: 检测到配置文件有错误,请先排除后再操作<br><br><a style="color:red;">' + isError.replace("\n", '<br>') + '</a>')
mw.restartWeb()
mw.removeBackFile(path)
return save_ret_data
def getRewriteConfApi(self):
siteName = request.form.get('siteName', '')
rewrite = self.getRewriteConf(siteName)
@@ -1208,14 +1242,15 @@ class site_api:
return mw.returnJson(False, '端口范围不合法!')
opid = mw.M('domain').where(
"name=? AND (port=? OR pid=?)", (domain, domain_port, pid)).getField('pid')
if opid:
if mw.M('sites').where('id=?', (opid,)).count():
return mw.returnJson(False, '指定域名已绑定过!')
mw.M('domain').where('pid=?', (opid,)).delete()
"name=? AND (port=? OR pid=?)", (domain_name, domain_port, pid,)).getField('pid')
if opid > 0:
return mw.returnJson(False, '您添加的域名[{}],已绑定!'.format(domain_name))
# if mw.M('sites').where('id=?', (opid,)).count():
# return mw.returnJson(False, '指定域名已绑定过!')
# mw.M('domain').where('pid=?', (opid,)).delete()
if mw.M('binding').where('domain=?', (domain,)).count():
return mw.returnJson(False, '您添加的域名已存在!')
return mw.returnJson(False, '您添加的域名,子目录已绑定!')
self.nginxAddDomain(webname, domain_name, domain_port)
@@ -1267,17 +1302,18 @@ class site_api:
content = content.replace('{$ROOT_DIR}', webdir)
content = content.replace('{$SERVER_MAIN}', siteInfo['name'])
content = content.replace('{$OR_REWRITE}', self.rewritePath)
content = content.replace('{$PHP_DIR}', self.setupPath + '/php')
content = content.replace('{$LOGPATH}', mw.getLogsDir())
conf += "\r\n" + content
shutil.copyfile(filename, '/tmp/backup.conf')
mw.backFile(filename)
mw.writeFile(filename, conf)
conf = mw.readFile(filename)
# 检查配置是否有误
isError = mw.checkWebConfig()
if isError != True:
shutil.copyfile('/tmp/backup.conf', filename)
mw.restoreFile(filename)
return mw.returnJson(False, 'ERROR: <br><a style="color:red;">' + isError.replace("\n", '<br>') + '</a>')
mw.M('binding').add('pid,domain,port,path,addtime',
@@ -1287,6 +1323,7 @@ class site_api:
(siteInfo['name'], dirName, domain))
mw.writeLog('网站管理', msg)
mw.restartWeb()
mw.removeBackFile(filename)
return mw.returnJson(True, '添加成功!')
def delDirBindApi(self):
@@ -2582,7 +2619,6 @@ location ^~ {from} {\n\
conf = conf.replace(listen, listen + http_ssl)
mw.backFile(file)
mw.writeFile(file, conf)
isError = mw.checkWebConfig()
if(isError != True):
@@ -2599,7 +2635,7 @@ location ^~ {from} {\n\
def saveCert(self, keyPath, certPath):
try:
certInfo = self.getCertName(certPath)
certInfo = mw.getCertName(certPath)
if not certInfo:
return mw.returnData(False, '证书解析失败!')
vpath = self.sslDir + '/' + certInfo['subject'].strip()
@@ -2612,70 +2648,6 @@ location ^~ {from} {\n\
except Exception as e:
return mw.returnData(False, '证书保存失败!')
# 转换时间
def strfDate(self, sdate):
return time.strftime('%Y-%m-%d', time.strptime(sdate, '%Y%m%d%H%M%S'))
# 获取证书名称
def getCertName(self, certPath):
if not os.path.exists(certPath):
return None
try:
import OpenSSL
result = {}
x509 = OpenSSL.crypto.load_certificate(
OpenSSL.crypto.FILETYPE_PEM, mw.readFile(certPath))
# 取产品名称
issuer = x509.get_issuer()
result['issuer'] = ''
if hasattr(issuer, 'CN'):
result['issuer'] = issuer.CN
if not result['issuer']:
is_key = [b'0', '0']
issue_comp = issuer.get_components()
if len(issue_comp) == 1:
is_key = [b'CN', 'CN']
for iss in issue_comp:
if iss[0] in is_key:
result['issuer'] = iss[1].decode()
break
if not result['issuer']:
if hasattr(issuer, 'O'):
result['issuer'] = issuer.O
# 取到期时间
result['notAfter'] = self.strfDate(
bytes.decode(x509.get_notAfter())[:-1])
# 取申请时间
result['notBefore'] = self.strfDate(
bytes.decode(x509.get_notBefore())[:-1])
# 取可选名称
result['dns'] = []
for i in range(x509.get_extension_count()):
s_name = x509.get_extension(i)
if s_name.get_short_name() in [b'subjectAltName', 'subjectAltName']:
s_dns = str(s_name).split(',')
for d in s_dns:
result['dns'].append(d.split(':')[1])
subject = x509.get_subject().get_components()
# 取主要认证名称
if len(subject) == 1:
result['subject'] = subject[0][1].decode()
else:
if not result['dns']:
for sub in subject:
if sub[0] == b'CN':
result['subject'] = sub[1].decode()
break
if 'subject' in result:
result['dns'].append(result['subject'])
else:
result['subject'] = result['dns'][0]
result['endtime'] = int(int(time.mktime(time.strptime(
result['notAfter'], "%Y-%m-%d")) - time.time()) / 86400)
return result
except:
return None
# 清除多余user.ini
def delUserInI(self, path, up=0):
for p1 in os.listdir(path):
+1 -1
View File
@@ -17,7 +17,7 @@ server
#ERROR-PAGE-END
#PHP-INFO-START
include enable-php-{$PHPVER}.conf;
include {$PHP_DIR}/conf/enable-php-{$PHPVER}.conf;
#PHP-INFO-END
#REWRITE-START
+23
View File
@@ -0,0 +1,23 @@
server
{
listen {$PORT};
listen [::]:{$PORT};
server_name {$SERVER_NAME};
index index.php index.html index.htm default.php default.htm default.html;
#SSL-START
#error_page 404/404.html;
#SSL-END
#PROXY-START
location ^~ / {
proxy_pass http://0.0.0.0:{$PANAL_PORT}/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
}
#PROXY-END
error_log {$LOGPATH}/{$SERVER_NAME}.error.log;
}
+4 -2
View File
@@ -80,11 +80,13 @@ class FtpPSClient:
return default_config
def injection_config(self, data):
host = data["ftp_host"].strip()
self.__host = host = data["ftp_host"].strip()
if host.find(':') == -1:
self.__port = self.default_port
else:
self.__host = host.split(':')[0]
self.__port = host.split(':')[1]
self.__host = data['ftp_host'].strip()
self.__user = data['ftp_user'].strip()
self.__password = data['ftp_pass'].strip()
bp = data['backup_path'].strip()
+21 -13
View File
@@ -57,9 +57,8 @@ def getArgs():
tmp[t[0]] = t[1]
elif args_len > 1:
for i in range(len(args)):
t = args[i].split(':')
t = args[i].split(':', 1)
tmp[t[0]] = t[1]
return tmp
@@ -180,28 +179,37 @@ def backupAllFunc(stype):
name = sys.argv[2]
num = sys.argv[3]
args = stype + " " + name + " " + num
cmd = 'python3 ' + mw.getRunDir() + '/scripts/backup.py ' + args
os.system(cmd)
# 开始执行上传信息
prefix_dict = {
"site": "web",
"database": "db",
"path": "path",
}
find_path = mw.getBackupDir() + '/' + stype + '/' + \
prefix_dict[stype] + '_' + name
args = stype + " " + name + " " + num
cmd = 'python3 ' + mw.getRunDir() + '/scripts/backup.py ' + args
if stype.find('database_') > -1:
plugin_name = stype.replace('database_', '')
args = "database " + name + " " + num
cmd = 'python3 ' + mw.getRunDir() + '/plugins/' + plugin_name + \
'/scripts/backup.py ' + args
os.system(cmd)
# 开始执行上传信息
bk_prefix = prefix_dict[stype]
bk_name = stype
if stype.find('database_') > -1:
bk_name = 'database'
plugin_name = stype.replace('database_', '')
bk_prefix = plugin_name + '/db'
find_path = mw.getBackupDir() + '/' + bk_name + '/' + bk_prefix + '_' + name
find_new_file = "ls " + find_path + \
"_* | grep '.gz' | cut -d \ -f 1 | awk 'END {print}'"
filename = mw.execShell(find_new_file)[0].strip()
if filename == "":
print("not find upload file!")
mw.echoInfo("not find upload file!")
return False
ftp = FtpPSClient()
@@ -251,7 +259,7 @@ if __name__ == "__main__":
print(deleteDir())
elif func == 'delete_file':
print(deleteFile())
elif in_array(func, ['site', 'database', 'path']):
elif in_array(func, ['site', 'database', 'path']) or func.find('database_') > -1:
print(backupAllFunc(func))
else:
print('error')
+1 -1
View File
@@ -1,6 +1,6 @@
{
"sort": 7,
"ps": "简单邮件服务[DEV]",
"ps": "[<b style='color:red;'>DEV</b>]简单邮件服务",
"name": "imail",
"title": "邮件服务",
"shell": "install.sh",
+4 -1
View File
@@ -21,10 +21,13 @@ OSNAME_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F
ARCH="amd64"
get_arch() {
TMP_ARCH=`arch`
if [ "$TMP_ARCH" == "x86_64" ];then
ARCH="amd64"
else if [ "$TMP_ARCH" == "aarch64" ];then
ARCH="arm64"
else
echo $ARCH
fi
}
+14
View File
@@ -40,4 +40,18 @@ CREATE TABLE IF NOT EXISTS `slave_id_rsa` (
`addtime` TEXT
);
-- 从库配置主库的[user]
-- drop table `slave_user`;
CREATE TABLE IF NOT EXISTS `slave_sync_user` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`ip` TEXT,
`port` TEXT,
`user` TEXT,
`pass` TEXT,
`mode` TEXT,
`cmd` TEXT,
`addtime` TEXT
);
+1
View File
@@ -70,6 +70,7 @@ replicate-ignore-db = information_schema
replicate-ignore-db = performance_schema
replicate-ignore-db = mysql
replicate-ignore-db = test
replicate-wild-ignore-table=mysql.%
innodb_data_home_dir = {$SERVER_APP_PATH}/data
+334 -24
View File
@@ -71,6 +71,13 @@ def checkArgs(data, ck=[]):
return (True, mw.returnJson(True, 'ok'))
def getBackupDir():
bk_path = mw.getBackupDir() + "/database/mariadb"
if not os.path.isdir(bk_path):
mw.execShell("mkdir -p {}".format(bk_path))
return bk_path
def getConf():
path = getServerDir() + '/etc/my.cnf'
return path
@@ -133,20 +140,27 @@ def contentReplace(content):
def pSqliteDb(dbname='databases'):
file = getServerDir() + '/mariadb.db'
name = 'mysql'
if not os.path.exists(file):
import_sql = mw.readFile(getPluginDir() + '/conf/mariadb.sql')
md5_sql = mw.md5(import_sql)
import_sign = False
save_md5_file = getServerDir() + '/import_sql.md5'
if os.path.exists(save_md5_file):
save_md5_sql = mw.readFile(save_md5_file)
if save_md5_sql != md5_sql:
import_sign = True
mw.writeFile(save_md5_file, md5_sql)
else:
mw.writeFile(save_md5_file, md5_sql)
if not os.path.exists(file) or import_sql:
conn = mw.M(dbname).dbPos(getServerDir(), name)
csql = mw.readFile(getPluginDir() + '/conf/mariadb.sql')
csql_list = csql.split(';')
csql_list = import_sql.split(';')
for index in range(len(csql_list)):
conn.execute(csql_list[index], ())
else:
# 现有run
# conn = mw.M(dbname).dbPos(getServerDir(), name)
# csql = mw.readFile(getPluginDir() + '/conf/mysql.sql')
# csql_list = csql.split(';')
# for index in range(len(csql_list)):
# conn.execute(csql_list[index], ())
conn = mw.M(dbname).dbPos(getServerDir(), name)
conn = mw.M(dbname).dbPos(getServerDir(), name)
return conn
@@ -273,6 +287,13 @@ def binLog():
return mw.returnJson(True, '设置成功!')
def cleanBinLog():
db = pMysqlDb()
cleanTime = time.strftime('%Y-%m-%d %H:%i:%s', time.localtime())
db.execute("PURGE MASTER LOGS BEFORE '" + cleanTime + "';")
return mw.returnJson(True, '清理BINLOG成功!')
def setSkipGrantTables(v):
'''
设置是否密码验证
@@ -675,7 +696,7 @@ def __createUser(dbname, username, password, address):
def getDbBackupListFunc(dbname=''):
bkDir = mw.getRootDir() + '/backup/database'
bkDir = getBackupDir()
blist = os.listdir(bkDir)
r = []
@@ -709,11 +730,11 @@ def importDbBackup():
file = args['file']
name = args['name']
file_path = mw.getRootDir() + '/backup/database/' + file
file_path_sql = mw.getRootDir() + '/backup/database/' + file.replace('.gz', '')
file_path = getBackupDir() + '/' + file
file_path_sql = getBackupDir() + '/' + file.replace('.gz', '')
if not os.path.exists(file_path_sql):
cmd = 'cd ' + mw.getRootDir() + '/backup/database && gzip -d ' + file
cmd = 'cd ' + mw.getBackupDir() + '/database/mariadb && gzip -d ' + file
mw.execShell(cmd)
pwd = pSqliteDb('config').where('id=?', (1,)).getField('mysql_root')
@@ -735,7 +756,7 @@ def importDbExternal():
file = args['file']
name = args['name']
import_dir = mw.getRootDir() + '/backup/import/'
import_dir = mw.getBackupDir() + '/import/'
file_path = import_dir + file
if not os.path.exists(file_path):
@@ -797,7 +818,7 @@ def deleteDbBackup():
path = args['path']
full_file = ""
bkDir = mw.getRootDir() + '/backup/database'
bkDir = getBackupDir()
full_file = bkDir + '/' + args['filename']
if path != "":
full_file = path + "/" + args['filename']
@@ -812,7 +833,10 @@ def getDbBackupList():
return data[1]
r = getDbBackupListFunc(args['name'])
bkDir = mw.getRootDir() + '/backup/database'
bkDir = getBackupDir()
if not os.path.exists(bkDir):
os.mkdir(bkDir)
rr = []
for x in range(0, len(r)):
p = bkDir + '/' + r[x]
@@ -835,7 +859,7 @@ def getDbBackupList():
def getDbBackupImportList():
bkImportDir = mw.getRootDir() + '/backup/import'
bkImportDir = mw.getBackupDir() + '/import'
if not os.path.exists(bkImportDir):
os.mkdir(bkImportDir)
@@ -1775,7 +1799,7 @@ def addMasterRepSlaveUser(version=''):
# address = args['address'].strip()
# dataAccess = args['dataAccess'].strip()
reg = "^[\w\.-]+$"
reg = "^[\w-]+$"
if not re.match(reg, username):
return mw.returnJson(False, '用户名不能带有特殊符号!')
checks = ['root', 'mysql', 'test', 'sys', 'panel_logs']
@@ -1796,11 +1820,15 @@ def addMasterRepSlaveUser(version=''):
sql = "GRANT REPLICATION SLAVE ON *.* TO '" + username + \
"'@'%' identified by '" + password + "';"
result = pdb.execute(sql)
result = pdb.execute('FLUSH PRIVILEGES;')
isError = isSqlError(result)
if isError != None:
return isError
sql_select = "grant select,lock tables,PROCESS on *.* to " + username + "@'%';"
pdb.execute(sql_select)
pdb.execute('FLUSH PRIVILEGES;')
addTime = time.strftime('%Y-%m-%d %X', time.localtime())
psdb.add('username,password,accept,ps,addtime',
(username, password, '%', '', addTime))
@@ -1837,10 +1865,16 @@ def getMasterRepSlaveUserCmd(version):
mode = recognizeDbMode()
# 查找同步点
# SELECT BINLOG_GTID_POS('master1-bin.000002', 561866201);
if mode == "gtid":
sql = "CHANGE MASTER TO MASTER_HOST='" + ip + "', MASTER_PORT=" + port + ", MASTER_USER='" + \
clist[0]['username'] + "', MASTER_PASSWORD='" + \
clist[0]['password'] + "', MASTER_AUTO_POSITION=1"
clist[0]['password'] + "', " + "MASTER_LOG_FILE='" + mstatus[0]["File"] + \
"',MASTER_LOG_POS=" + \
str(mstatus[0]["Position"]) + \
",MASTER_USE_GTID=slave_pos,MASTER_CONNECT_RETRY=10;"
else:
sql = "CHANGE MASTER TO MASTER_HOST='" + ip + "', MASTER_PORT=" + port + ", MASTER_USER='" + \
clist[0]['username'] + "', MASTER_PASSWORD='" + \
@@ -1926,6 +1960,123 @@ def getSlaveSSHList(version=''):
return mw.getJson(data)
def getSlaveSyncUserByIp(version=''):
args = getArgs()
data = checkArgs(args, ['ip'])
if not data[0]:
return data[1]
ip = args['ip']
conn = pSqliteDb('slave_sync_user')
data = conn.field('ip,port,user,pass,mode,cmd').where(
"ip=?", (ip,)).select()
return mw.returnJson(True, 'ok', data)
def addSlaveSyncUser(version=''):
import base64
args = getArgs()
data = checkArgs(args, ['ip'])
if not data[0]:
return data[1]
ip = args['ip']
if ip == "":
return mw.returnJson(True, 'ok')
data = checkArgs(args, ['port', 'user', 'pass', 'mode'])
if not data[0]:
return data[1]
cmd = args['cmd']
port = args['port']
user = args['user']
apass = args['pass']
mode = args['mode']
addTime = time.strftime('%Y-%m-%d %X', time.localtime())
conn = pSqliteDb('slave_sync_user')
data = conn.field('ip').where("ip=?", (ip,)).select()
if len(data) > 0:
res = conn.where("ip=?", (ip,)).save(
'port,user,pass,mode,cmd', (port, user, apass, mode, cmd))
else:
conn.add('ip,port,user,cmd,user,pass,mode,addtime',
(ip, port, user, cmd, user, apass, mode, addTime))
return mw.returnJson(True, '设置成功!')
def delSlaveSyncUser(version=''):
args = getArgs()
data = checkArgs(args, ['ip'])
if not data[0]:
return data[1]
ip = args['ip']
conn = pSqliteDb('slave_sync_user')
conn.where("ip=?", (ip,)).delete()
return mw.returnJson(True, '删除成功!')
def getSlaveSyncUserList(version=''):
args = getArgs()
data = checkArgs(args, ['page', 'page_size'])
if not data[0]:
return data[1]
page = int(args['page'])
page_size = int(args['page_size'])
conn = pSqliteDb('slave_sync_user')
limit = str((page - 1) * page_size) + ',' + str(page_size)
field = 'id,ip,port,user,pass,cmd,addtime'
clist = conn.field(field).limit(limit).order('id desc').select()
count = conn.count()
data = {}
_page = {}
_page['count'] = count
_page['p'] = page
_page['row'] = page_size
_page['tojs'] = args['tojs']
data['page'] = mw.getPage(_page)
data['data'] = clist
return mw.getJson(data)
def getSyncModeFile():
return getServerDir() + "/sync.mode"
def getSlaveSyncMode(version):
sync_mode = getSyncModeFile()
if os.path.exists(sync_mode):
mode = mw.readFile(sync_mode).strip()
return mw.returnJson(True, 'ok', mode)
return mw.returnJson(False, 'fail')
def setSlaveSyncMode(version):
args = getArgs()
data = checkArgs(args, ['mode'])
if not data[0]:
return data[1]
mode = args['mode']
sync_mode = getSyncModeFile()
if mode == 'none':
os.remove(sync_mode)
else:
mw.writeFile(sync_mode, mode)
return mw.returnJson(True, '设置成功', mode)
def getSlaveSSHByIp(version=''):
args = getArgs()
data = checkArgs(args, ['ip'])
@@ -2003,8 +2154,6 @@ def getSlaveList(version=''):
db = pMysqlDb()
dlist = db.query('show slave status')
# print(dlist)
ret = []
for x in range(0, len(dlist)):
tmp = {}
@@ -2014,6 +2163,24 @@ def getSlaveList(version=''):
tmp['Master_Log_File'] = dlist[x]["Master_Log_File"]
tmp['Slave_IO_Running'] = dlist[x]["Slave_IO_Running"]
tmp['Slave_SQL_Running'] = dlist[x]["Slave_SQL_Running"]
tmp['Last_Error'] = dlist[x]["Last_Error"]
tmp['Last_IO_Error'] = dlist[x]["Last_IO_Error"]
tmp['Last_SQL_Error'] = dlist[x]["Last_SQL_Error"]
tmp['Slave_SQL_Running_State'] = dlist[x]["Slave_SQL_Running_State"]
tmp['Error'] = ''
if tmp['Last_Error'] != '':
tmp['Error'] = tmp['Last_Error']
if tmp['Last_IO_Error'] != '':
tmp['Error'] = tmp['Last_IO_Error']
if tmp['Last_SQL_Error'] != '':
tmp['Error'] = tmp['Last_SQL_Error']
if tmp['Error'] == '':
tmp['Error'] = tmp['Slave_SQL_Running_State']
ret.append(tmp)
data = {}
data['data'] = ret
@@ -2030,6 +2197,47 @@ def getSlaveSyncCmd(version=''):
def initSlaveStatus(version=''):
mode_file = getSyncModeFile()
if not os.path.exists(mode_file):
return mw.returnJson(False, '需要先设置同步配置')
mode = mw.readFile(mode_file)
if mode == 'ssh':
return initSlaveStatusSSH(version)
if mode == 'sync-user':
return initSlaveStatusSyncUser(version)
def initSlaveStatusSyncUser(version=''):
conn = pSqliteDb('slave_sync_user')
data = conn.field('ip,port,user,pass,mode,cmd').find()
if len(data) < 1:
return mw.returnJson(False, '需要先添加同步用户配置!')
# print(data)
db = pMysqlDb()
dlist = db.query('show slave status')
if len(dlist) > 0:
return mw.returnJson(False, '已经初始化好了zz...')
u = data
mode_name = 'classic'
if u['mode'] == '1':
mode_name = 'gtid'
local_mode = recognizeDbMode()
if local_mode != mode_name:
return mw.returnJson(False, '同步模式不一致!')
# print(u['cmd'])
t = db.query(u['cmd'])
# print(t)
db.query("start slave")
return mw.returnJson(True, '初始化成功!')
def initSlaveStatusSSH(version=''):
db = pMysqlDb()
dlist = db.query('show slave status')
if len(dlist) > 0:
@@ -2100,6 +2308,41 @@ def initSlaveStatus(version=''):
def setSlaveStatus(version=''):
mode_file = getSyncModeFile()
if not os.path.exists(mode_file):
return mw.returnJson(False, '需要先设置同步配置')
mode = mw.readFile(mode_file)
if mode == 'ssh':
return setSlaveStatusSSH(version)
if mode == 'sync-user':
return setSlaveStatusSyncUser(version)
def setSlaveStatusSyncUser(version=''):
db = pMysqlDb()
dlist = db.query('show slave status')
if len(dlist) == 0:
return mw.returnJson(False, '需要手动添加同步账户或者执行初始化!')
if len(dlist) > 0 and (dlist[0]["Slave_IO_Running"] == 'Yes' or dlist[0]["Slave_SQL_Running"] == 'Yes'):
db.query('stop slave')
else:
ip = dlist[0]['Master_Host']
conn = pSqliteDb('slave_sync_user')
data = conn.field('ip,port,user,pass,mode,cmd').find()
if len(data) == 0:
return mw.returnJson(False, '没有数据无法重启!')
user = data['user']
apass = data['pass']
db.query("start slave")
# db.query("start slave user='{}' password='{}';".format(user, apass))
return mw.returnJson(True, '设置成功!')
def setSlaveStatusSSH(version=''):
db = pMysqlDb()
dlist = db.query('show slave status')
@@ -2168,6 +2411,61 @@ def writeDbSyncStatus(data):
def doFullSync(version=''):
mode_file = getSyncModeFile()
if not os.path.exists(mode_file):
return mw.returnJson(False, '需要先设置同步配置')
mode = mw.readFile(mode_file)
if mode == 'ssh':
return doFullSyncSSH(version)
if mode == 'sync-user':
return doFullSyncUser(version)
def doFullSyncUser(version=''):
args = getArgs()
data = checkArgs(args, ['db'])
if not data[0]:
return data[1]
sync_db = args['db']
db = pMysqlDb()
conn = pSqliteDb('slave_sync_user')
data = conn.field('ip,port,user,pass,mode,cmd').find()
user = data['user']
apass = data['pass']
port = data['port']
ip = data['ip']
bak_file = '/tmp/tmp.sql'
writeDbSyncStatus({'code': 0, 'msg': '开始同步...', 'progress': 0})
writeDbSyncStatus({'code': 1, 'msg': '远程导出数据...', 'progress': 20})
if not os.path.exists(bak_file):
dump_sql_data = getServerDir() + "/bin/mysqldump --force --opt --default-character-set=utf8 --single-transaction -h" + ip + " -P" + \
port + " -u" + user + " -p" + apass + " " + sync_db + " > " + bak_file
mw.execShell(dump_sql_data)
writeDbSyncStatus({'code': 2, 'msg': '本地导入数据...', 'progress': 40})
if os.path.exists(bak_file):
pwd = pSqliteDb('config').where('id=?', (1,)).getField('mysql_root')
sock = getSocketFile()
my_import_cmd = getServerDir() + '/bin/mysql -S ' + sock + ' -uroot -p' + pwd + \
' ' + sync_db + ' < ' + bak_file
mw.execShell(my_import_cmd)
db.query("start slave")
writeDbSyncStatus({'code': 6, 'msg': '从库重启完成...', 'progress': 100})
os.system("rm -rf " + bak_file)
return True
def doFullSyncSSH(version=''):
args = getArgs()
data = checkArgs(args, ['db'])
@@ -2473,6 +2771,18 @@ if __name__ == "__main__":
print(delSlaveSSH(version))
elif func == 'update_slave_ssh':
print(updateSlaveSSH(version))
elif func == 'get_slave_sync_user_list':
print(getSlaveSyncUserList(version))
elif func == 'get_slave_sync_user_by_ip':
print(getSlaveSyncUserByIp(version))
elif func == 'add_slave_sync_user':
print(addSlaveSyncUser(version))
elif func == 'del_slave_sync_user':
print(delSlaveSyncUser(version))
elif func == 'get_slave_sync_mode':
print(getSlaveSyncMode(version))
elif func == 'set_slave_sync_mode':
print(setSlaveSyncMode(version))
elif func == 'init_slave_status':
print(initSlaveStatus(version))
elif func == 'set_slave_status':
+1
View File
@@ -1,4 +1,5 @@
{
"hook":["database"],
"title":"MariaDB",
"tip":"soft",
"name":"mariadb",
+273 -22
View File
@@ -39,7 +39,7 @@ function myPostN(method,args,callback, title){
if (typeof(title) != 'undefined'){
_title = title;
}
$.post('/plugins/run', {name:'mysql', func:method, args:_args}, function(data) {
$.post('/plugins/run', {name:'mariadb', func:method, args:_args}, function(data) {
if(typeof(callback) == 'function'){
callback(data);
}
@@ -487,19 +487,6 @@ function showHidePass(obj){
}
}
function copyPass(password){
var clipboard = new ClipboardJS('#bt_copys');
clipboard.on('success', function (e) {
layer.msg('复制成功',{icon:1,time:2000});
});
clipboard.on('error', function (e) {
layer.msg('复制失败,浏览器不兼容!',{icon:2,time:2000});
});
$("#bt_copys").attr('data-clipboard-text',password);
$("#bt_copys").click();
}
function checkSelect(){
setTimeout(function () {
var num = $('input[type="checkbox"].check:checked').length;
@@ -668,8 +655,8 @@ function addDatabase(type){
<span class='tname'>数据库名</span>\
<div class='info-r'><input name='name' class='bt-input-text mr5' placeholder='新的数据库名称' type='text' style='width:65%' value=''>\
<select class='bt-input-text mr5 codeing_a5nGsm' name='codeing' style='width:27%'>\
<option value='utf8'>utf-8</option>\
<option value='utf8mb4'>utf8mb4</option>\
<option value='utf8'>utf-8</option>\
<option value='gbk'>gbk</option>\
<option value='big5'>big5</option>\
</select>\
@@ -1517,7 +1504,7 @@ function getMasterRepSlaveUserCmd(username, db=''){
title: '同步命令',
area: '500px',
content:"<form class='bt-form pd20 pb70' id='add_master'>\
<div class='line'>"+cmd+"</div>\
<div class='line' style='word-wrap: break-word;word-break: normal;'>"+cmd+"</div>\
<div class='bt-form-submit-btn'>\
<button type='button' class='btn btn-success btn-sm btn-title class-copy-cmd'>复制</button>\
</div>\
@@ -1808,11 +1795,27 @@ function addSlaveSSH(ip=''){
}
function delSlaveSSH(ip){
myPost('del_slave_ssh', {ip:ip}, function(rdata){
var rdata = $.parseJSON(rdata.data);
layer.msg(rdata.msg, {icon: rdata.status ? 1 : 2});
getSlaveSSHPage();
showMsg(rdata.msg,function(){
if (rdata.status){
getSlaveSSHPage();
}
},{icon: rdata.status ? 1 : 2}, 600);
});
}
function delSlaveSyncUser(ip){
myPost('del_slave_sync_user', {ip:ip}, function(rdata){
var rdata = $.parseJSON(rdata.data);
showMsg(rdata.msg,function(){
if (rdata.status){
getSlaveSyncUserPage();
}
},{icon: rdata.status ? 1 : 2}, 600);
});
}
@@ -1861,6 +1864,234 @@ function getSlaveSSHPage(page=1){
});
}
function addSlaveSyncUser(ip=''){
myPost('get_slave_sync_user_by_ip', {ip:ip}, function(rdata){
var rdata = $.parseJSON(rdata.data);
var ip = '127.0.0.1';
var port = "22";
var cmd = '';
var user = 'input_sync_user';
var pass = 'input_sync_pwd';
var mode = '0';
if (rdata.data.length>0){
ip = rdata.data[0]['ip'];
port = rdata.data[0]['port'];
cmd = rdata.data[0]['cmd'];
user = rdata.data[0]['user'];
pass = rdata.data[0]['pass'];
mode = rdata.data[0]['mode'];
}
var index = layer.open({
type: 1,
area: ['500px','470px'],
title: '同步账户',
closeBtn: 1,
shift: 5,
shadeClose: true,
btn:["确认","取消"],
content: "<form class='bt-form pd20'>\
<div class='line'><span class='tname'>IP</span><div class='info-r'><input name='ip' class='bt-input-text mr5' type='text' style='width:330px;' value='"+ip+"'></div></div>\
<div class='line'><span class='tname'>端口</span><div class='info-r'><input name='port' class='bt-input-text mr5' type='number' style='width:330px;' value='"+port+"'></div></div>\
<div class='line'><span class='tname'>同步账户</span><div class='info-r'><input name='user' class='bt-input-text mr5' type='text' style='width:330px;' value='"+user+"'></div></div>\
<div class='line'><span class='tname'>同步密码</span><div class='info-r'><input name='pass' class='bt-input-text mr5' type='text' style='width:330px;' value='"+pass+"'></div></div>\
<div class='line'>\
<span class='tname'>CMD[必填]</span>\
<div class='info-r'><textarea class='bt-input-text mr5' row='20' cols='30' name='cmd' style='width:330px;height:150px;'></textarea></div>\
</div>\
<input type='hidden' name='mode' value='"+mode+"' />\
</form>",
success:function(){
$('textarea[name="cmd"]').html(cmd);
$('textarea[name="cmd"]').change(function(){
var val = $(this).val();
var vlist = val.split(',');
var a = {};
for (var i in vlist) {
var tmp = toTrim(vlist[i]);
var tmp_a = tmp.split(" ");
var real_tmp = tmp_a[tmp_a.length-1];
var kv = real_tmp.split("=");
a[kv[0]] = kv[1].replace("'",'').replace("'",'');
}
$('input[name="ip"]').val(a['MASTER_HOST']);
$('input[name="port"]').val(a['MASTER_PORT']);
$('input[name="user"]').val(a['MASTER_USER']);
$('input[name="pass"]').val(a['MASTER_PASSWORD']);
console.log(a['MASTER_USE_GTID'],typeof(a['MASTER_USE_GTID']));
if (typeof(a['MASTER_USE_GTID']) != 'undefined' ){
$('input[name="mode"]').val('1');
}
});
},
yes:function(index){
var ip = $('input[name="ip"]').val();
var port = $('input[name="port"]').val();
var user = $('input[name="user"]').val();
var pass = $('input[name="pass"]').val();
var cmd = $('textarea[name="cmd"]').val();
var mode = $('input[name="mode"]').val();
var data = {ip:ip,port:port,cmd:cmd,user:user,pass:pass,mode:mode};
myPost('add_slave_sync_user', data, function(ret_data){
layer.close(index);
var rdata = $.parseJSON(ret_data.data);
showMsg(rdata.msg,function(){
if (rdata.status){
getSlaveSyncUserPage();
}
},{icon: rdata.status ? 1 : 2},600);
});
}
});
});
}
function getSlaveSyncUserPage(page=1){
var _data = {};
_data['page'] = page;
_data['page_size'] = 5;
_data['tojs'] ='getSlaveSyncUserPage';
myPost('get_slave_sync_user_list', _data, function(data){
var layerId = null;
var rdata = [];
try {
rdata = $.parseJSON(data.data);
} catch(e) {
console.log(e);
}
var list = '';
var user_list = rdata['data'];
for (i in user_list) {
var ip = user_list[i]['ip'];
var port = user_list[i]['port'];
var user = user_list[i]['user'];
var apass = user_list[i]['pass'];
var cmd = '未设置';
if (user_list[i]['cmd']!=''){
cmd = '已设置';
}
list += '<tr><td>'+ip+'</td>\
<td>'+port+'</td>\
<td>'+user+'</td>\
<td>'+apass+'</td>\
<td>'+cmd+'</td>\
<td>\
<a class="btlink" onclick="addSlaveSyncUser(\''+ip+'\');">修改</a> | \
<a class="btlink" onclick="delSlaveSyncUser(\''+ip+'\');">删除</a>\
</td>\
</tr>';
}
$('.get-slave-ssh-list tbody').html(list);
$('.dataTables_paginate_4').html(rdata['page']);
});
}
function getSlaveCfg(){
myPost('get_slave_sync_mode', '', function(data){
var rdata = $.parseJSON(data.data);
var mode_none = 'success';
var mode_ssh = 'danger';
var mode_sync_user = 'danger';
if(rdata.status){
var mode_none = 'danger';
if (rdata.data == 'ssh'){
var mode_ssh = 'success';
var mode_sync_user = 'danger';
} else {
var mode_ssh = 'danger';
var mode_sync_user = 'success';
}
}
layerId = layer.open({
type: 1,
title: '同步配置',
area: ['400px','180px'],
content:"<div class='bt-form pd20 c6'>\
<p class='conf_p'>\
<span class='f14 c6 mr20'>当前从库同步模式</span>\
<b class='f14 c6 mr20'></b>\
<button class='btn btn-"+mode_none+" btn-xs slave-db-mode btn-none'>无</button>\
<button class='btn btn-"+mode_ssh+" btn-xs slave-db-mode btn-ssh'>SSH</button>\
<button class='btn btn-"+mode_sync_user+" btn-xs slave-db-mode btn-sync-user'>同步账户</button>\
</p>\
<hr />\
<p class='conf_p'>\
<span class='f14 c6 mr20'>配置设置</span>\
<b class='f14 c6 mr20'></b>\
<button class='btn btn-success btn-xs btn-slave-ssh'>SSH</button>\
<button class='btn btn-success btn-xs btn-slave-user'>同步账户</button>\
</p>\
</div>",
success:function(){
$('.btn-slave-ssh').click(function(){
getSlaveSSHList();
});
$('.btn-slave-user').click(function(){
getSlaveUserList();
});
$('.slave-db-mode').click(function(){
var _this = this;
var mode = 'none';
if ($(this).hasClass('btn-ssh')){
mode = 'ssh';
}
if ($(this).hasClass('btn-sync-user')){
mode = 'sync-user';
}
myPost('set_slave_sync_mode', {mode:mode}, function(data){
var rdata = $.parseJSON(data.data);
showMsg(rdata.msg,function(){
$('.slave-db-mode').remove('btn-success').addClass('btn-danger');
$(_this).removeClass('btn-danger').addClass('btn-success');
},{icon:rdata.status?1:2},2000);
});
});
}
});
});
}
function getSlaveUserList(){
var page = '<div class="dataTables_paginate_4 dataTables_paginate paging_bootstrap page" style="margin-top:0px;"></div>';
page += '<div class="table_toolbar" style="left:0px;"><span class="sync btn btn-default btn-sm" onclick="addSlaveSyncUser()" title="">添加同步账户</span></div>';
layerId = layer.open({
type: 1,
title: '同步账户列表',
area: '500px',
content:"<div class='bt-form pd20 c6'>\
<div class='divtable mtb10'>\
<div><table class='table table-hover get-slave-ssh-list'>\
<thead><tr><th>IP</th><th>PORT</th><th>同步账户</th><th>同步密码</th><th>CMD</th><th>操作</th></tr></thead>\
<tbody></tbody>\
</table></div>\
"+page +"\
</div>\
</div>",
success:function(){
getSlaveSyncUserPage(1);
}
});
}
function getSlaveSSHList(page=1){
@@ -1981,7 +2212,7 @@ function masterOrSlaveConf(version=''){
for(i in rdata.data){
var v = rdata.data[i];
var status = "异常";
var status = "<a class='btlink db_error'>异常</>";
if (v['Slave_SQL_Running'] == 'Yes' && v['Slave_IO_Running'] == 'Yes'){
status = "正常";
}
@@ -2023,6 +2254,26 @@ function masterOrSlaveConf(version=''){
// <span class="sync btn btn-default btn-sm" onclick="getMasterRepSlaveList()" title="">添加</span>\
// </div>
$(".table_slave_status_list").html(con);
$('.db_error').click(function(){
layer.open({
type: 1,
title: '同步异常信息',
area: '500px',
content:"<form class='bt-form pd20 pb70'>\
<div class='line'>"+v['Error']+"</div>\
<div class='bt-form-submit-btn'>\
<button type='button' class='btn btn-success btn-sm btn-title class-copy-db-err'>复制</button>\
</div>\
</form>",
success:function(){
copyText(v['Error']);
$('.class-copy-db-err').click(function(){
copyText(v['Error']);
});
}
});
});
});
}
@@ -2100,7 +2351,7 @@ function masterOrSlaveConf(version=''){
<p class="conf_p">\
<span class="f14 c6 mr20">Slave[从]配置</span><span class="f14 c6 mr20"></span>\
<button class="btn '+(!rdata.slave_status ? 'btn-danger' : 'btn-success')+' btn-xs btn-slave">'+(!rdata.slave_status ? '未启动' : '已启动') +'</button>\
<button class="btn btn-success btn-xs" onclick="getSlaveSSHList()" >[主]SSH配置</button>\
<button class="btn btn-success btn-xs" onclick="getSlaveCfg()" >同步配置</button>\
<button class="btn btn-success btn-xs" onclick="initSlaveStatus()" >初始化</button>\
</p>\
<hr/>\
@@ -2174,10 +2425,10 @@ function masterOrSlaveConf(version=''){
getMasterDbList();
}
if (rdata.slave_status){
// if (rdata.slave_status){
getAsyncMasterDbList();
getAsyncDataList()
}
// }
});
}
getMasterStatus();
+1 -1
View File
@@ -40,7 +40,7 @@ class backupTools:
"----------------------------------------------------------------------------")
return
backup_path = mw.getRootDir() + '/backup/database'
backup_path = mw.getRootDir() + '/backup/database/mariadb'
if not os.path.exists(backup_path):
mw.execShell("mkdir -p " + backup_path)
+5 -4
View File
@@ -36,11 +36,12 @@ max_connect_errors = 100
open_files_limit = 2560
max_allowed_packet = 128M
#skip-external-locking
#skip-grant-tables
#loose-skip-innodb
skip_name_resolve = 1
skip-ssl
#skip-networking
#skip-name-resolve
#skip-external-locking
#loose-skip-innodb
#skip-grant-tables
log-bin=mysql-bin
binlog_format=mixed
+13
View File
@@ -40,4 +40,17 @@ CREATE TABLE IF NOT EXISTS `slave_id_rsa` (
`addtime` TEXT
);
-- 从库配置主库的[user]
-- drop table `slave_user`;
CREATE TABLE IF NOT EXISTS `slave_sync_user` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`ip` TEXT,
`port` TEXT,
`user` TEXT,
`pass` TEXT,
`mode` TEXT,
`cmd` TEXT,
`addtime` TEXT
);
+1
View File
@@ -13,6 +13,7 @@
<p onclick="myLogs();">日志</p>
<p onclick="pluginLogs('mysql-apt',$('.plugin_version').attr('version'),'show_log');">慢日志</p>
<p onclick="dbList()">管理列表</p>
<p onclick="masterOrSlaveConf($('.plugin_version').attr('version'))">主从配置</p>
</div>
<div class="bt-w-con pd15">
<div class="soft-man-con"></div>
+367 -35
View File
@@ -67,6 +67,13 @@ def getArgs():
return tmp
def getBackupDir():
bk_path = mw.getBackupDir() + "/database/mysql-apt"
if not os.path.isdir(bk_path):
mw.execShell("mkdir -p {}".format(bk_path))
return bk_path
def checkArgs(data, ck=[]):
for i in range(len(ck)):
if not ck[i] in data:
@@ -115,20 +122,27 @@ def contentReplace(content):
def pSqliteDb(dbname='databases'):
file = getServerDir() + '/mysql.db'
name = 'mysql'
if not os.path.exists(file):
import_sql = mw.readFile(getPluginDir() + '/conf/mysql.sql')
md5_sql = mw.md5(import_sql)
import_sign = False
save_md5_file = getServerDir() + '/import_sql.md5'
if os.path.exists(save_md5_file):
save_md5_sql = mw.readFile(save_md5_file)
if save_md5_sql != md5_sql:
import_sign = True
mw.writeFile(save_md5_file, md5_sql)
else:
mw.writeFile(save_md5_file, md5_sql)
if not os.path.exists(file) or import_sql:
conn = mw.M(dbname).dbPos(getServerDir(), name)
csql = mw.readFile(getPluginDir() + '/conf/mysql.sql')
csql_list = csql.split(';')
csql_list = import_sql.split(';')
for index in range(len(csql_list)):
conn.execute(csql_list[index], ())
else:
# 现有run
# conn = mw.M(dbname).dbPos(getServerDir(), name)
# csql = mw.readFile(getPluginDir() + '/conf/mysql.sql')
# csql_list = csql.split(';')
# for index in range(len(csql_list)):
# conn.execute(csql_list[index], ())
conn = mw.M(dbname).dbPos(getServerDir(), name)
conn = mw.M(dbname).dbPos(getServerDir(), name)
return conn
@@ -147,9 +161,16 @@ def pMysqlDb():
def initDreplace(version=''):
my_dir = getServerDir() + '/etc'
if not os.path.exists(my_dir):
os.mkdir(my_dir)
conf_dir = getServerDir() + '/etc'
mode_dir = conf_dir + '/mode'
conf_list = [
conf_dir,
mode_dir,
]
for conf in conf_list:
if not os.path.exists(conf):
os.mkdir(conf)
tmp_dir = getServerDir() + '/tmp'
if not os.path.exists(tmp_dir):
@@ -157,12 +178,26 @@ def initDreplace(version=''):
mw.execShell("chown -R mysql:mysql " + tmp_dir)
mw.execShell("chmod 750 " + tmp_dir)
mysql_conf = my_dir + '/my.cnf'
if not os.path.exists(mysql_conf):
mysql_conf_tpl = getPluginDir() + '/conf/my' + version + '.cnf'
content = mw.readFile(mysql_conf_tpl)
my_conf = conf_dir + '/my.cnf'
if not os.path.exists(my_conf):
tpl = getPluginDir() + '/conf/my' + version + '.cnf'
content = mw.readFile(tpl)
content = contentReplace(content)
mw.writeFile(mysql_conf, content)
mw.writeFile(my_conf, content)
classic_conf = mode_dir + '/classic.cnf'
if not os.path.exists(classic_conf):
tpl = getPluginDir() + '/conf/classic.cnf'
content = mw.readFile(tpl)
content = contentReplace(content)
mw.writeFile(classic_conf, content)
gtid_conf = mode_dir + '/gtid.cnf'
if not os.path.exists(gtid_conf):
tpl = getPluginDir() + '/conf/gtid.cnf'
content = mw.readFile(tpl)
content = contentReplace(content)
mw.writeFile(gtid_conf, content)
# systemd
systemDir = mw.systemdCfgDir()
@@ -650,7 +685,11 @@ def __createUser(dbname, username, password, address):
def getDbBackupListFunc(dbname=''):
bkDir = mw.getRootDir() + '/backup/database'
bkDir = getBackupDir()
if not os.path.exists(bkDir):
os.mkdir(bkDir)
blist = os.listdir(bkDir)
r = []
@@ -684,7 +723,7 @@ def importDbExternal():
file = args['file']
name = args['name']
import_dir = mw.getRootDir() + '/backup/import/'
import_dir = mw.getBackupDir() + '/import/'
file_path = import_dir + file
if not os.path.exists(file_path):
@@ -734,10 +773,13 @@ def importDbExternal():
pwd + ' ' + name + ' < ' + import_sql
# print(mysql_cmd)
os.system(mysql_cmd)
rdata = mw.execShell(mysql_cmd)
if ext != 'sql':
os.remove(import_sql)
if rdata[1].lower().find('error') > -1:
return mw.returnJson(False, rdata[1])
return mw.returnJson(True, 'ok')
@@ -750,11 +792,11 @@ def importDbBackup():
file = args['file']
name = args['name']
file_path = mw.getRootDir() + '/backup/database/' + file
file_path_sql = mw.getRootDir() + '/backup/database/' + file.replace('.gz', '')
file_path = mw.getBackupDir() + '/database/' + file
file_path_sql = mw.getBackupDir() + '/database/' + file.replace('.gz', '')
if not os.path.exists(file_path_sql):
cmd = 'cd ' + mw.getRootDir() + '/backup/database && gzip -d ' + file
cmd = 'cd ' + mw.getBackupDir() + '/database && gzip -d ' + file
mw.execShell(cmd)
pwd = pSqliteDb('config').where('id=?', (1,)).getField('mysql_root')
@@ -763,7 +805,12 @@ def importDbBackup():
' ' + name + ' < ' + file_path_sql
# print(mysql_cmd)
os.system(mysql_cmd)
# os.system(mysql_cmd)
rdata = mw.execShell(mysql_cmd)
if rdata[1].lower().find('error') > -1:
return mw.returnJson(False, rdata[1])
return mw.returnJson(True, 'ok')
@@ -775,7 +822,7 @@ def deleteDbBackup():
path = args['path']
full_file = ""
bkDir = mw.getRootDir() + '/backup/database'
bkDir = getBackupDir()
full_file = bkDir + '/' + args['filename']
if path != "":
full_file = path + "/" + args['filename']
@@ -790,7 +837,7 @@ def getDbBackupList():
return data[1]
r = getDbBackupListFunc(args['name'])
bkDir = mw.getRootDir() + '/backup/database'
bkDir = getBackupDir()
rr = []
for x in range(0, len(r)):
p = bkDir + '/' + r[x]
@@ -812,8 +859,7 @@ def getDbBackupList():
def getDbBackupImportList():
bkImportDir = mw.getRootDir() + '/backup/import'
bkImportDir = mw.getBackupDir() + '/import'
if not os.path.exists(bkImportDir):
os.mkdir(bkImportDir)
@@ -1094,7 +1140,7 @@ def addDb():
dataAccess = args['dataAccess'].strip()
ps = args['ps'].strip()
reg = "^[\w\.-]+$"
reg = "^[\w-]+$"
if not re.match(reg, args['name']):
return mw.returnJson(False, '数据库名称不能带有特殊符号!')
checks = ['root', 'mysql', 'test', 'sys', 'panel_logs']
@@ -1762,7 +1808,7 @@ def addMasterRepSlaveUser(version=''):
# address = args['address'].strip()
# dataAccess = args['dataAccess'].strip()
reg = "^[\w\.-]+$"
reg = "^[\w-]+$"
if not re.match(reg, username):
return mw.returnJson(False, '用户名不能带有特殊符号!')
checks = ['root', 'mysql', 'test', 'sys', 'panel_logs']
@@ -1790,17 +1836,18 @@ def addMasterRepSlaveUser(version=''):
if isError != None:
return isError
sql = "FLUSH PRIVILEGES;"
pdb.execute(sql)
else:
sql = "GRANT REPLICATION SLAVE ON *.* TO '" + username + \
"'@'%' identified by '" + password + "';"
result = pdb.execute(sql)
result = pdb.execute('FLUSH PRIVILEGES;')
isError = isSqlError(result)
if isError != None:
return isError
sql_select = "grant select,lock tables,PROCESS on *.* to " + username + "@'%';"
pdb.execute(sql_select)
pdb.execute('FLUSH PRIVILEGES;')
addTime = time.strftime('%Y-%m-%d %X', time.localtime())
psdb.add('username,password,accept,ps,addtime',
(username, password, '%', '', addTime))
@@ -1937,6 +1984,123 @@ def getSlaveSSHList(version=''):
return mw.getJson(data)
def getSlaveSyncUserByIp(version=''):
args = getArgs()
data = checkArgs(args, ['ip'])
if not data[0]:
return data[1]
ip = args['ip']
conn = pSqliteDb('slave_sync_user')
data = conn.field('ip,port,user,pass,mode,cmd').where(
"ip=?", (ip,)).select()
return mw.returnJson(True, 'ok', data)
def addSlaveSyncUser(version=''):
import base64
args = getArgs()
data = checkArgs(args, ['ip'])
if not data[0]:
return data[1]
ip = args['ip']
if ip == "":
return mw.returnJson(True, 'ok')
data = checkArgs(args, ['port', 'user', 'pass', 'mode'])
if not data[0]:
return data[1]
cmd = args['cmd']
port = args['port']
user = args['user']
apass = args['pass']
mode = args['mode']
addTime = time.strftime('%Y-%m-%d %X', time.localtime())
conn = pSqliteDb('slave_sync_user')
data = conn.field('ip').where("ip=?", (ip,)).select()
if len(data) > 0:
res = conn.where("ip=?", (ip,)).save(
'port,user,pass,mode,cmd', (port, user, apass, mode, cmd))
else:
conn.add('ip,port,user,cmd,user,pass,mode,addtime',
(ip, port, user, cmd, user, apass, mode, addTime))
return mw.returnJson(True, '设置成功!')
def delSlaveSyncUser(version=''):
args = getArgs()
data = checkArgs(args, ['ip'])
if not data[0]:
return data[1]
ip = args['ip']
conn = pSqliteDb('slave_sync_user')
conn.where("ip=?", (ip,)).delete()
return mw.returnJson(True, '删除成功!')
def getSlaveSyncUserList(version=''):
args = getArgs()
data = checkArgs(args, ['page', 'page_size'])
if not data[0]:
return data[1]
page = int(args['page'])
page_size = int(args['page_size'])
conn = pSqliteDb('slave_sync_user')
limit = str((page - 1) * page_size) + ',' + str(page_size)
field = 'id,ip,port,user,pass,cmd,addtime'
clist = conn.field(field).limit(limit).order('id desc').select()
count = conn.count()
data = {}
_page = {}
_page['count'] = count
_page['p'] = page
_page['row'] = page_size
_page['tojs'] = args['tojs']
data['page'] = mw.getPage(_page)
data['data'] = clist
return mw.getJson(data)
def getSyncModeFile():
return getServerDir() + "/sync.mode"
def getSlaveSyncMode(version):
sync_mode = getSyncModeFile()
if os.path.exists(sync_mode):
mode = mw.readFile(sync_mode).strip()
return mw.returnJson(True, 'ok', mode)
return mw.returnJson(False, 'fail')
def setSlaveSyncMode(version):
args = getArgs()
data = checkArgs(args, ['mode'])
if not data[0]:
return data[1]
mode = args['mode']
sync_mode = getSyncModeFile()
if mode == 'none':
os.remove(sync_mode)
else:
mw.writeFile(sync_mode, mode)
return mw.returnJson(True, '设置成功', mode)
def getSlaveSSHByIp(version=''):
args = getArgs()
data = checkArgs(args, ['ip'])
@@ -2023,6 +2187,24 @@ def getSlaveList(version=''):
tmp['Master_Log_File'] = dlist[x]["Master_Log_File"]
tmp['Slave_IO_Running'] = dlist[x]["Slave_IO_Running"]
tmp['Slave_SQL_Running'] = dlist[x]["Slave_SQL_Running"]
tmp['Last_Error'] = dlist[x]["Last_Error"]
tmp['Last_IO_Error'] = dlist[x]["Last_IO_Error"]
tmp['Last_SQL_Error'] = dlist[x]["Last_SQL_Error"]
tmp['Slave_SQL_Running_State'] = dlist[x]["Slave_SQL_Running_State"]
tmp['Error'] = ''
if tmp['Last_Error'] != '':
tmp['Error'] = tmp['Last_Error']
if tmp['Last_IO_Error'] != '':
tmp['Error'] = tmp['Last_IO_Error']
if tmp['Last_SQL_Error'] != '':
tmp['Error'] = tmp['Last_SQL_Error']
if tmp['Error'] == '':
tmp['Error'] = tmp['Slave_SQL_Running_State']
ret.append(tmp)
data = {}
data['data'] = ret
@@ -2039,6 +2221,47 @@ def getSlaveSyncCmd(version=''):
def initSlaveStatus(version=''):
mode_file = getSyncModeFile()
if not os.path.exists(mode_file):
return mw.returnJson(False, '需要先设置同步配置')
mode = mw.readFile(mode_file)
if mode == 'ssh':
return initSlaveStatusSSH(version)
if mode == 'sync-user':
return initSlaveStatusSyncUser(version)
def initSlaveStatusSyncUser(version=''):
conn = pSqliteDb('slave_sync_user')
data = conn.field('ip,port,user,pass,mode,cmd').find()
if len(data) < 1:
return mw.returnJson(False, '需要先添加同步用户配置!')
# print(data)
db = pMysqlDb()
dlist = db.query('show slave status')
if len(dlist) > 0:
return mw.returnJson(False, '已经初始化好了zz...')
u = data
mode_name = 'classic'
if u['mode'] == '1':
mode_name = 'gtid'
local_mode = recognizeDbMode()
if local_mode != mode_name:
return mw.returnJson(False, '同步模式不一致!')
t = db.query(u['cmd'])
# print(t)
db.query("start slave user='{}' password='{}';".format(
u['user'], u['pass']))
return mw.returnJson(True, '初始化成功!')
def initSlaveStatusSSH(version=''):
db = pMysqlDb()
dlist = db.query('show slave status')
if len(dlist) > 0:
@@ -2107,6 +2330,41 @@ def initSlaveStatus(version=''):
def setSlaveStatus(version=''):
mode_file = getSyncModeFile()
if not os.path.exists(mode_file):
return mw.returnJson(False, '需要先设置同步配置')
mode = mw.readFile(mode_file)
if mode == 'ssh':
return setSlaveStatusSSH(version)
if mode == 'sync-user':
return setSlaveStatusSyncUser(version)
def setSlaveStatusSyncUser(version=''):
db = pMysqlDb()
dlist = db.query('show slave status')
if len(dlist) == 0:
return mw.returnJson(False, '需要手动添加同步账户或者执行初始化!')
if len(dlist) > 0 and (dlist[0]["Slave_IO_Running"] == 'Yes' or dlist[0]["Slave_SQL_Running"] == 'Yes'):
db.query('stop slave')
else:
ip = dlist[0]['Master_Host']
conn = pSqliteDb('slave_sync_user')
data = conn.field('ip,port,user,pass,mode,cmd').find()
if len(data) == 0:
return mw.returnJson(False, '没有数据无法重启!')
user = data['user']
apass = data['pass']
db.query("start slave")
# db.query("start slave user='{}' password='{}';".format(user, apass))
return mw.returnJson(True, '设置成功!')
def setSlaveStatusSSH(version=''):
db = pMysqlDb()
dlist = db.query('show slave status')
@@ -2175,6 +2433,68 @@ def writeDbSyncStatus(data):
def doFullSync(version=''):
mode_file = getSyncModeFile()
if not os.path.exists(mode_file):
return mw.returnJson(False, '需要先设置同步配置')
mode = mw.readFile(mode_file)
if mode == 'ssh':
return doFullSyncSSH(version)
if mode == 'sync-user':
return doFullSyncUser(version)
def doFullSyncUser(version=''):
args = getArgs()
data = checkArgs(args, ['db'])
if not data[0]:
return data[1]
sync_db = args['db']
db = pMysqlDb()
conn = pSqliteDb('slave_sync_user')
data = conn.field('ip,port,user,pass,mode,cmd').find()
user = data['user']
apass = data['pass']
port = data['port']
ip = data['ip']
bak_file = '/tmp/tmp.sql'
writeDbSyncStatus({'code': 0, 'msg': '开始同步...', 'progress': 0})
dmp_option = ''
mode = recognizeDbMode()
if mode == 'gtid':
dmp_option = ' --set-gtid-purged=off '
writeDbSyncStatus({'code': 1, 'msg': '远程导出数据...', 'progress': 20})
if not os.path.exists(bak_file):
dump_sql_data = getServerDir() + "/bin/mysqldump " + dmp_option + " --force --opt --default-character-set=utf8 --single-transaction -h" + ip + " -P" + \
port + " -u" + user + " -p" + apass + " " + sync_db + " > " + bak_file
mw.execShell(dump_sql_data)
writeDbSyncStatus({'code': 2, 'msg': '本地导入数据...', 'progress': 40})
if os.path.exists(bak_file):
pwd = pSqliteDb('config').where('id=?', (1,)).getField('mysql_root')
sock = getSocketFile()
my_import_cmd = getServerDir() + '/bin/mysql -S ' + sock + ' -uroot -p' + pwd + \
' ' + sync_db + ' < ' + bak_file
mw.execShell(my_import_cmd)
if version == '8.0':
db.query("start slave user='{}' password='{}';".format(user, apass))
else:
db.query("start slave")
writeDbSyncStatus({'code': 6, 'msg': '从库重启完成...', 'progress': 100})
os.system("rm -rf " + bak_file)
return True
def doFullSyncSSH(version=''):
args = getArgs()
data = checkArgs(args, ['db'])
@@ -2506,6 +2826,18 @@ if __name__ == "__main__":
print(delSlaveSSH(version))
elif func == 'update_slave_ssh':
print(updateSlaveSSH(version))
elif func == 'get_slave_sync_user_list':
print(getSlaveSyncUserList(version))
elif func == 'get_slave_sync_user_by_ip':
print(getSlaveSyncUserByIp(version))
elif func == 'add_slave_sync_user':
print(addSlaveSyncUser(version))
elif func == 'del_slave_sync_user':
print(delSlaveSyncUser(version))
elif func == 'get_slave_sync_mode':
print(getSlaveSyncMode(version))
elif func == 'set_slave_sync_mode':
print(setSlaveSyncMode(version))
elif func == 'init_slave_status':
print(initSlaveStatus(version))
elif func == 'set_slave_status':
+2 -1
View File
@@ -1,9 +1,10 @@
{
"hook":["database"],
"title":"MySQL[APT]",
"tip":"soft",
"name":"mysql-apt",
"type":"运行环境",
"ps":"[<b style='color:red;'>潜龙勿用</b>]一种关系数据库管理系统[debian,ubuntu](极速安装)",
"ps":"一种关系数据库管理系统[debian,ubuntu](极速安装)",
"todo_versions":["5.7","8.0"],
"versions":["5.7","8.0"],
"shell":"install.sh",
+275 -27
View File
@@ -509,19 +509,6 @@ function showHidePass(obj){
}
}
function copyPass(password){
var clipboard = new ClipboardJS('#bt_copys');
clipboard.on('success', function (e) {
layer.msg('复制成功',{icon:1,time:2000});
});
clipboard.on('error', function (e) {
layer.msg('复制失败,浏览器不兼容!',{icon:2,time:2000});
});
$("#bt_copys").attr('data-clipboard-text',password);
$("#bt_copys").click();
}
function checkSelect(){
setTimeout(function () {
var num = $('input[type="checkbox"].check:checked').length;
@@ -744,7 +731,7 @@ function addDatabase(type){
layer.close(index);
dbList();
}
},{icon: rdata.status ? 1 : 2},600);
},{icon: rdata.status ? 1 : 2}, 2000);
});
}
});
@@ -1558,13 +1545,9 @@ function getMasterRepSlaveUserCmd(username, db=''){
function delMasterRepSlaveUser(username){
myPost('del_master_rep_slave_user', {username:username}, function(data){
var rdata = $.parseJSON(data.data);
layer.msg(rdata.msg);
$('.layui-layer-close1').click();
setTimeout(function(){
showMsg(rdata.msg, function(){
getMasterRepSlaveList();
},1000);
},{icon: rdata.status ? 1 : 2},1000)
});
}
@@ -1834,8 +1817,23 @@ function addSlaveSSH(ip=''){
function delSlaveSSH(ip){
myPost('del_slave_ssh', {ip:ip}, function(rdata){
var rdata = $.parseJSON(rdata.data);
layer.msg(rdata.msg, {icon: rdata.status ? 1 : 2});
getSlaveSSHPage();
showMsg(rdata.msg,function(){
if (rdata.status){
getSlaveSSHPage();
}
},{icon: rdata.status ? 1 : 2}, 600);
});
}
function delSlaveSyncUser(ip){
myPost('del_slave_sync_user', {ip:ip}, function(rdata){
var rdata = $.parseJSON(rdata.data);
showMsg(rdata.msg,function(){
if (rdata.status){
getSlaveSyncUserPage();
}
},{icon: rdata.status ? 1 : 2}, 600);
});
}
@@ -1885,6 +1883,237 @@ function getSlaveSSHPage(page=1){
}
function addSlaveSyncUser(ip=''){
myPost('get_slave_sync_user_by_ip', {ip:ip}, function(rdata){
var rdata = $.parseJSON(rdata.data);
var ip = '127.0.0.1';
var port = "22";
var cmd = '';
var user = 'input_sync_user';
var pass = 'input_sync_pwd';
var mode = '0';
if (rdata.data.length>0){
ip = rdata.data[0]['ip'];
port = rdata.data[0]['port'];
cmd = rdata.data[0]['cmd'];
user = rdata.data[0]['user'];
pass = rdata.data[0]['pass'];
mode = rdata.data[0]['mode'];
}
var index = layer.open({
type: 1,
area: ['500px','470px'],
title: '同步账户',
closeBtn: 1,
shift: 5,
shadeClose: true,
btn:["确认","取消"],
content: "<form class='bt-form pd20'>\
<div class='line'><span class='tname'>IP</span><div class='info-r'><input name='ip' class='bt-input-text mr5' type='text' style='width:330px;' value='"+ip+"'></div></div>\
<div class='line'><span class='tname'>端口</span><div class='info-r'><input name='port' class='bt-input-text mr5' type='number' style='width:330px;' value='"+port+"'></div></div>\
<div class='line'><span class='tname'>同步账户</span><div class='info-r'><input name='user' class='bt-input-text mr5' type='text' style='width:330px;' value='"+user+"'></div></div>\
<div class='line'><span class='tname'>同步密码</span><div class='info-r'><input name='pass' class='bt-input-text mr5' type='text' style='width:330px;' value='"+pass+"'></div></div>\
<div class='line'>\
<span class='tname'>CMD[最好填好]</span>\
<div class='info-r'><textarea class='bt-input-text mr5' row='20' cols='30' name='cmd' style='width:330px;height:150px;'></textarea></div>\
</div>\
<input type='hidden' name='mode' value='"+mode+"' />\
</form>",
success:function(){
$('textarea[name="cmd"]').html(cmd);
$('textarea[name="cmd"]').change(function(){
var val = $(this).val();
var vlist = val.split(',');
var a = {};
for (var i in vlist) {
var tmp = toTrim(vlist[i]);
var tmp_a = tmp.split(" ");
var real_tmp = tmp_a[tmp_a.length-1];
var kv = real_tmp.split("=");
a[kv[0]] = kv[1].replace("'",'').replace("'",'');
}
$('input[name="ip"]').val(a['MASTER_HOST']);
$('input[name="port"]').val(a['MASTER_PORT']);
$('input[name="user"]').val(a['MASTER_USER']);
$('input[name="pass"]').val(a['MASTER_PASSWORD']);
console.log(a['MASTER_AUTO_POSITION'],typeof(a['MASTER_AUTO_POSITION']));
if (typeof(a['MASTER_AUTO_POSITION']) != 'undefined' ){
$('input[name="mode"]').val('1');
}
});
},
yes:function(index){
var ip = $('input[name="ip"]').val();
var port = $('input[name="port"]').val();
var user = $('input[name="user"]').val();
var pass = $('input[name="pass"]').val();
var cmd = $('textarea[name="cmd"]').val();
var mode = $('input[name="mode"]').val();
var data = {ip:ip,port:port,cmd:cmd,user:user,pass:pass,mode:mode};
myPost('add_slave_sync_user', data, function(ret_data){
layer.close(index);
var rdata = $.parseJSON(ret_data.data);
showMsg(rdata.msg,function(){
if (rdata.status){
getSlaveSyncUserPage();
}
},{icon: rdata.status ? 1 : 2},600);
});
}
});
});
}
function getSlaveSyncUserPage(page=1){
var _data = {};
_data['page'] = page;
_data['page_size'] = 5;
_data['tojs'] ='getSlaveSyncUserPage';
myPost('get_slave_sync_user_list', _data, function(data){
var layerId = null;
var rdata = [];
try {
rdata = $.parseJSON(data.data);
} catch(e) {
console.log(e);
}
var list = '';
var user_list = rdata['data'];
for (i in user_list) {
var ip = user_list[i]['ip'];
var port = user_list[i]['port'];
var user = user_list[i]['user'];
var apass = user_list[i]['pass'];
var cmd = '未设置';
if (user_list[i]['cmd']!=''){
cmd = '已设置';
}
list += '<tr><td>'+ip+'</td>\
<td>'+port+'</td>\
<td>'+user+'</td>\
<td>'+apass+'</td>\
<td>'+cmd+'</td>\
<td>\
<a class="btlink" onclick="addSlaveSyncUser(\''+ip+'\');">修改</a> | \
<a class="btlink" onclick="delSlaveSyncUser(\''+ip+'\');">删除</a>\
</td>\
</tr>';
}
$('.get-slave-ssh-list tbody').html(list);
$('.dataTables_paginate_4').html(rdata['page']);
});
}
function getSlaveCfg(){
myPost('get_slave_sync_mode', '', function(data){
var rdata = $.parseJSON(data.data);
var mode_none = 'success';
var mode_ssh = 'danger';
var mode_sync_user = 'danger';
if(rdata.status){
var mode_none = 'danger';
if (rdata.data == 'ssh'){
var mode_ssh = 'success';
var mode_sync_user = 'danger';
} else {
var mode_ssh = 'danger';
var mode_sync_user = 'success';
}
}
layerId = layer.open({
type: 1,
title: '同步配置',
area: ['400px','180px'],
content:"<div class='bt-form pd20 c6'>\
<p class='conf_p'>\
<span class='f14 c6 mr20'>当前从库同步模式</span>\
<b class='f14 c6 mr20'></b>\
<button class='btn btn-"+mode_none+" btn-xs slave-db-mode btn-none'>无</button>\
<button class='btn btn-"+mode_ssh+" btn-xs slave-db-mode btn-ssh'>SSH</button>\
<button class='btn btn-"+mode_sync_user+" btn-xs slave-db-mode btn-sync-user'>同步账户</button>\
</p>\
<hr />\
<p class='conf_p'>\
<span class='f14 c6 mr20'>配置设置</span>\
<b class='f14 c6 mr20'></b>\
<button class='btn btn-success btn-xs btn-slave-ssh'>SSH</button>\
<button class='btn btn-success btn-xs btn-slave-user'>同步账户</button>\
</p>\
</div>",
success:function(){
$('.btn-slave-ssh').click(function(){
getSlaveSSHList();
});
$('.btn-slave-user').click(function(){
getSlaveUserList();
});
$('.slave-db-mode').click(function(){
var _this = this;
var mode = 'none';
if ($(this).hasClass('btn-ssh')){
mode = 'ssh';
}
if ($(this).hasClass('btn-sync-user')){
mode = 'sync-user';
}
myPost('set_slave_sync_mode', {mode:mode}, function(data){
var rdata = $.parseJSON(data.data);
showMsg(rdata.msg,function(){
$('.slave-db-mode').remove('btn-success').addClass('btn-danger');
$(_this).removeClass('btn-danger').addClass('btn-success');
},{icon:rdata.status?1:2},2000);
});
});
}
});
});
}
function getSlaveUserList(){
var page = '<div class="dataTables_paginate_4 dataTables_paginate paging_bootstrap page" style="margin-top:0px;"></div>';
page += '<div class="table_toolbar" style="left:0px;"><span class="sync btn btn-default btn-sm" onclick="addSlaveSyncUser()" title="">添加同步账户</span></div>';
layerId = layer.open({
type: 1,
title: '同步账户列表',
area: '500px',
content:"<div class='bt-form pd20 c6'>\
<div class='divtable mtb10'>\
<div><table class='table table-hover get-slave-ssh-list'>\
<thead><tr><th>IP</th><th>PORT</th><th>同步账户</th><th>同步密码</th><th>CMD</th><th>操作</th></tr></thead>\
<tbody></tbody>\
</table></div>\
"+page +"\
</div>\
</div>",
success:function(){
getSlaveSyncUserPage(1);
}
});
}
function getSlaveSSHList(page=1){
var page = '<div class="dataTables_paginate_4 dataTables_paginate paging_bootstrap page" style="margin-top:0px;"></div>';
@@ -2004,7 +2233,7 @@ function masterOrSlaveConf(version=''){
for(i in rdata.data){
var v = rdata.data[i];
var status = "异常";
var status = "<a class='btlink db_error'>异常</>";
if (v['Slave_SQL_Running'] == 'Yes' && v['Slave_IO_Running'] == 'Yes'){
status = "正常";
}
@@ -2046,6 +2275,26 @@ function masterOrSlaveConf(version=''){
// <span class="sync btn btn-default btn-sm" onclick="getMasterRepSlaveList()" title="">添加</span>\
// </div>
$(".table_slave_status_list").html(con);
$('.db_error').click(function(){
layer.open({
type: 1,
title: '同步异常信息',
area: '500px',
content:"<form class='bt-form pd20 pb70'>\
<div class='line'>"+v['Error']+"</div>\
<div class='bt-form-submit-btn'>\
<button type='button' class='btn btn-success btn-sm btn-title class-copy-db-err'>复制</button>\
</div>\
</form>",
success:function(){
copyText(v['Error']);
$('.class-copy-db-err').click(function(){
copyText(v['Error']);
});
}
});
});
});
}
@@ -2092,7 +2341,6 @@ function masterOrSlaveConf(version=''){
});
}
function getMasterStatus(){
myPost('get_master_status', '', function(rdata){
@@ -2123,7 +2371,7 @@ function masterOrSlaveConf(version=''){
<p class="conf_p">\
<span class="f14 c6 mr20">Slave[从]配置</span><span class="f14 c6 mr20"></span>\
<button class="btn '+(!rdata.slave_status ? 'btn-danger' : 'btn-success')+' btn-xs btn-slave">'+(!rdata.slave_status ? '未启动' : '已启动') +'</button>\
<button class="btn btn-success btn-xs" onclick="getSlaveSSHList()" >[主]SSH配置</button>\
<button class="btn btn-success btn-xs" onclick="getSlaveCfg()" >同步配置</button>\
<button class="btn btn-success btn-xs" onclick="initSlaveStatus()" >初始化</button>\
</p>\
<hr/>\
@@ -2197,10 +2445,10 @@ function masterOrSlaveConf(version=''){
getMasterDbList();
}
if (rdata.slave_status){
// if (rdata.slave_status){
getAsyncMasterDbList();
getAsyncDataList()
}
// }
});
}
getMasterStatus();
+1 -1
View File
@@ -44,7 +44,7 @@ class backupTools:
"----------------------------------------------------------------------------")
return
backup_path = mw.getRootDir() + '/backup/database'
backup_path = mw.getRootDir() + '/backup/database/mysql-apt'
if not os.path.exists(backup_path):
mw.execShell("mkdir -p " + backup_path)
+5 -4
View File
@@ -37,11 +37,12 @@ max_connect_errors = 100
open_files_limit = 2560
max_allowed_packet = 128M
#skip-external-locking
#skip-grant-tables
#loose-skip-innodb
skip_name_resolve = 1
skip-ssl
#skip-networking
#skip-name-resolve
#skip-external-locking
#loose-skip-innodb
#skip-grant-tables
log-bin=mysql-bin
binlog_format=mixed
+13
View File
@@ -40,4 +40,17 @@ CREATE TABLE IF NOT EXISTS `slave_id_rsa` (
`addtime` TEXT
);
-- 从库配置主库的[user]
-- drop table `slave_user`;
CREATE TABLE IF NOT EXISTS `slave_sync_user` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`ip` TEXT,
`port` TEXT,
`user` TEXT,
`pass` TEXT,
`mode` TEXT,
`cmd` TEXT,
`addtime` TEXT
);
+1
View File
@@ -13,6 +13,7 @@
<p onclick="myLogs();">日志</p>
<p onclick="pluginLogs('mysql-yum',$('.plugin_version').attr('version'),'show_log');">慢日志</p>
<p onclick="dbList()">管理列表</p>
<p onclick="masterOrSlaveConf($('.plugin_version').attr('version'))">主从配置</p>
</div>
<div class="bt-w-con pd15">
<div class="soft-man-con"></div>
+350 -37
View File
@@ -65,6 +65,13 @@ def checkArgs(data, ck=[]):
return (True, mw.returnJson(True, 'ok'))
def getBackupDir():
bk_path = mw.getBackupDir() + "/database/mysql-yum"
if not os.path.isdir(bk_path):
mw.execShell("mkdir -p {}".format(bk_path))
return bk_path
def getConf():
path = getServerDir() + '/etc/my.cnf'
return path
@@ -122,20 +129,27 @@ def contentReplace(content):
def pSqliteDb(dbname='databases'):
file = getServerDir() + '/mysql.db'
name = 'mysql'
if not os.path.exists(file):
import_sql = mw.readFile(getPluginDir() + '/conf/mysql.sql')
md5_sql = mw.md5(import_sql)
import_sign = False
save_md5_file = getServerDir() + '/import_sql.md5'
if os.path.exists(save_md5_file):
save_md5_sql = mw.readFile(save_md5_file)
if save_md5_sql != md5_sql:
import_sign = True
mw.writeFile(save_md5_file, md5_sql)
else:
mw.writeFile(save_md5_file, md5_sql)
if not os.path.exists(file) or import_sql:
conn = mw.M(dbname).dbPos(getServerDir(), name)
csql = mw.readFile(getPluginDir() + '/conf/mysql.sql')
csql_list = csql.split(';')
csql_list = import_sql.split(';')
for index in range(len(csql_list)):
conn.execute(csql_list[index], ())
else:
# 现有run
# conn = mw.M(dbname).dbPos(getServerDir(), name)
# csql = mw.readFile(getPluginDir() + '/conf/mysql.sql')
# csql_list = csql.split(';')
# for index in range(len(csql_list)):
# conn.execute(csql_list[index], ())
conn = mw.M(dbname).dbPos(getServerDir(), name)
conn = mw.M(dbname).dbPos(getServerDir(), name)
return conn
@@ -154,9 +168,16 @@ def pMysqlDb():
def initDreplace(version=''):
my_dir = getServerDir() + '/etc'
if not os.path.exists(my_dir):
os.mkdir(my_dir)
conf_dir = getServerDir() + '/etc'
mode_dir = conf_dir + '/mode'
conf_list = [
conf_dir,
mode_dir,
]
for conf in conf_list:
if not os.path.exists(conf):
os.mkdir(conf)
tmp_dir = getServerDir() + '/tmp'
if not os.path.exists(tmp_dir):
@@ -164,12 +185,26 @@ def initDreplace(version=''):
mw.execShell("chown -R mysql:mysql " + tmp_dir)
mw.execShell("chmod 750 " + tmp_dir)
mysql_conf = my_dir + '/my.cnf'
if not os.path.exists(mysql_conf):
mysql_conf_tpl = getPluginDir() + '/conf/my' + version + '.cnf'
content = mw.readFile(mysql_conf_tpl)
my_conf = conf_dir + '/my.cnf'
if not os.path.exists(my_conf):
tpl = getPluginDir() + '/conf/my' + version + '.cnf'
content = mw.readFile(tpl)
content = contentReplace(content)
mw.writeFile(mysql_conf, content)
mw.writeFile(my_conf, content)
classic_conf = mode_dir + '/classic.cnf'
if not os.path.exists(classic_conf):
tpl = getPluginDir() + '/conf/classic.cnf'
content = mw.readFile(tpl)
content = contentReplace(content)
mw.writeFile(classic_conf, content)
gtid_conf = mode_dir + '/gtid.cnf'
if not os.path.exists(gtid_conf):
tpl = getPluginDir() + '/conf/gtid.cnf'
content = mw.readFile(tpl)
content = contentReplace(content)
mw.writeFile(gtid_conf, content)
# systemd
systemDir = mw.systemdCfgDir()
@@ -226,6 +261,13 @@ def binLog():
return mw.returnJson(True, '设置成功!')
def cleanBinLog():
db = pMysqlDb()
cleanTime = time.strftime('%Y-%m-%d %H:%i:%s', time.localtime())
db.execute("PURGE MASTER LOGS BEFORE '" + cleanTime + "';")
return mw.returnJson(True, '清理BINLOG成功!')
def setSkipGrantTables(v):
'''
设置是否密码验证
@@ -629,7 +671,7 @@ def __createUser(dbname, username, password, address):
def getDbBackupListFunc(dbname=''):
bkDir = mw.getRootDir() + '/backup/database'
bkDir = getBackupDir()
blist = os.listdir(bkDir)
r = []
@@ -663,7 +705,7 @@ def importDbExternal():
file = args['file']
name = args['name']
import_dir = mw.getRootDir() + '/backup/import/'
import_dir = mw.getBackupDir() + '/import/'
file_path = import_dir + file
if not os.path.exists(file_path):
@@ -712,11 +754,13 @@ def importDbExternal():
mysql_cmd = getServerDir() + '/bin/usr/bin/mysql -S ' + sock + ' -uroot -p' + \
pwd + ' ' + name + ' < ' + import_sql
# print(mysql_cmd)
os.system(mysql_cmd)
rdata = mw.execShell(mysql_cmd)
if ext != 'sql':
os.remove(import_sql)
if rdata[1].lower().find('error') > -1:
return mw.returnJson(False, rdata[1])
return mw.returnJson(True, 'ok')
@@ -729,11 +773,11 @@ def importDbBackup():
file = args['file']
name = args['name']
file_path = mw.getRootDir() + '/backup/database/' + file
file_path_sql = mw.getRootDir() + '/backup/database/' + file.replace('.gz', '')
file_path = getBackupDir() + '/' + file
file_path_sql = getBackupDir() + '/' + file.replace('.gz', '')
if not os.path.exists(file_path_sql):
cmd = 'cd ' + mw.getRootDir() + '/backup/database && gzip -d ' + file
cmd = 'cd ' + mw.getBackupDir() + '/database && gzip -d ' + file
mw.execShell(cmd)
pwd = pSqliteDb('config').where('id=?', (1,)).getField('mysql_root')
@@ -741,8 +785,10 @@ def importDbBackup():
mysql_cmd = getServerDir() + '/bin/usr/bin/mysql -S ' + sock + ' -uroot -p' + pwd + \
' ' + name + ' < ' + file_path_sql
# print(mysql_cmd)
os.system(mysql_cmd)
rdata = mw.execShell(mysql_cmd)
if rdata[1].lower().find('error') > -1:
return mw.returnJson(False, rdata[1])
return mw.returnJson(True, 'ok')
@@ -754,7 +800,7 @@ def deleteDbBackup():
path = args['path']
full_file = ""
bkDir = mw.getRootDir() + '/backup/database'
bkDir = getBackupDir()
full_file = bkDir + '/' + args['filename']
if path != "":
full_file = path + "/" + args['filename']
@@ -769,7 +815,7 @@ def getDbBackupList():
return data[1]
r = getDbBackupListFunc(args['name'])
bkDir = mw.getRootDir() + '/backup/database'
bkDir = getBackupDir()
rr = []
for x in range(0, len(r)):
p = bkDir + '/' + r[x]
@@ -791,8 +837,7 @@ def getDbBackupList():
def getDbBackupImportList():
bkImportDir = mw.getRootDir() + '/backup/import'
bkImportDir = mw.getBackupDir() + '/import'
if not os.path.exists(bkImportDir):
os.mkdir(bkImportDir)
@@ -1073,7 +1118,7 @@ def addDb():
dataAccess = args['dataAccess'].strip()
ps = args['ps'].strip()
reg = "^[\w\.-]+$"
reg = "^[\w-]+$"
if not re.match(reg, args['name']):
return mw.returnJson(False, '数据库名称不能带有特殊符号!')
checks = ['root', 'mysql', 'test', 'sys', 'panel_logs']
@@ -1741,7 +1786,7 @@ def addMasterRepSlaveUser(version=''):
# address = args['address'].strip()
# dataAccess = args['dataAccess'].strip()
reg = "^[\w\.-]+$"
reg = "^[\w-]+$"
if not re.match(reg, username):
return mw.returnJson(False, '用户名不能带有特殊符号!')
checks = ['root', 'mysql', 'test', 'sys', 'panel_logs']
@@ -1769,17 +1814,18 @@ def addMasterRepSlaveUser(version=''):
if isError != None:
return isError
sql = "FLUSH PRIVILEGES;"
pdb.execute(sql)
else:
sql = "GRANT REPLICATION SLAVE ON *.* TO '" + username + \
"'@'%' identified by '" + password + "';"
result = pdb.execute(sql)
result = pdb.execute('FLUSH PRIVILEGES;')
isError = isSqlError(result)
if isError != None:
return isError
sql_select = "grant select,lock tables,PROCESS on *.* to " + username + "@'%';"
pdb.execute(sql_select)
pdb.execute('FLUSH PRIVILEGES;')
addTime = time.strftime('%Y-%m-%d %X', time.localtime())
psdb.add('username,password,accept,ps,addtime',
(username, password, '%', '', addTime))
@@ -1916,6 +1962,123 @@ def getSlaveSSHList(version=''):
return mw.getJson(data)
def getSlaveSyncUserByIp(version=''):
args = getArgs()
data = checkArgs(args, ['ip'])
if not data[0]:
return data[1]
ip = args['ip']
conn = pSqliteDb('slave_sync_user')
data = conn.field('ip,port,user,pass,mode,cmd').where(
"ip=?", (ip,)).select()
return mw.returnJson(True, 'ok', data)
def addSlaveSyncUser(version=''):
import base64
args = getArgs()
data = checkArgs(args, ['ip'])
if not data[0]:
return data[1]
ip = args['ip']
if ip == "":
return mw.returnJson(True, 'ok')
data = checkArgs(args, ['port', 'user', 'pass', 'mode'])
if not data[0]:
return data[1]
cmd = args['cmd']
port = args['port']
user = args['user']
apass = args['pass']
mode = args['mode']
addTime = time.strftime('%Y-%m-%d %X', time.localtime())
conn = pSqliteDb('slave_sync_user')
data = conn.field('ip').where("ip=?", (ip,)).select()
if len(data) > 0:
res = conn.where("ip=?", (ip,)).save(
'port,user,pass,mode,cmd', (port, user, apass, mode, cmd))
else:
conn.add('ip,port,user,cmd,user,pass,mode,addtime',
(ip, port, user, cmd, user, apass, mode, addTime))
return mw.returnJson(True, '设置成功!')
def delSlaveSyncUser(version=''):
args = getArgs()
data = checkArgs(args, ['ip'])
if not data[0]:
return data[1]
ip = args['ip']
conn = pSqliteDb('slave_sync_user')
conn.where("ip=?", (ip,)).delete()
return mw.returnJson(True, '删除成功!')
def getSlaveSyncUserList(version=''):
args = getArgs()
data = checkArgs(args, ['page', 'page_size'])
if not data[0]:
return data[1]
page = int(args['page'])
page_size = int(args['page_size'])
conn = pSqliteDb('slave_sync_user')
limit = str((page - 1) * page_size) + ',' + str(page_size)
field = 'id,ip,port,user,pass,cmd,addtime'
clist = conn.field(field).limit(limit).order('id desc').select()
count = conn.count()
data = {}
_page = {}
_page['count'] = count
_page['p'] = page
_page['row'] = page_size
_page['tojs'] = args['tojs']
data['page'] = mw.getPage(_page)
data['data'] = clist
return mw.getJson(data)
def getSyncModeFile():
return getServerDir() + "/sync.mode"
def getSlaveSyncMode(version):
sync_mode = getSyncModeFile()
if os.path.exists(sync_mode):
mode = mw.readFile(sync_mode).strip()
return mw.returnJson(True, 'ok', mode)
return mw.returnJson(False, 'fail')
def setSlaveSyncMode(version):
args = getArgs()
data = checkArgs(args, ['mode'])
if not data[0]:
return data[1]
mode = args['mode']
sync_mode = getSyncModeFile()
if mode == 'none':
os.remove(sync_mode)
else:
mw.writeFile(sync_mode, mode)
return mw.returnJson(True, '设置成功', mode)
def getSlaveSSHByIp(version=''):
args = getArgs()
data = checkArgs(args, ['ip'])
@@ -2018,6 +2181,47 @@ def getSlaveSyncCmd(version=''):
def initSlaveStatus(version=''):
mode_file = getSyncModeFile()
if not os.path.exists(mode_file):
return mw.returnJson(False, '需要先设置同步配置')
mode = mw.readFile(mode_file)
if mode == 'ssh':
return initSlaveStatusSSH(version)
if mode == 'sync-user':
return initSlaveStatusSyncUser(version)
def initSlaveStatusSyncUser(version=''):
conn = pSqliteDb('slave_sync_user')
data = conn.field('ip,port,user,pass,mode,cmd').find()
if len(data) < 1:
return mw.returnJson(False, '需要先添加同步用户配置!')
# print(data)
db = pMysqlDb()
dlist = db.query('show slave status')
if len(dlist) > 0:
return mw.returnJson(False, '已经初始化好了zz...')
u = data
mode_name = 'classic'
if u['mode'] == '1':
mode_name = 'gtid'
local_mode = recognizeDbMode()
if local_mode != mode_name:
return mw.returnJson(False, '同步模式不一致!')
t = db.query(u['cmd'])
# print(t)
db.query("start slave user='{}' password='{}';".format(
u['user'], u['pass']))
return mw.returnJson(True, '初始化成功!')
def initSlaveStatusSSH(version=''):
db = pMysqlDb()
dlist = db.query('show slave status')
if len(dlist) > 0:
@@ -2086,6 +2290,41 @@ def initSlaveStatus(version=''):
def setSlaveStatus(version=''):
mode_file = getSyncModeFile()
if not os.path.exists(mode_file):
return mw.returnJson(False, '需要先设置同步配置')
mode = mw.readFile(mode_file)
if mode == 'ssh':
return setSlaveStatusSSH(version)
if mode == 'sync-user':
return setSlaveStatusSyncUser(version)
def setSlaveStatusSyncUser(version=''):
db = pMysqlDb()
dlist = db.query('show slave status')
if len(dlist) == 0:
return mw.returnJson(False, '需要手动添加同步账户或者执行初始化!')
if len(dlist) > 0 and (dlist[0]["Slave_IO_Running"] == 'Yes' or dlist[0]["Slave_SQL_Running"] == 'Yes'):
db.query('stop slave')
else:
ip = dlist[0]['Master_Host']
conn = pSqliteDb('slave_sync_user')
data = conn.field('ip,port,user,pass,mode,cmd').find()
if len(data) == 0:
return mw.returnJson(False, '没有数据无法重启!')
user = data['user']
apass = data['pass']
db.query("start slave")
# db.query("start slave user='{}' password='{}';".format(user, apass))
return mw.returnJson(True, '设置成功!')
def setSlaveStatusSSH(version=''):
db = pMysqlDb()
dlist = db.query('show slave status')
@@ -2154,6 +2393,68 @@ def writeDbSyncStatus(data):
def doFullSync(version=''):
mode_file = getSyncModeFile()
if not os.path.exists(mode_file):
return mw.returnJson(False, '需要先设置同步配置')
mode = mw.readFile(mode_file)
if mode == 'ssh':
return doFullSyncSSH(version)
if mode == 'sync-user':
return doFullSyncUser(version)
def doFullSyncUser(version=''):
args = getArgs()
data = checkArgs(args, ['db'])
if not data[0]:
return data[1]
sync_db = args['db']
db = pMysqlDb()
conn = pSqliteDb('slave_sync_user')
data = conn.field('ip,port,user,pass,mode,cmd').find()
user = data['user']
apass = data['pass']
port = data['port']
ip = data['ip']
bak_file = '/tmp/tmp.sql'
writeDbSyncStatus({'code': 0, 'msg': '开始同步...', 'progress': 0})
dmp_option = ''
mode = recognizeDbMode()
if mode == 'gtid':
dmp_option = ' --set-gtid-purged=off '
writeDbSyncStatus({'code': 1, 'msg': '远程导出数据...', 'progress': 20})
if not os.path.exists(bak_file):
dump_sql_data = getServerDir() + "/bin/mysqldump " + dmp_option + " --force --opt --default-character-set=utf8 --single-transaction -h" + ip + " -P" + \
port + " -u" + user + " -p" + apass + " " + sync_db + " > " + bak_file
mw.execShell(dump_sql_data)
writeDbSyncStatus({'code': 2, 'msg': '本地导入数据...', 'progress': 40})
if os.path.exists(bak_file):
pwd = pSqliteDb('config').where('id=?', (1,)).getField('mysql_root')
sock = getSocketFile()
my_import_cmd = getServerDir() + '/bin/mysql -S ' + sock + ' -uroot -p' + pwd + \
' ' + sync_db + ' < ' + bak_file
mw.execShell(my_import_cmd)
if version == '8.0':
db.query("start slave user='{}' password='{}';".format(user, apass))
else:
db.query("start slave")
writeDbSyncStatus({'code': 6, 'msg': '从库重启完成...', 'progress': 100})
os.system("rm -rf " + bak_file)
return True
def doFullSyncSSH(version=''):
args = getArgs()
data = checkArgs(args, ['db'])
@@ -2479,6 +2780,18 @@ if __name__ == "__main__":
print(delSlaveSSH(version))
elif func == 'update_slave_ssh':
print(updateSlaveSSH(version))
elif func == 'get_slave_sync_user_list':
print(getSlaveSyncUserList(version))
elif func == 'get_slave_sync_user_by_ip':
print(getSlaveSyncUserByIp(version))
elif func == 'add_slave_sync_user':
print(addSlaveSyncUser(version))
elif func == 'del_slave_sync_user':
print(delSlaveSyncUser(version))
elif func == 'get_slave_sync_mode':
print(getSlaveSyncMode(version))
elif func == 'set_slave_sync_mode':
print(setSlaveSyncMode(version))
elif func == 'init_slave_status':
print(initSlaveStatus(version))
elif func == 'set_slave_status':
+2 -1
View File
@@ -1,9 +1,10 @@
{
"hook":["database"],
"title":"MySQL[YUM]",
"tip":"soft",
"name":"mysql-yum",
"type":"运行环境",
"ps":"[<b style='color:red;'>潜龙勿用</b>]一种关系数据库管理系统[centos](极速安装)",
"ps":"一种关系数据库管理系统[centos](极速安装)",
"todo_versions":["5.7","8.0"],
"versions":["5.7","8.0"],
"shell":"install.sh",
+278 -30
View File
@@ -265,7 +265,7 @@ function myPerfOpt() {
}
function reBootMySqld(){
pluginOpService('mysql-apt','restart','');
pluginOpService('mysql-yum','restart','');
}
@@ -509,19 +509,6 @@ function showHidePass(obj){
}
}
function copyPass(password){
var clipboard = new ClipboardJS('#bt_copys');
clipboard.on('success', function (e) {
layer.msg('复制成功',{icon:1,time:2000});
});
clipboard.on('error', function (e) {
layer.msg('复制失败,浏览器不兼容!',{icon:2,time:2000});
});
$("#bt_copys").attr('data-clipboard-text',password);
$("#bt_copys").click();
}
function checkSelect(){
setTimeout(function () {
var num = $('input[type="checkbox"].check:checked').length;
@@ -744,7 +731,7 @@ function addDatabase(type){
layer.close(index);
dbList();
}
},{icon: rdata.status ? 1 : 2},600);
},{icon: rdata.status ? 1 : 2}, 2000);
});
}
});
@@ -831,7 +818,7 @@ function openPhpmyadmin(name,username,password){
data = syncPost('/plugins/run',{'name':'phpmyadmin','func':'get_cfg'});
var rdata = $.parseJSON(data.data);
if (rdata.choose != 'mysql-apt'){
if (rdata.choose != 'mysql-yum'){
layer.msg('当前为['+rdata.choose+']模式,若要使用请切换模式.',{icon:2,shade: [0.3, '#000']});
return;
}
@@ -1220,7 +1207,7 @@ function myLogs(){
var limitCon = '<p class="conf_p">\
<span class="f14 c6 mr20">二进制日志 </span><span class="f14 c6 mr20">' + toSize(rdata.msg) + '</span>\
'+line_status+'\
<p class="f14 c6 mtb10" style="border-top:#ddd 1px solid; padding:10px 0">错误日志<button class="btn btn-default btn-clear btn-xs" style="float:right;">清理日志</button></p>\
<p class="f14 c6 mtb10" style="border-top:#ddd 1px solid; padding:10px 0">错误日志<button class="btn btn-default btn-clear btn-xs" style="float:right;" >清理日志</button></p>\
<textarea readonly style="margin: 0px;width: 100%;height: 440px;background-color: #333;color:#fff; padding:0 5px" id="error_log"></textarea>\
</p>';
$(".soft-man-con").html(limitCon);
@@ -1558,13 +1545,9 @@ function getMasterRepSlaveUserCmd(username, db=''){
function delMasterRepSlaveUser(username){
myPost('del_master_rep_slave_user', {username:username}, function(data){
var rdata = $.parseJSON(data.data);
layer.msg(rdata.msg);
$('.layui-layer-close1').click();
setTimeout(function(){
showMsg(rdata.msg, function(){
getMasterRepSlaveList();
},1000);
},{icon: rdata.status ? 1 : 2},1000)
});
}
@@ -1834,8 +1817,23 @@ function addSlaveSSH(ip=''){
function delSlaveSSH(ip){
myPost('del_slave_ssh', {ip:ip}, function(rdata){
var rdata = $.parseJSON(rdata.data);
layer.msg(rdata.msg, {icon: rdata.status ? 1 : 2});
getSlaveSSHPage();
showMsg(rdata.msg,function(){
if (rdata.status){
getSlaveSSHPage();
}
},{icon: rdata.status ? 1 : 2}, 600);
});
}
function delSlaveSyncUser(ip){
myPost('del_slave_sync_user', {ip:ip}, function(rdata){
var rdata = $.parseJSON(rdata.data);
showMsg(rdata.msg,function(){
if (rdata.status){
getSlaveSyncUserPage();
}
},{icon: rdata.status ? 1 : 2}, 600);
});
}
@@ -1885,6 +1883,237 @@ function getSlaveSSHPage(page=1){
}
function addSlaveSyncUser(ip=''){
myPost('get_slave_sync_user_by_ip', {ip:ip}, function(rdata){
var rdata = $.parseJSON(rdata.data);
var ip = '127.0.0.1';
var port = "22";
var cmd = '';
var user = 'input_sync_user';
var pass = 'input_sync_pwd';
var mode = '0';
if (rdata.data.length>0){
ip = rdata.data[0]['ip'];
port = rdata.data[0]['port'];
cmd = rdata.data[0]['cmd'];
user = rdata.data[0]['user'];
pass = rdata.data[0]['pass'];
mode = rdata.data[0]['mode'];
}
var index = layer.open({
type: 1,
area: ['500px','470px'],
title: '同步账户',
closeBtn: 1,
shift: 5,
shadeClose: true,
btn:["确认","取消"],
content: "<form class='bt-form pd20'>\
<div class='line'><span class='tname'>IP</span><div class='info-r'><input name='ip' class='bt-input-text mr5' type='text' style='width:330px;' value='"+ip+"'></div></div>\
<div class='line'><span class='tname'>端口</span><div class='info-r'><input name='port' class='bt-input-text mr5' type='number' style='width:330px;' value='"+port+"'></div></div>\
<div class='line'><span class='tname'>同步账户</span><div class='info-r'><input name='user' class='bt-input-text mr5' type='text' style='width:330px;' value='"+user+"'></div></div>\
<div class='line'><span class='tname'>同步密码</span><div class='info-r'><input name='pass' class='bt-input-text mr5' type='text' style='width:330px;' value='"+pass+"'></div></div>\
<div class='line'>\
<span class='tname'>CMD[最好填好]</span>\
<div class='info-r'><textarea class='bt-input-text mr5' row='20' cols='30' name='cmd' style='width:330px;height:150px;'></textarea></div>\
</div>\
<input type='hidden' name='mode' value='"+mode+"' />\
</form>",
success:function(){
$('textarea[name="cmd"]').html(cmd);
$('textarea[name="cmd"]').change(function(){
var val = $(this).val();
var vlist = val.split(',');
var a = {};
for (var i in vlist) {
var tmp = toTrim(vlist[i]);
var tmp_a = tmp.split(" ");
var real_tmp = tmp_a[tmp_a.length-1];
var kv = real_tmp.split("=");
a[kv[0]] = kv[1].replace("'",'').replace("'",'');
}
$('input[name="ip"]').val(a['MASTER_HOST']);
$('input[name="port"]').val(a['MASTER_PORT']);
$('input[name="user"]').val(a['MASTER_USER']);
$('input[name="pass"]').val(a['MASTER_PASSWORD']);
console.log(a['MASTER_AUTO_POSITION'],typeof(a['MASTER_AUTO_POSITION']));
if (typeof(a['MASTER_AUTO_POSITION']) != 'undefined' ){
$('input[name="mode"]').val('1');
}
});
},
yes:function(index){
var ip = $('input[name="ip"]').val();
var port = $('input[name="port"]').val();
var user = $('input[name="user"]').val();
var pass = $('input[name="pass"]').val();
var cmd = $('textarea[name="cmd"]').val();
var mode = $('input[name="mode"]').val();
var data = {ip:ip,port:port,cmd:cmd,user:user,pass:pass,mode:mode};
myPost('add_slave_sync_user', data, function(ret_data){
layer.close(index);
var rdata = $.parseJSON(ret_data.data);
showMsg(rdata.msg,function(){
if (rdata.status){
getSlaveSyncUserPage();
}
},{icon: rdata.status ? 1 : 2},600);
});
}
});
});
}
function getSlaveSyncUserPage(page=1){
var _data = {};
_data['page'] = page;
_data['page_size'] = 5;
_data['tojs'] ='getSlaveSyncUserPage';
myPost('get_slave_sync_user_list', _data, function(data){
var layerId = null;
var rdata = [];
try {
rdata = $.parseJSON(data.data);
} catch(e) {
console.log(e);
}
var list = '';
var user_list = rdata['data'];
for (i in user_list) {
var ip = user_list[i]['ip'];
var port = user_list[i]['port'];
var user = user_list[i]['user'];
var apass = user_list[i]['pass'];
var cmd = '未设置';
if (user_list[i]['cmd']!=''){
cmd = '已设置';
}
list += '<tr><td>'+ip+'</td>\
<td>'+port+'</td>\
<td>'+user+'</td>\
<td>'+apass+'</td>\
<td>'+cmd+'</td>\
<td>\
<a class="btlink" onclick="addSlaveSyncUser(\''+ip+'\');">修改</a> | \
<a class="btlink" onclick="delSlaveSyncUser(\''+ip+'\');">删除</a>\
</td>\
</tr>';
}
$('.get-slave-ssh-list tbody').html(list);
$('.dataTables_paginate_4').html(rdata['page']);
});
}
function getSlaveCfg(){
myPost('get_slave_sync_mode', '', function(data){
var rdata = $.parseJSON(data.data);
var mode_none = 'success';
var mode_ssh = 'danger';
var mode_sync_user = 'danger';
if(rdata.status){
var mode_none = 'danger';
if (rdata.data == 'ssh'){
var mode_ssh = 'success';
var mode_sync_user = 'danger';
} else {
var mode_ssh = 'danger';
var mode_sync_user = 'success';
}
}
layerId = layer.open({
type: 1,
title: '同步配置',
area: ['400px','180px'],
content:"<div class='bt-form pd20 c6'>\
<p class='conf_p'>\
<span class='f14 c6 mr20'>当前从库同步模式</span>\
<b class='f14 c6 mr20'></b>\
<button class='btn btn-"+mode_none+" btn-xs slave-db-mode btn-none'>无</button>\
<button class='btn btn-"+mode_ssh+" btn-xs slave-db-mode btn-ssh'>SSH</button>\
<button class='btn btn-"+mode_sync_user+" btn-xs slave-db-mode btn-sync-user'>同步账户</button>\
</p>\
<hr />\
<p class='conf_p'>\
<span class='f14 c6 mr20'>配置设置</span>\
<b class='f14 c6 mr20'></b>\
<button class='btn btn-success btn-xs btn-slave-ssh'>SSH</button>\
<button class='btn btn-success btn-xs btn-slave-user'>同步账户</button>\
</p>\
</div>",
success:function(){
$('.btn-slave-ssh').click(function(){
getSlaveSSHList();
});
$('.btn-slave-user').click(function(){
getSlaveUserList();
});
$('.slave-db-mode').click(function(){
var _this = this;
var mode = 'none';
if ($(this).hasClass('btn-ssh')){
mode = 'ssh';
}
if ($(this).hasClass('btn-sync-user')){
mode = 'sync-user';
}
myPost('set_slave_sync_mode', {mode:mode}, function(data){
var rdata = $.parseJSON(data.data);
showMsg(rdata.msg,function(){
$('.slave-db-mode').remove('btn-success').addClass('btn-danger');
$(_this).removeClass('btn-danger').addClass('btn-success');
},{icon:rdata.status?1:2},2000);
});
});
}
});
});
}
function getSlaveUserList(){
var page = '<div class="dataTables_paginate_4 dataTables_paginate paging_bootstrap page" style="margin-top:0px;"></div>';
page += '<div class="table_toolbar" style="left:0px;"><span class="sync btn btn-default btn-sm" onclick="addSlaveSyncUser()" title="">添加同步账户</span></div>';
layerId = layer.open({
type: 1,
title: '同步账户列表',
area: '500px',
content:"<div class='bt-form pd20 c6'>\
<div class='divtable mtb10'>\
<div><table class='table table-hover get-slave-ssh-list'>\
<thead><tr><th>IP</th><th>PORT</th><th>同步账户</th><th>同步密码</th><th>CMD</th><th>操作</th></tr></thead>\
<tbody></tbody>\
</table></div>\
"+page +"\
</div>\
</div>",
success:function(){
getSlaveSyncUserPage(1);
}
});
}
function getSlaveSSHList(page=1){
var page = '<div class="dataTables_paginate_4 dataTables_paginate paging_bootstrap page" style="margin-top:0px;"></div>';
@@ -2004,7 +2233,7 @@ function masterOrSlaveConf(version=''){
for(i in rdata.data){
var v = rdata.data[i];
var status = "异常";
var status = "<a class='btlink db_error'>异常</>";
if (v['Slave_SQL_Running'] == 'Yes' && v['Slave_IO_Running'] == 'Yes'){
status = "正常";
}
@@ -2046,6 +2275,26 @@ function masterOrSlaveConf(version=''){
// <span class="sync btn btn-default btn-sm" onclick="getMasterRepSlaveList()" title="">添加</span>\
// </div>
$(".table_slave_status_list").html(con);
$('.db_error').click(function(){
layer.open({
type: 1,
title: '同步异常信息',
area: '500px',
content:"<form class='bt-form pd20 pb70'>\
<div class='line'>"+v['Error']+"</div>\
<div class='bt-form-submit-btn'>\
<button type='button' class='btn btn-success btn-sm btn-title class-copy-db-err'>复制</button>\
</div>\
</form>",
success:function(){
copyText(v['Error']);
$('.class-copy-db-err').click(function(){
copyText(v['Error']);
});
}
});
});
});
}
@@ -2092,7 +2341,6 @@ function masterOrSlaveConf(version=''){
});
}
function getMasterStatus(){
myPost('get_master_status', '', function(rdata){
@@ -2123,7 +2371,7 @@ function masterOrSlaveConf(version=''){
<p class="conf_p">\
<span class="f14 c6 mr20">Slave[从]配置</span><span class="f14 c6 mr20"></span>\
<button class="btn '+(!rdata.slave_status ? 'btn-danger' : 'btn-success')+' btn-xs btn-slave">'+(!rdata.slave_status ? '未启动' : '已启动') +'</button>\
<button class="btn btn-success btn-xs" onclick="getSlaveSSHList()" >[主]SSH配置</button>\
<button class="btn btn-success btn-xs" onclick="getSlaveCfg()" >同步配置</button>\
<button class="btn btn-success btn-xs" onclick="initSlaveStatus()" >初始化</button>\
</p>\
<hr/>\
@@ -2197,10 +2445,10 @@ function masterOrSlaveConf(version=''){
getMasterDbList();
}
if (rdata.slave_status){
// if (rdata.slave_status){
getAsyncMasterDbList();
getAsyncDataList()
}
// }
});
}
getMasterStatus();
+1 -1
View File
@@ -44,7 +44,7 @@ class backupTools:
"----------------------------------------------------------------------------")
return
backup_path = mw.getRootDir() + '/backup/database'
backup_path = mw.getRootDir() + '/backup/database/mysql-yum'
if not os.path.exists(backup_path):
mw.execShell("mkdir -p " + backup_path)
+2 -1
View File
@@ -67,7 +67,8 @@ binlog-ignore-db = performance_schema
#slave
log-slave-updates = 1
skip-slave-start = 1
# Prevent replication from starting automatically with MySQL
#skip-slave-start = 1
#replicate-do-db
replicate-ignore-db = information_schema
replicate-ignore-db = performance_schema
+2 -1
View File
@@ -69,7 +69,8 @@ binlog-ignore-db = performance_schema
#slave
log-slave-updates = 1
skip-slave-start = 1
# Prevent replication from starting automatically with MySQL
#skip-slave-start = 1
#replicate-do-db
replicate-ignore-db = information_schema
replicate-ignore-db = performance_schema
+5 -5
View File
@@ -8,9 +8,6 @@ default-character-set = UTF8MB4
[mysqld]
!include {$SERVER_APP_PATH}/etc/mode/classic.cnf
;sha256_password_private_key_path=mysql.pem
;sha256_password_public_key_path=mysql.pub
pid-file = {$SERVER_APP_PATH}/data/mysql.pid
user = mysql
port = 3306
@@ -44,6 +41,7 @@ open_files_limit = 2560
max_allowed_packet = 128M
skip_name_resolve = 1
skip-ssl
#skip-networking
#skip-external-locking
#loose-skip-innodb
@@ -70,8 +68,10 @@ binlog-ignore-db = information_schema
binlog-ignore-db = performance_schema
#slave
log-slave-updates = 1
skip-slave-start = 1
# 5.7 It can not be opened later
#log-slave-updates = 1
# Prevent replication from starting automatically with MySQL
#skip-slave-start = 1
#replicate-do-db
replicate-ignore-db = information_schema
replicate-ignore-db = performance_schema
+2 -1
View File
@@ -68,7 +68,8 @@ binlog-ignore-db = performance_schema
#slave
log_replica_updates = 1
skip_replica_start = 1
# Prevent replication from starting automatically with MySQL
#skip_replica_start = 1
#replicate-do-db
replicate-ignore-db = information_schema
replicate-ignore-db = performance_schema
+13
View File
@@ -40,4 +40,17 @@ CREATE TABLE IF NOT EXISTS `slave_id_rsa` (
`addtime` TEXT
);
-- 从库配置主库的[user]
-- drop table `slave_user`;
CREATE TABLE IF NOT EXISTS `slave_sync_user` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`ip` TEXT,
`port` TEXT,
`user` TEXT,
`pass` TEXT,
`mode` TEXT,
`cmd` TEXT,
`addtime` TEXT
);
+1 -1
View File
@@ -53,7 +53,7 @@
.conf_p span {
display: inline-block;
margin-right: 10px;
width: 135px;
width: 115px;
text-align: right;
}
</style>
+328 -23
View File
@@ -127,20 +127,27 @@ def contentReplace(content):
def pSqliteDb(dbname='databases'):
file = getServerDir() + '/mysql.db'
name = 'mysql'
if not os.path.exists(file):
import_sql = mw.readFile(getPluginDir() + '/conf/mysql.sql')
md5_sql = mw.md5(import_sql)
import_sign = False
save_md5_file = getServerDir() + '/import_sql.md5'
if os.path.exists(save_md5_file):
save_md5_sql = mw.readFile(save_md5_file)
if save_md5_sql != md5_sql:
import_sign = True
mw.writeFile(save_md5_file, md5_sql)
else:
mw.writeFile(save_md5_file, md5_sql)
if not os.path.exists(file) or import_sql:
conn = mw.M(dbname).dbPos(getServerDir(), name)
csql = mw.readFile(getPluginDir() + '/conf/mysql.sql')
csql_list = csql.split(';')
csql_list = import_sql.split(';')
for index in range(len(csql_list)):
conn.execute(csql_list[index], ())
else:
# 现有run
# conn = mw.M(dbname).dbPos(getServerDir(), name)
# csql = mw.readFile(getPluginDir() + '/conf/mysql.sql')
# csql_list = csql.split(';')
# for index in range(len(csql_list)):
# conn.execute(csql_list[index], ())
conn = mw.M(dbname).dbPos(getServerDir(), name)
conn = mw.M(dbname).dbPos(getServerDir(), name)
return conn
@@ -899,11 +906,13 @@ def importDbExternal():
mysql_cmd = getServerDir() + '/bin/mysql -S ' + sock + ' -uroot -p' + \
pwd + ' ' + name + ' < ' + import_sql
# print(mysql_cmd)
os.system(mysql_cmd)
rdata = mw.execShell(mysql_cmd)
if ext != 'sql':
os.remove(import_sql)
if rdata[1].lower().find('error') > -1:
return mw.returnJson(False, rdata[1])
return mw.returnJson(True, 'ok')
@@ -923,13 +932,23 @@ def importDbBackup():
cmd = 'cd ' + mw.getRootDir() + '/backup/database && gzip -d ' + file
mw.execShell(cmd)
local_mode = recognizeDbMode()
if local_mode == 'gtid':
pdb = pMysqlDb()
pdb.execute('reset master')
pwd = pSqliteDb('config').where('id=?', (1,)).getField('mysql_root')
sock = getSocketFile()
mysql_cmd = getServerDir() + '/bin/mysql -S ' + sock + ' -uroot -p' + pwd + \
' ' + name + ' < ' + file_path_sql
# print(mysql_cmd)
os.system(mysql_cmd)
# os.system(mysql_cmd)
rdata = mw.execShell(mysql_cmd)
if rdata[1].lower().find('error') > -1:
return mw.returnJson(False, rdata[1])
return mw.returnJson(True, 'ok')
@@ -1260,7 +1279,7 @@ def addDb():
dataAccess = args['dataAccess'].strip()
ps = args['ps'].strip()
reg = "^[\w\.-]+$"
reg = "^[\w-]+$"
if not re.match(reg, args['name']):
return mw.returnJson(False, '数据库名称不能带有特殊符号!')
checks = ['root', 'mysql', 'test', 'sys', 'panel_logs']
@@ -1928,7 +1947,7 @@ def addMasterRepSlaveUser(version=''):
# address = args['address'].strip()
# dataAccess = args['dataAccess'].strip()
reg = "^[\w\.-]+$"
reg = "^[\w-]+$"
if not re.match(reg, username):
return mw.returnJson(False, '用户名不能带有特殊符号!')
checks = ['root', 'mysql', 'test', 'sys', 'panel_logs']
@@ -1956,17 +1975,18 @@ def addMasterRepSlaveUser(version=''):
if isError != None:
return isError
sql = "FLUSH PRIVILEGES;"
pdb.execute(sql)
else:
sql = "GRANT REPLICATION SLAVE ON *.* TO '" + username + \
sql = "grant replication SLAVE ON *.* TO '" + username + \
"'@'%' identified by '" + password + "';"
result = pdb.execute(sql)
result = pdb.execute('FLUSH PRIVILEGES;')
isError = isSqlError(result)
if isError != None:
return isError
sql_select = "grant select,lock tables,PROCESS on *.* to " + username + "@'%';"
pdb.execute(sql_select)
pdb.execute('FLUSH PRIVILEGES;')
addTime = time.strftime('%Y-%m-%d %X', time.localtime())
psdb.add('username,password,accept,ps,addtime',
(username, password, '%', '', addTime))
@@ -2103,6 +2123,123 @@ def getSlaveSSHList(version=''):
return mw.getJson(data)
def getSlaveSyncUserByIp(version=''):
args = getArgs()
data = checkArgs(args, ['ip'])
if not data[0]:
return data[1]
ip = args['ip']
conn = pSqliteDb('slave_sync_user')
data = conn.field('ip,port,user,pass,mode,cmd').where(
"ip=?", (ip,)).select()
return mw.returnJson(True, 'ok', data)
def addSlaveSyncUser(version=''):
import base64
args = getArgs()
data = checkArgs(args, ['ip'])
if not data[0]:
return data[1]
ip = args['ip']
if ip == "":
return mw.returnJson(True, 'ok')
data = checkArgs(args, ['port', 'user', 'pass', 'mode'])
if not data[0]:
return data[1]
cmd = args['cmd']
port = args['port']
user = args['user']
apass = args['pass']
mode = args['mode']
addTime = time.strftime('%Y-%m-%d %X', time.localtime())
conn = pSqliteDb('slave_sync_user')
data = conn.field('ip').where("ip=?", (ip,)).select()
if len(data) > 0:
res = conn.where("ip=?", (ip,)).save(
'port,user,pass,mode,cmd', (port, user, apass, mode, cmd))
else:
conn.add('ip,port,user,cmd,user,pass,mode,addtime',
(ip, port, user, cmd, user, apass, mode, addTime))
return mw.returnJson(True, '设置成功!')
def delSlaveSyncUser(version=''):
args = getArgs()
data = checkArgs(args, ['ip'])
if not data[0]:
return data[1]
ip = args['ip']
conn = pSqliteDb('slave_sync_user')
conn.where("ip=?", (ip,)).delete()
return mw.returnJson(True, '删除成功!')
def getSlaveSyncUserList(version=''):
args = getArgs()
data = checkArgs(args, ['page', 'page_size'])
if not data[0]:
return data[1]
page = int(args['page'])
page_size = int(args['page_size'])
conn = pSqliteDb('slave_sync_user')
limit = str((page - 1) * page_size) + ',' + str(page_size)
field = 'id,ip,port,user,pass,cmd,addtime'
clist = conn.field(field).limit(limit).order('id desc').select()
count = conn.count()
data = {}
_page = {}
_page['count'] = count
_page['p'] = page
_page['row'] = page_size
_page['tojs'] = args['tojs']
data['page'] = mw.getPage(_page)
data['data'] = clist
return mw.getJson(data)
def getSyncModeFile():
return getServerDir() + "/sync.mode"
def getSlaveSyncMode(version):
sync_mode = getSyncModeFile()
if os.path.exists(sync_mode):
mode = mw.readFile(sync_mode).strip()
return mw.returnJson(True, 'ok', mode)
return mw.returnJson(False, 'fail')
def setSlaveSyncMode(version):
args = getArgs()
data = checkArgs(args, ['mode'])
if not data[0]:
return data[1]
mode = args['mode']
sync_mode = getSyncModeFile()
if mode == 'none':
os.remove(sync_mode)
else:
mw.writeFile(sync_mode, mode)
return mw.returnJson(True, '设置成功', mode)
def getSlaveSSHByIp(version=''):
args = getArgs()
data = checkArgs(args, ['ip'])
@@ -2160,7 +2297,7 @@ def delSlaveSSH(version=''):
conn = pSqliteDb('slave_id_rsa')
conn.where("ip=?", (ip,)).delete()
return mw.returnJson(True, 'ok')
return mw.returnJson(True, '删除SSH成功!')
def updateSlaveSSH(version=''):
@@ -2189,6 +2326,24 @@ def getSlaveList(version=''):
tmp['Master_Log_File'] = dlist[x]["Master_Log_File"]
tmp['Slave_IO_Running'] = dlist[x]["Slave_IO_Running"]
tmp['Slave_SQL_Running'] = dlist[x]["Slave_SQL_Running"]
tmp['Last_Error'] = dlist[x]["Last_Error"]
tmp['Last_IO_Error'] = dlist[x]["Last_IO_Error"]
tmp['Last_SQL_Error'] = dlist[x]["Last_SQL_Error"]
tmp['Slave_SQL_Running_State'] = dlist[x]["Slave_SQL_Running_State"]
tmp['Error'] = ''
if tmp['Last_Error'] != '':
tmp['Error'] = tmp['Last_Error']
if tmp['Last_IO_Error'] != '':
tmp['Error'] = tmp['Last_IO_Error']
if tmp['Last_SQL_Error'] != '':
tmp['Error'] = tmp['Last_SQL_Error']
if tmp['Error'] == '':
tmp['Error'] = tmp['Slave_SQL_Running_State']
ret.append(tmp)
data = {}
data['data'] = ret
@@ -2205,6 +2360,47 @@ def getSlaveSyncCmd(version=''):
def initSlaveStatus(version=''):
mode_file = getSyncModeFile()
if not os.path.exists(mode_file):
return mw.returnJson(False, '需要先设置同步配置')
mode = mw.readFile(mode_file)
if mode == 'ssh':
return initSlaveStatusSSH(version)
if mode == 'sync-user':
return initSlaveStatusSyncUser(version)
def initSlaveStatusSyncUser(version=''):
conn = pSqliteDb('slave_sync_user')
data = conn.field('ip,port,user,pass,mode,cmd').find()
if len(data) < 1:
return mw.returnJson(False, '需要先添加同步用户配置!')
# print(data)
db = pMysqlDb()
dlist = db.query('show slave status')
if len(dlist) > 0:
return mw.returnJson(False, '已经初始化好了zz...')
u = data
mode_name = 'classic'
if u['mode'] == '1':
mode_name = 'gtid'
local_mode = recognizeDbMode()
if local_mode != mode_name:
return mw.returnJson(False, '同步模式不一致!')
t = db.query(u['cmd'])
# print(t)
db.query("start slave user='{}' password='{}';".format(
u['user'], u['pass']))
return mw.returnJson(True, '初始化成功!')
def initSlaveStatusSSH(version=''):
db = pMysqlDb()
dlist = db.query('show slave status')
if len(dlist) > 0:
@@ -2214,7 +2410,7 @@ def initSlaveStatus(version=''):
data = conn.field('ip,port,id_rsa').find()
if len(data) < 1:
return mw.returnJson(False, '需要先配置【[主]SSH配置】!')
return mw.returnJson(False, '需要先添加SSH配置!')
SSH_PRIVATE_KEY = "/tmp/t_ssh.txt"
ip = data['ip']
@@ -2273,11 +2469,46 @@ def initSlaveStatus(version=''):
def setSlaveStatus(version=''):
mode_file = getSyncModeFile()
if not os.path.exists(mode_file):
return mw.returnJson(False, '需要先设置同步配置')
mode = mw.readFile(mode_file)
if mode == 'ssh':
return setSlaveStatusSSH(version)
if mode == 'sync-user':
return setSlaveStatusSyncUser(version)
def setSlaveStatusSyncUser(version=''):
db = pMysqlDb()
dlist = db.query('show slave status')
if len(dlist) == 0:
return mw.returnJson(False, '需要手动添加同步账户或者执行初始化!')
if len(dlist) > 0 and (dlist[0]["Slave_IO_Running"] == 'Yes' or dlist[0]["Slave_SQL_Running"] == 'Yes'):
db.query('stop slave')
else:
ip = dlist[0]['Master_Host']
conn = pSqliteDb('slave_sync_user')
data = conn.field('ip,port,user,pass,mode,cmd').find()
if len(data) == 0:
return mw.returnJson(False, '没有数据无法重启!')
user = data['user']
apass = data['pass']
db.query("start slave")
# db.query("start slave user='{}' password='{}';".format(user, apass))
return mw.returnJson(True, '设置成功!')
def setSlaveStatusSSH(version=''):
db = pMysqlDb()
dlist = db.query('show slave status')
if len(dlist) == 0:
return mw.returnJson(False, '需要手动添加主服务命令或者执行[初始化]!')
return mw.returnJson(False, '需要手动添加主服务命令或者执行初始化!')
if len(dlist) > 0 and (dlist[0]["Slave_IO_Running"] == 'Yes' or dlist[0]["Slave_SQL_Running"] == 'Yes'):
db.query('stop slave')
@@ -2342,6 +2573,68 @@ def writeDbSyncStatus(data):
def doFullSync(version=''):
mode_file = getSyncModeFile()
if not os.path.exists(mode_file):
return mw.returnJson(False, '需要先设置同步配置')
mode = mw.readFile(mode_file)
if mode == 'ssh':
return doFullSyncSSH(version)
if mode == 'sync-user':
return doFullSyncUser(version)
def doFullSyncUser(version=''):
args = getArgs()
data = checkArgs(args, ['db'])
if not data[0]:
return data[1]
sync_db = args['db']
db = pMysqlDb()
conn = pSqliteDb('slave_sync_user')
data = conn.field('ip,port,user,pass,mode,cmd').find()
user = data['user']
apass = data['pass']
port = data['port']
ip = data['ip']
bak_file = '/tmp/tmp.sql'
writeDbSyncStatus({'code': 0, 'msg': '开始同步...', 'progress': 0})
dmp_option = ''
mode = recognizeDbMode()
if mode == 'gtid':
dmp_option = ' --set-gtid-purged=off '
writeDbSyncStatus({'code': 1, 'msg': '远程导出数据...', 'progress': 20})
if not os.path.exists(bak_file):
dump_sql_data = getServerDir() + "/bin/mysqldump " + dmp_option + " --force --opt --default-character-set=utf8 --single-transaction -h" + ip + " -P" + \
port + " -u" + user + " -p" + apass + " " + sync_db + " > " + bak_file
mw.execShell(dump_sql_data)
writeDbSyncStatus({'code': 2, 'msg': '本地导入数据...', 'progress': 40})
if os.path.exists(bak_file):
pwd = pSqliteDb('config').where('id=?', (1,)).getField('mysql_root')
sock = getSocketFile()
my_import_cmd = getServerDir() + '/bin/mysql -S ' + sock + ' -uroot -p' + pwd + \
' ' + sync_db + ' < ' + bak_file
mw.execShell(my_import_cmd)
if version == '8.0':
db.query("start slave user='{}' password='{}';".format(user, apass))
else:
db.query("start slave")
writeDbSyncStatus({'code': 6, 'msg': '从库重启完成...', 'progress': 100})
os.system("rm -rf " + bak_file)
return True
def doFullSyncSSH(version=''):
args = getArgs()
data = checkArgs(args, ['db'])
@@ -2653,6 +2946,18 @@ if __name__ == "__main__":
print(delSlaveSSH(version))
elif func == 'update_slave_ssh':
print(updateSlaveSSH(version))
elif func == 'get_slave_sync_user_list':
print(getSlaveSyncUserList(version))
elif func == 'get_slave_sync_user_by_ip':
print(getSlaveSyncUserByIp(version))
elif func == 'add_slave_sync_user':
print(addSlaveSyncUser(version))
elif func == 'del_slave_sync_user':
print(delSlaveSyncUser(version))
elif func == 'get_slave_sync_mode':
print(getSlaveSyncMode(version))
elif func == 'set_slave_sync_mode':
print(setSlaveSyncMode(version))
elif func == 'init_slave_status':
print(initSlaveStatus(version))
elif func == 'set_slave_status':
+275 -27
View File
@@ -509,19 +509,6 @@ function showHidePass(obj){
}
}
function copyPass(password){
var clipboard = new ClipboardJS('#bt_copys');
clipboard.on('success', function (e) {
layer.msg('复制成功',{icon:1,time:2000});
});
clipboard.on('error', function (e) {
layer.msg('复制失败,浏览器不兼容!',{icon:2,time:2000});
});
$("#bt_copys").attr('data-clipboard-text',password);
$("#bt_copys").click();
}
function checkSelect(){
setTimeout(function () {
var num = $('input[type="checkbox"].check:checked').length;
@@ -744,7 +731,7 @@ function addDatabase(type){
layer.close(index);
dbList();
}
},{icon: rdata.status ? 1 : 2},600);
},{icon: rdata.status ? 1 : 2}, 2000);
});
}
});
@@ -1558,13 +1545,9 @@ function getMasterRepSlaveUserCmd(username, db=''){
function delMasterRepSlaveUser(username){
myPost('del_master_rep_slave_user', {username:username}, function(data){
var rdata = $.parseJSON(data.data);
layer.msg(rdata.msg);
$('.layui-layer-close1').click();
setTimeout(function(){
showMsg(rdata.msg, function(){
getMasterRepSlaveList();
},1000);
},{icon: rdata.status ? 1 : 2},1000)
});
}
@@ -1834,8 +1817,23 @@ function addSlaveSSH(ip=''){
function delSlaveSSH(ip){
myPost('del_slave_ssh', {ip:ip}, function(rdata){
var rdata = $.parseJSON(rdata.data);
layer.msg(rdata.msg, {icon: rdata.status ? 1 : 2});
getSlaveSSHPage();
showMsg(rdata.msg,function(){
if (rdata.status){
getSlaveSSHPage();
}
},{icon: rdata.status ? 1 : 2}, 600);
});
}
function delSlaveSyncUser(ip){
myPost('del_slave_sync_user', {ip:ip}, function(rdata){
var rdata = $.parseJSON(rdata.data);
showMsg(rdata.msg,function(){
if (rdata.status){
getSlaveSyncUserPage();
}
},{icon: rdata.status ? 1 : 2}, 600);
});
}
@@ -1885,6 +1883,237 @@ function getSlaveSSHPage(page=1){
}
function addSlaveSyncUser(ip=''){
myPost('get_slave_sync_user_by_ip', {ip:ip}, function(rdata){
var rdata = $.parseJSON(rdata.data);
var ip = '127.0.0.1';
var port = "22";
var cmd = '';
var user = 'input_sync_user';
var pass = 'input_sync_pwd';
var mode = '0';
if (rdata.data.length>0){
ip = rdata.data[0]['ip'];
port = rdata.data[0]['port'];
cmd = rdata.data[0]['cmd'];
user = rdata.data[0]['user'];
pass = rdata.data[0]['pass'];
mode = rdata.data[0]['mode'];
}
var index = layer.open({
type: 1,
area: ['500px','470px'],
title: '同步账户',
closeBtn: 1,
shift: 5,
shadeClose: true,
btn:["确认","取消"],
content: "<form class='bt-form pd20'>\
<div class='line'><span class='tname'>IP</span><div class='info-r'><input name='ip' class='bt-input-text mr5' type='text' style='width:330px;' value='"+ip+"'></div></div>\
<div class='line'><span class='tname'>端口</span><div class='info-r'><input name='port' class='bt-input-text mr5' type='number' style='width:330px;' value='"+port+"'></div></div>\
<div class='line'><span class='tname'>同步账户</span><div class='info-r'><input name='user' class='bt-input-text mr5' type='text' style='width:330px;' value='"+user+"'></div></div>\
<div class='line'><span class='tname'>同步密码</span><div class='info-r'><input name='pass' class='bt-input-text mr5' type='text' style='width:330px;' value='"+pass+"'></div></div>\
<div class='line'>\
<span class='tname'>CMD[最好填好]</span>\
<div class='info-r'><textarea class='bt-input-text mr5' row='20' cols='30' name='cmd' style='width:330px;height:150px;'></textarea></div>\
</div>\
<input type='hidden' name='mode' value='"+mode+"' />\
</form>",
success:function(){
$('textarea[name="cmd"]').html(cmd);
$('textarea[name="cmd"]').change(function(){
var val = $(this).val();
var vlist = val.split(',');
var a = {};
for (var i in vlist) {
var tmp = toTrim(vlist[i]);
var tmp_a = tmp.split(" ");
var real_tmp = tmp_a[tmp_a.length-1];
var kv = real_tmp.split("=");
a[kv[0]] = kv[1].replace("'",'').replace("'",'');
}
$('input[name="ip"]').val(a['MASTER_HOST']);
$('input[name="port"]').val(a['MASTER_PORT']);
$('input[name="user"]').val(a['MASTER_USER']);
$('input[name="pass"]').val(a['MASTER_PASSWORD']);
console.log(a['MASTER_AUTO_POSITION'],typeof(a['MASTER_AUTO_POSITION']));
if (typeof(a['MASTER_AUTO_POSITION']) != 'undefined' ){
$('input[name="mode"]').val('1');
}
});
},
yes:function(index){
var ip = $('input[name="ip"]').val();
var port = $('input[name="port"]').val();
var user = $('input[name="user"]').val();
var pass = $('input[name="pass"]').val();
var cmd = $('textarea[name="cmd"]').val();
var mode = $('input[name="mode"]').val();
var data = {ip:ip,port:port,cmd:cmd,user:user,pass:pass,mode:mode};
myPost('add_slave_sync_user', data, function(ret_data){
layer.close(index);
var rdata = $.parseJSON(ret_data.data);
showMsg(rdata.msg,function(){
if (rdata.status){
getSlaveSyncUserPage();
}
},{icon: rdata.status ? 1 : 2},600);
});
}
});
});
}
function getSlaveSyncUserPage(page=1){
var _data = {};
_data['page'] = page;
_data['page_size'] = 5;
_data['tojs'] ='getSlaveSyncUserPage';
myPost('get_slave_sync_user_list', _data, function(data){
var layerId = null;
var rdata = [];
try {
rdata = $.parseJSON(data.data);
} catch(e) {
console.log(e);
}
var list = '';
var user_list = rdata['data'];
for (i in user_list) {
var ip = user_list[i]['ip'];
var port = user_list[i]['port'];
var user = user_list[i]['user'];
var apass = user_list[i]['pass'];
var cmd = '未设置';
if (user_list[i]['cmd']!=''){
cmd = '已设置';
}
list += '<tr><td>'+ip+'</td>\
<td>'+port+'</td>\
<td>'+user+'</td>\
<td>'+apass+'</td>\
<td>'+cmd+'</td>\
<td>\
<a class="btlink" onclick="addSlaveSyncUser(\''+ip+'\');">修改</a> | \
<a class="btlink" onclick="delSlaveSyncUser(\''+ip+'\');">删除</a>\
</td>\
</tr>';
}
$('.get-slave-ssh-list tbody').html(list);
$('.dataTables_paginate_4').html(rdata['page']);
});
}
function getSlaveCfg(){
myPost('get_slave_sync_mode', '', function(data){
var rdata = $.parseJSON(data.data);
var mode_none = 'success';
var mode_ssh = 'danger';
var mode_sync_user = 'danger';
if(rdata.status){
var mode_none = 'danger';
if (rdata.data == 'ssh'){
var mode_ssh = 'success';
var mode_sync_user = 'danger';
} else {
var mode_ssh = 'danger';
var mode_sync_user = 'success';
}
}
layerId = layer.open({
type: 1,
title: '同步配置',
area: ['400px','180px'],
content:"<div class='bt-form pd20 c6'>\
<p class='conf_p'>\
<span class='f14 c6 mr20'>当前从库同步模式</span>\
<b class='f14 c6 mr20'></b>\
<button class='btn btn-"+mode_none+" btn-xs slave-db-mode btn-none'>无</button>\
<button class='btn btn-"+mode_ssh+" btn-xs slave-db-mode btn-ssh'>SSH</button>\
<button class='btn btn-"+mode_sync_user+" btn-xs slave-db-mode btn-sync-user'>同步账户</button>\
</p>\
<hr />\
<p class='conf_p'>\
<span class='f14 c6 mr20'>配置设置</span>\
<b class='f14 c6 mr20'></b>\
<button class='btn btn-success btn-xs btn-slave-ssh'>SSH</button>\
<button class='btn btn-success btn-xs btn-slave-user'>同步账户</button>\
</p>\
</div>",
success:function(){
$('.btn-slave-ssh').click(function(){
getSlaveSSHList();
});
$('.btn-slave-user').click(function(){
getSlaveUserList();
});
$('.slave-db-mode').click(function(){
var _this = this;
var mode = 'none';
if ($(this).hasClass('btn-ssh')){
mode = 'ssh';
}
if ($(this).hasClass('btn-sync-user')){
mode = 'sync-user';
}
myPost('set_slave_sync_mode', {mode:mode}, function(data){
var rdata = $.parseJSON(data.data);
showMsg(rdata.msg,function(){
$('.slave-db-mode').remove('btn-success').addClass('btn-danger');
$(_this).removeClass('btn-danger').addClass('btn-success');
},{icon:rdata.status?1:2},2000);
});
});
}
});
});
}
function getSlaveUserList(){
var page = '<div class="dataTables_paginate_4 dataTables_paginate paging_bootstrap page" style="margin-top:0px;"></div>';
page += '<div class="table_toolbar" style="left:0px;"><span class="sync btn btn-default btn-sm" onclick="addSlaveSyncUser()" title="">添加同步账户</span></div>';
layerId = layer.open({
type: 1,
title: '同步账户列表',
area: '500px',
content:"<div class='bt-form pd20 c6'>\
<div class='divtable mtb10'>\
<div><table class='table table-hover get-slave-ssh-list'>\
<thead><tr><th>IP</th><th>PORT</th><th>同步账户</th><th>同步密码</th><th>CMD</th><th>操作</th></tr></thead>\
<tbody></tbody>\
</table></div>\
"+page +"\
</div>\
</div>",
success:function(){
getSlaveSyncUserPage(1);
}
});
}
function getSlaveSSHList(page=1){
var page = '<div class="dataTables_paginate_4 dataTables_paginate paging_bootstrap page" style="margin-top:0px;"></div>';
@@ -2004,7 +2233,7 @@ function masterOrSlaveConf(version=''){
for(i in rdata.data){
var v = rdata.data[i];
var status = "异常";
var status = "<a class='btlink db_error'>异常</>";
if (v['Slave_SQL_Running'] == 'Yes' && v['Slave_IO_Running'] == 'Yes'){
status = "正常";
}
@@ -2046,6 +2275,26 @@ function masterOrSlaveConf(version=''){
// <span class="sync btn btn-default btn-sm" onclick="getMasterRepSlaveList()" title="">添加</span>\
// </div>
$(".table_slave_status_list").html(con);
$('.db_error').click(function(){
layer.open({
type: 1,
title: '同步异常信息',
area: '500px',
content:"<form class='bt-form pd20 pb70'>\
<div class='line'>"+v['Error']+"</div>\
<div class='bt-form-submit-btn'>\
<button type='button' class='btn btn-success btn-sm btn-title class-copy-db-err'>复制</button>\
</div>\
</form>",
success:function(){
copyText(v['Error']);
$('.class-copy-db-err').click(function(){
copyText(v['Error']);
});
}
});
});
});
}
@@ -2092,7 +2341,6 @@ function masterOrSlaveConf(version=''){
});
}
function getMasterStatus(){
myPost('get_master_status', '', function(rdata){
@@ -2123,7 +2371,7 @@ function masterOrSlaveConf(version=''){
<p class="conf_p">\
<span class="f14 c6 mr20">Slave[从]配置</span><span class="f14 c6 mr20"></span>\
<button class="btn '+(!rdata.slave_status ? 'btn-danger' : 'btn-success')+' btn-xs btn-slave">'+(!rdata.slave_status ? '未启动' : '已启动') +'</button>\
<button class="btn btn-success btn-xs" onclick="getSlaveSSHList()" >[主]SSH配置</button>\
<button class="btn btn-success btn-xs" onclick="getSlaveCfg()" >同步配置</button>\
<button class="btn btn-success btn-xs" onclick="initSlaveStatus()" >初始化</button>\
</p>\
<hr/>\
@@ -2197,10 +2445,10 @@ function masterOrSlaveConf(version=''){
getMasterDbList();
}
if (rdata.slave_status){
// if (rdata.slave_status){
getAsyncMasterDbList();
getAsyncDataList()
}
// }
});
}
getMasterStatus();
+1 -1
View File
@@ -5,7 +5,7 @@
"name": "php-apt",
"title": "PHP[APT]",
"coexist": true,
"versions": ["56","70","71","72","73","74","80","81"],
"versions": ["56","70","71","72","73","74","80","81","82"],
"install_pre_inspection":true,
"tip": "soft",
"checks": "server/php-apt/VERSION",
+6
View File
@@ -89,6 +89,12 @@ if [ "${action}" == "install" ] && [ -d ${serverPath}/php-apt/${type} ];then
cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${type:0:1}.${type:1:2} install redis
cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${type:0:1}.${type:1:2} install memcached
echo "install PHP-APT[${type}] extend end"
if [ ! -f /usr/local/bin/composer ];then
cd /tmp
curl -sS https://getcomposer.org/installer | php
mv composer.phar /usr/local/bin/composer
fi
fi
+49
View File
@@ -0,0 +1,49 @@
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
curPath=`pwd`
rootPath=$(dirname "$curPath")
rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
sourcePath=${serverPath}/source
sysName=`uname`
install_tmp=${rootPath}/tmp/mw_install.pl
#获取信息和版本
# bash /www/server/mdsever-web/scripts/getos.sh
bash ${rootPath}/scripts/getos.sh
OSNAME=`cat ${rootPath}/data/osname.pl`
VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
version=8.2
PHP_VER=82
Install_php()
{
#------------------------ install start ------------------------------------#
apt -y install php${version} php${version}-fpm php${version}-dev
if [ "$?" == "0" ];then
mkdir -p $serverPath/php-apt/${PHP_VER}
fi
#------------------------ install end ------------------------------------#
}
Uninstall_php()
{
#------------------------ uninstall start ------------------------------------#
apt -y remove php${version} php${version}-*
rm -rf $serverPath/php-apt/${PHP_VER}
echo "卸载php-${version}..." > $install_tmp
#------------------------ uninstall start ------------------------------------#
}
action=${1}
if [ "${1}" == 'install' ];then
Install_php
else
Uninstall_php
fi
+6
View File
@@ -78,6 +78,12 @@ if [ "${action}" == "install" ] && [ -d ${serverPath}/php-yum/${type} ];then
cd ${rootPath}/plugins/php-yum/versions && bash common.sh ${type} install redis
cd ${rootPath}/plugins/php-yum/versions && bash common.sh ${type} install memcached
echo "install PHP-YUM[${type}] extend end"
if [ ! -f /usr/local/bin/composer ];then
cd /tmp
curl -sS https://getcomposer.org/installer | php
mv composer.phar /usr/local/bin/composer
fi
fi
+16 -3
View File
@@ -32,9 +32,9 @@ if [ ! -d $curPath/versions/$2 ];then
fi
if [ "${action}" == "install" ] && [ -d $serverPath/php/${type} ];then
exit 0
fi
# if [ "${action}" == "install" ] && [ -d $serverPath/php/${type} ];then
# exit 0
# fi
if [ "${action}" == "uninstall" ];then
@@ -61,6 +61,7 @@ if [ "${action}" == "install" ] && [ -d ${serverPath}/php/${type} ];then
# cd /www/server/mdserver-web/plugins/php/versions/common && bash iconv.sh install 53
# cd /www/server/mdserver-web/plugins/php/versions/common && bash gd.sh install 56
# cd /www/server/mdserver-web/plugins/php/versions/common && bash openssl.sh install 56
# cd /www/server/mdserver-web/plugins/php/versions/common && bash sodium.sh install 81
cd ${rootPath}/plugins/php/versions/common && bash gd.sh install ${type}
cd ${rootPath}/plugins/php/versions/common && bash iconv.sh install ${type}
cd ${rootPath}/plugins/php/versions/common && bash exif.sh install ${type}
@@ -71,7 +72,19 @@ if [ "${action}" == "install" ] && [ -d ${serverPath}/php/${type} ];then
cd ${rootPath}/plugins/php/versions/common && bash gettext.sh install ${type}
cd ${rootPath}/plugins/php/versions/common && bash redis.sh install ${type}
cd ${rootPath}/plugins/php/versions/common && bash memcached.sh install ${type}
cd ${rootPath}/plugins/php/versions/common && bash zlib.sh install ${type}
if [ "${type}" -gt "72" ];then
cd ${rootPath}/plugins/php/versions/common && bash zip.sh install ${type}
fi
echo "install PHP${type} extend end"
if [ ! -f /usr/local/bin/composer ];then
cd /tmp
curl -sS https://getcomposer.org/installer | php
mv composer.phar /usr/local/bin/composer
fi
fi
+7 -1
View File
@@ -13,10 +13,16 @@ rootPath=$(dirname "$rootPath")
SERVER_ROOT=$rootPath/lib
SOURCE_ROOT=$rootPath/source/lib
cn=$(curl -fsSL -m 10 http://ipinfo.io/json | grep "\"country\": \"CN\"")
HTTP_PREFIX="https://"
if [ ! -z "$cn" ];then
HTTP_PREFIX="https://ghproxy.com/"
fi
if [ ! -d ${SERVER_ROOT}/icu ];then
if [ ! -f ${SOURCE_ROOT}/icu4c-52_2-src.tgz ];then
wget -O ${SOURCE_ROOT}/icu4c-52_2-src.tgz https://github.com/unicode-org/icu/releases/download/release-52-2/icu4c-52_2-src.tgz
wget --no-check-certificate -O ${SOURCE_ROOT}/icu4c-52_2-src.tgz ${HTTP_PREFIX}github.com/unicode-org/icu/releases/download/release-52-2/icu4c-52_2-src.tgz
fi
if [ ! -d ${SERVER_ROOT}/icu/52.2 ];then
+7 -1
View File
@@ -13,9 +13,15 @@ rootPath=$(dirname "$rootPath")
SERVER_ROOT=$rootPath/lib
SOURCE_ROOT=$rootPath/source/lib
cn=$(curl -fsSL -m 10 http://ipinfo.io/json | grep "\"country\": \"CN\"")
HTTP_PREFIX="https://"
if [ ! -z "$cn" ];then
HTTP_PREFIX="https://ghproxy.com/"
fi
if [ ! -d ${SERVER_ROOT}/libiconv ];then
cd $SOURCE_ROOT
wget -O libiconv-1.15.tar.gz https://github.com/midoks/mdserver-web/releases/download/init/libiconv-1.15.tar.gz -T 5
wget --no-check-certificate -O libiconv-1.15.tar.gz ${HTTP_PREFIX}github.com/midoks/mdserver-web/releases/download/init/libiconv-1.15.tar.gz -T 5
tar zxvf libiconv-1.15.tar.gz
cd libiconv-1.15
./configure --prefix=${SERVER_ROOT}/libiconv --enable-static && make && make install
+1 -1
View File
@@ -25,7 +25,7 @@ done
if [ $ISFIND == "0" ];then
cd $SOURCE_ROOT
if [ ! -f ${SOURCE_ROOT}/libmcrypt-2.5.8.tar.gz ];then
wget -O libmcrypt-2.5.8.tar.gz --no-check-certificate https://sourceforge.net/projects/mcrypt/files/Libmcrypt/2.5.8/libmcrypt-2.5.8.tar.gz -T 20
wget --no-check-certificate -O libmcrypt-2.5.8.tar.gz https://sourceforge.net/projects/mcrypt/files/Libmcrypt/2.5.8/libmcrypt-2.5.8.tar.gz -T 20
fi
tar -zxvf libmcrypt-2.5.8.tar.gz
+1 -1
View File
@@ -30,7 +30,7 @@ SOURCE_ROOT=$rootPath/source/lib
if [ ! -d ${SERVER_ROOT}/libmemcached ];then
cd ${SOURCE_ROOT}
if [ ! -f ${SOURCE_ROOT}/libmemcached-1.0.18.tar.gz ];then
wget -O libmemcached-1.0.18.tar.gz https://launchpad.net/libmemcached/1.0/1.0.18/+download/libmemcached-1.0.18.tar.gz -T 20
wget --no-check-certificate -O libmemcached-1.0.18.tar.gz https://launchpad.net/libmemcached/1.0/1.0.18/+download/libmemcached-1.0.18.tar.gz -T 20
fi
tar -zxf libmemcached-1.0.18.tar.gz
cd libmemcached-1.0.18
+29
View File
@@ -0,0 +1,29 @@
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
curPath=`pwd`
rootPath=$(dirname "$curPath")
rootPath=$(dirname "$rootPath")
rootPath=$(dirname "$rootPath")
rootPath=$(dirname "$rootPath")
# echo $rootPath
SERVER_ROOT=$rootPath/lib
SOURCE_ROOT=$rootPath/source/lib
VERSION=1.0.18
#----------------------------- libsodium start -------------------------#
if [ ! -f /usr/local/lib/libsodium.so ];then
cd ${SOURCE_ROOT}
if [ ! -f ${SOURCE_ROOT}/libsodium-${VERSION}-stable.tar.gz ];then
# wget --no-check-certificate -O libsodium-1.0.18-stable.tar.gz https://download.libsodium.org/libsodium/releases/libsodium-1.0.18-stable.tar.gz -T 20
wget --no-check-certificate -O libsodium-${VERSION}-stable.tar.gz https://download.libsodium.org/libsodium/releases/libsodium-${VERSION}-stable.tar.gz -T 20
fi
tar -zxvf libsodium-${VERSION}-stable.tar.gz
cd libsodium-stable
./configure && make && make check && sudo make install
fi
#----------------------------- libsodium end -------------------------#
+1 -1
View File
@@ -17,7 +17,7 @@ if [ ! -d ${SERVER_ROOT}/libzip ];then
cd $SOURCE_ROOT
if [ ! -f ${SOURCE_ROOT}/libzip-1.3.2.tar.gz ];then
wget -O libzip-1.3.2.tar.gz --no-check-certificate https://nih.at/libzip/libzip-1.3.2.tar.gz -T 20
wget --no-check-certificate -O libzip-1.3.2.tar.gz --no-check-certificate https://nih.at/libzip/libzip-1.3.2.tar.gz -T 20
fi
tar -zxvf libzip-1.3.2.tar.gz
+7 -1
View File
@@ -13,11 +13,17 @@ rootPath=$(dirname "$rootPath")
SERVER_ROOT=$rootPath/lib
SOURCE_ROOT=$rootPath/source/lib
cn=$(curl -fsSL -m 10 http://ipinfo.io/json | grep "\"country\": \"CN\"")
HTTP_PREFIX="https://"
if [ ! -z "$cn" ];then
HTTP_PREFIX="https://ghproxy.com/"
fi
which onig-config
if [ "$?" != "0" ];then
cd ${SOURCE_ROOT}
if [ ! -f ${SOURCE_ROOT}/oniguruma-6.9.4.tar.gz ];then
wget -O ${SOURCE_ROOT}/oniguruma-6.9.4.tar.gz https://github.com/kkos/oniguruma/archive/v6.9.4.tar.gz
wget --no-check-certificate -O ${SOURCE_ROOT}/oniguruma-6.9.4.tar.gz ${HTTP_PREFIX}github.com/kkos/oniguruma/archive/v6.9.4.tar.gz
fi
cd ${SOURCE_ROOT} && tar -zxvf oniguruma-6.9.4.tar.gz
cd ${SOURCE_ROOT}/oniguruma-6.9.4 && ./autogen.sh && ./configure --prefix=/usr && make && make install
+1 -1
View File
@@ -16,7 +16,7 @@ SOURCE_ROOT=$rootPath/source/lib
if [ ! -d ${SERVER_ROOT}/openssl ];then
cd ${SOURCE_ROOT}
if [ ! -f ${SOURCE_ROOT}/openssl-1.1.1p.tar.gz ];then
wget -O ${SOURCE_ROOT}/openssl-1.1.1p.tar.gz https://www.openssl.org/source/openssl-1.1.1p.tar.gz
wget --no-check-certificate -O ${SOURCE_ROOT}/openssl-1.1.1p.tar.gz https://www.openssl.org/source/openssl-1.1.1p.tar.gz
fi
tar -zxvf openssl-1.1.1p.tar.gz
cd openssl-1.1.1p
+7 -1
View File
@@ -13,10 +13,16 @@ rootPath=$(dirname "$rootPath")
SERVER_ROOT=$rootPath/lib
SOURCE_ROOT=$rootPath/source/lib
cn=$(curl -fsSL -m 10 http://ipinfo.io/json | grep "\"country\": \"CN\"")
HTTP_PREFIX="https://"
if [ ! -z "$cn" ];then
HTTP_PREFIX="https://ghproxy.com/"
fi
if [ ! -d ${SERVER_ROOT}/openssl10 ];then
cd ${SOURCE_ROOT}
if [ ! -f ${SOURCE_ROOT}/openssl-1.0.2q.tar.gz ];then
wget https://github.com/midoks/mdserver-web/releases/download/init/openssl-1.0.2q.tar.gz -T 20
wget --no-check-certificate ${HTTP_PREFIX}github.com/midoks/mdserver-web/releases/download/init/openssl-1.0.2q.tar.gz -T 20
fi
tar -zxf openssl-1.0.2q.tar.gz
cd openssl-1.0.2q
+7 -1
View File
@@ -13,11 +13,17 @@ rootPath=$(dirname "$rootPath")
SERVER_ROOT=$rootPath/lib
SOURCE_ROOT=$rootPath/source/lib
cn=$(curl -fsSL -m 10 http://ipinfo.io/json | grep "\"country\": \"CN\"")
HTTP_PREFIX="https://"
if [ ! -z "$cn" ];then
HTTP_PREFIX="https://ghproxy.com/"
fi
if [ ! -d ${SERVER_ROOT}/zlib ];then
cd $SOURCE_ROOT
if [ ! -f ${SOURCE_ROOT}/zlib-1.2.11.tar.gz ];then
wget -O zlib-1.2.11.tar.gz --no-check-certificate https://github.com/madler/zlib/archive/v1.2.11.tar.gz -T 20
wget --no-check-certificate -O zlib-1.2.11.tar.gz ${HTTP_PREFIX}github.com/madler/zlib/archive/v1.2.11.tar.gz -T 20
fi
tar -zxvf zlib-1.2.11.tar.gz
+2 -1
View File
@@ -106,7 +106,8 @@ if [ ! -d $serverPath/php/${PHP_VER} ];then
$OPTIONS \
--enable-fastcgi \
--enable-fpm
make ZEND_EXTRA_LIBS='-liconv' && make install && make clean
# ZEND_EXTRA_LIBS='-liconv'
make && make install && make clean
fi
if [ "$?" != "0" ];then
+3 -2
View File
@@ -47,8 +47,9 @@ Install_lib()
cd $php_lib && tar xvf ${LIBNAME}-${LIBV}.tgz
cd ${LIBNAME}-${LIBV}
$serverPath/php/$version/bin/phpize
./configure --with-php-config=$serverPath/php/$version/bin/php-config \
--with-icu-dir=${serverPath}/lib/icu
# --with-icu-dir=${serverPath}/lib/icu
./configure --with-php-config=$serverPath/php/$version/bin/php-config
make clean && make && make install && make clean
fi
+2 -3
View File
@@ -16,7 +16,7 @@ function version_lt() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)"
function version_ge() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" == "$1"; }
version=8.2.0RC7
version=8.2.0
PHP_VER=82
Install_php()
{
@@ -32,8 +32,7 @@ cd ${rootPath}/plugins/php/lib && /bin/bash zlib.sh
if [ ! -d $sourcePath/php/php${PHP_VER} ];then
if [ ! -f $sourcePath/php/php-${version}.tar.xz ];then
# wget --no-check-certificate -O $sourcePath/php/php-${version}.tar.xz https://downloads.php.net/~pierrick/php-${version}.tar.xz
wget --no-check-certificate -O $sourcePath/php/php-${version}.tar.xz https://downloads.php.net/~sergey/php-${version}.tar.xz
wget --no-check-certificate -O $sourcePath/php/php-${version}.tar.xz https://www.php.net/distributions/php-${version}.tar.xz
fi
cd $sourcePath/php && tar -Jxf $sourcePath/php/php-${version}.tar.xz
+1 -1
View File
@@ -40,7 +40,7 @@ cd $DIR
PHP_VER_LIST=(53 54 55 56 70 71 72 73 74 80 81 82)
PHP_EXT_LIST=(ioncube ZendGuardLoader pdo mysqlnd sqlite3 openssl pcntl opcache mcrypt fileinfo \
exif gd intl memcache memcached redis imagemagick xdebug xhprof \
swoole yaf yar yac apc mongo mongodb solr seaslog mbstring iconv)
swoole yaf yar yac apc mongo mongodb solr seaslog mbstring iconv phalcon)
for PHP_VER in ${PHP_VER_LIST[@]}; do
echo "php${PHP_VER} -- start"
+17 -2
View File
@@ -49,6 +49,7 @@ Install_lib()
if [ ! -f "$extFile" ];then
if [ ! -d $sourcePath/php${version}/ext ];then
echo "cd ${rootPath}/plugins/php && /bin/bash install.sh install ${version}"
cd ${rootPath}/plugins/php && /bin/bash install.sh install ${version}
fi
@@ -57,9 +58,23 @@ Install_lib()
$serverPath/php/$version/bin/phpize
./configure --with-php-config=$serverPath/php/$version/bin/php-config
# It is considered as a temporary bug
if [ "$version" == "81" ] || [ "$version" == "82" ];then
bash ${rootPath}/scripts/getos.sh
OSNAME=`cat ${rootPath}/data/osname.pl`
if [ "$OSNAME" == 'centos' ];then
FILE_softmagic=$sourcePath/php${version}/ext/${LIBNAME}/libmagic/softmagic.c
FIND_UNDEF_STRNDUP=`cat $FILE_softmagic|grep '#undef strndup'`
if [ "$version" -gt "74" ] && [ "$FIND_UNDEF_STRNDUP" == "" ];then
sed -i $BAK "s/char \*strndup/#undef strndup\nchar \*strndup/g" $FILE_softmagic
fi
fi
fi
FIND_C99=`cat Makefile|grep c99`
if [ "$FIND_C99" == "" ];then
sed -i $BAK 's/CFLAGS \=/CFLAGS \= -std=c99/g' Makefile
if [ "$version" -gt "74" ] && [ "$FIND_C99" == "" ];then
sed -i $BAK 's/CFLAGS \=/CFLAGS \= -std=gnu99/g' Makefile
fi
make clean && make && make install && make clean
+115
View File
@@ -0,0 +1,115 @@
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
curPath=`pwd`
appPath=$(dirname "$curPath")
rootPath=$(dirname "$curPath")
rootPath=$(dirname "$rootPath")
rootPath=$(dirname "$rootPath")
rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
sourcePath=${serverPath}/source/php
actionType=$1
version=$2
LIBNAME=gmp
LIBV=0
if [ "$version" == "53" ];then
echo "i wont support it"
exit
fi
LIB_PATH_NAME=lib/php
if [ -d $serverPath/php/${version}/lib64 ];then
LIB_PATH_NAME=lib64
fi
NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
sysName=`uname`
if [ "$sysName" == "Darwin" ];then
BAK='_bak'
else
BAK=''
fi
Install_lib()
{
isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
if [ "${isInstall}" != "" ];then
echo "php-$version 已安装${LIBNAME},请选择其它版本!"
return
fi
if [ ! -f "$extFile" ];then
if [ ! -d $sourcePath/php${version}/ext ];then
cd ${rootPath}/plugins/php && /bin/bash install.sh install ${version}
fi
cd $sourcePath/php${version}/ext/${LIBNAME}
$serverPath/php/$version/bin/phpize
./configure --with-php-config=$serverPath/php/$version/bin/php-config
# FIND_C99=`cat Makefile|grep c99`
# if [ "$FIND_C99" == "" ];then
# sed -i $BAK 's/CFLAGS \=/CFLAGS \= -std=c99/g' Makefile
# fi
make clean && make && make install && make clean
fi
if [ ! -f "$extFile" ];then
echo "ERROR!"
return
fi
echo "" >> $serverPath/php/$version/etc/php.ini
echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
bash ${rootPath}/plugins/php/versions/lib.sh $version restart
echo '==========================================================='
echo 'successful!'
}
Uninstall_lib()
{
if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
echo "php-$version 未安装,请选择其它版本!"
return
fi
if [ ! -f "$extFile" ];then
echo "php-$version 未安装${LIBNAME},请选择其它版本!"
return
fi
echo $serverPath/php/$version/etc/php.ini
sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
rm -f $extFile
bash ${rootPath}/plugins/php/versions/lib.sh $version restart
echo '==============================================='
echo 'successful!'
}
if [ "$actionType" == 'install' ];then
Install_lib
elif [ "$actionType" == 'uninstall' ];then
Uninstall_lib
fi
+7 -4
View File
@@ -17,10 +17,13 @@ sysName=`uname`
actionType=$1
version=$2
if [ "$version" -gt "56" ];then
echo "not need"
exit 1
fi
if [ "$version" -gt "56" ] && [ "$version" -lt "80" ];then
LIBV=4.0.5.2
fi
if [ "$version" -gt "74" ];then
LIBV=8.0
fi
LIB_PATH_NAME=lib/php
+121
View File
@@ -0,0 +1,121 @@
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
curPath=`pwd`
rootPath=$(dirname "$curPath")
rootPath=$(dirname "$rootPath")
rootPath=$(dirname "$rootPath")
rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
sourcePath=${serverPath}/source/php
actionType=$1
version=$2
LIBNAME=phalcon
LIBV=4.1.2
if [ "$version" -lt "73" ];then
echo "not support!"
exit 1
fi
if [ "$version" -gt "74" ];then
LIBV=5.1.2
fi
LIB_PATH_NAME=lib/php
if [ -d $serverPath/php/${version}/lib64 ];then
LIB_PATH_NAME=lib64
fi
NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
sysName=`uname`
if [ "$sysName" == "Darwin" ];then
BAK='_bak'
else
BAK=''
fi
Install_lib()
{
isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
if [ "${isInstall}" != "" ];then
echo "php-$version 已安装${LIBNAME},请选择其它版本!"
return
fi
if [ ! -f "$extFile" ];then
php_lib=$sourcePath/php_lib
mkdir -p $php_lib
if [ ! -f $php_lib/${LIBNAME}-${LIBV}.tgz ];then
wget -O $php_lib/${LIBNAME}-${LIBV}.tgz http://pecl.php.net/get/${LIBNAME}-${LIBV}.tgz
cd $php_lib
tar xvf ${LIBNAME}-${LIBV}.tgz
fi
cd $php_lib/${LIBNAME}-${LIBV}
$serverPath/php/$version/bin/phpize
./configure --with-php-config=$serverPath/php/$version/bin/php-config
make clean && make && make install && make clean
fi
while [[ ! -f "$extFile" ]];
do
echo -e ".\c"
sleep 0.5
if [ ! -f "$extFile" ];then
echo "ERROR!"
fi
let n+=1
if [ $n -gt 8 ];then
echo "WAIT " $n "TIMES FAIL!"
return;
fi
done
echo "" >> $serverPath/php/$version/etc/php.ini
echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
bash ${rootPath}/plugins/php/versions/lib.sh $version restart
echo '==========================================================='
echo 'successful!'
}
Uninstall_lib()
{
if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
echo "php-$version 未安装,请选择其它版本!"
return
fi
if [ ! -f "$extFile" ];then
echo "php-$version 未安装${LIBNAME},请选择其它版本!"
return
fi
echo $serverPath/php/$version/etc/php.ini
sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
rm -f $extFile
bash ${rootPath}/plugins/php/versions/lib.sh $version restart
echo '==============================================='
echo 'successful!'
}
if [ "$actionType" == 'install' ];then
Install_lib
elif [ "$actionType" == 'uninstall' ];then
Uninstall_lib
fi
+2
View File
@@ -21,6 +21,8 @@ if [ "$version" == "52" ];then
LIBV=2.2.7
elif [ "$version" -lt "70" ];then
LIBV=4.2.0
elif [ "$version" -gt "74" ];then
LIBV=5.3.5
else
echo 'ok'
fi
+106
View File
@@ -0,0 +1,106 @@
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
curPath=`pwd`
rootPath=$(dirname "$curPath")
rootPath=$(dirname "$rootPath")
rootPath=$(dirname "$rootPath")
rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
sourcePath=${serverPath}/source/php
actionType=$1
version=$2
sysName=`uname`
LIBNAME=sodium
LIBV=2.0.23
LIB_PATH_NAME=lib/php
if [ -d $serverPath/php/${version}/lib64 ];then
LIB_PATH_NAME=lib64
fi
NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
if [ "$sysName" == "Darwin" ];then
BAK='_bak'
else
BAK=''
fi
Install_lib()
{
isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
if [ "${isInstall}" != "" ];then
echo "php-$version 已安装${LIBNAME},请选择其它版本!"
return
fi
cd ${rootPath}/plugins/php/lib && bash libsodium.sh
if [ ! -f "$extFile" ];then
php_lib=$sourcePath/php_lib
mkdir -p $php_lib
if [ ! -d $php_lib/${LIBNAME}-${LIBV} ];then
wget -O $php_lib/${LIBNAME}-${LIBV}.tgz http://pecl.php.net/get/lib${LIBNAME}-${LIBV}.tgz
cd $php_lib && tar xvf ${LIBNAME}-${LIBV}.tgz
fi
cd $php_lib/lib${LIBNAME}-${LIBV}
$serverPath/php/$version/bin/phpize
./configure --with-php-config=$serverPath/php/$version/bin/php-config --with-sodium=$serverPath/lib/libsodium
make clean && make && make install && make clean
fi
if [ ! -f "$extFile" ];then
echo "ERROR!"
return;
fi
echo "" >> $serverPath/php/$version/etc/php.ini
echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
bash ${rootPath}/plugins/php/versions/lib.sh $version restart
echo '==========================================================='
echo 'successful!'
}
Uninstall_lib()
{
if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
echo "php-$version 未安装,请选择其它版本!"
return
fi
if [ ! -f "$extFile" ];then
echo "php-$version 未安装${LIBNAME},请选择其它版本!"
return
fi
echo $serverPath/php/$version/etc/php.ini
sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
sed -i $BAK "/\[${LIBNAME}\]/d" $serverPath/php/$version/etc/php.ini
rm -rf $extFile
bash ${rootPath}/plugins/php/versions/lib.sh $version restart
echo '==============================================='
echo 'successful!'
}
if [ "$actionType" == 'install' ];then
Install_lib
elif [ "$actionType" == 'uninstall' ];then
Uninstall_lib
fi
+5 -8
View File
@@ -19,17 +19,14 @@ LIBV=4.8.10
if [ "$version" -lt "70" ];then
LIBV=1.10.1
elif [ "$version" == "71" ];then
LIBV=4.5.2
elif [ "$version" == "70" ];then
LIBV=4.3.0
elif [ "$version" == "71" ];then
LIBV=4.5.2
elif [ "$version" -gt "74" ];then
LIBV=5.0.1
else
echo '72+'
fi
if [ "$version" -eq "82" ];then
echo "not need"
exit 1
echo 'other?'
fi
LIB_PATH_NAME=lib/php
-5
View File
@@ -31,11 +31,6 @@ if [ "$version" -eq "72" ] || [ "$version" -eq "73" ];then
LIBV=3.2.5
fi
if [ "$version" -eq "82" ];then
echo "not need"
exit 1
fi
LIB_PATH_NAME=lib/php
if [ -d $serverPath/php/${version}/lib64 ];then
-6
View File
@@ -21,12 +21,6 @@ if [ "$version" -lt "70" ];then
LIBV=1.2.5
fi
if [ "$version" -eq "82" ];then
echo "wait php82 release"
exit 0
fi
LIB_PATH_NAME=lib/php
if [ -d $serverPath/php/${version}/lib64 ];then
LIB_PATH_NAME=lib64
+58 -5
View File
@@ -62,6 +62,40 @@
"shell": "ioncube.sh",
"check": "ioncube.so"
},
{
"name": "sodium",
"versions": [
"70",
"71",
"72",
"73",
"74",
"80",
"81",
"82"
],
"type": "加密库",
"msg": "Sodium加密库的包装器",
"shell": "sodium.sh",
"check": "sodium.so"
},
{
"name": "gmp",
"versions": [
"70",
"71",
"72",
"73",
"74",
"80",
"81",
"82"
],
"type": "运算库",
"msg": "一个开源的数学运算库",
"shell": "gmp.sh",
"check": "gmp.so"
},
{
"name": "opcache",
"versions": [
@@ -314,7 +348,15 @@
"53",
"54",
"55",
"56"
"56",
"70",
"71",
"72",
"73",
"74",
"80",
"81",
"82"
],
"type": "缓存器",
"msg": "强大的内容缓存器,不支持集群",
@@ -486,6 +528,19 @@
"shell": "eaccelerator.sh",
"check": "eaccelerator.so"
},
{
"name": "phalcon",
"versions": [
"73",
"74",
"80",
"81"
],
"type": "框架",
"msg": "Phalcon是一个全栈PHP框架。",
"shell": "phalcon.sh",
"check": "phalcon.so"
},
{
"name": "yaf",
"versions": [
@@ -499,8 +554,7 @@
"73",
"74",
"80",
"81",
"82"
"81"
],
"type": "框架",
"msg": "Yaf是一个C语言编写的PHP框架",
@@ -520,8 +574,7 @@
"73",
"74",
"80",
"81",
"82"
"81"
],
"type": "框架",
"msg": "Yar是一个RPC框架",
-2
View File
@@ -82,9 +82,7 @@ def getHomePage():
def getPhpVer(expect=55):
import json
v = site_api.site_api().getPhpVersion()
v = json.loads(v)
is_find = False
for i in range(len(v)):
t = int(v[i]['version'])
+27
View File
@@ -1,3 +1,30 @@
<style type="text/css">
.db_list{
padding: 0px 0 10px 0;
font-size: 13px;
line-height: 35px;
height: 45px;
}
.db_list a{
margin-right: 15px;
color: #555;
font-weight: 600;
}
.db_list span:nth-child(1){
display: block;
float: left;
padding-left: 15px;
background: #ececec;
border-radius: 3px;
}
#db_tools button {
margin-right: 10px;
}
#db_tools button:last-child{
margin-right: 0;
}
</style>
<div class="bt-form">
<div class='plugin_version'></div>
<div class="bt-w-main">
+9 -12
View File
@@ -70,12 +70,10 @@ def getArgs():
def getBackupDir():
bk_path = mw.getBackupDir() + "/pg"
bk_path = mw.getBackupDir() + "/database/postgresql"
if not os.path.isdir(bk_path):
mw.execShell("mkdir -p {}/upload".format(bk_path))
bk_path_upload = bk_path + "/upload"
return bk_path_upload
mw.execShell("mkdir -p {}".format(bk_path))
return bk_path
def checkArgs(data, ck=[]):
@@ -674,7 +672,7 @@ def setUserPwd(version=''):
def getDbBackupListFunc(dbname=''):
bkDir = mw.getRootDir() + '/backup/database'
bkDir = getBackupDir()
blist = os.listdir(bkDir)
r = []
@@ -708,11 +706,11 @@ def importDbBackup():
file = args['file']
name = args['name']
file_path = mw.getRootDir() + '/backup/database/' + file
file_path_sql = mw.getRootDir() + '/backup/database/' + file.replace('.gz', '')
file_path = getBackupDir() + '/' + file
file_path_sql = getBackupDir() + '/' + file.replace('.gz', '')
if not os.path.exists(file_path_sql):
cmd = 'cd ' + mw.getRootDir() + '/backup/database && gzip -d ' + file
cmd = 'cd ' + getBackupDir() + ' && gzip -d ' + file
mw.execShell(cmd)
pwd = pSqliteDb('config').where('id=?', (1,)).getField('pg_root')
@@ -731,8 +729,7 @@ def deleteDbBackup():
if not data[0]:
return data[1]
bkDir = mw.getRootDir() + '/backup/database'
bkDir = getBackupDir()
os.remove(bkDir + '/' + args['filename'])
return mw.returnJson(True, 'ok')
@@ -744,7 +741,7 @@ def getDbBackupList():
return data[1]
r = getDbBackupListFunc(args['name'])
bkDir = mw.getRootDir() + '/backup/database'
bkDir = getBackupDir()
rr = []
for x in range(0, len(r)):
p = bkDir + '/' + r[x]
+1
View File
@@ -1,4 +1,5 @@
{
"hook":["database"],
"title":"PostgreSQL",
"tip":"soft",
"name":"postgresql",
-13
View File
@@ -259,19 +259,6 @@ function showHidePass(obj){
}
}
function copyPass(password){
var clipboard = new ClipboardJS('#bt_copys');
clipboard.on('success', function (e) {
layer.msg('复制成功',{icon:1,time:2000});
});
clipboard.on('error', function (e) {
layer.msg('复制失败,浏览器不兼容!',{icon:2,time:2000});
});
$("#bt_copys").attr('data-clipboard-text',password);
$("#bt_copys").click();
}
function checkSelect(){
setTimeout(function () {
var num = $('input[type="checkbox"].check:checked').length;
+1
View File
@@ -17,6 +17,7 @@ AntiWarez yes
Bind 0.0.0.0,21
Umask 133:022
MinUID 100
PassivePortRange 39000 40000
AllowUserFXP no
AllowAnonymousFXP no
ProhibitDotFilesWrite no
+17
View File
@@ -81,6 +81,22 @@ def contentReplace(content):
return content
def ftp_release_port(port):
from collections import namedtuple
try:
import firewall_api
firewall_api.firewall_api().addAcceptPortArgs(port, 'pure-ftpd', 'port')
return port
except Exception as e:
return "Release failed {}".format(e)
def openFtpPort():
for i in ["21", "39000:40000"]:
ftp_release_port(i)
return True
def initDreplace():
file_tpl = getInitDTpl()
@@ -89,6 +105,7 @@ def initDreplace():
initD_path = getServerDir() + '/init.d'
if not os.path.exists(initD_path):
os.mkdir(initD_path)
openFtpPort()
file_bin = initD_path + '/' + getPluginName()
# initd replace
+2 -2
View File
@@ -53,8 +53,8 @@ Install_pureftp()
# curl -sSLo pure-ftpd-1.0.49.tar.gz https://download.pureftpd.org/pub/pure-ftpd/releases/pure-ftpd-1.0.49.tar.gz
if [ ! -f $serverPath/source/pureftp/pure-ftpd-${VER}.tar.gz ];then
wget --no-check-certificate -O $serverPath/source/pureftp/pure-ftpd-${VER}.tar.gz $DOWNLOAD
# curl -sSLo $serverPath/source/pureftp/pure-ftpd-${VER}.tar.gz $DOWNLOAD
# wget --no-check-certificate -O $serverPath/source/pureftp/pure-ftpd-${VER}.tar.gz $DOWNLOAD
curl -sSLo $serverPath/source/pureftp/pure-ftpd-${VER}.tar.gz $DOWNLOAD
fi
#检测文件是否损坏.
+4
View File
@@ -108,6 +108,10 @@ Install_App()
echo '安装完成' > $install_tmp
cd $rootPath && python3 ${rootPath}/plugins/webstats/index.py start
# delete install data
rm -rf $serverPath/source/webstats
}
Uninstall_App()
-1
View File
@@ -71,7 +71,6 @@ def getHomePage():
def getPhpVer(expect=56):
import json
v = site_api.site_api().getPhpVersion()
v = json.loads(v)
for i in range(len(v)):
t = int(v[i]['version'])
if (t >= expect):
+2 -1
View File
@@ -1,6 +1,6 @@
setuptools>=33.1.1
Werkzeug>=1.0.1
wheel==0.33.6
wheel==0.37.1
gunicorn==20.1.0
requests==2.27.1
urllib3>=1.21.1
@@ -27,3 +27,4 @@ Jinja2>=2.11.2
flask-caching>=1.10.1
bcrypt==3.1.3
PyMySQL==1.0.2
whitenoise==5.3.0
+16
View File
@@ -0,0 +1,16 @@
location / {
set $replace 'tmplate_replace';
set $spider_request '0';
if ($http_user_agent ~* '(baiduspider|360sipder|Sogou Orion spider|Sogou News Spider|Sogou blog|Sogou spider2|Sogou inst spider|Sogou web spider|Sogou spider|trendiction|Yahoo|semrush|Toutiao|Google|qihoobot|Googlebot|Googlebot-Mobile|Googlebot-Image|Mediapartners-Google|Adsbot-Google|Feedfetcher-Google|Yahoo! Slurp|Yahoo! Slurp China|YoudaoBot|Sosospider|MSNBot|ia_archiver|Tomato Bot)') {
set $spider_request '1';
}
if ($spider_request = '0' ) {
set $replace 'tmplate_replace';
}
sub_filter 'tmplate_replace' $replace;
sub_filter_once on;
sub_filter_types *;
}
+21 -7
View File
@@ -27,6 +27,8 @@ from flask import render_template_string, abort
from flask_caching import Cache
from flask_session import Session
from whitenoise import WhiteNoise
sys.path.append(os.getcwd() + "/class/core")
import db
@@ -36,6 +38,9 @@ import config_api
app = Flask(__name__, template_folder='templates/default')
app.config.version = config_api.config_api().getVersion()
app.wsgi_app = WhiteNoise(
app.wsgi_app, root="route/static/", prefix="static/", max_age=604800)
cache = Cache(config={'CACHE_TYPE': 'simple'})
cache.init_app(app, config={'CACHE_TYPE': 'simple'})
@@ -186,11 +191,11 @@ def isLogined():
return True
if os.path.exists('data/api_login.txt'):
content = mw.readFile('data/api_login.txt')
session['login'] = True
session['username'] = content
os.remove('data/api_login.txt')
# if os.path.exists('data/api_login.txt'):
# content = mw.readFile('data/api_login.txt')
# session['login'] = True
# session['username'] = content
# os.remove('data/api_login.txt')
return False
@@ -218,9 +223,18 @@ def publicObject(toObject, func, action=None, get=None):
# print(os)
# return mw.getLocalIp()
# 仅针对webhook插件
@app.route("/.well-known/acme-challenge/<val>")
def wellknow(val=None):
# 申请面板ssl使用
f = mw.getRunDir() + "/tmp/.well-known/acme-challenge/" + val
if os.path.exists(f):
return mw.readFile(f)
return ''
@app.route("/hook")
def webhook():
# 仅针对webhook插件
input_args = {
'access_key': request.args.get('access_key', '').strip(),
'params': request.args.get('params', '').strip()
@@ -336,7 +350,7 @@ def doLogin():
# session['overdue'] = int(time.time()) + 7
# fix 跳转时,数据消失,可能是跨域问题
mw.writeFile('data/api_login.txt', userInfo['username'])
# mw.writeFile('data/api_login.txt', userInfo['username'])
return mw.returnJson(True, '登录成功,正在跳转...')
+69 -24
View File
@@ -68,7 +68,6 @@ $('input[name="backup_path"]').change(function(){
$('input[name="bind_domain"]').change(function(){
var domain = $(this).val();
console.log(domain);
$('.btn_bind_domain').removeAttr('disabled');
$('.btn_bind_domain').unbind().click(function(){
$.post('/config/set_panel_domain','domain='+domain, function(rdata){
@@ -77,6 +76,13 @@ $('input[name="bind_domain"]').change(function(){
});
});
$('input[name="bind_ssl"]').click(function(){
var open_ssl = $(this).prop("checked");
$.post('/config/set_panel_ssl',{}, function(rdata){
showMsg(rdata.msg,function(){window.location.reload();},{icon:rdata.status?1:2},2000);
},'json');
});
/** op **/
@@ -353,16 +359,33 @@ function getPanelSSL(){
var loadT = layer.msg('正在获取证书信息...',{icon:16,time:0,shade: [0.3, '#000']});
$.post('/config/get_panel_ssl',{},function(cert){
layer.close(loadT);
var cert_data = '';
if (cert['info']){
cert_data = "<div class='ssl_state_info'><div class='state_info_flex'>\
<div class='state_item'><span>证书品牌:</span><span class='ellipsis_text ssl_issue'>"+cert['info']['issuer']+"</span></div>\
<div class='state_item'><span>到期时间:</span><span class='btlink ssl_endtime'>剩余"+cert['info']['endtime']+"天到期</span></div>\
</div>\
<div class='state_info_flex'>\
<div class='state_item'><span>认证域名:</span><span class='ellipsis_text ssl_subject'>"+cert['info']['subject']+"</span></div>\
</div></div>";
}
var certBody = '<div class="tab-con">\
<div class="myKeyCon ptb15">\
<div class="ssl-con-key pull-left mr20">密钥(KEY)<br>\
<textarea id="key" class="bt-input-text">'+cert.privateKey+'</textarea>\
</div>\
<div class="ssl-con-key pull-left">证书(PEM格式)<br>\
<textarea id="csr" class="bt-input-text">'+cert.certPem+'</textarea>\
'+cert_data+'\
<div class="custom_certificate_info">\
<div class="ssl-con-key pull-left mr20">密钥(KEY)<br>\
<textarea id="key" class="bt-input-text">'+cert.privateKey+'</textarea>\
</div>\
<div class="ssl-con-key pull-left">证书(PEM格式)<br>\
<textarea id="csr" class="bt-input-text">'+cert.certPem+'</textarea>\
</div>\
</div>\
<div class="ssl-btn pull-left mtb15" style="width:100%">\
<button class="btn btn-success btn-sm" onclick="savePanelSSL()">保存</button>\
<button class="btn btn-success btn-sm save-panel-ssl">保存</button>\
<button class="btn btn-success btn-sm apply-lets-ssl">申请Lets证书</button>\
</div>\
</div>\
<ul class="help-info-text c7 pull-left">\
@@ -377,28 +400,50 @@ function getPanelSSL(){
closeBtn: 1,
shift: 5,
shadeClose: false,
content:certBody
content:certBody,
success:function(layero, layer_id){
//保存SSL
$('.save-panel-ssl').click(function(){
var data = {
privateKey:$("#key").val(),
certPem:$("#csr").val()
}
var loadT = layer.msg('正在安装并设置SSL组件,这需要几分钟时间...',{icon:16,time:0,shade: [0.3, '#000']});
$.post('/config/save_panel_ssl',data,function(rdata){
layer.close(loadT);
if(rdata.status){
layer.closeAll();
}
layer.msg(rdata.msg,{icon:rdata.status?1:2});
},'json');
});
//申请Lets证书
$('.apply-lets-ssl').click(function(){
showSpeedWindow('正在申请...', 'site.get_let_logs', function(layers,index){
$.post('/config/apply_panel_let_ssl',{},function(rdata){
layer.close(loadT);
if(rdata.status){
layer.close(index);
var tdata = rdata['data'];
$('.ssl_issue').text(tdata['info']['issuer']);
$('.ssl_endtime').text("剩余"+tdata['info']['endtime']+"天到期");
$('.ssl_subject').text(tdata['info']['subject']);
$('textarea[name="key"]').val(tdata['info']['privateKey']);
$('textarea[name="csr"]').val(tdata['info']['certPem']);
}
layer.msg(rdata.msg,{icon:rdata.status?1:2});
},'json');
});
});
}
});
},'json');
}
function savePanelSSL(){
var data = {
privateKey:$("#key").val(),
certPem:$("#csr").val()
}
var loadT = layer.msg('正在安装并设置SSL组件,这需要几分钟时间...',{icon:16,time:0,shade: [0.3, '#000']});
$.post('/config/save_panel_ssl',data,function(rdata){
layer.close(loadT);
if(rdata.status){
layer.closeAll();
}
layer.msg(rdata.msg,{icon:rdata.status?1:2});
},'json');
}
function removeTempAccess(id){
$.post('/config/remove_temp_login', {id:id}, function(rdata){
showMsg(rdata.msg, function(){
+263 -196
View File
@@ -1,13 +1,3 @@
function isURL(str_url){
var strRegex = '^(https|http|ftp|rtsp|mms)?://.+';
var re=new RegExp(strRegex);
if (re.test(str_url)){
return (true);
}else{
return (false);
}
}
var num = 0;
//查看任务日志
function getLogs(id){
@@ -40,6 +30,16 @@ function getLogs(id){
},'json');
}
function getBackupName(hook_data, name){
for (var i = 0; i < hook_data.length; i++) {
if (hook_data[i]['name'] == 'backup_ftp'){
return hook_data[i]['title'];
}
}
return name;
}
function getCronData(page){
var load = layer.msg(lan.public.the,{icon:16,time:0,shade: [0.3, '#000']});
$.post("/crontab/list?p="+page,'', function(rdata){
@@ -61,10 +61,10 @@ function getCronData(page){
}
var cron_backupto = '-';
if (rdata.data[i]['stype'] == 'site' || rdata.data[i]['stype']=='database' ){
if (rdata.data[i]['stype'] == 'site' || rdata.data[i]['stype']=='logs' || rdata.data[i]['stype']=='path' || rdata.data[i]['stype']=='database' || rdata.data[i]['stype'].indexOf('database_')>-1 ){
cron_backupto = '本地磁盘';
if (rdata.data[i]['backup_to'] != 'localhost'){
cron_backupto = rdata.data[i]['backup_to'];
cron_backupto = getBackupName(rdata['backup_hook'],rdata.data[i]['backup_to']);
}
}
@@ -228,19 +228,21 @@ function planAdd(){
var sType = $(".planjs").find("b").attr("val");
var sBody = encodeURIComponent($("#implement textarea[name='sBody']").val());
if(sType == 'toFile'){
if($("#viewfile").val() == ''){
layer.msg('请选择脚本文件!',{icon:2});
return;
}
} else {
if (sType == 'toShell'){
if(sBody == ''){
$("#implement textarea[name='sBody']").focus();
layer.msg('脚本代码不能为空!',{icon:2});
return;
}
}
if(sType == 'toFile'){
if($("#viewfile").val() == ''){
layer.msg('请选择脚本文件!',{icon:2});
return;
}
}
var urladdress = $("#urladdress").val();
if(sType == 'toUrl'){
@@ -295,7 +297,6 @@ function planAdd(){
$("#set-Config input[name='sName']").val(sName);
layer.msg('正在添加,请稍候...!',{icon:16,time:0,shade: [0.3, '#000']});
var data = $("#set-Config").serialize() + '&sBody='+sBody + '&urladdress=' + urladdress;
// console.log(data);
$.post('/crontab/add',data,function(rdata){
if(!rdata.status) {
layer.msg(rdata.msg,{icon:2, time:2000});
@@ -413,10 +414,18 @@ function initDropdownMenu(){
toBackup('sites');
$(".controls").html('备份网站');
break;
case 'database_mariadb':
case 'database_postgresql':
case 'database_mysql-apt':
case 'database_mysql-yum':
case 'database':
toBackup('databases');
toBackup(type);
$(".controls").html('备份数据库');
break;
case 'path':
toBackup('path');
$(".controls").html('备份目录');
break;
case 'logs':
toBackup('logs');
$(".controls").html('切割网站');
@@ -438,49 +447,72 @@ function toBackup(type){
sMsg = '备份网站';
sType = "sites";
break;
case 'databases':
case 'database_mariadb':
case 'database_postgresql':
case 'database_mysql-apt':
case 'database_mysql-yum':
case 'database':
sMsg = '备份数据库';
sType = "databases";
suffix = type.replace('database','')
if (suffix != ''){
suffix = suffix.replace('_','')
sMsg = '备份数据库['+suffix+']';
}
sType = type;
break;
case 'logs':
sMsg = '切割日志';
sType = "sites";
sType = "logs";
break;
case 'path':
sMsg = '备份目录';
sType = "path";
break;
}
var data='type='+sType
var data = 'type='+sType;
$.post('/crontab/get_data_list',data,function(rdata){
$(".planname input[name='name']").attr('readonly','true').css({"background-color":"#f6f6f6","color":"#666"});
var sOpt = "";
if(rdata.data.length == 0){
layer.msg(lan.public.list_empty,{icon:2})
return
return;
}
for(var i=0;i<rdata.data.length;i++){
if(i==0){
$(".planname input[name='name']").val(sMsg+'['+rdata.data[i].name+']');
}
sOpt += '<li><a role="menuitem" tabindex="-1" href="javascript:;" value="'+rdata.data[i].name+'">'+rdata.data[i].name+'['+rdata.data[i].ps+']</a></li>';
}
if (sType != 'path'){
sOpt = '<li><a role="menuitem" tabindex="-1" href="javascript:;" value="backupAll">所有</a></li>' + sOpt;
}
var orderOpt = '';
for (var i=0;i<rdata.orderOpt.length;i++){
orderOpt += '<li><a role="menuitem" tabindex="-1" href="javascript:;" value="'+rdata.orderOpt[i].name+'">'+rdata.orderOpt[i].title+'</a></li>'
}
var changeDir = '';
if (sType == 'path'){
changeDir = '<span class="glyphicon glyphicon-folder-open cursor mr20 changePathDir" style="float:left;line-height: 30px;"></span>';
}
var sBody = '<div class="dropdown pull-left mr20">\
<button class="btn btn-default dropdown-toggle" type="button" id="backdata" data-toggle="dropdown" style="width:auto">\
var sBody = '<div class="dropdown pull-left mr20 check">\
<button class="btn btn-default dropdown-toggle sname" type="button" id="backdata" data-toggle="dropdown" style="width:auto">\
<b id="sName" val="'+rdata.data[0].name+'">'+rdata.data[0].name+'['+rdata.data[0].ps+']</b> <span class="caret"></span>\
</button>\
<ul class="dropdown-menu" role="menu" aria-labelledby="backdata">\
<li><a role="menuitem" tabindex="-1" href="javascript:;" value="backupAll">所有</a></li>\
'+sOpt+'\
</ul>\
<ul class="dropdown-menu" role="menu" aria-labelledby="backdata">'+sOpt+'</ul>\
</div>\
'+ changeDir +'\
<div class="textname pull-left mr20">备份到</div>\
<div class="dropdown planBackupTo pull-left mr20">\
<button class="btn btn-default dropdown-toggle" type="button" id="excode" data-toggle="dropdown" style="width:auto;">\
<b val="localhost">服务器磁盘</b> <span class="caret"></span>\
<b val="localhost">服务器磁盘</b><span class="caret"></span>\
</button>\
<ul class="dropdown-menu" role="menu" aria-labelledby="excode">\
<li><a role="menuitem" tabindex="-1" href="javascript:;" value="localhost">服务器磁盘</a></li>\
@@ -493,12 +525,22 @@ function toBackup(type){
</div>';
$("#implement").html(sBody);
getselectname();
$('.changePathDir').click(function(){
changePathCallback($('#sName').val(),function(select_dir){
$(".planname input[name='name']").val('备份目录['+select_dir+']');
$('#implement .sname b').attr('val',select_dir).text(select_dir);
});
});
$(".dropdown ul li a").click(function(){
var sName = $("#sName").attr("val");
if(!sName) return;
$(".planname input[name='name']").val(sMsg+'['+sName+']');
});
},'json');
}
@@ -531,11 +573,17 @@ function editTaskInfo(id){
sNameArray:[],
backupsArray:[],
create:function(callback){
for(var i = 0; i <obj['sTypeArray'].length; i++){
if(obj.from['stype'] == obj['sTypeArray'][i][0]){
sTypeName = obj['sTypeArray'][i][1];
if (obj.from['stype'].indexOf('database_')>-1){
name = obj.from['stype'].replace('database_','');
sTypeName = '备份数据库['+name+']';
sTypeDom += '<li><a role="menuitem" href="javascript:;" value="'+ obj.from['stype'] +'">'+ sTypeName +'</a></li>';
} else {
for(var i = 0; i <obj['sTypeArray'].length; i++){
if(obj.from['stype'] == obj['sTypeArray'][i][0]){
sTypeName = obj['sTypeArray'][i][1];
}
sTypeDom += '<li><a role="menuitem" href="javascript:;" value="'+ obj['sTypeArray'][i][0] +'">'+ obj['sTypeArray'][i][1] +'</a></li>';
}
sTypeDom += '<li><a role="menuitem" href="javascript:;" value="'+ obj['sTypeArray'][i][0] +'">'+ obj['sTypeArray'][i][1] +'</a></li>';
}
for(var i = 0; i <obj['cycleArray'].length; i++){
@@ -548,8 +596,8 @@ function editTaskInfo(id){
weekDom += '<li><a role="menuitem" href="javascript:;" value="'+ obj['weekArray'][i][0] +'">'+ obj['weekArray'][i][1] +'</a></li>';
}
if(obj.from.stype == 'site' || obj.from.stype == 'database' || obj.from.stype == 'path' || obj.from.stype == 'logs'){
$.post('/crontab/get_data_list',{type:obj.from.stype == 'databases'?'database':'sites'},function(rdata){
if(obj.from.stype == 'site' || obj.from.stype == 'database' || obj.from.stype == 'path' || obj.from.stype == 'logs' || obj.from['stype'].indexOf('database_')>-1){
$.post('/crontab/get_data_list',{type:obj.from.stype},function(rdata){
// console.log(rdata);
obj.sNameArray = rdata.data;
obj.sNameArray.unshift({name:'ALL',ps:'所有'});
@@ -575,10 +623,16 @@ function editTaskInfo(id){
}
};
obj.create(function(){
var changeDir = '';
if (obj.from.stype == 'path'){
changeDir = '<span class="glyphicon glyphicon-folder-open cursor mr20 changePathDir" style="float:left;line-height: 30px;"></span>';
}
layer.open({
type:1,
title:'编辑计划任务-['+rdata.name+']',
area: ['850px','450px'],
area: ['850px','440px'],
skin:'layer-create-content',
shadeClose:false,
closeBtn:1,
@@ -622,7 +676,7 @@ function editTaskInfo(id){
<div class="clearfix plan ptb10 site_list" style="display:none">\
<span class="typename controls c4 pull-left f14 text-right mr20">'+ sTypeName +'</span>\
<div style="line-height:34px"><div class="dropdown pull-left mr20 sName_btn" style="display:'+ (obj.from.sType != "path"?'block;':'none') +'">\
<button class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown" style="width:auto" disabled="disabled">\
<button class="btn btn-default dropdown-toggle sname" type="button" data-toggle="dropdown" style="width:auto" disabled="disabled">\
<b id="sName" val="'+ obj.from.sname +'">'+ obj.from.sname +'</b>\
<span class="caret"></span>\
</button>\
@@ -631,6 +685,7 @@ function editTaskInfo(id){
<div class="info-r" style="float: left;margin-right: 25px;display:'+ (obj.from.sType == "path"?'block;':'none') +'">\
<input id="inputPath" class="bt-input-text mr5 " type="text" name="path" value="'+ obj.from.sName +'" placeholder="备份目录" style="width:208px;height:33px;" disabled="disabled">\
</div>\
'+changeDir+'\
<div class="textname pull-left mr20">备份到</div>\
<div class="dropdown pull-left mr20">\
<button class="btn btn-default dropdown-toggle backup_btn" type="button" data-toggle="dropdown" style="width:auto;">\
@@ -660,165 +715,177 @@ function editTaskInfo(id){
<div class="clearfix plan ptb10">\
<div class="bt-submit plan-submits " style="margin-left: 141px;">保存编辑</div>\
</div>\
</div>'
</div>',
success:function(){
$('.changePathDir').click(function(){
changePathCallback($('#sName').val(),function(select_dir){
$('input[name="name"]').val('备份目录['+select_dir+']');
$('.sName_btn .sname b').attr('val',select_dir).text(select_dir);
obj.from.sname = select_dir;
});
});
if(obj.from.stype == 'toShell'){
$('.site_list').hide();
} else if (obj.from.stype == 'rememory') {
$('.site_list').hide();
} else if ( obj.from.stype == 'toUrl'){
$('.site_list').hide();
} else {
$('.site_list').show();
}
obj.from.minute = $('.minute_create').val();
obj.from.hour = $('.hour_create').val();
obj.from.where1 = $('.where1_create').val();
$('.sName_create').blur(function () {
obj.from.name = $(this).val();
});
$('.where1_create').blur(function () {
obj.from.where1 = $(this).val();
});
$('.hour_create').blur(function () {
obj.from.hour = $(this).val();
});
$('.minute_create').blur(function () {
obj.from.minute = $(this).val();
});
$('.save_create').blur(function () {
obj.from.save = $(this).val();
});
$('.sBody_create').blur(function () {
obj.from.sbody = $(this).val();
});
$('.url_create').blur(function () {
obj.from.urladdress = $(this).val();
});
$('[aria-labelledby="cycle"] a').unbind().click(function () {
$('.cycle_btn').find('b').attr('val',$(this).attr('value')).html($(this).html());
var type = $(this).attr('value');
switch(type){
case 'day':
$('.week_btn').hide();
$('.where1_input').hide();
$('.hour_input').show().find('input').val('1');
$('.minute_input').show().find('input').val('30');
obj.from.week = '';
obj.from.type = '';
obj.from.hour = 1;
obj.from.minute = 30;
break;
case 'day-n':
$('.week_btn').hide();
$('.where1_input').show().find('input').val('1');
$('.hour_input').show().find('input').val('1');
$('.minute_input').show().find('input').val('30');
obj.from.week = '';
obj.from.where1 = 1;
obj.from.hour = 1;
obj.from.minute = 30;
break;
case 'hour':
$('.week_btn').hide();
$('.where1_input').hide();
$('.hour_input').hide();
$('.minute_input').show().find('input').val('30');
obj.from.week = '';
obj.from.where1 = '';
obj.from.hour = '';
obj.from.minute = 30;
break;
case 'hour-n':
$('.week_btn').hide();
$('.where1_input').hide();
$('.hour_input').show().find('input').val('1');
$('.minute_input').show().find('input').val('30');
obj.from.week = '';
obj.from.where1 = '';
obj.from.hour = 1;
obj.from.minute = 30;
break;
case 'minute-n':
$('.week_btn').hide();
$('.where1_input').hide();
$('.hour_input').hide();
$('.minute_input').show();
obj.from.week = '';
obj.from.where1 = '';
obj.from.hour = '';
obj.from.minute = 30;
console.log(obj.from);
break;
case 'week':
$('.week_btn').show();
$('.where1_input').hide();
$('.hour_input').show();
$('.minute_input').show();
obj.from.week = 1;
obj.from.where1 = '';
obj.from.hour = 1;
obj.from.minute = 30;
break;
case 'month':
$('.week_btn').hide();
$('.where1_input').show();
$('.hour_input').show();
$('.minute_input').show();
obj.from.week = '';
obj.from.where1 = 1;
obj.from.hour = 1;
obj.from.minute = 30;
break;
}
obj.from.type = $(this).attr('value');
});
$('[aria-labelledby="week"] a').unbind().click(function () {
$('.week_btn').find('b').attr('val',$(this).attr('value')).html($(this).html());
obj.from.week = $(this).attr('value');
});
$('[aria-labelledby="backupTo"] a').unbind().click(function () {
$('.backup_btn').find('b').attr('val',$(this).attr('value')).html($(this).html());
obj.from.backup_to = $(this).attr('value');
});
$('.plan-submits').unbind().click(function(){
if(obj.from.type == 'hour-n'){
obj.from.where1 = obj.from.hour;
obj.from.hour = '';
} else if(obj.from.type == 'minute-n') {
obj.from.where1 = obj.from.minute;
obj.from.minute = '';
}
var loadT = layer.msg('正在保存编辑内容,请稍后...',{icon:16,time:0,shade: [0.3, '#000']});
$.post('/crontab/modify_crond',obj.from,function(rdata){
if (!rdata.status){
layer.msg(rdata.msg,{icon:rdata.status?1:2});
return;
}
showMsg(rdata.msg, function(){
layer.closeAll();
getCronData(1);
initDropdownMenu();
},{icon:rdata.status?1:2}, 2000);
},'json');
});
}
,cancel: function(){
initDropdownMenu();
}
});
setTimeout(function(){
if(obj.from.stype == 'toShell'){
$('.site_list').hide();
}else if(obj.from.stype == 'rememory'){
$('.site_list').hide();
}else if( obj.from.stype == 'toUrl'){
$('.site_list').hide();
}else{
$('.site_list').show();
}
obj.from.minute = $('.minute_create').val();
obj.from.hour = $('.hour_create').val();
obj.from.where1 = $('.where1_create').val();
$('.sName_create').blur(function () {
obj.from.name = $(this).val();
});
$('.where1_create').blur(function () {
obj.from.where1 = $(this).val();
});
$('.hour_create').blur(function () {
obj.from.hour = $(this).val();
});
$('.minute_create').blur(function () {
obj.from.minute = $(this).val();
});
$('.save_create').blur(function () {
obj.from.save = $(this).val();
});
$('.sBody_create').blur(function () {
obj.from.sbody = $(this).val();
});
$('.url_create').blur(function () {
obj.from.urladdress = $(this).val();
});
$('[aria-labelledby="cycle"] a').unbind().click(function () {
$('.cycle_btn').find('b').attr('val',$(this).attr('value')).html($(this).html());
var type = $(this).attr('value');
switch(type){
case 'day':
$('.week_btn').hide();
$('.where1_input').hide();
$('.hour_input').show().find('input').val('1');
$('.minute_input').show().find('input').val('30');
obj.from.week = '';
obj.from.type = '';
obj.from.hour = 1;
obj.from.minute = 30;
break;
case 'day-n':
$('.week_btn').hide();
$('.where1_input').show().find('input').val('1');
$('.hour_input').show().find('input').val('1');
$('.minute_input').show().find('input').val('30');
obj.from.week = '';
obj.from.where1 = 1;
obj.from.hour = 1;
obj.from.minute = 30;
break;
case 'hour':
$('.week_btn').hide();
$('.where1_input').hide();
$('.hour_input').hide();
$('.minute_input').show().find('input').val('30');
obj.from.week = '';
obj.from.where1 = '';
obj.from.hour = '';
obj.from.minute = 30;
break;
case 'hour-n':
$('.week_btn').hide();
$('.where1_input').hide();
$('.hour_input').show().find('input').val('1');
$('.minute_input').show().find('input').val('30');
obj.from.week = '';
obj.from.where1 = '';
obj.from.hour = 1;
obj.from.minute = 30;
break;
case 'minute-n':
$('.week_btn').hide();
$('.where1_input').hide();
$('.hour_input').hide();
$('.minute_input').show();
obj.from.week = '';
obj.from.where1 = '';
obj.from.hour = '';
obj.from.minute = 30;
console.log(obj.from);
break;
case 'week':
$('.week_btn').show();
$('.where1_input').hide();
$('.hour_input').show();
$('.minute_input').show();
obj.from.week = 1;
obj.from.where1 = '';
obj.from.hour = 1;
obj.from.minute = 30;
break;
case 'month':
$('.week_btn').hide();
$('.where1_input').show();
$('.hour_input').show();
$('.minute_input').show();
obj.from.week = '';
obj.from.where1 = 1;
obj.from.hour = 1;
obj.from.minute = 30;
break;
}
obj.from.type = $(this).attr('value');
});
$('[aria-labelledby="week"] a').unbind().click(function () {
$('.week_btn').find('b').attr('val',$(this).attr('value')).html($(this).html());
obj.from.week = $(this).attr('value');
});
$('[aria-labelledby="backupTo"] a').unbind().click(function () {
$('.backup_btn').find('b').attr('val',$(this).attr('value')).html($(this).html());
obj.from.backup_to = $(this).attr('value');
});
$('.plan-submits').unbind().click(function(){
if(obj.from.type == 'hour-n'){
obj.from.where1 = obj.from.hour;
obj.from.hour = '';
} else if(obj.from.type == 'minute-n') {
obj.from.where1 = obj.from.minute;
obj.from.minute = '';
}
var loadT = layer.msg('正在保存编辑内容,请稍后...',{icon:16,time:0,shade: [0.3, '#000']});
$.post('/crontab/modify_crond',obj.from,function(rdata){
if (!rdata.status){
layer.msg(rdata.msg,{icon:rdata.status?1:2});
return;
}
showMsg(rdata.msg, function(){
layer.closeAll();
getCronData(1);
initDropdownMenu();
},{icon:rdata.status?1:2}, 2000);
},'json');
});
},100);
});
},'json');
}
+29 -15
View File
@@ -289,7 +289,7 @@ function openFilename(obj){
var ext = getSuffixName(path);
// console.log(path,ext);
if (inArray(ext,['html','htm','php','txt','md','js','css','scss','json','c','h','pl','py','java','log','conf'])){
if (inArray(ext,['html','htm','php','txt','md','js','css','scss','json','c','h','pl','py','java','log','conf','sh','json'])){
onlineEditFile(0, path);
}
@@ -300,13 +300,12 @@ function openFilename(obj){
if (inArray(ext,['svg'])){
$.post("/files/get_body", "path=" + encodeURIComponent(path), function(rdata) {
if (rdata.data.status){
layer.open({
type:1,
closeBtn: 1,
title:false,
area: '600px',
title:"SVG预览",
area: '400px',
shadeClose: true,
content: '<div class="showpicdiv">'+rdata.data.data+'</div>'
});
@@ -317,6 +316,10 @@ function openFilename(obj){
}
}
function searchFile(p){
getFiles(p);
}
//取数据
function getFiles(Path) {
var searchtype = Path;
@@ -342,7 +345,14 @@ function getFiles(Path) {
var data = 'path=' + Path;
var loadT = layer.load();
var totalSize = 0;
$.post('/files/get_dir?p=' + p + '&showRow=' + showRow + search, data, function(rdata) {
var search_all = '';
var all = $('#search_all').hasClass('active');
if(all){
search_all = "&all=yes";
}
$.post('/files/get_dir?p=' + p + '&showRow=' + showRow + search + search_all, data, function(rdata) {
layer.close(loadT);
var rows = ['10','50','100','200','500','1000','2000'];
@@ -444,8 +454,7 @@ function getFiles(Path) {
<a class='btlink' href='javascript:;' onclick=\"cutFile('" + rdata.PATH +"/"+ fmp[0] + "')\">剪切</a> | \
<a class='btlink' href='javascript:;' onclick=\"reName(0,'" + fmp[0] + "')\">重命名</a> | \
<a class='btlink' href=\"javascript:setChmod(0,'" + rdata.PATH +"/"+ fmp[0] + "');\">权限</a> | \
<a class='btlink' href=\"javascript:zip('" + rdata.PATH +"/" +fmp[0] + "');\">压缩</a> | \
"+bodyZip+download+"\
<a class='btlink' href=\"javascript:zip('" + rdata.PATH +"/" +fmp[0] + "');\">压缩</a> | "+bodyZip+download+"\
<a class='btlink' href='javascript:;' onclick=\"deleteFile('" + rdata.PATH +"/"+ fmp[0] + "')\">删除</a>\
</span></td>\
</tr>";
@@ -459,9 +468,9 @@ function getFiles(Path) {
</div>";
}
}
var dirInfo = '(共{1}个目录与{2}个文件,大小:'.replace('{1}',rdata.DIR.length+'').replace('{2}',rdata.DIR.length+'')+'<font id="pathSize">'
var dirInfo = '(共{1}个目录与{2}个文件,大小:'.replace('{1}',rdata.DIR.length+'').replace('{2}',rdata.FILES.length+'')+'<font id="pathSize">'
+ (toSize(totalSize))+'<a class="btlink ml5" onClick="getPathSize()">获取</a></font>)';
$("#DirInfo").html(dirInfo);
$("#dir_info").html(dirInfo);
if( getCookie('rank') == 'a' ){
var tablehtml = '<table width="100%" border="0" cellpadding="0" cellspacing="0" class="table table-hover">\
<thead>\
@@ -791,7 +800,7 @@ function getSuffixName(fileName){
'gif','ico','jpeg','jpg','JPG','png','psd','webp','ape','avi','flv','mkv','mov','mp3','mp4','mpeg',
'mpg','rm','rmvb','swf','wav','webm','wma','wmv','doc','docm','dotx','dotm','dot','rtf','docx','pdf',
'fdf','ppt','pptm','pot','potm','pptx','txt','xls','csv','xlsm','xlsb','xlsx','7z','gz','cab','iso',
'rar','zip','bt','file','apk','css','scss','svg','pl','py','php','md'];
'rar','zip','bt','file','apk','css','scss','svg','pl','py','php','md','json','sh'];
var extLastName = extArr[extArr.length - 1];
for(var i=0; i<exts.length; i++){
if(exts[i]==extLastName){
@@ -1093,7 +1102,7 @@ function reName(type, fileName) {
closeBtn: 1,
area: '320px',
title: '重命名',
btn:["确定","保存"],
btn:["确定","取消"],
content: '<div class="bt-form pd20">\
<div class="line">\
<input type="text" class="bt-input-text" name="Name" id="newFileName" value="' + fileName + '" placeholder="文件名" style="width:100%" />\
@@ -1355,11 +1364,12 @@ function getImage(fileName){
var imgUrl = '/files/download?filename='+fileName;
layer.open({
type:1,
offset: '150px',
closeBtn: 1,
title:false,
area: '500px',
title:"图片预览",
area: '400px',
shadeClose: true,
content: '<div class="showpicdiv"><img src="'+imgUrl+'"></div>'
content: '<div class="showpicdiv"><img style="max-width:400px;" src="'+imgUrl+'"></div>'
});
}
@@ -1605,10 +1615,13 @@ function getPathSize(){
$("body").not(".def-log").click(function(){
$("#rmenu").hide()
});
//指定路径
$("#DirPathPlace input").keyup(function(e){
if(e.keyCode == 13) {
getFiles($(this).val());
var fpath = $(this).val();
fpath = filterPath(fpath);
getFiles(fpath);
}
});
@@ -1652,6 +1665,7 @@ function pathLeft(){
$("#PathPlaceBtn ul").css("left",0);
}
}
//路径快捷点击
$("#PathPlaceBtn").on("click", function(e){
if($("#DirPathPlace").is(":hidden")){
+69 -6
View File
@@ -16,6 +16,10 @@ function toSize(a) {
}
}
function toTrim(x) {
return x.replace(/^\s+|\s+$/gm,'');
}
function inArray(f, arr){
for (var i = 0; i < arr.length; i++) {
if (f == arr[i]) {
@@ -100,6 +104,19 @@ function isContains(str, substr) {
return str.indexOf(substr) >= 0;
}
function filterPath(path){
var path_arr = path.split('/');
var path_new = [];
for (var i = 0; i < path_arr.length; i++) {
if (path_arr[i]!=''){
path_new.push(path_arr[i]);
}
}
var rdata = "/"+path_new.join('/');
return rdata;
}
function msgTpl(msg, args){
if (typeof args == 'string'){
return msg.replace('{1}', args);
@@ -206,6 +223,47 @@ function getFormatTime(tm, format) {
}
function changePathCallback(default_dir, callback) {
var c = layer.open({
type: 1,
area: "650px",
title: '选择目录',
closeBtn: 1,
shift: 5,
shadeClose: false,
content: "<div class='changepath'><div class='path-top'><button type='button' class='btn btn-default btn-sm' onclick='backFile()'><span class='glyphicon glyphicon-share-alt'></span>返回</button>\
<div class='place' id='PathPlace'>当前路径:<span></span></div></div><div class='path-con'><div class='path-con-left'><dl><dt id='changecomlist' onclick='backMyComputer()'>计算机</dt></dl></div>\
<div class='path-con-right'><ul class='default' id='computerDefautl'></ul><div class='file-list divtable'>\
<table class='table table-hover' style='border:0 none'>\
<thead><tr class='file-list-head'><th width='40%'>文件名</th><th width='20%'>修改时间</th><th width='10%'>权限</th><th width='10%'>所有者</th><th width='10%'></th></tr></thead>\
<tbody id='tbody' class='list-list'></tbody></table></div></div></div></div><div class='getfile-btn' style='margin-top:0'>\
<button type='button' class='btn btn-default btn-sm pull-left' onclick='createFolder()'>新建文件夹</button>\
<button type='button' class='btn btn-danger btn-sm mr5 btn-close'>关闭</button>\
<button type='button' class='btn btn-success btn-sm btn-choose'>选择</button>\
</div>",
success:function(layero,layer_index){
$('.btn-close').click(function(){
layer.close(layer_index);
});
$('.btn-choose').click(function(){
var a = $("#PathPlace").find("span").text();
a = a.replace(new RegExp(/(\\)/g), "/");
a_len = a.length;
if (a[a_len-1] == '/'){
a = a.substr(0,a_len-1);
}
callback(a);
layer.close(layer_index);
});
}
});
getDiskList(default_dir);
activeDisk();
}
function changePath(d) {
setCookie('SetId', d);
setCookie('SetName', '');
@@ -223,11 +281,11 @@ function changePath(d) {
<thead><tr class='file-list-head'><th width='40%'>文件名</th><th width='20%'>修改时间</th><th width='10%'>权限</th><th width='10%'>所有者</th><th width='10%'></th></tr></thead>\
<tbody id='tbody' class='list-list'></tbody></table></div></div></div></div><div class='getfile-btn' style='margin-top:0'>\
<button type='button' class='btn btn-default btn-sm pull-left' onclick='createFolder()'>新建文件夹</button>\
<button type='button' class='btn btn-danger btn-sm mr5' onclick=\"layer.close(getCookie('ChangePath'))\">关闭</button>\
<button type='button' class='btn btn-danger btn-sm mr5' onclick=\"layer.close(getCookie('changePath'))\">关闭</button>\
<button type='button' class='btn btn-success btn-sm' onclick='getfilePath()'>选择</button>\
</div>"
});
setCookie("ChangePath", c);
setCookie("changePath", c);
var b = $("#" + d).val();
tmp = b.split(".");
if(tmp[tmp.length - 1] == "gz") {
@@ -408,7 +466,8 @@ function getfilePath() {
}
$("#" + getCookie("SetId")).val(a + getCookie("SetName"));
layer.close(getCookie("ChangePath"));
layer.close(getCookie("changePath"));
return a;
}
function setCookie(a, c) {
@@ -626,7 +685,7 @@ function divcenter() {
}
function copyText(value) {
var clipboard = new ClipboardJS('#bt_copys');
var clipboard = new ClipboardJS('#mw_copys');
clipboard.on('success', function (e) {
layer.msg('复制成功',{icon:1,time:2000});
});
@@ -634,8 +693,12 @@ function copyText(value) {
clipboard.on('error', function (e) {
layer.msg('复制失败,浏览器不兼容!',{icon:2,time:2000});
});
$("#bt_copys").attr('data-clipboard-text',value);
$("#bt_copys").click();
$("#mw_copys").attr('data-clipboard-text',value);
$("#mw_copys").click();
}
function copyPass(value){
copyText(value);
}
function isChineseChar(b) {
+2 -6
View File
@@ -15,7 +15,7 @@
var type = '';
if ( typeof(type_id) == 'undefined' ){
type = '&type_id=0';
type = '&type_id=-1';
} else {
type = '&type_id='+type_id;
}
@@ -2379,7 +2379,7 @@ function configFile(webSite){
function saveConfigFile(webSite,encoding,path){
var data = 'encoding='+encoding+'&data='+encodeURIComponent($("#configBody").val())+'&path='+path;
var loadT = layer.msg('保存中...',{icon:16,time:0,shade: [0.3, '#000']});
$.post('/files/save_body',data,function(rdata){
$.post('/site/save_host_conf',data,function(rdata){
layer.close(loadT);
if(rdata.status){
layer.msg(rdata.msg,{icon:1});
@@ -2538,7 +2538,6 @@ function changeDefault(type){
},'json');
}
function getClassType(){
var select = $('.site_type > select');
$.post('/site/get_site_types',function(rdata){
@@ -2557,9 +2556,6 @@ function getClassType(){
}
getClassType();
function setClassType(){
$.post('/site/get_site_types',function(rdata){
var list = '';
+69 -12
View File
@@ -1887,33 +1887,35 @@ html .menu .menu_exit:hover {
color: #fff;
line-height: 24px;
text-align: center;
cursor: pointer
cursor: pointer;
}
.plan {
padding: 10px 20px;
margin-bottom: 10px
margin-bottom: 10px;
}
.plan .typename {
line-height: 34px;
width: 120px
width: 120px;
font-size: 12px;
}
.plan .textname {
height: 34px;
line-height: 34px;
float: left
float: left;
}
.plan .planname input {
height: 34px;
width: 260px;
padding: 0 12px
padding: 0 12px;
}
.plan .dropdown button b {
font-weight: normal
font-weight: normal;
font-size: 12px;
}
.plan .dropdown-menu {
@@ -2901,23 +2903,23 @@ html .menu .menu_exit:hover {
}
.table>tbody>tr>td {
vertical-align: middle
vertical-align: middle;
}
.showpicdiv {
max-height: 500px;
max-height: 400px;
overflow: hidden;
width: 500px
width: 400px;
}
#BarTools {
font-size: 16px;
float: left;
margin-right: 20px
margin-right: 20px;
}
#BarTools>button {
top: 0
top: 0;
}
.comlist {
@@ -4813,8 +4815,63 @@ select[disabled]{
bottom:10px;
right:10px;
}
.shell_btn_close{
.shell_btn_close {
margin-right:8px;
}
.file_search {
position: absolute;
right: 58px;
font-size: 12px;
color: #666;
width: 85px;
border-top: none;
background: #fff;
height: 8px;
line-height: 48px;
top: 0;
}
.file_search .file_search_checked {
position: relative;
height: 15px;
width: 15px;
border-radius: 1px;
cursor: pointer;
text-align: center;
margin: 0;
vertical-align: sub;
border:1px solid #aaa;
border-radius: 2px;
display: inline-block;
font-family: 'Glyphicons Halflings';
font-style: normal;
font-weight: 400;
line-height: 1;
-webkit-font-smoothing: antialiased;
}
.file_search .file_search_checked.active {
background: #20a53a;
color: #fff;
border-color:#20a53a ;
}
.file_search .file_search_checked.active:after {
content: "\e013";
font-size: 12px;
transform: scale(.85);
position: absolute;
left: .75px;
top: .75px;
}
.file_search label {
position: absolute;
right: 5px;
cursor: pointer;
top: 0;
font-weight: 400;
}
+2
View File
File diff suppressed because one or more lines are too long
+3 -3
View File
@@ -121,13 +121,13 @@
<span class="set-info c7">为面板绑定一个访问域名,<b style="color: red;">注意:一旦绑定域名,只能通过域名访问面板</b></span>
</p>
<!-- <p class="mtb15">
<p class="mtb15">
<span class="set-tit text-right" title="面板SSL" style="float: left;">面板SSL</span>
<input id="cfg_ssl" name="bind_ssl" class="btswitch btswitch-ios" type="checkbox" {{data['ssl']}}>
<label class="btswitch-btn ml5" for="cfg_ssl" style="float: left;margin-top:4px;" onclick="getPanelSSL()"></label>
<label class="btswitch-btn ml5" for="cfg_ssl" style="float: left;margin-top:4px;"></label>
<button ype="button" class="btn btn-default btn-xs panel_api_btn" style="vertical-align: middle; margin-left: 10px" onclick="getPanelSSL();">面板SSL配置</button>
<span class="set-info c7">为面板设置https协议访问,提升面板访问<b style="color: red;">安全性</b></span>
</p> -->
</p>
<p class="mtb15">
<span class="set-tit text-right" title="BasicAuth认证" style="float: left;">BasicAuth认证</span>
+126 -122
View File
@@ -3,138 +3,142 @@
{% block content %}
<div class="main-content">
<div class="container-fluid" style="padding-bottom:54px">
<div class="pos-box bgw mtb15">
<div class="position f14 c9 pull-left">
<a class="plr10 c4" href="/">首页</a>/<span class="plr10 c4">计划任务</span>
</div>
<div class="container-fluid" style="padding-bottom:54px">
<div class="pos-box bgw mtb15">
<div class="position f14 c9 pull-left">
<a class="plr10 c4" href="/">首页</a>/<span class="plr10 c4">计划任务</span>
</div>
<div class="setbox bgw mtb15">
<div class="title c6 plr15">
<h3 class="f16">添加计划任务</h3>
</div>
<div class="setbox bgw mtb15">
<div class="title c6 plr15">
<h3 class="f16">添加计划任务</h3>
</div>
<div class="setting-con ptb20">
<div class="clearfix plan">
<span class="typename c4 pull-left f14 text-right mr20">任务类型</span>
<div class="dropdown planjs pull-left mr20">
<button class="btn btn-default dropdown-toggle" type="button" id="excode" data-toggle="dropdown" style="width:auto">
<b val="toShell">Shell脚本</b>
<span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu" aria-labelledby="excode">
<li><a role="menuitem" tabindex="-1" href="javascript:;" value="toShell">Shell脚本</a></li>
<li><a role="menuitem" tabindex="-1" href="javascript:;" value="site">备份网站</a></li>
<li><a role="menuitem" tabindex="-1" href="javascript:;" value="database">备份数据库</a></li>
{% for db in data['hook_database'] %}
<li><a role="menuitem" tabindex="-1" href="javascript:;" value="database_{{db['name']}}">备份数据库【{{db['title']}}】</a></li>
{% endfor %}
<li><a role="menuitem" tabindex="-1" href="javascript:;" value="path">备份目录</a></li>
<li><a role="menuitem" tabindex="-1" href="javascript:;" value="logs">日志切割</a></li>
<li><a role="menuitem" tabindex="-1" href="javascript:;" value="rememory">释放内存</a></li>
<li><a role="menuitem" tabindex="-1" href="javascript:;" value="toUrl">访问URL</a></li>
</ul>
</div>
<span class="planSign"><i>*</i>任务类型包含以下部分:Shell脚本、备份网站、备份数据库、备份目录、日志切割、释放内存、访问URL</span>
</div>
<div class="setting-con ptb20">
<div class="clearfix plan">
<span class="typename c4 pull-left f14 text-right mr20">任务类型</span>
<div class="dropdown planjs pull-left mr20">
<button class="btn btn-default dropdown-toggle" type="button" id="excode" data-toggle="dropdown" style="width:auto">
<b val="toShell">Shell脚本</b>
<span class="caret"></span>
</button>
<div class="clearfix plan">
<span class="typename c4 pull-left f14 text-right mr20">任务名称</span>
<div class="planname pull-left"><input type="text" name="name" class="bt-input-text"></div>
</div>
<div class="clearfix plan">
<span class="typename c4 pull-left f14 text-right mr20">执行周期</span>
<div class="dropdown plancycle pull-left mr20">
<button class="btn btn-default dropdown-toggle" type="button" id="cycle" data-toggle="dropdown" style="width:94px">
<b val="week">每星期</b>
<span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu" aria-labelledby="cycle">
<li><a role="menuitem" tabindex="-1" href="javascript:;" value="day">每天</a></li>
<li><a role="menuitem" tabindex="-1" href="javascript:;" value="day-n">N天</a></li>
<li><a role="menuitem" tabindex="-1" href="javascript:;" value="hour">每小时</a></li>
<li><a role="menuitem" tabindex="-1" href="javascript:;" value="hour-n">N小时</a></li>
<li><a role="menuitem" tabindex="-1" href="javascript:;" value="minute-n">N分钟</a></li>
<li><a role="menuitem" tabindex="-1" href="javascript:;" value="week">每星期</a></li>
<li><a role="menuitem" tabindex="-1" href="javascript:;" value="month">每月</a></li>
</ul>
</div>
<div id="ptime" class="pull-left">
<div class="dropdown planweek pull-left mr20">
<button class="btn btn-default dropdown-toggle" type="button" id="excode" data-toggle="dropdown">
<b val="1">周一</b>
<span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu" aria-labelledby="excode">
<li><a role="menuitem" tabindex="-1" href="javascript:;" value="toShell">Shell脚本</a></li>
<li><a role="menuitem" tabindex="-1" href="javascript:;" value="site">备份网站</a></li>
<li><a role="menuitem" tabindex="-1" href="javascript:;" value="database">备份数据库</a></li>
<li><a role="menuitem" tabindex="-1" href="javascript:;" value="logs">日志切割</a></li>
<li><a role="menuitem" tabindex="-1" href="javascript:;" value="rememory">释放内存</a></li>
<li><a role="menuitem" tabindex="-1" href="javascript:;" value="toUrl">访问URL</a></li>
<li><a role="menuitem" tabindex="-1" href="javascript:;" value="1">周一</a></li>
<li><a role="menuitem" tabindex="-1" href="javascript:;" value="2">周二</a></li>
<li><a role="menuitem" tabindex="-1" href="javascript:;" value="3">周三</a></li>
<li><a role="menuitem" tabindex="-1" href="javascript:;" value="4">周四</a></li>
<li><a role="menuitem" tabindex="-1" href="javascript:;" value="5">周五</a></li>
<li><a role="menuitem" tabindex="-1" href="javascript:;" value="6">周六</a></li>
<li><a role="menuitem" tabindex="-1" href="javascript:;" value="0">周日</a></li>
</ul>
</div>
<span class="planSign"><i>*</i>任务类型包含以下部分:Shell脚本、备份网站、备份数据库、日志切割、释放内存、访问URL</span>
</div>
<div class="clearfix plan">
<span class="typename c4 pull-left f14 text-right mr20">任务名称</span>
<div class="planname pull-left"><input type="text" name="name" class="bt-input-text"></div>
</div>
<div class="clearfix plan">
<span class="typename c4 pull-left f14 text-right mr20">执行周期</span>
<div class="dropdown plancycle pull-left mr20">
<button class="btn btn-default dropdown-toggle" type="button" id="cycle" data-toggle="dropdown" style="width:94px">
<b val="week">每星期</b>
<span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu" aria-labelledby="cycle">
<li><a role="menuitem" tabindex="-1" href="javascript:;" value="day">每天</a></li>
<li><a role="menuitem" tabindex="-1" href="javascript:;" value="day-n">N天</a></li>
<li><a role="menuitem" tabindex="-1" href="javascript:;" value="hour">每小时</a></li>
<li><a role="menuitem" tabindex="-1" href="javascript:;" value="hour-n">N小时</a></li>
<li><a role="menuitem" tabindex="-1" href="javascript:;" value="minute-n">N分钟</a></li>
<li><a role="menuitem" tabindex="-1" href="javascript:;" value="week">每星期</a></li>
<li><a role="menuitem" tabindex="-1" href="javascript:;" value="month">每月</a></li>
</ul>
<div class="plan_hms pull-left mr20 bt-input-text">
<span><input type="number" name="hour" value="1" maxlength="2" max="23" min="0"></span>
<span class="name">小时</span>
</div>
<div id="ptime" class="pull-left">
<div class="dropdown planweek pull-left mr20">
<button class="btn btn-default dropdown-toggle" type="button" id="excode" data-toggle="dropdown">
<b val="1">周一</b>
<span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu" aria-labelledby="excode">
<li><a role="menuitem" tabindex="-1" href="javascript:;" value="1">周一</a></li>
<li><a role="menuitem" tabindex="-1" href="javascript:;" value="2">周二</a></li>
<li><a role="menuitem" tabindex="-1" href="javascript:;" value="3">周三</a></li>
<li><a role="menuitem" tabindex="-1" href="javascript:;" value="4">周四</a></li>
<li><a role="menuitem" tabindex="-1" href="javascript:;" value="5">周五</a></li>
<li><a role="menuitem" tabindex="-1" href="javascript:;" value="6">周六</a></li>
<li><a role="menuitem" tabindex="-1" href="javascript:;" value="0">周日</a></li>
</ul>
</div>
<div class="plan_hms pull-left mr20 bt-input-text">
<span><input type="number" name="hour" value="1" maxlength="2" max="23" min="0"></span>
<span class="name">小时</span>
</div>
<div class="plan_hms pull-left mr20 bt-input-text">
<span><input type="number" name="minute" value="30" maxlength="2" max="59" min="0"></span>
<span class="name">分钟</span>
</div>
<div class="plan_hms pull-left mr20 bt-input-text">
<span><input type="number" name="minute" value="30" maxlength="2" max="59" min="0"></span>
<span class="name">分钟</span>
</div>
</div>
<div class="clearfix plan">
<span class="typename controls c4 pull-left f14 text-right mr20">脚本内容</span>
<div id="implement" style="line-height:34px"><textarea class="txtsjs bt-input-text" name="sBody"></textarea></div>
</div>
<div class="clearfix plan">
<div class="bt-submit plan-submit" onclick="planAdd();">添加任务</div>
</div>
<ul class="help-info-text c7 ptb15 mlr20">
<li>当添加完备份任务,应该手动运行一次,并检查备份包是否完整</li>
<li>磁盘容量不够、数据库密码错误、网络不稳定等原因,可能导致数据备份不完整</li>
</ul>
</div>
<form id="set-Config" action="/crontab/add" enctype="multipart/form-data" method="post" style="display: none;">
<input type="text" name="name" value="" />
<input type="text" name="type" value="" />
<input type="number" name="where1" value="" />
<input type="number" name="hour" value="" />
<input type="number" name="minute" value="" />
<input type="text" name="week" value="" />
<input type="text" name="sType" value="" />
<textarea name='sBody'></textarea>
<input id="sFile" type="file" name="sFile" value="" />
<input id="sName" type="text" name="sName" value="" />
<input id="backupTo" type="text" name="backupTo" value="localhost" />
<input id="urladdress" type="text" name="urladdress" value="" />
<input type="number" name="save" value="" />
<input type="submit" />
</form>
<div class="clearfix plan">
<span class="typename controls c4 pull-left f14 text-right mr20">脚本内容</span>
<div id="implement" style="line-height:34px"><textarea class="txtsjs bt-input-text" name="sBody"></textarea></div>
</div>
<div class="clearfix plan">
<div class="bt-submit plan-submit" onclick="planAdd();">添加任务</div>
</div>
<ul class="help-info-text c7 ptb15 mlr20">
<li>当添加完备份任务,应该手动运行一次,并检查备份包是否完整</li>
<li>磁盘容量不够、数据库密码错误、网络不稳定等原因,可能导致数据备份不完整</li>
</ul>
</div>
<div class="setbox bgw mtb15">
<div class="title c6 plr15">
<h3 class="f16">任务列表</h3>
<span id="allDelete" style="float: right;display: none;">
<button onclick="allDeleteCron();" class="btn btn-default btn-sm">删除选中</button>
</span>
</div>
<div class="setting-con divtable pd15">
<table class="table table-hover">
<thead>
<tr>
<th width='30px'><input onclick="checkSelect();" type="checkbox" id="setBox"></th>
<th>任务名称</th>
<th>状态</th>
<th>周期</th>
<th>执行时机</th>
<th>保存数量</th>
<th>备份到</th>
<th>添加时间</th>
<th width="190">操作</th>
</tr>
</thead>
<tbody id="cronbody"></tbody>
</table>
<div class="dataTables_paginate paging_bootstrap pagination">
<ul id="softPage" class="page"></ul>
</div>
<form id="set-Config" action="/crontab/add" enctype="multipart/form-data" method="post" style="display: none;">
<input type="text" name="name" value="" />
<input type="text" name="type" value="" />
<input type="number" name="where1" value="" />
<input type="number" name="hour" value="" />
<input type="number" name="minute" value="" />
<input type="text" name="week" value="" />
<input type="text" name="sType" value="" />
<textarea name='sBody'></textarea>
<input id="sFile" type="file" name="sFile" value="" />
<input type="text" name="sName" value="" />
<input id="backupTo" type="text" name="backupTo" value="localhost" />
<input id="urladdress" type="text" name="urladdress" value="" />
<input type="number" name="save" value="" />
<input type="submit" />
</form>
</div>
<div class="setbox bgw mtb15">
<div class="title c6 plr15">
<h3 class="f16">任务列表</h3>
<span id="allDelete" style="float: right;display: none;">
<button onclick="allDeleteCron();" class="btn btn-default btn-sm">删除选中</button>
</span>
</div>
<div class="setting-con divtable pd15">
<table class="table table-hover">
<thead>
<tr>
<th width='30px'><input onclick="checkSelect();" type="checkbox" id="setBox"></th>
<th>任务名称</th>
<th>状态</th>
<th>周期</th>
<th>执行时机</th>
<th>保存数量</th>
<th>备份到</th>
<th>添加时间</th>
<th width="190">操作</th>
</tr>
</thead>
<tbody id="cronbody"></tbody>
</table>
<div class="dataTables_paginate paging_bootstrap pagination">
<ul id="softPage" class="page"></ul>
</div>
</div>
</div>
+13 -4
View File
@@ -13,11 +13,15 @@
<span id='DirPathPlace' class="pull-left"><input id="fileInputPath" type="text"></span>
<span id='PathPlaceBtn' class="pull-left"></span>
</div>
<div class="pull-left mlr15" style="line-height:26px"><span id='DirInfo'></span></div>
<div class="pull-left mlr15" style="line-height:26px"><span id='dir_info'></span></div>
<div class="search pull-right" style="position: absolute; top: 6px; right: 5px;">
<form target="hid" onsubmit='getFiles(1)'>
<input type="text" id="SearchValue" class="ser-text pull-left" placeholder="" />
<button type="button" class="ser-sub pull-left" onclick='getFiles(1)'></button>
<form target="hid">
<input type="text" id="SearchValue" class="ser-text pull-left" placeholder="搜索文件/目录" />
<button type="button" class="ser-sub pull-left" onclick='searchFile(1)'></button>
<div class="file_search" draggable="false">
<div id="search_all" class="file_search_checked"></div>
<label for="search_all">包含子目录</label>
</div>
</form>
<iframe name='hid' id="hid" style="display:none"></iframe>
</div>
@@ -93,5 +97,10 @@ $("#set_list").click(function() {
$(".refreshBtn").click(function() {
getFiles(getCookie('open_dir_path'));
})
$('#search_all').click(function(){
$(this).toggleClass('active');
});
</script>
{% endblock %}
+1 -1
View File
@@ -47,7 +47,7 @@
<div class="btpc-plus" onclick="bindPanel(0,'b')">+</div>
</div>
</div>
<button style="display: none;" id="bt_copys" class="bt_copy" data-clipboard-text=""></button>
<button style="display: none;" id="mw_copys" class="mw_copy" data-clipboard-text=""></button>
<link rel="stylesheet" href="/static/build/xterm.css?v={{config.version}}">
<link rel="stylesheet" href="/static/build/addons/fullscreen/fullscreen.css?v={{config.version}}">
<script src="/static/js/jquery-1.10.2.min.js?v={{config.version}}"></script>

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