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.
- inpanel/__init__.py +29 -0
- inpanel/acme.py +289 -0
- inpanel/aliyuncs.py +269 -0
- inpanel/app.py +514 -0
- inpanel/apt.py +223 -0
- inpanel/base.py +200 -0
- inpanel/config.py +139 -0
- inpanel/data/config.ini +29 -0
- inpanel/data/history +5 -0
- inpanel/data/runlogs.ini +12 -0
- inpanel/data/update_info.ini +4 -0
- inpanel/dnf.py +225 -0
- inpanel/lib/__init__.py +9 -0
- inpanel/lib/async_process.py +98 -0
- inpanel/lib/filelock.py +82 -0
- inpanel/lib/magic.py +233 -0
- inpanel/lib/pxssh.py +310 -0
- inpanel/lib/pyDes.py +867 -0
- inpanel/mod/__init__.py +0 -0
- inpanel/mod/apt.py +316 -0
- inpanel/mod/certificate.py +268 -0
- inpanel/mod/config.py +372 -0
- inpanel/mod/cron.py +315 -0
- inpanel/mod/disk.py +322 -0
- inpanel/mod/dnf.py +239 -0
- inpanel/mod/file.py +722 -0
- inpanel/mod/firewall.py +199 -0
- inpanel/mod/firewall_base.py +137 -0
- inpanel/mod/firewall_firewalld.py +170 -0
- inpanel/mod/firewall_iptables.py +176 -0
- inpanel/mod/firewall_macos.py +213 -0
- inpanel/mod/firewall_ufw.py +128 -0
- inpanel/mod/ftp.py +231 -0
- inpanel/mod/httpd.py +1205 -0
- inpanel/mod/lighttpd.py +26 -0
- inpanel/mod/login.py +98 -0
- inpanel/mod/mysql.py +577 -0
- inpanel/mod/named.py +26 -0
- inpanel/mod/nginx.py +2327 -0
- inpanel/mod/package.py +33 -0
- inpanel/mod/package_apt.py +62 -0
- inpanel/mod/package_base.py +77 -0
- inpanel/mod/package_dnf.py +62 -0
- inpanel/mod/package_map.py +366 -0
- inpanel/mod/package_yum.py +64 -0
- inpanel/mod/php.py +289 -0
- inpanel/mod/process.py +291 -0
- inpanel/mod/proftpd.py +26 -0
- inpanel/mod/pureftpd.py +26 -0
- inpanel/mod/query.py +108 -0
- inpanel/mod/server.py +1319 -0
- inpanel/mod/service.py +262 -0
- inpanel/mod/setting.py +228 -0
- inpanel/mod/shell.py +78 -0
- inpanel/mod/ssh.py +280 -0
- inpanel/mod/system.py +220 -0
- inpanel/mod/task.py +1337 -0
- inpanel/mod/ufw.py +14 -0
- inpanel/mod/user.py +306 -0
- inpanel/mod/vsftpd.py +67 -0
- inpanel/mod/yum.py +243 -0
- inpanel/plugins/__init__.py +10 -0
- inpanel/plugins/__pycache__/__init__.cpython-313.pyc +0 -0
- inpanel/plugins/acme/static/index.html +10 -0
- inpanel/plugins/acme/static/index.js +13 -0
- inpanel/plugins/shadowsocks-libev/__pycache__/info.cpython-313.pyc +0 -0
- inpanel/plugins/shadowsocks-libev/info.py +73 -0
- inpanel/plugins/shadowsocks-libev/static/index.html +70 -0
- inpanel/plugins/shadowsocks-libev/static/index.js +18 -0
- inpanel/public/css/global.css +138 -0
- inpanel/public/favicon.ico +0 -0
- inpanel/public/images/alipay.jpg +0 -0
- inpanel/public/images/loading.gif +0 -0
- inpanel/public/images/weixin.jpg +0 -0
- inpanel/public/index.html +94 -0
- inpanel/public/js/controllers/controllers.js +566 -0
- inpanel/public/js/controllers/database.js +590 -0
- inpanel/public/js/controllers/ecs.js +624 -0
- inpanel/public/js/controllers/file.js +940 -0
- inpanel/public/js/controllers/plugins.js +105 -0
- inpanel/public/js/controllers/service.js +1460 -0
- inpanel/public/js/controllers/site.js +1241 -0
- inpanel/public/js/controllers/utils.js +2319 -0
- inpanel/public/js/core.js +221 -0
- inpanel/public/js/directives.js +784 -0
- inpanel/public/js/filters.js +395 -0
- inpanel/public/js/services.js +257 -0
- inpanel/public/lib/angular/1.0.2/angular-resource.min.js +10 -0
- inpanel/public/lib/angular/1.0.2/angular.min.js +158 -0
- inpanel/public/lib/bootstrap/css/bootstrap-theme.css +587 -0
- inpanel/public/lib/bootstrap/css/bootstrap-theme.css.map +1 -0
- inpanel/public/lib/bootstrap/css/bootstrap-theme.min.css +6 -0
- inpanel/public/lib/bootstrap/css/bootstrap-theme.min.css.map +1 -0
- inpanel/public/lib/bootstrap/css/bootstrap.css +6757 -0
- inpanel/public/lib/bootstrap/css/bootstrap.css.map +1 -0
- inpanel/public/lib/bootstrap/css/bootstrap.min.css +6 -0
- inpanel/public/lib/bootstrap/css/bootstrap.min.css.map +1 -0
- inpanel/public/lib/bootstrap/fonts/glyphicons-halflings-regular.eot +0 -0
- inpanel/public/lib/bootstrap/fonts/glyphicons-halflings-regular.svg +288 -0
- inpanel/public/lib/bootstrap/fonts/glyphicons-halflings-regular.ttf +0 -0
- inpanel/public/lib/bootstrap/fonts/glyphicons-halflings-regular.woff +0 -0
- inpanel/public/lib/bootstrap/fonts/glyphicons-halflings-regular.woff2 +0 -0
- inpanel/public/lib/bootstrap/img/glyphicons-halflings-white.png +0 -0
- inpanel/public/lib/bootstrap/img/glyphicons-halflings.png +0 -0
- inpanel/public/lib/bootstrap/js/bootstrap.js +2377 -0
- inpanel/public/lib/bootstrap/js/bootstrap.min.js +7 -0
- inpanel/public/lib/bootstrap/js/npm.js +13 -0
- inpanel/public/lib/codemirror/codemirror-compressed.js +1 -0
- inpanel/public/lib/codemirror/codemirror.css +173 -0
- inpanel/public/lib/jquery/jquery-1.12.4.min.js +5 -0
- inpanel/public/lib/md5/md5.js +379 -0
- inpanel/public/lib/md5/md5.min.js +1 -0
- inpanel/public/partials/database/index.html +172 -0
- inpanel/public/partials/database/mysql/dbedit.html +462 -0
- inpanel/public/partials/database/mysql/dbnew.html +348 -0
- inpanel/public/partials/database/mysql/useredit.html +310 -0
- inpanel/public/partials/database/mysql/usernew.html +97 -0
- inpanel/public/partials/directives/message.html +20 -0
- inpanel/public/partials/directives/navbar.html +25 -0
- inpanel/public/partials/directives/selector.html +57 -0
- inpanel/public/partials/directives/srvbase.html +72 -0
- inpanel/public/partials/directives/srvext.html +34 -0
- inpanel/public/partials/directives/srvfile.html +23 -0
- inpanel/public/partials/directives/srvinstall.html +34 -0
- inpanel/public/partials/directives/srvlog.html +23 -0
- inpanel/public/partials/directives/srvminiop.html +28 -0
- inpanel/public/partials/directives/srvuninstall.html +38 -0
- inpanel/public/partials/directives/srvupdate.html +30 -0
- inpanel/public/partials/ecs/account.html +156 -0
- inpanel/public/partials/ecs/ecs.html +198 -0
- inpanel/public/partials/ecs/index.html +40 -0
- inpanel/public/partials/ecs/setings.html +459 -0
- inpanel/public/partials/file/file.html +501 -0
- inpanel/public/partials/file/trash.html +95 -0
- inpanel/public/partials/ftp.html +36 -0
- inpanel/public/partials/log.html +83 -0
- inpanel/public/partials/login.html +79 -0
- inpanel/public/partials/logout.html +22 -0
- inpanel/public/partials/main.html +534 -0
- inpanel/public/partials/plugins/index.html +55 -0
- inpanel/public/partials/plugins/plugins.html +13 -0
- inpanel/public/partials/secure.html +13 -0
- inpanel/public/partials/service/apache.html +147 -0
- inpanel/public/partials/service/cron.html +71 -0
- inpanel/public/partials/service/index.html +293 -0
- inpanel/public/partials/service/iptables.html +40 -0
- inpanel/public/partials/service/lighttpd.html +63 -0
- inpanel/public/partials/service/mariadb.html +113 -0
- inpanel/public/partials/service/memcache.html +36 -0
- inpanel/public/partials/service/minio.html +44 -0
- inpanel/public/partials/service/mongodb.html +44 -0
- inpanel/public/partials/service/mysql.html +113 -0
- inpanel/public/partials/service/named.html +61 -0
- inpanel/public/partials/service/nginx.html +296 -0
- inpanel/public/partials/service/ntp.html +40 -0
- inpanel/public/partials/service/php.html +250 -0
- inpanel/public/partials/service/proftpd.html +59 -0
- inpanel/public/partials/service/pureftpd.html +59 -0
- inpanel/public/partials/service/redis.html +40 -0
- inpanel/public/partials/service/samba.html +58 -0
- inpanel/public/partials/service/sendmail.html +40 -0
- inpanel/public/partials/service/ssh.html +186 -0
- inpanel/public/partials/service/tomcat.html +65 -0
- inpanel/public/partials/service/vsftpd.html +195 -0
- inpanel/public/partials/setting.html +299 -0
- inpanel/public/partials/site/apache/site.html +277 -0
- inpanel/public/partials/site/index.html +344 -0
- inpanel/public/partials/site/nginx/site.html +558 -0
- inpanel/public/partials/sorry.html +19 -0
- inpanel/public/partials/storage/autofm.html +103 -0
- inpanel/public/partials/storage/backup.html +42 -0
- inpanel/public/partials/storage/index.html +360 -0
- inpanel/public/partials/storage/migrate.html +93 -0
- inpanel/public/partials/storage/movedata.html +98 -0
- inpanel/public/partials/storage/remote.html +77 -0
- inpanel/public/partials/utils/cron.html +486 -0
- inpanel/public/partials/utils/firewall.html +266 -0
- inpanel/public/partials/utils/index.html +193 -0
- inpanel/public/partials/utils/network.html +118 -0
- inpanel/public/partials/utils/process.html +239 -0
- inpanel/public/partials/utils/repository.html +373 -0
- inpanel/public/partials/utils/shell.html +28 -0
- inpanel/public/partials/utils/ssl.html +319 -0
- inpanel/public/partials/utils/time.html +109 -0
- inpanel/public/partials/utils/user.html +359 -0
- inpanel/public/robots.txt +2 -0
- inpanel/remote.py +109 -0
- inpanel/templates/file/preview.html +34 -0
- inpanel/templates/index.html +93 -0
- inpanel/utils.py +282 -0
- inpanel/web.py +1811 -0
- inpanel/yum.py +756 -0
- inpanel-1.2.1.dist-info/METADATA +399 -0
- inpanel-1.2.1.dist-info/RECORD +198 -0
- inpanel-1.2.1.dist-info/WHEEL +5 -0
- inpanel-1.2.1.dist-info/entry_points.txt +2 -0
- inpanel-1.2.1.dist-info/licenses/LICENSE +30 -0
- 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'))
|