InPanel 1.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. inpanel/__init__.py +29 -0
  2. inpanel/acme.py +289 -0
  3. inpanel/aliyuncs.py +269 -0
  4. inpanel/app.py +514 -0
  5. inpanel/apt.py +223 -0
  6. inpanel/base.py +200 -0
  7. inpanel/config.py +139 -0
  8. inpanel/data/config.ini +29 -0
  9. inpanel/data/history +5 -0
  10. inpanel/data/runlogs.ini +12 -0
  11. inpanel/data/update_info.ini +4 -0
  12. inpanel/dnf.py +225 -0
  13. inpanel/lib/__init__.py +9 -0
  14. inpanel/lib/async_process.py +98 -0
  15. inpanel/lib/filelock.py +82 -0
  16. inpanel/lib/magic.py +233 -0
  17. inpanel/lib/pxssh.py +310 -0
  18. inpanel/lib/pyDes.py +867 -0
  19. inpanel/mod/__init__.py +0 -0
  20. inpanel/mod/apt.py +316 -0
  21. inpanel/mod/certificate.py +268 -0
  22. inpanel/mod/config.py +372 -0
  23. inpanel/mod/cron.py +315 -0
  24. inpanel/mod/disk.py +322 -0
  25. inpanel/mod/dnf.py +239 -0
  26. inpanel/mod/file.py +722 -0
  27. inpanel/mod/firewall.py +199 -0
  28. inpanel/mod/firewall_base.py +137 -0
  29. inpanel/mod/firewall_firewalld.py +170 -0
  30. inpanel/mod/firewall_iptables.py +176 -0
  31. inpanel/mod/firewall_macos.py +213 -0
  32. inpanel/mod/firewall_ufw.py +128 -0
  33. inpanel/mod/ftp.py +231 -0
  34. inpanel/mod/httpd.py +1205 -0
  35. inpanel/mod/lighttpd.py +26 -0
  36. inpanel/mod/login.py +98 -0
  37. inpanel/mod/mysql.py +577 -0
  38. inpanel/mod/named.py +26 -0
  39. inpanel/mod/nginx.py +2327 -0
  40. inpanel/mod/package.py +33 -0
  41. inpanel/mod/package_apt.py +62 -0
  42. inpanel/mod/package_base.py +77 -0
  43. inpanel/mod/package_dnf.py +62 -0
  44. inpanel/mod/package_map.py +366 -0
  45. inpanel/mod/package_yum.py +64 -0
  46. inpanel/mod/php.py +289 -0
  47. inpanel/mod/process.py +291 -0
  48. inpanel/mod/proftpd.py +26 -0
  49. inpanel/mod/pureftpd.py +26 -0
  50. inpanel/mod/query.py +108 -0
  51. inpanel/mod/server.py +1319 -0
  52. inpanel/mod/service.py +262 -0
  53. inpanel/mod/setting.py +228 -0
  54. inpanel/mod/shell.py +78 -0
  55. inpanel/mod/ssh.py +280 -0
  56. inpanel/mod/system.py +220 -0
  57. inpanel/mod/task.py +1337 -0
  58. inpanel/mod/ufw.py +14 -0
  59. inpanel/mod/user.py +306 -0
  60. inpanel/mod/vsftpd.py +67 -0
  61. inpanel/mod/yum.py +243 -0
  62. inpanel/plugins/__init__.py +10 -0
  63. inpanel/plugins/__pycache__/__init__.cpython-313.pyc +0 -0
  64. inpanel/plugins/acme/static/index.html +10 -0
  65. inpanel/plugins/acme/static/index.js +13 -0
  66. inpanel/plugins/shadowsocks-libev/__pycache__/info.cpython-313.pyc +0 -0
  67. inpanel/plugins/shadowsocks-libev/info.py +73 -0
  68. inpanel/plugins/shadowsocks-libev/static/index.html +70 -0
  69. inpanel/plugins/shadowsocks-libev/static/index.js +18 -0
  70. inpanel/public/css/global.css +138 -0
  71. inpanel/public/favicon.ico +0 -0
  72. inpanel/public/images/alipay.jpg +0 -0
  73. inpanel/public/images/loading.gif +0 -0
  74. inpanel/public/images/weixin.jpg +0 -0
  75. inpanel/public/index.html +94 -0
  76. inpanel/public/js/controllers/controllers.js +566 -0
  77. inpanel/public/js/controllers/database.js +590 -0
  78. inpanel/public/js/controllers/ecs.js +624 -0
  79. inpanel/public/js/controllers/file.js +940 -0
  80. inpanel/public/js/controllers/plugins.js +105 -0
  81. inpanel/public/js/controllers/service.js +1460 -0
  82. inpanel/public/js/controllers/site.js +1241 -0
  83. inpanel/public/js/controllers/utils.js +2319 -0
  84. inpanel/public/js/core.js +221 -0
  85. inpanel/public/js/directives.js +784 -0
  86. inpanel/public/js/filters.js +395 -0
  87. inpanel/public/js/services.js +257 -0
  88. inpanel/public/lib/angular/1.0.2/angular-resource.min.js +10 -0
  89. inpanel/public/lib/angular/1.0.2/angular.min.js +158 -0
  90. inpanel/public/lib/bootstrap/css/bootstrap-theme.css +587 -0
  91. inpanel/public/lib/bootstrap/css/bootstrap-theme.css.map +1 -0
  92. inpanel/public/lib/bootstrap/css/bootstrap-theme.min.css +6 -0
  93. inpanel/public/lib/bootstrap/css/bootstrap-theme.min.css.map +1 -0
  94. inpanel/public/lib/bootstrap/css/bootstrap.css +6757 -0
  95. inpanel/public/lib/bootstrap/css/bootstrap.css.map +1 -0
  96. inpanel/public/lib/bootstrap/css/bootstrap.min.css +6 -0
  97. inpanel/public/lib/bootstrap/css/bootstrap.min.css.map +1 -0
  98. inpanel/public/lib/bootstrap/fonts/glyphicons-halflings-regular.eot +0 -0
  99. inpanel/public/lib/bootstrap/fonts/glyphicons-halflings-regular.svg +288 -0
  100. inpanel/public/lib/bootstrap/fonts/glyphicons-halflings-regular.ttf +0 -0
  101. inpanel/public/lib/bootstrap/fonts/glyphicons-halflings-regular.woff +0 -0
  102. inpanel/public/lib/bootstrap/fonts/glyphicons-halflings-regular.woff2 +0 -0
  103. inpanel/public/lib/bootstrap/img/glyphicons-halflings-white.png +0 -0
  104. inpanel/public/lib/bootstrap/img/glyphicons-halflings.png +0 -0
  105. inpanel/public/lib/bootstrap/js/bootstrap.js +2377 -0
  106. inpanel/public/lib/bootstrap/js/bootstrap.min.js +7 -0
  107. inpanel/public/lib/bootstrap/js/npm.js +13 -0
  108. inpanel/public/lib/codemirror/codemirror-compressed.js +1 -0
  109. inpanel/public/lib/codemirror/codemirror.css +173 -0
  110. inpanel/public/lib/jquery/jquery-1.12.4.min.js +5 -0
  111. inpanel/public/lib/md5/md5.js +379 -0
  112. inpanel/public/lib/md5/md5.min.js +1 -0
  113. inpanel/public/partials/database/index.html +172 -0
  114. inpanel/public/partials/database/mysql/dbedit.html +462 -0
  115. inpanel/public/partials/database/mysql/dbnew.html +348 -0
  116. inpanel/public/partials/database/mysql/useredit.html +310 -0
  117. inpanel/public/partials/database/mysql/usernew.html +97 -0
  118. inpanel/public/partials/directives/message.html +20 -0
  119. inpanel/public/partials/directives/navbar.html +25 -0
  120. inpanel/public/partials/directives/selector.html +57 -0
  121. inpanel/public/partials/directives/srvbase.html +72 -0
  122. inpanel/public/partials/directives/srvext.html +34 -0
  123. inpanel/public/partials/directives/srvfile.html +23 -0
  124. inpanel/public/partials/directives/srvinstall.html +34 -0
  125. inpanel/public/partials/directives/srvlog.html +23 -0
  126. inpanel/public/partials/directives/srvminiop.html +28 -0
  127. inpanel/public/partials/directives/srvuninstall.html +38 -0
  128. inpanel/public/partials/directives/srvupdate.html +30 -0
  129. inpanel/public/partials/ecs/account.html +156 -0
  130. inpanel/public/partials/ecs/ecs.html +198 -0
  131. inpanel/public/partials/ecs/index.html +40 -0
  132. inpanel/public/partials/ecs/setings.html +459 -0
  133. inpanel/public/partials/file/file.html +501 -0
  134. inpanel/public/partials/file/trash.html +95 -0
  135. inpanel/public/partials/ftp.html +36 -0
  136. inpanel/public/partials/log.html +83 -0
  137. inpanel/public/partials/login.html +79 -0
  138. inpanel/public/partials/logout.html +22 -0
  139. inpanel/public/partials/main.html +534 -0
  140. inpanel/public/partials/plugins/index.html +55 -0
  141. inpanel/public/partials/plugins/plugins.html +13 -0
  142. inpanel/public/partials/secure.html +13 -0
  143. inpanel/public/partials/service/apache.html +147 -0
  144. inpanel/public/partials/service/cron.html +71 -0
  145. inpanel/public/partials/service/index.html +293 -0
  146. inpanel/public/partials/service/iptables.html +40 -0
  147. inpanel/public/partials/service/lighttpd.html +63 -0
  148. inpanel/public/partials/service/mariadb.html +113 -0
  149. inpanel/public/partials/service/memcache.html +36 -0
  150. inpanel/public/partials/service/minio.html +44 -0
  151. inpanel/public/partials/service/mongodb.html +44 -0
  152. inpanel/public/partials/service/mysql.html +113 -0
  153. inpanel/public/partials/service/named.html +61 -0
  154. inpanel/public/partials/service/nginx.html +296 -0
  155. inpanel/public/partials/service/ntp.html +40 -0
  156. inpanel/public/partials/service/php.html +250 -0
  157. inpanel/public/partials/service/proftpd.html +59 -0
  158. inpanel/public/partials/service/pureftpd.html +59 -0
  159. inpanel/public/partials/service/redis.html +40 -0
  160. inpanel/public/partials/service/samba.html +58 -0
  161. inpanel/public/partials/service/sendmail.html +40 -0
  162. inpanel/public/partials/service/ssh.html +186 -0
  163. inpanel/public/partials/service/tomcat.html +65 -0
  164. inpanel/public/partials/service/vsftpd.html +195 -0
  165. inpanel/public/partials/setting.html +299 -0
  166. inpanel/public/partials/site/apache/site.html +277 -0
  167. inpanel/public/partials/site/index.html +344 -0
  168. inpanel/public/partials/site/nginx/site.html +558 -0
  169. inpanel/public/partials/sorry.html +19 -0
  170. inpanel/public/partials/storage/autofm.html +103 -0
  171. inpanel/public/partials/storage/backup.html +42 -0
  172. inpanel/public/partials/storage/index.html +360 -0
  173. inpanel/public/partials/storage/migrate.html +93 -0
  174. inpanel/public/partials/storage/movedata.html +98 -0
  175. inpanel/public/partials/storage/remote.html +77 -0
  176. inpanel/public/partials/utils/cron.html +486 -0
  177. inpanel/public/partials/utils/firewall.html +266 -0
  178. inpanel/public/partials/utils/index.html +193 -0
  179. inpanel/public/partials/utils/network.html +118 -0
  180. inpanel/public/partials/utils/process.html +239 -0
  181. inpanel/public/partials/utils/repository.html +373 -0
  182. inpanel/public/partials/utils/shell.html +28 -0
  183. inpanel/public/partials/utils/ssl.html +319 -0
  184. inpanel/public/partials/utils/time.html +109 -0
  185. inpanel/public/partials/utils/user.html +359 -0
  186. inpanel/public/robots.txt +2 -0
  187. inpanel/remote.py +109 -0
  188. inpanel/templates/file/preview.html +34 -0
  189. inpanel/templates/index.html +93 -0
  190. inpanel/utils.py +282 -0
  191. inpanel/web.py +1811 -0
  192. inpanel/yum.py +756 -0
  193. inpanel-1.2.1.dist-info/METADATA +399 -0
  194. inpanel-1.2.1.dist-info/RECORD +198 -0
  195. inpanel-1.2.1.dist-info/WHEEL +5 -0
  196. inpanel-1.2.1.dist-info/entry_points.txt +2 -0
  197. inpanel-1.2.1.dist-info/licenses/LICENSE +30 -0
  198. inpanel-1.2.1.dist-info/top_level.txt +1 -0
inpanel/mod/service.py ADDED
@@ -0,0 +1,262 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (c) 2017-2026 Jackson Dou
4
+ # All rights reserved.
5
+ #
6
+ # InPanel is distributed under the terms of the (new) BSD License.
7
+ # The full license can be found in 'LICENSE'.
8
+
9
+ '''Module for Service Management.'''
10
+
11
+
12
+ import os
13
+ # from mod_shell import async_command
14
+ from glob import glob
15
+ from pathlib import Path
16
+ from shlex import split
17
+ from subprocess import PIPE, Popen
18
+
19
+ from ..base import kernel_name, os_name
20
+
21
+
22
+ def web_handler(context):
23
+ '''for web server'''
24
+ action = context.get_argument('action', '')
25
+
26
+ if action == 'start':
27
+ name = context.get_argument('name', '')
28
+ service_label = context.get_argument('service', '')
29
+ if not name:
30
+ name = service_label
31
+ if do_start(name):
32
+ context.write({'code': 0, 'msg': f'{name} 已启动成功'})
33
+ else:
34
+ context.write({'code': -1, 'msg': f'{name} 启动运行失败'})
35
+ elif action == 'stop':
36
+ name = context.get_argument('name', '')
37
+ service_label = context.get_argument('service', '')
38
+ if not name:
39
+ name = service_label
40
+ if do_stop(name):
41
+ context.write({'code': 0, 'msg': f'{name} 已成功停止运行'})
42
+ else:
43
+ context.write({'code': -1, 'msg': f'{name} 停止运行失败'})
44
+ elif action == 'restart':
45
+ name = context.get_argument('name', '')
46
+ service_label = context.get_argument('service', '')
47
+ if not name:
48
+ name = service_label
49
+ if do_start(name):
50
+ context.write({'code': 0, 'msg': f'{name} 已重新启动'})
51
+ else:
52
+ context.write({'code': -1, 'msg': f'{name} 重新启动失败'})
53
+ elif action == 'chkconfig':
54
+ autostart = context.get_argument('autostart', '')
55
+ if not name:
56
+ name = service_label
57
+
58
+ autostart_str = {'on': '启用', 'off': '禁用'}
59
+ if set_autostart(service_label, autostart == 'on' and True or False):
60
+ context.write({'code': 0, 'msg': f'成功{autostart_str[autostart]} {name} 自动启动!'})
61
+ else:
62
+ context.write({'code': -1, 'msg': f'{autostart_str[autostart]} {name} 自动启动失败!'})
63
+ elif action == 'get_autostart_list':
64
+ context.write({'code': 0, 'data': get_autostart_list()})
65
+ else:
66
+ context.write({'code': -1, 'msg': '未定义的操作!'})
67
+
68
+ def do_start(service_name=None):
69
+ '''start service'''
70
+ if not service_name:
71
+ return False
72
+ return True
73
+
74
+
75
+ def do_stop(service_name=None):
76
+ '''stop service'''
77
+ if not service_name:
78
+ return False
79
+ return True
80
+
81
+
82
+ def do_restart(service_name=None):
83
+ '''stop service then start service'''
84
+ if not service_name:
85
+ return False
86
+ do_stop()
87
+ do_start()
88
+
89
+
90
+ def get_status(name = None):
91
+ '''get '''
92
+ if name is None:
93
+ return False
94
+ return True
95
+
96
+
97
+ def do_search():
98
+ '''set service auto start on system up'''
99
+ return True
100
+
101
+
102
+ def get_list():
103
+ '''Return a list of all service'''
104
+ return []
105
+
106
+
107
+ def get_autostart_list():
108
+ """Return a list of the autostart service name.
109
+ """
110
+ if kernel_name == 'Linux':
111
+ if os_name == 'Ubuntu':
112
+ return []
113
+ startlevel = -1
114
+ with open('/etc/inittab', encoding='utf-8') as f:
115
+ for line in f:
116
+ if line.startswith('id:'):
117
+ startlevel = line.split(':')[1]
118
+ break
119
+ if startlevel == -1:
120
+ p = Popen(split('runlevel'), stdout=PIPE, close_fds=True)
121
+ startlevel = int(p.stdout.read().decode().strip().replace('N ', ''))
122
+ p.wait()
123
+
124
+ rcpath = f'/etc/rc.d/rc{startlevel}.d/'
125
+ enable_service_path = '/etc/systemd/system/multi-user.target.wants/'
126
+ services = [
127
+ Path(os.readlink(filepath))
128
+ for filepath in glob(f'{rcpath}/S*')
129
+ ]
130
+ services += [
131
+ Path(filePath).replace('.service', '')
132
+ for filePath in glob(f'{enable_service_path}*.service')
133
+ ]
134
+ return services
135
+ elif kernel_name == 'Darwin':
136
+ return []
137
+ # others
138
+ return []
139
+
140
+
141
+ def set_autostart(service_name=None, autostart=True):
142
+ """Add or remove service to autostart list.
143
+ E.g: chkconfig service_name on|off
144
+ """
145
+ if not service_name:
146
+ return False
147
+ if kernel_name == 'Linux':
148
+ cmdbin = 'chkconfig'
149
+ status = 'on' if autostart else 'off'
150
+ cmd = f'{cmdbin} {service_name} {status}'
151
+ cmd = split(cmd)
152
+ p = Popen(cmd, stdout=PIPE, close_fds=True)
153
+ p.stdout.read()
154
+ return p.wait() == 0 and True or False
155
+ elif kernel_name == 'Darwin':
156
+ return False
157
+ else:
158
+ return False
159
+
160
+ def do_add():
161
+ '''create service config file'''
162
+ return True
163
+
164
+
165
+ __all__ = [
166
+ 'do_start', 'do_stop', 'do_restart', 'do_search', 'get_list',
167
+ 'get_autostart_list', 'set_autostart'
168
+ ]
169
+
170
+ class Service(object):
171
+
172
+ '''supported service operate script'''
173
+ service_items = {
174
+ 'inpanel': False,
175
+ 'nginx': False,
176
+ 'httpd': False,
177
+ 'vsftpd': False,
178
+ 'mysqld': False,
179
+ 'redis': False,
180
+ 'memcached': False,
181
+ 'mongod': False,
182
+ 'php-fpm': False,
183
+ 'sendmail': False,
184
+ 'postfix': False,
185
+ 'sshd': False,
186
+ 'iptables': False,
187
+ 'crond': False,
188
+ 'ntpd': False,
189
+ 'named': False,
190
+ 'lighttpd': False,
191
+ 'proftpd': False,
192
+ 'pure-ftpd': False,
193
+ 'smb': False
194
+ }
195
+
196
+ pidnames = {
197
+ 'sendmail': ['sm-client'],
198
+ 'smb': ['smbd']
199
+ }
200
+
201
+ subsys_locks = (
202
+ 'iptables'
203
+ )
204
+
205
+ @classmethod
206
+ def status(self, service = None):
207
+ if not service:
208
+ return None
209
+ initscript = f'/etc/init.d/{service}'
210
+ if not Path(initscript).exists():
211
+ initscript = f'/usr/lib/systemd/system/{service}.service'
212
+ if not Path(initscript).exists():
213
+ return None
214
+
215
+ pidfile = f'/var/run/{service}.pid'
216
+ if not Path(pidfile).is_file():
217
+ p = glob(f'/var/run/{service}/*.pid')
218
+ if len(p) > 0:
219
+ pidfile = p[0]
220
+ else:
221
+ # some services have special pid filename
222
+ if service in Service.pidnames:
223
+ for pidname in Service.pidnames[service]:
224
+ pidfile = f'/var/run/{pidname}.pid'
225
+ if Path(pidfile).is_file():
226
+ break
227
+ else:
228
+ pidfile = None
229
+ else:
230
+ pidfile = None
231
+ if not pidfile:
232
+ # not always corrent, some services dead but the lock still exists
233
+ # some services don't have the pidfile
234
+ if service in Service.subsys_locks:
235
+ if Path(f'/var/lock/subsys/{service}').exists():
236
+ return 'running'
237
+
238
+ # try execute pidof to find the pidfile
239
+ cmd_ = split(f'pidof -c -o %%PPID -x {service}')
240
+ p = Popen(cmd_, stdout=PIPE, close_fds=True)
241
+ pid = p.stdout.read().decode().strip()
242
+ p.wait()
243
+
244
+ if not pid:
245
+ return 'stopped'
246
+
247
+ if pidfile:
248
+ with open(pidfile, encoding='utf-8') as f:
249
+ pid = f.readline().strip()
250
+ if not pid:
251
+ return 'stopped'
252
+ if not Path(f'/proc/{pid}').exists():
253
+ return 'stopped'
254
+
255
+ return 'running'
256
+
257
+
258
+
259
+ if __name__ == '__main__':
260
+ autostart_services = get_autostart_list()
261
+ for service in Service.support_services:
262
+ print('* Status of %s: %s (autostart: %s)' % (service, Service.status(service), str(service in autostart_services)))
inpanel/mod/setting.py ADDED
@@ -0,0 +1,228 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (c) 2017-2026 Jackson Dou
4
+ # All rights reserved.
5
+ #
6
+ # InPanel is distributed under the terms of the (new) BSD License.
7
+ # The full license can be found in 'LICENSE'.
8
+
9
+ """Module for Settings Management."""
10
+
11
+ import time
12
+ from hashlib import md5
13
+ from hmac import new as hmac_new
14
+ from base64 import b64decode
15
+ from pathlib import Path
16
+
17
+ import tornado.httpclient
18
+ import tornado.escape
19
+ from ..base import app_api
20
+ from .. import utils
21
+ from .config import update_info_config
22
+ from . import server
23
+
24
+
25
+ async def handle_get(context, section):
26
+ """Handle GET requests for settings."""
27
+ if section == 'auth':
28
+ username = context.config.get('auth', 'username')
29
+ passwordcheck = context.config.getboolean('auth', 'passwordcheck')
30
+ context.write({'username': username, 'passwordcheck': passwordcheck})
31
+ context.finish()
32
+
33
+ elif section == 'runtime':
34
+ mode = context.config.get('runtime', 'mode')
35
+ loginlockexpire = context.config.getint('runtime', 'loginlockexpire')
36
+ loginfails = context.config.getint('runtime', 'loginfails')
37
+ loginlock = context.config.getboolean('runtime', 'loginlock')
38
+ if not mode:
39
+ mode = 'prod'
40
+ context.config.set('runtime', 'mode', 'prod')
41
+ context.write({
42
+ 'mode': mode,
43
+ 'loginlockexpire': loginlockexpire,
44
+ 'loginfails': loginfails,
45
+ 'loginlock': loginlock
46
+ })
47
+ context.finish()
48
+
49
+ elif section == 'server':
50
+ ip = context.config.get('server', 'ip')
51
+ port = context.config.get('server', 'port')
52
+ forcehttps = context.config.getboolean('server', 'forcehttps')
53
+ sslkey = context.config.get('server', 'sslkey')
54
+ sslcrt = context.config.get('server', 'sslcrt')
55
+ context.write({'forcehttps': forcehttps, 'ip': ip, 'port': port, 'sslkey': sslkey, 'sslcrt': sslcrt})
56
+ context.finish()
57
+
58
+ elif section == 'accesskey':
59
+ accesskey = context.config.get('auth', 'accesskey')
60
+ accesskeyenable = context.config.getboolean('auth', 'accesskeyenable')
61
+ context.write({'accesskey': accesskey, 'accesskeyenable': accesskeyenable})
62
+ context.finish()
63
+
64
+ elif section == 'upver':
65
+ force = context.get_argument('force', '')
66
+ update_info = update_info_config()
67
+ lastcheck = update_info.get('update', 'lastcheck')
68
+ lastcheck = 0 if lastcheck == '' else int(lastcheck)
69
+
70
+ # detect new version daily
71
+ if force or time.time() > lastcheck + 86400:
72
+ http_client = tornado.httpclient.AsyncHTTPClient()
73
+ try:
74
+ response = await http_client.fetch(app_api['latest'])
75
+ if response.error:
76
+ context.write({'code': -1, 'msg': '获取新版本信息失败!'})
77
+ else:
78
+ data = response.body.decode('utf-8')
79
+ if not data:
80
+ context.write({'code': -1, 'msg': '获取新版本信息失败!'})
81
+ else:
82
+ update_info.set('update', 'lastcheck', int(time.time()))
83
+ update_info.set('update', 'updateinfo', data)
84
+ try:
85
+ data = tornado.escape.json_decode(data)
86
+ context.write({'code': 0, 'msg':'', 'data': data})
87
+ except:
88
+ context.write({'code': -1, 'msg': '获取新版本信息失败!'})
89
+ except:
90
+ context.write({'code': -1, 'msg': '获取新版本信息失败!'})
91
+ else:
92
+ data = update_info.get('update', 'updateinfo')
93
+ try:
94
+ data = tornado.escape.json_decode(data)
95
+ except:
96
+ data = None
97
+
98
+ context.write({'code': 0, 'msg': '', 'data': data})
99
+
100
+ context.finish()
101
+
102
+
103
+ def handle_post(context, section):
104
+ """Handle POST requests for settings."""
105
+ if section == 'auth':
106
+ if context.config.get('runtime', 'mode') == 'demo':
107
+ context.write({'code': -1, 'msg': '演示模式不允许修改用户名和密码!'})
108
+ return
109
+
110
+ username = context.get_argument('username', '')
111
+ password = context.get_argument('password', '')
112
+ passwordc = context.get_argument('passwordc', '')
113
+ passwordcheck = context.get_argument('passwordcheck', '')
114
+
115
+ if username == '':
116
+ context.write({'code': -1, 'msg': '用户名不能为空!'})
117
+ return
118
+ if password != passwordc:
119
+ context.write({'code': -1, 'msg': '两次密码输入不一致!'})
120
+ return
121
+
122
+ if passwordcheck != 'on':
123
+ passwordcheck = 'off'
124
+ context.config.set('auth', 'passwordcheck', passwordcheck)
125
+
126
+ if username != '':
127
+ context.config.set('auth', 'username', username)
128
+ if password != '':
129
+ key = md5(utils.randstr().encode('utf-8')).hexdigest()
130
+ pwd = hmac_new(key.encode('utf-8'), password.encode('utf-8'), md5).hexdigest()
131
+ context.config.set('auth', 'password', '%s:%s' % (pwd, key))
132
+
133
+ context.write({'code': 0, 'msg': '账号设置更新成功!'})
134
+
135
+ elif section == 'server':
136
+ if context.config.get('runtime', 'mode') == 'demo':
137
+ context.write({'code': -1, 'msg': '演示模式不允许修改服务绑定地址!'})
138
+ return
139
+
140
+ ip = context.get_argument('ip', '*')
141
+ if ip != '*' and ip != '':
142
+ if not utils.is_valid_ip(ip):
143
+ context.write({'code': -1, 'msg': '%s 不是有效的IP地址!' % ip})
144
+ return
145
+ netifaces = server.ServerInfo.netifaces()
146
+ ips = [netiface['ip'] for netiface in netifaces]
147
+ if not ip in ips:
148
+ msg = '<p>%s 不是该服务器的IP地址!</p><p>可用的IP地址有:<br>%s</p>' % (ip, '<br>'.join(ips))
149
+ context.write({'code': -1, 'msg': msg})
150
+ return
151
+
152
+ port = context.get_argument('port', '14433')
153
+ port = int(port)
154
+ if not port > 5000 and port < 65535:
155
+ context.write({'code': -1, 'msg': '端口范围必须在 5000 到 65535 之间!'})
156
+ return
157
+
158
+ context.config.set('server', 'ip', ip)
159
+ context.config.set('server', 'port', port)
160
+
161
+ sslkey = context.get_argument('sslkey', '')
162
+ if sslkey == '' or Path(sslkey).is_file():
163
+ context.config.set('server', 'sslkey', sslkey)
164
+ else:
165
+ context.write({'code': -1, 'msg': 'SSL私钥文件不存在,请仔细检查!'})
166
+ return
167
+
168
+ sslcrt = context.get_argument('sslcrt', '')
169
+ if sslcrt == '' or Path(sslcrt).is_file():
170
+ context.config.set('server', 'sslcrt', sslcrt)
171
+ else:
172
+ context.write({'code': -1, 'msg': 'SSL证书文件不存在,请仔细检查!'})
173
+ return
174
+
175
+ forcehttps = context.get_argument('forcehttps', '')
176
+ if forcehttps == 'on':
177
+ if not sslkey or not Path(sslkey).is_file():
178
+ context.config.set('server', 'forcehttps', 'off')
179
+ context.write({'code': -1, 'msg': '请填写SSL私钥文件!'})
180
+ return
181
+ elif not sslcrt or not Path(sslcrt).is_file():
182
+ context.config.set('server', 'forcehttps', 'off')
183
+ context.write({'code': -1, 'msg': '请填写SSL证书文件!'})
184
+ return
185
+ context.config.set('server', 'forcehttps', forcehttps)
186
+ else:
187
+ context.config.set('server', 'forcehttps', 'off')
188
+
189
+ context.write({'code': 0, 'msg': '服务设置更新成功!将在重启服务后生效。'})
190
+
191
+ elif section == 'runtime':
192
+ if context.config.get('runtime', 'mode') == 'demo':
193
+ context.write({'code': -1, 'msg': '演示模式不允许修改到其他模式!'})
194
+ return
195
+
196
+ mode = context.get_argument('mode', 'prod')
197
+ if mode not in ('prod', 'demo'):
198
+ mode = 'prod'
199
+ context.config.set('runtime', 'mode', mode)
200
+
201
+ context.write({'code': 0, 'msg': '运行设置更新成功!'})
202
+
203
+ elif section == 'accesskey':
204
+ if context.config.get('runtime', 'mode') == 'demo':
205
+ context.write({'code': -1, 'msg': '演示模式不允许修改远程控制设置!'})
206
+ return
207
+
208
+ accesskey = context.get_argument('accesskey', '')
209
+ accesskeyenable = context.get_argument('accesskeyenable', '')
210
+
211
+ if accesskeyenable == 'on' and accesskey == '':
212
+ context.write({'code': -1, 'msg': '远程控制密钥不能为空!'})
213
+ return
214
+
215
+ if accesskey != '':
216
+ try:
217
+ if len(b64decode(accesskey)) != 32:
218
+ raise Exception()
219
+ except:
220
+ context.write({'code': -1, 'msg': '远程控制密钥格式不正确!'})
221
+ return
222
+
223
+ if accesskeyenable != 'on':
224
+ accesskeyenable = 'off'
225
+ context.config.set('auth', 'accesskeyenable', accesskeyenable)
226
+ context.config.set('auth', 'accesskey', accesskey)
227
+
228
+ context.write({'code': 0, 'msg': '远程控制设置更新成功!'})
inpanel/mod/shell.py ADDED
@@ -0,0 +1,78 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (c) 2017-2026 Jackson Dou
4
+ # All rights reserved.
5
+ #
6
+ # InPanel is distributed under the terms of the New BSD License.
7
+ # The full license can be found in 'LICENSE'.
8
+ '''Module for Shell.'''
9
+
10
+ import shlex
11
+ import subprocess as sbps
12
+ from asyncio import create_subprocess_shell, create_task
13
+ from asyncio import subprocess as async_subprocess
14
+
15
+
16
+ async def async_command(command):
17
+ '''async command'''
18
+ proc = await create_subprocess_shell(command,
19
+ stdout=async_subprocess.PIPE,
20
+ stderr=async_subprocess.PIPE,
21
+ shell=True)
22
+ output, _ = await proc.communicate()
23
+ return (proc.returncode, output.decode('utf-8'))
24
+
25
+
26
+ async def async_task(func, *args, **kwds):
27
+ '''Make an asynchronous function.'''
28
+ async def wrapper():
29
+ callback = None
30
+ if hasattr(kwds, 'callback'):
31
+ callback = kwds['callback']
32
+ if callback:
33
+ del kwds['callback']
34
+ result = func(*args, **kwds)
35
+ print('result-origin', result)
36
+ if callback:
37
+ return callback(result)
38
+ else:
39
+ return result
40
+ result = await create_task(wrapper())
41
+ return result
42
+
43
+
44
+ def run(cmd, shell=False):
45
+ if shell:
46
+ return sbps.call(cmd, shell=shell)
47
+ else:
48
+ return sbps.call(shlex.split(cmd))
49
+
50
+
51
+ def exec_command(cmd, cwd):
52
+ '''executive command
53
+ '''
54
+ cmd = shlex.split(cmd)
55
+ cwd = cwd or '/'
56
+ # print('shell cmd', cmd)
57
+ try:
58
+ p = sbps.Popen(cmd,
59
+ stdout=sbps.PIPE,
60
+ stderr=sbps.PIPE,
61
+ close_fds=True,
62
+ shell=True,
63
+ cwd=cwd)
64
+ if p.wait() == 0:
65
+ result = p.stdout.read() # result = p.stdout.readlines()
66
+ # print(p.cc.read())
67
+ else:
68
+ result = p.stderr.read() # result = p.stderr.readlines()
69
+ return {'code': p.wait(), 'cwd': cwd, 'data': result, 'msg': 'success'}
70
+ except:
71
+ return {'code': -1, 'data': '', 'msg': 'error'}
72
+
73
+
74
+ if __name__ == '__main__':
75
+ print(exec_command('cd /var/db', '/var'))
76
+ # print(exec_command('who -a'))
77
+ # print(exec_command('ls')['code'] == 0)
78
+ # print(shlex.split('uname -a - b'))