BtRepository.php 8.4 KB
<?php
/**
 * Created by PhpStorm.
 * User: zhl
 * Date: 2022/11/1
 * Time: 11:56
 */

namespace App\Repositories;

use App\Models\BtSites;
use App\Repositories\Bt\Bt;
use Illuminate\Support\Facades\Cache;

/**
 * Class BtRepository
 * @package App\Repositories
 */
class BtRepository
{
    public $instance = [];

    /**
     * @param $domain
     * @param $ssl_open
     * @param $ssl_auto
     * @param $ssl_auto_day
     * @param int $ssl_status
     * @return array|bool
     */
    public function createBtSite($domain, $ssl_open, $ssl_auto, $ssl_auto_day, $ssl_status = 0)
    {
        $domain_array = parse_url($domain);
        $host = $domain_array['host'];

        // 如果站点已经存在, 更新数据
        $site = BtSites::getSiteByDomain($host);
        if ($site) {
            $result = BtSites::createBtSite($host, $site->site_id, $ssl_open, $ssl_status, $ssl_auto, $ssl_auto_day);
            return $result->toArray();
        }

        $host_array = explode('.', $host);
        if (FALSE == empty($host_array[0]) && $host_array[0] == 'www')
            unset($host_array[0]);
        $domain_top = implode('.', $host_array);

        $web_name = ['domain' => $host, 'domainlist' => [$domain_top], 'count' => 0];
        $array = [
            'webname' => json_encode($web_name),
            'path' => env('PROJECT_PATH') ? env('PROJECT_PATH') : dirname(dirname(app_path())) . DIRECTORY_SEPARATOR . $domain,
            'type_id' => 0,
            'type' => 'PHP',
            'version' => '74',
            'port' => '80',
            'ps' => $host,
            'ftp' => false,
            'ftp_username' => '',
            'ftp_password' => '',
            'sql' => false,
            'codeing' => '',
            'datauser' => '',
            'datapassword' => '',
        ];

        $bt = $this->getBtObject();
        $result = $bt->AddSite($array);
        // result 返回信息
        // $result = ['siteStatus' => true, 'siteId' => 39, 'ftpStatus' => false, 'databaseStatus' => false,];
        if (empty($result) || empty($result['siteId']))
            return false;
        // 伪静态设置
        $htaccess = '# SEO URL Settings
              # Nginx configuration of OC htaccess
              # location = /sitemap.xml {
              #   rewrite ^(.*)$ /index.php?route=feed/google_sitemap break; 
              # } 
            
              #location = /googlebase.xml {
              #  rewrite ^(.*)$ /index.php?route=feed/google_base break; 
              #} 
            
              location / {
                # This try_files directive is used to enable SEO-friendly URLs for OpenCart
                try_files $uri $uri/ @opencart;
                # 隐藏index.php
                if (!-e $request_filename) {
                    rewrite  ^/(.*)$ /index.php?$1 last;
                }
              } 
            
              location @opencart {
                rewrite ^/(.+)$ /index.php?_route_=$1 last;
              }
              # End SEO settings';
        $bt->SaveFileBody($host, $htaccess);

        $result = BtSites::createBtSite($host, $result['siteId'], $ssl_open, $ssl_status, $ssl_auto, $ssl_auto_day);
        $site = $result->toArray();
        unset($site['id']);
        unset($site['created_at']);
        unset($site['updated_at']);
        unset($site['is_del']);
        return $site;
    }

    /**
     * 删除站点
     * @param $domain
     * @return bool
     */
    public function deleteBtSite($domain)
    {
        $domain_array = parse_url($domain);
        $host = $domain_array['host'];

        $bt = $this->getBtObject();

        $site = BtSites::getSiteByDomain($host);
        // $id = $site->site_id;
        // 获取bt数据 可能要可靠一些
        $result = $bt->Websites($host);
        if (empty($result['data']))
            return true;
        $id = 0;
        foreach ($result['data'] as $v) {
            if ($v['name'] == $host) {
                $id = $v['id'];
                break;
            }
        }

        if (empty($id))
            return false;
        $result = $bt->WebDeleteSite($id,$host,false,false,false);
        // $result 返回信息
        // $result = ['status' => true, 'msg' => '站点删除成功!',]
        if (empty($result['status']))
            return false;

        if ($site) {
            $site->is_del = BtSites::IS_DEL_TRUE;
            $site->save();
        }
        return true;
    }

    /**
     * 申请ssl 并设置ssl访问
     * @param $host
     * @return bool
     */
    public function applySsl($host)
    {
        $site = BtSites::getSiteByDomain($host);
        if (empty($site) || empty($site->ssl_open))
            return false;

        $bt = $this->getBtObject();
        // $domains = [["id" => 71, "pid" => 35, "name" => "www.shopk.com", "port" => 80, "addtime" => "2022-11-03 14:22:08",]];
        $domain_list =  $bt->WebDoaminList($site->site_id);
        // 被删除的站点 返回空数组 不做数据删除同步 可能bt接口问题
        if (empty($domain_list))
            return false;

        // $result = ["status" => false, "msg" => "指定网站配置文件不存在!"];
        // $result = ["status" => true, "oid" => -1, "domain" => [["name" => "*.shopk.com"],["name" => "www.shopk.com"]], "key" => "key", "csr" => "csr", "type" => 0, "httpTohttps" => true,
        //    "cert_data" => ["issuer" => "TrustAsia TLS RSA CA", "notAfter" => "2023-02-22", "notBefore" => "2022-02-21", "dns" => [0 => "*.shopk.com", 1 => "shopk.com"],
        //    "subject" => "*.shopk.com", "endtime" => 110], "email" => "test@message.com", "index" => null, "auth_type" => "http", "push" => ["status" => false]];
        $ssl = $bt->GetSSL($host);
        // 已经有ssl证书了
        if (FALSE == empty($ssl['key'])) {
            $bt->SetSSL('1', $host, $ssl['key'], $ssl['csr']);
            // 开启强制https
            // $bt->HttpToHttps($host);
            $site->ssl_status = 1;
            $site->save();
            return true;
        }


        if (Cache::get('apply_ssl_' . $host))
            return false;
        $domain = array_column($domain_list, 'name');
        $result = $bt->ApplyCert(json_encode($domain), $site->site_id);
        if (empty($result) || empty($result['cert'])) {
            // 创建失败 host冻结15分钟
            Cache::put('apply_ssl_' . $host, 1, 890);
            return false;
        }

        $bt->SetSSL('1', $host, $result['private_key'], $result['cert']);
        // 开启强制https
        // $bt->HttpToHttps($host);
        $site->ssl_status = 1;
        $site->save();
        return true;
    }

    /**
     * 续签ssl
     * @param $host
     * @return bool
     */
    public function renewalSsl($host)
    {
        $site = BtSites::getSiteByDomain($host);
        if (empty($site) || empty($site->ssl_open) || empty($site->ssl_auto))
            return false;

        $bt = $this->getBtObject();
        // $result = ["status" => false, "oid" => -1, "domain" => [["name" => "test.hagro.cn"], ["name" => "test.hagro.cn"]], "key" => false, "csr" => false,
        //      "type" => -1, "httpTohttps" => false, "cert_data" => [], "email" => "test@message.com", "index" => "", "auth_type" => "http", "push" => ["status" => false]];
        $ssl = $bt->GetSSL($host);
        if (empty($ssl['status']))
            return $this->applySsl($host);

        $end = strtotime($ssl["cert_data"]["notAfter"]);

        if ($end - time() >= $site->ssl_auto_day * 86400)
            return true;

        $domain_list =  $bt->WebDoaminList($site->site_id);
        // 被删除的站点 返回空数组 不做数据删除同步 可能bt接口问题
        if (empty($domain_list))
            return false;

        // 不是宝塔生成的ssl 不能续签, 直接申请ssl
        $result = $bt->RenewCert($ssl["index"]);
        if (empty($result['status']))
            return $this->applySsl($host);

        $bt->SetSSL(1, $host, $result['private_key'], $result['cert']);
        return true;
    }

    /**
     * 获取bt对象
     * @param string $key
     * @param string $panel
     * @return Bt
     */
    public function getBtObject($key = '', $panel = '')
    {
        $key = $key ?: env('BT_KEY');
        $panel = $panel ?: env('BT_PANEL');
        $object_key = md5($key . $panel);
        if (empty($this->instance[$object_key])) {
            $bt = new Bt($panel, $key);
            $this->instance[$object_key] = $bt;
        }
        return $this->instance[$object_key];
    }
}