forked from hjdhnx/dr_py
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathadmin.py
402 lines (371 loc) · 14.6 KB
/
admin.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# File : admin.py
# Author: DaShenHan&道长-----先苦后甜,任凭晚风拂柳颜------
# Date : 2022/9/6
import os
import ujson
from flask import Blueprint,request,render_template,render_template_string,jsonify,make_response,redirect
from controllers.service import storage_service,rules_service,parse_service
from base.R import R
from base.database import db
from utils.log import logger
import shutil
from utils.update import getLocalVer,getOnlineVer,download_new_version,download_lives,copy_to_update
from utils import parser
from utils.env import get_env,update_env
from utils.web import getParmas,verfy_token
from js.rules import getRules,getCacheCount
from utils.parser import runJScode
from werkzeug.utils import secure_filename
from utils.web import md5
from utils.common_api import js_render
admin = Blueprint("admin", __name__)
# @admin.route("/",methods=['get'])
# def index():
# return R.ok(msg='欢迎进入首页',data=None)
# @admin.route("/info",methods=['get'])
# def info_all():
# data = storage_service.query_all()
# return R.ok(data=data)
@admin.route('/')
def admin_index(): # 管理员界面
if not verfy_token():
return render_template('login.html')
lsg = storage_service()
live_url = lsg.getItem('LIVE_URL')
use_py = lsg.getItem('USE_PY')
js0_password = lsg.getItem('JS0_PASSWORD')
# print(f'live_url:', live_url)
rules = getRules('js')
# print(rules)
cache_count = getCacheCount()
# print(cache_count)
return render_template('admin.html',js0_password=js0_password, pystate=use_py,rules=rules,cache_count=cache_count, ver=getLocalVer(), live_url=live_url)
@admin.route('/settings')
def admin_settings(): # 管理员界面
if not verfy_token():
return render_template('login.html')
lsg = storage_service()
# conf_list = 'LIVE_URL|USE_PY|PLAY_URL|PLAY_DISABLE|LAZYPARSE_MODE|WALL_PAPER_ENABLE|WALL_PAPER|UNAME|PWD|LIVE_MODE|LIVE_URL|CATE_EXCLUDE|TAB_EXCLUDE'.split('|')
conf_lists = lsg.getStoreConf()
# print(conf_lists)
return render_template('settings.html', conf_lists=conf_lists,ver=getLocalVer())
@admin.route('/save_conf',methods=['POST'])
def admin_save_conf(): # 管理员界面
if not verfy_token():
# return render_template('login.html')
return R.error('请登录后再试')
key = getParmas('key')
value = getParmas('value')
print(f'key:{key},value:{value}')
lsg = storage_service()
res_id = lsg.setItem(key,value)
return R.success(f'修改成功,记录ID为:{res_id}')
@admin.route('/update_env',methods=['POST'])
def admin_update_env(): # 更新环境变量中的某个值
if not verfy_token():
# return render_template('login.html')
return R.error('请登录后再试')
key = getParmas('key')
value = getParmas('value')
print(f'key:{key},value:{value}')
ENV = update_env(key,value)
return R.success(f'修改成功,最新的完整ENV见data',data=ENV)
@admin.route("/view/<name>",methods=['GET'])
def admin_view_rule(name):
return js_render(name)
# if not name or not name.split('.')[-1] in ['js','txt','py','json']:
# return R.error(f'非法猥亵,未指定文件名。必须包含js|txt|json|py')
# try:
# env = get_env()
# # print(env)
# if env.get('js_proxy'):
# js_proxy = env['js_proxy']
# burl = request.base_url
# if '=>' in js_proxy:
# oldsrc = js_proxy.split('=>')[0]
# if oldsrc in burl:
# newsrc = js_proxy.split('=>')[1]
# # print(f'js1源代理已启用,全局替换{oldsrc}为{newsrc}')
# rurl = burl.replace(oldsrc, newsrc)
# if burl != rurl:
# jscode = parser.getJs(name, 'js')
# # rjscode = render_template_string(jscode, env=env)
# rjscode = jscode
# for k in env:
# # print(f'${k}', f'{env[k]}')
# if f'${k}' in rjscode:
# rjscode = rjscode.replace(f'${k}', f'{env[k]}')
# # rjscode = render_template_string(jscode, **env)
# if rjscode.strip() == jscode.strip(): # 无需渲染才代理
# return redirect(rurl)
# else:
# logger.info(f'{name}由于存在环境变量无法被依赖代理')
#
# return parser.toJs(name,'js',env)
# except Exception as e:
# return R.error(f'非法猥亵\n{e}')
@admin.route('/clear/<name>')
def admin_clear_rule(name):
if not name or not name.split('.')[-1] in ['js','txt','py','json']:
return R.error(f'非法猥亵,未指定文件名。必须包含js|txt|json|py')
if not verfy_token():
return render_template('login.html')
file_path = os.path.abspath(f'js/{name}')
print(file_path)
if not os.path.exists(file_path):
return R.error('服务端没有此文件!'+file_path)
os.remove(file_path)
return R.ok('成功删除文件:'+file_path)
@admin.route('/get_ver')
def admin_get_ver():
if not verfy_token():
# return render_template('login.html')
return R.error('请登录后再试')
lsg = storage_service()
update_proxy = lsg.getItem('UPDATE_PROXY')
online_ver,msg = getOnlineVer(update_proxy)
return jsonify({'local_ver':getLocalVer(),'online_ver':online_ver,'msg':msg})
@admin.route('/update_db')
def admin_update_db():
if not verfy_token():
# return render_template('login.html')
return R.error('请登录后再试')
old_dbfile = 'migrations'
if os.path.exists(old_dbfile):
logger.info(f'开始删除历史数据库迁移文件:{old_dbfile}')
shutil.rmtree(old_dbfile)
db.session.execute('drop table if exists alembic_version')
cmd = 'flask db migrate && flask db upgrade'
if not os.path.exists('migrations'):
cmd = 'flask db init && '+cmd
logger.info(f'开始执行cmd:{cmd}')
result = os.system(cmd)
logger.info(f'cmd执行结果:{result}')
return R.success('数据库升级完毕')
@admin.route('/update_ver')
def admin_update_ver():
if not verfy_token():
return R.failed('请登录后再试')
lsg = storage_service()
update_proxy = lsg.getItem('UPDATE_PROXY')
msg = download_new_version(update_proxy)
return R.success(msg)
@admin.route('/rule_state/<int:state>',methods=['POST'])
def admin_rule_state(state=0): # 管理员修改规则状态
if not verfy_token():
return R.error('请登录后再试')
names = getParmas('names')
if not names:
return R.success(f'修改失败,没有传递names参数')
rule_list = names.split(',')
rules = rules_service()
# print(rules.query_all())
# print(rules.getState(rule_list[0]))
# print(rule_list)
success_list = []
for rule in rule_list:
try:
res_id = rules.setState(rule,state)
success_list.append(f'{rule}:{res_id}')
except:
success_list.append(rule)
return R.success(f'修改成功,服务器反馈信息为:{success_list}')
@admin.route('/rule_order/<int:order>',methods=['POST'])
def admin_rule_order(order=0): # 管理员修改规则顺序
if not verfy_token():
return R.error('请登录后再试')
names = getParmas('names')
if not names:
return R.success(f'修改失败,没有传递names参数')
rule_list = names.split(',')
rules = rules_service()
# print(rules.query_all())
# print(rules.getState(rule_list[0]))
# print(rule_list)
success_list = []
rule_list.reverse() # 倒序解决时间多重排序问题
for rule in rule_list:
try:
res_id = rules.setOrder(rule,order)
success_list.append(f'{rule}:{res_id}')
except:
success_list.append(rule)
return R.success(f'修改成功,服务器反馈信息为:{success_list}')
@admin.route('/parse/save_data',methods=['POST'])
def admin_parse_save_data(): # 管理员保存拖拽排序后的解析数据
if not verfy_token():
return R.error('请登录后再试')
data = getParmas('data')
if not data:
return R.success(f'修改失败,没有传递data参数')
parse = parse_service()
success_list = []
data = ujson.loads(data)
new_list = []
new_data = []
for nd in data:
if not nd.get('url') and nd.get('name') != '🌐Ⓤ':
continue
if nd['url'] not in new_list:
new_data.append(nd)
new_list.append(nd['url'])
print(f'去重前:{len(data)},去重后:{len(new_data)}')
for i in range(len(new_data)):
d = new_data[i]
# if not d.get('url') and d.get('name') != '🌐Ⓤ':
# continue
obj = {
'name':d.get('name', ''),
'url':d.get('url', ''),
'state':d.get('state',1),
'type': d.get('state',0),
'order':i+1,
'ext':d.get('ext',''),
'header':d.get('header',''),
}
# print(obj)
try:
parse.saveData(obj)
success_list.append(f'parse:{d["url"]}')
# print(obj)
# print(200,obj)
except Exception as e:
success_list.append(d["url"])
print(f'{d["url"]}失败:{e}')
# print(len(success_list))
return R.success(f'修改成功,服务器反馈信息为:{success_list}')
@admin.route('/force_update')
def admin_force_update():
if not verfy_token():
return R.failed('请登录后再试')
ret = copy_to_update()
if ret:
msg = '升级成功'
return R.success(msg)
else:
msg = '升级失败。具体原因只能去看实时日志(通过9001端口)'
return R.failed(msg)
@admin.route('/update_lives')
def admin_update_lives():
url = getParmas('url')
if not url:
return R.failed('未提供被同步的直播源远程地址!')
if not verfy_token():
return R.failed('请登录后再试')
live_url = url
success = download_lives(live_url)
if success:
return R.success(f'直播源{live_url}同步成功')
else:
return R.failed(f'直播源{live_url}同步失败')
@admin.route('/write_live_url')
def admin_write_live_url():
url = getParmas('url')
if not url:
return R.failed('未提供修改后的直播源地址!')
if not verfy_token():
return R.failed('请登录后再试')
lsg = storage_service()
id = lsg.setItem('LIVE_URL',url)
msg = f'已修改的配置记录id为:{id}'
return R.success(msg)
@admin.route('/change_use_py')
def admin_change_use_py():
if not verfy_token():
return R.failed('请登录后再试')
lsg = storage_service()
use_py = lsg.getItem('USE_PY')
new_use_py = '' if use_py else '1'
state = '开启' if new_use_py else '关闭'
id = lsg.setItem('USE_PY', new_use_py)
msg = f'已修改的配置记录id为:{id},结果为{state}'
return R.success(msg)
# @admin.route('/get_use_py')
# def admin_get_use_py():
# if not verfy_token():
# return R.failed('请登录后再试')
# lsg = storage_service()
# use_py = lsg.getItem('USE_PY')
# state = 1 if use_py else 0
# return R.success(state)
@admin.route('/upload', methods=['GET', 'POST'])
def upload_file():
if not verfy_token():
return render_template('login.html')
if request.method == 'POST':
try:
file = request.files['file']
filename = secure_filename(file.filename)
print(f'推荐安全文件命名:{filename}')
savePath = f'js/{file.filename}'
if os.path.exists(savePath):
return R.failed(f'上传失败,文件已存在,请先查看删除再试')
with open('js/模板.js', encoding='utf-8') as f2:
before = f2.read().split('export')[0]
upcode = file.stream.read().decode('utf-8')
check_to_run = before + upcode
# print(check_to_run)
try:
loader, _ = runJScode(check_to_run)
rule = loader.eval('rule')
if not rule:
return R.failed('文件上传失败,检测到上传的文件不是drpy框架支持的源代码')
except:
return R.failed('文件上传失败,检测到上传的文件不是drpy框架支持的源代码')
print(savePath)
file.seek(0) # 读取后变成空文件,重新赋能
file.save(savePath)
return R.success('文件上传成功')
except Exception as e:
return R.failed(f'文件上传失败!{e}')
else:
# return render_template('upload.html')
return R.failed('文件上传失败')
@admin.route('/login',methods=['GET','POST'])
def login_api():
username = getParmas('username')
password = getParmas('password')
autologin = getParmas('autologin')
if not all([username,password]):
return R.failed('账号密码字段必填')
token = md5(f'{username};{password}')
check = verfy_token(token=token)
if check:
# response = make_response(redirect('/admin'))
response = make_response(R.success('登录成功'))
response.set_cookie('token', token)
return response
else:
return R.failed('登录失败,用户名或密码错误')
@admin.route('/logtail')
def admin_logtail():
if not verfy_token():
return R.failed('请登录后再试')
return render_template('logtail.html')
@admin.route('/lives')
def admin_lives():
if not verfy_token():
return R.failed('请登录后再试')
# print(dir(request))
# 完整地址: request.base_url url
# 带http的前缀 host_url root_url
# 不带http的前缀 host
# 当前路径 path
host_url = request.host_url
def get_lives():
base_path = os.path.dirname(os.path.abspath(__file__)) # 当前文件所在目录
# print(base_path)
live_path = os.path.join(base_path, '../txt/lives')
# print(live_path)
files = os.listdir(live_path)
# print(files)
# files = list(filter(lambda x: str(x).endswith('.txt') and str(x).find('模板') < 0, files))
files = list(filter(lambda x: str(x).split('.')[-1] in ['txt','json','m3u'] and str(x).find('模板') < 0, files))
files = [f'{host_url}lives?path=txt/lives/{file}' for file in files]
return files
files = '\n'.join(get_lives())
response = make_response(files)
response.headers['Content-Type'] = 'text/plain; charset=utf-8'
return response