2024年6月

效果如下:
{11CA8F1E-EEF8-4cdd-9D07-D285C625402E}.png
{81A103AC-8943-43b1-A8CB-40B317050042}.png

  • 1.主题functions下添加:
function display_product_container($title, $describe, $coverImg,$downloadHtml) {
    // 生成1到10之间的随机整数
    $random_m = rand(1, 10);
    
    echo '<div class="product-container" style="opacity: 0;">
        <div class="product-box relative" >
            <div class="product-background absolute"  style="
            height: 100%;
            width: 100%;
            background-image: url(' . $coverImg . ');
            z-index: -1;
            content: &quot;&quot;;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            filter: blur(20px);
            position: absolute;
            "></div>
            <div class="product-row relative">
                <div class="payrow-6 payrow-left">
                    <div class="relative zib-slider">
                        <div>
                            <div  aria-live="polite" style="transition-duration: 0ms;">
                                <div class="imgfront"  data-swiper-slide-index="0" style="width: 100%;" role="group" aria-label="1 / 3">
                                    <span>
                                        <img  src="' . esc_url($coverImg) . '" >
                                    </span>
                                </div>
                            </div>
                            
                        </div>
                    </div>
                </div>
                <div class="payrow-6 payrow-right">
                    <div class="pay-content">
                        <div class="product-header">' . esc_html($title) . '</div>
                        <div class="product-doc">PPT介绍: ' . esc_html($describe) . '</div>
                    </div>
                    <div class="more-but text-center">
    <a target="_self" class="but hollow c-white" href="mailto:upptx@hotmail.com" data-original-title="发邮件" data-toggle="tooltip">联系作者</a>
    <a target="_self" class="but hollow c-white" href="mailto:upptx@hotmail.com" data-original-title="发邮件" data-toggle="tooltip">侵权投诉</a>
    <a target="_self" class="but hollow c-white" href="#" data-original-title="广告" data-toggle="tooltip">为爱发电</a>
</div>
               '. $downloadHtml .'
                </div>
                                    
            </div>
        </div>
    </div>';
}
add_action('qk_product_container', 'display_product_container', 10, 4);





  • 然后找到qkua\qkua\Modules\Templates\Single.php

搜索get_breadcrumbs()

替换这个function为:

  //获取文章内面包屑导航
    public static function get_breadcrumbs(){
        
        if(!qk_get_option('single_breadcrumb_open')) return;
        
        $categorys = get_the_category();
        $html = '';
        
        if ($categorys) {
            $category = $categorys[0];
            $html      .= '
            <ul class="breadcrumb wrapper">
                <li><a href="' . get_bloginfo('url') . '">首页</a></li>
                <li>' . get_category_parents($category->term_id, true,'</li><li>').'正文</li>
             </ul>';
        }
        $post_id = get_the_id();
        $thumb = Post::get_post_thumb($post_id);
        $title1 = get_the_title(); // 获取当前文章标题

        // 找到第一个 '-' 的位置
        $pos = strpos($title1, '-');

        if ($pos !== false) {
        // 获取第一个 '-' 前面的部分
        $title = substr($title1, 0, $pos);
        } else {
        // 如果没有找到 '-', 保持原始标题
        $title = $title1;
        }
        
        $describe = $title1.",是为各类学术和专业展示而设计的,旨在提供一个结构清晰、视觉效果出色且易于使用的展示工具。无论您是进行论文答辩、课程讲解、项目汇报,还是参与学术会议,这款PPT模板都能满足您的需求。";
        $coverImg =$thumb;
        
        
        $post_id = get_the_ID();
        
        $download_open = get_post_meta($post_id,'qk_single_post_download_open',true);
        //是否开启文章下载功能
        if(!$download_open) return;
        
        $download_data = get_post_meta($post_id,'qk_single_post_download_group',true);
        $download_data = is_array($download_data) ? $download_data : array();
        $download_data = apply_filters('filter_download_data',$download_data,$post_id);

        if(empty($download_data) || !is_array($download_data)) return;
        
        $tabs = '';
        
        if(count($download_data) > 1) {
            $tabs .= '<div class="scroll-tabs-wrapper" ref="scrollTab">
                        <ul class="tabs-content">';
            foreach ($download_data as $key => $value) {
                
                $title = isset($value['title']) ? $value['title'] : get_the_title($post_id);
                
                $tabs .= '<li class="tab-item" :class="[{\'active\':'.$key.' === index}]" @click="changeTab('.$key.')">
                                <div class="thumb">'.(isset($value['thumb']) && $value['thumb'] ? '<img src="'.qk_get_thumb(array('url'=>$value['thumb'],'width'=>40,'height'=>40)).'" />' : '<b>'.($key + 1).'</b>').'</div>
                                <span class="text-ellipsis">'.$title.'</span>
                            </li>';
            }
            
            $tabs .= '</ul>
                    </div>';
        }
        
        $downloadHtml= '
            <div class="post_download mg-a"   style="margin:30px 0px 20px 0px"  >
                <div id="download-box" class="download-box box qk-radius" style="background-color:revert; " ref="downloadBox">
                    '.$tabs.'
                    <div class="download-list"  style="color: white"  v-if="data" v-cloak>
                        <div class="download-list-item">
                            <div class="attrs-list" v-if="data.attrs.length">
                                <div class="attr-item" v-for="(item,index) in data.attrs">
                                    <span>{{item.key}}:</span>
                                    <span>{{item.value}}</span>
                                </div>
                            </div>
                            <div class="rights"   style="background-color:revert;padding:0px; " >
                                <div @click="show = !show" :class="[{open:show}]">
                                    <div class="current-user"  v-if="!data.current_user.can.allow">
                                        <span v-if="data.current_user.can.type == \'money\'">需支付 <span style=" color: var(--color-primary); ">¥</span> <span style=" font-size: 20px; color: var(--color-primary); " v-text="data.current_user.can.value">28</span>
                                        </span>
                                        <span v-if="data.current_user.can.type == \'credit\'">需支付 <span style=" font-size: 20px; color: var(--color-primary); " v-text="data.current_user.can.value"></span><span style=" color: var(--color-primary); "> 积分</span>
                                        </span>
                                        <span v-if="data.current_user.can.type == \'free\'">免费下载</span>
                                        <span v-if="data.current_user.can.type == \'comment\'">评论后下载</span>
                                        <span v-if="data.current_user.can.type == \'login\'">登录后下载</span>
                                        <span v-if="data.current_user.can.type == \'password\'">输入密码下载</span>
                                        <span v-if="data.current_user.can.type == \'none\'">当前为指定权限用户下载</span>
                                        <i class="ri-arrow-right-s-line"></i>
                                    </div>
                                    <div class="current-user" v-else>
                                        <span v-if="data.current_user.can.free_count">今日免费下载剩余 <span style=" font-size: 20px; color: var(--color-primary); ">{{data.current_user.can.free_count}}</span> 次 </span>
                                        <span v-else>已获得下载权限</span>
                                    </div>
                                    <div class="download-btn">
                                        <button style=" padding: 6px 16px; " @click.stop="go()">
                                            <i class="ri-download-fill"></i>下载
                                        </button>
                                    </div>
                                </div>
                                <ul class="list" v-if="show">
                                    <li class="item" v-for="(item,index) in data.rights" :class="item.lv == data.current_user.lv.lv.lv || item.lv == data.current_user.lv.vip.lv ? \'current\' : \'\'">
                                        <div>
                                            <span>{{item.name}}</span>
                                        </div>
                                        <div>
                                            <span v-if="item.type == \'money\'">¥<span v-text="item.value"></span></span>
                                            <span v-if="item.type == \'credit\'"><span v-text="item.value"></span> 积分</span>
                                            <span v-if="item.type == \'free\'">免费下载</span>
                                            <span v-if="item.type == \'comment\'">评论后下载</span>
                                            <span v-if="item.type == \'login\'">登录后下载</span>
                                            <span v-if="item.type == \'password\'">输入密码下载</span>
                                        </div>
                                    </li>
                                </ul>
                            </div>
                            <!--<div class="qk-flex" style="
    margin: 12px;
    width: 100%;
    font-size: 12px;
    color: var(--color-text-secondary);
    line-height: 12px;
"><i class="ri-error-warning-line" style="
    font-size: 14px;
    margin-right: 2px;
"></i>尊贵的VIP用户您每日免费下载资源次数,剩余 <span class="font-number" style="
    color: var(--color-primary);
">19</span> 次                        </div>-->
                        </div>
                    </div>
                </div>
            </div>
            <!--#资源下载-->
        ';
        echo do_action('qk_product_container', $title, $describe, $coverImg,$downloadHtml).$html;
    }
    
    
  • 添加css
 /*文章页面顶部css开始*/
 
:root {
    --linear-bg-1: radial-gradient(#ba6187 40%, rgb(144 224 255 / 0%) 60%) -620px -180px no-repeat, radial-gradient(#743fc3d6 33%, rgb(255 203 87 / 0%) 67%) -120px -24px no-repeat, radial-gradient(#b4684e 40%, rgb(144 224 255 / 0%) 70%) -470px 150px no-repeat, #9e4fa9;
    --linear-bg-2: radial-gradient(#24868994 20%, rgb(134 217 249 / 0%) 60%) -120px -180px no-repeat, radial-gradient(#454faa 33%, rgb(255 203 87 / 0%) 67%) -10px -4px no-repeat, radial-gradient(#7238ae 40%, rgb(144 224 255 / 0%) 70%) -470px 150px no-repeat, #4147b8;
    --linear-bg-3: radial-gradient(#bb552975 23%, rgb(247 197 86 / 0%) 67% 100%) 385px -24px, radial-gradient(#984cbe 30%, rgb(179 105 248 / 0%) 67% 100%) 122px -120px, radial-gradient(#661cac 15%, rgb(134 219 252 / 0%) 50% 100%) -620px 0, radial-gradient(#383d9ff5 25%, rgb(132 215 247 / 0%) 50% 100%) 520px -250px, #6841c0;
    --linear-bg-4: radial-gradient(#3a7ebe 20%, rgb(134 217 249 / 0%) 60%) -100px -300px no-repeat, radial-gradient(#405ed6 13%, rgb(255 203 87 / 0%) 50%) 300px -124px no-repeat, radial-gradient(#684ec8 40%, rgb(144 224 255 / 0%) 70%) 40px 18px no-repeat, #246d79;
    --linear-bg-5: radial-gradient(#865cc2 40%, rgb(144 224 255 / 0%) 60%) -620px -180px no-repeat, radial-gradient(#4956c6 33%, rgb(255 203 87 / 0%) 67%) -120px -24px no-repeat, radial-gradient(#3e77ab 40%, rgb(144 224 255 / 0%) 64%) 100px 150px no-repeat, #1f5b98;
    --linear-bg-6: radial-gradient(#277367 20%, rgb(134 217 249 / 0%) 60%) -120px -180px no-repeat, radial-gradient(#434ead 33%, rgb(255 203 87 / 0%) 67%) -120px -24px no-repeat, radial-gradient(#5b46a8 40%, rgb(144 224 255 / 0%) 70%) -470px 150px no-repeat, #1b4d89;
    --linear-bg-7: radial-gradient(#29843fba 23%, rgb(247 197 86 / 0%) 67% 100%) -285px -24px, radial-gradient(#365692 0, rgb(251 63 72 / 0%) 60% 100%) -40px 290px, #29727b;
    --linear-bg-8: radial-gradient(#c75c6e 23%, rgb(247 197 86 / 0%) 67% 100%) -285px -24px, radial-gradient(#bf7830 25%, rgb(132 215 247 / 0%) 50% 100%) 520px -250px, #b4663d;
    --linear-bg-9: radial-gradient(#4776c3 40%, rgb(144 224 255 / 0%) 60%) -235px -380px no-repeat, radial-gradient(#6e4cac 40%, rgb(144 224 255 / 0%) 70%) 100px 250px no-repeat, #5c5bdf;
    --linear-bg-10: radial-gradient(#b06553 23%, rgb(247 197 86 / 0%) 67% 100%) -285px -24px, radial-gradient(#9e4353 0, rgb(251 63 72 / 0%) 60% 100%) -540px 90px, radial-gradient(#8e43b6 30%, rgb(179 105 248 / 0%) 67% 100%) -122px -120px, #a75375;

    --linear-bg-1m: linear-gradient(135deg, #2ea2aa 0%, #5038c8 100%);
    --linear-bg-2m: linear-gradient(135deg, #3f41a7 0%, #27acd1 100%);
    --linear-bg-3m: linear-gradient(135deg, #9971d3 0%, #4538c8 100%);
    --linear-bg-4m: linear-gradient(110deg, #6936b3 0%, #228382 100%);
    --linear-bg-5m: linear-gradient(117deg, #e987f2 0%, #6f2d94 100%);
    --linear-bg-6m: linear-gradient(117deg, #845eea 0%, #cd3e6f 100%);
    --linear-bg-7m: linear-gradient(117deg, #e29a78 0%, #ce5656 100%);
    --linear-bg-8m: linear-gradient(117deg, #77ad5e 0%, #20778f 100%);
    --linear-bg-9m: linear-gradient(117deg, #51a364 0%, #166e9e 100%);
    --linear-bg-10m: linear-gradient(117deg, #dc3576 0%, #1b80b7 100%);
}

.product-container {
    opacity: 1 !important;
    transition: opacity .5s;
}

.pay-content {
    max-width: 390px;
}

.post_download.mg-a {
    max-width: 390px;
}

.download-list-item .attrs-list .attr-item {
    width: revert;
    font-size: 14px;
}

.product-box {
    padding: 40px 0 40px 0;
    color: #fff;
    position: sticky;
}


.product-box .product-row {
    max-width: 1200px;
    margin: auto;
    display: flex;
    justify-content: space-between;
}

.product-row .payrow-6 {
    display: inline-block;
    vertical-align: middle;
    width: calc(50%);
    display: revert;
    align-self: center;
}

.payrow-6.payrow-left img{
     height: 565px;
     width: 400px;
    }

.product-header {
    font-size: 50px;
    font-family:'zti' !important;
    letter-spacing: .1em;
}

.product-doc{
    margin-top: 10px ;
}

.more-but {
    margin-top: 30px;
    display: flex;
    justify-content: space-between;
    max-width: 390px;
}

.more-but .but.hollow {
    padding: .5em 1.5em;
}

.more-but .but.hollow:hover {
    color: #555;
}

.but{
    border-radius: 4px;
    display: inline-block;
    border-radius: 4px;
    transition: .15s;
    border: 1px solid var(--color-text-secondary);
}
.article-report {
    display: none;
}

.post_download.mg-b {
    display: none;
}
.payrow-6.payrow-right{
color: #fff; /* 白色字体 */
text-shadow: 1px 1px 2px #000; /* 黑色阴影 */

}
.download-list-item .attrs-list .attr-item span:first-of-type {
    color: #fff;
    text-shadow: 1px 1px 2px #000;
}

.download-list-item {
    border: 1px solid var(--color-text-secondary);
    border-radius: var(--radius);
    overflow: hidden;
    color: #fff; /* 白色字体 */
    text-shadow: 1px 1px 2px #000; /* 黑色阴影 */
}
@media (max-width:768px) {
.product-box {
    padding: 0px;
}
    .payrow-6.payrow-left img{
     margin-top: 0 !important;
    }

    .product-box .product-row {
        padding: 0;
    }

  
    .product-box .product-row {
    text-align: center;
    margin: auto;
    display: revert;
    }
    
    


    .product-row .payrow-6 {
        padding: 10px;
        width: 100%;
    }

    .payrow-6.payrow-left {
        margin-bottom: 20px;
        padding: 0;
    }

    .payrow-6.payrow-left .radius8 {
        border-radius: 0;
    }
 .more-but {
        margin-top: 30px;
        margin-bottom: 30px;
    }
    
.payrow-6.payrow-left img {
    vertical-align: top;
    position: absolute;
    top: 0;
    left: 0;
    width: 400px;
    height: auto;
}

.imgfront{
    margin-right: 15px;
    height: 630px;
    overflow: hidden;
    position: relative;
}
.product-header {
    font-size: 33px;
    letter-spacing: .1em;
    margin-bottom: 20px;
    margin-top: -70px;
}
.product-container {
    margin-top: -16px;
}
}
  
 /*文章页面顶部css结束*/
 

  

以上一些细节自行修改。

前言:群晖自带的 Docker 容器中有很多宝塔面板的版本,并且环境已经预装好。然而,一般在创建容器后会遇到各种错误。因此,最佳的方式是创建一个 CentOS 容器,然后在其中拉取官方版本的宝塔面板。

步骤一:拉取 CentOS 镜像

  1. 进入 Docker 注册表,搜索并拉取 CentOS 镜像。

f2e78acb6a46a2396c18de6678dcf48e.png

  1. 创建 CentOS 容器目录。

cefb33174c5d0668b9d7b62a1a2c1851.png

步骤二:开启 Open vSwitch 功能

为了使 CentOS 容器与群晖共享局域网,便于搭建网站,需要开启群晖自带的 Open vSwitch 功能。

90efcb2b7af4fae020fab7b587fca457.png

步骤三:创建 macvlan 网络

通过 SSH 连接到 NAS,并创建 macvlan 网络:

  1. 查看网关和 IP 段:

    netstat -nr | grep default
    

    例如,返回结果为:

   default via 192.168.0.10 dev ovs_eth0 src 192.168.0.109

其中 192.168.0.10 是网关,192.168.0 是 IP 段,ovs_eth0 是网络接口名称。

  1. 创建 macvlan 网络:
 docker network create -d macvlan --subnet=192.168.0.0/24 --gateway=192.168.0.10 -o parent=ovs_eth0 bridge-host

  1. 如果输入有误,删除网络:
 docker network rm bridge-host

步骤四:配置 CentOS 容器

  1. 创建 CentOS 容器并查看详情以获得 IP 地址。

%7BC8DEE323-8D6E-4ac1-9AD6-382C285B0020%7D.png
e3a6e85c7977dacd0f10d3cadae7eafa.png

  1. 如果不喜欢在容器内操作,可以配置 CentOS 的 SSH 登录。安装 SSH 服务:
   yum install passwd openssl openssh-server -y
  1. 启动 SSH 服务:

    /usr/sbin/sshd -D
    

    如果提示文件不存在,执行以下命令:

    ssh-keygen -q -t rsa -b 2048 -f /etc/ssh/ssh_host_rsa_key -N ''
    ssh-keygen -q -t ecdsa -f /etc/ssh/ssh_host_ecdsa_key -N ''
    ssh-keygen -t dsa -f /etc/ssh/ssh_host_ed25519_key -N ''
    sed -i "s/#UsePrivilegeSeparation.*/UsePrivilegeSeparation no/g" /etc/ssh/sshd_config
    sed -i "s/UsePAM.*/UsePAM no/g" /etc/ssh/sshd_config
    
    
  2. 再次启动 SSH 服务:

    /usr/sbin/sshd -D &
    

    尽管会提示 WARNING: 'UsePAM no' is not supported in Red Hat Enterprise Linux and may cause several problems.,按回车键继续执行启动即可。

/usr/sbin/sshd -D 

  1. 修改 SSH 密码:

    passwd
    

步骤五:安装宝塔面板

  1. 拉取宝塔官方版本:

    yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh
    
  2. 如果出现 URL 不存在错误,解决方法如下:

    sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*
    sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*
    yum install PACKAGE
    

    安装成功后,再次安装宝塔即可。

宝塔安装成功后,返回的内网登录地址即为宝塔网址。为了安全,删除安全入口:

rm -f /www/server/panel/data/admin_path.pl

然后,你可以通过 CentOS IP 加上宝塔端口愉快地使用宝塔面板了。

前言:在使用Frp对WordPress进行穿透时,可能会遇到以下两个问题:

网站首页能正常访问,但样式丢失。浏览器查看网络情况,发现样式请求依然是HTTP资源。
管理后台无法访问,提示重定向次数过多。
为了解决这些问题,可以按照以下步骤进行操作。

方案一

错误原因:

nginx代理的https页面中加载的内容使用的http协议导致报错。

解决方法:

增加add_header Content-Security-Policy "upgrade-insecure-requests" 将http协议升级为https。

server {
listen       80;
listen  [::]:80;
server_name test.example.cn;
listen 443 ssl;
charset utf-8;
ssl_certificate /www/server/panel/vhost/nginx/frpssl/example.cn.pem;
ssl_certificate_key /www/server/panel/vhost/nginx/frpssl/example.cn.key;
if ($server_port !~ 443){
    rewrite ^(/.*)$ https://$host$1 permanent;
}
location / {
    proxy_set_header      X-Forwarded-Proto https;
    proxy_set_header      X-Forwarded-Port 443;
    add_header Content-Security-Policy "upgrade-insecure-requests";
    proxy_set_header Host $host;
    proxy_pass http://127.0.0.1:端口;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
}

}

方案二

在wp网站根目录的wp-config.php里加入:

define('WP_HOME', 'http://'.$_SERVER ['HTTP_HOST']); 
define('WP_SITEURL', 'http://'.$_SERVER ['HTTP_HOST']);

强制HTTP,就可以登录后台,然后在安装插件:[scode type="green"]SSL 不安全内容修复器[/scode]

进入插件设置,先择简单修复

简单修复

然后选择HTTPS检测
选择检测

保存更改即可。

前言:在使用宝塔面板配置Minio的API接口时,可能会遇到一些反向代理的bug。例如,如果直接建站,然后配置ssl和反向代理,虽然可以进入minio网页端,但是在配置s3时候是无法连接的的。为了确保API接口能够正常运行,我们需要手动创建一个配置文件。本文将介绍如何在宝塔面板上为Minio配置反向代理,并提供一个示例配置文件。

  • 步骤一:创建配置文件

首先,我们需要在宝塔面板的Nginx配置目录中创建一个新的配置文件。在本文中,我们将创建一个名为minio_proxy.conf的文件。

cd /www/server/panel/vhost/nginx
nano minio_proxy.conf
  • 步骤二:配置反向代理

在配置文件中,我们将设置两个server块,一个用于标准的Minio服务,另一个用于Minio的API接口。以下是示例配置文件内容:

server {
    listen       80;
    listen  [::]:80;
    server_name oss.example.com;
    listen 443 ssl;
    charset utf-8;
    ssl_certificate /www/server/panel/vhost/nginx/frpssl/oss.example.com.pem;
    ssl_certificate_key /www/server/panel/vhost/nginx/frpssl/oss.example.com.key;
    if ($server_port !~ 443){
        rewrite ^(/.*)$ https://$host$1 permanent;
    }
    location / {
        proxy_set_header Host $host;
        proxy_pass http://127.0.0.1:9001;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
    }
}

server {
    listen       80;
    listen  [::]:80;
    server_name api.oss.example.com;
    listen 443 ssl;
    charset utf-8;
    ssl_certificate /www/server/panel/vhost/nginx/frpssl/api.oss.example.com.pem;
    ssl_certificate_key /www/server/panel/vhost/nginx/frpssl/api.oss.example.com.key;
    if ($server_port !~ 443){
        rewrite ^(/.*)$ https://$host$1 permanent;
    }
    location / {
        proxy_set_header Host $host;
        proxy_pass http://127.0.0.1:9000;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
    }
}


在上述配置中,我们使用了两个域名oss.example.com和api.oss.example.com分别对应Minio的服务和API接口。同时,我们设置了SSL证书路径以及反向代理的目标地址。

  • 步骤三:修改域名

根据实际情况,将示例中的域名oss.example.com和api.oss.example.com替换为你的实际域名。例如,如果你的域名是oss.mydomain.com和api.oss.mydomain.com,则需要相应地进行替换。

  • 步骤四:重启Nginx服务

完成配置文件的修改后,保存并退出编辑器。然后,重启Nginx服务使配置生效。

service nginx restart
  • 结论

通过以上步骤,我们成功在宝塔面板上配置了Minio的反向代理。该配置确保了Minio服务和API接口能够正常工作,同时提供了SSL支持。如果在配置过程中遇到问题,请检查配置文件的路径和域名是否正确,确保Minio服务在本地相应端口上运行。

前言:在云服务器的不稳定情况下,备份是确保数据安全的关键。为了避免突发崩溃导致数据丢失,建立备份习惯至关重要。下面是如何设置备份网站目录和数据库的步骤:

  1. 备份网站目录

通过定期备份网站目录,你可以在服务器崩溃时保留网站数据。首先,进入宝塔的计划任务,选择shell脚本,并创建一个名为“minio备份网站”的任务。设定定时任务每天0点执行一次。

MINIO_ACCESS_KEY="" # MinIO的访问密钥
MINIO_SECRET_KEY="" # MinIO的私密密钥
MINIO_ENDPOINT=""   # MinIO的端点
MINIO_BUCKET="web" # MinIO的储存桶名

SOURCE_DIRECTORY="/www/wwwroot/backup_directory"  # 网站备份目录

TIMESTAMP=$(date +"%Y-%m-%d_%H-%M-%S")

BACKUP_FILE="/tmp/backup_web_$TIMESTAMP.tar.gz"

echo "正在创建 $SOURCE_DIRECTORY 的tar归档文件..."

tar -czf "$BACKUP_FILE" -C "$(dirname "$SOURCE_DIRECTORY")" "$(basename "$SOURCE_DIRECTORY")"

echo "正在上传备份文件至 MinIO 存储桶..."

mc alias set minio "$MINIO_ENDPOINT" "$MINIO_ACCESS_KEY" "$MINIO_SECRET_KEY" --api S3v4

mc cp "$BACKUP_FILE" "minio/$MINIO_BUCKET/backup_website_web/" 

echo "清理中..."

rm "$BACKUP_FILE" 

echo "备份成功。"

确保修改以上脚本中的参数以适应你的环境,如MinIO的访问密钥、私密密钥、端点和储存桶名等。

  1. 备份数据库

宝塔自带了数据库的备份功能,你可以设置每天0点定时备份数据库,并保留一份数据。此外,然后通过以下脚本创建一个上传数据库备份至MinIO的任务,定时每天2点执行一次:

MINIO_ACCESS_KEY="" # MinIO的访问密钥
MINIO_SECRET_KEY="" # MinIO的私密密钥
MINIO_ENDPOINT=""   # MinIO的端点
MINIO_BUCKET="web" # MinIO的储存桶名

SOURCE_DIRECTORY="/www/backup/database/mysql/crontab_backup/database_name"  

TIMESTAMP=$(date +"%Y-%m-%d_%H-%M-%S")

BACKUP_FILE="/tmp/backup_sql_$TIMESTAMP.tar.gz"

echo "Creating tar archive of $SOURCE_DIRECTORY..."

tar -czf "$BACKUP_FILE" -C "$(dirname "$SOURCE_DIRECTORY")" "$(basename "$SOURCE_DIRECTORY")"

echo "Uploading backup file to MinIO bucket..."

mc alias set minio "$MINIO_ENDPOINT" "$MINIO_ACCESS_KEY" "$MINIO_SECRET_KEY" --api S3v4

mc cp "$BACKUP_FILE" "minio/$MINIO_BUCKET/backup_database_sql/"

echo "Cleaning up..."

rm "$BACKUP_FILE" 

echo "Backup completed successfully."
  1. 安装MinIO mc

为了使用MinIO mc工具,你需要先安装它。以下是安装MinIO mc的步骤:

# 下载mc
wget https://dl.minio.org.cn/client/mc/release/linux-amd64/mc
# 赋予执行权限
chmod +x ./mc
# 移动到全局
cp ./mc  /usr/local/bin/

安装完成后,输入 mc -help 来验证是否安装成功。

以上脚本针对单个网站或数据库,如果需要备份全部网站或数据库,只需调整目录即可。如果需要按需备份,需要自行修改脚本。