# -*- coding: utf-8 -*-

from flask.views import View, MethodView 
from flask import Flask, jsonify, request,Response, send_from_directory, redirect, abort, g
import sys
sys.path.append("/opt/cdnfly/master/")
from model.db import Db, LogDb
from view.util import client_error, AESCipher,send_mail, admin_required,login_required,need_admin, is_number, is_email, is_empty, Op, is_wildcard_domain, is_name, is_des, is_server_name, is_key_cert_valid, is_domain, is_ip, lock, create_node_task,is_valid_time,is_valid_size,is_port,client_success
import json
import random
reload(sys) 
sys.setdefaultencoding('utf8')
import importlib
import os
import re

class ConfigAPI(MethodView):
    decorators = [login_required]

    def get(self, para):
        if "config_id" in para:
            config_id = para['config_id']
            return self.get_single_config(config_id)

        else:
            return self.get_all_config()

    def delete(self, para):
        need_admin()
        config_id = para['config_id']
        scope_name, scope_id, config_type, config_name = config_id.split("-",3)
        if scope_name == "global":
            client_error("不允许删除全局配置", "config-193")

        conn = Db()
        try:
            lock(conn, "config")
            conn.execute("delete from config where scope_name=%s and scope_id=%s and type=%s and name=%s",(scope_name,scope_id,config_type,config_name,) )
            conn.commit()
            client_success("删除config成功")

        except:
            conn.rollback()
            raise

        finally:
            conn.close()         

    def put(self, para):
        content = request.get_json(silent=True,force=True)
        if "config_id" in para:
            config_id = para['config_id']
            scope_name, scope_id, config_type, config_name = config_id.split("-",3)
            content['scope_name'] = scope_name
            content['scope_id'] = scope_id
            content['type'] = config_type
            content['name'] = config_name

        conn = Db()
        try:
            lock(conn, "config")
            config_id, diff = self.check_para(conn, content)
            Op.update(conn, "config", config_id, diff)
            conn.commit()
            client_success("更新config成功")

        except:
            conn.rollback()
            raise

        finally:
            conn.close() 

    def get_single_config(self, config_id):
        scope_name, scope_id, config_type, config_name = config_id.split("-",3)
        if (config_type == "site" and config_name in ["clean_url","clean_dir","ip-unlock-max-limit","ip-unlock-max-per-limit","pre_cache_url"]) or (config_type == "system" and config_name == "recharge" ):
            pass
        else:
            need_admin()

        conn = Db()
        try:
            ret = conn.fetchone("select c.*, t.state, t.ret, t.type as task_type from config c left join task t on t.id=c.task_id where c.scope_name=%s and c.scope_id=%s and c.type=%s and c.name=%s ", 
                                (scope_name,scope_id,config_type,config_name, ) )
            if ret is None:
                client_success(None, None)

            # 普通用户获取recharge时，只返回转账说明和默认支付
            if config_name == "recharge" and g.type == 2:
                value = json.loads(ret['value'])
                wxpay_sub_type = ""
                alipay_sub_type = ""
                if "subtype" in value['wxpay']:
                    wxpay_sub_type = value['wxpay']['subtype']

                if "subtype" in value['alipay']:
                    alipay_sub_type = value['alipay']['subtype']

                ret = {"default-pay":value['default-pay'], "transfer": {"state": value['transfer']['state'],"data": value['transfer']['data']}, "wxpay": {"state": value['wxpay']['state'],"subtype":wxpay_sub_type },"alipay":{"state":value['alipay']['state'],"subtype":alipay_sub_type }}

            client_success(None, ret)

        finally:
            conn.close()

    def get_all_config(self):
        # 需要管理员权限
        need_admin()

        conn = Db()

        scope_name = request.args.get('scope_name')
        scope_id = request.args.get('scope_id')
        config_type = request.args.get('type')
        config_name = request.args.get('name')

        sql_select = "select * from config "
        para = ()

        sql_where = "where 1=1 "

        # scope_name
        if not is_empty(scope_name):
            sql_where += "and scope_name = %s "
            para = para + (scope_name,)

        # scope_id
        if not is_empty(scope_id):
            sql_where += "and scope_id = %s "
            para = para + (scope_id,)

        # config_name
        if not is_empty(config_name):
            sql_where += "and name = %s "
            para = para + (config_name,)  

        # config_type
        if not is_empty(config_type):
            sql_where += "and type = %s "
            para = para + (config_type,)

        try:
            sql = sql_select + sql_where
            ret = conn.fetchall(sql, para)
            client_success("",ret)

        finally:
            conn.close()

    def check_para(self, conn, content):
        # 需要管理员权限
        need_admin()

        para = ()
        set_list = []
        diff = []

        # scope_name
        if "scope_name" in content:
            scope_name = content['scope_name']
            if is_empty(scope_name):
                client_error("scope_name不能为空", "config-2")
            
            if scope_name not in ["global","region","node"]:
                client_error("scope_name只允许global, region, node", "config-3")     

        else:
            client_error("请指定scope_name", "config-4")

        # scope_id
        if "scope_id" in content:
            scope_id = content['scope_id']
            if not is_number(scope_id):
                client_error("scope_id需要为数字", "config-5")

        else:
            client_error("请指定scope_id", "config-6")


        # type(必要，不允许修改)
        if "type" in content:
            config_type = content['type']
            if is_empty(config_type):
                client_error("type不能为空", "config-7")

            if not re.match(r"^[a-z]+[a-z_]+$", config_type):
                client_error("config_type需要以字母开头,且只包括字母和_","config-8")

        else:
            client_error("请输入config类型", "config-9")


        # name(必要，不允许修改)
        if "name" in content:
            name = content['name']
            config_name = name
            if is_empty(name):
                client_error("name不能为空", "config-10")

            if not re.match(r"^[a-z]+[0-9a-z_-]+$", name):
                client_error("name需要以字母开头,且只包括字母、数字和_-","config-11")

        else:
            client_error("请输入config类型", "config-12")

        # 定义同步节点范围
        if scope_name == "global":
            node_ids = 0
        elif scope_name == "region":
            node_ids = "g{region_id}".format(region_id=scope_id)
        elif scope_name == "node":
            node_ids = scope_id

        config_id = "{scope_name}-{scope_id}-{config_type}-{name}".format(scope_name=scope_name,scope_id=scope_id,config_type=config_type,name=name)
        config = conn.fetchone("select * from config where scope_name=%s and scope_id=%s and type=%s and name=%s",(scope_name,scope_id,config_type,name,))
        if config:
            is_update = True
        else:
            is_update = False    

        # value(必要，可为空)
        task_id = None
        if "value" in content:
            value = str(content['value'])
            if (is_update and config['value'] != value) or not is_update:

                # name=nginx-config-file 更新时，创建任务
                if config_type == "nginx_config":
                    self.check_nginx_config_value(value, scope_name)
                    main_type = "nginx全局配置"
                    action = "同步"
                    name = "同步Nginx全局配置"
                    res_ids = config_id
                    task_id = create_node_task(conn, main_type, action, name, res_ids, node_ids)
                    set_list.append("task_id=%s")
                    para = para + (task_id,)

                elif config_type == "error_page":
                    main_type = "error-page"
                    action = "同步"
                    name = "同步错误页面"
                    res_ids = config_id
                    task_id = create_node_task(conn, main_type, action, name, res_ids, node_ids)

                    set_list.append("task_id=%s")
                    para = para + (task_id,)

                elif config_type == "openresty_config":
                    self.check_openresty_config(value, scope_name)
                    main_type = "openresty配置"
                    action = "同步"
                    name = "同步openresty配置"
                    res_ids = config_id
                    task_id = create_node_task(conn, main_type, action, name, res_ids, node_ids)

                    set_list.append("task_id=%s")
                    para = para + (task_id,)             

                elif config_type in ["site"]:
                    if name in ["related-config-min-limit","related-config-max-times-limit","black-ip-limit","white-ip-limit","max-domain-persite-limit",
                                "clean_url","clean_dir","pre_cache_url","pre_cache_timeout","ip-unlock-max-limit","ip-unlock-max-per-limit","cc-rule-max-limit",
                                "acl-max-limit","download-access-log-limit","download-access-log-retain"]:
                        if not is_number(value):
                            client_error("{config_type}.{name}的值不是有效有数字".format(config_type=config_type,name=name), "config-13")

                    elif name in ["download-access-log-tmp-dir"]:
                        if not value.startswith("/"):
                            client_error("目录需以/开头","config-14")

                    elif name in ["listen-default-http-80"]:
                        if value not in ["1","0"]:
                            client_error("{name}可选值为0或1".format(name=name),"config-15")

                        # 创建任务，来决定是否创建默认的http 80站点，用于签发证书用
                        main_type = "默认80站点配置"
                        action = "同步"
                        name = "同步默认80站点配置"
                        res_ids = config_id
                        task_id = create_node_task(conn, main_type, action, name, res_ids, node_ids)

                        set_list.append("task_id=%s")
                        para = para + (task_id,)

                elif config_type in ["stream"]:
                    if name in ["custom-port-not-allow"]:
                        for p in value.split():
                            if not is_port(p):
                                client_error("端口{p}无效".format(p=p), "config-16")
                    
                    elif name in ["related-config-min-limit","related-config-max-times-limit","acl-max-limit"]:
                        if not is_number(value):
                            client_error("{config_type}.{name}的值不是有效有数字".format(config_type=config_type,name=name), "config-17")

                elif config_type == "site_stream":
                    if name == "custom-port-not-allow":
                        for p in value.split():
                            if not is_port(p):
                                client_error("端口{p}无效".format(p=p), "config-18")

                    elif name == "custom-port-allow":
                        # 格式为  11, 80-2000
                        for p_range in value.split(","):
                            for p in p_range.split("-"):
                                if not is_port(p.strip()):
                                    client_error("端口{p}无效".format(p=p), "config-19")

                        # 删除所有空格
                        value = value.replace(" ","") 

                        # 创建任务，来应用net.ipv4.ip_local_reserved_ports
                        main_type = "允许端口范围配置"
                        action = "同步"
                        name = "同步允许端口范围配置"
                        res_ids = config_id
                        task_id = create_node_task(conn, main_type, action, name, res_ids, node_ids)

                        set_list.append("task_id=%s")
                        para = para + (task_id,)

                elif config_type == "site_default_config":
                    if name in ["http_listen-port","http_listen-backend_port","https_listen-port","https_listen-backend_port"]:
                        if not is_port(value):
                            client_error("{name}值不是有效的端口号".format(name=name),"config-20")

                    elif name in ["http_listen-backend_protocol","https_listen-backend_protocol"]:
                        if value not in ["http","https"]:
                            client_error("{name}可选值为http或https".format(name=name),"config-21")

                    elif name in ["https_listen-hsts","https_listen-http2","gzip_enable","websocket_enable"]:
                        if value not in ["0","1"]:
                            client_error("{name}可选值为0或1".format(name=name),"config-22")

                    elif name == "https_listen-proxy_ssl_protocols":
                        for p in value.split():
                            if p not in ["SSLv2","SSLv3","TLSv1","TLSv1.1","TLSv1.2","TLSv1.3"]:
                                client_error("proxy_ssl_protocols无效", "config-23")
                    elif name == "balance_way":
                        if value not in ["ip_hash","rr","url_hash","least_conn","random"]:
                            client_error("balance_way无效", "config-24")

                    elif name == "cc_default_rule":
                        if not conn.fetchone("select * from cc_rule where enable=1 and internal is true and id=%s", value):
                            client_error("无法找到{value}规则".format(value=value), "config-25")  

                    elif name == "gzip_types":
                        if is_empty(value):
                            client_error("gzip_types值不能为空","config-26")

                    elif name == "proxy_cache":
                        self.check_proxy_cache(value)

                    elif name == "post_size_limit":
                        if not is_number(value):
                            client_error("不是有效数字","config-30")

                elif config_type == "stream_default_config":
                    if name == "listen_protocol":
                        if value not in ["tcp","udp"]:
                            client_error("listen_protocol可选tcp和udp", "config-27")

                    elif name == "balance_way":
                        if value not in ["ip_hash","rr","url_hash","least_conn","random"]:
                            client_error("balance_way无效", "config-28")                        

                    elif name == "proxy_protocol":
                        if value not in ["0","1"]:
                            client_error("proxy_protocol可选0或1", "config-29")

                elif config_type == "system":             
                    log_conn = LogDb()      

                    if name in ["keep-job-days","keep-login-log-days","keep-op-log-days","keep-task-log-days",
                                "keep-traffic-history-days","backup_keep_days","max_site_stream_sync_one_time","login_session_valid_time","node_max_failed"]:
                        if not is_number(value):
                            client_error("不是有效数字","config-30")

                    elif name in ["backup_rate"]:
                        if not is_valid_time(value):
                            client_error("时间无效","config-31")

                    elif name in ["node_health_check","record_sync","package_expire_close_site","traffic_excceed_close_site"]:
                        if value not in ["0","1"]:
                            client_error("可选值为0或1","config-32")

                    elif name in ["backup_dir"]:
                        if not value.startswith("/"):
                            client_error("目录需以/开头", "config-33")
                    
                    elif name in ["keep-access-log-days", "keep-node-log-days"]:
                        if not is_number(value):
                            client_error("不是有效数字","config-34")

                        if name == "keep-access-log-days":
                            es_path = "_ilm/policy/access_log_policy"

                        if name == "keep-node-log-days":
                            es_path = "_ilm/policy/node_log_policy"

                        body = {
                                  "policy": {
                                    "phases": {
                                      "hot": {
                                        "actions": {
                                          "rollover": {
                                            "max_age": "1d" 
                                          }
                                        }
                                      },
                                      "delete": {
                                        "min_age": value+"d",
                                        "actions": {
                                          "delete": {} 
                                        }
                                      }
                                    }
                                  }
                                }   
                        ret = log_conn.put(es_path, body)
                        if ("acknowledged" not in ret) or (ret['acknowledged'] != True):
                            client_error("更新失败，错误:{err}".format(err=ret['error']),"config-35")
                
                    elif name == "dns_config":
                        value = self.get_domain_line(conn, value)

                    elif name == "sms_config":
                        self.check_sms_config(value)

                    elif name == "smtp":
                        self.check_smtp(value)

                    elif name == "register_require":
                        self.check_register_require(conn, value)

                    elif name == "record_repair":
                        if value == "1":
                            # 创建任务
                            conn.execute("insert into task values (null, 0, 20,'DNS记录修复','record_repair',null,null,null,now(),null,null,null,1,'pending',0,null,null) ")
                            task_id = conn.insert_id()
                            conn.execute("update config set task_id=%s where scope_name='global' and type='system' and name=%s ", (task_id,name,) )

                    elif name == "node_monitor_config":
                        self.check_node_monitor_config(value)

                    elif name == "https_cert":
                        with open("/opt/cdnfly/master/conf/ssl.cert","w") as fp:
                            fp.write(value)

                    elif name == "https_key":
                        with open("/opt/cdnfly/master/conf/ssl.key","w") as fp:
                            fp.write(value)

                set_list.append("value=%s")
                para = para + (value,)

                # 如果值小于1000才记录
                if len(str(value)) < 1000 and is_update:
                    diff.append({"field":"value","old":config['value'],"new":value})    

        else:
            client_error("请输入config值", "config-36")

        # enable(非必要，默认值)
        if "enable" in content:
            enable = content['enable']
            if is_empty(enable):
                client_error("enable不能为空", "config-37")

            if enable not in [0,1]:
                client_error("enable不正确", "config-38")

            if is_update and config['enable'] != enable:    
                set_list.append("enable=%s")
                para = para + (enable,)
                diff.append({"field":"enable","old":config['enable'],"new":enable})

        else:
            enable = True

        if is_update:
            set_list.append("update_at=now()")
            set_str = ",".join(set_list)
            sql = "update config set {set_str} where scope_name=%s and scope_id=%s and type=%s and name=%s ".format(set_str=set_str)
            para = para + (scope_name,scope_id,config_type,config_name,)
            conn.execute(sql, para)
            if len(json.dumps(diff)) > 60000:
                diff = []
                
            return  config_id,json.dumps(diff)

        else:
            sql = "insert into config values (%s, %s,%s,%s, %s, now(),now(),%s,null)"
            para = (config_name, value, config_type,scope_id,scope_name, enable, )    
            conn.execute(sql, para)

            if task_id:
                sql = "update config set task_id=%s where scope_name=%s and scope_id=%s and type=%s and name=%s "
                para = (task_id, scope_name,scope_id,config_type,config_name,)
                conn.execute(sql, para)                

            return config_id, None

    def check_register_require(self, conn, value):
        value = json.loads(value)
        if "username" not in value or "phone" not in value or "email" not in value or "qq" not in value:
            client_error("username,phone,email或qq找不到", "config-41")

        username = value['username']
        qq = value['qq']
        email = value['email']
        phone = value['phone']

        if "need" not in username or "need" not in qq or "need" not in email or "need" not in phone:
            client_error("need找不到", "config-42")

        if "verify" not in email or "need" not in phone:
            client_error("verify找不到", "config-43")

        # 用户，邮箱，手机至少有一个设置
        if not username['need'] and not email['need'] and not phone['need']:
            client_error("用户名,邮箱和手机至少设置一项填写","config-44")

        if email['need'] and email['verify']:
            smtp_data = json.loads(conn.fetchone("select * from config where name='smtp' and type='system'  ")['value'])
            if len(smtp_data) == 0:
                client_error("开启邮箱验证前，请先设置SMTP", "config-45")

        if phone['need'] and phone['verify']:
            sms_data = json.loads(conn.fetchone("select * from config where name='sms_config' and type='system' ")['value'])
            if len(sms_data) == 0:
                client_error("开启手机验证时，请先设置手机短信", "config-46")

    def check_node_monitor_config(self, value):
        value = json.loads(value)
        # monitor_api
        if "monitor_api" in value:
            monitor_api = value['monitor_api']
            if monitor_api and not monitor_api.startswith("http://") and not monitor_api.startswith("https://"):
                client_error("monitor_api需要以http://或者https://开头","config-199")

        # interval
        if "interval" not in value:
            client_error("缺少interval参数","config-201")

        interval = value['interval']
        if not is_number(interval):
            client_error("interval参数不正确","config-202")

        if int(interval) < 30 or int(interval) > 300:
            client_error("interval参数范围在30-300之间","config-205")

        # failed_times
        if "failed_times" not in value:
            client_error("缺少failed_times参数","config-203")

        failed_times = value['failed_times']
        if not is_number(failed_times):
            client_error("failed_times参数不正确","config-204")        

        if int(failed_times) < 1 or int(failed_times) > 10:
            client_error("failed_times参数范围在1-10之间","config-206")

        # failed_rate
        if "failed_rate" not in value:
            client_error("缺少failed_rate参数","config-207")

        failed_rate = value['failed_rate']
        if not is_number(failed_rate):
            client_error("failed_rate参数不正确","config-208")        

        if int(failed_rate) < 1 or int(failed_rate) > 100:
            client_error("failed_rate参数范围在1-100之间","config-209")


    def check_proxy_cache(self, value):
        proxy_cache = json.loads(value)
        if not isinstance(proxy_cache, list):
            client_error("proxy_cache格式不正确", "config-47")

        if not is_empty(proxy_cache):
            for i in range(len(proxy_cache)):
                # type
                if "type" in proxy_cache[i]:
                    p_type = proxy_cache[i]['type']
                    if is_empty(p_type):
                        client_error("proxy_cache type不能为空", "config-48")

                else:
                    client_error("缺少proxy cache type值","config-49")   

                # content
                if "content" in proxy_cache[i]:
                    p_content = proxy_cache[i]['content']
                    if is_empty(p_content):
                        client_error("proxy_cache content不能为空", "config-50")

                else:
                    client_error("缺少proxy cache 缓存值","config-50")   

                # expire
                if "expire" in proxy_cache[i]:
                    expire = proxy_cache[i]['expire']
                    if is_empty(expire):
                        client_error("proxy_cache expire不能为空", "config-52")

                else:
                    client_error("缺少proxy cache缓存时间","config-53")    

                # unit
                if "unit" in proxy_cache[i]:
                    unit = proxy_cache[i]['unit']
                    if is_empty(unit):
                        client_error("proxy_cache unit不能为空", "config-54")

                else:
                    client_error("缺少proxy cache缓存时间单位","config-55")    

                # ignore_arg
                if "ignore_arg" in proxy_cache[i]:
                    ignore_arg = proxy_cache[i]['ignore_arg']
                    if is_empty(ignore_arg):
                        client_error("proxy_cache ignore_arg不能为空", "config-56")

                else:
                    client_error("ignore_arg不能为空","config-57")


                # proxy_ignore_headers
                if "proxy_ignore_headers" in proxy_cache[i]:
                    proxy_ignore_headers = proxy_cache[i]['proxy_ignore_headers']

                else:
                    client_error("proxy_ignore_headers不能为空","config-58")


                # no_cache
                if "no_cache" in proxy_cache[i]:
                    no_cache = proxy_cache[i]['no_cache']

                else:
                    client_error("no_cache不能为空", "config-59")


                if p_type not in ["suffix","dir","full_path"]:
                    client_error("type无效", "config-60")

                if p_type == "suffix":
                    for c in p_content.split("|"):
                        if not re.match(r"^[0-9a-zA-Z_-]+$",c):
                            client_error("后缀名不合法,只允许数字、英文字母、_-", "config-61")

                elif p_type == "dir":
                    for c in p_content.split("|"):
                        if not c.startswith("/") or not c.endswith("/"):
                            client_error("目录名必须以/开头和/结尾", "config-62")

                elif p_type == "full_path":
                    for c in p_content.split("|"):
                        if not c.startswith("/"):
                            client_error("全路径必须以/开头", "config-63")                        

                if not is_number(expire):
                    client_error("缓存时间不对", "config-64")

                if unit not in ["d","h","m","s"]:
                    client_error("缓存单位不对", "config-65")

                if ignore_arg not in [0,1]:
                    client_error("ignore_arg不对", "config-66")

                if proxy_ignore_headers:
                    for p in proxy_ignore_headers.split():
                        if p not in ["X-Accel-Expires", "Expires", "Cache-Control", "Set-Cookie", "Vary"]:
                            client_error("proxy_ignore_headers不对", "config-67")

                if not is_empty(no_cache):
                    for n in no_cache:
                        if "variable" in n:
                            variable = n['variable']
                        else:
                            client_error("no_cache缺少variable", "config-68")    

                        if "string" in n:
                            string = n['string'] # 生成map时，记得转义正则的特殊字符
                        else:
                            client_error("no_cache缺少string", "config-69")

                        if (variable not in ["$args","$content_type","$content_length","$host","$https","$is_args","$query_string","$remote_addr","$request_method","$request_uri","$scheme","$status","$uri"]) and (not re.match(r"^(\$arg_|\$cookie_|\$http_).*",variable)):
                            client_error("{variable}变量不允许".format(variable=variable), "config-70")

                        if is_empty(string):
                            client_error("string不能为空", "config-71")        

    def check_smtp(self, value):
        # 检查值
        try:
            content = json.loads(value)

        except ValueError:
            client_error("值非json", "config-72")        

        if "ip" in content:
            ip = content['ip']
            if is_empty(ip):
                client_error("请输入ip或域名", "config-73")  

            if not is_ip(ip) and not is_domain(ip):
                client_error("请输入正确的ip或域名", "config-74") 

        else:
            client_error("请输入ip或域名", "config-75")   

        if "use_ssl" in content:
            use_ssl = content['use_ssl']
            if is_empty(use_ssl):
                client_error("请指定use_ssl", "config-76")  

            if use_ssl not in [0,1]:
                client_error("请输入正确的use_ssl", "config-77") 

        else:
            client_error("请指定use_ssl", "config-78") 

        if "port" in content:
            port = content['port']
            if is_empty(port):
                client_error("请输入port", "config-79")  

            if not is_port(port):
                client_error("请输入正确的port", "config-80") 

        else:
            client_error("请输入port", "config-81")  

        if "user" in content:
            user = content['user']
            if is_empty(user):
                client_error("请输入user", "config-82")  

        else:
            client_error("请输入user", "config-83")

        if "pwd" in content:
            pwd = content['pwd']
            if is_empty(pwd):
                client_error("请输入pwd", "config-84")  

        else:
            client_error("请输入pwd", "config-85")

        # 检查是否能连接
        title = u"测试邮件标题"
        data = u"测试邮件正文"
        ok, err = send_mail(user, title, data, smtp_auth={"ip":ip,"port":port,"user":user,"pwd":pwd,"use_ssl":use_ssl})
        if err:
            client_error("测试发送邮件失败,原因：{err}".format(err=err), "config-86")

    def get_domain_line(self, conn, value):
        # 检查值
        try:
            content = json.loads(value)

        except ValueError:
            client_error("值非json", "config-87")

        # id
        if "id" not in content:
            client_error("请输入ID", "config-88")

        auth_id = content['id']
        if is_empty(auth_id):
            client_error("请输入ID", "config-89")

        # token
        if "token" not in content:
            client_error("请输入Token", "config-90")  

        auth_token = content['token']
        if is_empty(auth_token):
            client_error("请输入Token", "config-91")

        # domain
        if "domain" not in content:
            client_error("请输入域名", "config-92")  

        domain = content['domain']
        if is_empty(domain):
            client_error("请输入域名", "config-93")  
            
        # dns
        if "dns" not in content:
            client_error("请选择DNS", "config-94")

        dns = content['dns']
        if is_empty(dns):
            client_error("请选择DNS", "config-95")

        # 查询设置前的dns
        default_line_id = {"cloudflare":"0", "aliyun":"default","dnspod_cn":"0","dnspod_com":"0", "dnsdun":"默认","dnsdotcom":"0"}
        default_line_name = {"cloudflare":"Default", "aliyun":"默认","dnspod_cn":"默认","dnspod_com":"默认", "dnsdun":"默认","dnsdotcom":"默认"}

        dns_config = json.loads(conn.fetchone("select value from config where name='dns_config' and type='system' ")['value'])
        if dns_config:
            dns_pre = dns_config['dns']
            line_id_pre = default_line_id[dns_pre]
            line_id_now = default_line_id[dns]

            line_name_now = default_line_name[dns]

            if dns_pre != dns:
                conn.execute("update line set line_id=%s,line_name=%s where line_id=%s ",(line_id_now,line_name_now,line_id_pre, ) )

        # zone id
        if "zone_id" not in content and dns == "huaweicloud":
            client_error("请输入zone id", "config-96")

        gateway = None
        if dns == "huaweicloud":
            zone_id = content['zone_id']
            if is_empty(zone_id):
                client_error("zone_id不能为空", "config-97")

        elif dns == "dnsdun":
            if "gateway" not in content:
                client_error("dnsdun api地址不能为空", "config-98")

            gateway = content['gateway']
            if is_empty(gateway ):
                client_error("dnsdun api地址不能为空", "config-99")

        # ttl
        if "ttl" not in content:
            client_error("请输入TTL", "config-100")    

        ttl = content['ttl']

        if is_empty(ttl):
            client_error("请输入TTL", "config-101")   

        if not is_number(ttl):
            client_error("请输入正确的TTL", "config-102")   

        if dns not in ["aliyun", "dnsdun", "dnspod_cn", "dnspod_com", "huaweicloud", "dnsdotcom","cloudflare"]:
            client_error("指定的dns不支持", "config-103")        

        # 获取线路列表
        module_name = 'tasks.dnsapi.{dns}'.format(dns=dns)
        dns_module = importlib.import_module(module_name)

        dns_auth = json.dumps({"id": auth_id, "token":auth_token,"gateway":gateway})
        dns_ins = dns_module.Dns(dns_auth)

        if dns == "huaweicloud":
            token_path = "/opt/huaweicloud_token"
            if os.path.exists(token_path):
                os.remove(token_path)

            ret, err = dns_ins.get_lines(domain, zone_id)
        else:
            ret, err = dns_ins.get_lines(domain)

        if err:
            client_error("保存失败,原因:{err}".format(err=err), "config-104")

        content["lines"] = ret
        return json.dumps(content)

    def check_sms_config(self, value):
        # 检查值
        try:
            json_value = json.loads(value)

        except ValueError:
            client_error("值非json", "config-105")

        if "type" not in json_value:
            client_error("找不到type", "config-106")

        sms_type = json_value['type']
        if sms_type not in ["smsbao"]:
            client_error("所指定的sms提供商不支持", "config-107")

        if "username" not in json_value:
            client_error("找不到username", "config-108")

        if is_empty(json_value['username']):
            client_error("username不能为空", "config-109")

        if "password" not in json_value:
            client_error("找不到password", "config-110")

        if is_empty(json_value['password']):
            client_error("password不能为空", "config-111")                    

        module_name = 'tasks.sms.{sms_type}'.format(sms_type=sms_type)
        sms_module = importlib.import_module(module_name)

        auth = json.dumps({"username": json_value['username'], "password":json_value['password']})
        sms_ins = sms_module.Sms(auth)
        ok, err = sms_ins.test()
        if not ok:
            client_error(err,"config-112")


    def check_nginx_config_value(self,value, scope_name):
        # 检查值
        try:
            json_value = json.loads(value)

        except ValueError:
            client_error("值非json", "config-113")

        # worker_rlimit_nofile
        if "worker_rlimit_nofile" in json_value:
            worker_rlimit_nofile = json_value['worker_rlimit_nofile']

            if not is_number(worker_rlimit_nofile):
                client_error("worker_rlimit_nofile不是有效的数字","config-114")

        else:
            if scope_name == "global": client_error("找不到worker_rlimit_nofile","config-115")

        # worker_shutdown_timeout
        if "worker_shutdown_timeout" in json_value:
            worker_shutdown_timeout = json_value['worker_shutdown_timeout']
            ok, err = is_valid_time(worker_shutdown_timeout)
            if not ok:
                client_error("worker_shutdown_timeout {err}".format(err=err),"config-116")

        else:
            if scope_name == "global": client_error("找不到worker_shutdown_timeout","config-117")

        # worker_connections
        if "worker_connections" in json_value:
            worker_connections = json_value['worker_connections']

            if not is_number(worker_connections):
                client_error("worker_connections不是有效的数字","config-118")

        else:
            if scope_name == "global": client_error("找不到worker_connections","config-119")

        # worker_processes
        if "worker_processes" in json_value:
            worker_processes = json_value['worker_processes']

            if not is_number(worker_processes) and worker_processes != "auto":
                client_error("worker_processes不是有效的数字或auto","config-120")

        else:
            if scope_name == "global": client_error("找不到worker_processes","config-121")


        # resolver
        if "resolver" in json_value:
            resolver = json_value['resolver']
            for r in resolver.split():
                if not is_ip(r):
                    client_error("resolver的{r}不是有效的ip".format(r=r),"config-122")

        else:
            if scope_name == "global": client_error("找不到resolver","config-123")

        # resolver_timeout
        if "resolver_timeout" in json_value:
            resolver_timeout = json_value['resolver_timeout']
            ok, err = is_valid_time(resolver_timeout)
            if not ok:
                client_error("resolver_timeout {err}".format(err=err),"config-124")

        else:
            if scope_name == "global": client_error("找不到resolver_timeout","config-125")

        if "http" in json_value:
            # proxy_cache_dir
            if "proxy_cache_dir" in json_value['http']:
                proxy_cache_dir = json_value['http']['proxy_cache_dir']
                if is_empty(proxy_cache_dir):
                    client_error("缓存目录不能为空", "config-214")

                if proxy_cache_dir == "/":
                    client_error("缓存目录不能为/，请填写子目录", "config-213")

                if not proxy_cache_dir.startswith("/"):
                    client_error("缓存目录需以/开头", "config-215") 

            else:
                if scope_name == "global": client_error("找不到proxy_cache_dir","config-216")            

            # gzip_comp_level
            if "gzip_comp_level" in json_value['http']:
                gzip_comp_level = json_value['http']['gzip_comp_level']
                if not is_number(gzip_comp_level):
                    client_error("gzip_comp_level不是有效的数字","config-126")

                if int(gzip_comp_level) < 1 or int(gzip_comp_level) > 9:
                    client_error("gzip_comp_level的值应该在1-9之间", "config-127")

            else:
                if scope_name == "global": client_error("找不到gzip_comp_level","config-128")

            # gzip_http_version
            if "gzip_http_version" in json_value['http']:
                gzip_http_version = json_value['http']['gzip_http_version']
                if gzip_http_version not in ["1.0","1.1"]:
                    client_error("gzip_http_version值应该为1.0或1.1", "config-129")

            else:
                if scope_name == "global": client_error("找不到gzip_http_version","config-130")

            # gzip_min_length
            if "gzip_min_length" in json_value['http']:
                gzip_min_length = json_value['http']['gzip_min_length']
                ok, err = is_valid_size(gzip_min_length)
                if not ok:
                    client_error("gzip_min_length {err}".format(err=err), "config-131")

            else:
                if scope_name == "global": client_error("找不到gzip_min_length","config-132")

            # gzip_vary
            if "gzip_vary" in json_value['http']:
                gzip_vary = json_value['http']['gzip_vary']
                if gzip_vary not in ["on","off"]:
                    client_error("gzip_vary值应该为on或off", "config-133")

            else:
                if scope_name == "global": client_error("找不到gzip_vary","config-134")

            # proxy_buffering
            if "proxy_buffering" in json_value['http']:
                proxy_buffering = json_value['http']['proxy_buffering']
                if proxy_buffering not in ["on","off"]:
                    client_error("proxy_buffering值应该为on或off", "config-135")

            else:
                if scope_name == "global": client_error("找不到proxy_buffering","config-136")

            # proxy_cache_methods
            if "proxy_cache_methods" in json_value['http']:
                proxy_cache_methods = json_value['http']['proxy_cache_methods']
                for p in proxy_cache_methods.split():
                    if p not in ["GET","HEAD","POST"]:
                        client_error("proxy_cache_methods值应该为GET、HEAD或POST", "config-137")

            else:
                if scope_name == "global": client_error("找不到proxy_cache_methods","config-138")

            # proxy_http_version
            if "proxy_http_version" in json_value['http']:
                proxy_http_version = json_value['http']['proxy_http_version']
                if proxy_http_version not in ["1.0","1.1"]:
                    client_error("proxy_http_version值应该为1.0或1.1", "config-139")

            else:
                if scope_name == "global": client_error("找不到proxy_http_version","config-140")


            # proxy_max_temp_file_size
            if "proxy_max_temp_file_size" in json_value['http']:
                proxy_max_temp_file_size = json_value['http']['proxy_max_temp_file_size']
                ok, err = is_valid_size(proxy_max_temp_file_size)
                if not ok:
                    client_error("proxy_max_temp_file_size {err}".format(err=err), "config-141")

            else:
                if scope_name == "global": client_error("找不到proxy_max_temp_file_size","config-142")

            # proxy_next_upstream
            if "proxy_next_upstream" in json_value['http']:
                proxy_next_upstream = json_value['http']['proxy_next_upstream']
                for p in proxy_next_upstream.split():
                    if p not in ["error","timeout","invalid_header","http_500","http_502","http_503","http_504","http_403","http_404","http_429","non_idempotent","off"]:
                        client_error("proxy_next_upstream值无效", "config-143")

            else:
                if scope_name == "global": client_error("找不到proxy_next_upstream","config-144")    

            # proxy_connect_timeout
            if "proxy_connect_timeout" in json_value['http']:
                proxy_connect_timeout = json_value['http']['proxy_connect_timeout']
                ok, err = is_valid_time(proxy_connect_timeout)
                if not ok:
                    client_error("proxy_connect_timeout {err}".format(err=err),"config-145")

            else:
                if scope_name == "global": client_error("找不到proxy_connect_timeout","config-146")
                        
            # proxy_send_timeout
            if "proxy_send_timeout" in json_value['http']:
                proxy_send_timeout = json_value['http']['proxy_send_timeout']
                ok, err = is_valid_time(proxy_send_timeout)
                if not ok:
                    client_error("proxy_send_timeout {err}".format(err=err),"config-147")

            else:
                if scope_name == "global": client_error("找不到proxy_send_timeout","config-148")

            # proxy_read_timeout
            if "proxy_read_timeout" in json_value['http']:
                proxy_read_timeout = json_value['http']['proxy_read_timeout']
                ok, err = is_valid_time(proxy_read_timeout)
                if not ok:
                    client_error("proxy_read_timeout {err}".format(err=err),"config-149")

            else:
                if scope_name == "global": client_error("找不到proxy_read_timeout","config-150")

            # server
            if "server" in json_value['http']:
                server = json_value['http']['server']
                if is_empty(server):
                    client_error("server不能为空", "config-151")

            else:
                if scope_name == "global": client_error("找不到server","config-152")

            # client_max_body_size
            if "client_max_body_size" in json_value['http']:
                client_max_body_size = json_value['http']['client_max_body_size']
                ok, err = is_valid_size(client_max_body_size)
                if not ok:
                    client_error("client_max_body_size {err}".format(err=err), "config-153")

            else:
                if scope_name == "global": client_error("找不到client_max_body_size","config-154")

            # default_type
            if "default_type" in json_value['http']:
                default_type = json_value['http']['default_type']
                if is_empty(default_type):
                    client_error("default_type不能为空", "config-155")

            else:
                if scope_name == "global": client_error("找不到default_type","config-156")

            # keepalive_requests
            if "keepalive_requests" in json_value['http']:
                keepalive_requests = json_value['http']['keepalive_requests']
                if not is_number(keepalive_requests):
                    client_error("keepalive_requests不是有效的数字", "config-157")

            else:
                if scope_name == "global": client_error("找不到keepalive_requests","config-158")

            # keepalive_timeout
            if "keepalive_timeout" in json_value['http']:
                keepalive_timeout = json_value['http']['keepalive_timeout']
                ok, err = is_valid_time(keepalive_timeout)
                if not ok:
                    client_error("keepalive_timeout {err}".format(err=err),"config-159")

            else:
                if scope_name == "global": client_error("找不到keepalive_timeout","config-160")

            # log_not_found
            if "log_not_found" in json_value['http']:
                log_not_found = json_value['http']['log_not_found']
                if log_not_found not in ["on","off"]:
                    client_error("log_not_found值应该为on或off", "config-161")

            else:
                if scope_name == "global": client_error("找不到log_not_found","config-162")

            # server_names_hash_max_size
            if "server_names_hash_max_size" in json_value['http']:
                server_names_hash_max_size = json_value['http']['server_names_hash_max_size']
                if not is_number(server_names_hash_max_size):
                    client_error("server_names_hash_max_size值不是有效的数字", "config-163")

            else:
                if scope_name == "global": client_error("找不到server_names_hash_max_size","config-164")

            # server_tokens
            if "server_tokens" in json_value['http']:
                server_tokens = json_value['http']['server_tokens']
                if server_tokens not in ["on","off"]:
                    client_error("server_tokens值应该为on或off", "config-165")

            else:
                if scope_name == "global": client_error("找不到server_tokens","config-166")
        else:
            if scope_name == "global": client_error("找不到http","config-190")

        if "stream" in json_value:
            # proxy_connect_timeout
            if "proxy_connect_timeout" in json_value['stream']:
                proxy_connect_timeout = json_value['stream']['proxy_connect_timeout']
                ok, err = is_valid_time(proxy_connect_timeout)
                if not ok:
                    client_error("proxy_connect_timeout {err}".format(err=err),"config-167")

            else:
                if scope_name == "global": client_error("找不到proxy_connect_timeout","config-168")

            # proxy_timeout
            if "proxy_timeout" in json_value['stream']:
                proxy_timeout = json_value['stream']['proxy_timeout']
                ok, err = is_valid_time(proxy_timeout)
                if not ok:
                    client_error("proxy_timeout {err}".format(err=err),"config-169")

            else:
                if scope_name == "global": client_error("找不到proxy_timeout","config-170")
        else:
            if scope_name == "global": client_error("找不到stream","config-191")

    def check_openresty_config(self, value, scope_name):
        # 检查值
        try:
            json_value = json.loads(value)

        except ValueError:
            client_error("值非json", "config-171")

        if scope_name == "global" and "key" not in  json_value:
            client_error("找不到key", "config-212")

        if "log" in json_value:
            # port
            if "port" in json_value['log']:
                port = json_value['log']['port']
                if not is_port(port):
                    client_error("log.port不是一个有效的端口", "config-172")

            else:
                if scope_name == "global": client_error("找不到log.port","config-173")

            # host
            if "host" in json_value['log']:
                host = json_value['log']['host']
                if not is_ip(host):
                    client_error("log.host不是一个有效的ip", "config-174")

            else:
                if scope_name == "global": client_error("找不到log.host","config-175")

            # log_level
            if "log_level" in json_value['log']:
                log_level = json_value['log']['log_level']
                if log_level not in ["debug","info","error","warning"]:
                    client_error("log.log_level可选值为debug info error warning", "config-176")

            else:
                if scope_name == "global": client_error("找不到log.log_level","config-177")

            # debug_ip
            if "debug_ip" in json_value['log']:
                debug_ip = json_value['log']['debug_ip']
                if debug_ip and not is_ip(debug_ip):
                    client_error("log.debug_ip不是一个有效的ip", "config-178")

            else:
                if scope_name == "global": client_error("找不到log.debug_ip","config-179")
        else:
            if scope_name == "global": client_error("找不到log","config-192")

        # slider_html
        if "slider_html" in json_value:
            slider_html = json_value['slider_html']
            if is_empty(slider_html):
                client_error("slider_html不能为空", "config-180")

        else:
            if scope_name == "global": client_error("找不到slider_html","config-181")

        # block_time
        if "block_time" in json_value:
            block_time = json_value['block_time']
            if not is_number(block_time):
                client_error("黑名单时间不正确", "config-210")

            if int(block_time) <= 0 or int(block_time) > 2147483:
                client_error("黑名单时间有效范围为0-2147483", "config-211")


        # captcha_html
        if "captcha_html" in json_value:
            captcha_html = json_value['captcha_html']
            if is_empty(captcha_html):
                client_error("captcha_html不能为空", "config-182")

        else:
            if scope_name == "global": client_error("找不到captcha_html","config-183")

        # click_html
        if "click_html" in json_value:
            click_html = json_value['click_html']
            if is_empty(click_html):
                client_error("click_html不能为空", "config-184")

        else:
            if scope_name == "global": client_error("找不到click_html","config-185")

        # block_time
        if "block_time" in json_value:
            block_time = json_value['block_time']
            if not is_number(block_time):
                client_error("block_time值不是数字", "config-186")

        else:
            if scope_name == "global": client_error("找不到block_time","config-187")

        # white_time
        if "white_time" in json_value:
            white_time = json_value['white_time']
            if not is_number(white_time):
                client_error("white_time值不是数字", "config-188")

        else:
            if scope_name == "global": client_error("找不到white_time","config-189")


        # custom_white
        if "custom_white" in json_value:
            custom_white = json_value['custom_white']
            for ip in custom_white.split():
                ip_arr = ip.split("/")
                if len(ip_arr) == 1:
                    if not is_ip(ip_arr[0]):
                        client_error("ip {ip}有误".format(ip=ip), "config-194")

                elif len(ip_arr) == 2:
                    if not is_ip(ip_arr[0]):
                        client_error("ip {ip}有误".format(ip=ip), "config-195")

                    if ip_arr[1] not in ["16","24"]:
                        client_error("掩码只支持16、24", "config-196")

                else:
                    client_error("ip {ip}有误".format(ip=ip), "config-197")


        # custom_black
        if "custom_black" in json_value:
            custom_black = json_value['custom_black']
            for ip in custom_black.split():
                ip_arr = ip.split("/")
                if len(ip_arr) == 1:
                    if not is_ip(ip_arr[0]):
                        client_error("ip {ip}有误".format(ip=ip), "config-198")

                elif len(ip_arr) == 2:
                    if not is_ip(ip_arr[0]):
                        client_error("ip {ip}有误".format(ip=ip), "config-199")

                    if ip_arr[1] not in ["16","24"]:
                        client_error("掩码只支持16、24", "config-200")

                else:
                    client_error("ip {ip}有误".format(ip=ip), "config-201")
