Files
dock/404
2025-10-31 00:25:08 +08:00

1036 lines
27 KiB
Bash
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# OpenResty/Nginx 403 & 404 错误页面一键部署脚本
# 支持Docker环境和传统安装方式
set -e
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# 日志函数
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
log_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
log_debug() { echo -e "${PURPLE}[DEBUG]${NC} $1"; }
# 检查运行环境
check_environment() {
log_info "检查运行环境..."
# 检查是否是Docker环境
if docker ps &>/dev/null; then
# 查找Nginx或OpenResty容器
local nginx_container=$(docker ps --filter "name=nginx" --format "{{.Names}}" | head -1)
local openresty_container=$(docker ps --filter "name=openresty" --format "{{.Names}}" | head -1)
if [ -n "$openresty_container" ]; then
log_success "找到OpenResty容器: $openresty_container"
echo "docker:$openresty_container"
return 0
elif [ -n "$nginx_container" ]; then
log_success "找到Nginx容器: $nginx_container"
echo "docker:$nginx_container"
return 0
else
log_warning "未找到Nginx/OpenResty容器尝试其他方式"
fi
fi
# 检查系统安装的OpenResty
if command -v openresty &>/dev/null; then
log_success "找到系统安装的OpenResty"
echo "system:openresty"
return 0
fi
# 检查系统安装的Nginx
if command -v nginx &>/dev/null; then
log_success "找到系统安装的Nginx"
echo "system:nginx"
return 0
fi
log_error "未找到Nginx或OpenResty"
return 1
}
# 创建403错误页面
create_403_page() {
local host_data_dir="/boot/data/dataxn"
log_info "正在创建日系动漫风格403错误页面..."
# 检查宿主机目录是否存在
if [ ! -d "$host_data_dir" ]; then
log_warning "目录 $host_data_dir 不存在,正在创建..."
mkdir -p "$host_data_dir"
fi
local html_path="$host_data_dir/403.html"
cat > "$html_path" << 'EOF'
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>访问被拒绝 - 403错误</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Arial Rounded MT Bold', 'Hiragino Maru Gothic ProN', 'Yu Gothic', sans-serif;
}
body {
background: linear-gradient(135deg, #a1c4fd 0%, #c2e9fb 100%);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
color: #2c3e50;
}
.container {
max-width: 800px;
width: 100%;
background-color: rgba(255, 255, 255, 0.9);
border-radius: 25px;
padding: 40px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);
text-align: center;
position: relative;
overflow: hidden;
border: 3px solid #3498db;
}
.anime-character {
width: 180px;
height: 200px;
margin: 0 auto 20px;
position: relative;
}
.character-head {
width: 120px;
height: 120px;
background-color: #fff;
border-radius: 50%;
margin: 0 auto;
position: relative;
border: 3px solid #3498db;
z-index: 2;
}
.character-hair {
width: 140px;
height: 80px;
background-color: #2980b9;
border-radius: 70px 70px 0 0;
position: absolute;
top: -20px;
left: 50%;
transform: translateX(-50%);
z-index: 1;
}
.character-eyes {
display: flex;
justify-content: space-around;
width: 80px;
margin: 40px auto 0;
}
.eye {
width: 20px;
height: 30px;
background-color: #2c3e50;
border-radius: 50%;
position: relative;
overflow: hidden;
}
.eye::after {
content: '';
position: absolute;
width: 10px;
height: 10px;
background-color: white;
border-radius: 50%;
top: 5px;
left: 5px;
}
.character-mouth {
width: 30px;
height: 10px;
background-color: #e74c3c;
border-radius: 0 0 15px 15px;
margin: 15px auto 0;
}
.character-body {
width: 100px;
height: 80px;
background-color: #3498db;
border-radius: 20px 20px 0 0;
margin: -10px auto 0;
position: relative;
z-index: 1;
}
.error-code {
font-size: 120px;
font-weight: bold;
color: #e74c3c;
text-shadow: 3px 3px 0 #f39c12;
margin: 10px 0;
line-height: 1;
}
h1 {
font-size: 32px;
margin-bottom: 20px;
color: #2c3e50;
}
p {
font-size: 18px;
line-height: 1.6;
margin-bottom: 30px;
color: #34495e;
}
.action-buttons {
display: flex;
justify-content: center;
gap: 15px;
flex-wrap: wrap;
}
.btn {
padding: 12px 25px;
border-radius: 50px;
text-decoration: none;
font-weight: bold;
transition: all 0.3s ease;
display: inline-flex;
align-items: center;
justify-content: center;
border: none;
cursor: pointer;
font-size: 16px;
}
.btn-primary {
background-color: #3498db;
color: white;
box-shadow: 0 4px 0 #2980b9;
}
.btn-secondary {
background-color: #9b59b6;
color: white;
box-shadow: 0 4px 0 #8e44ad;
}
.btn-warning {
background-color: #e67e22;
color: white;
box-shadow: 0 4px 0 #d35400;
}
.btn:hover {
transform: translateY(-3px);
box-shadow: 0 6px 0 rgba(0, 0, 0, 0.2);
}
.btn:active {
transform: translateY(1px);
box-shadow: 0 2px 0 rgba(0, 0, 0, 0.2);
}
.floating-elements {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
pointer-events: none;
}
.floating-element {
position: absolute;
opacity: 0.7;
animation: float 6s ease-in-out infinite;
font-size: 24px;
}
.element-1 {
top: 10%;
left: 5%;
animation-delay: 0s;
}
.element-2 {
top: 20%;
right: 10%;
animation-delay: 2s;
}
.element-3 {
bottom: 30%;
left: 15%;
animation-delay: 4s;
}
@keyframes float {
0%, 100% {
transform: translateY(0);
}
50% {
transform: translateY(-20px);
}
}
.security-icon {
font-size: 80px;
margin-bottom: 20px;
animation: bounce 2s infinite;
}
@keyframes bounce {
0%, 100% {
transform: translateY(0);
}
50% {
transform: translateY(-10px);
}
}
@media (max-width: 600px) {
.container {
padding: 25px;
}
.error-code {
font-size: 80px;
}
h1 {
font-size: 24px;
}
p {
font-size: 16px;
}
.action-buttons {
flex-direction: column;
align-items: center;
}
.btn {
width: 100%;
max-width: 250px;
}
}
</style>
</head>
<body>
<div class="container">
<div class="floating-elements">
<div class="floating-element element-1">🚫</div>
<div class="floating-element element-2">🔒</div>
<div class="floating-element element-3">⚡</div>
</div>
<div class="security-icon">🔐</div>
<div class="anime-character">
<div class="character-hair"></div>
<div class="character-head">
<div class="character-eyes">
<div class="eye"></div>
<div class="eye"></div>
</div>
<div class="character-mouth"></div>
</div>
<div class="character-body"></div>
</div>
<div class="error-code">403</div>
<h1>アクセスが拒否されました!</h1>
<p>申し訳ありませんが、このページへのアクセス権限がありません。<br>
守られし領域へは、選ばれし者だけが入ることが許されています。</p>
<div class="action-buttons">
<a href="/" class="btn btn-primary">ホームに戻る</a>
<a href="javascript:history.back()" class="btn btn-secondary">前のページに戻る</a>
<a href="mailto:admin@example.com" class="btn btn-warning">管理者に連絡</a>
</div>
</div>
<script>
// 添加闪烁效果
document.addEventListener('DOMContentLoaded', function() {
const container = document.querySelector('.container');
for (let i = 0; i < 10; i++) {
const sparkle = document.createElement('div');
sparkle.classList.add('sparkle');
// 随机位置
sparkle.style.left = Math.random() * 100 + '%';
sparkle.style.top = Math.random() * 100 + '%';
// 随机大小
const size = Math.random() * 8 + 3;
sparkle.style.width = size + 'px';
sparkle.style.height = size + 'px';
sparkle.style.backgroundColor = getRandomColor();
// 随机延迟
sparkle.style.animationDelay = Math.random() * 5 + 's';
container.appendChild(sparkle);
}
function getRandomColor() {
const colors = ['#3498db', '#e74c3c', '#f39c12', '#9b59b6'];
return colors[Math.floor(Math.random() * colors.length)];
}
});
</script>
</body>
</html>
EOF
log_success "403页面已创建: $html_path"
}
# 创建404错误页面更新版
create_404_page() {
local host_data_dir="/boot/data/dataxn"
log_info "正在创建日系动漫风格404错误页面..."
# 检查宿主机目录是否存在
if [ ! -d "$host_data_dir" ]; then
log_warning "目录 $host_data_dir 不存在,正在创建..."
mkdir -p "$host_data_dir"
fi
local html_path="$host_data_dir/404.html"
cat > "$html_path" << 'EOF'
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>页面未找到 - 404错误</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Arial Rounded MT Bold', 'Hiragino Maru Gothic ProN', 'Yu Gothic', sans-serif;
}
body {
background: linear-gradient(135deg, #ffdde1 0%, #ee9ca7 100%);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
color: #5a3d5c;
}
.container {
max-width: 800px;
width: 100%;
background-color: rgba(255, 255, 255, 0.85);
border-radius: 25px;
padding: 40px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
text-align: center;
position: relative;
overflow: hidden;
border: 3px solid #ffb6c1;
}
.anime-character {
width: 180px;
height: 200px;
margin: 0 auto 20px;
position: relative;
}
.character-head {
width: 120px;
height: 120px;
background-color: #fff;
border-radius: 50%;
margin: 0 auto;
position: relative;
border: 3px solid #ffb6c1;
z-index: 2;
}
.character-hair {
width: 140px;
height: 80px;
background-color: #ff69b4;
border-radius: 70px 70px 0 0;
position: absolute;
top: -20px;
left: 50%;
transform: translateX(-50%);
z-index: 1;
}
.character-eyes {
display: flex;
justify-content: space-around;
width: 80px;
margin: 40px auto 0;
}
.eye {
width: 20px;
height: 30px;
background-color: #5a3d5c;
border-radius: 50%;
position: relative;
overflow: hidden;
}
.eye::after {
content: '';
position: absolute;
width: 10px;
height: 10px;
background-color: white;
border-radius: 50%;
top: 5px;
left: 5px;
}
.character-mouth {
width: 30px;
height: 10px;
background-color: #ff69b4;
border-radius: 0 0 15px 15px;
margin: 15px auto 0;
}
.character-body {
width: 100px;
height: 80px;
background-color: #ffb6c1;
border-radius: 20px 20px 0 0;
margin: -10px auto 0;
position: relative;
z-index: 1;
}
.error-code {
font-size: 120px;
font-weight: bold;
color: #ff69b4;
text-shadow: 3px 3px 0 #ffb6c1;
margin: 10px 0;
line-height: 1;
}
h1 {
font-size: 32px;
margin-bottom: 20px;
color: #5a3d5c;
}
p {
font-size: 18px;
line-height: 1.6;
margin-bottom: 30px;
color: #7a5c7d;
}
.action-buttons {
display: flex;
justify-content: center;
gap: 15px;
flex-wrap: wrap;
}
.btn {
padding: 12px 25px;
border-radius: 50px;
text-decoration: none;
font-weight: bold;
transition: all 0.3s ease;
display: inline-flex;
align-items: center;
justify-content: center;
border: none;
cursor: pointer;
font-size: 16px;
}
.btn-primary {
background-color: #ff69b4;
color: white;
box-shadow: 0 4px 0 #d4508f;
}
.btn-secondary {
background-color: #b19cd9;
color: white;
box-shadow: 0 4px 0 #8a7bb5;
}
.btn:hover {
transform: translateY(-3px);
box-shadow: 0 6px 0 rgba(0, 0, 0, 0.2);
}
.btn:active {
transform: translateY(1px);
box-shadow: 0 2px 0 rgba(0, 0, 0, 0.2);
}
.floating-elements {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
pointer-events: none;
}
.floating-element {
position: absolute;
opacity: 0.7;
animation: float 6s ease-in-out infinite;
}
.element-1 {
top: 10%;
left: 5%;
animation-delay: 0s;
}
.element-2 {
top: 20%;
right: 10%;
animation-delay: 2s;
}
.element-3 {
bottom: 30%;
left: 15%;
animation-delay: 4s;
}
@keyframes float {
0%, 100% {
transform: translateY(0);
}
50% {
transform: translateY(-20px);
}
}
.sparkle {
position: absolute;
width: 20px;
height: 20px;
background-color: #fff;
border-radius: 50%;
opacity: 0;
animation: sparkle 3s linear infinite;
}
@keyframes sparkle {
0%, 100% {
opacity: 0;
transform: scale(0);
}
50% {
opacity: 1;
transform: scale(1);
}
}
@media (max-width: 600px) {
.container {
padding: 25px;
}
.error-code {
font-size: 80px;
}
h1 {
font-size: 24px;
}
p {
font-size: 16px;
}
.action-buttons {
flex-direction: column;
align-items: center;
}
.btn {
width: 100%;
max-width: 250px;
}
}
</style>
</head>
<body>
<div class="container">
<div class="floating-elements">
<div class="floating-element element-1">🌸</div>
<div class="floating-element element-2">⭐</div>
<div class="floating-element element-3">🐱</div>
</div>
<div class="anime-character">
<div class="character-hair"></div>
<div class="character-head">
<div class="character-eyes">
<div class="eye"></div>
<div class="eye"></div>
</div>
<div class="character-mouth"></div>
</div>
<div class="character-body"></div>
</div>
<div class="error-code">404</div>
<h1>おっと!ページが見つかりません</h1>
<p>申し訳ありませんが、お探しのページは存在しないか、移動した可能性があります。<br>
迷子になったキャラクターのように、ページもどこかへ行ってしまったようです。</p>
<div class="action-buttons">
<a href="/" class="btn btn-primary">ホームに戻る</a>
<a href="javascript:history.back()" class="btn btn-secondary">前のページに戻る</a>
</div>
</div>
<script>
// 添加闪烁效果
document.addEventListener('DOMContentLoaded', function() {
const container = document.querySelector('.container');
for (let i = 0; i < 15; i++) {
const sparkle = document.createElement('div');
sparkle.classList.add('sparkle');
// 随机位置
sparkle.style.left = Math.random() * 100 + '%';
sparkle.style.top = Math.random() * 100 + '%';
// 随机大小
const size = Math.random() * 10 + 5;
sparkle.style.width = size + 'px';
sparkle.style.height = size + 'px';
// 随机延迟
sparkle.style.animationDelay = Math.random() * 5 + 's';
container.appendChild(sparkle);
}
});
</script>
</body>
</html>
EOF
log_success "404页面已创建: $html_path"
}
# 为Docker容器创建Nginx配置
create_docker_nginx_config() {
local container_name="$1"
log_info "为容器 $container_name 创建Nginx错误页面配置..."
# 创建配置内容
local config_content=$(cat << 'EOF'
# 自定义错误页面配置
error_page 403 /403.html;
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
location = /403.html {
root /usr/share/nginx/html;
internal;
allow all;
}
location = /404.html {
root /usr/share/nginx/html;
internal;
allow all;
}
location = /50x.html {
root /usr/share/nginx/html;
internal;
allow all;
}
EOF
)
# 将配置写入容器
local config_path="/etc/nginx/conf.d/custom_errors.conf"
echo "$config_content" | docker exec -i "$container_name" tee "$config_path" > /dev/null
if [ $? -eq 0 ]; then
log_success "Nginx错误页面配置已写入容器: $config_path"
else
log_error "无法写入容器配置"
return 1
fi
}
# 为系统安装创建Nginx配置
create_system_nginx_config() {
local service_type="$1"
log_info "为系统安装的 $service_type 创建错误页面配置..."
local nginx_conf_dir=""
case "$service_type" in
"openresty")
nginx_conf_dir="/usr/local/openresty/nginx/conf"
if [ ! -d "$nginx_conf_dir" ]; then
nginx_conf_dir="/etc/openresty"
fi
;;
"nginx")
nginx_conf_dir="/etc/nginx"
;;
esac
if [ ! -d "$nginx_conf_dir" ]; then
log_error "找不到配置目录: $nginx_conf_dir"
return 1
fi
local config_path="$nginx_conf_dir/conf.d/custom_errors.conf"
# 创建配置内容
local config_content=$(cat << EOF
# 自定义错误页面配置
error_page 403 /403.html;
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
location = /403.html {
root /usr/share/nginx/html;
internal;
allow all;
}
location = /404.html {
root /usr/share/nginx/html;
internal;
allow all;
}
location = /50x.html {
root /usr/share/nginx/html;
internal;
allow all;
}
EOF
)
# 写入配置
mkdir -p "$(dirname "$config_path")"
echo "$config_content" > "$config_path"
log_success "Nginx错误页面配置已创建: $config_path"
}
# 测试配置
test_config() {
local env_type="$1"
local target="$2"
log_info "测试配置..."
case "$env_type" in
"docker")
if docker exec "$target" nginx -t; then
log_success "Docker容器配置测试通过"
return 0
else
log_error "Docker容器配置测试失败"
return 1
fi
;;
"system")
if sudo nginx -t 2>/dev/null || sudo openresty -t 2>/dev/null; then
log_success "系统服务配置测试通过"
return 0
else
log_error "系统服务配置测试失败"
return 1
fi
;;
esac
}
# 重新加载服务
reload_service() {
local env_type="$1"
local target="$2"
log_info "重新加载服务..."
case "$env_type" in
"docker")
if docker exec "$target" nginx -s reload; then
log_success "Docker容器配置已重新加载"
return 0
else
log_error "Docker容器重新加载失败"
return 1
fi
;;
"system")
if systemctl reload nginx 2>/dev/null || systemctl reload openresty 2>/dev/null || nginx -s reload 2>/dev/null; then
log_success "系统服务配置已重新加载"
return 0
else
log_error "系统服务重新加载失败"
return 1
fi
;;
esac
}
# 显示完成信息
show_completion() {
local env_type="$1"
local target="$2"
echo
log_success "日系动漫风格403 & 404错误页面部署完成"
echo
log_info "部署详情:"
log_info "- 403页面: /boot/data/dataxn/403.html"
log_info "- 404页面: /boot/data/dataxn/404.html"
case "$env_type" in
"docker")
log_info "- 容器名称: $target"
log_info "- 配置路径: /etc/nginx/conf.d/custom_errors.conf"
;;
"system")
log_info "- 服务类型: $target"
log_info "- 配置路径: /etc/nginx/conf.d/custom_errors.conf"
;;
esac
echo
log_info "测试方法:"
log_info "403测试: curl http://你的域名/禁止访问的路径"
log_info "404测试: curl http://你的域名/不存在的页面"
echo
log_success "现在您的OpenResty/Nginx将显示美观的日系动漫风格错误页面"
}
# 主函数
main() {
echo
log_info "开始部署日系动漫风格403 & 404错误页面..."
echo
# 检查环境
local env_info=$(check_environment)
if [ $? -ne 0 ]; then
exit 1
fi
local env_type=$(echo "$env_info" | cut -d: -f1)
local target=$(echo "$env_info" | cut -d: -f2)
log_debug "环境类型: $env_type, 目标: $target"
# 创建错误页面
create_403_page
create_404_page
# 创建配置
case "$env_type" in
"docker")
create_docker_nginx_config "$target"
;;
"system")
create_system_nginx_config "$target"
;;
esac
# 测试并重新加载
if test_config "$env_type" "$target"; then
reload_service "$env_type" "$target"
show_completion "$env_type" "$target"
else
log_error "部署失败,请检查配置"
exit 1
fi
}
# 显示使用说明
usage() {
echo "OpenResty/Nginx 403 & 404 错误页面一键部署脚本"
echo
echo "使用方法: $0 [选项]"
echo
echo "选项:"
echo " -h, --help 显示此帮助信息"
echo " -i, --install 执行安装部署"
echo
echo "支持环境:"
echo " - Docker容器 (Nginx/OpenResty)"
echo " - 系统安装 (Nginx/OpenResty)"
echo
echo "示例:"
echo " $0 --install 部署403和404页面"
}
# 参数处理
case "${1:-}" in
-h|--help)
usage
exit 0
;;
-i|--install|"")
main
;;
*)
log_error "未知选项: $1"
usage
exit 1
;;
esac