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/config.py ADDED
@@ -0,0 +1,372 @@
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 Configurations Management.'''
10
+
11
+ from configparser import RawConfigParser
12
+ from pathlib import Path
13
+
14
+ from ..base import config_file, runlogs_path, update_info_path, config_path, run_type
15
+ from ..lib.filelock import FileLock
16
+
17
+
18
+ def load_config(inifile=None, configs=None):
19
+ '''the configurations for InPanel'''
20
+ if run_type == 'source':
21
+ ssl_cert_path = str(Path(config_path) / 'certificate')
22
+ else:
23
+ ssl_cert_path = '/etc/inpanel/certificate'
24
+
25
+ default_configs = {
26
+ 'server': {
27
+ 'ip': '*',
28
+ 'port': '14433',
29
+ 'forcehttps': 'off', # force use https
30
+ 'sslkey': str(Path(ssl_cert_path) / 'inpanel.key'),
31
+ 'sslcrt': str(Path(ssl_cert_path) / 'inpanel.crt')
32
+ },
33
+ 'auth': {
34
+ 'username': 'admin',
35
+ 'password': 'a17351f4e092ba7d87a7a90c170a5cf6:1bdf574a782e8f006676ee1225743a6b', # admin
36
+ 'passwordcheck': 'on',
37
+ 'accesskey': '', # empty access key never validated
38
+ 'accesskeyenable': 'off',
39
+ },
40
+ 'runtime': {
41
+ 'mode': 'prod', # format: demo | prod
42
+ 'loginlock': 'off',
43
+ 'loginfails': '0',
44
+ 'loginlockexpire': '0',
45
+ },
46
+ 'time': {
47
+ 'timezone': '' # format: timezone = Asia/Shanghai
48
+ },
49
+ 'ecs': {
50
+ 'accounts': ''
51
+ },
52
+ 'inpanel': {
53
+ 'Instance Name': 'Access key'
54
+ }
55
+ } if configs is None else configs
56
+
57
+ return Config(config_file if inifile is None else inifile, default_configs)
58
+
59
+ def runlogs_config():
60
+ # to recode running data logs
61
+ return load_config(runlogs_path, {
62
+ 'file': {
63
+ 'lastdir': str(Path.home()), # user Home path
64
+ 'lastfile': '',
65
+ }
66
+ })
67
+
68
+
69
+ def update_info_config():
70
+ # to record version update information separately from config
71
+ return load_config(update_info_path, {
72
+ 'update': {
73
+ 'lastcheck': '0',
74
+ 'updateinfo': '',
75
+ }
76
+ })
77
+
78
+ class Config(object):
79
+ def __init__(self, inifile=None, configs=None):
80
+ if inifile is None:
81
+ return None
82
+
83
+ inifile_path = Path(inifile)
84
+ if not inifile_path.parent.exists():
85
+ inifile_path.parent.mkdir(parents=True, exist_ok=True)
86
+
87
+ self.inifile = inifile
88
+ self.cfg = RawConfigParser()
89
+
90
+ with FileLock(self.inifile):
91
+ if inifile_path.exists():
92
+ self.cfg.read(self.inifile)
93
+
94
+ # initialize configurations
95
+ default_configs = {} if configs is None else configs
96
+ needupdate = False
97
+ for sec, secdata in default_configs.items():
98
+ if not self.cfg.has_section(sec):
99
+ self.cfg.add_section(sec)
100
+ needupdate = True
101
+ for opt, val in secdata.items():
102
+ if not self.cfg.has_option(sec, opt):
103
+ self.cfg.set(sec, opt, str(val))
104
+ needupdate = True
105
+
106
+ # update ini file
107
+ if needupdate:
108
+ self.update(False)
109
+
110
+ def update(self, lock=True):
111
+ if lock:
112
+ flock = FileLock(self.inifile)
113
+ flock.acquire()
114
+
115
+ try:
116
+ inifp = open(self.inifile, 'w', encoding='utf-8')
117
+ self.cfg.write(inifp)
118
+ inifp.close()
119
+ if lock:
120
+ flock.release()
121
+ return True
122
+ except:
123
+ if lock:
124
+ flock.release()
125
+ return False
126
+
127
+ def has_option(self, section, option):
128
+ return self.cfg.has_option(section, option)
129
+
130
+ def remove_option(self, section, option):
131
+ return self.cfg.remove_option(section, option)
132
+
133
+ def get(self, section, option):
134
+ if self.cfg.has_option(section, option):
135
+ return self.cfg.get(section, option)
136
+ else:
137
+ return None
138
+
139
+ def getboolean(self, section, option):
140
+ return self.cfg.getboolean(section, option)
141
+
142
+ def getint(self, section, option):
143
+ return self.cfg.getint(section, option)
144
+
145
+ def has_section(self, section):
146
+ return self.cfg.has_section(section)
147
+
148
+ def add_section(self, section):
149
+ return self.cfg.add_section(section)
150
+
151
+ def remove_section(self, section=None):
152
+ if section is None:
153
+ return False
154
+ else:
155
+ return self.cfg.remove_section(section)
156
+
157
+ def set(self, section, option, value):
158
+ try:
159
+ self.cfg.set(section, option, value)
160
+ except:
161
+ return False
162
+ return self.update()
163
+
164
+ def get_section_list(self):
165
+ '''Return a list of section names, excluding [DEFAULT]'''
166
+ return self.cfg.sections()
167
+
168
+ def get_option_list(self, section):
169
+ '''Return a list of option names for the given section name.'''
170
+ return self.cfg.options(section)
171
+
172
+ def get_config_list(self):
173
+ '''Return a list of all config for the given config file.'''
174
+ config_list = []
175
+ sections = self.cfg.sections()
176
+ for section in sections:
177
+ sec = {'section': section, 'option': {}}
178
+ options = self.cfg.options(section)
179
+ for key in options:
180
+ sec['option'][key] = self.cfg.get(section, key)
181
+ config_list.append(sec)
182
+ return config_list
183
+
184
+ def get_config(self):
185
+ '''Return a dict of all config for the given config file.'''
186
+ config = {}
187
+ for section in self.cfg.sections():
188
+ config[section] = {}
189
+ for item in self.cfg.options(section):
190
+ config[section][item] = self.cfg.get(section, item)
191
+ return config
192
+
193
+ def addsection(self, section, data):
194
+ '''add one section'''
195
+ try:
196
+ if not self.cfg.has_section(section):
197
+ self.cfg.add_section(section)
198
+ for option, value in data.items():
199
+ self.cfg.set(section, option, value)
200
+ return self.update(False)
201
+ except:
202
+ return False
203
+
204
+ def addsections(self, section):
205
+ '''add some sections'''
206
+ try:
207
+ for sec, data in section.items():
208
+ if not self.cfg.has_section(sec):
209
+ self.cfg.add_section(sec)
210
+ for option, value in data.items():
211
+ self.cfg.set(sec, option, value)
212
+ return self.update(False)
213
+ except:
214
+ return False
215
+
216
+ def raw_loadconfig(filepath, return_sort=False, delimiter='=', quoter=' "\'', overwrite=True):
217
+ """Read config from file.
218
+ """
219
+ filepath = Path(filepath)
220
+ if not filepath.exists():
221
+ return None
222
+ config = {}
223
+ if return_sort:
224
+ sortlist = []
225
+ with open(filepath, encoding='utf-8') as f:
226
+ for line in f:
227
+ pair = line.strip().split(delimiter)
228
+ if len(pair) != 2:
229
+ continue
230
+ k, v = [x.strip(quoter) for x in pair]
231
+ if return_sort:
232
+ sortlist.append(k)
233
+ if overwrite:
234
+ config[k] = v
235
+ else:
236
+ if not k in config:
237
+ config[k] = []
238
+ config[k].append(v)
239
+ if return_sort:
240
+ return (config, sortlist)
241
+ else:
242
+ return config
243
+
244
+
245
+ def raw_saveconfig(filepath, config, sortlist=[], delimiter='=', quoter='"'):
246
+ """Write config to file.
247
+ """
248
+ filepath = Path(filepath)
249
+ if not filepath.exists():
250
+ return False
251
+ lines = []
252
+
253
+ # write the item in sortlist first
254
+ if len(sortlist) > 0:
255
+ for k in sortlist:
256
+ if k in config:
257
+ line = '%s%s%s%s%s\n' % (k, delimiter, quoter, config[k], quoter)
258
+ del config[k]
259
+ lines.append(line)
260
+
261
+ # then write the rest items
262
+ for k, v in config.items():
263
+ if isinstance(v, list):
264
+ for vv in v:
265
+ line = '%s%s%s%s%s\n' % (k, delimiter, quoter, vv, quoter)
266
+ lines.append(line)
267
+ else:
268
+ line = '%s%s%s%s%s\n' % (k, delimiter, quoter, v, quoter)
269
+ lines.append(line)
270
+
271
+ with open(filepath, 'w', encoding='utf-8') as f:
272
+ f.writelines(lines)
273
+ return True
274
+
275
+
276
+ def loadconfig(filepath, keymap=None, delimiter='=', quoter=' "\''):
277
+ """Load config from file and parse it to dict.
278
+ """
279
+ raw_config = raw_loadconfig(filepath)
280
+ if raw_config is None:
281
+ return None
282
+ if keymap is None:
283
+ config = dict((k, v) for k, v in raw_config.items())
284
+ else:
285
+ config = dict((keymap[k], v) for k, v in raw_config.items() if k in keymap)
286
+ return config
287
+
288
+
289
+ def saveconfig(filepath, config, keymap=None, delimiter='=', read_quoter=' "\'', write_quoter='"'):
290
+ """Save config to file."""
291
+ raw_config, sortlist = raw_loadconfig(filepath, return_sort=True, delimiter=delimiter, quoter=read_quoter)
292
+ if raw_config is None:
293
+ return False
294
+ for k, v in config.items():
295
+ if keymap is None:
296
+ raw_config[k] = v
297
+ else:
298
+ if k in keymap:
299
+ raw_config[keymap[k]] = v
300
+ else:
301
+ return False
302
+ return raw_saveconfig(filepath, raw_config, sortlist, delimiter=delimiter, quoter=write_quoter)
303
+
304
+
305
+ def readconfig(filepath, readfunc, **params):
306
+ """Read config from file.
307
+ """
308
+ filepath = Path(filepath)
309
+ with open(filepath, encoding='utf-8') as f:
310
+ for line in f:
311
+ rt = readfunc(line.strip(), **params)
312
+ if rt is not None:
313
+ return rt
314
+
315
+
316
+ def writeconfig(filepath, readfunc, writefunc, **params):
317
+ """Write config to file.
318
+ """
319
+ filepath = Path(filepath)
320
+ lines = []
321
+ linemeet = False
322
+ with open(filepath, encoding='utf-8') as f:
323
+ for line in f:
324
+ rt = readfunc(line.strip(), **params)
325
+ if rt is not None:
326
+ linemeet = True
327
+ line = writefunc(line, **params)
328
+ if line is not None:
329
+ lines.append(line+'\n')
330
+ else:
331
+ lines.append(line)
332
+
333
+ # generate a new line if no line meet
334
+ if not linemeet:
335
+ line = writefunc(None, **params)
336
+ if line is not None:
337
+ lines.append(line+'\n')
338
+
339
+ with open(filepath, 'w', encoding='utf-8') as f:
340
+ f.writelines(lines)
341
+ return True
342
+
343
+
344
+ __all__ = ['load_config', 'runlogs_config', 'update_info_config', 'Config', 'raw_loadconfig', 'raw_saveconfig', 'loadconfig', 'saveconfig', 'readconfig', 'writeconfig']
345
+
346
+ if __name__ == '__main__':
347
+ import json
348
+ default_config = {
349
+ '1.1.1.1': {
350
+ 'server': '1.1.1.1',
351
+ 'port': 8023,
352
+ 'accesskey': 'O64Td9EWEj8THu+RlHuoO8tjJzUmdsx37pTluP+aVc0='
353
+ },
354
+ '1.1.1.2': {
355
+ 'server': '1.1.1.1',
356
+ 'port': 8023,
357
+ 'accesskey': 'O64Td9EWEj8THu+RlHuoO8tjJzUmdsx37pTluP+aVc0='
358
+ }
359
+ }
360
+ config = Config('data/clients.ini', {})
361
+ print(config)
362
+ print(config.get_section_list())
363
+ config.update()
364
+ config.addsection('accccc', {'abc': 2345, 'ccc': 34567654345})
365
+ config.addsections(default_config)
366
+ print(config.get_section_list())
367
+ # print(config.update())
368
+ print('abc')
369
+ config_list = config.get_config_list()
370
+ print(json.dumps(config_list))
371
+ config_dict = config.get_config()
372
+ print(json.dumps(config_dict))
inpanel/mod/cron.py ADDED
@@ -0,0 +1,315 @@
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 Cron Jobs Management."""
10
+
11
+ from pathlib import Path
12
+ import re
13
+
14
+
15
+ crontab = '/etc/crontab'
16
+ cronspool = '/var/spool/cron/'
17
+ cfg_map = {
18
+ 'SHELL': 'shell',
19
+ 'MAILTO': 'mailto',
20
+ 'HOME': 'home',
21
+ 'PATH': 'path'
22
+ }
23
+
24
+
25
+ def load_config():
26
+ try:
27
+ if not Path(crontab).exists():
28
+ return {}
29
+ except OSError:
30
+ return {}
31
+
32
+ config = {}
33
+ with open(crontab, 'r', encoding='utf-8') as f:
34
+ lines = f.readlines()
35
+
36
+ while len(lines) > 0:
37
+ line = lines.pop(0)
38
+ out = line.strip()
39
+ if not out or out.startswith('#'):
40
+ continue
41
+
42
+ k = out.strip().split('=')[0]
43
+ if k and k in cfg_map:
44
+ config[cfg_map[k]] = out.split('=')[1]
45
+
46
+ return config
47
+
48
+
49
+ def update_config(configs):
50
+ cmap_reverse = dict((v, k) for k, v in cfg_map.items())
51
+ new_config = {}
52
+ for k, v in configs.items():
53
+ if k in cmap_reverse:
54
+ new_config[cmap_reverse[k]] = v
55
+ return save_config(crontab, new_config)
56
+
57
+
58
+ def save_config(filepath, config):
59
+ try:
60
+ if not Path(filepath).exists():
61
+ return False
62
+ except OSError:
63
+ return False
64
+
65
+ with open(filepath, 'r', encoding='utf-8') as f:
66
+ lines = f.readlines()
67
+
68
+ output = []
69
+ while len(lines) > 0:
70
+ line = lines.pop(0)
71
+ out = line.strip()
72
+ if not out or out.startswith('#'):
73
+ output.append('%s' % (line))
74
+ continue
75
+
76
+ k = out.split('=')[0]
77
+ if k:
78
+ if k in config:
79
+ output.append('%s=%s\n' % (k, config[k]))
80
+ else:
81
+ output.append('%s' % (line))
82
+
83
+ with open(filepath, 'w', encoding='utf-8') as f:
84
+ f.writelines(output)
85
+ return True
86
+
87
+ return False
88
+
89
+
90
+ def cron_list(level='normal', user=None):
91
+ '''
92
+ parser Cron config to python object (array)
93
+ return a list of cron jobs
94
+ '''
95
+ if level == 'normal':
96
+ if user is None:
97
+ return None
98
+ spool = str(Path(cronspool) / user)
99
+ elif level == 'system':
100
+ spool = crontab
101
+
102
+ try:
103
+ if not Path(spool).exists():
104
+ return None
105
+ except OSError:
106
+ return None
107
+
108
+ crons = []
109
+ with open(spool, encoding='utf-8') as f:
110
+ i = 0
111
+ for line in f:
112
+ line = line.strip()
113
+ line_user = ''
114
+ if re.findall(r"^\d|^\*|^\-", line):
115
+ if level == 'normal':
116
+ text = re.split(r'\s+', line, 5)
117
+ command = text[5]
118
+ line_user = user
119
+ elif level == 'system':
120
+ # this user's list
121
+ text = re.split(r'\s+', line, 6)
122
+ if user and user != text[5]:
123
+ continue
124
+ else:
125
+ line_user = text[5]
126
+ command = text[6]
127
+ else:
128
+ continue
129
+ i += 1
130
+ crons.append({
131
+ 'id': i,
132
+ 'minute': text[0],
133
+ 'hour': text[1],
134
+ 'day': text[2],
135
+ 'month': text[3],
136
+ 'weekday': text[4],
137
+ 'command': command,
138
+ 'user': line_user
139
+ })
140
+ return crons
141
+
142
+
143
+ def cron_add(user, minute, hour, day, month, weekday, command, level):
144
+ '''add normal or system cron
145
+ '''
146
+ if level == 'system':
147
+ if user is None or user == '' or len(user) == 0:
148
+ return False
149
+ spool = crontab
150
+ line = "%s %s %s %s %s %s %s\n" % (minute, hour, day, month, weekday, user, command)
151
+ else:
152
+ user = user or 'root'
153
+ spool = str(Path(cronspool) / user)
154
+ line = "%s %s %s %s %s %s\n" % (minute, hour, day, month, weekday, command)
155
+
156
+ with open(spool, 'a+', encoding='utf-8') as f:
157
+ f.write(line)
158
+ return True
159
+
160
+ return False
161
+
162
+
163
+ def cron_mod(user, id, minute, hour, day, month, weekday, command, level, currlist=''):
164
+ '''modify normal or system cron
165
+ '''
166
+ if user is None or id is None:
167
+ return False
168
+ if level == 'system':
169
+ spool = crontab
170
+ cron_line = "%s %s %s %s %s %s %s\n" % (minute, hour, day, month, weekday, user, command)
171
+ else:
172
+ spool = str(Path(cronspool) / user)
173
+ cron_line = "%s %s %s %s %s %s\n" % (minute, hour, day, month, weekday, command)
174
+
175
+ with open(spool, 'r', encoding='utf-8') as f:
176
+ lines = f.readlines()
177
+
178
+ i, j = 0, 0
179
+ for line in lines:
180
+ j += 1
181
+ if re.findall(r"^\d|^\*|^\-", line):
182
+ if level == 'normal':
183
+ i += 1
184
+ elif level == 'system':
185
+ # if currlist is this user's list
186
+ if currlist and currlist == user:
187
+ text = re.split(r'\s+', line, 6)
188
+ if user == text[5]:
189
+ i += 1
190
+ else:
191
+ i += 1
192
+ else:
193
+ continue
194
+ if str(i) == str(id):
195
+ lines[j-1] = cron_line
196
+ break
197
+
198
+ with open(spool, 'w+', encoding='utf-8') as f:
199
+ f.writelines(lines)
200
+ return True
201
+
202
+ return False
203
+
204
+
205
+ def cron_del(user, id, level, currlist=''):
206
+ if user is None or id is None:
207
+ return False
208
+ spool = crontab if level == 'system' else str(Path(cronspool) / user)
209
+
210
+ with open(spool, 'r', encoding='utf-8') as f:
211
+ lines = f.readlines()
212
+
213
+ i, j = 0, 0
214
+ for line in lines:
215
+ j += 1
216
+ if re.findall(r"^\d|^\*|^\-", line):
217
+ if level == 'normal':
218
+ i += 1
219
+ elif level == 'system':
220
+ if currlist and currlist == user:
221
+ text = re.split(r'\s+', line, 6)
222
+ if user == text[5]:
223
+ i += 1
224
+ else:
225
+ i += 1
226
+ else:
227
+ continue
228
+ if str(i) == str(id):
229
+ del lines[j-1]
230
+ break
231
+
232
+ with open(spool, 'w+', encoding='utf-8') as f:
233
+ f.writelines(lines)
234
+ return True
235
+
236
+ return False
237
+
238
+
239
+ def web_handler(context):
240
+ action = context.get_argument('action', '')
241
+
242
+ if action == 'get_settings':
243
+ context.write({'code': 0, 'msg': '获取 Cron 服务配置信息成功!', 'data': load_config()})
244
+
245
+ if action == 'save_settings':
246
+ mailto = context.get_argument('mailto', '')
247
+ rt = update_config({'mailto': mailto})
248
+ if rt:
249
+ context.write({'code': 0, 'msg': '设置保存成功!'})
250
+ else:
251
+ context.write({'code': -1, 'msg': '设置保存失败!'})
252
+
253
+ user = context.get_argument('user', '')
254
+ level = context.get_argument('level', 'normal')
255
+
256
+ if action == 'cron_list':
257
+ context.write({'code': 0, 'msg': '获取定时任务成功!', 'data': cron_list(user=user, level=level)})
258
+
259
+ elif action in ('cron_add', 'cron_mod'):
260
+ command = context.get_argument('command', '')
261
+ if command == '':
262
+ context.write({'code': -1, 'msg': '请输入命令!'})
263
+ return
264
+
265
+ if level == 'system' and user == '':
266
+ context.write({'code': -1, 'msg': '请输入用户'})
267
+ return
268
+
269
+ minute = context.get_argument('minute', '')
270
+ hour = context.get_argument('hour', '')
271
+ day = context.get_argument('day', '')
272
+ month = context.get_argument('month', '')
273
+ weekday = context.get_argument('weekday', '')
274
+ weekday = context.get_argument('weekday', '')
275
+
276
+ if action == 'cron_add':
277
+ if cron_add(user, minute, hour, day, month, weekday, command, level):
278
+ context.write({'code': 0, 'msg': '定时任务添加成功!'})
279
+ else:
280
+ context.write({'code': -1, 'msg': '定时任务添加失败!'})
281
+
282
+ elif action == 'cron_mod':
283
+ cronid = context.get_argument('cronid', '')
284
+ currlist = context.get_argument('currlist', '')
285
+ if cron_mod(user, cronid, minute, hour, day, month, weekday, command, level, currlist):
286
+ context.write({'code': 0, 'msg': '定时任务修改成功!'})
287
+ else:
288
+ context.write({'code': -1, 'msg': '定时任务修改失败!'})
289
+
290
+ elif action == 'cron_del':
291
+ cronid = context.get_argument('cronid', '')
292
+ currlist = context.get_argument('currlist', '')
293
+ if cron_del(user, cronid, level, currlist):
294
+ context.write({'code': 0, 'msg': '定时任务删除成功!'})
295
+ else:
296
+ context.write({'code': -1, 'msg': '定时任务删除失败!'})
297
+
298
+
299
+ if __name__ == "__main__":
300
+ import json
301
+ crontab = '/Users/douzhenjiang/test/inpanel/test/crontab'
302
+ cronspool = '/Users/douzhenjiang/test/inpanel/test/var_spool_cron'
303
+ # print crontab
304
+ # os.system("top")
305
+ # print loadconfig(crontab, cfg_map)
306
+ # print raw_loadconfig(crontab)
307
+ # print(load_config())
308
+ # print update_config({'shell': 'shelshelshel', 'home': 'homehomehome', 'path':'abc'})
309
+ # print dict((v, k) for k, v in cfg_map.items())
310
+
311
+ config = cron_list(level='system', user='root')
312
+ # config = cron_list(level='system', user='apache')
313
+ # config = cron_list(level='system')
314
+ print(json.dumps(config))
315
+ # print(cron_add('root', '*','*','*','*','*', 'command'))