1590 lines
61 KiB
Python
1590 lines
61 KiB
Python
|
|
# coding: utf-8
|
||
|
|
# ------------------------------
|
||
|
|
# 域名管理
|
||
|
|
# ------------------------------
|
||
|
|
import ipaddress
|
||
|
|
import json
|
||
|
|
import os.path
|
||
|
|
import shutil
|
||
|
|
import sys
|
||
|
|
import threading
|
||
|
|
import time
|
||
|
|
from datetime import datetime
|
||
|
|
from typing import Tuple, Dict
|
||
|
|
|
||
|
|
if not "class/" in sys.path:
|
||
|
|
sys.path.insert(0, "class/")
|
||
|
|
if not "class_v2/" in sys.path:
|
||
|
|
sys.path.insert(0, "class_v2/")
|
||
|
|
|
||
|
|
import public
|
||
|
|
|
||
|
|
try:
|
||
|
|
import dns.resolver
|
||
|
|
except ImportError:
|
||
|
|
try:
|
||
|
|
public.ExecShell("btpip install dnspython")
|
||
|
|
import dns.resolver
|
||
|
|
except ImportError:
|
||
|
|
public.print_log("install dnspython fail.")
|
||
|
|
|
||
|
|
from acme_v2 import acme_v2
|
||
|
|
from config_v2 import config
|
||
|
|
from panelDnsapi import extract_zone
|
||
|
|
from panel_site_v2 import panelSite
|
||
|
|
from public.aaModel import Q
|
||
|
|
from public.exceptions import HintException
|
||
|
|
from public.validate import Param
|
||
|
|
from .config import (
|
||
|
|
DNS_MAP,
|
||
|
|
WorkFor,
|
||
|
|
UserFor,
|
||
|
|
PANEL_DOMAIN,
|
||
|
|
PANEL_LIMIT_DOMAIN,
|
||
|
|
MANUAL_APPLY_PL,
|
||
|
|
)
|
||
|
|
from .model import (
|
||
|
|
DnsDomainProvider,
|
||
|
|
DnsDomainRecord,
|
||
|
|
DnsDomainSSL,
|
||
|
|
DnsDomainTask,
|
||
|
|
)
|
||
|
|
|
||
|
|
from .service import (
|
||
|
|
DomainValid,
|
||
|
|
CertHandler,
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def fix_log(ssl: DnsDomainSSL, log: str) -> None:
|
||
|
|
if not ssl or not log:
|
||
|
|
return
|
||
|
|
if not ssl.log or ssl.log != log:
|
||
|
|
ssl.log = log
|
||
|
|
ssl.save()
|
||
|
|
|
||
|
|
|
||
|
|
# noinspection PyUnusedLocal
|
||
|
|
class DomainObject:
|
||
|
|
date_format = "%Y-%m-%d"
|
||
|
|
vhost = os.path.join(public.get_panel_path(), "vhost")
|
||
|
|
mail_db_file = "/www/vmail/postfixadmin.db"
|
||
|
|
manual_apply = MANUAL_APPLY_PL
|
||
|
|
deploy_map = {
|
||
|
|
1: UserFor.sites,
|
||
|
|
2: UserFor.panel,
|
||
|
|
3: UserFor.mails,
|
||
|
|
4: UserFor.account,
|
||
|
|
}
|
||
|
|
|
||
|
|
def __init__(self):
|
||
|
|
self.supports = list(DNS_MAP.keys())
|
||
|
|
if not os.path.exists(self.manual_apply):
|
||
|
|
public.writeFile(self.manual_apply, json.dumps({}))
|
||
|
|
|
||
|
|
@staticmethod
|
||
|
|
def _clear_task_force():
|
||
|
|
# clear task, 3 hours
|
||
|
|
try:
|
||
|
|
one_hours = 1000 * 60 * 60 * 3
|
||
|
|
out_time = round(time.time() * 1000) - one_hours
|
||
|
|
DnsDomainTask.objects.filter(create_time__lte=out_time).delete()
|
||
|
|
except Exception:
|
||
|
|
pass
|
||
|
|
|
||
|
|
@staticmethod
|
||
|
|
def _end_time(data_str: str) -> int:
|
||
|
|
try:
|
||
|
|
if not data_str:
|
||
|
|
return 0
|
||
|
|
today = datetime.today().date()
|
||
|
|
end_date = datetime.strptime(data_str, DomainObject.date_format).date()
|
||
|
|
return max((end_date - today).days - 1 if today <= end_date else 0, 0)
|
||
|
|
except ValueError:
|
||
|
|
return 0
|
||
|
|
|
||
|
|
@staticmethod
|
||
|
|
def _process_key(data: dict, key: list = None) -> dict:
|
||
|
|
if not key:
|
||
|
|
return data
|
||
|
|
if "api_user" in key: # 隐藏api_user
|
||
|
|
k = "api_user"
|
||
|
|
if len(data.get(k, "")) > 0:
|
||
|
|
data[k] = data[k][:len(data[k]) // 2] + "***"
|
||
|
|
if "record" in key: # 隐藏cf自带域名
|
||
|
|
k = "record"
|
||
|
|
endswith_str = f'.{data.get("domain", "")}'
|
||
|
|
if data.get(k, "").endswith(endswith_str):
|
||
|
|
data[k] = data[k].replace(endswith_str, "")
|
||
|
|
return data
|
||
|
|
|
||
|
|
@staticmethod
|
||
|
|
def _add_ssl_info(data: dict) -> dict:
|
||
|
|
new_domains = []
|
||
|
|
domains = data.get("domains", [])
|
||
|
|
for domain in domains:
|
||
|
|
ssl_obj = DnsDomainSSL.objects.filter(
|
||
|
|
provider_id=data.get("id", 0), dns__contains=domain,
|
||
|
|
).order_by("-create_time").first()
|
||
|
|
if not ssl_obj:
|
||
|
|
ssl_info = {
|
||
|
|
"id": 0,
|
||
|
|
"end_time": -1,
|
||
|
|
"end_date": "-",
|
||
|
|
"alarm": 0,
|
||
|
|
"auto_renew": 0,
|
||
|
|
}
|
||
|
|
else:
|
||
|
|
ssl_info = {
|
||
|
|
"id": ssl_obj.id,
|
||
|
|
"end_time": DomainObject._end_time(ssl_obj.not_after),
|
||
|
|
"end_date": ssl_obj.not_after,
|
||
|
|
"alarm": ssl_obj.alarm,
|
||
|
|
"auto_renew": ssl_obj.auto_renew,
|
||
|
|
}
|
||
|
|
new_domains.append(
|
||
|
|
{"name": domain, "ssl_info": ssl_info}
|
||
|
|
)
|
||
|
|
data["domains"] = new_domains
|
||
|
|
return data
|
||
|
|
|
||
|
|
@staticmethod
|
||
|
|
def _add_task_info(data: dict, task_name: str = None) -> dict:
|
||
|
|
# 初始化, 申请任意域名, 续签, 同步
|
||
|
|
tasks_obj = DnsDomainTask.objects.filter(
|
||
|
|
provider_id=data.get("id", -1), task_status__lt=100
|
||
|
|
)
|
||
|
|
if task_name:
|
||
|
|
tasks_obj.filter(task_name=task_name)
|
||
|
|
|
||
|
|
data["task"] = tasks_obj.order_by("-create_time").as_list()
|
||
|
|
return data
|
||
|
|
|
||
|
|
def get_dns_support(self, get):
|
||
|
|
res = list(set(self.supports))
|
||
|
|
if "YakPanelDns" in res:
|
||
|
|
res.remove("YakPanelDns")
|
||
|
|
return public.success_v2(res)
|
||
|
|
|
||
|
|
# =========== 托管商 ===========
|
||
|
|
def sync_dns_info(self, get):
|
||
|
|
"""
|
||
|
|
对账号立即同步域名信息
|
||
|
|
"""
|
||
|
|
from .service import SyncService
|
||
|
|
target_id = get.id if hasattr(get, "id") else None
|
||
|
|
instance = SyncService(target_id)
|
||
|
|
task = threading.Thread(
|
||
|
|
target=instance.process, kwargs=({"apply_new": True})
|
||
|
|
)
|
||
|
|
task.start()
|
||
|
|
return public.success_v2(public.lang("success"))
|
||
|
|
|
||
|
|
def list_dns_api(self, get):
|
||
|
|
"""
|
||
|
|
dns api 列表
|
||
|
|
"""
|
||
|
|
public.set_module_logs("sys_domain", "Domain_SSL_Open", 1)
|
||
|
|
try:
|
||
|
|
get.validate([
|
||
|
|
Param("p").Integer(),
|
||
|
|
Param("limit").Integer(),
|
||
|
|
Param("pid").Integer(),
|
||
|
|
], [
|
||
|
|
public.validate.trim_filter(),
|
||
|
|
])
|
||
|
|
except Exception as ex:
|
||
|
|
public.print_log("error info: {}".format(ex))
|
||
|
|
return public.return_message(-1, 0, str(ex))
|
||
|
|
|
||
|
|
# clear task
|
||
|
|
self._clear_task_force()
|
||
|
|
from .service import check_legal, init_aaDns
|
||
|
|
check_legal()
|
||
|
|
init_aaDns()
|
||
|
|
page = int(getattr(get, "p", 1))
|
||
|
|
limit = int(getattr(get, "limit", 100))
|
||
|
|
obj = DnsDomainProvider.objects.all()
|
||
|
|
if hasattr(get, "pid"):
|
||
|
|
obj = DnsDomainProvider.objects.filter(id=get.pid)
|
||
|
|
total = obj.count()
|
||
|
|
result = obj.limit(limit).offset((page - 1) * limit).as_list()
|
||
|
|
for r in result:
|
||
|
|
r = self._process_key(r, key=["api_user"])
|
||
|
|
r = self._add_ssl_info(r)
|
||
|
|
r = self._add_task_info(r)
|
||
|
|
return public.success_v2({"data": result, "total": total})
|
||
|
|
|
||
|
|
def create_dns_api(self, get):
|
||
|
|
try:
|
||
|
|
get.validate([
|
||
|
|
Param("name").String().Require(),
|
||
|
|
Param("api_user").String().Require(),
|
||
|
|
Param("api_key").String().Require(),
|
||
|
|
Param("permission").String(),
|
||
|
|
Param("alias").String().Require(),
|
||
|
|
Param("status").Integer(),
|
||
|
|
], [
|
||
|
|
public.validate.trim_filter(),
|
||
|
|
])
|
||
|
|
except Exception as ex:
|
||
|
|
public.print_log("error info: {}".format(ex))
|
||
|
|
return public.return_message(-1, 0, str(ex))
|
||
|
|
if "*" in get.api_user:
|
||
|
|
return public.fail_v2(public.lang("'*' Symbols that are not allowed"))
|
||
|
|
if hasattr(get, "status"):
|
||
|
|
get.status = int(get.status)
|
||
|
|
if get.name not in self.supports:
|
||
|
|
return public.fail_v2(public.lang(f"Provider not support! Support DNS provider :{self.supports}"))
|
||
|
|
if get.name == "CloudFlareDns":
|
||
|
|
if not hasattr(get, "permission") or get.permission not in ["limit", "global"]:
|
||
|
|
return public.fail_v2(public.lang("CloudFlareDns Permission must be 'limit' or 'global'!"))
|
||
|
|
else:
|
||
|
|
get.permission = "-"
|
||
|
|
|
||
|
|
if DnsDomainProvider.objects.filter(alias=get.alias).first():
|
||
|
|
return public.fail_v2(public.lang("Alias already exists!"))
|
||
|
|
|
||
|
|
if DnsDomainProvider.objects.filter(
|
||
|
|
api_user=get.api_user, api_key=get.api_key, name=get.name,
|
||
|
|
).first():
|
||
|
|
return public.fail_v2(public.lang(f"Account already exists!"))
|
||
|
|
|
||
|
|
try:
|
||
|
|
dns = DnsDomainProvider(**get.get_items())
|
||
|
|
if not dns.is_pro():
|
||
|
|
return public.fail_v2(public.lang("Please Upgrade PRO Version!"))
|
||
|
|
dns.dns_obj.verify()
|
||
|
|
dns.save()
|
||
|
|
dns.init_ssl_myself_thread()
|
||
|
|
public.set_module_logs("sys_domain", "Add_Dns_Api", 1)
|
||
|
|
return public.success_v2(public.lang("Save Successfully!"))
|
||
|
|
except Exception as ex:
|
||
|
|
raise ex
|
||
|
|
|
||
|
|
def delete_dns_api(self, get):
|
||
|
|
try:
|
||
|
|
get.validate([
|
||
|
|
Param("id").Integer().Require(),
|
||
|
|
], [
|
||
|
|
public.validate.trim_filter(),
|
||
|
|
])
|
||
|
|
except Exception as ex:
|
||
|
|
public.print_log("error info: {}".format(ex))
|
||
|
|
return public.return_message(-1, 0, str(ex))
|
||
|
|
DnsDomainRecord.objects.filter(provider_id=int(get.id)).delete()
|
||
|
|
provider = DnsDomainProvider.objects.filter(id=int(get.id)).first()
|
||
|
|
msg = f"DNS: {provider.name} Alias: {provider.alias} , Delete Successfully!"
|
||
|
|
provider.delete()
|
||
|
|
public.WriteLog("DnsSSLManager", msg)
|
||
|
|
return public.success_v2(public.lang("Delete Successfully!"))
|
||
|
|
|
||
|
|
def edit_dns_api(self, get):
|
||
|
|
if not hasattr(get, "id"):
|
||
|
|
return public.fail_v2(public.lang("id is required"))
|
||
|
|
if hasattr(get, "status"):
|
||
|
|
get.status = int(get.status)
|
||
|
|
if hasattr(get, "name") and get.name == "CloudFlareDns":
|
||
|
|
if not hasattr(get, "permission") or get.permission not in ["limit", "global"]:
|
||
|
|
return public.fail_v2(public.lang("CloudFlareDns Permission must be 'limit' or 'global'"))
|
||
|
|
if hasattr(get, "user") and "*" in get.api_user:
|
||
|
|
return public.fail_v2(public.lang("'*' symbols that are not allowed"))
|
||
|
|
|
||
|
|
# alias 不允许重复
|
||
|
|
if hasattr(get, "alias") and DnsDomainProvider.objects.filter(alias=get.alias).first():
|
||
|
|
return public.fail_v2(public.lang("Alias already exists!"))
|
||
|
|
try:
|
||
|
|
dns = DnsDomainProvider.objects.filter(id=get.id).first()
|
||
|
|
for k, v in get.get_items().items():
|
||
|
|
if hasattr(dns, k) and k != "id":
|
||
|
|
setattr(dns, k, v)
|
||
|
|
# 仅当开启时候校验
|
||
|
|
if dns.status == 1:
|
||
|
|
dns.dns_obj.verify()
|
||
|
|
if dns.name != "YakPanelDns":
|
||
|
|
DnsDomainProvider.objects.filter(id=get.id).update(dns.as_dict())
|
||
|
|
else: # 兼容 aaDNS 服务状态变更
|
||
|
|
from ssl_dnsV2.dns_manager import DnsManager
|
||
|
|
status_map = {1: "restart", 0: "stop"}
|
||
|
|
DnsManager().change_service_status(status=status_map.get(get.status))
|
||
|
|
except Exception as ex:
|
||
|
|
return public.fail_v2(str(ex))
|
||
|
|
return public.success_v2(public.lang("Save Successfully!"))
|
||
|
|
|
||
|
|
# =========== dns 记录 ===========
|
||
|
|
def list_dns_record(self, get):
|
||
|
|
try:
|
||
|
|
get.validate([
|
||
|
|
Param("p").Integer(),
|
||
|
|
Param("limit").Integer(),
|
||
|
|
Param("search_pid").Integer().Require(),
|
||
|
|
Param("domain").String(),
|
||
|
|
Param("search").String(),
|
||
|
|
Param("search_and").String(),
|
||
|
|
], [
|
||
|
|
public.validate.trim_filter(),
|
||
|
|
])
|
||
|
|
except Exception as ex:
|
||
|
|
public.print_log("error info: {}".format(ex))
|
||
|
|
return public.return_message(-1, 0, str(ex))
|
||
|
|
page = int(getattr(get, "p", 1))
|
||
|
|
limit = int(getattr(get, "limit", 100))
|
||
|
|
pid = int(get.search_pid)
|
||
|
|
provider = DnsDomainProvider.objects.find_one(id=pid)
|
||
|
|
if not provider:
|
||
|
|
return public.fail_v2(public.lang("Provider not found!"))
|
||
|
|
if hasattr(get, "domain") and get.domain:
|
||
|
|
domain_name = get.domain
|
||
|
|
else:
|
||
|
|
domain_name = provider.domains[0] if provider.domains else ""
|
||
|
|
from .service import RecordCache
|
||
|
|
RecordCache.record_ensure(domain_name)
|
||
|
|
|
||
|
|
# search_and
|
||
|
|
if hasattr(get, "search_and"):
|
||
|
|
try:
|
||
|
|
search_and = json.loads(get.search_and)
|
||
|
|
except:
|
||
|
|
search_and = {}
|
||
|
|
|
||
|
|
obj = DnsDomainRecord.objects.filter(
|
||
|
|
provider_id=pid, domain=domain_name, **search_and
|
||
|
|
)
|
||
|
|
# no search, no search_and
|
||
|
|
elif not hasattr(get, "search"):
|
||
|
|
obj = DnsDomainRecord.objects.filter(provider_id=pid, domain=domain_name)
|
||
|
|
|
||
|
|
# only search
|
||
|
|
else:
|
||
|
|
if hasattr(get, "search_and") and hasattr(get, "search"):
|
||
|
|
return public.fail_v2(
|
||
|
|
public.lang("search_and and search can not be used at the same time")
|
||
|
|
)
|
||
|
|
|
||
|
|
obj = DnsDomainRecord.objects.filter(
|
||
|
|
Q(provider_id=pid, domain=domain_name) & (
|
||
|
|
Q(record__like=get.search) | Q(record_value__like=get.search)
|
||
|
|
)
|
||
|
|
)
|
||
|
|
|
||
|
|
try:
|
||
|
|
total = obj.count()
|
||
|
|
result = obj.limit(limit).offset((page - 1) * limit).as_list()
|
||
|
|
except Exception as e:
|
||
|
|
raise HintException(e)
|
||
|
|
|
||
|
|
data = [
|
||
|
|
self._process_key(x, key=["api_user", "record"]) for x in result
|
||
|
|
]
|
||
|
|
data.sort(key=lambda x: (x["record_type"], x["record"]))
|
||
|
|
return public.success_v2({"data": data, "total": total})
|
||
|
|
|
||
|
|
def create_dns_record(self, get):
|
||
|
|
try:
|
||
|
|
get.validate([
|
||
|
|
Param("pid").Integer().Require(),
|
||
|
|
Param("domain").String().Require(),
|
||
|
|
Param("record").String().Require(),
|
||
|
|
Param("record_type").String().Require(),
|
||
|
|
Param("record_value").String().Require(),
|
||
|
|
Param("priority").Integer().Require(),
|
||
|
|
Param("ttl").Integer().Require(),
|
||
|
|
Param("proxy").Integer(),
|
||
|
|
Param("ps").String(),
|
||
|
|
], [
|
||
|
|
public.validate.trim_filter(),
|
||
|
|
])
|
||
|
|
except Exception as ex:
|
||
|
|
public.print_log("error info: {}".format(ex))
|
||
|
|
return public.return_message(-1, 0, str(ex))
|
||
|
|
|
||
|
|
provider = DnsDomainProvider.objects.filter(id=int(get.pid)).first()
|
||
|
|
body = {
|
||
|
|
"provider_id": provider.id,
|
||
|
|
"provider_name": provider.name,
|
||
|
|
"api_user": provider.api_user,
|
||
|
|
"domain": get.domain,
|
||
|
|
"record": get.record,
|
||
|
|
"record_type": get.record_type,
|
||
|
|
"record_value": get.record_value,
|
||
|
|
"ttl": int(get.ttl),
|
||
|
|
"proxy": int(get.proxy),
|
||
|
|
"priority": int(get.priority),
|
||
|
|
}
|
||
|
|
if hasattr(get, "ps"):
|
||
|
|
body["ps"] = get.ps
|
||
|
|
response = provider.model_create_dns_record(body)
|
||
|
|
if not response.get("status"):
|
||
|
|
return public.fail_v2(response.get("msg"))
|
||
|
|
return public.success_v2(public.lang("Save Successfully!"))
|
||
|
|
|
||
|
|
def delete_dns_record(self, get):
|
||
|
|
try:
|
||
|
|
get.validate([
|
||
|
|
Param("id").Integer().Require(),
|
||
|
|
], [
|
||
|
|
public.validate.trim_filter(),
|
||
|
|
])
|
||
|
|
except Exception as ex:
|
||
|
|
public.print_log("error info: {}".format(ex))
|
||
|
|
return public.return_message(-1, 0, str(ex))
|
||
|
|
record = DnsDomainRecord.objects.find_one(id=int(get.id))
|
||
|
|
if not record:
|
||
|
|
return public.fail_v2(public.lang("DNS record Not Found!"))
|
||
|
|
provider = DnsDomainProvider.objects.find_one(id=record.provider_id)
|
||
|
|
if not provider:
|
||
|
|
return public.fail_v2(public.lang("DNS Provider Not Found!"))
|
||
|
|
response = provider.model_delete_dns_record(int(get.id))
|
||
|
|
if not response.get("status"):
|
||
|
|
return public.fail_v2(response.get("msg"))
|
||
|
|
return public.success_v2(public.lang("Delete Successfully!"))
|
||
|
|
|
||
|
|
def edit_dns_record(self, get):
|
||
|
|
try:
|
||
|
|
get.validate([
|
||
|
|
Param("id").Integer().Require(),
|
||
|
|
Param("pid").Integer().Require(),
|
||
|
|
Param("domain").String().Require(),
|
||
|
|
Param("record").String().Require(),
|
||
|
|
Param("record_type").String().Require(),
|
||
|
|
Param("record_value").String().Require(),
|
||
|
|
Param("ttl").Integer().Require(),
|
||
|
|
Param("proxy").Integer().Require(),
|
||
|
|
# -1 is not MX record
|
||
|
|
Param("priority").Integer("between", [-1, 65535]).Require(),
|
||
|
|
Param("ps").String(),
|
||
|
|
], [
|
||
|
|
public.validate.trim_filter(),
|
||
|
|
])
|
||
|
|
except Exception as ex:
|
||
|
|
public.print_log("error info: {}".format(ex))
|
||
|
|
return public.return_message(-1, 0, str(ex))
|
||
|
|
pid = int(get.pid)
|
||
|
|
record_id = int(get.id)
|
||
|
|
ps = get.ps if hasattr(get, "ps") else ""
|
||
|
|
# check if change
|
||
|
|
real_change = False
|
||
|
|
provider = DnsDomainProvider.objects.find_one(id=pid)
|
||
|
|
if not provider:
|
||
|
|
return public.fail_v2(public.lang("DNS Provider Not Found!"))
|
||
|
|
target = DnsDomainRecord.objects.find_one(id=record_id)
|
||
|
|
if not target:
|
||
|
|
return public.fail_v2(public.lang("DNS Record Not Found!"))
|
||
|
|
new_body = {
|
||
|
|
"provider_id": provider.id,
|
||
|
|
"provider_name": provider.name,
|
||
|
|
"api_user": provider.api_user,
|
||
|
|
"domain": get.domain,
|
||
|
|
"record": get.record,
|
||
|
|
"record_type": get.record_type,
|
||
|
|
"record_value": get.record_value,
|
||
|
|
"ttl": int(get.ttl),
|
||
|
|
"proxy": int(get.proxy),
|
||
|
|
"priority": int(get.priority),
|
||
|
|
"ps": ps,
|
||
|
|
}
|
||
|
|
if any([
|
||
|
|
target.record != get.record,
|
||
|
|
target.record_type != get.record_type,
|
||
|
|
target.record_value != get.record_value,
|
||
|
|
target.ttl != int(get.ttl),
|
||
|
|
target.proxy != int(get.proxy),
|
||
|
|
target.priority != int(get.priority),
|
||
|
|
]):
|
||
|
|
real_change = True
|
||
|
|
if not real_change:
|
||
|
|
DnsDomainRecord.objects.filter(id=record_id).update(new_body)
|
||
|
|
return public.success_v2(public.lang("Update Successfully!"))
|
||
|
|
# real change
|
||
|
|
try:
|
||
|
|
update = provider.model_edit_dns_record(record_id, new_body)
|
||
|
|
if update.get("status"):
|
||
|
|
return public.success_v2(public.lang("Update Successfully!"))
|
||
|
|
else:
|
||
|
|
return public.fail_v2(update.get("msg", "Update Failed..."))
|
||
|
|
except Exception as ex:
|
||
|
|
return public.fail_v2(str(ex))
|
||
|
|
|
||
|
|
# ========== 域名管理概况 SSL =======
|
||
|
|
def list_domain_details(self, get):
|
||
|
|
try:
|
||
|
|
get.validate([
|
||
|
|
Param("p").Integer(),
|
||
|
|
Param("limit").Integer(),
|
||
|
|
Param("id").Integer().Require(),
|
||
|
|
Param("domain").String(),
|
||
|
|
], [
|
||
|
|
public.validate.trim_filter(),
|
||
|
|
])
|
||
|
|
except Exception as ex:
|
||
|
|
public.print_log("error info: {}".format(ex))
|
||
|
|
return public.return_message(-1, 0, str(ex))
|
||
|
|
|
||
|
|
page = int(getattr(get, "p", 1))
|
||
|
|
limit = int(getattr(get, "limit", 100))
|
||
|
|
provider = DnsDomainProvider.objects.find_one(id=get.id)
|
||
|
|
if not provider:
|
||
|
|
return public.fail_v2(public.lang("DNS Provider Not Found!"))
|
||
|
|
|
||
|
|
data = self._add_ssl_info(provider.as_dict())
|
||
|
|
data = data.get("domains", [])
|
||
|
|
if hasattr(get, "domain"): # filter domain
|
||
|
|
data = [x for x in data if get.domain in x.get("name", "")]
|
||
|
|
|
||
|
|
bulitin = True if provider.name == "YakPanelDns" else False
|
||
|
|
for d in data:
|
||
|
|
d["records"] = DnsDomainRecord.objects.filter(
|
||
|
|
domain=d.get("name", ""), provider_id=int(get.id)
|
||
|
|
).count()
|
||
|
|
if bulitin:
|
||
|
|
from ssl_dnsV2.model import DnsResolve
|
||
|
|
resolve = DnsResolve.objects.filter(domain=d.get("name", "")).fields(
|
||
|
|
"ns_resolve", "a_resolve", "tips"
|
||
|
|
).first()
|
||
|
|
if not resolve:
|
||
|
|
d["dns_resolve"] = {"ns_resolve": 0, "a_resolve": 0, "tips": "Not Found Msg"}
|
||
|
|
else:
|
||
|
|
d["dns_resolve"] = resolve.as_dict()
|
||
|
|
|
||
|
|
total = len(data)
|
||
|
|
start = (page - 1) * limit
|
||
|
|
end = start + limit
|
||
|
|
return public.success_v2({"data": data[start:end], "total": total})
|
||
|
|
|
||
|
|
# =========== SSL ============
|
||
|
|
def _get_alarm_status(self) -> bool:
|
||
|
|
# 校验alarm
|
||
|
|
task_path = f"{public.get_panel_path()}/data/mod_push_data/task.json"
|
||
|
|
if not os.path.exists(task_path):
|
||
|
|
return False
|
||
|
|
task = public.readFile(task_path)
|
||
|
|
if not task:
|
||
|
|
return False
|
||
|
|
try:
|
||
|
|
task_info = json.loads(task)
|
||
|
|
except:
|
||
|
|
task_info = []
|
||
|
|
for t in task_info:
|
||
|
|
if t.get("task_data", {}).get("type") == "ssl":
|
||
|
|
return True
|
||
|
|
return False
|
||
|
|
|
||
|
|
def _get_verify(self, ssl: DnsDomainSSL) -> str:
|
||
|
|
if not ssl.auth_info:
|
||
|
|
return "dns01_manual"
|
||
|
|
auth_type = ssl.auth_info.get("auth_type", "")
|
||
|
|
auth_to = ssl.auth_info.get("auth_to", "").rstrip("/").replace("//", "/")
|
||
|
|
if auth_type == "http":
|
||
|
|
return "http01"
|
||
|
|
elif auth_type == "dns":
|
||
|
|
try:
|
||
|
|
if not "|" in auth_to:
|
||
|
|
return "dns01_manual"
|
||
|
|
info = auth_to.split("|")
|
||
|
|
if len(info) == 3 and info[0]:
|
||
|
|
return "dns01"
|
||
|
|
else:
|
||
|
|
return "dns01_manual"
|
||
|
|
except:
|
||
|
|
return "dns01_manual"
|
||
|
|
|
||
|
|
elif DomainValid.is_ip(ssl.dns):
|
||
|
|
if ssl.info.get("issuer_O") == "Let's Encrypt":
|
||
|
|
return "http01"
|
||
|
|
|
||
|
|
elif auth_to == "" or any("*" in d for d in ssl.dns):
|
||
|
|
return "dns01_manual"
|
||
|
|
|
||
|
|
return ""
|
||
|
|
|
||
|
|
def list_ssl_info(self, get):
|
||
|
|
"""
|
||
|
|
证书列表
|
||
|
|
"""
|
||
|
|
try:
|
||
|
|
get.validate([
|
||
|
|
Param("p").Integer(),
|
||
|
|
Param("limit").String(),
|
||
|
|
Param("is_order").Integer(),
|
||
|
|
Param("search").String(),
|
||
|
|
], [
|
||
|
|
public.validate.trim_filter(),
|
||
|
|
])
|
||
|
|
get.is_order = 0 if not hasattr(get, "is_order") else int(get.is_order)
|
||
|
|
if not hasattr(get, "search"):
|
||
|
|
get.search = ""
|
||
|
|
except Exception as ex:
|
||
|
|
public.print_log("error info: {}".format(ex))
|
||
|
|
return public.return_message(-1, 0, str(ex))
|
||
|
|
|
||
|
|
alarm_status = self._get_alarm_status()
|
||
|
|
page = int(getattr(get, "p", 1))
|
||
|
|
limit = int(getattr(get, "limit", 100))
|
||
|
|
ssl_obj = DnsDomainSSL.objects.filter(
|
||
|
|
Q(is_order=int(get.is_order)) & (Q(dns__like=get.search) | Q(subject__like=get.search))
|
||
|
|
).order_by("-create_time")
|
||
|
|
total = ssl_obj.count()
|
||
|
|
ssl_obj.limit(limit).offset((page - 1) * limit)
|
||
|
|
data = [
|
||
|
|
self._add_task_info(
|
||
|
|
data={
|
||
|
|
"hash": ssl.hash,
|
||
|
|
"provider": ssl.info.get("issuer_O", "unknown"),
|
||
|
|
"issuer": ssl.info.get("issuer", "unknown"),
|
||
|
|
"verify_domains": ssl.dns,
|
||
|
|
"end_time": self._end_time(ssl.not_after),
|
||
|
|
"end_date": ssl.not_after,
|
||
|
|
"auto_renew": ssl.auto_renew,
|
||
|
|
"last_apply_time": ssl.info.get("notBefore", ""),
|
||
|
|
"cert": {
|
||
|
|
"csr": public.readFile(ssl.path + "/fullchain.pem"), # 证书
|
||
|
|
"key": public.readFile(ssl.path + "/privkey.pem"), # 密钥
|
||
|
|
},
|
||
|
|
"log": ssl.log if ssl.log else ssl.get_ssl_log(),
|
||
|
|
"user_for": ssl.user_for,
|
||
|
|
"alarm": ssl.alarm if alarm_status else 0,
|
||
|
|
"verify": self._get_verify(ssl)
|
||
|
|
},
|
||
|
|
) for ssl in ssl_obj
|
||
|
|
]
|
||
|
|
return public.success_v2({"data": data, "total": total})
|
||
|
|
|
||
|
|
def download_cert(self, get):
|
||
|
|
"""
|
||
|
|
下载
|
||
|
|
"""
|
||
|
|
try:
|
||
|
|
get.validate([
|
||
|
|
Param("hash").String().Require(),
|
||
|
|
], [
|
||
|
|
public.validate.trim_filter(),
|
||
|
|
])
|
||
|
|
except Exception as ex:
|
||
|
|
public.print_log("error info: {}".format(ex))
|
||
|
|
return public.return_message(-1, 0, str(ex))
|
||
|
|
file_path = "/www/server/panel/vhost" + f"/ssl_saved/{get.hash}"
|
||
|
|
if not os.path.exists(file_path):
|
||
|
|
return public.fail_v2(public.lang("SSL Certificate Not Found!"))
|
||
|
|
|
||
|
|
CertHandler.make_last_info(file_path, force=True)
|
||
|
|
|
||
|
|
download_path = os.path.join(self.vhost, "ssl_saved/download")
|
||
|
|
os.makedirs(download_path, exist_ok=True)
|
||
|
|
output_path = os.path.join(download_path, f"{get.hash}")
|
||
|
|
if os.path.exists(f"{output_path}.zip"):
|
||
|
|
public.ExecShell(f"rm -f {output_path}.zip")
|
||
|
|
try:
|
||
|
|
shutil.make_archive(output_path, "zip", file_path)
|
||
|
|
except Exception as e:
|
||
|
|
return public.fail_v2(f"error: {str(e)}")
|
||
|
|
return public.success_v2(f"{output_path}.zip")
|
||
|
|
|
||
|
|
def one_cilck_renew(self, get):
|
||
|
|
"""
|
||
|
|
一键全量续签
|
||
|
|
"""
|
||
|
|
from .service import make_suer_renew_task
|
||
|
|
make_suer_renew_task() # 确保任务存在
|
||
|
|
echo = public.md5(public.md5("domain_ssl_renew_lets_ssl_bt"))
|
||
|
|
task = public.S("crontab").where("echo=?", echo).find()
|
||
|
|
if not task:
|
||
|
|
raise HintException("Cron Domian Renew task not found, please try again later!")
|
||
|
|
execstr = f"{public.GetConfigValue("setup_path")}/cron/{echo}"
|
||
|
|
public.ExecShell(f"chmod +x {execstr}")
|
||
|
|
public.ExecShell(f"nohup {execstr} start >> {execstr}.log 2>&1 &")
|
||
|
|
return public.success_v2({"id": task["id"]})
|
||
|
|
|
||
|
|
def renew_cert_process(self, get):
|
||
|
|
"""
|
||
|
|
续签, 带进度, 带部署
|
||
|
|
"""
|
||
|
|
try:
|
||
|
|
get.validate([
|
||
|
|
Param("hash").String().Require(),
|
||
|
|
], [
|
||
|
|
public.validate.trim_filter()
|
||
|
|
])
|
||
|
|
except Exception as ex:
|
||
|
|
public.print_log("error info: {}".format(ex))
|
||
|
|
return public.return_message(-1, 0, str(ex))
|
||
|
|
invalid_msg = public.lang(
|
||
|
|
"This Certificate auth info is invalid, cannot renew! Please Apply for a New Certificate."
|
||
|
|
"It will be renewed for you automatically in the future"
|
||
|
|
)
|
||
|
|
ssl_obj = DnsDomainSSL.objects.filter(hash=get.hash).first()
|
||
|
|
if not ssl_obj:
|
||
|
|
raise HintException(public.lang("SSL Certificate Not Found!"))
|
||
|
|
is_ip_ssl = DomainValid.is_ip(ssl_obj.dns)
|
||
|
|
if not ssl_obj.auth_info and not is_ip_ssl:
|
||
|
|
ssl_obj.renew_status = 0
|
||
|
|
ssl_obj.save()
|
||
|
|
raise HintException(invalid_msg)
|
||
|
|
|
||
|
|
day = 3 if is_ip_ssl else 30
|
||
|
|
ts_month = day * 24 * 60 * 60 * 1000
|
||
|
|
months = int(time.time() * 1000) + ts_month
|
||
|
|
debug = public.readFile("/www/server/panel/data/debug.pl") or "False"
|
||
|
|
if debug.lower() == "false" and ssl_obj.not_after_ts > months:
|
||
|
|
raise HintException(public.lang(f"SSL Certificate is less than {day} days, no need to renew!"))
|
||
|
|
|
||
|
|
auth_type = ssl_obj.auth_info.get("auth_type")
|
||
|
|
auth_to = ssl_obj.auth_info.get("auth_to", "").rstrip("/").replace("//", "/")
|
||
|
|
args = public.dict_obj()
|
||
|
|
args.domains = json.dumps(ssl_obj.dns)
|
||
|
|
|
||
|
|
if auth_type == "http" or (auth_type == "dns" and auth_to == "dns"):
|
||
|
|
if auth_to: # 有 site
|
||
|
|
# 明确的http方式, 或 手动认证申请的dns, 尝试http
|
||
|
|
args.auth_type = "http"
|
||
|
|
args.deploy = 1
|
||
|
|
args.site_id = public.M("sites").where("path=?", (auth_to,)).getField("id") or "-1"
|
||
|
|
return self.apply_new_ssl(args)
|
||
|
|
else: # todo 新场景, 无 site 续签 ip ssl, 暂时仅支持面板ssl
|
||
|
|
if not is_ip_ssl or not ssl_obj.panel_uf == ["panel"]:
|
||
|
|
raise HintException("only panel ip ssl support http01 renew now!")
|
||
|
|
if len(ssl_obj.dns) > 1:
|
||
|
|
raise HintException("only single ip ssl support http01 renew now!")
|
||
|
|
from .service import init_panel_http, generate_panel_task
|
||
|
|
task_obj = generate_panel_task({"domain": ssl_obj.dns[0]})
|
||
|
|
http_task = threading.Thread(
|
||
|
|
target=init_panel_http, args=(ssl_obj.dns[0], task_obj, True)
|
||
|
|
)
|
||
|
|
http_task.start()
|
||
|
|
return public.success_v2({
|
||
|
|
"result": public.lang("Apply Successfully! please wait for a moment"),
|
||
|
|
"task_id": task_obj.id,
|
||
|
|
"path": task_obj.task_log,
|
||
|
|
})
|
||
|
|
|
||
|
|
elif auth_type == "dns":
|
||
|
|
args.auth_type = "dns"
|
||
|
|
return self.apply_new_ssl(args)
|
||
|
|
|
||
|
|
else:
|
||
|
|
raise HintException(invalid_msg)
|
||
|
|
|
||
|
|
def manual_apply_vaild(self, get):
|
||
|
|
"""
|
||
|
|
手动申请验证
|
||
|
|
"""
|
||
|
|
try:
|
||
|
|
get.validate([
|
||
|
|
Param("site_id").Integer().Require(),
|
||
|
|
Param("domains").String().Require(),
|
||
|
|
], [
|
||
|
|
public.validate.trim_filter(),
|
||
|
|
])
|
||
|
|
get.site_id = str(get.site_id)
|
||
|
|
domains = [x.strip() for x in list(set(get.domains.split(",")))]
|
||
|
|
domains.sort()
|
||
|
|
except Exception as ex:
|
||
|
|
public.print_log("error info: {}".format(ex))
|
||
|
|
return public.return_message(-1, 0, str(ex))
|
||
|
|
manual_apply: Dict[str, str] = json.loads(
|
||
|
|
public.readFile(self.manual_apply)
|
||
|
|
)
|
||
|
|
domians_index = CertHandler.original_md5(domains)
|
||
|
|
if domians_index not in manual_apply.keys():
|
||
|
|
return public.fail_v2(public.lang("Order Not Found!"))
|
||
|
|
new_get = public.dict_obj()
|
||
|
|
new_get.index = manual_apply[domians_index]
|
||
|
|
vaild = acme_v2().validate_domain(new_get)
|
||
|
|
|
||
|
|
if vaild.get("save_path"):
|
||
|
|
ssl_hash = CertHandler.get_hash(vaild.get("cert", "") + vaild.get("root", ""))
|
||
|
|
ssl_obj = DnsDomainSSL.objects.filter(hash=ssl_hash).first()
|
||
|
|
site_name = public.M("sites").where("id=?", get.site_id).getField("name")
|
||
|
|
if site_name and ssl_obj:
|
||
|
|
ssl_obj.deploy_sites([site_name])
|
||
|
|
if domians_index in manual_apply.keys():
|
||
|
|
del manual_apply[domians_index]
|
||
|
|
public.writeFile(self.manual_apply, json.dumps(manual_apply))
|
||
|
|
return public.success_v2(public.lang("Apply Successfully!"))
|
||
|
|
|
||
|
|
if vaild.get("status") is True:
|
||
|
|
if domians_index in manual_apply.keys():
|
||
|
|
del manual_apply[domians_index]
|
||
|
|
public.writeFile(self.manual_apply, json.dumps(manual_apply))
|
||
|
|
return public.success_v2(public.lang("Apply Successfully!"))
|
||
|
|
try:
|
||
|
|
return public.fail_v2((str(vaild.get("msg", "Vaild failed! please try again"))))
|
||
|
|
except:
|
||
|
|
return public.fail_v2("Vaild failed! please try again")
|
||
|
|
|
||
|
|
def manual_apply_check(self, get):
|
||
|
|
"""
|
||
|
|
手动申请验证兜底
|
||
|
|
"""
|
||
|
|
try:
|
||
|
|
get.validate([
|
||
|
|
Param("site_id").Integer().Require(),
|
||
|
|
Param("domains").Array().Filter(json.loads)
|
||
|
|
], [
|
||
|
|
public.validate.trim_filter(),
|
||
|
|
])
|
||
|
|
domains = [x.strip() for x in list(set(get.domains))]
|
||
|
|
domains.sort()
|
||
|
|
target = CertHandler.original_md5(domains)
|
||
|
|
except Exception as ex:
|
||
|
|
public.print_log("error info: {}".format(ex))
|
||
|
|
return public.return_message(-1, 0, str(ex))
|
||
|
|
manual_apply: Dict[str, str] = json.loads(
|
||
|
|
public.readFile(self.manual_apply)
|
||
|
|
)
|
||
|
|
target_detail = {}
|
||
|
|
for k in list(manual_apply.keys()):
|
||
|
|
new_get = public.dict_obj()
|
||
|
|
new_get.index = manual_apply[k]
|
||
|
|
detail = acme_v2().get_order_detail(new_get) # loop for clean expires
|
||
|
|
if target == k:
|
||
|
|
target_detail = detail.get("message")
|
||
|
|
return public.success_v2(target_detail)
|
||
|
|
|
||
|
|
def apply_new_ssl(self, get):
|
||
|
|
"""
|
||
|
|
申请证书
|
||
|
|
get.domains = '["www.a.com","a.com"]'
|
||
|
|
get.auth_type = dns/http/dns_manual
|
||
|
|
get.auto_wildcard = 0/1 # 自动组合泛域名
|
||
|
|
get.deploy = -1/0/ 是否需要重新部署ssl
|
||
|
|
get.site_id = 站点id, 匹配具体的站点
|
||
|
|
|
||
|
|
:return:
|
||
|
|
1. dns 自动验证 2. http 文件验证
|
||
|
|
{
|
||
|
|
"result": success_msg,
|
||
|
|
"task_id": task.id,
|
||
|
|
"path": http_task.task_log,
|
||
|
|
}
|
||
|
|
3. dns_manual 手动验证
|
||
|
|
返回订单详情 dict , 或者免认证期间 直接下发ssl成功消息 str
|
||
|
|
"""
|
||
|
|
try:
|
||
|
|
get.validate([
|
||
|
|
Param("domains").String().Require(),
|
||
|
|
Param("auth_type").String().Require(),
|
||
|
|
Param("auto_wildcard").Integer(),
|
||
|
|
Param("deploy").Integer(),
|
||
|
|
Param("site_id").Integer(),
|
||
|
|
], [
|
||
|
|
public.validate.trim_filter(),
|
||
|
|
])
|
||
|
|
get.domains = json.loads(get.domains)
|
||
|
|
if get.auth_type not in ["dns", "http", "dns_manual"]:
|
||
|
|
return public.fail_v2(public.lang("auth_type must be 'dns', 'http' or 'dns_manual'"))
|
||
|
|
if len(get.domains) == 0:
|
||
|
|
return public.fail_v2(public.lang("domains is empty"))
|
||
|
|
get.deploy = getattr(get, "deploy", -1)
|
||
|
|
get.site_id = int(getattr(get, "site_id", -1))
|
||
|
|
auto_wildcard = False if int(getattr(get, "auto_wildcard", 0)) == 0 else True
|
||
|
|
except Exception as ex:
|
||
|
|
public.print_log("error info: {}".format(ex))
|
||
|
|
return public.return_message(-1, 0, str(ex))
|
||
|
|
|
||
|
|
success_msg = "Apply Successfully! please wait for a moment"
|
||
|
|
deploy_flag = self.deploy_map.get(int(get.deploy))
|
||
|
|
apply_domains = [x.strip() for x in list(set(get.domains))]
|
||
|
|
apply_domains.sort()
|
||
|
|
|
||
|
|
if len(apply_domains) == 0:
|
||
|
|
return public.fail_v2(public.lang("Domains Error: domains is empty"))
|
||
|
|
|
||
|
|
from .model import apply_cert
|
||
|
|
from .service import generate_sites_task
|
||
|
|
if get.auth_type == "dns": # auto dns verify
|
||
|
|
provider = None
|
||
|
|
for d in apply_domains:
|
||
|
|
temp_root, _, _ = extract_zone(d)
|
||
|
|
provider = DnsDomainProvider.objects.filter(
|
||
|
|
domains__contains=temp_root
|
||
|
|
).first()
|
||
|
|
if not provider:
|
||
|
|
return public.fail_v2(public.lang(
|
||
|
|
f"Dns01 Verify Errot, DNS Provider Not Found! for {temp_root}"
|
||
|
|
))
|
||
|
|
dns_task = generate_sites_task({}, get.site_id)
|
||
|
|
threading.Thread(target=provider.model_apply_cert,
|
||
|
|
kwargs=({
|
||
|
|
"domains": apply_domains,
|
||
|
|
"auto_wildcard": auto_wildcard,
|
||
|
|
"deploy": deploy_flag,
|
||
|
|
"task_obj": dns_task,
|
||
|
|
})).start()
|
||
|
|
return public.success_v2({
|
||
|
|
"result": public.lang(success_msg),
|
||
|
|
"task_id": dns_task.id,
|
||
|
|
"path": dns_task.task_log,
|
||
|
|
})
|
||
|
|
|
||
|
|
elif get.auth_type == "http": # file verify
|
||
|
|
from .service import find_site_with_domain
|
||
|
|
site = find_site_with_domain(get.domains[0])
|
||
|
|
if not site and not DomainValid.is_ip(get.domains):
|
||
|
|
return public.fail_v2(public.lang("Http01 Verify Error, but Site Not Found."))
|
||
|
|
if site.get("status") != "1":
|
||
|
|
return public.fail_v2(public.lang("Http01 Verify Error, but Site is Not Running"))
|
||
|
|
if any("*." in i for i in get.domains):
|
||
|
|
return public.fail_v2(public.lang(
|
||
|
|
"Http01 Verify Error: The wildcard domain name can only be verified by DNS."
|
||
|
|
))
|
||
|
|
http_task = generate_sites_task({}, get.site_id)
|
||
|
|
threading.Thread(target=apply_cert,
|
||
|
|
kwargs=({
|
||
|
|
"domains": apply_domains,
|
||
|
|
"auth_to": site.get("path"),
|
||
|
|
"auth_type": "http",
|
||
|
|
"task_obj": http_task,
|
||
|
|
"deploy": deploy_flag,
|
||
|
|
})).start()
|
||
|
|
return public.success_v2({
|
||
|
|
"result": public.lang(success_msg),
|
||
|
|
"task_id": http_task.id,
|
||
|
|
"path": http_task.task_log,
|
||
|
|
})
|
||
|
|
|
||
|
|
elif get.auth_type == "dns_manual": # manual dns verify
|
||
|
|
manual_apply: Dict[str, str] = json.loads(
|
||
|
|
public.readFile(self.manual_apply)
|
||
|
|
)
|
||
|
|
domian_index = CertHandler.original_md5(apply_domains)
|
||
|
|
if domian_index in manual_apply.keys():
|
||
|
|
new_get = public.dict_obj()
|
||
|
|
new_get.index = manual_apply[domian_index]
|
||
|
|
return acme_v2().get_order_detail(new_get)
|
||
|
|
|
||
|
|
apply_res = acme_v2().apply_cert_domain(
|
||
|
|
domains=apply_domains,
|
||
|
|
auth_to="dns",
|
||
|
|
auth_type="dns",
|
||
|
|
auto_wildcard=auto_wildcard,
|
||
|
|
)
|
||
|
|
if apply_res.get("save_path"): # 免认证期间将跳过验证, 直接下发ssl, 进行部署覆盖
|
||
|
|
ssl_hash = CertHandler.get_hash(apply_res.get("cert", "") + apply_res.get("root", ""))
|
||
|
|
ssl_obj = DnsDomainSSL.objects.filter(hash=ssl_hash).first()
|
||
|
|
site_name = public.M("sites").where("id=?", get.site_id).getField("name")
|
||
|
|
deploy = {}
|
||
|
|
if site_name and ssl_obj:
|
||
|
|
deploy = ssl_obj.deploy_sites([site_name])
|
||
|
|
if domian_index in manual_apply.keys():
|
||
|
|
del manual_apply[domian_index]
|
||
|
|
public.writeFile(self.manual_apply, json.dumps(manual_apply))
|
||
|
|
success_msg = {"result": apply_res.get("msg", "Apply Successfully!")}
|
||
|
|
if deploy.get("status"):
|
||
|
|
success_msg.update({"deploy": deploy.get("status")})
|
||
|
|
return public.success_v2(public.lang(success_msg))
|
||
|
|
|
||
|
|
if apply_res.get("index"): # 新申请, 返回订单详情
|
||
|
|
manual_apply.update({domian_index: apply_res.get("index")})
|
||
|
|
public.writeFile(self.manual_apply, json.dumps(manual_apply))
|
||
|
|
new_get = public.dict_obj()
|
||
|
|
new_get.index = apply_res.get("index")
|
||
|
|
return acme_v2().get_order_detail(new_get)
|
||
|
|
|
||
|
|
if apply_res.get("status") is False:
|
||
|
|
return public.fail_v2(apply_res.get("msg", "Apply Failed!"))
|
||
|
|
return public.success_v2(public.lang("Apply Successfully!"))
|
||
|
|
|
||
|
|
return public.fail_v2(public.lang("Unknown Auth Type"))
|
||
|
|
|
||
|
|
def upload_cert(self, get):
|
||
|
|
"""
|
||
|
|
上传证书
|
||
|
|
"""
|
||
|
|
try:
|
||
|
|
get.validate([
|
||
|
|
Param("key").String().Require(),
|
||
|
|
Param("cert").String().Require(),
|
||
|
|
], [
|
||
|
|
public.validate.trim_filter(),
|
||
|
|
])
|
||
|
|
except Exception as ex:
|
||
|
|
public.print_log("error info: {}".format(ex))
|
||
|
|
return public.return_message(-1, 0, str(ex))
|
||
|
|
try:
|
||
|
|
from ssl_domainModelV2.service import CertHandler
|
||
|
|
data = CertHandler().save_by_data(get.cert, get.key)
|
||
|
|
if not data:
|
||
|
|
return public.fail_v2(public.lang("update cert failed"))
|
||
|
|
return public.success_v2(data)
|
||
|
|
except Exception as ex:
|
||
|
|
return public.fail_v2(str(ex))
|
||
|
|
|
||
|
|
def remove_cert(self, get):
|
||
|
|
"""
|
||
|
|
删除证书
|
||
|
|
"""
|
||
|
|
try:
|
||
|
|
get.validate([
|
||
|
|
Param("hash").String().Require(),
|
||
|
|
], [
|
||
|
|
public.validate.trim_filter(),
|
||
|
|
])
|
||
|
|
except Exception as ex:
|
||
|
|
public.print_log("error info: {}".format(ex))
|
||
|
|
return public.return_message(-1, 0, str(ex))
|
||
|
|
ssl_obj = DnsDomainSSL.objects.find_one(hash=get.hash)
|
||
|
|
if not ssl_obj:
|
||
|
|
return public.fail_v2(public.lang("SSL Certificate Not Found!"))
|
||
|
|
# vhost/ssl为site ssl证书存放目录
|
||
|
|
cert_name = ssl_obj.subject.replace("*.", "")
|
||
|
|
vpath = os.path.join(self.vhost, "ssl", cert_name)
|
||
|
|
if os.path.exists(vpath):
|
||
|
|
public.ExecShell("rm -rf " + vpath)
|
||
|
|
|
||
|
|
try:
|
||
|
|
ssl_obj.delete()
|
||
|
|
except Exception as ex:
|
||
|
|
public.print_log("error info: {}".format(ex))
|
||
|
|
return public.fail_v2(str(ex))
|
||
|
|
|
||
|
|
return public.success_v2(public.lang("Remove Successfully!"))
|
||
|
|
|
||
|
|
def switch_auto_renew(self, get):
|
||
|
|
"""
|
||
|
|
自动续签开关
|
||
|
|
"""
|
||
|
|
try:
|
||
|
|
get.validate([
|
||
|
|
Param("hash").String().Require(),
|
||
|
|
], [
|
||
|
|
public.validate.trim_filter(),
|
||
|
|
])
|
||
|
|
except Exception as ex:
|
||
|
|
public.print_log("error info: {}".format(ex))
|
||
|
|
return public.return_message(-1, 0, str(ex))
|
||
|
|
|
||
|
|
ssl_obj = DnsDomainSSL.objects.find_one(hash=get.hash)
|
||
|
|
if not ssl_obj:
|
||
|
|
return public.fail_v2(public.lang("SSL Not Found!"))
|
||
|
|
# make_suer_renew_task() # make suer corn task
|
||
|
|
open_map = {0: 1, 1: 0}
|
||
|
|
ssl_obj.auto_renew = open_map.get(ssl_obj.auto_renew, 1)
|
||
|
|
ssl_obj.save()
|
||
|
|
return public.success_v2(public.lang("Setting Successfully!"))
|
||
|
|
|
||
|
|
def switch_ssl_alarm(self, get):
|
||
|
|
"""
|
||
|
|
证书告警开关
|
||
|
|
"""
|
||
|
|
try:
|
||
|
|
get.validate([
|
||
|
|
Param("hash").String().Require(),
|
||
|
|
Param("alarm").Integer().Require(),
|
||
|
|
], [
|
||
|
|
public.validate.trim_filter(),
|
||
|
|
])
|
||
|
|
except Exception as ex:
|
||
|
|
public.print_log("error info: {}".format(ex))
|
||
|
|
return public.return_message(-1, 0, str(ex))
|
||
|
|
ssl_obj = DnsDomainSSL.objects.find_one(hash=get.hash)
|
||
|
|
if not ssl_obj:
|
||
|
|
return public.fail_v2(public.lang("SSL Not Found!"))
|
||
|
|
|
||
|
|
alarm = int(get.alarm)
|
||
|
|
if alarm == 1: # open
|
||
|
|
from .service import make_suer_alarm_task
|
||
|
|
make_suer_alarm_task() # make suer alarm task
|
||
|
|
ssl_obj.alarm = alarm
|
||
|
|
ssl_obj.save()
|
||
|
|
else: # close
|
||
|
|
ssl_obj.alarm = alarm
|
||
|
|
ssl_obj.save()
|
||
|
|
return public.success_v2(public.lang("Setting Successfully!"))
|
||
|
|
|
||
|
|
# =========== Deploy ================
|
||
|
|
@staticmethod
|
||
|
|
def __add_match_flag(targes: list, dns_obj: DnsDomainSSL, key_word: str = None, match_domain: str = None) -> list:
|
||
|
|
if key_word and match_domain: # 不能同时存在
|
||
|
|
return targes
|
||
|
|
|
||
|
|
for t in targes:
|
||
|
|
temp_name = t.get(key_word, "") if key_word else match_domain
|
||
|
|
if DomainValid.match_ssl_dns(temp_name, dns_obj):
|
||
|
|
t["match"] = 1
|
||
|
|
return targes
|
||
|
|
|
||
|
|
def cert_domain_list(self, get):
|
||
|
|
"""
|
||
|
|
证书管理列表
|
||
|
|
"""
|
||
|
|
try:
|
||
|
|
get.validate([
|
||
|
|
Param("hash").String().Require(),
|
||
|
|
], [
|
||
|
|
public.validate.trim_filter(),
|
||
|
|
])
|
||
|
|
except Exception as ex:
|
||
|
|
public.print_log("error info: {}".format(ex))
|
||
|
|
return public.return_message(-1, 0, str(ex))
|
||
|
|
|
||
|
|
ssl_obj = DnsDomainSSL.objects.find_one(hash=get.hash)
|
||
|
|
if not ssl_obj:
|
||
|
|
return public.fail_v2(public.lang("SSL Certificate Not Found!"))
|
||
|
|
# ====================== sites ======================
|
||
|
|
# 不考虑status
|
||
|
|
sites = public.S("sites").field(
|
||
|
|
"id", "name", "path", "status", "type_id", "project_type",
|
||
|
|
).select()
|
||
|
|
for s in sites:
|
||
|
|
domains = public.S("domain").where("pid=?", (s["id"],)).field("name").select()
|
||
|
|
if not domains:
|
||
|
|
continue
|
||
|
|
if all([
|
||
|
|
DomainValid.match_ssl_dns(d.get("name", ""), ssl_obj) for d in domains if d.get("name")
|
||
|
|
]):
|
||
|
|
s["match"] = 1
|
||
|
|
# ====================== mails ======================
|
||
|
|
mails = []
|
||
|
|
if os.path.exists(self.mail_db_file):
|
||
|
|
# 不考虑active
|
||
|
|
mails = public.S("domain", self.mail_db_file).field(
|
||
|
|
"domain", "a_record", "active"
|
||
|
|
).select()
|
||
|
|
mails = self.__add_match_flag(mails, ssl_obj, "domain")
|
||
|
|
|
||
|
|
# ====================== panel ======================
|
||
|
|
panel = []
|
||
|
|
panel_ssl = config().GetPanelSSL(get=None)
|
||
|
|
if panel_ssl.get("status") == 0:
|
||
|
|
panel = [panel_ssl.get("message")]
|
||
|
|
|
||
|
|
current_domain = ""
|
||
|
|
if os.path.exists(PANEL_LIMIT_DOMAIN):
|
||
|
|
limit_domain = public.readFile(PANEL_LIMIT_DOMAIN)
|
||
|
|
if limit_domain: # 如果有限制域名
|
||
|
|
current_domain = limit_domain
|
||
|
|
else:
|
||
|
|
if os.path.exists(PANEL_DOMAIN): # 如果有配置过的域名
|
||
|
|
current_domain = public.readFile(PANEL_DOMAIN)
|
||
|
|
|
||
|
|
if current_domain and panel:
|
||
|
|
panel = self.__add_match_flag(panel, ssl_obj, None, current_domain)
|
||
|
|
|
||
|
|
data = {
|
||
|
|
"sites": sites,
|
||
|
|
"mails": mails,
|
||
|
|
"panel": panel,
|
||
|
|
"accounts": [],
|
||
|
|
}
|
||
|
|
return public.success_v2(data)
|
||
|
|
|
||
|
|
@staticmethod
|
||
|
|
def __before_deploy(get: public.dict_obj) -> Tuple[DnsDomainSSL, public.dict_obj]:
|
||
|
|
"""
|
||
|
|
证书部署前通用检查
|
||
|
|
:return: ssl obj, get obj
|
||
|
|
"""
|
||
|
|
try:
|
||
|
|
get.validate([
|
||
|
|
Param("hash").String().Require(),
|
||
|
|
Param("domains").String(),
|
||
|
|
Param("append").Integer(),
|
||
|
|
Param("recover").Integer(),
|
||
|
|
], [
|
||
|
|
public.validate.trim_filter(),
|
||
|
|
])
|
||
|
|
except Exception as ex:
|
||
|
|
raise ex
|
||
|
|
|
||
|
|
if hasattr(get, "domains"):
|
||
|
|
try:
|
||
|
|
get.domains = json.loads(get.domains)
|
||
|
|
except json.decoder.JSONDecodeError as js_err:
|
||
|
|
raise HintException("json decode error: {}".format(str(js_err)))
|
||
|
|
|
||
|
|
for k in ["recover", "append"]:
|
||
|
|
if hasattr(get, k):
|
||
|
|
try:
|
||
|
|
setattr(get, k, int(getattr(get, k)))
|
||
|
|
except TypeError as tr:
|
||
|
|
raise tr
|
||
|
|
|
||
|
|
ssl_obj = DnsDomainSSL.objects.find_one(hash=get.hash)
|
||
|
|
if not ssl_obj:
|
||
|
|
raise Exception("SSL Certificate Not Found!")
|
||
|
|
|
||
|
|
return ssl_obj, get
|
||
|
|
|
||
|
|
def cert_deploy_sites(self, get):
|
||
|
|
"""
|
||
|
|
证书部署到 选定的 sites
|
||
|
|
get.hash
|
||
|
|
get.domain (site name)
|
||
|
|
get.append=1 追加模式, 其余为替换模式
|
||
|
|
"""
|
||
|
|
try:
|
||
|
|
# 返回ssl对象, get对象
|
||
|
|
ssl_obj, get = self.__before_deploy(get)
|
||
|
|
except Exception as ex:
|
||
|
|
public.print_log("error info: {}".format(ex))
|
||
|
|
return public.fail_v2(str(ex))
|
||
|
|
# 网站入口单独部署
|
||
|
|
if hasattr(get, "append") and get.append == 1:
|
||
|
|
result = ssl_obj.deploy_sites(site_names=get.domains)
|
||
|
|
else: # 域名管理批量处理
|
||
|
|
# 不同的元素
|
||
|
|
diff = list(set(ssl_obj.sites_uf).symmetric_difference(set(get.domains)))
|
||
|
|
# 1, 需要移除的sites
|
||
|
|
for remove in [x for x in diff if x in set(ssl_obj.sites_uf)]:
|
||
|
|
new_get = public.dict_obj()
|
||
|
|
new_get.siteName = remove
|
||
|
|
new_get.updateOf = 1
|
||
|
|
new_get.reload = 0
|
||
|
|
try:
|
||
|
|
# close site's ssl conf
|
||
|
|
remove_res = panelSite().CloseSSLConf(new_get)
|
||
|
|
except Exception as e:
|
||
|
|
public.print_log(f"remove site ssl error info: {str(e)}")
|
||
|
|
continue
|
||
|
|
# 2, 移除diff之后, 部署
|
||
|
|
result = ssl_obj.deploy_sites(site_names=get.domains, replace=True)
|
||
|
|
return public.return_message(0 if result.get("status") else -1, 0, public.lang(result.get("msg")))
|
||
|
|
|
||
|
|
def cert_deploy_mails(self, get):
|
||
|
|
"""
|
||
|
|
证书部署到 选定的 mails
|
||
|
|
只涉及替换全部
|
||
|
|
"""
|
||
|
|
try:
|
||
|
|
ssl_obj, get = self.__before_deploy(get)
|
||
|
|
except Exception as ex:
|
||
|
|
public.print_log("error info: {}".format(ex))
|
||
|
|
return public.fail_v2(str(ex))
|
||
|
|
|
||
|
|
# 不同的元素
|
||
|
|
diff = list(set(ssl_obj.mails_uf).symmetric_difference(set(get.domains)))
|
||
|
|
# 1, 需要移除的mails
|
||
|
|
try:
|
||
|
|
import sys
|
||
|
|
if os.path.exists("/www/server/panel/plugin/mail_sys"):
|
||
|
|
sys.path.insert(1, "/www/server/panel/plugin/mail_sys")
|
||
|
|
from plugin.mail_sys.mail_sys_main import mail_sys_main
|
||
|
|
|
||
|
|
for remove in [x for x in diff if x in set(ssl_obj.mails_uf)]:
|
||
|
|
args = public.dict_obj()
|
||
|
|
args.csr = ""
|
||
|
|
args.key = ""
|
||
|
|
args.domain = remove
|
||
|
|
args.act = "delete"
|
||
|
|
try:
|
||
|
|
mail_sys_main().set_mail_certificate_multiple(args)
|
||
|
|
except:
|
||
|
|
continue
|
||
|
|
except Exception as err:
|
||
|
|
public.print_log("remove mail ssl error info: {}".format(str(err)))
|
||
|
|
|
||
|
|
# 2, 移除diff之后, 部署
|
||
|
|
result = ssl_obj.deploy_mails(get.domains)
|
||
|
|
if result.get("status"):
|
||
|
|
return public.success_v2(public.lang("Deploy Mail's SSL Successfully!"))
|
||
|
|
|
||
|
|
return public.fail_v2(public.lang(result.get("msg", "Deploy Faild...")))
|
||
|
|
|
||
|
|
def cert_deploy_accounts(self, get):
|
||
|
|
"""
|
||
|
|
证书部署到 选定的 accounts
|
||
|
|
"""
|
||
|
|
try:
|
||
|
|
ssl_obj, get = self.__before_deploy(get)
|
||
|
|
except Exception as ex:
|
||
|
|
public.print_log("error info: {}".format(ex))
|
||
|
|
return public.fail_v2(str(ex))
|
||
|
|
|
||
|
|
return public.success_v2(public.lang("Deploy Account's SSL Successfully!"))
|
||
|
|
|
||
|
|
def cert_deploy_panel(self, get):
|
||
|
|
"""
|
||
|
|
指定部署到 panel 主面板, or 恢复自签证书
|
||
|
|
"""
|
||
|
|
try:
|
||
|
|
get.validate([
|
||
|
|
Param("hash").String().Require(),
|
||
|
|
Param("recover").Integer().Require(),
|
||
|
|
], [
|
||
|
|
public.validate.trim_filter(),
|
||
|
|
])
|
||
|
|
except Exception as ex:
|
||
|
|
public.print_log("error info: {}".format(ex))
|
||
|
|
return public.fail_v2(str(ex))
|
||
|
|
|
||
|
|
ssl_obj, get = self.__before_deploy(get)
|
||
|
|
res = ssl_obj.deploy_panel(recover=int(get.recover))
|
||
|
|
if res.get("status"):
|
||
|
|
return public.success_v2(public.lang("Deploy Panel's SSL Successfully!"))
|
||
|
|
return public.fail_v2(public.lang(res.get("msg", "Deploy Panel's SSL Failed!")))
|
||
|
|
|
||
|
|
# =========== Site ===========
|
||
|
|
def get_sites(self, get):
|
||
|
|
"""
|
||
|
|
获取所有网站
|
||
|
|
"""
|
||
|
|
return public.success_v2([
|
||
|
|
x for x in public.S("sites").field("id", "name", "project_type").select()
|
||
|
|
])
|
||
|
|
|
||
|
|
def check_domain_automatic(self, get):
|
||
|
|
"""
|
||
|
|
dns自动化的检测服务
|
||
|
|
涵盖site, mail, panel, account等
|
||
|
|
:return: {
|
||
|
|
"hash": "", 适用证书
|
||
|
|
"domain": "", 检查的域名
|
||
|
|
"support": [
|
||
|
|
"auto", 自动解析
|
||
|
|
"ssl_cert",自动部署
|
||
|
|
"cf_proxy", cf代理
|
||
|
|
],
|
||
|
|
}
|
||
|
|
"""
|
||
|
|
try:
|
||
|
|
get.validate([
|
||
|
|
Param("domain").String().Require(),
|
||
|
|
], [
|
||
|
|
public.validate.trim_filter(),
|
||
|
|
])
|
||
|
|
except Exception as ex:
|
||
|
|
public.print_log("error info: {}".format(ex))
|
||
|
|
return public.return_message(-1, 0, str(ex))
|
||
|
|
result = {
|
||
|
|
"hash": "",
|
||
|
|
"domain": get.domain,
|
||
|
|
}
|
||
|
|
support = list()
|
||
|
|
root, _, _ = extract_zone(get.domain)
|
||
|
|
if DnsDomainProvider.objects.filter(domains__contains=root).first():
|
||
|
|
support.append("auto") # 自动解析功能
|
||
|
|
support.append("ssl_cert") # 自动部署证书
|
||
|
|
|
||
|
|
exist = None
|
||
|
|
for ssl in DomainValid.get_best_ssl(get.domain):
|
||
|
|
if DomainValid.match_ssl_dns(get.domain, ssl, False):
|
||
|
|
exist = ssl
|
||
|
|
break
|
||
|
|
|
||
|
|
if exist is not None:
|
||
|
|
result["hash"] = exist.hash
|
||
|
|
if bool("CloudFlareDns" in exist.auth_info.get("auth_to", "")):
|
||
|
|
# ip为内网地址不可以开启代理
|
||
|
|
try:
|
||
|
|
local_ip = public.GetLocalIp()
|
||
|
|
provate = ipaddress.IPv4Address(local_ip).is_private
|
||
|
|
except Exception:
|
||
|
|
provate = True
|
||
|
|
if not provate:
|
||
|
|
# 是否支持 CF 代理
|
||
|
|
support.append("cf_proxy")
|
||
|
|
result["support"] = support
|
||
|
|
return public.success_v2(result)
|
||
|
|
|
||
|
|
def ssl_tasks_status(self, get):
|
||
|
|
"""
|
||
|
|
获取任务状态
|
||
|
|
"""
|
||
|
|
try:
|
||
|
|
get.validate([
|
||
|
|
Param("task_type").Integer(),
|
||
|
|
Param("task_id").Integer(),
|
||
|
|
], [
|
||
|
|
public.validate.trim_filter(),
|
||
|
|
])
|
||
|
|
except Exception as ex:
|
||
|
|
public.print_log("error info: {}".format(ex))
|
||
|
|
return public.return_message(-1, 0, str(ex))
|
||
|
|
|
||
|
|
if not hasattr(get, "task_id"):
|
||
|
|
task_type = WorkFor.sites if not hasattr(get, "task_type") else int(get.task_type)
|
||
|
|
data = DnsDomainTask.objects.filter(
|
||
|
|
task_type=task_type, task_status__lt=100,
|
||
|
|
).as_list()
|
||
|
|
else:
|
||
|
|
objs = DnsDomainTask.objects.filter(id=get.task_id).first()
|
||
|
|
data = objs.as_dict() if objs else "Task Not Found!"
|
||
|
|
return public.success_v2(data)
|
||
|
|
|
||
|
|
# ========== Panel ===========
|
||
|
|
def get_panel_domain(self, get=None):
|
||
|
|
if not os.path.exists(PANEL_DOMAIN):
|
||
|
|
# 填充已经存在的限制domain
|
||
|
|
if os.path.exists(PANEL_LIMIT_DOMAIN):
|
||
|
|
limit_domain = public.readFile(PANEL_LIMIT_DOMAIN)
|
||
|
|
if limit_domain:
|
||
|
|
public.writeFile(PANEL_DOMAIN, limit_domain, "w")
|
||
|
|
else:
|
||
|
|
public.writeFile(PANEL_DOMAIN, "", "w")
|
||
|
|
return public.success_v2({"domain": limit_domain})
|
||
|
|
|
||
|
|
else:
|
||
|
|
public.writeFile(PANEL_DOMAIN, "", "w")
|
||
|
|
return public.success_v2({"domain": ""})
|
||
|
|
|
||
|
|
return public.success_v2({"domain": public.readFile(PANEL_DOMAIN)})
|
||
|
|
|
||
|
|
def set_panel_domain_ssl(self, get):
|
||
|
|
"""
|
||
|
|
get.domain = {
|
||
|
|
"hash": "xxx",
|
||
|
|
"domain": "example.com",
|
||
|
|
"support": ["auto" (自动解析), "ssl_cert" (自动部署), "cf_proxy" (开启cf代理)],
|
||
|
|
"auth_type": "http" (http验证) or "dns" (dns验证),
|
||
|
|
}
|
||
|
|
"""
|
||
|
|
try:
|
||
|
|
get.validate([
|
||
|
|
Param("domain").String().Require(),
|
||
|
|
], [
|
||
|
|
public.validate.trim_filter(),
|
||
|
|
])
|
||
|
|
get.domain = json.loads(get.domain)
|
||
|
|
except Exception as ex:
|
||
|
|
public.print_log("error info: {}".format(ex))
|
||
|
|
return public.return_message(-1, 0, str(ex))
|
||
|
|
auth_type = get.domain.get("auth_type")
|
||
|
|
domain = get.domain.get("domain", "").strip()
|
||
|
|
if "*." in domain:
|
||
|
|
return public.fail_v2(public.lang("Wildcard domain is not supported!"))
|
||
|
|
|
||
|
|
if DomainValid.is_ip(domain) and DomainValid.is_private_ip(domain):
|
||
|
|
# 如果是ip, 且不是公网
|
||
|
|
return public.fail_v2(public.lang("Private IP address is not supported!"))
|
||
|
|
|
||
|
|
if not DomainValid.is_valid_domain(domain) and not (DomainValid.is_ip(domain)):
|
||
|
|
# 既不是合法域名, 也不是ip
|
||
|
|
return public.fail_v2(public.lang("Domain or IP addr Not Valid!"))
|
||
|
|
|
||
|
|
get.domain["domain"] = domain
|
||
|
|
if not domain:
|
||
|
|
return public.fail_v2(public.lang("domain is empty"))
|
||
|
|
if auth_type not in ["http", "dns"]:
|
||
|
|
return public.fail_v2(public.lang("auth_type must be 'http' or 'dns'"))
|
||
|
|
|
||
|
|
# real chage
|
||
|
|
from .service import generate_panel_task
|
||
|
|
task_obj = generate_panel_task(get.domain)
|
||
|
|
if auth_type == "dns":
|
||
|
|
support = get.domain.get("support")
|
||
|
|
if "cf_proxy" in support:
|
||
|
|
support.remove("cf_proxy")
|
||
|
|
get.domain["support"] = support
|
||
|
|
from .service import init_panel_dns
|
||
|
|
dns_task = threading.Thread(
|
||
|
|
target=init_panel_dns, args=(get.domain, task_obj)
|
||
|
|
)
|
||
|
|
dns_task.start()
|
||
|
|
else:
|
||
|
|
from .service import init_panel_http
|
||
|
|
http_task = threading.Thread(
|
||
|
|
target=init_panel_http, args=(domain, task_obj)
|
||
|
|
)
|
||
|
|
http_task.start()
|
||
|
|
return public.success_v2(
|
||
|
|
{
|
||
|
|
"result": "Set Panel's SSL Successfully! Please wait a few times.",
|
||
|
|
"task_id": task_obj.id
|
||
|
|
}
|
||
|
|
)
|
||
|
|
|
||
|
|
# ========== Mail check ===========
|
||
|
|
def mail_record_check(self, get):
|
||
|
|
"""
|
||
|
|
邮箱域名解析检查
|
||
|
|
get.domain = "example.com"
|
||
|
|
"""
|
||
|
|
try:
|
||
|
|
get.validate([
|
||
|
|
Param("domain").String().Require(),
|
||
|
|
], [
|
||
|
|
public.validate.trim_filter(),
|
||
|
|
])
|
||
|
|
except Exception as ex:
|
||
|
|
public.print_log("error info: {}".format(ex))
|
||
|
|
return public.return_message(-1, 0, str(ex))
|
||
|
|
domain = get.domain.strip()
|
||
|
|
if not DomainValid.is_valid_domain(domain):
|
||
|
|
return public.fail_v2(public.lang("Domain Not Valid!"))
|
||
|
|
domain_info = {
|
||
|
|
'hash': '',
|
||
|
|
'domain': domain
|
||
|
|
}
|
||
|
|
from .service import get_mail_record
|
||
|
|
records = get_mail_record(domain_info)
|
||
|
|
if isinstance(records, str):
|
||
|
|
return public.fail_v2(public.lang(records))
|
||
|
|
return public.success_v2(records)
|
||
|
|
|
||
|
|
|
||
|
|
def move_old_account():
|
||
|
|
old_dns_api = os.path.join(public.get_panel_path(), "config/dns_api.json")
|
||
|
|
read = public.readFile(old_dns_api)
|
||
|
|
once = os.path.join(public.get_panel_path(), "config/dns_api_once.pl")
|
||
|
|
if not os.path.exists(once):
|
||
|
|
public.writeFile(once, "0")
|
||
|
|
|
||
|
|
if public.readFile(once) != "0":
|
||
|
|
return
|
||
|
|
try:
|
||
|
|
dnsapi_config = json.loads(read) if read else []
|
||
|
|
except Exception:
|
||
|
|
return
|
||
|
|
for dns in dnsapi_config:
|
||
|
|
try:
|
||
|
|
if dns.get("name") == "CloudFlareDns":
|
||
|
|
dns_name = "CloudFlareDns"
|
||
|
|
key = ""
|
||
|
|
account = ""
|
||
|
|
for cf in dns.get("data", []):
|
||
|
|
if cf.get("key") == "SAVED_CF_MAIL" and cf.get("value") != "":
|
||
|
|
account = cf.get("value", "")
|
||
|
|
elif cf.get("key") == "SAVED_CF_KEY" and cf.get("value") != "":
|
||
|
|
key = cf.get("value")
|
||
|
|
if dns_name and key:
|
||
|
|
try:
|
||
|
|
exists = DnsDomainProvider.objects.filter(
|
||
|
|
name=dns_name, api_user=account, api_key=key
|
||
|
|
).first()
|
||
|
|
if not exists:
|
||
|
|
if os.path.exists('/www/server/panel/data/cf_limit_api.pl'):
|
||
|
|
limit = True
|
||
|
|
else:
|
||
|
|
limit = False
|
||
|
|
get = public.dict_obj()
|
||
|
|
get.name = dns_name
|
||
|
|
get.api_user = account
|
||
|
|
get.api_key = key
|
||
|
|
get.permission = "limit" if limit is True else "global"
|
||
|
|
get.alias = "myCloudFlareDns"
|
||
|
|
DomainObject().create_dns_api(get)
|
||
|
|
except:
|
||
|
|
pass
|
||
|
|
elif dns.get("name") == "NameCheapDns":
|
||
|
|
dns_name = "NameCheapDns"
|
||
|
|
key = ""
|
||
|
|
account = ""
|
||
|
|
for nc in dns.get("data", []):
|
||
|
|
if nc.get("key") == "SAVED_NC_ACCOUNT" and nc.get("value") != "":
|
||
|
|
account = nc.get("value", "")
|
||
|
|
elif nc.get("key") == "SAVED_CX_APIKEY" and nc.get("value") != "":
|
||
|
|
key = nc.get("value")
|
||
|
|
if dns_name and key and account:
|
||
|
|
try:
|
||
|
|
exists = DnsDomainProvider.objects.filter(
|
||
|
|
name=dns_name, api_user=account, api_key=key
|
||
|
|
).first()
|
||
|
|
if not exists:
|
||
|
|
try:
|
||
|
|
get = public.dict_obj()
|
||
|
|
get.name = dns_name
|
||
|
|
get.api_user = account
|
||
|
|
get.api_key = key
|
||
|
|
get.alias = "myNameCheapDns"
|
||
|
|
DomainObject().create_dns_api(get)
|
||
|
|
except Exception:
|
||
|
|
import traceback
|
||
|
|
public.print_log(traceback.format_exc())
|
||
|
|
except:
|
||
|
|
pass
|
||
|
|
except Exception:
|
||
|
|
continue
|
||
|
|
public.writeFile(once, "1")
|
||
|
|
|
||
|
|
|
||
|
|
move_old_account()
|