# coding: utf-8
# +-------------------------------------------------------------------
# | yakpanel
# +-------------------------------------------------------------------
# | Copyright (c) 2015-2099 YakPanel(https://www.yakpanel.com) All rights reserved.
# +-------------------------------------------------------------------
# | Author: sww <hwl@yakpanel.com>
# +-------------------------------------------------------------------

import json
import os
import re
import time
import traceback
import public
import json
from flask import request
from public.validate import Param
from datetime import datetime

try:
    from YakPanel import cache
    import requests
except:
    pass



class crontab:
    field = 'id,name,type,where1,where_hour,where_minute,echo,addtime,status,save,backupTo,sName,sBody,sType,urladdress,save_local,notice,notice_channel,db_type,split_type,split_value,type_id,rname,keyword,post_param,flock,time_set,backup_mode,db_backup_path,time_type,special_time,log_cut_path,user_agent,version,table_list,result,second'
    # field = 'id,name,type,where1,where_hour,where_minute,echo,addtime,status,save,backupTo,sName,sBody,sType,urladdress,save_local,notice,notice_channel,flock,time_set,backup_mode,db_backup_path,time_type,special_time,log_cut_path,user_agent,version,table_list,result,second'
    def __init__(self):
        try:
            cront = public.M('crontab').order("id desc").field(self.field).select()
        except Exception as e:
            pass
            # try:
            #    public.check_database_field("crontab.db", "crontab")
            # except Exception as e:
            #     pass
        
        cront = public.M('crontab').order("id desc").field(self.field).select()
        if type(cront) == str:
            public.M('crontab').execute("ALTER TABLE 'crontab' ADD 'status' INTEGER DEFAULT 1", ())
            public.M('crontab').execute("ALTER TABLE 'crontab' ADD 'save' INTEGER DEFAULT 3", ())
            public.M('crontab').execute("ALTER TABLE 'crontab' ADD 'backupTo' TEXT DEFAULT off", ())
            public.M('crontab').execute("ALTER TABLE 'crontab' ADD 'sName' TEXT", ())
            public.M('crontab').execute("ALTER TABLE 'crontab' ADD 'sBody' TEXT", ())
            public.M('crontab').execute("ALTER TABLE 'crontab' ADD 'sType' TEXT", ())
            public.M('crontab').execute("ALTER TABLE 'crontab' ADD 'urladdress' TEXT", ())
            public.M('crontab').execute("ALTER TABLE 'crontab' ADD 'save_local' INTEGER DEFAULT 0", ())
            public.M('crontab').execute("ALTER TABLE 'crontab' ADD 'notice' INTEGER DEFAULT 0", ())
            public.M('crontab').execute("ALTER TABLE 'crontab' ADD 'notice_channel' TEXT DEFAULT ''", ())
            public.M('crontab').execute("ALTER TABLE 'crontab' ADD 'db_type' TEXT DEFAULT ''", ())
            public.M('crontab').execute("UPDATE 'crontab' SET 'db_type'='mysql' WHERE sType='database' and db_type=''", ())
            public.M('crontab').execute("ALTER TABLE 'crontab' ADD 'split_type' TEXT DEFAULT ''", ())
            public.M('crontab').execute("ALTER TABLE 'crontab' ADD 'split_value' INTEGER DEFAULT 0", ())
            public.M('crontab').execute("ALTER TABLE 'crontab' ADD 'rname' TEXT DEFAULT ''", ())
            public.M('crontab').execute("ALTER TABLE 'crontab' ADD 'type_id' INTEGER", ())
            public.M('crontab').execute("PRAGMA foreign_keys=on", ())
            public.M('crontab').execute("ALTER TABLE 'crontab' ADD CONSTRAINT 'fk_type_id' FOREIGN KEY ('type_id') REFERENCES 'crontab_types' ('id')", ())
            public.M('crontab').execute("ALTER TABLE 'crontab' ADD 'keyword' TEXT DEFAULT ''", ())
            public.M('crontab').execute("ALTER TABLE 'crontab' ADD 'post_param' TEXT DEFAULT ''", ())
            public.M('crontab').execute("ALTER TABLE 'crontab' ADD 'flock' INTEGER DEFAULT 0", ())
            public.M('crontab').execute("ALTER TABLE 'crontab' ADD 'time_set' TEXT DEFAULT ''", ())
            public.M('crontab').execute("ALTER TABLE 'crontab' ADD 'backup_mode' TEXT DEFAULT ''", ())
            public.M('crontab').execute("ALTER TABLE 'crontab' ADD 'db_backup_path' TEXT DEFAULT ''", ())
            public.M('crontab').execute("ALTER TABLE 'crontab' ADD 'time_type' TEXT DEFAULT ''", ())
            public.M('crontab').execute("ALTER TABLE 'crontab' ADD 'special_time' TEXT DEFAULT ''", ())
            public.M('crontab').execute("ALTER TABLE 'crontab' ADD 'log_cut_path' TEXT DEFAULT ''", ())
            public.M('crontab').execute("ALTER TABLE 'crontab' ADD 'user_agent' TEXT DEFAULT ''", ())
            public.M('crontab').execute("ALTER TABLE 'crontab' ADD 'version' TEXT DEFAULT ''", ())
            public.M('crontab').execute("ALTER TABLE 'crontab' ADD 'table_list' TEXT DEFAULT ''", ())
            public.M('crontab').execute("ALTER TABLE 'crontab' ADD 'result' INTEGER DEFAULT 1", ())
            public.M('crontab').execute("ALTER TABLE 'crontab' ADD 'second' TEXT DEFAULT ''", ())
            cront = public.M('crontab').order("id desc").field(self.field).select()

        public.check_table('crontab_types',
                           '''CREATE TABLE "crontab_types" (
                                            "id" INTEGER PRIMARY KEY AUTOINCREMENT,
                                            "name" VARCHAR DEFAULT '',
                                            "ps" VARCHAR DEFAULT '');''')

    def get_zone(self, get):
        try:
            try:
                import pytz
            except:
                import os
                os.system("btpip install pytz")
                import pytz
            areadict = {}
            for i in pytz.all_timezones:
                if i.find('/') != -1:
                    area, zone = i.split('/')[0], i.split('/')[1]
                    if area not in areadict:
                        areadict[area] = [zone]
                    areadict[area].append(zone)
            for k, v in areadict.items():
                if k == 'status': continue
                areadict[k] = sorted(list(set(v)))
            # 取具体时区
            # 取具体时区地区
            result = public.ExecShell('ls -l /etc/localtime')
            area = result[0].split('/')[-2].strip()
            zone = result[0].split('/')[-1].strip()
            # areadict['status'] = [area, zone]
            return public.return_message(0,0,areadict)
        except:
            return public.return_message(-1,0, public.lang('Failed to obtain time zone!'))


    # 获取所有domain
    def get_domain(self, get=None):
        try:
            domains = public.M('domain').field('name').select()
            domains = ['http://' + i['name'] for i in domains]
            return domains
        except:
            return traceback.format_exc()

    # 设置置顶
    def set_task_top(self, get=None):
        """
        设置任务置顶，不传参数查询设置的计划任务列表
        :param get: task_id
        :return:
        """
        cron_task_top_path = '/www/server/panel/data/cron_task_top'
        if os.path.exists(cron_task_top_path):
            task_top = json.loads(public.readFile(cron_task_top_path))
        else:
            task_top = {'list': []}
        if get and hasattr(get, 'task_id'):
            task_top['list'] = [i for i in task_top['list'] if i != get['task_id']]
            task_top['list'].append(get['task_id'])
            public.writeFile(cron_task_top_path, json.dumps(task_top))
            return public.returnMsg(True, public.lang('Set to top successfully!'))
        return task_top

    # 取消置顶
    def cancel_top(self, get):
        """
        取消任务置顶
        :param get:task_id
        :return:
        """
        cron_task_top_path = '/www/server/panel/data/cron_task_top'
        if os.path.exists(cron_task_top_path):
            task_top = json.loads(public.readFile(cron_task_top_path))
        else:
            return public.returnMsg(True, 'Cancel pinned successfully!')
        if hasattr(get, 'task_id'):
            task_top['list'].remove(get['task_id'])
            public.writeFile(cron_task_top_path, json.dumps(task_top))
            return public.returnMsg(True, 'Cancel pinned successfully!')
        else:
            return public.returnMsg(False, public.lang('Please enter the unpinned ID！'))


    # 取计划任务列表
    def GetCrontab(self, get):
        try:
            self.checkBackup()
            self.__clean_log()
            type_id = get.type_id if (hasattr(get, 'type_id') and get.type_id is not None) else ""
            db_obj = public.M('crontab')
            query = db_obj.order("id desc").field(self.field)
            # 根据类型筛选任务
            if type_id:
                query = self._filter_by_type_id(query,type_id)
            # 获取所有任务数据
            all_tasks = query.select()
            # 获取置顶任务列表
            top_list = self.set_task_top()['list']
            top_data, other_data = self._partition_tasks(all_tasks, top_list)
            top_data = self._sort_tasks(top_data,get)
            other_data = self._sort_tasks(other_data,get)

            # 重新组织任务顺序
            data = top_data + other_data

            # 搜索过滤
            if hasattr(get, 'search') and get.search:
                data = self.search_tasks(data, get.search)
            
            # 应用分页
            paged_data, page_data = self._paginate(data, get)       
            # 格式化任务数据
            self._format_task(paged_data, top_list)        
            result = self._construct_result(db_obj, page_data, paged_data)
            if result:
                __CLOUD_TITLE = {
                "qiniu": "Qiniu Cloud Storage",
                "alioss": "Alibaba Cloud OSS",
                "ftp": "FTP Storage",
                "bos": "Baidu Cloud BOS",
                "obs": "Huawei Cloud OBS",
                "aws_s3": "AWS S3",
                "gdrive": "Google Drive",
                "msonedrive": "Microsoft OneDrive",
                "gcloud_storage": "Google Cloud Storage",
                "upyun": "Upyun Storage",
                "jdcloud": "JD Cloud Storage",
                "txcos": "Tencent Cloud COS",
                'tianyiyun': "Tianyi Cloud ZOS",
                'webdav':"WebDav",
                'minio':"MinIO Storage",
                'dogecloud':"Duoji Cloud COS",
                'localhost':'Local Disk'
                }
                for index in range(len(result)):
                    if 'backupTo' in result[index]:
                        try:
                            result[index]['backupTo'] = __CLOUD_TITLE[result[index]['backupTo']]
                        except:
                            result[index]['backupTo'] = result[index]['backupTo']
            return public.return_message(0,0,result)
    
        except Exception as e:
            return public.return_message(-1,0, public.lang('Query failed: ' + str(e)))


    def _filter_by_type_id(self,query,type_id):
        filters={
            '-1':('name like ?','%Do not delete%'),
            '0':('name not like ?','%Do not delete%'),
            '-2':('status=?',1),
            '-3':('status=?',0)
        }
        if type_id in filters:
            return query.where(*filters[type_id])
        return query.where('type_id=?',type_id)

    def _partition_tasks(self, all_tasks, top_list):
        # 使用 set 加速查找
        top_set = set(top_list)
        # 获取 top_data
        top_data = [task for task in all_tasks if str(task['id']) in top_set]
        # 按照 top_list 的顺序对 top_data 进行排序
        top_data = sorted(top_data, key=lambda x: top_list.index(str(x['id'])))
        # 获取 other_data
        other_data = [task for task in all_tasks if str(task['id']) not in top_set]
        return top_data, other_data

    def get_type_name(self, task):
        name = task.get('name', '')
        type_id = task.get('type_id', '')
        type_names = []
        if type_id == 0:
            if name and isinstance(name, str):
                if 'do not delete' in name.lower():
                    type_names.append('System Task')
                else:
                    type_names.append('Default')
        type_name = public.M('crontab_types').where("id=?", (type_id,)).getField('name')
        if type_name:
            type_names.append(type_name)

        return ', '.join(type_names)
    
    def _sort_tasks(self,tasks,get):
        order_param=getattr(get,'order_param',None)
        if order_param:
            sort_key,order=order_param.split(' ') 
            reverse_order=order=='desc'
            if "rname" in order_param:
                    for task in tasks:
                        if not task.get('rname'):
                            task['rname'] = task['name']  # 将没有值的 rname 设置为 name 的值
            if "addtime" in order_param:
                for task in tasks:
                    task['addtime']=self.get_addtime(task)
                    task['addtime_calculated'] = True
            return sorted(tasks,key=lambda x:x[sort_key],reverse=reverse_order)
        return tasks 
  

    def _paginate(self,data,get):

        total_count=len(data)
        p=int(get.p) if hasattr(get,'p')else None
        count=int(get.count) if hasattr(get,'count')else None
        if p and count:
            start=(p-1)*count
            end=start+count
            page_data=public.get_page(total_count,p,count)
            paged_data=data[start:end]
        else:
            page_data=None
            paged_data=data
        return paged_data,page_data

    def _format_task(self,paged_data,top_list):
        top_set=set(top_list)
        for task in paged_data:
            task['type_zh']=self._get_task_type_zh(task)
            task['cycle']=self.generate_cycle(task['type'],task['where1'],task['where_hour'],task['where_minute'],task['sType'],task['second'])
            if not task.get('addtime_calculated'):
                task['addtime'] = self.get_addtime(task)
            task['backup_mode']=1 if task['backup_mode']=="1" else 0
            db_backup_path = public.M('config').where("id=?", ('1',)).getField('backup_path')
            task['db_backup_path'] = task.get('db_backup_path') or db_backup_path
            task['rname'] = task.get('rname') or task['name']
            task['sort'] = 1 if str(task["id"]) in top_set else 0
            try:
                task['user'] = self.parse_user_from_sbody(task['sBody'])
            except:pass
            # 从sBody中移除sudo -u部分，只显示实际命令
            if 'sudo -u' in task['sBody']:
                task['sBody'] = task['sBody'].split("bash -c '", 1)[-1].rstrip("'")
            # 只显示实际命令
            if "<<'EOF'" in task['sBody']:
                task['sBody'] = task['sBody'].split("<<'EOF'", 1)[-1].strip().rstrip("EOF").strip()

            # 任务不存在 ，标记为停止
            if task['type'] == 'once':
                res = public.ExecShell(f"systemctl show {task['echo'][:-3] + '.timer'} -p LastTriggerUSecMonotonic --value")[0].strip()
                if res not in ['0','']:
                    task['status'] == 0
                    public.M('crontab').where('id = ?', (task['id'],)).update({'status': 0})
                task['cycle'] = public.lang("{} Execute once", task['where1'])

            self.get_mysql_increment_save(task)
            self.format_cycle(task)

            if not task['type_id']:
                task['type_id']=0
            task['type_name'] = self.get_type_name(task)  # 添加分类名称

    def _get_task_type_zh(self,task):
        if task['type'] == "day":                        
            return public.getMsg('CRONTAB_TODAY')
        elif task['type'] == "day-n":
            return public.getMsg('CRONTAB_N_TODAY', (str(task['where1']),))
        elif task['type'] == "hour":
            return public.getMsg('CRONTAB_HOUR')
        elif task['type'] == "hour-n":
            return public.getMsg('CRONTAB_N_HOUR', (str(task['where1']),))
        elif task['type'] == "minute-n":           
            if task['second']:
                task['type'] ="second-n"
                return public.getMsg('CRONTAB_N_SECOND', (str(task['where1']),))
            return public.getMsg('CRONTAB_N_MINUTE', (str(task['where1']),))
        elif task['type'] == "week":
            task['type_zh'] = public.getMsg('CRONTAB_WEEK')
            if not task['where1']: task['where1'] = '0'
            return task['type_zh']
        elif task['type'] == "month":
            return public.getMsg(public.lang('CRONTAB_MONTH'))
            
    def get_mysql_increment_save(self,task):
        if task['sType']=="mysql_increment_backup":
            save = public.M("mysql_increment_backup").where("cron_id=?", (task['id'],)).count()
            if save>=0:
                task['save']=save
            else:
                save=""    
    def get_addtime(self,task):
        log_file='/www/server/cron/{}.log'.format(task['echo'])
        if os.path.exists(log_file):
            return self.get_last_exec_time(log_file)
        else:
            return " "        
    def search_tasks(self, data, search_term):
        return [item for item in data if search_term in item['name'] or search_term in item['sName'] or search_term in item['addtime'] or search_term in item['echo']]

    def generate_cycle(self, type, where1, where_hour, where_minute,sType,second:None):
        try:
            if where1 and type != "week": 
               where1 = int(where1)
            cycle = ""
            # week_days = ["一", "二", "三", "四", "五", "六", "日"]
            week_days = {
                '1': public.lang('Monday'),
                '2': public.lang('Tuesday'),
                '3': public.lang('Wednesday'),
                '4': public.lang('Thursday'),
                '5': public.lang('Friday'),
                '6': public.lang('Saturday'),
                '7': public.lang('Sunday')
            }
            
            if type == "day":
                cycle = public.lang("Execute once a day for {}:{}", where_hour, where_minute)
            elif type == "day-n":
                cycle = public.lang("Execute once every {} days on {}:{}",where1, where_hour, where_minute)
            elif type == "hour":
                cycle = public.lang("Execute once every {} minute of the hour",where_minute)
            elif type == "hour-n":
                # start_time = "{:02d}:{:02d}".format(where_hour, where_minute)
                # cycle = "从每天从00:00开始，每隔{}小时执行一次，直到1天结束（例如：{}".format(where1, start_time)
                cycle = public.lang("Starting from 0:00 every day, execute every {} minute of {} hours",where_minute,where1)
            elif type == "minute-n":
                # start_time = "0，{}等分钟）".format(where_minute)
                # cycle = "每小时的第0分钟开始，每隔{}分钟执行一次，直到1小时结束（例如：{}".format(where_minute, start_time)
                cycle = public.lang("Starting from the 0th minute of each hour, execute every {} minutes",where1)
                # current_minute = where_minute
            elif type == "week":
                cycle = public.lang("Execute once every {} on {}:{}",week_days[where1], where_hour, where_minute)
            elif type == "month":
                cycle = public.lang("Execute once on the {}:{} of the {} day of each month",where_hour, where_minute,where1)
            elif type == "second-n":
                cycle = public.lang("Execute every {} seconds", second)
            if sType == "startup_services":
                cycle = public.lang("Start up and execute once")
            return cycle
        except:
            # print(traceback.format_exc())
            pass

    def parse_user_from_sbody(self,sBody):
        if isinstance(sBody, str):
            # 使用正则表达式提取 sudo -u 后面的用户名
            match = re.search(r'^sudo\s+-u\s+(\S+)', sBody)
            return match.group(1) if match else 'root'
        else:
            return 'root'

    def format_cycle(self,item):
        week_str = ''
        if item['time_type'] in ['sweek', 'sday', 'smonth']:
            item['type'] = 'sweek'  # 对于这三种情况，类型都统一处理为 'sweek'
            if item['time_type'] == 'sweek':
                week_str = self.toweek(item['time_set'])  # 假设 self.toweek() 方法存在且能正常工作
            cycle_prefix = "every day" if item['time_type'] == 'sday' else "monthly" + item['time_set'] + "day" if item['time_type'] == 'smonth' else "every" + week_str
            item['type_zh'] = item['special_time'] if item['time_type'] in ['sday', 'smonth'] else week_str
            item['cycle'] = cycle_prefix + item['special_time'] + " execute"
        elif item['sType'] == 'site_restart':
            item['cycle'] = "every day " + item['special_time'] + " execute"

    def toweek(self, days):
        week_days = {
        '1': 'Monday',
        '2': 'Tuesday',
        '3': 'Wednesday',
        '4': 'Thursday',
        '5': 'Friday',
        '6': 'Saturday',
        '7': 'Sunday'
        }
        day_list = str(days).split(',')
        for day in day_list:
            if day not in week_days:
                print('Invalid day:', day)
                return ''
        return ','.join(week_days[day] for day in day_list)
    def _construct_result(self, db_obj, page_data, paged_data):
        if page_data:
            result =  {'page': page_data, 'data': paged_data} 
            if db_obj.ERR_INFO:
                    result['error']=db_obj.ERR_INFO
        else:
            result =paged_data
            if db_obj.ERR_INFO:
                    return []
        return result  

    def get_backup_list(self, args):
        '''
            @name 获取指定备份任务的备份文件列表
            @author hwliang
            @param args<dict> 参数{
                cron_id<int> 任务ID 必填
                p<int> 页码 默认1
                rows<int> 每页显示条数 默认10
                callback<string> jsonp回调函数  默认为空
            }
            @return <dict>{
                page<str> 分页HTML
                data<list> 数据列表
            }
        '''

        p = args.get('p/d', 1)
        rows = args.get('rows/d', 10)
        tojs = args.get('tojs/s', '')
        callback = args.get('callback/s', '') if tojs else tojs

        cron_id = args.get('cron_id/d')
        
        # 首先检查计划任务的类型
        crontab = public.M('crontab').where('id=?', (cron_id,)).find()
        data = []
        if crontab:
            if "Incremental backup of database" in crontab['name']:
                data = self.get_backup_data('mysql_increment_backup', cron_id, p, rows, callback)
            elif crontab.get('sType') == 'site':
                data = self.get_backup_data_all_site(cron_id, p, rows, callback)
            else:
                data = self.get_backup_data('backup', cron_id, p, rows, callback)
        return public.return_message(0,0, data)

    def get_backup_data_all_site(self, cron_id: int, p: int, rows: int, callback: str) -> dict:
        sites_tables = ['backup', 'wordpress_backups']
        count = 0
        site_data = []
        for table in sites_tables:
            temp_count = public.M(table).where('cron_id=?', (cron_id,)).count()
            count += temp_count
            temp_data = public.M(table).where('cron_id=?', (cron_id,)).select()
            if table == 'wordpress_backups':
                temp_data = [
                    {
                        'id': x.get('id'),
                        'type': 0,
                        'name': x.get('bak_file', '').split('|')[-1].split('/')[-1],
                        'pid': x.get('s_id'),
                        'filename': x.get('bak_file', ''),
                        'size': x.get('size', 0),
                        'addtime': time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(x.get('bak_time'))),
                        'ps': 'No',
                        'cron_id': x.get('cron_id'),
                    } for x in temp_data if isinstance(x, dict)
                ]
            site_data.extend(temp_data)

        data = public.get_page(count, p, rows, callback)
        data['data'] = site_data[((p - 1) * rows):((p - 1) * rows + rows)]
        return data

    def get_backup_data(self, table, cron_id, p, rows, callback):
        count = public.M(table).where('cron_id=?', (cron_id,)).count()
        data = public.get_page(count, p, rows, callback)
        data['data'] = public.M(table).where('cron_id=?', (cron_id,)).limit(data['row'], data['shift']).select()
        if table=="mysql_increment_backup":
            # 更新filename字段
            if data['data']:
                cloud_storage_fields = [
                    'localhost', 'ftp', 'alioss', 'txcos', 'qiniu', 
                    'aws_s3', 'upyun', 'obs', 'bos', 'gcloud_storage', 
                    'gdrive', 'msonedrive', 'jdcloud',"tianyiyun","webdav","minio","dogecloud"
                ]
                for i in data['data']:
                    for field in cloud_storage_fields:
                        if i[field]:
                            i['filename'] = i[field]
                            break
        return data
    
    def get_last_exec_time(self, log_file):
        '''
            @name 获取上次执行时间
            @author hwliang
            @param log_file<string> 日志文件路径
            @return format_date
        '''
        exec_date = ''
        # try:
        #     log_body = public.GetNumLines(log_file, 20)
        #     if log_body:
        #         log_arr = log_body.split('\n')
        #         date_list = []
        #         for i in log_arr:
        #             if i.find('★') != -1 and i.find('[') != -1 and i.find(']') != -1:
        #                 date_list.append(i)
        #         if date_list:
        #             exec_date = date_list[-1].split(']')[0].split('[')[1]
        # except:
        #     pass

        # finally:
        if not exec_date:
            exec_date = public.format_date(times=int(os.path.getmtime(log_file)))
        return exec_date
    
    # 清理日志
    def __clean_log(self):
        try:
            log_file = '/www/server/cron'
            if not os.path.exists(log_file): return False
            for f in os.listdir(log_file):
                if f[-4:] != '.log': continue
                filename = log_file + '/' + f
                if os.path.getsize(filename) < 1048576 / 2: continue
                tmp = public.GetNumLines(filename, 100)
                public.writeFile(filename, tmp)
        except:
            pass

    # 转换大写星期
    def toWeek(self, num):
        wheres = {
                0   :   public.lang("Sunday"),
                1   :   public.lang("Monday"),
                2   :   public.lang("Tuesday"),
                3   :   public.lang("Wednesday"),
                4   :   public.lang("Thursday"),
                5   :   public.lang("Friday"),
                6   :   public.lang("Saturday")
        }
        try:
            return wheres[num]
        except:
            return ''

    # 检查环境
    def checkBackup(self):
        if cache.get('check_backup'): return None

        # 检查备份表是否正确
        if not public.M('sqlite_master').db('backup').where('type=? AND name=? AND sql LIKE ?',
                                               ('table', 'backup', '%cron_id%')).count():
            public.M('backup').execute("ALTER TABLE 'backup' ADD 'cron_id' INTEGER DEFAULT 0", ())

        # 检查备份脚本是否存在
        filePath = public.GetConfigValue('setup_path') + '/panel/script/backup'
        if not os.path.exists(filePath):
            public.downloadFile(public.GetConfigValue('home') + '/linux/backup.sh', filePath)
        # 检查日志切割脚本是否存在
        filePath = public.GetConfigValue('setup_path') + '/panel/script/logsBackup'
        if not os.path.exists(filePath):
            public.downloadFile(public.GetConfigValue('home') + '/linux/logsBackup.py', filePath)
        # 检查计划任务服务状态
        import system
        sm = system.system()
        if os.path.exists('/etc/init.d/crond'):
            if not public.process_exists('crond'): public.ExecShell('/etc/init.d/crond start')
        elif os.path.exists('/etc/init.d/cron'):
            if not public.process_exists('cron'): public.ExecShell('/etc/init.d/cron start')
        elif os.path.exists('/usr/lib/systemd/system/crond.service'):
            if not public.process_exists('crond'): public.ExecShell('systemctl start crond')
        cache.set('check_backup', True, 3600)

    # 设置计划任务状态
    def set_cron_status(self, get):
        id = get['id']
        cronInfo = public.M('crontab').where('id=?', (id,)).field(self.field).find()
        if not cronInfo:
            return public.return_message(-1,0, public.lang("No data was found for the corresponding scheduled task. Please refresh the page to check if the scheduled task exists!"))

        # 处理一次性定时任务
        if cronInfo['type'] == 'once':
            cron_time = datetime.strptime(cronInfo['where1'], "%Y-%m-%d %H:%M:%S")
            if cron_time <= datetime.now():
                return public.return_message(-1, 0, public.lang(f'The execution time has passed. Please reconfigure the task! '))

            if get.get('if_stop') == 'true':
                public.ExecShell(f'systemctl stop {cronInfo['echo'][:-3] + '.timer'}')
            else:
                public.ExecShell(f'systemctl start {cronInfo['echo'][:-3] + '.timer'}')

        status_msg = ['Stop', 'Start']
        status = 1
        if cronInfo['status'] == status:
            status = 0
            if not self.remove_for_crond(cronInfo['echo']):
                return public.return_message(-1,0, public.lang('Unable to write to file, please check if system hardening is enabled!'))
        else:
            cronInfo['status'] = 1
            if not self.sync_to_crond(cronInfo):
                return public.return_message(-1,0, public.lang('Unable to write to file, please check if system hardening is enabled!'))

        public.M('crontab').where('id=?', (id,)).setField('status', status)
        public.WriteLog(public.lang('crontab tasks'), public.lang('Modify the status of the scheduled task ['+cronInfo ['name']+'] to ['+status_msg[status]+']'))
        cronPath = '/www/server/cron'
        cronName = cronInfo['echo']
        if_stop = get.get('if_stop', '')
        if if_stop:
            self.stop_cron_task(cronPath, cronName, if_stop)
        return public.return_message(0,0, public.lang('Setup successfully!'))

    def set_cron_status_all(self, get):
        """
        批量设置计划任务状态
        :param get: type:stop, start, del, exec    id_list:[1,2,3]
        :return:
        """
        if not hasattr(get, 'type'):
            return public.return_message(-1,0, public.lang('parameter error'))
        if not hasattr(get, 'id_list'):
            return public.return_message(-1,0, public.lang('parameter error'))
        # 停止或开启
        if get.type not in ['stop', 'start', 'del', 'exec']:
            return public.return_message(-1,0, public.lang('parameter error'))
        if get.type == 'stop' or get.type == 'start':
            id_list = json.loads(get['id_list'])
            status = 1 if get.type == 'start' else 0
            status_msg = ['Stop', 'Start']
            data = []
            for id in id_list:
                try:
                    name = public.M('crontab').where('id=?', (id,)).field('name').find().get('name', '')
                    cronInfo = public.M('crontab').where('id=?', (id,)).field(self.field).find()

                    # 补充定时任务
                    if cronInfo['type'] == 'once':
                        cron_time = datetime.strptime(cronInfo['where1'], "%Y-%m-%d %H:%M:%S")
                        if cron_time <= datetime.now(): # 跳过
                            continue

                        if get.type == 'stop':
                            public.ExecShell(f'systemctl stop {cronInfo['echo'][:-3] + '.timer'}')
                        else:
                            public.ExecShell(f'systemctl start {cronInfo['echo'][:-3] + '.timer'}')

                    if not cronInfo:
                        data.append({id: public.lang('The scheduled task with this ID does not exist'), 'status': False})
                        continue
                    if status == 1:
                        if not self.sync_to_crond(cronInfo):
                            return public.return_message(-1,0, public.lang('Writing scheduled task failed, please check if the disk is writable or if system hardening is enabled!'))
                    else:
                        if not self.remove_for_crond(cronInfo['echo']):
                            return public.return_message(-1,0, public.lang('Writing scheduled task failed, please check if the disk is writable or if system hardening is enabled!'))
                    public.M('crontab').where('id=?', (id,)).setField('status', status)
                    cronPath = '/www/server/cron'
                    cronName = cronInfo['echo']
                    if_stop = get.if_stop
                    self.stop_cron_task(cronPath, cronName, if_stop)
                except:
                    data.append({name: public.lang("{} Setting failed",(status_msg[status])), 'status': False})
                else:
                    data.append({name: ("{} Setup successfully!".format(status_msg[status])), 'status': True})
            return public.return_message(0,0,data)
        # 删除
        if get.type == 'del':
            id_list = json.loads(get['id_list'])
            data = []
            for id in id_list:
                try:
                    name = public.M('crontab').where('id=?', (id,)).field('name').find().get('name', '')
                    if not name:
                        data.append({id: public.lang('The scheduled task with this ID does not exist'), 'status': False})
                        continue
                    get = public.to_dict_obj({'id': id})
                    res = self.DelCrontab(get)
                except:
                    pass
                data.append({name: public.lang("Delete {}",("succeeded" if res['status']==0 else "fail")), 'status': res['status']})
            return public.return_message(0,0,data)
        # 执行
        if get.type == 'exec':
            id_list = json.loads(get['id_list'])
            data = []
            for id in id_list:
                try:
                    name = public.M('crontab').where('id=?', (id,)).field('name').find().get('name', '')
                    if not name:
                        data.append({id: public.lang('The scheduled task with this ID does not exist'), 'status': False})
                        continue
                    get = public.to_dict_obj({'id': id})
                    res = self.StartTask(get)
                except:
                    pass
                data.append({name:public.lang( "Execution {}",("succeeded" if res['status']==0 else "fail")), 'status': res['status']})
            return public.return_message(0,0,data)


    # 修改计划任务
    def modify_crond(self, get):
        try:
            if re.search('<.*?>', get['name']):
                return public.return_message(-1,0, public.lang("The category name cannot contain HTML statements"))
            if get['sType'] == 'toShell':
                sBody = get['sBody']
                get['sBody'] = sBody.replace('\r\n', '\n')
                # 如果user有值，则修改sBody
                user = get.get('user', 'root')
                if user :
                    if get["sType"] != "toShell":
                        get['sBody'] = "sudo -u {0} bash -c '{1}'".format(user, get['sBody'])
                    else:
                        get['sBody'] = f"sudo -u {user} bash <<'EOF'\n{get['sBody']}\nEOF"

                if get.get('version',''):
                    version = get['version'].replace(".", "")
                    get['sBody'] = get['sBody'].replace("${1/./}", version)
            if len(get['name']) < 1:
                return public.return_message(-1,0, public.lang('CRONTAB_TASKNAME_EMPTY'))
            id = get['id']
            cronInfo = public.M('crontab').where('id=?', (id,)).field(self.field).find()

            try:
                # 处理定时任务
                if cronInfo['type'] == 'once' and get['type'] == 'once':
                    timer_path = '/etc/systemd/system/' + cronInfo['echo'][:-3] + '.timer'
                    if not os.path.exists(timer_path):
                        return public.return_message(-1,0, public.lang('The timer file does not exist. Try deleting this task and then recreate it!'))

                    try:
                        execute_datetime = datetime.strptime(get['where1'], "%Y-%m-%d %H:%M:%S")
                    except ValueError:
                        raise ValueError(public.lang(f'Execute time format error: {get['where1']}, required: YYYY-MM-DD HH:MM:SS'))

                    timer_content = f"""[Unit]
Description=Timer for once task: {cronInfo['echo'][:-3] + '.timer'}

[Timer]
OnCalendar={execute_datetime} 
Persistent=false

[Install]
WantedBy=timers.target
"""
                    public.writeFile(timer_path, timer_content)
                    # 重启配置
                    public.ExecShell('systemctl daemon-reload')
                    public.ExecShell(f'systemctl restart {cronInfo['echo'][:-3] + '.timer'}')
                    public.ExecShell(f'systemctl reset-failed {cronInfo['echo'][:-3] + '.timer'}')
            except Exception as e:
                return public.return_message(-1,0,str(e))


            if get['type']=='sweek':

            # if get['type']=='sweek':
                self.modify_values(cronInfo['echo'],get['time_type'],get['special_time'],get['time_set']) 
                get['type']='minute-n'

            if get['type']=="second-n":
                get['type']="minute-n"
                get['where1']= "1"
                get['hour']=1
                get['minute']=1
                get['flock']=0
            cuonConfig, get, name = self.GetCrondCycle(get)            

            projectlog = self.modify_project_log_split(cronInfo, get)
            if projectlog.modify():
                return public.return_message(0,0, projectlog.msg)
            if not get['where1']: get['where1'] = get['week']
            del (cronInfo['id'])
            del (cronInfo['addtime'])
            cronInfo['name'] = get['name']
            if cronInfo['sType'] == "sync_time": cronInfo['sName'] = get['sName']
            cronInfo['type'] = get['type']
            cronInfo['where1'] = get['where1']
            cronInfo['where_hour'] = get['hour']
            cronInfo['where_minute'] = get['minute']
            cronInfo['save'] = get['save']
            cronInfo['backupTo'] = get['backupTo']
            cronInfo['sBody'] = get['sBody']
            cronInfo['urladdress'] = get['urladdress']
            cronInfo['time_type']=get.get('time_type','')
            cronInfo['special_time']=get.get('special_time','')
            cronInfo['time_set']=get.get('time_set','')
            cronInfo['second']=get.get('second','')
            if get.get('db_backup_path')=="/www/backup":
                db_backup_path=""
            else:
                db_backup_path=get.get('db_backup_path','')
            columns = 'status,type,where1,where_hour,where_minute,save,backupTo,sName,sBody,urladdress,db_type,split_type,split_value,rname,post_param,flock,time_set,backup_mode,db_backup_path,time_type,special_time,user_agent,version,table_list,second'
            values = (get['type'], get['where1'], get['hour'],
                      get['minute'], get['save'], get['backupTo'], cronInfo['sName'], get['sBody']
                      , get['urladdress'], get.get("db_type"), get.get("split_type"), get.get("split_value"), get['name'], get.get('post_param', ''), get.get('flock', 0),get.get('time_set',''),get.get('backup_mode', ''),db_backup_path,get.get('time_type',''),get.get('special_time',''),get.get('user_agent',''),get.get('version',''),get.get('table_list',''),get.get('second',''))
            if 'save_local' in get:
                columns += ",save_local, notice, notice_channel"
                values = (1,get['type'], get['where1'], get['hour'],
                          get['minute'], get['save'], get['backupTo'], cronInfo['sName'], get['sBody'],
                          get['urladdress'], get.get("db_type"), get.get("split_type"), get.get("split_value"), get['name'], get.get('post_param', ''), get.get('flock', 0),get.get('time_set',''),get.get('backup_mode', ''),db_backup_path,get.get('time_type',''),get.get('special_time',''),get.get('user_agent',''),get.get('version',''),get.get('table_list',''),get.get('second',''),
                          get['save_local'], get["notice"],get["notice_channel"])
            if cronInfo['status'] != 0 and cronInfo['type'] != 'once':
                if not self.remove_for_crond(cronInfo['echo']):
                    return public.return_message(-1,0, public.lang('Writing scheduled task failed, please check if the disk is writable or if system hardening is enabled!'))
                # if cronInfo['status'] == 0: return public.returnMsg(False, '当前任务处于停止状态,请开启任务后再修改!')
                if not self.sync_to_crond(cronInfo):
                    return public.return_message(-1,0, public.lang('Writing scheduled task failed, please check if the disk is writable or if system hardening is enabled!'))
            public.M('crontab').where('id=?', (id,)).save(columns, values)
            public.WriteLog(public.lang('crontab tasks'), public.lang('Successfully modified plan task ['+cronInfo ['name']+']'))
            return public.return_message(0,0, public.lang('Modified successfully'))
        except:
             return public.return_message(-1,0,traceback.format_exc())



    # 获取指定任务数据
    def get_crond_find(self, get):
        id = int(get.id)
        data = public.M('crontab').where('id=?', (id,)).field(self.field).find()
        return public.return_message(0,0,data)

    # 同步到crond
    def sync_to_crond(self, cronInfo):
        if not 'status' in cronInfo: return False
        if 'where_hour' in cronInfo:
            cronInfo['hour'] = cronInfo['where_hour']
            cronInfo['minute'] = cronInfo['where_minute']
            cronInfo['week'] = cronInfo['where1']
        cuonConfig, cronInfo, name = self.GetCrondCycle(cronInfo)
        cronPath = public.GetConfigValue('setup_path') + '/cron'
        cronName = self.GetShell(cronInfo)
        if type(cronName) == dict: return cronName
        cuonConfig += ' ' + cronPath + '/' + cronName + ' >> ' + cronPath + '/' + cronName + '.log 2>&1'

        # 移除flock模式，与部分系统不兼容，会导致任务无法执行
        # if int(cronInfo.get('flock', 0)) == 1:
        #     flock_name = cronPath + '/' + cronName + '.lock'
        #     public.writeFile(flock_name, '')
        #     os.system('chmod 777 {}'.format(flock_name))
        #     cuonConfig += ' flock -xn ' + cronPath + '/' + cronName + '.lock' + ' -c ' + cronPath + '/' + cronName + ' >> ' + cronPath + '/' + cronName + '.log 2>&1'
        # else:
        #     cuonConfig += ' ' + cronPath + '/' + cronName + ' >> ' + cronPath + '/' + cronName + '.log 2>&1'

        wRes = self.WriteShell(cuonConfig)
        if wRes['status'] != 0: return False
        self.CrondReload()
        return True

    def ensure_execute_commands_script(self,get):
        cronName = public.md5(public.md5(str(time.time()) + '_bt'))
        script_path = '/etc/init.d/execute_commands'
        systemd_service_path = '/etc/systemd/system/execute_commands.service'

        # For systemd systems
        if os.path.exists('/bin/systemctl') or os.path.exists('/usr/bin/systemctl'):
            if not os.path.exists(systemd_service_path):
                with open(systemd_service_path, 'w') as service_file:
                    service_content = """[Unit]
    Description=Custom Service to execute commands at startup
    After=network.target

    [Service]
    Type=simple
    ExecStart=btpython /www/server/panel/script/execute_commands.py
    Restart=on-failure

    [Install]
    WantedBy=multi-user.target
    """
                    service_file.write(service_content)

                os.system('systemctl daemon-reload')
                os.system('systemctl start execute_commands.service')
                os.system('systemctl enable execute_commands.service')
                print("Systemd service created and enabled successfully.")

        # For SysVinit systems
        else:
            if not os.path.exists(script_path):
                with open(script_path, 'w') as script_file:
                    script_content = """#! /bin/sh
    # chkconfig: 2345 55 25

    ### BEGIN INIT INFO
    # Provides:          custom_service
    # Required-Start:    $all
    # Required-Stop:     $all
    # Default-Start:     2 3 4 5
    # Default-Stop:      0 1 6
    # Short-Description: Custom Service
    # Description:       Executes user-defined shell commands at startup
    ### END INIT INFO

    # Author:   Your Name
    # website:  Your Website

    PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

    case "$1" in
        start)
            echo -n "Starting custom_service... "
            if [ -f /var/run/custom_service.pid ];then
                mPID=$(cat /var/run/custom_service.pid)
                isStart=`ps ax | awk '{ print $1 }' | grep -e "^${mPID}$"`
                if [ "$isStart" != "" ];then
                    echo "custom_service (pid $mPID) already running."
                    exit 1
                fi
            fi
            nohup btpython /www/server/panel/script/execute_commands.py > /dev/null 2>&1 &
            pid=$!
            echo $pid > /var/run/custom_service.pid
            echo " done"
            ;;
        stop)
            echo "Custom Service does not support stop operation."
            ;;
        status)
            if [ -f /var/run/custom_service.pid ];then
                mPID=`cat /var/run/custom_service.pid`
                isStart=`ps ax | awk '{ print $1 }' | grep -e "^${mPID}$"`
                if [ "$isStart" != '' ];then
                    echo "custom_service is running with PID $mPID."
                    exit 0
                else
                    echo "custom_service is stopped"
                    exit 0
                fi
            else
                echo "custom_service is stopped"
                exit 0
            fi
            ;;
        *)
            echo "Usage: $0 {start|status}"
            exit 1
            ;;
    esac

    exit 0"""
                    script_file.write(script_content)
                    os.chmod(script_path, 0o755)  # Set execute permission
                print("Init script created successfully.")
                
                if os.path.exists('/usr/sbin/update-rc.d'):
                    os.system('update-rc.d -f execute_commands defaults')
                print("Service configured for SysVinit successfully.")

        if get.get('db_backup_path') == "/www/backup":
            db_backup_path = ""
        else:
            db_backup_path = get.get('db_backup_path', '')
        columns = 'name,type,where1,where_hour,where_minute,echo,addtime,\
                status,save,backupTo,sType,sName,sBody,urladdress,db_type,split_type,split_value,keyword,post_param,flock,time_set,backup_mode,db_backup_path,time_type,special_time,user_agent,version,table_list'
        values = (public.xssencode2(get['name']), get['type'], get['where1'], get['hour'],
                  get['minute'], cronName, time.strftime('%Y-%m-%d %X', time.localtime()),
                  1, get['save'], get['backupTo'], get['sType'], get['sName'], get['sBody'],
                  get['urladdress'], get.get("db_type"), get.get("split_type"), get.get("split_value"), get.get('keyword', ''), get.get('post_param', ''), get.get('flock', 0), get.get('time_set', ''),
                  get.get('backup_mode', ''), db_backup_path, get.get('time_type', ''), get.get('special_time', ''), get.get('user_agent', ''), get.get('verison', ''), get.get('table_list', ''))
        if "save_local" in get:
            columns += ",save_local,notice,notice_channel"
            values = (public.xssencode2(get['name']), get['type'], get['where1'], get['hour'],
                      get['minute'], cronName, time.strftime('%Y-%m-%d %X', time.localtime()),
                      1, get['save'], get['backupTo'], get['sType'], get['sName'], get['sBody'],
                      get['urladdress'], get.get("db_type"), get.get("split_type"), get.get("split_value"), get.get('keyword', ''), get.get('post_param', ''), get.get('flock', 0),
                      get.get('time_set', ''), get.get('backup_mode', ''), db_backup_path, get.get('time_type', ''), get.get('special_time', ''), get.get('user_agent', ''), get.get('verison', ''),
                      get.get('table_list', ''),
                      get["save_local"], get['notice'], get['notice_channel'])
        addData = public.M('crontab').add(columns, values)
        public.add_security_logs('crontab tasks', 'Add plan task ['+get['name']+'] successful'+str(values))
        if type(addData) == str:
            return public.returnMsg(False, addData)
        public.WriteLog(public.lang('crontab tasks'), public.lang('Successfully added scheduled task ['+get['name']+']'))
        if addData > 0:
            result = public.returnMsg(True, public.lang('ADD_SUCCESS'))
            result['id'] = addData
            return result
        return public.returnMsg(False, public.lang('Failed to add'))

    # 添加计划任务
    def AddCrontab(self, get):
        try:
            if get['type']=="second-n":
                get['type']="minute-n"
                get['where1']= "1"
                get['hour']=1
                get['minute']=1
                get['flock']=0
            if len(get['name']) < 1:
                return public.return_message(-1,0, public.lang('CRONTAB_TASKNAME_EMPTY'))
            if get['sType'] == 'toShell':
                get['sBody'] = get['sBody'].replace('\r\n', '\n')

            # 修改sBody
            user = get.get('user', 'root')
            if user:
                if get["sType"] != "toShell":
                    get['sBody'] = "sudo -u {0} bash -c '{1}'".format(user, get['sBody'])
                else:
                    get['sBody'] = f"sudo -u {user} bash <<'EOF'\n{get['sBody']}\nEOF"


            # 如果get中有version键，就替换sBody中的版本号占位符
                if get.get('version',''):
                    version = get['version'].replace(".", "")
                    get['sBody'] = get['sBody'].replace("${1/./}", version)

            # 新增定时任务：在指定时间执行一次
            if get['type'] == 'once' and get['sType'] == 'toShell':
                return self.add_once_crontab(get)

            if get['sType'] == 'startup_services':
                return self.ensure_execute_commands_script(get)  # 检查并创建脚本
                
            if get['type']=='sweek':
               get['type']='minute-n'
            cuonConfig, get, name = self.GetCrondCycle(get)
            cronPath = public.GetConfigValue('setup_path') + '/cron'
            cronName = self.GetShell(get)

            if type(cronName) == dict: return cronName
            # 移除flock模式，与部分系统不兼容，会导致任务无法执行
            # if int(get.get('flock', 0)) == 1:
            #     flock_name = cronPath + '/' + cronName + '.lock'
            #     public.writeFile(flock_name, '')
            #     os.system('chmod 777 {}'.format(flock_name))
            #     cuonConfig += ' flock -xn ' + cronPath + '/' + cronName + '.lock' + ' -c ' + cronPath + '/' + cronName + ' >> ' + cronPath + '/' + cronName + '.log 2>&1'
            # else:
            cuonConfig += ' ' + cronPath + '/' + cronName + ' >> ' + cronPath + '/' + cronName + '.log 2>&1'
            wRes = self.WriteShell(cuonConfig)
            if wRes['status'] != 0: return wRes
            self.CrondReload()
            if get.get('db_backup_path')=="/www/backup":
                db_backup_path=""
            else:
                db_backup_path=get.get('db_backup_path','')
            columns = 'name,type,where1,where_hour,where_minute,echo,addtime,\
                    status,save,backupTo,sType,sName,sBody,urladdress,db_type,split_type,split_value,keyword,post_param,flock,time_set,backup_mode,db_backup_path,time_type,special_time,user_agent,version,table_list,result,second'
            values = (public.xssencode2(get['name']), get['type'], get['where1'], get['hour'],
                    get['minute'], cronName, time.strftime('%Y-%m-%d %X', time.localtime()),
                    1, get['save'], get['backupTo'], get['sType'], get['sName'], get['sBody'],
                    get['urladdress'], get.get("db_type"), get.get("split_type"), get.get("split_value"), get.get('keyword', ''), get.get('post_param', ''), get.get('flock', 0),get.get('time_set', ''),get.get('backup_mode',''),db_backup_path,get.get('time_type',''),get.get('special_time',''),get.get('user_agent',''),get.get('verison',''),get.get('table_list',''),get.get('result',1),get.get('second',''))
            if "save_local" in get:
                columns += ",save_local,notice,notice_channel"
                values = (public.xssencode2(get['name']), get['type'], get['where1'], get['hour'],
                        get['minute'], cronName, time.strftime('%Y-%m-%d %X', time.localtime()),
                        1, get['save'], get['backupTo'], get['sType'], get['sName'], get['sBody'],
                        get['urladdress'], get.get("db_type"), get.get("split_type"), get.get("split_value"), get.get('keyword', ''), get.get('post_param', ''), get.get('flock', 0),get.get('time_set', ''),get.get('backup_mode', ''),db_backup_path,get.get('time_type',''),get.get('special_time',''),get.get('user_agent',''),get.get('verison',''),get.get('table_list',''),get.get('result',1),get.get('second',''),
                        get["save_local"], get['notice'], get['notice_channel'])
            addData = public.M('crontab').add(columns, values)
            public.add_security_logs('crontab tasks', 'Add plan task ['+get['name']+'] successful'+str(values))
            if type(addData) == str:
                return public.return_message(-1,0, addData)
            public.WriteLog(public.lang('crontab tasks'), public.lang('Successfully added scheduled task ['+get['name']+']'))
            if addData > 0:
                result = public.return_message(0,0, public.lang('ADD_SUCCESS'))
                result['message']['id'] = addData
                return result
            return public.return_message(-1,0, public.lang('Failed to add'))
        except Exception as e:
            return public.return_message(-1,0, public.lang(str(e)))

    # 添加一次性定时任务
    def add_once_crontab(self, get):
        try:
            # 执行时间校验
            execute_time_str = get.get('where1', '').strip()
            if not execute_time_str:
                raise ValueError(public.lang('Missing required parameter: execute_time (format: YYYY-MM-DD HH:MM:SS)'))

            if not get['sBody'].strip():
                raise ValueError(public.lang('CRONTAB_TASKBODY_EMPTY'))

            try:
                execute_datetime = datetime.strptime(execute_time_str, "%Y-%m-%d %H:%M:%S")
            except ValueError:
                raise ValueError(public.lang(f'Execute time format error: {execute_time_str}, required: YYYY-MM-DD HH:MM:SS'))

            if execute_datetime <= datetime.now():
                raise ValueError(public.lang(f'Execute time must be in the future'))

            # 生成脚本文件
            cronPath = public.GetConfigValue('setup_path') + '/cron'
            os.makedirs(cronPath, mode=0o755, exist_ok=True)
            cronName = f"once_{int(time.time())}.sh"
            scriptPath = os.path.join(cronPath, cronName)
            logPath = os.path.join(cronPath, f"{cronName}.log")
            script_content = f"""#!/bin/bash
    {get['sBody']}"""

            # 写入脚本并授权
            public.writeFile(scriptPath, script_content)
            os.chmod(scriptPath, 0o755)

             # 使用systemd-timer
            """
                默认执行用户：root
                执行类型：一次性 oneshot
                执行超时：一小时 3600
            """
            systemd_task_name = cronName[:-3]
            service_path = f"/etc/systemd/system/{systemd_task_name}.service"  # 服务文件
            timer_path = f"/etc/systemd/system/{systemd_task_name}.timer"  # 定时器文件

            # 生成 systemd 服务文件
            service_content = f"""[Unit]
Description=Once crontab task: {public.xssencode2(systemd_task_name)}
After=network.target

[Service]
Type=oneshot
User=root

ExecStart=/bin/bash -c '{scriptPath} >> {logPath} 2>&1'
TimeoutSec=3600
            """

            # 生成 systemd 定时器文件
            timer_content = f"""[Unit]
Description=Timer for once task: {public.xssencode2(systemd_task_name)}

[Timer]
OnCalendar={execute_time_str} 
Persistent=false

[Install]
WantedBy=timers.target
            """

            # 写入 systemd 配置文件
            public.writeFile(service_path, service_content)
            public.writeFile(timer_path, timer_content)

            # 设置 systemd 配置文件权限（符合系统要求：644）
            os.chmod(service_path, 0o644)
            os.chmod(timer_path, 0o644)

            # 重新加载 systemd 配置
            reload_result = public.ExecShell('systemctl daemon-reload')
            if reload_result[1] != '':
                raise ValueError(f"systemd reload failed: {reload_result[1]}")

            # 启动定时器
            start_result = public.ExecShell(f'systemctl start {systemd_task_name}.timer')
            if start_result[1] != '':
                raise ValueError(f"Timer start failed: {start_result[1]}")
            # 数据库记录
            db_backup_path = "" if get.get('db_backup_path') == "/www/backup" else get.get('db_backup_path', '')
            columns = 'name,type,where1,where_hour,where_minute,echo,\
                            status,save,backupTo,sType,sName,sBody,urladdress,db_type,split_type,split_value,keyword,post_param,flock,time_set,backup_mode,db_backup_path,time_type,special_time,user_agent,version,table_list,result,second'
            values = (
                public.xssencode2(get['name']), 'once', execute_datetime, 0, 0, cronName,
                1, get.get('save', 0), get.get('backupTo', ''), get['sType'], get.get('sName', ''), get['sBody'],
                get.get('urladdress', ''), get.get("db_type"), get.get("split_type"), get.get("split_value"),
                get.get('keyword', ''), get.get('post_param', ''), get.get('flock', 0), '',
                get.get('backup_mode', ''), db_backup_path, '', '', get.get('user_agent', ''),
                get.get('version', ''), get.get('table_list', ''), get.get('result', 1), ''
            )

            addData = public.M('crontab').add(columns, values)
            public.add_security_logs('crontab tasks',
                                     f'Add once task [{get["name"]}] success, execute time: {execute_time_str}')

            if isinstance(addData, str):
                raise ValueError(addData)
            public.WriteLog(public.lang('crontab tasks'), public.lang(f'Successfully added once task [{get["name"]}]'))
            if addData > 0:
                result = public.return_message(0, 0, public.lang('ADD_SUCCESS'))
                result['message']['id'] = addData
                result['message']['execute_time'] = execute_time_str
                return result
            raise ValueError(public.lang('Failed to add once task'))
        except Exception as e:
            try:
                if 'scriptPath' in locals() and os.path.exists(scriptPath):
                    os.remove(scriptPath)
                if 'logPath' in locals() and os.path.exists(logPath):
                    os.remove(logPath)
                if 'service_path' in locals() and os.path.exists(service_path):
                    os.remove(service_path)
                if 'timer_path' in locals() and os.path.exists(timer_path):
                    os.remove(timer_path)
            except:
                pass
            raise ValueError(e)

    # 构造周期
    def GetCrondCycle(self, params):
        cuonConfig = ""
        name = ""
        if params['type'] == "day":
            cuonConfig = self.GetDay(params)
            name = public.getMsg('CRONTAB_TODAY')
        elif params['type'] == "day-n":
            cuonConfig = self.GetDay_N(params)
            name = public.getMsg('CRONTAB_N_TODAY', (params['where1'],))
        elif params['type'] == "hour":
            cuonConfig = self.GetHour(params)
            name = public.getMsg('CRONTAB_HOUR')
        elif params['type'] == "hour-n":
            cuonConfig = self.GetHour_N(params)
            name = public.getMsg('CRONTAB_HOUR')
        elif params['type'] == "minute-n":
            cuonConfig = self.Minute_N(params)
        elif params['type'] == "week":
            params['where1'] = params['week']
            cuonConfig = self.Week(params)
        elif params['type'] == "month":
            cuonConfig = self.Month(params)
        return cuonConfig, params, name

    # 取任务构造Day
    def GetDay(self, param):
        cuonConfig = "{0} {1} * * * ".format(param['minute'], param['hour'])
        return cuonConfig

    # 取任务构造Day_n
    def GetDay_N(self, param):
        cuonConfig = "{0} {1} */{2} * * ".format(param['minute'], param['hour'], param['where1'])
        return cuonConfig

    # 取任务构造Hour
    def GetHour(self, param):
        cuonConfig = "{0} * * * * ".format(param['minute'])
        return cuonConfig

    # 取任务构造Hour-N
    def GetHour_N(self, param):
        cuonConfig = "{0} */{1} * * * ".format(param['minute'], param['where1'])
        return cuonConfig

    # 取任务构造Minute-N
    def Minute_N(self, param):
        cuonConfig = "*/{0} * * * * ".format(param['where1'])
        return cuonConfig

    # 取任务构造week
    def Week(self, param):
        cuonConfig = "{0} {1} * * {2}".format(param['minute'], param['hour'], param['week'])
        return cuonConfig

    # 取任务构造Month
    def Month(self, param):
        cuonConfig = "{0} {1} {2} * * ".format(param['minute'], param['hour'], param['where1'])
        return cuonConfig

    # 取数据列表
    def GetDataList(self, get):
        data = {}
        if get['type'] == 'databases':
            data['data'] = public.M(get['type']).where("type=?", "MySQL").field('name,ps').select()
        else:
            data['data'] = public.M(get['type']).field('name,ps').select()
        for i in data['data']:
            if 'ps' in i:
                try:
                    if i['ps'] is None: continue
                    i['ps'] = public.xsssec(i['ps'])  # 防止数据库为空时，xss防御报错  2020-11-25
                except:
                    pass
        data['orderOpt'] = []
        import json
        tmp = public.readFile('data/libList.conf')
        if not tmp: return data
        libs = json.loads(tmp)
        for lib in libs:
            if not 'opt' in lib: continue
            filename = 'plugin/{}'.format(lib['opt'])
            if not os.path.exists(filename): continue
            tmp = {}
            tmp['name'] = lib['name']
            tmp['value'] = lib['opt']
            data['orderOpt'].append(tmp)
        return public.return_message(0,0,data)

    # 取任务日志
    def GetLogs(self, get):
        id = get['id']
        sType = public.M('crontab').where("id=?", (id,)).getField('sType')
        if sType == 'webshell':
            try:
                logs = self.GetWebShellLogs(get)
                return logs
            except:
                pass
        echo = public.M('crontab').where("id=?", (id,)).field('echo').find()
        if not echo:
            return public.return_message(-1,0, public.lang("No data was found for the corresponding scheduled task. Please refresh the page to check if the scheduled task exists!"))
        logFile = public.GetConfigValue('setup_path') + '/cron/' + echo['echo'] + '.log'
        if not os.path.exists(logFile): return public.return_message(0,0, public.lang('Task logs are empty!'))
        log = public.GetNumLines(logFile, 2000)
        return public.return_message(0,0, public.xsssec(log))

    # 清理任务日志
    def DelLogs(self, get):
        try:
            id = get['id']
            echo = public.M('crontab').where("id=?", (id,)).getField('echo')
            logFile = public.GetConfigValue('setup_path') + '/cron/' + echo + '.log'
            if not os.path.exists(logFile): return public.return_message(0,0, public.lang('Task logs emptied!'))
            os.remove(logFile)
            return public.return_message(0,0, public.lang('Task logs emptied!'))
        except:
            return public.return_message(-1,0, public.lang('Failed to empty task logs!'))

    # 删除计划任务
    def DelCrontab(self, get):
        try:
            id = get['id']
            # 尝试删除数据库增量备份表中的数据
            public.M("mysql_increment_settings").where("cron_id=?", (id)).delete()
            find = public.M('crontab').where("id=?", (id,)).field('name,echo,type').find()
            if not find: return public.return_message(-1,0, public.lang('The specified task does not exist!'))
            if not self.remove_for_crond(find['echo']): return public.return_message(-1,0, public.lang('Unable to write to file, please check if system hardening is enabled!'))

            # 删除一次性定时任务
            if find['type'] == 'once':
                try:
                    # 删除任务配置文件
                    timer = '/etc/systemd/system/' + find['echo'][:-3] + '.timer'
                    service = '/etc/systemd/system/' + find['echo'][:-3] + '.service'
                    if os.path.exists(timer):
                        public.ExecShell(f'rm -rf {timer}')
                    if os.path.exists(service):
                        public.ExecShell(f'rm -rf {service}')

                    # 清理并重载任务
                    public.ExecShell(f'systemctl stop {find['echo'][:-3] + '.timer'}')
                    public.ExecShell(f'systemctl daemon-reload')
                    public.ExecShell(f'systemctl reset-failed {find['echo'][:-3] + '.timer'}')
                except:
                    pass

            cronPath = public.GetConfigValue('setup_path') + '/cron'
            sfile = cronPath + '/' + find['echo']
            if os.path.exists(sfile): os.remove(sfile)
            sfile = cronPath + '/' + find['echo'] + '.log'
            if os.path.exists(sfile): os.remove(sfile)

            public.M('crontab').where("id=?", (id,)).delete()
            public.add_security_logs(public.lang("Delete scheduled tasks"), public.lang("Delete scheduled tasks: {}", find['name']))
            public.WriteLog('TYPE_CRON', 'CRONTAB_DEL', (find['name'],))
            return public.return_message(0,0, public.lang('DEL_SUCCESS'))
        except:
            return public.return_message(-1,0, public.lang('DEL_ERROR'))

    # 从crond删除
    def remove_for_crond(self, echo):
        file = self.get_cron_file()
        if not os.path.exists(file):
            return False
        conf = public.readFile(file)
        if not conf: return True
        if conf.find(str(echo)) == -1: return True
        rep = ".+" + str(echo) + ".+\n"
        conf = re.sub(rep, "", conf)
        try:
            if not public.writeFile(file, conf): return False
        except:
            return False
        self.CrondReload()
        return True

    # 取执行脚本
    def GetShell(self, param):
        type = param['sType']
        if not 'echo' in param:
            cronName = public.md5(public.md5(str(time.time()) + '_bt'))
        else:
            cronName = param['echo']
        if type == 'toFile':
            shell = param.sFile
        else:
            cronPath = '/www/server/cron'  
            cronFile = '{}/{}.pl'.format(cronPath,cronName)
            head = "#!/bin/bash\nPATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin\nexport PATH\n"
            head += "echo $$ > " + cronFile + "\n"  # 将PID保存到文件中
            second = param.get('second', "")
            time_type=param['type']
            if second:
                time_type="second-n"
                head += 'if [[ $1 != "start" ]]; then\n'
                head += ' btpython /www/server/panel/script/second_task.py {} {} \n'.format(second,cronName)
                head += ' exit 0\n'
                head += 'fi\n'
            public.ExecShell("chmod +x /www/server/panel/script/modify_second_cron.sh")
            public.ExecShell("nohup /www/server/panel/script/modify_second_cron.sh {} {} {} &".format(time_type,second,cronName) )
                
            time_type = param.get('time_type', '')
            if time_type:
                time_list=param.get('time_set', '')
                special_time=param.get('special_time', '')           
                # if time_type == "sweek":
                # 调用 Python 脚本进行时间检查
                head += 'if [[ $1 != "start" ]]; then\n'
                head += ' if ! btpython /www/server/panel/script/time_check.py time_type={} special_time={} time_list={}; then\n'.format(time_type, ",".join(special_time.split(",")), ",".join(time_list.split(",")))
                head += '   exit 1\n'
                head += ' fi\n'
                head += 'fi\n'
            if param['sType']=="site_restart":
                special_time=param.get('special_time', '')
                # 调用 Python 脚本进行时间检查
                head += 'if [[ $1 != "start" ]]; then\n'
                head += ' if ! btpython /www/server/panel/script/special_time.py special_time={} ; then\n'.format(",".join(special_time.split(",")))
                head += '   exit 1\n'
                head += ' fi\n'
                head += 'fi\n'
            log = '-access_log'
            python_bin = "{} -u".format(public.get_python_bin())
            if public.get_webserver() == 'nginx':
                log = '.log'
            if type in ['site', 'path'] and param['sBody'] != 'undefined' and len(param['sBody']) > 1:
                exclude_files = ','.join([n.strip() for n in param['sBody'].split('\n') if n.strip()])
                if exclude_files and 'bash -c ' in exclude_files: 
                    exclude_files = exclude_files.split('bash -c ')[1].replace("'",'').strip()
                head += f'export BT_EXCLUDE="{exclude_files}"\n'
            attach_param = " " + cronName
            log_cut_path = param['log_cut_path'] if hasattr(param,'log_cut_path') else '/www/wwwlogs/'
            special_time= param['special_time'] if hasattr(param,'special_time') else ''
            wheres = {
                'path': head + python_bin + " " + public.GetConfigValue(
                    'setup_path') + "/panel/script/backup.py path " + param['sName'] + " " + str(
                    param['save']) + attach_param,
                'site': head + python_bin + " " + public.GetConfigValue(
                    'setup_path') + "/panel/script/backup.py site " + param['sName'] + " " + str(
                    param['save']) + attach_param,
                'database': head + python_bin + " " + public.GetConfigValue(
                    'setup_path') + "/panel/script/backup.py database " + param['sName'] + " " + str(
                    param['save']) + attach_param,
                'logs': head + python_bin + " " + public.GetConfigValue('setup_path') + "/panel/script/logsBackup " +
                        param['sName'] + " " + str(param['save']) + " " + log_cut_path,
                'rememory': head + "/bin/bash " + public.GetConfigValue('setup_path') + '/panel/script/rememory.sh',
                'sync_time': head + python_bin + " " + public.GetConfigValue(
                    'setup_path') + "/panel/script/sync_time.py {}".format(param['sName']),
                'webshell': head + python_bin + " " + public.GetConfigValue(
                    'setup_path') + '/panel/class/webshell_check.py site ' + param['sName'] + ' ' + param['urladdress'],
                'mysql_increment_backup': head + python_bin + " " + public.GetConfigValue(
                    'setup_path') + '/panel/script/loader_binlog.py --echo_id=' + cronName,
                'special_log': head + python_bin + " " + public.GetConfigValue('setup_path') + "/panel/script/rotate_log_special.py " +
                               str(param['save']) + " " + param['sName'],
                'site_restart':head + python_bin + " " + public.GetConfigValue('setup_path') + "/panel/script/move_config.py " +
                              " " + param['sName'] + " " + special_time,
                'log_cleanup':head + python_bin + " " + public.GetConfigValue('setup_path') + "/panel/script/log_cleanup.py " +
                              " " + param['sName'],
            }
            try:
                shell = wheres[type]
            except:
                if type=="site_restart":
                    lines = shell.split('\n')
                    last_line = lines[-1]               
                    new_command = '''
if [[ $1 == "start" ]]; then
        {} start
else
        {}
                    
fi
'''.format(last_line, last_line)
                    shell = shell.replace(last_line, new_command)
                # 设置 User-Agent 头
                user_agent = "-H 'User-Agent: {}'".format(param['user_agent']) if hasattr(param,'user_agent') else ''
                if type == 'toUrl':
                    # shell = head + "curl -sS --connect-timeout 10 -m 3600 '" + param['urladdress'] + "'"
                    shell = head + "curl -sS -L {} --connect-timeout 10 -m 3600 '{}'".format(user_agent, param['urladdress'])
                elif type == 'to_post':
                    param1 = {}
                    for i in json.loads(param['post_param']):
                        param1[i['paramName']] = i['paramValue']
                    # shell = head + '''curl -sS -X POST --connect-timeout 10 -m 3600 -H "Content-Type: application/json"  -d '{}' {} '''.format(json.dumps(param1),
                    #                                                                                                                            param['urladdress'])
                    shell = head + '''curl -sS -L -X POST {} --connect-timeout 10 -m 3600 -H "Content-Type: application/json"  -d '{}' {} '''.format(user_agent, json.dumps(param1), param['urladdress'])
                # elif type == 'toPython':
                #     ...
                else:
                    shell = head + param['sBody'].replace("\r\n", "\n")
            cronPath = '/www/server/cron'  # 修改为实际的路径
            cronFile = f'{cronPath}/{cronName}.pl'
            logname=f'{cronPath}/{cronName}.log'
            shell += f'''
echo "----------------------------------------------------------------------------"
endDate=`date +"%Y-%m-%d %H:%M:%S"`
echo "★[$endDate] Successful"
echo "----------------------------------------------------------------------------"
if [[ "$1" != "start" ]]; then
    btpython /www/server/panel/script/log_task_analyzer.py {logname}
fi
rm -f {cronFile}
'''
        if type == 'toShell' and param.get('notice') and param['notice_channel'] and param['notice_channel'] and len(param.get('keyword', '')):
            shell += "btpython /www/server/panel/script/shell_push.py {} {} {} {} &".format(cronName, param['notice_channel'], param['keyword'], param['name'])
        cronPath = public.GetConfigValue('setup_path') + '/cron'
        if not os.path.exists(cronPath): public.ExecShell('mkdir -p ' + cronPath)
        file = cronPath + '/' + cronName
        public.writeFile(file, self.CheckScript(shell))
        public.ExecShell('chmod 750 ' + file)
        return cronName
        # except Exception as ex:
        # return public.returnMsg(False, 'FILE_WRITE_ERR' + str(ex))



    # 检查脚本
    def CheckScript(self, shell):
        keys = ['shutdown', 'init 0', 'mkfs', 'passwd', 'chpasswd', '--stdin', 'mkfs.ext', 'mke2fs']
        for key in keys:
            shell = shell.replace(key, '[***]')
        return shell

    # 重载配置
    def CrondReload(self):
        if os.path.exists('/etc/init.d/crond'):
            public.ExecShell('/etc/init.d/crond reload')
        elif os.path.exists('/etc/init.d/cron'):
            public.ExecShell('service cron restart')
        else:
            public.ExecShell("systemctl reload crond")

    # 将Shell脚本写到文件
    def WriteShell(self, config):
        u_file = '/var/spool/cron/crontabs/root'
        file = self.get_cron_file()
        if not os.path.exists(file): 
            if not public.writeFile(file, ''):
                return public.return_message(-1,0, 'Unable to write to file, please check if system hardening is enabled!')
               
        conf = public.readFile(file)
        if type(conf) == bool: return public.return_message(-1,0, 'Failed to read file!')
        conf += config + "\n"
        if public.writeFile(file, conf):
            if not os.path.exists(u_file):
                public.ExecShell("chmod 600 '" + file + "' && chown root.root " + file)
            else:
                public.ExecShell("chmod 600 '" + file + "' && chown root.crontab " + file)
            return public.return_message(0,0,'')
        return public.return_message(-1,0, 'Unable to write to file, please check if system hardening is enabled!')


    # 立即执行任务
    def StartTask(self, get):
        echo = public.M('crontab').where('id=?', (get.id,)).getField('echo')
        if not echo:
            return public.return_message(-1,0, public.lang("No data was found for the corresponding scheduled task. Please refresh the page to check if the scheduled task exists!"))
        execstr = public.GetConfigValue('setup_path') + '/cron/' + echo
        public.ExecShell('chmod +x ' + execstr)
        public.ExecShell('nohup ' + execstr +' start >> ' + execstr + '.log 2>&1 &')
        return public.return_message(0,0, public.lang('CRONTAB_TASK_EXEC'))

    # 获取计划任务文件位置
    def get_cron_file(self):
        u_path = '/var/spool/cron/crontabs'
        u_file = u_path + '/root'
        c_file = '/var/spool/cron/root'
        cron_path = c_file
        if not os.path.exists(u_path):
            cron_path = c_file

        if os.path.exists("/usr/bin/apt-get"):
            cron_path = u_file
        elif os.path.exists('/usr/bin/yum'):
            cron_path = c_file

        if cron_path == u_file:
            if not os.path.exists(u_path):
                os.makedirs(u_path, 472)
                public.ExecShell("chown root:crontab {}".format(u_path))
        if not os.path.exists(cron_path):
            public.writeFile(cron_path, "")
        return cron_path

    def modify_project_log_split(self, cronInfo, get):

        def _test_project_type(self, project_type):
            if project_type == "Node project":
                return "nodojsModel"
            elif project_type == "Java Project":
                return "javaModel"
            elif project_type == "GO Project":
                return "goModel"
            elif project_type == "Others":
                return "otherModel"
            elif project_type == "Python project":
                return "pythonModel"
            else:
                return None

        def the_init(self, cronInfo, get: dict):
            self.get = get
            self.cronInfo = cronInfo
            self.msg = ""
            self.flag = False
            name = get["name"]
            if name.find("Running log segmentation") != -1:
                try:
                    project_type, project_name = name.split("]", 2)[1].split("[", 1)
                    project_type = self._test_project_type(project_type)
                except:
                    self.project_type = None
                    return
            else:
                self.project_type = None
                return

            self.project_type = project_type
            self.project_name = project_name
            conf_path = '{}/data/run_log_split.conf'.format(public.get_panel_path())
            data = json.loads(public.readFile(conf_path))
            self.log_size = int(data[self.project_name]["log_size"]) / 1024 / 1024

        def modify(self):
            from importlib import import_module
            if not self.project_type:
                return False
            if self.cronInfo["type"] != self.get['type']:
                self.msg = "The execution cycle cannot be modified by cutting the running log"
                return True
            get = public.dict_obj()
            get.name = self.project_name
            get.log_size = self.log_size
            if get.log_size != 0:
                get.hour = "2"
                get.minute = str(self.get['where1'])
            else:
                get.hour = str(self.get['hour'])
                get.minute = str(self.get['minute'])
            get.num = str(self.get["save"])

            model = import_module(".{}".format(self.project_type), package="projectModel")

            res = getattr(model.main(), "mamger_log_split")(get)
            self.msg = res["msg"]
            self.flag = res["status"]

            return True

        attr = {
            "__init__": the_init,
            "_test_project_type": _test_project_type,
            "modify": modify,
        }
        return type("ProjectLog", (object,), attr)(cronInfo, get)

    # 检查指定的url是否通
    def check_url_connecte(self, get):
        if 'url' not in get or not get['url']:
            return public.return_message(-1,0, 'Please provide the URL!')

        try:
            start_time = time.time()
            response = requests.get(get['url'], timeout=30)
            response.encoding = 'utf-8'
            end_time = time.time()
            
            response_time = "{}ms".format(int(round(end_time - start_time, 2) * 1000))
            result = {'status_code': response.status_code, 
                    'txt': public.xsssec(response.text),
                    'time': response_time}
            status = 0 if response.status_code == 200 else -1
            return public.return_message(status,0,result)

        except requests.exceptions.Timeout as err:
            end_time = time.time()
            response_time = "{}ms".format(int(round(end_time - start_time, 2) * 1000))
            return public.return_message(-1,0,{'status_code': '', 'txt': 'request timeout: {}'.format(err), 'time': response_time})
        except requests.exceptions.ConnectionError as err:
            end_time = time.time()
            response_time = "{}ms".format(int(round(end_time - start_time, 2) * 1000))
            return public.return_message(-1,0,{'status_code': '', 'txt': 'connection error: {}'.format(err), 'time': response_time})
        except requests.exceptions.HTTPError as err:
            end_time = time.time()
            response_time = "{}ms".format(int(round(end_time - start_time, 2) * 1000))
            return public.return_message(-1,0,{'status_code': err.response.status_code, 'txt': 'HTTP Error: {}'.format(err), 'time': response_time})
        except requests.exceptions.RequestException as err:
            end_time = time.time()
            response_time = "{}ms".format(int(round(end_time - start_time, 2) * 1000))
            return public.return_message(-1,0,{'status_code': '', 'txt': 'Request exception: {}'.format(err), 'time': response_time})


    # 获取各个类型数据库
    def GetDatabases(self, get):
        from panel_mysql_v2 import panelMysql
        db_type = getattr(get, "db_type", "mysql")

        crontab_databases = public.M("crontab").field("id,sName").where("LOWER(type)=LOWER(?)", (db_type)).select()
        for db in crontab_databases:
            db["sName"] = set(db["sName"].split(","))

        if db_type == "redis":
            # 默认ALL
            return public.return_message(0, 0, [])

        databases = public.M("databases").field("name,ps").where("LOWER(type)=LOWER(?)", (db_type)).select()

        for database in databases:
            try:
                if database.get("name") is None: continue
                table_list = panelMysql().query("show tables from `{db_name}`;".format(db_name=database["name"]))
                if not isinstance(table_list, list):
                    continue
                cron_id = public.M("mysql_increment_settings").where("tb_name == ''", ()).getField("cron_id")
                database["table_list"] = [{"tb_name": "all", "value": "", "cron_id": cron_id if cron_id else None}]
                for tb_name in table_list:
                    cron_id = public.M("mysql_increment_settings").where("tb_name in (?)", (tb_name[0])).getField("cron_id")
                    database["table_list"].append({"tb_name": tb_name[0], "value": tb_name[0], "cron_id": cron_id if cron_id else None})

                database["cron_id"] = []
                for db in crontab_databases:
                    if database["name"] in db["sName"]:
                        database["cron_id"].append(db["id"])
            except Exception as e:
                print(e)
        return public.return_message(0,0,databases)

    # 取任务日志
    def GetWebShellLogs(self, get):
        id = get['id']
        echo_result = public.M('crontab').where("id=?", (id,)).field('echo').find()
        # 确保echo_result总是字典形式
        if isinstance(echo_result, list) and echo_result and isinstance(echo_result[0], dict):
            echo = echo_result[0]  # 如果是列表，则取列表的第一个字典元素
        elif isinstance(echo_result, dict):
            echo = echo_result
        else:
            return public.return_message(-1,0, public.lang("Task execution failed! No matching task record found."))

        # 从这一点开始，echo变量将是字典形式，并且包含'echo'键
        if 'echo' in echo:
            logFile = public.GetConfigValue('setup_path') + '/cron/' + echo['echo'] + '.log'
        else:
            return public.return_message(-1,0, public.lang("Task execution failed! The 'echo' information is missing from the task record."))
        
        if not os.path.exists(logFile): return public.return_message(-1,0, public.lang('Task logs are empty!'))
        logs = public.readFile(logFile)
        logs = public.xsssec(logs)
        logs = logs.split('\n')
        if hasattr(get, 'time_search') and get.time_search != '' and get.time_search != '[]':
            time_logs = []
            time_search = json.loads(get.time_search)
            start_time = int(time_search[0])
            end_time = int(time_search[1])
            for i in range(len(logs) - 1, -1, -1):
                infos = re.findall(r'【(.+?)】', logs[i])
                try:
                    infos_time = time.strptime(infos[0], "%Y-%m-%d %H:%M:%S")
                    infos_time = time.mktime(infos_time)
                    if infos_time > start_time and infos_time < end_time:
                        time_logs.append(logs[i])
                except:
                    pass
            time_logs.reverse()
            logs = time_logs

        if hasattr(get, 'type') and get.type != '':
            if get.type == 'warring':
                warring_logs = []
                for i in range(len(logs)):
                    if '【warring】' in logs[i]:
                        warring_logs.append(logs[i])
                logs = warring_logs

        for i in range(len(logs)):
            if '【warring】' in logs[i]:
                logs[i] = '<span style="background-color:rgba(239, 8, 8, 0.8)">{}</span>'.format(logs[i])
        logs = '\n'.join(logs)
        if logs:
            return public.return_message(0,0, public.lang(logs))
        else:
            return public.return_message(-1,0, public.lang('Task logs are empty!'))

    def download_logs(self, get):
        try:
            id = int(get['id'])
            echo = public.M('crontab').where("id=?", (id,)).field('echo').find()
            logFile = public.GetConfigValue('setup_path') + '/cron/' + echo['echo'] + '.log'
            if not os.path.exists(logFile): public.writeFile(logFile, "")
            logs = public.readFile(logFile)
            logs = logs.split('\n')
            if hasattr(get, 'day') and get.day != '':
                day = int(get.day)
                time_logs = []
                end_time = int(time.time())
                start_time = end_time - day * 86400
                for i in range(len(logs), 0, -1):
                    try:
                        infos = re.findall(r'【(.+?)】', logs[i])
                        infos_time = time.strptime(infos[0], "%Y-%m-%d %H:%M:%S")
                        infos_time = time.mktime(infos_time)
                        if infos_time > start_time and infos_time < end_time:
                            time_logs.append(logs[i])
                        if infos_time < start_time:
                            break
                    except:
                        pass
                time_logs.reverse()
                logs = time_logs
            if hasattr(get, 'type') and get.type != '':
                if get.type == 'warring':
                    warring_logs = []
                    for i in range(len(logs)):
                        if '【warring】' in logs[i]:
                            warring_logs.append(logs[i])
                    logs = warring_logs
            logs = '\n'.join(logs)
            public.writeFile('/tmp/{}.log'.format(echo['echo']), logs)
            return public.returnMsg(True, '/tmp/{}.log'.format(echo['echo']))
        except:
            return public.returnMsg(False, public.lang('Download failed!'))

    def clear_logs(self, get):
        try:
            id = int(get['id'])
            echo = public.M('crontab').where("id=?", (id,)).field('echo').find()
            logFile = public.GetConfigValue('setup_path') + '/cron/' + echo['echo'] + '.log'
            if not os.path.exists(logFile): return public.returnMsg(False, public.lang('Task logs are empty!'))
            logs = public.readFile(logFile)
            logs = logs.split('\n')
            if hasattr(get, 'day') and get.day != '':
                day = int(get.day)
                end_time = int(time.time())
                start_time = end_time - day * 86400

                last_idx = len(logs) - 1
                for i in range(len(logs) - 1, -1, -1):
                    info_obj = re.search(r'[【\[](\d+-\d+-\d+\s+\d+:\d+:\d+)[】\]]', logs[i])
                    if info_obj:
                        add_info_time = info_obj.group(1)
                        add_info_time = time.strptime(add_info_time, "%Y-%m-%d %H:%M:%S")
                        add_info_time = time.mktime(add_info_time)
                        if add_info_time < start_time:
                            break
                        last_idx = i
                logs = logs[last_idx:]
            else:
                logs = []
            public.writeFile(logFile, '\n'.join(logs))
            return public.returnMsg(True, public.lang('Clear successfully!'))
        except:
            return public.returnMsg(False,public.lang('Clearing failed!'))


    def cloud_backup_download(self, get):
        if not hasattr(get, 'filename'):
            return public.return_message(-1,0, public.lang('Please enter filename!'))
        if get.filename:
            if "|webdav|" in get.filename:
                import sys
                if '/www/server/panel/plugin/webdav' not in sys.path:
                    sys.path.insert(0, '/www/server/panel/plugin/webdav')
                try:
                    from webdav_main import webdav_main as webdav
                    path=webdav().cloud_download_file(get)['msg']
                except:
                    return public.return_message(-1,0, public.lang('Please install webdav storage first!'))
            else:
                path = get.filename.split('|')[0]
            if os.path.exists(path):
                return public.return_message(0,0,{'is_loacl': True, 'path': path})
        if not hasattr(get, 'cron_id'):
            return public.return_message(-1,0, 'Please pass in cron_id!')
        if "|" not in get.filename:
            return public.return_message(-1,0, 'The file does not exist!')
        cron_data = public.M('crontab').where('id=?', (get.cron_id,)).field('sType,sName,db_type').find()
        cloud_name = get.filename.split('|')[1]
        file_name = get.filename.split('|')[-1]
        names = cron_data['sName'].split(',')
        if names == ['ALL']:
            table = ''
            if cron_data['sType'] == 'site':
                table = 'sites'
            if cron_data['sType'] == 'database':
                table = 'databases'
            if not table:
                return public.return_message(-1,0, 'Data error!')
            names = public.M(table).field('name').select()
            names = [i.get('name') for i in names]

        if cron_data['sType']=="path":
            names = [os.path.basename(i) for i in list(names) if os.path.basename(i) in file_name]
        else:
            names = [i for i in list(names) if i in file_name]

        if not names:
            return public.return_message(-1,0, public.lang('No corresponding file found, please manually download from cloud storage'))
        if  cron_data['db_type']=="redis":
            name="redis"
        else:
            name = names[-1]
        import class_v2.cloud_stora_upload_v2 as CloudStoraUpload
        c = CloudStoraUpload.CloudStoraUpload()
        c.run(cloud_name)
        if c.obj is None:
            return public.return_message(-1,0, public.lang('Cloud storage object not initialized correctly!'))
        url = ''
        backup_path = c.obj.backup_path
        if cron_data['sType'] == 'site':
            path = os.path.join(backup_path, 'site', name)
            data = c.obj.get_list(path)
            for i in data['list']:
                if i['name'] == file_name:
                    url = i['download']

            if not url:
                path = os.path.join(backup_path, 'site')
                data = c.obj.get_list(path)
                for i in data['list']:
                    if i['name'] == file_name:
                        url = i['download']
                        break
        elif cron_data['sType'] == 'database':
            path = os.path.join(backup_path, 'database', cron_data['db_type'], name)
            data = c.obj.get_list(path)
            for i in data['list']:
                if i['name'] == file_name:
                    url = i['download']
                    break
            if not url:
                path = os.path.join(backup_path, 'database')
                data = c.obj.get_list(path)
                for i in data['list']:
                    if i['name'] == file_name:
                        url = i['download']
                        break
        elif cron_data['sType'] == 'path':
            path = os.path.join(backup_path, 'path', file_name.split('_')[1])
            data = c.obj.get_list(path)
            for i in data['list']:
                if i['name'] == file_name:
                    url = i['download']
                    break
        elif cron_data['sType'] == 'mysql_increment_backup':
            # path = os.path.join(backup_path, 'mysql_bin_log', file_name.split('_')[1],'databases')
            if "full" in get.filename:
                path = os.path.join(backup_path, 'mysql_bin_log', file_name.split('_')[2],'databases')
            else:
                path = os.path.join(backup_path, 'mysql_bin_log', file_name.split('_')[1],'databases')
            data = c.obj.get_list(path)
            for i in data['list']:
                if i['name'] == file_name:
                    url = i['download']
                    break
        if url == '':
            return public.return_message(-1,0, public.lang('The file was not found in the cloud storage!'))
        return public.return_message(0,0,{'is_loacl': False, 'path': url})

    def get_crontab_types(self, get):
        data = public.M("crontab_types").field("id,name,ps").order("id asc").select()
        return  public.return_message(0,0,data)

    def add_crontab_type(self, get):
        # get.name =  html.escape(get.name.strip())
        get.name = public.xsssec(get.name.strip())
        if re.search('<.*?>', get.name):
            return public.return_message(-1,0, public.lang("The category name cannot contain HTML statements"))
        if not get.name:
            return public.return_message(-1,0, public.lang("Classification name cannot be empty"))
        if len(get.name) > 16:
            return public.return_message(-1,0, public.lang("The length of the category name cannot exceed 16 digits"))

        crontab_type_sql = public.M('crontab_types')

        if get.name in {"shell script", "Backup website", "Backup Database", "Incremental backup of database", "Log cutting", "backup directory", "Trojan killing", "synchronize", "free memory", "Accessing URL", "System tasks"}:
            return public.return_message(-1,0, "The specified category name already exists")

        if crontab_type_sql.where('name=?', (get.name,)).count() > 0:
            return public.return_message(-1,0, public.lang("The specified category name already exists"))

        # 添加新的计划任务分类
        crontab_type_sql.add("name", (get.name,))

        return public.return_message(0,0, public.lang('Added successfully'))

    def remove_crontab_type(self, get):
        crontab_type_sql = public.M('crontab_types')
        crontab_sql = public.M('crontab')
        crontab_type_id = get.id

        if crontab_type_sql.where('id=?', (crontab_type_id,)).count() == 0:
            return public.return_message(-1,0, public.lang("The specified category does not exist"))

        name = crontab_type_sql.where('id=?', (crontab_type_id,)).field('name').find().get('name', '')
        # if name in {"toShell", "site", "database", "enterpriseBackup", "logs", "path", "webshel", "syncTime", "rememory", "toUrl", "系统任务"}:
        #     return public.returnMsg(False, "这是默认类型，无法删除")

        # 删除指定的计划任务分类
        crontab_type_sql.where('id=?', (crontab_type_id,)).delete()

        # 找到 crontab 表中的相关数据，并设置其 sType 和 type_id 字段为空
        crontab_sql.where('type_id=?', (crontab_type_id,)).save('type_id', (''))

        return public.return_message(0,0, public.lang("Category deleted"))

    def modify_crontab_type_name(self, get):
        get.name = public.xsssec(get.name.strip())
        # get.name =  html.escape(get.name.strip())
        if re.search('<.*?>', get.name):
            return public.return_message(-1,0, public.lang("The category name cannot contain HTML statements"))
        if not get.name:
            return public.return_message(-1,0, public.lang("Classification name cannot be empty"))
        if len(get.name) > 16:
            return public.return_message(-1,0, public.lang("The length of the category name cannot exceed 16 digits"))

        crontab_type_sql = public.M('crontab_types')
        crontab_type_id = get.id

        if crontab_type_sql.where('id=?', (crontab_type_id,)).count() == 0:
            return public.return_message(-1,0, public.lang("The specified category does not exist"))

        if get.name in {"shell script", "Backup website", "Backup Database", "Incremental backup of database", "Log cutting", "backup directory", "Trojan killing", "synchronize", "free memory", "Accessing URL", "System tasks"}:
            return public.return_message(-1,0, public.lang("The name cannot be changed to the default task classification name of the system"))

        if crontab_type_sql.where('name=? AND id!=?', (get.name, crontab_type_id)).count() > 0:
            return public.return_message(-1,0, public.lang("The specified category name already exists"))

        # 修改指定的计划任务分类名称
        crontab_type_sql.where('id=?', (crontab_type_id,)).setField('name', get.name)

        return public.return_message(0,0, public.lang("Modified successfully"))

    def set_crontab_type(self, get):
        try:
            crontab_ids = json.loads(get.crontab_ids)
            crontab_sql = public.M("crontab")
            crontab_type_sql = public.M("crontab_types")

            # sType= public.M('crontab_types').where('id=?', (get['type_id'],)).field('name').find().get('name', '')
            crontab_type_id = get.id
            if crontab_type_id=="-1" or crontab_type_id=="0":
                return public.return_message(-1,0,public.lang("Cannot be set as system classification or default classification!"))
            if crontab_type_sql.where('id=?', (crontab_type_id,)).count() == 0:
                return public.return_message(-1,0, public.lang("The specified category does not exist"))
            for s_id in crontab_ids:
                crontab_sql.where("id=?", (s_id,)).save('type_id', (crontab_type_id))

            return public.return_message(0,0, public.lang("Setting successful!"))
        except Exception as e:
            return public.return_message(-1,0, public.lang("Setting failed" + str(e)))


    def export_crontab_to_json(self, get):
        try:
            # 获取前端发送的id值，可以是逗号分隔的字符串
            task_ids = get.get('ids', None)
            
            if task_ids:
                # 去除方括号和多余的空格
                task_ids = task_ids.strip('[]').replace(' ', '')
                # 将逗号分隔的字符串转换为列表
                task_id_list = task_ids.split(',')
                # 使用where条件和in语句选择对应的计划任务
                crontab_data = public.M('crontab').where('id in ({})'.format(','.join('?' * len(task_id_list))), tuple(task_id_list)).field(self.field).select()
            else:
                crontab_data = public.M('crontab').order("id asc").field(self.field).select()
            
            # 遍历 crontab_data 列表
            # print(crontab_data)
            for task in crontab_data:
                # 将每个任务的 type_id 字段设置为空
                task['type_id'] = ""
                # # 删除 echo 字段
                # if 'echo' in task:
                #     del task['echo']
            
            # 将数据转换为 JSON 格式
            json_data = json.dumps(crontab_data)
            
            # 将 JSON 数据写入文件
            with open('/tmp/cron_task_data.json', 'w') as f:
                f.write(json_data)
            
            return public.returnMsg(True, public.lang("/tmp/cron_task_data.json"))
        except Exception as e:
            return public.returnMsg(False, public.lang("Export failed:" + str(e)))


    def import_crontab_from_json(self, get):
        try:
            file = request.files['file']           
            overwrite = get.get('overwrite') == '1'
            if file:
                json_data = file.read().decode('utf-8')

                try:
                    crontab_data = json.loads(json_data)
                except ValueError as e:
                    return public.returnMsg(False, public.lang("Unable to parse JSON file!"))

                if not isinstance(crontab_data, list):
                    return public.returnMsg(False, public.lang("The JSON file content format is incorrect!"))

                existing_tasks = public.M('crontab').order("id desc").field(self.field).select()
                existing_names = {task['name'] for task in existing_tasks} if overwrite else set()

                successful_imports = 0
                failed_tasks = []
                skipped_tasks = []
                successful_tasks = [] 
                required_keys = [
                    'name', 'type', 'where1', 'where_hour', 'where_minute', 'addtime', 'status', 'save', 'backupTo',
                    'sName', 'sBody', 'sType', 'urladdress', 'save_local', 'notice', 'notice_channel', 'db_type', 'split_type',
                    'split_value', 'keyword', 'post_param', 'flock', 'time_set', 'backup_mode', 'db_backup_path', 'time_type',
                    'special_time', 'user_agent', 'version', 'table_list', 'result', 'log_cut_path', 'rname', 'type_id', 'second','stop_site'
                ]

                for task in crontab_data:
                    if overwrite and task['name'] in existing_names:
                        skipped_tasks.append(task['name'])
                        continue 
                    
                    # 创建新任务字典时，特别处理 where_hour 和 where_minute
                    new_task = {}
                    for key in required_keys:
                        if key == 'where_hour':
                            key='hour'
                            new_task[key] = task.get('where_hour', '')
                        elif key == 'where_minute':
                            key='minute'
                            new_task[key] = task.get('where_minute', '')
                        else:
                            new_task[key] = task.get(key, '')
                    new_task['result'] = 1  # 设置默认 result 为 1
                    result = self.AddCrontab(new_task)
                    if result.get('status', False):
                        successful_imports += 1
                        successful_tasks.append(task['name'])  
                    else:
                        failed_tasks.append(task['name'])

                message = public.lang("Successfully imported {} scheduled tasks",str(successful_imports))
                result = {
                    "status": True,
                    "msg": message,
                    "skipped_tasks": skipped_tasks,
                    "failed_tasks": failed_tasks,
                    "successful_tasks": successful_tasks  
                }
                return result

            else:
                return public.returnMsg(False, public.lang("Please choose to import the file!"))
        except Exception as e:
            return public.returnMsg(False, public.lang("Import failed! {0}",str(e)))

    def stop_cron_task(self, cronPath, cronName, if_stop):
        cronFile = '{}/{}.pl'.format(cronPath,cronName)
        if if_stop == "True":
            if os.path.exists(cronFile):
                try:
                    # 读取文件内容，获取 PID
                    with open(cronFile, 'r') as file:
                        pid = file.read().strip()
                    os.system('kill -9 {}'.format(pid))
                    os.remove(cronFile)
                except:
                    pass

    def set_atuo_start_syssafe(self, get):
        try:
            if not hasattr(get, 'time'):
                return public.returnMsg(False, public.lang("Please pass in the time parameter!"))
            time = int(get.time)
            public.ExecShell('/etc/init.d/bt_syssafe stop')
            data = {
                'type': 2,
                'time': time,
                'name': 'syssafe',
                'title': 'Reinforcement of Pagoda System',
                'fun': 'set_open',
                'args': {
                    'status': 1
                }
            }
            public.set_tasks_run(data)
            return public.returnMsg(True, public.lang("Temporary system shutdown and reinforcement successful!"))
        except Exception as e:
            public.ExecShell('/etc/init.d/bt_syssafe start')
            return public.returnMsg(False, public.lang("Temporary shutdown of system reinforcement failed!" + str(e)))

    def set_atuo_start_syssafe(self, get):
        try:
            if not hasattr(get, 'time'):
                return public.returnMsg(False, public.lang("Please pass in the time parameter!"))
            time = int(get.time)
            public.ExecShell('/etc/init.d/bt_syssafe stop')
            data = {
                'type': 2,
                'time': time,
                'name': 'syssafe',
                'title': 'Reinforcement of Pagoda System',
                'fun': 'set_open',
                'args': {
                    'status': 1
                }

            }
            public.set_tasks_run(data)
            return public.returnMsg(True, public.lang("Temporary system shutdown and reinforcement successful!"))
        except Exception as e:
            return public.returnMsg(False, public.lang("Temporary shutdown of system reinforcement failed!" + str(e)))

    def set_rotate_log(self, get):
        try:
            p = crontab()
            task_name = '[Do not delete] Cut plan task log'
            status = get.status
            numbers = get.numbers

            public.M('crontab').where('name=?', (task_name,)).setField('status', status)
            public.M('crontab').where('name=?', (task_name,)).setField('save', numbers)
            # public.M('crontab').where('name=?', (task_name,)).setField('sBody', sBody)
            if get.status == "1":

                return public.returnMsg(True, public.lang("Successfully enabled log cutting"))
            else:
                return public.returnMsg(True, public.lang("Successfully closed log cutting"))
            # print("开启日志切割成功")
        except Exception as e:
            return public.returnMsg(False, public.lang("Failed to enable log cutting" + str(e)))
            # print("开启日志切割失败 ")

    def get_rotate_log_config(self, get):
        try:
            p = crontab()
            task_name = '[Do not delete] Cut plan task log'

            if public.M('crontab').where('name=?', (task_name,)).count() == 0:
                task = {
                    "name": task_name,
                    "type": "day-n",
                    "where1": "1",
                    "hour": "0",
                    "minute": "0",
                    "week": "",
                    "sType": "toShell",
                    "sName": "",
                    "backupTo": "",
                    "save": "10",
                    "sBody": "btpython /www/server/panel/script/rotate_log.py 10",
                    "urladdress": "",
                    "status": "1"
                }
                p.AddCrontab(task)
            crontab_data = public.M('crontab').where('name=?', (task_name,)).select()[0]
            status = crontab_data['status']
            numbers = crontab_data['save']
            info = {"status": status, "numbers": numbers}
            return public.returnMsg(True, info)
        except Exception as e:
            return public.returnMsg(False, public.lang("Acquisition failed：" + str(e)))

    def get_restart_project_config(self, get):
        try:
            # import sys
            # sys.path.append("..")  # 添加上一级目录到系统路径
            # import crontab
            # import public
            model_name=get.model_name
            project_name=get.project_name
            task_name = '[Do Not Delete] Scheduled Restart {} Project {}'.format(model_name,project_name)
            sBody='btpython /www/server/panel/script/restart_project.py {} {}'.format(model_name,project_name)
            public.M('crontab').where('name=?', (task_name,)).select()
            if public.M('crontab').where('name=?', (task_name,)).count() == 0:
                task = {
                    "name": task_name,
                    "type": "day",
                    "where1":"" ,
                    "hour": "0",
                    "minute":"0",
                    "week": "",
                    "sType": "toShell",
                    "sName": "",
                    "backupTo": "",
                    "save": "10",
                    "sBody": sBody,
                    "urladdress": "",
                    "status":"0"
                }
                crontab().AddCrontab(task)
                public.M('crontab').where('name=?', (task_name,)).setField('status', 0)
            crontab_data_list = public.M('crontab').where('name=?', (task_name,)).select()
            if crontab_data_list:
                crontab_data = crontab_data_list[0]
                status = crontab_data['status']
                return public.success_v2(crontab_data)
            else:
                return public.fail_v2(public.lang("Failed to create scheduled task {}, please check if system hardening is enabled or disk condition",task_name))
        except Exception as e:
            return public.fail_v2(public.lang("Acquisition failed："+str(e)))

    def set_restart_project(self,get):
        try:
            status=get.status
            hour = get.get('hour', 0)
            minute = get.get('minute', 0)
            model_name=get.model_name
            project_name=get.project_name
            task_name = '[Do Not Delete] Scheduled Restart {} Project {}'.format(model_name,project_name)

            crontab_data_list = public.M('crontab').where('name=?', (task_name,)).select()
            if crontab_data_list:
                public.M('crontab').where('name=?', (task_name,)).setField('status', status)
                public.M('crontab').where('name=?', (task_name,)).setField('where_hour', hour)
                public.M('crontab').where('name=?', (task_name,)).setField('where_minute', minute)
                if get.status=="1":
                    return public.success_v2(public.lang("Successfully opened"))
                else:
                    return public.success_v2(public.lang("Successfully closed"))
            else:
                return public.fail_v2(public.lang("Failed to set scheduled task {}, please check if system hardening is enabled or disk condition",task_name))
        except Exception as e:
            return public.fail_v2(public.lang("Setting failed："+str(e)))


    def modify_values(self, cronName, new_time_type, new_special_time, new_time_list):
        cronName = cronName 
        cronPath = '/www/server/cron'  
        cronFile = '{}/{}'.format(cronPath, cronName)
        # 打开文件
        with open(cronFile, 'r') as file:
            # 读取文件内容
            lines = file.readlines()

        # 进行你的修改
        for i, line in enumerate(lines):
            if "btpython /www/server/panel/script/time_check.py" in line:
                lines[i] = 'if ! btpython /www/server/panel/script/time_check.py time_type={} special_time={} time_list={}; then\n'.format(new_time_type, new_special_time, new_time_list)

        # 保存修改
        with open(cronFile, 'w') as file:
            file.writelines(lines)

    def set_execute_script(self, get):

        # get._ws.send(public.getJson({
        #     "result": False,
        # }))
        if '_ws' not in get:
            return False

        public.ExecShell("chmod +x /www/server/panel/script/check_crontab.sh")
        # 使用nohup运行脚本，并将输出重定向到/www/test.txt，同时确保命令在后台运行
        exec_result=public.ExecShell("nohup /www/server/panel/script/check_crontab.sh > /www/check_crontab.txt 2>&1 &")
        if exec_result:  # 假设ExecShell返回的第一个元素是成功与否的标志
            # 以只读模式打开日志文件，并移动到文件末尾
            if not os.path.exists("/www/check_crontab.txt"):
                public.writeFile("/www/check_crontab.txt", "")
            with open("/www/check_crontab.txt", "r") as log_file:
                while True:
                    # 读取新的一行
                    line = log_file.readline()
                    if not line:
                        time.sleep(1)  # 如果没有新内容，则稍等片刻再尝试读取
                        continue
                    # 发送新读取的行
                    # print(line.strip())
                    get._ws.send(public.getJson({
                    "callback":"set_execute_script",
                    "result":line.strip()

                    }))
                    # get._ws.send(line.strip())  # 使用strip()移除行尾的换行符
                    if line.strip()=="successful":
                        break
            return True
        else:
            get._ws.send(public.lang("Script execution failed"))
            return False
    def get_system_user_list(self, get):
        all_user = False
        if get is not None:
            if hasattr(get, "all_user"):
                all_user = True
        root_and_www = []  # 新增列表存储root和www用户
        other_users = []   # 新增列表存储其他用户
        with open('/etc/passwd', 'r', encoding='utf-8', errors='ignore') as fp:
            for line in fp.readlines():
                tmp = line.split(':')
                if len(tmp) > 2:  # 检查 tmp 是否有足够的元素
                    user_name = tmp[0]
                    try:
                        uid = int(tmp[2])
                    except ValueError:
                        continue  # 如果 uid 不是有效的整数，跳过这行
                    if user_name in ['root', 'www']:
                        root_and_www.append(user_name)  # 直接添加到root和www列表
                        continue
                    if uid == 0:
                        other_users.append(user_name)
                        continue
                    if uid >= 1000:
                        other_users.append(user_name)
                        continue
                    if all_user:
                        other_users.append(user_name)  # 添加所有用户（根据all_user标志）

        # 合并列表，确保root和www在前
        return root_and_www + list(set(other_users) - set(root_and_www))

    # 增量备份获取数据库信息
    def get_databases(self, get):
        from panelMysql import panelMysql
        # try:
        #     import PluginLoader
        #     return PluginLoader.module_run('binlog', 'get_databases', get)
        # except Exception as err:
        #     return {"status":False, "msg": err}
        try:
            database_list = public.M("databases").field("name").where("sid=0 and LOWER(type)=LOWER(?)", ("mysql")).select()
            for database in database_list:
                database["value"] = database["name"]
                cron_id = public.M("mysql_increment_settings").where("db_name=?", (database["name"])).getField("cron_id")
                database["cron_id"] = cron_id if cron_id else None

                table_list = panelMysql().query("show tables from `{db_name}`;".format(db_name=database["name"]))
                if not isinstance(table_list, list):
                    continue
                cron_id = public.M("mysql_increment_settings").where("tb_name == ''", ()).getField("cron_id")
                database["table_list"] = [{"tb_name": "all", "value": "", "cron_id": cron_id if cron_id else None}]
                for tb_name in table_list:
                    cron_id = public.M("mysql_increment_settings").where("tb_name in (?)", (tb_name[0])).getField("cron_id")
                    database["table_list"].append({"tb_name": tb_name[0], "value": tb_name[0], "cron_id": cron_id if cron_id else None})
            return {"status": True, "msg": "ok", "data": database_list}
        except Exception as err:
            return {"status":False, "msg": err}

    @staticmethod
    def _cron_name_to_config_type(name: str) -> tuple[str, str]:
        if name == "mysql":  # backup all the mysql
            sType, db_type = "database", "mysql"
        elif name == "site":  # backup all the site
            sType, db_type = "site", "mysql"
        else:
            sType, db_type = "", ""
        return sType, db_type

    # 获取自动备份配置状态信息
    def get_auto_config(self, get):
        try:
            get.validate([
                Param('name').Require().String(),
            ], [
                public.validate.trim_filter(),
            ])
        except Exception as ex:
            return public.return_message(-1, 0, str(ex))

        name = get.name
        sType, db_type = self._cron_name_to_config_type(name)
        if not sType or not db_type:
            return public.success_v2({"status": 0})

        crontab_data_list = public.M('crontab').where('sType=? AND db_type=?', (sType, db_type)).select()
        if isinstance(crontab_data_list, str):
            return public.fail_v2(f"Query failed, {crontab_data_list}")
        crontab_data = crontab_data_list[0] if crontab_data_list else {"status": 0}
        return public.success_v2(crontab_data)

    # 设置自动备份配置状态
    def set_auto_config(self, get):
        try:
            get.validate([
                Param('name').Require().String(),
            ], [
                public.validate.trim_filter(),
            ])
        except Exception as ex:
            return public.return_message(-1, 0, str(ex))

        name = get.name
        sType, db_type = self._cron_name_to_config_type(name)
        if not sType or not db_type:
            return public.fail_v2("Set failed!")
        if public.M('crontab').where('sType=? AND db_type=?', (sType, db_type)).count() == 0:
            task_name_map = {
                "mysql": public.lang("Auto Backup Database[ALL]--mysql"),
                "site": public.lang("Auto Backup Site[ALL]"),
            }
            task_info = {
                "name": task_name_map.get(name),
                "type": "day",
                "where1": "1",
                "week": "1",
                "hour": "1",
                "minute": "30",
                "second": "",
                "save": "3",
                "backupTo": "localhost",
                "sName": "ALL",
                "sBody": "",
                "sType": sType,
                "urladdress": "http://",
                "save_local": "0",
                "db_type": db_type,
                "notice": 0,
                "notice_channel": "",
            }
            res = crontab().AddCrontab(task_info)
            if res.get("status") == 0:
                return public.success_v2("set success!")
            else:
                return public.fail_v2("set failed!")
        else:
            pk = {
                "id": public.M('crontab').where('sType=? AND db_type=?', (sType, db_type)).getField('id')
            }
            return crontab().set_cron_status(public.to_dict_obj(pk))
