442 lines
14 KiB
Bash
Executable File
442 lines
14 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# Nginx Autoindex 文件列表部署脚本
|
|
# 自动部署美化版的nginx文件列表系统
|
|
|
|
set -e
|
|
|
|
echo "================================"
|
|
echo "Nginx Autoindex 文件列表部署脚本"
|
|
echo "================================"
|
|
echo ""
|
|
|
|
# 检测nginx是否安装
|
|
echo "检查nginx是否已安装..."
|
|
if ! command -v nginx &> /dev/null; then
|
|
echo "❌ 错误: 未检测到nginx"
|
|
echo "请先安装nginx: sudo apt-get install nginx"
|
|
exit 1
|
|
fi
|
|
|
|
echo "✓ nginx已安装: $(nginx -v 2>&1)"
|
|
echo ""
|
|
|
|
# 设置变量
|
|
AUTOINDEX_DIR="/var/www/autoindex"
|
|
FILES_DIR="/var/www/files"
|
|
NGINX_CONFIG="/etc/nginx/sites-available/autoindex-files"
|
|
NGINX_ENABLED="/etc/nginx/sites-enabled/autoindex-files"
|
|
|
|
# 检测是否为root用户
|
|
if [ "$EUID" -ne 0 ]; then
|
|
echo "❌ 请使用root权限运行此脚本: sudo ./deploy.sh"
|
|
exit 1
|
|
fi
|
|
|
|
echo "开始部署..."
|
|
echo ""
|
|
|
|
# 1. 创建目录
|
|
echo "1. 创建必要目录..."
|
|
mkdir -p "$AUTOINDEX_DIR"
|
|
mkdir -p "$FILES_DIR"
|
|
echo "✓ 目录创建完成"
|
|
echo ""
|
|
|
|
# 2. 复制header.html
|
|
echo "2. 部署header.html..."
|
|
cat > "$AUTOINDEX_DIR/header.html" << 'EOF'
|
|
<style>
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
max-width: 1200px;
|
|
margin: 0 auto;
|
|
padding: 0.5rem;
|
|
background: #f8fafc;
|
|
color: #1e293b;
|
|
}
|
|
|
|
h1 {
|
|
background: transparent;
|
|
padding: 0;
|
|
border-radius: 0;
|
|
box-shadow: none;
|
|
margin-bottom: 0;
|
|
font-size: 1.1rem;
|
|
font-weight: 600;
|
|
}
|
|
|
|
pre {
|
|
background: white;
|
|
border-radius: 6px;
|
|
padding: 0;
|
|
margin: 0;
|
|
font-family: inherit;
|
|
overflow: hidden;
|
|
line-height: 1;
|
|
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
|
|
}
|
|
|
|
hr {
|
|
display: none;
|
|
}
|
|
</style>
|
|
|
|
<div style="background: white; padding: 0.5rem 1rem; border-radius: 6px; margin-bottom: 0.5rem; box-shadow: 0 1px 2px rgba(0,0,0,0.05);">
|
|
<h1 style="margin: 0; padding: 0; box-shadow: none; font-size: 1.1rem; color: #1e293b;">📁 文件列表</h1>
|
|
</div>
|
|
EOF
|
|
echo "✓ header.html部署完成"
|
|
echo ""
|
|
|
|
# 3. 复制footer.html
|
|
echo "3. 部署footer.html..."
|
|
cat > "$AUTOINDEX_DIR/footer.html" << 'FOOTEREOF'
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// 添加搜索框
|
|
const searchBox = document.createElement('div');
|
|
searchBox.innerHTML = `
|
|
<input type="text" id="search" placeholder="搜索文件..."
|
|
style="width: 100%; padding: 0.6rem; margin-bottom: 1rem;
|
|
border: 2px solid #e2e8f0; border-radius: 6px;
|
|
font-size: 0.9rem; background: white; color: #1e293b;">
|
|
`;
|
|
document.body.insertBefore(searchBox, document.querySelector('pre'));
|
|
|
|
const pre = document.querySelector('pre');
|
|
if (!pre) return;
|
|
|
|
// 添加表格头部
|
|
const headerDiv = document.createElement('div');
|
|
headerDiv.innerHTML = `
|
|
<div style="display: flex; background: #f8fafc; padding: 0.3rem 1rem;
|
|
border-bottom: 1px solid #e2e8f0; font-weight: 600; font-size: 0.8rem; color: #64748b;">
|
|
<div style="flex: 1; line-height: 1.5;">📋 文件名</div>
|
|
<div style="width: 100px; text-align: right; margin-right: 1rem; line-height: 1.5;">大小</div>
|
|
<div style="width: 140px; text-align: right; margin-right: 1rem; line-height: 1.5;">修改时间</div>
|
|
<div style="width: 50px; text-align: center; line-height: 1.5;">操作</div>
|
|
</div>
|
|
`;
|
|
pre.parentNode.insertBefore(headerDiv, pre);
|
|
|
|
// 获取原始HTML并解析
|
|
const originalHTML = pre.innerHTML;
|
|
const regex = /<a href="([^"]*)"[^>]*>([^<]*)<\/a>\s*([^<]*?)(?=<a|$)/g;
|
|
|
|
let newHTML = '';
|
|
let match;
|
|
|
|
while ((match = regex.exec(originalHTML)) !== null) {
|
|
const href = match[1];
|
|
const filename = match[2];
|
|
const afterText = match[3].trim();
|
|
|
|
const parts = afterText.split(/\s+/).filter(p => p && p !== '-');
|
|
let date = '-', size = '-';
|
|
|
|
if (parts.length >= 2) {
|
|
date = parts[0] + ' ' + parts[1];
|
|
if (parts.length >= 3 && parts[2] !== '-') {
|
|
size = parts[2];
|
|
}
|
|
}
|
|
|
|
const icon = getFileIcon(filename);
|
|
|
|
const isFolder = filename.endsWith('/');
|
|
const linkColor = isFolder ? '#3b82f6' : '#1e293b';
|
|
|
|
newHTML += `
|
|
<div style="display: flex; align-items: center; padding: 0.15rem 1rem;
|
|
background: white; transition: background-color 0.2s;"
|
|
onmouseenter="this.style.backgroundColor='#f8fafc'"
|
|
onmouseleave="this.style.backgroundColor='white'">
|
|
<a href="${href}" style="flex: 1; display: flex; text-decoration: none;
|
|
color: ${linkColor}; overflow: hidden;"
|
|
data-filename="${filename.toLowerCase()}">
|
|
<span style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 0.9rem; line-height: 1.2;">${filename}</span>
|
|
</a>
|
|
<div style="width: 100px; text-align: right; color: #64748b; font-size: 0.85rem; line-height: 1.2; margin-right: 1rem;">${formatSize(size)}</div>
|
|
<div style="width: 140px; text-align: right; color: #64748b; font-size: 0.85rem; line-height: 1.2; margin-right: 1rem;">${formatDate(date)}</div>
|
|
<div style="width: 50px; text-align: center;"><button onclick="copyFileUrl(event, '${href}')" title="复制链接" style="padding: 0.15rem 0.4rem; border: 1px solid #d1d5db; border-radius: 3px; background: white; color: #374151; cursor: pointer; font-size: 0.75rem; line-height: 1.3; font-weight: 500; transition: all 0.2s;" onmouseenter="this.style.backgroundColor='#eff6ff'; this.style.borderColor='#3b82f6'; this.style.color='#2563eb';" onmouseleave="this.style.backgroundColor='white'; this.style.borderColor='#d1d5db'; this.style.color='#374151';">复制</button></div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
while (pre.firstChild) {
|
|
pre.removeChild(pre.firstChild);
|
|
}
|
|
|
|
pre.style.cssText = `
|
|
background: white;
|
|
border-radius: 6px;
|
|
padding: 0;
|
|
margin: 0;
|
|
font-family: inherit;
|
|
overflow: hidden;
|
|
display: block;
|
|
font-size: 0;
|
|
line-height: 0;
|
|
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
|
|
`;
|
|
|
|
pre.innerHTML = newHTML;
|
|
|
|
document.getElementById('search').addEventListener('input', function(e) {
|
|
const term = e.target.value.toLowerCase();
|
|
const rows = pre.querySelectorAll('div[onmouseenter]');
|
|
rows.forEach(row => {
|
|
const link = row.querySelector('a');
|
|
const filename = link ? link.dataset.filename || '' : '';
|
|
row.style.display = filename.includes(term) ? 'flex' : 'none';
|
|
});
|
|
});
|
|
});
|
|
|
|
function getFileIcon(filename) {
|
|
if (filename.endsWith('/')) return '📁';
|
|
const ext = filename.split('.').pop().toLowerCase();
|
|
const icons = {
|
|
'pdf': '📕', 'doc': '📘', 'docx': '📘',
|
|
'jpg': '🖼️', 'jpeg': '🖼️', 'png': '🖼️', 'gif': '🖼️',
|
|
'mp4': '🎬', 'avi': '🎬', 'mov': '🎬', 'mkv': '🎬',
|
|
'mp3': '🎵', 'wav': '🎵', 'ogg': '🎵',
|
|
'zip': '📦', 'rar': '📦', '7z': '📦', 'tar': '📦',
|
|
'exe': '⚙️', 'msi': '⚙️'
|
|
};
|
|
return icons[ext] || '📄';
|
|
}
|
|
|
|
function formatSize(size) {
|
|
if (!size || size === '-') return '-';
|
|
if (size.match(/[KMGT]/i)) return size;
|
|
|
|
const num = parseFloat(size);
|
|
if (isNaN(num)) return size;
|
|
|
|
if (num < 1024) return num + 'B';
|
|
if (num < 1048576) return (num/1024).toFixed(1) + 'K';
|
|
if (num < 1073741824) return (num/1048576).toFixed(1) + 'M';
|
|
return (num/1073741824).toFixed(1) + 'G';
|
|
}
|
|
|
|
function formatDate(dateStr) {
|
|
if (!dateStr || dateStr === '-') return '-';
|
|
return dateStr.replace(/(\d{2})-(\w{3})-(\d{4}) (\d{2}):(\d{2})/, '$3-$2-$1 $4:$5');
|
|
}
|
|
|
|
function copyFileUrl(event, href) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
|
|
let basePath = window.location.pathname;
|
|
if (basePath.endsWith('/')) {
|
|
basePath = basePath.slice(0, -1);
|
|
}
|
|
if (!href.startsWith('/')) {
|
|
href = '/' + href;
|
|
}
|
|
|
|
let decodedHref = href;
|
|
try {
|
|
decodedHref = decodeURIComponent(href);
|
|
} catch (e) {}
|
|
|
|
const fullUrl = window.location.origin + basePath + decodedHref;
|
|
|
|
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
navigator.clipboard.writeText(fullUrl).then(() => {
|
|
const button = event.target.closest('button');
|
|
if (button) {
|
|
const originalText = button.textContent;
|
|
button.textContent = '已复制';
|
|
button.style.color = '#10b981';
|
|
button.style.borderColor = '#10b981';
|
|
button.style.backgroundColor = '#f0fdf4';
|
|
|
|
setTimeout(() => {
|
|
button.textContent = originalText;
|
|
button.style.color = '#374151';
|
|
button.style.borderColor = '#d1d5db';
|
|
button.style.backgroundColor = 'white';
|
|
}, 1500);
|
|
}
|
|
}).catch(err => {
|
|
const button = event.target.closest('button');
|
|
fallbackCopyTextToClipboard(fullUrl, button);
|
|
});
|
|
} else {
|
|
const button = event.target.closest('button');
|
|
fallbackCopyTextToClipboard(fullUrl, button);
|
|
}
|
|
}
|
|
|
|
function fallbackCopyTextToClipboard(text, button) {
|
|
const textArea = document.createElement('textarea');
|
|
textArea.value = text;
|
|
textArea.style.position = 'fixed';
|
|
textArea.style.top = '0';
|
|
textArea.style.left = '0';
|
|
textArea.style.width = '2em';
|
|
textArea.style.height = '2em';
|
|
textArea.style.padding = '0';
|
|
textArea.style.border = 'none';
|
|
textArea.style.outline = 'none';
|
|
textArea.style.boxShadow = 'none';
|
|
textArea.style.background = 'transparent';
|
|
document.body.appendChild(textArea);
|
|
textArea.focus();
|
|
textArea.select();
|
|
|
|
try {
|
|
const successful = document.execCommand('copy');
|
|
if (successful && button) {
|
|
const originalText = button.textContent;
|
|
button.textContent = '已复制';
|
|
button.style.color = '#10b981';
|
|
button.style.borderColor = '#10b981';
|
|
button.style.backgroundColor = '#f0fdf4';
|
|
|
|
setTimeout(() => {
|
|
button.textContent = originalText;
|
|
button.style.color = '#374151';
|
|
button.style.borderColor = '#d1d5db';
|
|
button.style.backgroundColor = 'white';
|
|
}, 1500);
|
|
}
|
|
} catch (err) {
|
|
console.error('复制失败:', err);
|
|
}
|
|
|
|
document.body.removeChild(textArea);
|
|
}
|
|
</script>
|
|
|
|
<div style="margin-top: 2rem; text-align: center; color: #64748b; font-size: 0.875rem;">
|
|
<p>由 nginx autoindex 提供支持</p>
|
|
</div>
|
|
FOOTEREOF
|
|
echo "✓ footer.html部署完成"
|
|
echo ""
|
|
|
|
# 4. 创建nginx配置
|
|
echo "4. 创建nginx配置..."
|
|
cat > "$NGINX_CONFIG" << 'EOF'
|
|
# nginx autoindex 美化配置
|
|
|
|
server {
|
|
listen 80;
|
|
server_name _;
|
|
|
|
root /var/www/files;
|
|
charset utf-8;
|
|
|
|
location / {
|
|
# 启用autoindex
|
|
autoindex on;
|
|
autoindex_exact_size off; # 显示KB/MB而不是字节
|
|
autoindex_localtime on; # 使用本地时间
|
|
autoindex_format html;
|
|
|
|
# 注入自定义样式
|
|
add_before_body /autoindex-header.html;
|
|
add_after_body /autoindex-footer.html;
|
|
}
|
|
|
|
# 提供header文件
|
|
location = /autoindex-header.html {
|
|
alias /var/www/autoindex/header.html;
|
|
}
|
|
|
|
# 提供footer文件
|
|
location = /autoindex-footer.html {
|
|
alias /var/www/autoindex/footer.html;
|
|
}
|
|
|
|
# 禁止访问隐藏文件
|
|
location ~ /\. {
|
|
deny all;
|
|
return 404;
|
|
}
|
|
}
|
|
EOF
|
|
echo "✓ nginx配置创建完成"
|
|
echo ""
|
|
|
|
# 5. 启用配置
|
|
echo "5. 启用nginx配置..."
|
|
# 删除默认配置
|
|
if [ -f "/etc/nginx/sites-enabled/default" ]; then
|
|
rm -f /etc/nginx/sites-enabled/default
|
|
echo "✓ 已删除默认配置"
|
|
fi
|
|
|
|
# 创建软链接
|
|
ln -sf "$NGINX_CONFIG" "$NGINX_ENABLED"
|
|
echo "✓ 配置已启用"
|
|
echo ""
|
|
|
|
# 6. 设置权限
|
|
echo "6. 设置目录权限..."
|
|
chmod 755 "$AUTOINDEX_DIR"
|
|
chmod 644 "$AUTOINDEX_DIR"/*.html
|
|
chmod 755 "$FILES_DIR"
|
|
echo "✓ 权限设置完成"
|
|
echo ""
|
|
|
|
# 7. 测试nginx配置
|
|
echo "7. 测试nginx配置..."
|
|
if nginx -t 2>&1 | grep -q "successful"; then
|
|
echo "✓ nginx配置测试通过"
|
|
else
|
|
echo "❌ nginx配置测试失败"
|
|
nginx -t
|
|
exit 1
|
|
fi
|
|
echo ""
|
|
|
|
# 8. 重载nginx
|
|
echo "8. 重载nginx服务..."
|
|
systemctl reload nginx
|
|
echo "✓ nginx已重载"
|
|
echo ""
|
|
|
|
# 9. 创建示例文件
|
|
echo "9. 创建示例文件..."
|
|
cat > "$FILES_DIR/README.md" << 'EOF'
|
|
# 欢迎使用文件列表系统
|
|
|
|
这是一个美化版的nginx autoindex文件列表系统。
|
|
|
|
## 功能特点
|
|
- 搜索文件
|
|
- 复制文件链接
|
|
- 美观的界面
|
|
- 文件/文件夹颜色区分
|
|
|
|
将你的文件放到 /var/www/files 目录即可。
|
|
EOF
|
|
|
|
# 创建示例目录
|
|
mkdir -p "$FILES_DIR/documents"
|
|
mkdir -p "$FILES_DIR/images"
|
|
mkdir -p "$FILES_DIR/downloads"
|
|
|
|
echo "✓ 示例文件创建完成"
|
|
echo ""
|
|
|
|
echo "================================"
|
|
echo "✅ 部署完成!"
|
|
echo "================================"
|
|
echo ""
|
|
echo "访问地址: http://$(hostname -I | awk '{print $1}')"
|
|
echo ""
|
|
echo "文件存放目录: $FILES_DIR"
|
|
echo "配置文件位置: $NGINX_CONFIG"
|
|
echo ""
|
|
echo "提示:"
|
|
echo "- 将文件放到 $FILES_DIR 即可在网页上看到"
|
|
echo "- 修改配置后运行: sudo systemctl reload nginx"
|
|
echo ""
|