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/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()