File size: 20,201 Bytes
3a5cf48
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
import json
import os
import time

import public
import requests

from mod.project.ssl import plugin

class main:
    def __init__(self):
        if not os.path.exists("/www/server/deploy_plugin"):
            os.makedirs("/www/server/deploy_plugin")

        # 部署授权表
        self.M("").execute("""create table if not exists deploy_auths
(
    `id`          integer not null
        constraint deploy_auths_pk
            primary key autoincrement,
    `name`        TEXT    not null,
    `config`      TEXT    not null,
    `type`        TEXT    not null,
    `create_time` TEXT,
    `update_time` TEXT
);""", ())

        # 部署目标表
        self.M("").execute("""create table if not exists deploy_targets
(
    `id`          integer not null
        constraint deploy_targets_pk
            primary key autoincrement,
    `auth_id`    integer not null,
    `name`        TEXT    not null,
    `action`      TEXT    not null,
    `params`      TEXT    not null,
    `type`        TEXT    not null,
    `create_time` TEXT,
    `update_time` TEXT
);""",())

    def M(self, table_name):
        import db
        sql = db.Sql()
        sql._Sql__DB_FILE = public.get_panel_path() + '/data/db/ssl_data.db'
        sql._Sql__encrypt_keys = []
        return sql.table(table_name)

    def get_plugins(self, get):
        try:
            cloud_plugins = requests.get("{}/thirdparty_deploy/deploy_plugins.json".format(public.get_url()), timeout=10).json()
            if type(cloud_plugins) != list:
                cloud_plugins = []
        except:
            cloud_plugins = []
        local_plugins = plugin.get_plugins()
        plugins = []
        for cp in cloud_plugins:
            cp['install'] = True
            cp['update'] = False

            for lp in local_plugins:
                if cp['name'] == lp['name']:
                    cp['install'] = False
                    cp.update(lp)
                    if cp['version'] != lp['version']:
                        cp['update'] = True
                    break
            plugins.append(cp)
        for lp in local_plugins:
            exist = False
            for cp in cloud_plugins:
                if cp['name'] == lp['name']:
                    exist = True
                    break
            if not exist:
                lp['install'] = False
                lp['update'] = False
                plugins.append(lp)
        # 排序:已安装的在前,未安装的在后;btpanel插件排第一
        plugins.sort(key=lambda x: (x['install']))

        return plugins

    def install_plugin(self, get):
        if 'plugin_name' not in get or not get.plugin_name:
            return {'status': False, 'msg': '缺少参数plugin_name'}
        name = get.plugin_name

        if os.path.exists("/tmp/deploy_plugin_{}.log".format(name)):
            return public.returnMsg(False, '已有相同安装任务在队列中,请稍后再试')
        else:
            # 判断系统架构
            arch = public.ExecShell("uname -m")[0].strip()
            if arch == 'x86_64':
                get.plugin_name += '-x86_64'
            elif arch in ['aarch64']:
                get.plugin_name += '-aarch64'
            else:
                return public.returnMsg(False, '不支持当前系统架构:{}'.format(arch))

            public.ExecShell("(wget -O /www/server/deploy_plugin/{} {}/thirdparty_deploy/deploy_plugin/{} -T 10 && chmod 755 /www/server/deploy_plugin/{}) >> /tmp/deploy_plugin_{}.log 2>&1".format(
                name, public.get_url(), get.plugin_name, name, name))
            public.ExecShell("rm -f /tmp/deploy_plugin_{}.log".format(name))

            if not os.path.exists("/www/server/deploy_plugin/{}".format(name)):
                return public.returnMsg(False, '插件下载安装失败,请稍后重试')

            return public.returnMsg(True, '安装完成')

    def uninstall_plugin(self, get):
        if 'plugin_name' not in get or not get.plugin_name:
            return {'status': False, 'msg': '缺少参数plugin_name'}
        local_plugins = plugin.get_plugins()
        for lp in local_plugins:
            if lp['name'] == get.plugin_name:
                if self.M('deploy_auths').where("type=?", (get.plugin_name,)).count() > 0:
                    return {'status': False, 'msg': '请先删除该插件相关的授权配置'}
                try:
                    os.remove(lp['path'])
                    return {'status': True, 'msg': '卸载成功'}
                except Exception as e:
                    return {'status': False, 'msg': '卸载失败:{}'.format(str(e))}
        return {'status': False, 'msg': '插件未安装'}

    # 新增授权
    def add_deploy_auth(self, get):
        if 'name' not in get or not get.name:
            return {'status': False, 'msg': '缺少参数name'}
        if 'config' not in get or not get.config:
            return {'status': False, 'msg': '缺少参数config'}
        if 'type' not in get or not get.type:
            return {'status': False, 'msg': '缺少参数type'}
        # 获取插件配置
        local_plugins = plugin.get_plugins()
        plugin_config = None
        for lp in local_plugins:
            if lp['name'] == get.type:
                plugin_config = lp['config']
                break
        if not plugin_config:
            return {'status': False, 'msg': '未知的授权类型'}
        # 检查config
        try:
            config = json.loads(get.config)
        except Exception as e:
            return {'status': False, 'msg': '参数config格式错误,必须为JSON格式'}
        for key in plugin_config:
            if key["name"] not in config and key['required']:
                return {'status': False, 'msg': '参数config缺少字段{}'.format(key["name"])}

        # 检查同名授权是否存在
        exist = self.M('deploy_auths').where('name=?', (get.name,)).count()
        if exist > 0:
            return {'status': False, 'msg': '同名授权已存在'}

        data = {
            'name': get.name,
            'config': get.config,
            'type': get.type,
            'create_time': time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()),
            'update_time': time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
        }
        self.M('deploy_auths').insert(data)
        return {'status': True, 'msg': '添加成功'}

    # 修改授权
    def edit_deploy_auth(self, get):
        if 'auth_id' not in get or not get.auth_id:
            return {'status': False, 'msg': '缺少参数id'}
        if 'name' not in get or not get.name:
            return {'status': False, 'msg': '缺少参数name'}
        if 'config' not in get or not get.config:
            return {'status': False, 'msg': '缺少参数config'}
        if 'type' not in get or not get.type:
            return {'status': False, 'msg': '缺少参数type'}
        # 获取插件配置
        local_plugins = plugin.get_plugins()
        plugin_config = None
        for lp in local_plugins:
            if lp['name'] == get.type:
                plugin_config = lp['config']
                break
        if not plugin_config:
            return {'status': False, 'msg': '未知的授权类型'}
        # 检查config
        try:
            config = json.loads(get.config)
        except Exception as e:
            return {'status': False, 'msg': '参数config格式错误,必须为JSON格式'}
        for key in plugin_config:
            if key["name"] not in config and key['required']:
                return {'status': False, 'msg': '参数config缺少字段{}'.format(key["name"])}

        # 检查同名授权是否存在
        exist = self.M('deploy_auths').where('name=? AND id!=?', (get.name, get.auth_id)).count()
        if exist > 0:
            return {'status': False, 'msg': '同名授权已存在'}

        data = {
            'name': get.name,
            'config': get.config,
            'type': get.type,
            'update_time': time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
        }
        self.M('deploy_auths').where('id=?', (get.auth_id,)).update(data)
        return {'status': True, 'msg': '修改成功'}

    # 删除授权
    def delete_deploy_auth(self, get):
        if 'auth_id' not in get or not get.auth_id:
            return {'status': False, 'msg': '缺少参数id'}

        if self.M('deploy_targets').where('auth_id=?', (get.auth_id,)).count() > 0:
            return {'status': False, 'msg': '请先删除该授权相关的部署目标'}

        self.M('deploy_auths').where('id=?', (get.auth_id,)).delete()
        return {'status': True, 'msg': '删除成功'}

    # 获取授权列表
    def get_deploy_auths(self, get):
        w_sql = ""
        if "search" in get and get.search:
            w_sql += " and name like'%{}%'".format(get.search)
        local_plugins = plugin.get_plugins()
        plugin_name_dic = {lp['name']: lp["description"] for lp in local_plugins}
        w_sql += " and type in ('{}')".format("','".join(plugin_name_dic.keys()))

        p = 1
        if "p" in get and get.p:
            p = int(get.p)
        limit = 10
        if "limit" in get and get.limit:
            limit = int(get.limit)
        if p == -1:
            data = self.M("deploy_auths").where("1=1 {}".format(w_sql), ()).order('create_time desc').select()
        else:
            data = self.M("deploy_auths").where("1=1 {}".format(w_sql), ()).order('create_time desc').limit('{},{}'.format((p - 1) * limit, limit)).select()
        count = self.M("deploy_auths").where("1=1 {}".format(w_sql), ()).count()
        for d in data:
            try:
                d['config'] = json.loads(d['config'])
            except:
                d['config'] = {}
            d["description"] = plugin_name_dic[d["type"]]
        return {'data': data, 'count': count}

    # 新增部署目标
    def add_deploy_target(self, get):
        if 'auth_id' not in get or not get.auth_id:
            return {'status': False, 'msg': '缺少参数auth_id'}
        if 'name' not in get or not get.name:
            return {'status': False, 'msg': '缺少参数name'}
        if 'method' not in get or not get.method:
            return {'status': False, 'msg': '缺少参数method'}
        if 'params' not in get or not get.params:
            return {'status': False, 'msg': '缺少参数params'}
        if 'type' not in get or not get.type:
            return {'status': False, 'msg': '缺少参数type'}

        # 检查同名目标是否存在
        exist = self.M('deploy_targets').where('name=?', (get.name,)).count()
        if exist > 0:
            return {'status': False, 'msg': '同名部署目标已存在'}
        # 检查授权是否存在
        auth = self.M('deploy_auths').where('id=? and type=?', (get.auth_id, get.type)).find()
        if not auth:
            return {'status': False, 'msg': '授权不存在'}
        # 检查插件和部署方法是否存在
        local_plugins = plugin.get_plugins()
        plugin_found = False
        for lp in local_plugins:
            if lp['name'] == get.type:
                plugin_found = True
                action_found = False
                for action in lp['actions']:
                    if action['name'] == get.method.strip():
                        action_found = True
                        # 检查params格式
                        if action['params'] is None:
                            if get.params.strip() != '{}' and get.params.strip() != '':
                                return {'status': False, 'msg': '参数params格式错误,该方法不需要参数'}
                            break
                        try:
                            params = json.loads(get.params)
                        except Exception as e:
                            return {'status': False, 'msg': '参数params格式错误,必须为JSON格式'}
                        for key in action['params']:
                            if key["name"] not in params and key['required']:
                                return {'status': False, 'msg': '参数params缺少字段{}'.format(key["name"])}
                        break
                if not action_found:
                    return {'status': False, 'msg': '部署方法不存在'}
                break
        if not plugin_found:
            return {'status': False, 'msg': '插件不存在'}

        data = {
            'auth_id': get.auth_id,
            'name': get.name,
            'action': get.method,
            'params': get.params,
            'type': get.type,
            'create_time': time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()),
            'update_time': time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
        }
        self.M('deploy_targets').insert(data)
        return {'status': True, 'msg': '添加成功'}

    # 修改部署目标
    def edit_deploy_target(self, get):
        if 'target_id' not in get or not get.target_id:
            return {'status': False, 'msg': '缺少参数id'}
        if 'auth_id' not in get or not get.auth_id:
            return {'status': False, 'msg': '缺少参数auth_id'}
        if 'name' not in get or not get.name:
            return {'status': False, 'msg': '缺少参数name'}
        if 'method' not in get or not get.method:
            return {'status': False, 'msg': '缺少参数method'}
        if 'params' not in get or not get.params:
            return {'status': False, 'msg': '缺少参数params'}
        if 'type' not in get or not get.type:
            return {'status': False, 'msg': '缺少参数type'}

        # 检查同名目标是否存在
        exist = self.M('deploy_targets').where('name=? AND id!=?', (get.name, get.target_id)).count()
        if exist > 0:
            return {'status': False, 'msg': '同名部署目标已存在'}
        # 检查授权是否存在
        auth = self.M('deploy_auths').where('id=? and type=?', (get.auth_id, get.type)).find()
        if not auth:
            return {'status': False, 'msg': '授权不存在'}
        # 检查插件和部署方法是否存在
        local_plugins = plugin.get_plugins()
        plugin_found = False
        for lp in local_plugins:
            if lp['name'] == get.type:
                plugin_found = True
                action_found = False
                for action in lp['actions']:
                    if action['name'] == get.method:
                        action_found = True
                        # 检查params格式
                        if action['params'] is None:
                            if get.params.strip() != '{}' and get.params.strip() != '':
                                return {'status': False, 'msg': '参数params格式错误,该方法不需要参数'}
                            break
                        try:
                            params = json.loads(get.params)
                        except Exception as e:
                            return {'status': False, 'msg': '参数params格式错误,必须为JSON格式'}
                        for key in action['params']:
                            if key["name"] not in params and key['required']:
                                return {'status': False, 'msg': '参数params缺少字段{}'.format(key["name"])}
                        break
                if not action_found:
                    return {'status': False, 'msg': '部署方法不存在'}
                break
        if not plugin_found:
            return {'status': False, 'msg': '插件不存在'}
        data = {
            'auth_id': get.auth_id,
            'name': get.name,
            'action': get.method,
            'params': get.params,
            'type': get.type,
            'update_time': time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
        }
        self.M('deploy_targets').where('id=?', (get.target_id,)).update(data)
        return {'status': True, 'msg': '修改成功'}

    # 删除部署目标
    def delete_deploy_target(self, get):
        if 'target_id' not in get or not get.target_id:
            return {'status': False, 'msg': '缺少参数id'}
        self.M('deploy_targets').where('id=?', (get.target_id,)).delete()
        return {'status': True, 'msg': '删除成功'}

    # 获取部署目标列表
    def get_deploy_targets(self, get):
        w_sql = ""
        if "search" in get and get.search:
            w_sql += " and name like'%{}%'".format(get.search)
        if "type" in get and get.type:
            w_sql += " and type='{}'".format(get.type)
        local_plugins = plugin.get_plugins()
        plugin_name_dic = {lp['name']: {"description": lp["description"], "actions": { a["name"]: a["description"] for a in lp["actions"]}} for lp in local_plugins}
        w_sql += " and type in ('{}')".format("','".join(plugin_name_dic.keys()))

        p = 1
        if "p" in get and get.p:
            p = int(get.p)
        limit = 10
        if "limit" in get and get.limit:
            limit = int(get.limit)
        if p == -1:
            data = self.M("deploy_targets").where("1=1 {}".format(w_sql), ()).order('create_time desc').select()
        else:
            data = self.M("deploy_targets").where("1=1 {}".format(w_sql), ()).limit('{},{}'.format((p - 1) * limit, limit)).order('create_time desc').select()
        count = self.M("deploy_targets").where("1=1 {}".format(w_sql), ()).count()
        for d in data:
            try:
                d['params'] = json.loads(d['params'])
            except:
                d['params'] = {}

            auth_name = self.M('deploy_auths').where('id=?', (d['auth_id'],)).getField('name')
            if auth_name:
                d["description"] = auth_name+" - "+plugin_name_dic[d["type"]]["description"]
            else:
                d["description"] = plugin_name_dic[d["type"]]["description"]
            d["action_description"] = plugin_name_dic[d["type"]]["actions"].get(d["action"], "")
        return {'data': data, 'count': count}

    def get_actions(self, get):
        if 'plugin_name' not in get or not get.plugin_name:
            return {'status': False, 'msg': '缺少参数plugin_name'}
        return plugin.get_actions(get.plugin_name)

    def execute_action(self, get):
        # 获取证书来源
        if "cert" in get and get.cert and "key" in get and get.key:
            cert = get.cert
            key = get.key
        elif "oid" in get and get.oid:
            import panelSSL
            ssl_obj = panelSSL.panelSSL()
            rep = ssl_obj.get_order_find(get)
            cert = rep['certificate']+"\n"+rep['caCertificate']
            key = rep['privateKey']
        elif ("ssl_hash" in get and get.ssl_hash) or ("index" in get and get.index):
            from sslModel import certModel
            cert_obj = certModel.main()
            cert_info = cert_obj.GetCert(get)
            if 'privkey' not in cert_info or not cert_info['privkey']:
                return {'status': False, 'msg': '证书私钥不存在'}
            cert = cert_info['fullchain']
            key = cert_info['privkey']
        else:
            return {'status': False, 'msg': '缺少证书参数'}
        if "target_id" not in get or not get.target_id:
            return {'status': False, 'msg': '缺少参数target_id'}
        # 获取部署目标
        target = self.M('deploy_targets').where('id=?', (get.target_id,)).find()
        if not target:
            return {'status': False, 'msg': '部署目标不存在'}
        # 获取授权
        auth = self.M('deploy_auths').where('id=?', (target['auth_id'],)).find()
        if not auth:
            return {'status': False, 'msg': '部署授权不存在'}
        # 组装参数
        try:
            params = json.loads(target['params'])
            config = json.loads(auth['config'])
        except Exception as e:
            return {'status': False, 'msg': '部署目标参数格式错误'}
        params.update(config)
        params["cert"] = cert
        params["key"] = key

        return plugin.call_plugin(auth["type"], target["action"], params)