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/app.py
ADDED
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
#
|
|
4
|
+
# Copyright (c) 2017-2026 Jackson Dou
|
|
5
|
+
# All rights reserved.
|
|
6
|
+
#
|
|
7
|
+
# InPanel is distributed under the terms of The New BSD License.
|
|
8
|
+
# The full license can be found in 'LICENSE'.
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
import signal
|
|
12
|
+
import sys
|
|
13
|
+
import errno
|
|
14
|
+
import time
|
|
15
|
+
import shutil
|
|
16
|
+
import subprocess
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
import logging
|
|
20
|
+
from tornado import httpserver, ioloop
|
|
21
|
+
|
|
22
|
+
from . import web
|
|
23
|
+
from .base import pidfile, logfile, logerror, config_file, config_path, logging_path, run_type, DEBUG, root_path
|
|
24
|
+
from .mod.process import remove_pid_file, save_pid_file, WebRequestProcess
|
|
25
|
+
from .mod.config import load_config
|
|
26
|
+
from .utils import make_cookie_secret
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _read_pid(pidfile):
|
|
30
|
+
"""read pid from pidfile, return None if not exists or invalid"""
|
|
31
|
+
pidfile_path = Path(pidfile)
|
|
32
|
+
if not pidfile_path.exists():
|
|
33
|
+
return None
|
|
34
|
+
try:
|
|
35
|
+
with open(pidfile, 'r', encoding='utf-8') as f:
|
|
36
|
+
return int(f.read().strip())
|
|
37
|
+
except (ValueError, IOError):
|
|
38
|
+
return None
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _is_running(pid):
|
|
42
|
+
"""check if process with pid is running"""
|
|
43
|
+
if pid is None or pid <= 0:
|
|
44
|
+
return False
|
|
45
|
+
try:
|
|
46
|
+
os.kill(pid, 0)
|
|
47
|
+
return True
|
|
48
|
+
except OSError:
|
|
49
|
+
return False
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _wait_stop(pid, timeout=10):
|
|
53
|
+
"""wait for process to stop, return True if stopped"""
|
|
54
|
+
for _ in range(timeout * 2):
|
|
55
|
+
if not _is_running(pid):
|
|
56
|
+
return True
|
|
57
|
+
time.sleep(0.5)
|
|
58
|
+
return False
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _print_help():
|
|
62
|
+
print('''Usage: inpanel <command> [options]
|
|
63
|
+
|
|
64
|
+
Service Control:
|
|
65
|
+
start Start the InPanel service (background)
|
|
66
|
+
stop Stop the InPanel service
|
|
67
|
+
status Show the InPanel service status
|
|
68
|
+
restart Restart the InPanel service
|
|
69
|
+
reload Reload the InPanel service (restart)
|
|
70
|
+
run Run InPanel in foreground (for debug / systemd)
|
|
71
|
+
|
|
72
|
+
Configuration:
|
|
73
|
+
config Configuration management (use 'inpanel config' for help)
|
|
74
|
+
|
|
75
|
+
Package Management:
|
|
76
|
+
uninstall [--purge] [--purge-config] [--purge-logs]
|
|
77
|
+
Uninstall InPanel
|
|
78
|
+
--purge Remove all config and log files
|
|
79
|
+
--purge-config Remove config files only
|
|
80
|
+
--purge-logs Remove log files only
|
|
81
|
+
|
|
82
|
+
Other:
|
|
83
|
+
version Show version information
|
|
84
|
+
help Show this help message
|
|
85
|
+
|
|
86
|
+
Examples:
|
|
87
|
+
inpanel start
|
|
88
|
+
inpanel stop
|
|
89
|
+
inpanel status
|
|
90
|
+
inpanel config list
|
|
91
|
+
inpanel uninstall
|
|
92
|
+
inpanel uninstall --purge
|
|
93
|
+
''')
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def cmd_start():
|
|
97
|
+
"""start the service in background"""
|
|
98
|
+
pid = _read_pid(pidfile)
|
|
99
|
+
if pid and _is_running(pid):
|
|
100
|
+
print(f'InPanel is already running with PID: {pid}')
|
|
101
|
+
sys.exit(1)
|
|
102
|
+
|
|
103
|
+
if pid and not _is_running(pid):
|
|
104
|
+
remove_pid_file(pidfile)
|
|
105
|
+
|
|
106
|
+
try:
|
|
107
|
+
Path(logfile).parent.mkdir(parents=True, exist_ok=True)
|
|
108
|
+
Path(logerror).parent.mkdir(parents=True, exist_ok=True)
|
|
109
|
+
except PermissionError:
|
|
110
|
+
print('[错误] 没有权限创建日志目录!')
|
|
111
|
+
print(f' 需要创建目录: {Path(logfile).parent}')
|
|
112
|
+
print('[提示] 请使用 sudo 或以 root 用户运行:')
|
|
113
|
+
print(' sudo inpanel start')
|
|
114
|
+
sys.exit(1)
|
|
115
|
+
|
|
116
|
+
try:
|
|
117
|
+
pid = os.fork()
|
|
118
|
+
if pid > 0:
|
|
119
|
+
time.sleep(1)
|
|
120
|
+
child_pid = _read_pid(pidfile)
|
|
121
|
+
if child_pid and _is_running(child_pid):
|
|
122
|
+
print(f'InPanel: started with PID: {child_pid}')
|
|
123
|
+
config = load_config(config_file)
|
|
124
|
+
server_port = config.get('server', 'port')
|
|
125
|
+
force_https = config.getboolean('server', 'forcehttps')
|
|
126
|
+
print(f'InPanel: running on: http{"s" if force_https else ""}://*:{server_port}')
|
|
127
|
+
else:
|
|
128
|
+
print('InPanel: failed to start, check log file: %s' % logfile)
|
|
129
|
+
sys.exit(1)
|
|
130
|
+
return
|
|
131
|
+
except OSError as e:
|
|
132
|
+
print(f'InPanel: fork failed: {e}')
|
|
133
|
+
sys.exit(1)
|
|
134
|
+
|
|
135
|
+
# child process: detach and run server
|
|
136
|
+
os.setsid()
|
|
137
|
+
os.umask(0)
|
|
138
|
+
|
|
139
|
+
# redirect stdin/stdout/stderr to log file
|
|
140
|
+
sys.stdout.flush()
|
|
141
|
+
sys.stderr.flush()
|
|
142
|
+
with open('/dev/null', 'r') as f:
|
|
143
|
+
os.dup2(f.fileno(), sys.stdin.fileno())
|
|
144
|
+
with open(logfile, 'a') as f:
|
|
145
|
+
os.dup2(f.fileno(), sys.stdout.fileno())
|
|
146
|
+
with open(logerror, 'a') as f:
|
|
147
|
+
os.dup2(f.fileno(), sys.stderr.fileno())
|
|
148
|
+
|
|
149
|
+
run_server()
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def cmd_stop():
|
|
153
|
+
"""stop the service"""
|
|
154
|
+
pid = _read_pid(pidfile)
|
|
155
|
+
if not pid:
|
|
156
|
+
print('InPanel is not running (no pid file)')
|
|
157
|
+
sys.exit(0)
|
|
158
|
+
if not _is_running(pid):
|
|
159
|
+
print('InPanel is not running (stale pid file)')
|
|
160
|
+
remove_pid_file(pidfile)
|
|
161
|
+
sys.exit(0)
|
|
162
|
+
try:
|
|
163
|
+
os.kill(pid, signal.SIGTERM)
|
|
164
|
+
print(f'InPanel: stopping (PID: {pid})...')
|
|
165
|
+
if _wait_stop(pid, 10):
|
|
166
|
+
remove_pid_file(pidfile)
|
|
167
|
+
print('InPanel: stopped')
|
|
168
|
+
else:
|
|
169
|
+
print('InPanel: stop timeout, trying force kill...')
|
|
170
|
+
os.kill(pid, signal.SIGKILL)
|
|
171
|
+
time.sleep(1)
|
|
172
|
+
remove_pid_file(pidfile)
|
|
173
|
+
print('InPanel: force killed')
|
|
174
|
+
except OSError as e:
|
|
175
|
+
print(f'InPanel: stop failed: {e}')
|
|
176
|
+
remove_pid_file(pidfile)
|
|
177
|
+
sys.exit(1)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def cmd_status():
|
|
181
|
+
"""show service status"""
|
|
182
|
+
pid = _read_pid(pidfile)
|
|
183
|
+
if not pid:
|
|
184
|
+
print('InPanel: not running')
|
|
185
|
+
sys.exit(1)
|
|
186
|
+
if not _is_running(pid):
|
|
187
|
+
print(f'InPanel: not running (removing stale pid file: {pid})')
|
|
188
|
+
remove_pid_file(pidfile)
|
|
189
|
+
sys.exit(1)
|
|
190
|
+
config = load_config(config_file)
|
|
191
|
+
server_port = config.get('server', 'port')
|
|
192
|
+
force_https = config.getboolean('server', 'forcehttps')
|
|
193
|
+
mode = config.get('runtime', 'mode')
|
|
194
|
+
print(f'InPanel: running (PID: {pid})')
|
|
195
|
+
print(f'InPanel: port: {server_port}, mode: {mode}, https: {force_https}')
|
|
196
|
+
sys.exit(0)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def cmd_restart():
|
|
200
|
+
"""restart the service"""
|
|
201
|
+
pid = _read_pid(pidfile)
|
|
202
|
+
if pid and _is_running(pid):
|
|
203
|
+
cmd_stop()
|
|
204
|
+
time.sleep(1)
|
|
205
|
+
cmd_start()
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def cmd_reload():
|
|
209
|
+
"""reload the service (same as restart)"""
|
|
210
|
+
print('InPanel: reloading (restart)...')
|
|
211
|
+
cmd_restart()
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def cmd_uninstall(args=None):
|
|
215
|
+
"""uninstall inpanel"""
|
|
216
|
+
if os.geteuid() != 0:
|
|
217
|
+
print('Error: uninstall must be run as root.')
|
|
218
|
+
sys.exit(1)
|
|
219
|
+
|
|
220
|
+
purge_config = False
|
|
221
|
+
purge_logs = False
|
|
222
|
+
if args:
|
|
223
|
+
for arg in args:
|
|
224
|
+
if arg == '--purge-config' or arg == '--purge':
|
|
225
|
+
purge_config = True
|
|
226
|
+
purge_logs = True
|
|
227
|
+
elif arg == '--purge-logs':
|
|
228
|
+
purge_logs = True
|
|
229
|
+
|
|
230
|
+
pid = _read_pid(pidfile)
|
|
231
|
+
if pid and _is_running(pid):
|
|
232
|
+
print('Stopping InPanel service...')
|
|
233
|
+
try:
|
|
234
|
+
os.kill(pid, signal.SIGTERM)
|
|
235
|
+
if not _wait_stop(pid, 10):
|
|
236
|
+
os.kill(pid, signal.SIGKILL)
|
|
237
|
+
time.sleep(1)
|
|
238
|
+
except OSError:
|
|
239
|
+
pass
|
|
240
|
+
remove_pid_file(pidfile)
|
|
241
|
+
|
|
242
|
+
bin_path = '/usr/bin/inpanel'
|
|
243
|
+
systemd_service = '/usr/lib/systemd/system/inpanel.service'
|
|
244
|
+
systemd_service_etc = '/etc/systemd/system/inpanel.service'
|
|
245
|
+
initd_script = '/etc/init.d/inpanel'
|
|
246
|
+
|
|
247
|
+
print('Uninstalling InPanel...')
|
|
248
|
+
|
|
249
|
+
# remove systemd service
|
|
250
|
+
if os.path.exists(systemd_service):
|
|
251
|
+
try:
|
|
252
|
+
subprocess.run(['systemctl', 'stop', 'inpanel'], stderr=subprocess.DEVNULL)
|
|
253
|
+
subprocess.run(['systemctl', 'disable', 'inpanel'], stderr=subprocess.DEVNULL)
|
|
254
|
+
except Exception:
|
|
255
|
+
pass
|
|
256
|
+
os.remove(systemd_service)
|
|
257
|
+
print(f' removed: {systemd_service}')
|
|
258
|
+
if os.path.exists(systemd_service_etc):
|
|
259
|
+
os.remove(systemd_service_etc)
|
|
260
|
+
print(f' removed: {systemd_service_etc}')
|
|
261
|
+
try:
|
|
262
|
+
subprocess.run(['systemctl', 'daemon-reload'], stderr=subprocess.DEVNULL)
|
|
263
|
+
except Exception:
|
|
264
|
+
pass
|
|
265
|
+
|
|
266
|
+
# remove init.d script
|
|
267
|
+
if os.path.exists(initd_script):
|
|
268
|
+
try:
|
|
269
|
+
subprocess.run(['chkconfig', '--del', 'inpanel'], stderr=subprocess.DEVNULL)
|
|
270
|
+
except Exception:
|
|
271
|
+
pass
|
|
272
|
+
try:
|
|
273
|
+
subprocess.run(['update-rc.d', '-f', 'inpanel', 'remove'], stderr=subprocess.DEVNULL)
|
|
274
|
+
except Exception:
|
|
275
|
+
pass
|
|
276
|
+
os.remove(initd_script)
|
|
277
|
+
print(f' removed: {initd_script}')
|
|
278
|
+
|
|
279
|
+
# remove binary / console script
|
|
280
|
+
if os.path.exists(bin_path):
|
|
281
|
+
os.remove(bin_path)
|
|
282
|
+
print(f' removed: {bin_path}')
|
|
283
|
+
|
|
284
|
+
# remove python package via pip
|
|
285
|
+
if run_type == 'system':
|
|
286
|
+
try:
|
|
287
|
+
subprocess.run([sys.executable, '-m', 'pip', 'uninstall', '-y', 'inpanel'],
|
|
288
|
+
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
289
|
+
print(' removed: inpanel python package')
|
|
290
|
+
except Exception:
|
|
291
|
+
# try to find and remove manually
|
|
292
|
+
pkg_path = Path(__file__).parent.resolve()
|
|
293
|
+
if pkg_path.exists() and ('site-packages' in str(pkg_path) or 'dist-packages' in str(pkg_path)):
|
|
294
|
+
egg_info = pkg_path.parent / 'inpanel.egg-info'
|
|
295
|
+
if egg_info.exists():
|
|
296
|
+
shutil.rmtree(egg_info, ignore_errors=True)
|
|
297
|
+
shutil.rmtree(pkg_path, ignore_errors=True)
|
|
298
|
+
print(f' removed: {pkg_path}')
|
|
299
|
+
|
|
300
|
+
# remove config files (optional)
|
|
301
|
+
if purge_config and config_path and os.path.exists(config_path):
|
|
302
|
+
shutil.rmtree(config_path, ignore_errors=True)
|
|
303
|
+
print(f' removed: {config_path} (config files)')
|
|
304
|
+
|
|
305
|
+
# remove log files (optional)
|
|
306
|
+
if purge_logs and logging_path and os.path.exists(logging_path):
|
|
307
|
+
shutil.rmtree(logging_path, ignore_errors=True)
|
|
308
|
+
print(f' removed: {logging_path} (log files)')
|
|
309
|
+
|
|
310
|
+
pidfile_dir = os.path.dirname(pidfile)
|
|
311
|
+
if os.path.exists(pidfile):
|
|
312
|
+
os.remove(pidfile)
|
|
313
|
+
|
|
314
|
+
print()
|
|
315
|
+
print('InPanel uninstalled successfully.')
|
|
316
|
+
if not purge_config:
|
|
317
|
+
print(f'Config files are kept at: {config_path}')
|
|
318
|
+
print(f'Use --purge to remove all data.')
|
|
319
|
+
sys.exit(0)
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def run_server():
|
|
323
|
+
"""run the server in foreground"""
|
|
324
|
+
try:
|
|
325
|
+
Path(logfile).parent.mkdir(parents=True, exist_ok=True)
|
|
326
|
+
Path(logerror).parent.mkdir(parents=True, exist_ok=True)
|
|
327
|
+
except PermissionError:
|
|
328
|
+
print('[错误] 没有权限创建日志目录!')
|
|
329
|
+
print(f' 需要创建目录: {Path(logfile).parent}')
|
|
330
|
+
print('[提示] 请使用 sudo 或以 root 用户运行:')
|
|
331
|
+
print(' sudo inpanel start')
|
|
332
|
+
sys.exit(1)
|
|
333
|
+
|
|
334
|
+
logging.basicConfig(
|
|
335
|
+
filename=logfile,
|
|
336
|
+
level=logging.INFO,
|
|
337
|
+
format='%(asctime)s - %(levelname)s - %(message)s'
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
error_handler = logging.FileHandler(logerror)
|
|
341
|
+
error_handler.setLevel(logging.ERROR)
|
|
342
|
+
error_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
|
|
343
|
+
error_handler.setFormatter(error_formatter)
|
|
344
|
+
logging.getLogger().addHandler(error_handler)
|
|
345
|
+
|
|
346
|
+
print('InPanel: starting')
|
|
347
|
+
logging.info('InPanel: starting')
|
|
348
|
+
logging.info(f'InPanel: config file: {config_file}')
|
|
349
|
+
logging.info(f'InPanel: pid file: {pidfile}')
|
|
350
|
+
logging.info(f'InPanel: runtime type: {run_type}')
|
|
351
|
+
logging.info(f'InPanel: runtime path: {root_path}')
|
|
352
|
+
|
|
353
|
+
settings = {
|
|
354
|
+
'debug' : DEBUG,
|
|
355
|
+
'autoreload' : DEBUG,
|
|
356
|
+
'cookie_secret' : 'debug' if DEBUG else make_cookie_secret(),
|
|
357
|
+
'root_path' : root_path,
|
|
358
|
+
'data_path' : config_path,
|
|
359
|
+
'index_path' : str(Path(root_path) / 'templates' / 'index.html'),
|
|
360
|
+
'template_path' : str(Path(root_path) / 'templates'),
|
|
361
|
+
'static_path' : str(Path(root_path) / 'public'),
|
|
362
|
+
'plugins_path' : str(Path(root_path) / 'plugins'),
|
|
363
|
+
'xsrf_cookies' : True,
|
|
364
|
+
'gzip' : True
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
router = [
|
|
368
|
+
(r'/api/xsrf', web.XsrfHandler),
|
|
369
|
+
(r'/api/authstatus', web.AuthStatusHandler),
|
|
370
|
+
(r'/api/login', web.LoginHandler),
|
|
371
|
+
(r'/api/logout', web.LogoutHandler),
|
|
372
|
+
(r'/api/query/(.+)', web.QueryHandler),
|
|
373
|
+
(r'/api/network/(.+?)(?:/(.+))?', web.UtilsNetworkHandler),
|
|
374
|
+
(r'/api/process/(.+?)(?:/(.+))?', WebRequestProcess),
|
|
375
|
+
(r'/api/time/(.+?)(?:/(.+))?', web.UtilsTimeHandler),
|
|
376
|
+
(r'/api/ssl/(.+?)(?:/(.+))?', web.SSLTLSHandler),
|
|
377
|
+
(r'/api/repos/yum/(.+?)(?:/(.+))?', web.RepoYumHandler),
|
|
378
|
+
(r'/api/repos/dnf/(.+?)(?:/(.+))?', web.RepoDnfHandler),
|
|
379
|
+
(r'/api/repos/apt/(.+?)(?:/(.+))?', web.RepoAptHandler),
|
|
380
|
+
(r'/api/firewall/(.+)', web.FirewallHandler),
|
|
381
|
+
(r'/api/setting/(.+)', web.SettingHandler),
|
|
382
|
+
(r'/api/operation/(.+)', web.OperationHandler),
|
|
383
|
+
(r'/page/file/preview/(.+)', web.FilePreviewHandler),
|
|
384
|
+
(r'/page/(.+)/(.+)', web.PageHandler),
|
|
385
|
+
(r'/api/backend/(.+)', web.BackendHandler),
|
|
386
|
+
(r'/api/sitepackage/(.+)', web.SitePackageHandler),
|
|
387
|
+
(r'/api/client/(.+)', web.ClientHandler),
|
|
388
|
+
(r'/api/plugins/(.*)', web.StaticFileHandler, { 'path': settings['plugins_path']}),
|
|
389
|
+
(r'/api/file/download/(.+)', web.FileDownloadHandler, { 'path': '/' }),
|
|
390
|
+
(r'/api/file/upload', web.FileUploadHandler),
|
|
391
|
+
(r'/api/version', web.VersionHandler),
|
|
392
|
+
(r'/((?:css|js|js.min|lib|partials|images|favicon\.ico|robots\.txt)(?:\/.*)?)', web.StaticFileHandler, { 'path': settings['static_path'] }),
|
|
393
|
+
(r'/', web.IndexHandler),
|
|
394
|
+
(r'/($)', web.StaticFileHandler, { 'path': settings['index_path'] }),
|
|
395
|
+
(r'/.*', web.ErrorHandler, { 'status_code': 404 })
|
|
396
|
+
]
|
|
397
|
+
|
|
398
|
+
application = web.Application(router, **settings)
|
|
399
|
+
|
|
400
|
+
config = load_config(config_file)
|
|
401
|
+
mode = config.get('runtime', 'mode')
|
|
402
|
+
|
|
403
|
+
logging.info(f'InPanel: runtime mode: {mode}')
|
|
404
|
+
print(f'InPanel: runtime mode: {mode}')
|
|
405
|
+
|
|
406
|
+
server_ip = config.get('server', 'ip')
|
|
407
|
+
server_port = config.get('server', 'port')
|
|
408
|
+
if server_ip == '*':
|
|
409
|
+
server_ip = ''
|
|
410
|
+
force_https = config.getboolean('server', 'forcehttps')
|
|
411
|
+
sslkey = config.get('server', 'sslkey')
|
|
412
|
+
sslcrt = config.get('server', 'sslcrt')
|
|
413
|
+
|
|
414
|
+
ssl = {'certfile': sslcrt, 'keyfile': sslkey} if force_https else None
|
|
415
|
+
|
|
416
|
+
server = httpserver.HTTPServer(application, ssl_options=ssl)
|
|
417
|
+
try:
|
|
418
|
+
server.listen(port=server_port, address=server_ip)
|
|
419
|
+
except OSError as err:
|
|
420
|
+
if err.errno in (errno.EADDRINUSE, 48):
|
|
421
|
+
error_msg = f'端口 {server_port} 已被占用!'
|
|
422
|
+
print(f'\n[错误] {error_msg}')
|
|
423
|
+
print('[提示] 请先停止占用该端口的进程,或修改配置文件中的端口号。')
|
|
424
|
+
print(f'[命令] lsof -i :{server_port} # 查看占用进程')
|
|
425
|
+
print('[命令] kill -9 <PID> # 终止占用进程')
|
|
426
|
+
logging.error(f'{error_msg} 请检查端口占用情况')
|
|
427
|
+
elif err.errno in (errno.EACCES, 13):
|
|
428
|
+
error_msg = f'没有权限绑定端口 {server_port}!'
|
|
429
|
+
print(f'\n[错误] {error_msg}')
|
|
430
|
+
print('[提示] 端口号小于 1024 需要 root 权限运行。')
|
|
431
|
+
logging.error(f'{error_msg} 需要 root 权限')
|
|
432
|
+
else:
|
|
433
|
+
error_msg = f'绑定端口失败:{err}'
|
|
434
|
+
print(f'\n[错误] {error_msg}')
|
|
435
|
+
logging.error(error_msg)
|
|
436
|
+
sys.exit(1)
|
|
437
|
+
|
|
438
|
+
pid = os.getpid()
|
|
439
|
+
save_pid_file(pidfile, pid)
|
|
440
|
+
|
|
441
|
+
logging.info(f'InPanel: started with PID: {pid}')
|
|
442
|
+
print(f'InPanel: started with PID: {pid}')
|
|
443
|
+
|
|
444
|
+
logging.info(f'InPanel: running on: http{"s" if force_https else ""}://{server_ip}:{server_port}')
|
|
445
|
+
print(f'InPanel: running on: http{"s" if force_https else ""}://{server_ip}:{server_port}')
|
|
446
|
+
|
|
447
|
+
def shutdown_handler(signum, frame):
|
|
448
|
+
logging.info('InPanel: shutting down')
|
|
449
|
+
print()
|
|
450
|
+
print('InPanel: shutting down')
|
|
451
|
+
remove_pid_file(pidfile)
|
|
452
|
+
logging.info(f"InPanel: PID file {pidfile} removed")
|
|
453
|
+
ioloop.IOLoop.current().add_callback(ioloop.IOLoop.current().stop)
|
|
454
|
+
|
|
455
|
+
signal.signal(signal.SIGTERM, shutdown_handler)
|
|
456
|
+
signal.signal(signal.SIGINT, shutdown_handler)
|
|
457
|
+
|
|
458
|
+
ioloop.IOLoop.current().start()
|
|
459
|
+
|
|
460
|
+
logging.info('InPanel: stopped')
|
|
461
|
+
print('InPanel: stopped')
|
|
462
|
+
sys.exit(0)
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
def main():
|
|
466
|
+
# route 'config' subcommand to config module
|
|
467
|
+
if len(sys.argv) > 1 and sys.argv[1] == 'config':
|
|
468
|
+
from .config import main as config_main
|
|
469
|
+
config_main(sys.argv[2:])
|
|
470
|
+
return
|
|
471
|
+
|
|
472
|
+
# service control commands
|
|
473
|
+
if len(sys.argv) > 1:
|
|
474
|
+
cmd = sys.argv[1].lower()
|
|
475
|
+
if cmd == 'start':
|
|
476
|
+
cmd_start()
|
|
477
|
+
return
|
|
478
|
+
elif cmd == 'stop':
|
|
479
|
+
cmd_stop()
|
|
480
|
+
return
|
|
481
|
+
elif cmd == 'status':
|
|
482
|
+
cmd_status()
|
|
483
|
+
return
|
|
484
|
+
elif cmd == 'restart':
|
|
485
|
+
cmd_restart()
|
|
486
|
+
return
|
|
487
|
+
elif cmd == 'reload':
|
|
488
|
+
cmd_reload()
|
|
489
|
+
return
|
|
490
|
+
elif cmd == 'uninstall':
|
|
491
|
+
cmd_uninstall(sys.argv[2:])
|
|
492
|
+
return
|
|
493
|
+
elif cmd == 'run':
|
|
494
|
+
run_server()
|
|
495
|
+
return
|
|
496
|
+
elif cmd == 'version' or cmd == '-v' or cmd == '--version':
|
|
497
|
+
from . import __version__
|
|
498
|
+
print(f'InPanel v{__version__}')
|
|
499
|
+
return
|
|
500
|
+
elif cmd == 'help' or cmd == '-h' or cmd == '--help':
|
|
501
|
+
_print_help()
|
|
502
|
+
return
|
|
503
|
+
else:
|
|
504
|
+
print(f'Unknown command: {cmd}')
|
|
505
|
+
print()
|
|
506
|
+
_print_help()
|
|
507
|
+
sys.exit(1)
|
|
508
|
+
|
|
509
|
+
# no arguments: show help
|
|
510
|
+
_print_help()
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
if __name__ == '__main__':
|
|
514
|
+
main()
|