初始化
This commit is contained in:
@@ -0,0 +1,185 @@
|
||||
# H3C ONU设备管理系统 - 环境变量配置示例
|
||||
# 复制此文件为 .env 并修改相应配置
|
||||
|
||||
# ============================================
|
||||
# 应用基础配置
|
||||
# ============================================
|
||||
|
||||
# 应用环境 (development, production)
|
||||
APP_ENV=production
|
||||
|
||||
# 调试模式 (true/false)
|
||||
DEBUG=false
|
||||
|
||||
# 应用密钥 (使用 openssl rand -hex 32 生成)
|
||||
SECRET_KEY=your-secret-key-change-in-production
|
||||
|
||||
# 时区设置
|
||||
TZ=Asia/Shanghai
|
||||
|
||||
# ============================================
|
||||
# 数据库配置 (PostgreSQL)
|
||||
# ============================================
|
||||
|
||||
# PostgreSQL连接字符串
|
||||
# 格式: postgresql://username:password@host:port/database
|
||||
DATABASE_URL=postgresql://h3c_user:your_password@postgres-host:5432/h3c_onu_ms
|
||||
|
||||
# 数据库连接池配置
|
||||
DATABASE_POOL_SIZE=20
|
||||
DATABASE_MAX_OVERFLOW=40
|
||||
DATABASE_POOL_RECYCLE=3600
|
||||
|
||||
# ============================================
|
||||
# Redis配置
|
||||
# ============================================
|
||||
|
||||
# Redis连接URL (如果使用外部Redis,修改为外部地址)
|
||||
REDIS_URL=redis://:redispass@redis:6379/0
|
||||
|
||||
# Redis密码 (容器内Redis使用)
|
||||
REDIS_PASSWORD=redispass
|
||||
|
||||
# Redis连接池大小
|
||||
REDIS_POOL_SIZE=10
|
||||
|
||||
# ============================================
|
||||
# Casdoor认证配置
|
||||
# ============================================
|
||||
|
||||
# Casdoor服务地址
|
||||
CASDOOR_ENDPOINT=https://casdoor.example.com
|
||||
|
||||
# 应用Client ID
|
||||
CASDOOR_CLIENT_ID=your_casdoor_client_id
|
||||
|
||||
# 应用Client Secret
|
||||
CASDOOR_CLIENT_SECRET=your_casdoor_client_secret
|
||||
|
||||
# 应用Certificate
|
||||
CASDOOR_CERTIFICATE=your_casdoor_certificate
|
||||
|
||||
# 组织名称
|
||||
CASDOOR_ORG_NAME=your_organization
|
||||
|
||||
# 应用名称
|
||||
CASDOOR_APP_NAME=h3c-onu-ms
|
||||
|
||||
# ============================================
|
||||
# SSH连接配置
|
||||
# ============================================
|
||||
|
||||
# SSH连接超时时间(秒)
|
||||
SSH_TIMEOUT=30
|
||||
|
||||
# 最大并发连接数
|
||||
SSH_MAX_CONNECTIONS=10
|
||||
|
||||
# SSH重试次数
|
||||
SSH_RETRY_COUNT=3
|
||||
|
||||
# SSH连接保持时间(秒)
|
||||
SSH_KEEPALIVE=60
|
||||
|
||||
# ============================================
|
||||
# 任务调度配置
|
||||
# ============================================
|
||||
|
||||
# 自动检查间隔(秒) - 30分钟
|
||||
CHECK_INTERVAL=1800
|
||||
|
||||
# 手动刷新冷却时间(秒) - 5分钟
|
||||
MANUAL_COOLDOWN=300
|
||||
|
||||
# 历史记录保留天数
|
||||
HISTORY_RETENTION=90
|
||||
|
||||
# 批量检查设备数量
|
||||
BATCH_CHECK_SIZE=100
|
||||
|
||||
# ============================================
|
||||
# 前端配置
|
||||
# ============================================
|
||||
|
||||
# API基础URL (前端访问后端地址)
|
||||
VITE_API_BASE_URL=http://localhost:8000
|
||||
|
||||
# Casdoor前端配置
|
||||
VITE_CASDOOR_ENDPOINT=https://casdoor.example.com
|
||||
VITE_CASDOOR_CLIENT_ID=your_casdoor_client_id
|
||||
VITE_CASDOOR_ORG_NAME=your_organization
|
||||
VITE_CASDOOR_APP_NAME=h3c-onu-ms
|
||||
|
||||
# 应用标题
|
||||
VITE_APP_TITLE=H3C ONU设备管理系统
|
||||
|
||||
# ============================================
|
||||
# 日志配置
|
||||
# ============================================
|
||||
|
||||
# 日志级别 (DEBUG, INFO, WARNING, ERROR)
|
||||
LOG_LEVEL=INFO
|
||||
|
||||
# 日志文件路径
|
||||
LOG_FILE=/app/logs/app.log
|
||||
|
||||
# 日志轮转大小
|
||||
LOG_ROTATION=10MB
|
||||
|
||||
# 日志保留天数
|
||||
LOG_RETENTION=30
|
||||
|
||||
# ============================================
|
||||
# 邮件通知配置 (可选)
|
||||
# ============================================
|
||||
|
||||
# SMTP服务器
|
||||
# SMTP_HOST=smtp.example.com
|
||||
# SMTP_PORT=587
|
||||
# SMTP_USER=your_email@example.com
|
||||
# SMTP_PASSWORD=your_email_password
|
||||
# SMTP_FROM=noreply@example.com
|
||||
|
||||
# ============================================
|
||||
# 监控配置
|
||||
# ============================================
|
||||
|
||||
# 健康检查端口
|
||||
HEALTH_CHECK_PORT=8000
|
||||
|
||||
# 监控指标端口
|
||||
METRICS_PORT=8000
|
||||
|
||||
# ============================================
|
||||
# 安全配置
|
||||
# ============================================
|
||||
|
||||
# CORS允许的域名 (逗号分隔)
|
||||
CORS_ORIGINS=http://localhost:8080,http://localhost:5173
|
||||
|
||||
# 速率限制配置
|
||||
RATE_LIMIT_PER_MINUTE=60
|
||||
RATE_LIMIT_PER_HOUR=1000
|
||||
|
||||
# ============================================
|
||||
# 备份配置
|
||||
# ============================================
|
||||
|
||||
# 数据库备份目录
|
||||
BACKUP_DIR=/app/backups
|
||||
|
||||
# 备份保留天数
|
||||
BACKUP_RETENTION=30
|
||||
|
||||
# 自动备份时间 (cron格式)
|
||||
BACKUP_SCHEDULE="0 2 * * *"
|
||||
|
||||
# ============================================
|
||||
# 开发环境覆盖配置
|
||||
# ============================================
|
||||
|
||||
# 如果是开发环境,取消注释以下配置
|
||||
# DEBUG=true
|
||||
# DATABASE_URL=postgresql://h3c_user:password@localhost:5432/h3c_onu_ms_dev
|
||||
# REDIS_URL=redis://localhost:6379/0
|
||||
# VITE_API_BASE_URL=http://localhost:8000
|
||||
@@ -0,0 +1,104 @@
|
||||
services:
|
||||
# 后端API服务
|
||||
backend:
|
||||
build:
|
||||
context: ../backend
|
||||
dockerfile: Dockerfile
|
||||
container_name: h3c-onu-ms-backend
|
||||
ports:
|
||||
- "8001:8000"
|
||||
environment:
|
||||
- DATABASE_URL=${DATABASE_URL}
|
||||
- REDIS_URL=${REDIS_URL}
|
||||
- CASDOOR_ENDPOINT=${CASDOOR_ENDPOINT}
|
||||
- CASDOOR_CLIENT_ID=${CASDOOR_CLIENT_ID}
|
||||
- CASDOOR_CLIENT_SECRET=${CASDOOR_CLIENT_SECRET}
|
||||
- CASDOOR_CERTIFICATE=${CASDOOR_CERTIFICATE}
|
||||
- CASDOOR_ORG_NAME=${CASDOOR_ORG_NAME}
|
||||
- CASDOOR_APP_NAME=${CASDOOR_APP_NAME}
|
||||
- SECRET_KEY=${SECRET_KEY}
|
||||
- DEBUG=${DEBUG:-false}
|
||||
volumes:
|
||||
- ../backend/logs:/app/logs
|
||||
- ../backend/static:/app/static
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
|
||||
# Celery Worker - 处理异步任务
|
||||
celery-worker:
|
||||
build:
|
||||
context: ../backend
|
||||
dockerfile: Dockerfile
|
||||
container_name: h3c-onu-ms-celery-worker
|
||||
command: celery -A app.core.celery_app worker --loglevel=info --concurrency=4
|
||||
environment:
|
||||
- DATABASE_URL=${DATABASE_URL}
|
||||
- REDIS_URL=${REDIS_URL}
|
||||
- CASDOOR_ENDPOINT=${CASDOOR_ENDPOINT}
|
||||
- CASDOOR_CLIENT_ID=${CASDOOR_CLIENT_ID}
|
||||
- CASDOOR_CLIENT_SECRET=${CASDOOR_CLIENT_SECRET}
|
||||
- CASDOOR_CERTIFICATE=${CASDOOR_CERTIFICATE}
|
||||
- CASDOOR_ORG_NAME=${CASDOOR_ORG_NAME}
|
||||
- CASDOOR_APP_NAME=${CASDOOR_APP_NAME}
|
||||
- SECRET_KEY=${SECRET_KEY}
|
||||
volumes:
|
||||
- ../backend/logs:/app/logs
|
||||
depends_on:
|
||||
- backend
|
||||
restart: unless-stopped
|
||||
|
||||
# Celery Beat - 定时任务调度
|
||||
celery-beat:
|
||||
build:
|
||||
context: ../backend
|
||||
dockerfile: Dockerfile
|
||||
container_name: h3c-onu-ms-celery-beat
|
||||
command: celery -A app.core.celery_app beat --loglevel=info
|
||||
environment:
|
||||
- DATABASE_URL=${DATABASE_URL}
|
||||
- REDIS_URL=${REDIS_URL}
|
||||
- CASDOOR_ENDPOINT=${CASDOOR_ENDPOINT}
|
||||
- CASDOOR_CLIENT_ID=${CASDOOR_CLIENT_ID}
|
||||
- CASDOOR_CLIENT_SECRET=${CASDOOR_CLIENT_SECRET}
|
||||
- CASDOOR_CERTIFICATE=${CASDOOR_CERTIFICATE}
|
||||
- CASDOOR_ORG_NAME=${CASDOOR_ORG_NAME}
|
||||
- CASDOOR_APP_NAME=${CASDOOR_APP_NAME}
|
||||
- SECRET_KEY=${SECRET_KEY}
|
||||
volumes:
|
||||
- ../backend/logs:/app/logs
|
||||
- ../backend/celerybeat-schedule:/app/celerybeat-schedule
|
||||
depends_on:
|
||||
- backend
|
||||
restart: unless-stopped
|
||||
|
||||
# 前端应用
|
||||
frontend:
|
||||
build:
|
||||
context: ../frontend
|
||||
dockerfile: Dockerfile
|
||||
container_name: h3c-onu-ms-frontend
|
||||
ports:
|
||||
- "8081:80"
|
||||
environment:
|
||||
- VITE_API_BASE_URL=${VITE_API_BASE_URL:-http://localhost:8001}
|
||||
- VITE_CASDOOR_ENDPOINT=${VITE_CASDOOR_ENDPOINT}
|
||||
- VITE_CASDOOR_CLIENT_ID=${VITE_CASDOOR_CLIENT_ID}
|
||||
- VITE_CASDOOR_ORG_NAME=${VITE_CASDOOR_ORG_NAME}
|
||||
- VITE_CASDOOR_APP_NAME=${VITE_CASDOOR_APP_NAME}
|
||||
depends_on:
|
||||
- backend
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:80"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
|
||||
networks:
|
||||
default:
|
||||
name: h3c-onu-ms-network
|
||||
@@ -0,0 +1,165 @@
|
||||
# H3C ONU设备管理系统 - Nginx站点配置
|
||||
|
||||
# 前端静态文件服务
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
|
||||
# 根目录指向前端构建文件
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
# Gzip压缩
|
||||
gzip_static on;
|
||||
|
||||
# 静态文件缓存
|
||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
access_log off;
|
||||
}
|
||||
|
||||
# 前端路由支持 (Vue Router history模式)
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
expires -1;
|
||||
add_header Cache-Control "no-store, no-cache, must-revalidate";
|
||||
}
|
||||
|
||||
# API代理到后端
|
||||
location /api/ {
|
||||
proxy_pass http://backend:8000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_set_header X-Forwarded-Port $server_port;
|
||||
|
||||
# 超时设置
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 60s;
|
||||
proxy_read_timeout 60s;
|
||||
|
||||
# 缓冲区设置
|
||||
proxy_buffering on;
|
||||
proxy_buffer_size 4k;
|
||||
proxy_buffers 8 4k;
|
||||
proxy_busy_buffers_size 8k;
|
||||
|
||||
# 禁用代理缓存
|
||||
proxy_cache off;
|
||||
}
|
||||
|
||||
# WebSocket支持
|
||||
location /ws/ {
|
||||
proxy_pass http://backend:8000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
|
||||
# WebSocket超时设置
|
||||
proxy_read_timeout 3600s;
|
||||
proxy_send_timeout 3600s;
|
||||
}
|
||||
|
||||
# 静态文件代理 (后端静态文件)
|
||||
location /static/ {
|
||||
alias /usr/share/nginx/static/;
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
access_log off;
|
||||
}
|
||||
|
||||
# 健康检查端点
|
||||
location /health {
|
||||
proxy_pass http://backend:8000/health;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
access_log off;
|
||||
}
|
||||
|
||||
# 监控指标端点
|
||||
location /metrics {
|
||||
proxy_pass http://backend:8000/metrics;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
access_log off;
|
||||
}
|
||||
|
||||
# API文档
|
||||
location /docs {
|
||||
proxy_pass http://backend:8000/docs;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
}
|
||||
|
||||
location /redoc {
|
||||
proxy_pass http://backend:8000/redoc;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
}
|
||||
|
||||
# 错误页面
|
||||
error_page 404 /index.html;
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
|
||||
location = /50x.html {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
}
|
||||
|
||||
# HTTPS配置 (需要SSL证书)
|
||||
# server {
|
||||
# listen 443 ssl http2;
|
||||
# server_name your-domain.com;
|
||||
#
|
||||
# # SSL证书配置
|
||||
# ssl_certificate /etc/nginx/ssl/your-domain.com.crt;
|
||||
# ssl_certificate_key /etc/nginx/ssl/your-domain.com.key;
|
||||
#
|
||||
# # SSL优化配置
|
||||
# ssl_protocols TLSv1.2 TLSv1.3;
|
||||
# ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384;
|
||||
# ssl_prefer_server_ciphers off;
|
||||
# ssl_session_cache shared:SSL:10m;
|
||||
# ssl_session_timeout 10m;
|
||||
#
|
||||
# # HSTS头
|
||||
# add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
#
|
||||
# # 其他配置与HTTP相同
|
||||
# root /usr/share/nginx/html;
|
||||
# index index.html;
|
||||
#
|
||||
# location / {
|
||||
# try_files $uri $uri/ /index.html;
|
||||
# }
|
||||
#
|
||||
# location /api/ {
|
||||
# proxy_pass http://backend:8000;
|
||||
# # ... 其他代理配置
|
||||
# }
|
||||
#
|
||||
# # 强制HTTP重定向到HTTPS
|
||||
# # if ($scheme != "https") {
|
||||
# # return 301 https://$host$request_uri;
|
||||
# # }
|
||||
# }
|
||||
|
||||
# 重定向HTTP到HTTPS (如果启用HTTPS)
|
||||
# server {
|
||||
# listen 80;
|
||||
# server_name your-domain.com;
|
||||
# return 301 https://$server_name$request_uri;
|
||||
# }
|
||||
@@ -0,0 +1,56 @@
|
||||
user nginx;
|
||||
worker_processes auto;
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
use epoll;
|
||||
multi_accept on;
|
||||
}
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
# 日志格式
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
access_log /var/log/nginx/access.log main;
|
||||
|
||||
# 基础配置
|
||||
sendfile on;
|
||||
tcp_nopush on;
|
||||
tcp_nodelay on;
|
||||
keepalive_timeout 65;
|
||||
types_hash_max_size 2048;
|
||||
client_max_body_size 100M;
|
||||
|
||||
# Gzip压缩
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_min_length 1024;
|
||||
gzip_proxied any;
|
||||
gzip_comp_level 6;
|
||||
gzip_types
|
||||
text/plain
|
||||
text/css
|
||||
text/xml
|
||||
text/javascript
|
||||
application/json
|
||||
application/javascript
|
||||
application/xml+rss
|
||||
application/atom+xml
|
||||
image/svg+xml;
|
||||
|
||||
# 安全头
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||
|
||||
# 包含站点配置
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
}
|
||||
Executable
+362
@@ -0,0 +1,362 @@
|
||||
#!/bin/bash
|
||||
|
||||
# H3C ONU设备管理系统 - 部署脚本
|
||||
# 使用方法: ./deploy.sh [环境] [操作]
|
||||
# 环境: dev, prod (默认: prod)
|
||||
# 操作: up, down, restart, logs, build (默认: up)
|
||||
|
||||
set -e
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 默认值
|
||||
ENV=${1:-prod}
|
||||
ACTION=${2:-up}
|
||||
COMPOSE_FILE="docker-compose.yml"
|
||||
|
||||
# 根据环境选择配置文件
|
||||
if [ "$ENV" = "dev" ]; then
|
||||
COMPOSE_FILE="docker-compose.dev.yml"
|
||||
echo -e "${BLUE}使用开发环境配置${NC}"
|
||||
elif [ "$ENV" = "prod" ]; then
|
||||
COMPOSE_FILE="docker-compose.yml"
|
||||
echo -e "${BLUE}使用生产环境配置${NC}"
|
||||
else
|
||||
echo -e "${RED}错误: 未知环境 '$ENV'${NC}"
|
||||
echo "可用环境: dev, prod"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 检查Docker Compose文件是否存在
|
||||
if [ ! -f "$COMPOSE_FILE" ]; then
|
||||
echo -e "${RED}错误: Docker Compose文件 '$COMPOSE_FILE' 不存在${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 检查环境变量文件
|
||||
if [ ! -f ".env" ]; then
|
||||
echo -e "${YELLOW}警告: .env 文件不存在,从 .env.example 复制${NC}"
|
||||
if [ -f ".env.example" ]; then
|
||||
cp .env.example .env
|
||||
echo -e "${YELLOW}请编辑 .env 文件并设置正确的配置${NC}"
|
||||
exit 1
|
||||
else
|
||||
echo -e "${RED}错误: .env.example 文件也不存在${NC}"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# 加载环境变量
|
||||
set -a
|
||||
source .env
|
||||
set +a
|
||||
|
||||
# 函数:显示帮助信息
|
||||
show_help() {
|
||||
echo "H3C ONU设备管理系统 - 部署脚本"
|
||||
echo ""
|
||||
echo "使用方法: $0 [环境] [操作]"
|
||||
echo ""
|
||||
echo "环境:"
|
||||
echo " dev 开发环境"
|
||||
echo " prod 生产环境 (默认)"
|
||||
echo ""
|
||||
echo "操作:"
|
||||
echo " up 启动服务 (默认)"
|
||||
echo " down 停止服务"
|
||||
echo " restart 重启服务"
|
||||
echo " logs 查看日志"
|
||||
echo " build 构建镜像"
|
||||
echo " ps 查看服务状态"
|
||||
echo " exec 进入容器"
|
||||
echo " backup 备份数据"
|
||||
echo " restore 恢复数据"
|
||||
echo ""
|
||||
echo "示例:"
|
||||
echo " $0 prod up # 启动生产环境"
|
||||
echo " $0 dev logs # 查看开发环境日志"
|
||||
echo " $0 prod restart # 重启生产环境"
|
||||
}
|
||||
|
||||
# 函数:检查Docker是否运行
|
||||
check_docker() {
|
||||
if ! docker info > /dev/null 2>&1; then
|
||||
echo -e "${RED}错误: Docker未运行或当前用户无权限${NC}"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 函数:检查Docker Compose是否安装
|
||||
check_docker_compose() {
|
||||
if ! command -v docker-compose &> /dev/null; then
|
||||
echo -e "${RED}错误: Docker Compose未安装${NC}"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 函数:启动服务
|
||||
start_services() {
|
||||
echo -e "${GREEN}启动服务...${NC}"
|
||||
docker-compose -f "$COMPOSE_FILE" up -d
|
||||
|
||||
# 等待服务启动
|
||||
echo -e "${BLUE}等待服务启动...${NC}"
|
||||
sleep 10
|
||||
|
||||
# 检查服务状态
|
||||
check_services_status
|
||||
}
|
||||
|
||||
# 函数:停止服务
|
||||
stop_services() {
|
||||
echo -e "${YELLOW}停止服务...${NC}"
|
||||
docker-compose -f "$COMPOSE_FILE" down
|
||||
}
|
||||
|
||||
# 函数:重启服务
|
||||
restart_services() {
|
||||
echo -e "${YELLOW}重启服务...${NC}"
|
||||
docker-compose -f "$COMPOSE_FILE" restart
|
||||
}
|
||||
|
||||
# 函数:查看日志
|
||||
show_logs() {
|
||||
echo -e "${BLUE}查看日志 (Ctrl+C退出)...${NC}"
|
||||
docker-compose -f "$COMPOSE_FILE" logs -f
|
||||
}
|
||||
|
||||
# 函数:构建镜像
|
||||
build_images() {
|
||||
echo -e "${GREEN}构建Docker镜像...${NC}"
|
||||
docker-compose -f "$COMPOSE_FILE" build --no-cache
|
||||
|
||||
# 清理未使用的镜像
|
||||
echo -e "${BLUE}清理未使用的镜像...${NC}"
|
||||
docker image prune -f
|
||||
}
|
||||
|
||||
# 函数:查看服务状态
|
||||
show_status() {
|
||||
echo -e "${BLUE}服务状态:${NC}"
|
||||
docker-compose -f "$COMPOSE_FILE" ps
|
||||
|
||||
echo -e "\n${BLUE}容器资源使用:${NC}"
|
||||
docker stats --no-stream $(docker-compose -f "$COMPOSE_FILE" ps -q)
|
||||
}
|
||||
|
||||
# 函数:进入容器
|
||||
exec_container() {
|
||||
echo -e "${BLUE}选择要进入的容器:${NC}"
|
||||
echo "1) backend (后端API)"
|
||||
echo "2) frontend (前端)"
|
||||
echo "3) celery-worker (Celery Worker)"
|
||||
echo "4) celery-beat (Celery Beat)"
|
||||
echo "5) nginx (Nginx)"
|
||||
echo "6) redis (Redis)"
|
||||
|
||||
read -p "请输入数字 (1-6): " choice
|
||||
|
||||
case $choice in
|
||||
1) CONTAINER="backend" ;;
|
||||
2) CONTAINER="frontend" ;;
|
||||
3) CONTAINER="celery-worker" ;;
|
||||
4) CONTAINER="celery-beat" ;;
|
||||
5) CONTAINER="nginx" ;;
|
||||
6) CONTAINER="redis" ;;
|
||||
*)
|
||||
echo -e "${RED}无效的选择${NC}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
echo -e "${GREEN}进入 $CONTAINER 容器...${NC}"
|
||||
docker-compose -f "$COMPOSE_FILE" exec "$CONTAINER" sh
|
||||
}
|
||||
|
||||
# 函数:备份数据
|
||||
backup_data() {
|
||||
BACKUP_DIR="backups/$(date +%Y%m%d_%H%M%S)"
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
|
||||
echo -e "${GREEN}开始备份数据...${NC}"
|
||||
|
||||
# 备份数据库
|
||||
echo -e "${BLUE}备份数据库...${NC}"
|
||||
DB_CONTAINER=$(docker-compose -f "$COMPOSE_FILE" ps -q backend)
|
||||
if [ -n "$DB_CONTAINER" ]; then
|
||||
docker exec "$DB_CONTAINER" pg_dump "$DATABASE_URL" > "$BACKUP_DIR/database.sql"
|
||||
echo "数据库备份完成: $BACKUP_DIR/database.sql"
|
||||
else
|
||||
echo -e "${YELLOW}警告: 数据库容器未运行${NC}"
|
||||
fi
|
||||
|
||||
# 备份Redis数据
|
||||
echo -e "${BLUE}备份Redis数据...${NC}"
|
||||
REDIS_CONTAINER=$(docker-compose -f "$COMPOSE_FILE" ps -q redis)
|
||||
if [ -n "$REDIS_CONTAINER" ]; then
|
||||
docker exec "$REDIS_CONTAINER" redis-cli --rdb /data/dump.rdb
|
||||
docker cp "$REDIS_CONTAINER:/data/dump.rdb" "$BACKUP_DIR/redis.rdb"
|
||||
echo "Redis备份完成: $BACKUP_DIR/redis.rdb"
|
||||
else
|
||||
echo -e "${YELLOW}警告: Redis容器未运行${NC}"
|
||||
fi
|
||||
|
||||
# 备份配置文件
|
||||
echo -e "${BLUE}备份配置文件...${NC}"
|
||||
cp .env "$BACKUP_DIR/"
|
||||
cp "$COMPOSE_FILE" "$BACKUP_DIR/"
|
||||
|
||||
# 创建备份信息文件
|
||||
echo "备份时间: $(date)" > "$BACKUP_DIR/backup.info"
|
||||
echo "环境: $ENV" >> "$BACKUP_DIR/backup.info"
|
||||
echo "版本: $(git describe --tags 2>/dev/null || echo '未知')" >> "$BACKUP_DIR/backup.info"
|
||||
|
||||
# 压缩备份文件
|
||||
echo -e "${BLUE}压缩备份文件...${NC}"
|
||||
tar -czf "$BACKUP_DIR.tar.gz" "$BACKUP_DIR"
|
||||
rm -rf "$BACKUP_DIR"
|
||||
|
||||
echo -e "${GREEN}备份完成: $BACKUP_DIR.tar.gz${NC}"
|
||||
}
|
||||
|
||||
# 函数:恢复数据
|
||||
restore_data() {
|
||||
echo -e "${YELLOW}警告: 恢复数据将覆盖现有数据${NC}"
|
||||
read -p "请输入要恢复的备份文件路径: " BACKUP_FILE
|
||||
|
||||
if [ ! -f "$BACKUP_FILE" ]; then
|
||||
echo -e "${RED}错误: 备份文件不存在${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 解压备份文件
|
||||
BACKUP_DIR="${BACKUP_FILE%.tar.gz}"
|
||||
echo -e "${BLUE}解压备份文件...${NC}"
|
||||
tar -xzf "$BACKUP_FILE"
|
||||
|
||||
# 停止服务
|
||||
echo -e "${BLUE}停止服务...${NC}"
|
||||
docker-compose -f "$COMPOSE_FILE" down
|
||||
|
||||
# 恢复数据库
|
||||
if [ -f "$BACKUP_DIR/database.sql" ]; then
|
||||
echo -e "${BLUE}恢复数据库...${NC}"
|
||||
# 这里需要根据实际情况调整数据库恢复命令
|
||||
# 例如: psql -d database -f backup.sql
|
||||
echo "数据库恢复命令需要根据实际情况配置"
|
||||
fi
|
||||
|
||||
# 恢复Redis
|
||||
if [ -f "$BACKUP_DIR/redis.rdb" ]; then
|
||||
echo -e "${BLUE}恢复Redis数据...${NC}"
|
||||
# 这里需要根据实际情况调整Redis恢复命令
|
||||
echo "Redis恢复命令需要根据实际情况配置"
|
||||
fi
|
||||
|
||||
# 恢复配置文件
|
||||
if [ -f "$BACKUP_DIR/.env" ]; then
|
||||
echo -e "${BLUE}恢复配置文件...${NC}"
|
||||
cp "$BACKUP_DIR/.env" .
|
||||
fi
|
||||
|
||||
# 清理临时文件
|
||||
rm -rf "$BACKUP_DIR"
|
||||
|
||||
# 启动服务
|
||||
echo -e "${BLUE}启动服务...${NC}"
|
||||
docker-compose -f "$COMPOSE_FILE" up -d
|
||||
|
||||
echo -e "${GREEN}数据恢复完成${NC}"
|
||||
}
|
||||
|
||||
# 函数:检查服务状态
|
||||
check_services_status() {
|
||||
echo -e "${BLUE}检查服务健康状态...${NC}"
|
||||
|
||||
# 检查后端服务
|
||||
if curl -f http://localhost:8000/health > /dev/null 2>&1; then
|
||||
echo -e "${GREEN}✓ 后端服务运行正常${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ 后端服务异常${NC}"
|
||||
fi
|
||||
|
||||
# 检查前端服务
|
||||
if curl -f http://localhost:8080 > /dev/null 2>&1; then
|
||||
echo -e "${GREEN}✓ 前端服务运行正常${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ 前端服务异常${NC}"
|
||||
fi
|
||||
|
||||
# 检查Redis
|
||||
REDIS_CONTAINER=$(docker-compose -f "$COMPOSE_FILE" ps -q redis)
|
||||
if [ -n "$REDIS_CONTAINER" ] && docker exec "$REDIS_CONTAINER" redis-cli ping > /dev/null 2>&1; then
|
||||
echo -e "${GREEN}✓ Redis服务运行正常${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ Redis服务异常${NC}"
|
||||
fi
|
||||
|
||||
echo -e "\n${GREEN}部署完成!${NC}"
|
||||
echo -e "前端访问: ${BLUE}http://localhost:8080${NC}"
|
||||
echo -e "API文档: ${BLUE}http://localhost:8000/docs${NC}"
|
||||
echo -e "健康检查: ${BLUE}http://localhost:8000/health${NC}"
|
||||
}
|
||||
|
||||
# 主程序
|
||||
main() {
|
||||
# 显示标题
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
echo -e "${GREEN} H3C ONU设备管理系统 - 部署工具${NC}"
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
echo ""
|
||||
|
||||
# 检查依赖
|
||||
check_docker
|
||||
check_docker_compose
|
||||
|
||||
# 根据操作执行相应命令
|
||||
case "$ACTION" in
|
||||
up)
|
||||
start_services
|
||||
;;
|
||||
down)
|
||||
stop_services
|
||||
;;
|
||||
restart)
|
||||
restart_services
|
||||
;;
|
||||
logs)
|
||||
show_logs
|
||||
;;
|
||||
build)
|
||||
build_images
|
||||
;;
|
||||
ps)
|
||||
show_status
|
||||
;;
|
||||
exec)
|
||||
exec_container
|
||||
;;
|
||||
backup)
|
||||
backup_data
|
||||
;;
|
||||
restore)
|
||||
restore_data
|
||||
;;
|
||||
help|--help|-h)
|
||||
show_help
|
||||
;;
|
||||
*)
|
||||
echo -e "${RED}错误: 未知操作 '$ACTION'${NC}"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# 执行主程序
|
||||
main "$@"
|
||||
Executable
+389
@@ -0,0 +1,389 @@
|
||||
#!/bin/bash
|
||||
|
||||
# H3C ONU设备管理系统 - 数据库初始化脚本
|
||||
# 用于初始化数据库和创建基础数据
|
||||
|
||||
set -e
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 加载环境变量
|
||||
if [ -f "../.env" ]; then
|
||||
set -a
|
||||
source ../.env
|
||||
set +a
|
||||
else
|
||||
echo -e "${RED}错误: .env 文件不存在${NC}"
|
||||
echo "请先复制 .env.example 为 .env 并配置数据库连接"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 检查数据库连接字符串
|
||||
if [ -z "$DATABASE_URL" ]; then
|
||||
echo -e "${RED}错误: DATABASE_URL 未设置${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 从DATABASE_URL解析连接信息
|
||||
parse_db_url() {
|
||||
local url="$1"
|
||||
# 格式: postgresql://username:password@host:port/database
|
||||
local pattern="postgresql://([^:]+):([^@]+)@([^:]+):([^/]+)/(.+)"
|
||||
|
||||
if [[ $url =~ $pattern ]]; then
|
||||
DB_USER="${BASH_REMATCH[1]}"
|
||||
DB_PASSWORD="${BASH_REMATCH[2]}"
|
||||
DB_HOST="${BASH_REMATCH[3]}"
|
||||
DB_PORT="${BASH_REMATCH[4]}"
|
||||
DB_NAME="${BASH_REMATCH[5]}"
|
||||
else
|
||||
echo -e "${RED}错误: 无法解析DATABASE_URL${NC}"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
parse_db_url "$DATABASE_URL"
|
||||
|
||||
# 函数:检查PostgreSQL连接
|
||||
check_postgres_connection() {
|
||||
echo -e "${BLUE}检查PostgreSQL连接...${NC}"
|
||||
|
||||
if PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d postgres -c "SELECT 1;" > /dev/null 2>&1; then
|
||||
echo -e "${GREEN}✓ PostgreSQL连接成功${NC}"
|
||||
return 0
|
||||
else
|
||||
echo -e "${RED}✗ PostgreSQL连接失败${NC}"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 函数:检查数据库是否存在
|
||||
check_database_exists() {
|
||||
echo -e "${BLUE}检查数据库 '$DB_NAME' 是否存在...${NC}"
|
||||
|
||||
if PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -c "SELECT 1;" > /dev/null 2>&1; then
|
||||
echo -e "${GREEN}✓ 数据库已存在${NC}"
|
||||
return 0
|
||||
else
|
||||
echo -e "${YELLOW}数据库不存在${NC}"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 函数:创建数据库
|
||||
create_database() {
|
||||
echo -e "${BLUE}创建数据库 '$DB_NAME'...${NC}"
|
||||
|
||||
# 创建数据库
|
||||
PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d postgres <<EOF
|
||||
CREATE DATABASE $DB_NAME;
|
||||
EOF
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo -e "${GREEN}✓ 数据库创建成功${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ 数据库创建失败${NC}"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 函数:创建数据库用户(如果不存在)
|
||||
create_database_user() {
|
||||
echo -e "${BLUE}检查数据库用户 '$DB_USER'...${NC}"
|
||||
|
||||
# 检查用户是否存在
|
||||
USER_EXISTS=$(PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d postgres -tAc "SELECT 1 FROM pg_roles WHERE rolname='$DB_USER'")
|
||||
|
||||
if [ "$USER_EXISTS" = "1" ]; then
|
||||
echo -e "${GREEN}✓ 用户已存在${NC}"
|
||||
else
|
||||
echo -e "${BLUE}创建用户 '$DB_USER'...${NC}"
|
||||
|
||||
# 需要以postgres用户创建新用户
|
||||
# 这里假设有postgres用户的密码或者使用trust认证
|
||||
echo -e "${YELLOW}注意: 需要以postgres用户权限创建新用户${NC}"
|
||||
echo "请手动执行以下命令:"
|
||||
echo " psql -h $DB_HOST -p $DB_PORT -U postgres -d postgres"
|
||||
echo " CREATE USER $DB_USER WITH PASSWORD '$DB_PASSWORD';"
|
||||
echo " GRANT ALL PRIVILEGES ON DATABASE $DB_NAME TO $DB_USER;"
|
||||
|
||||
read -p "是否已手动创建用户? (y/n): " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
echo -e "${YELLOW}跳过用户创建${NC}"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# 函数:授予权限
|
||||
grant_privileges() {
|
||||
echo -e "${BLUE}授予用户权限...${NC}"
|
||||
|
||||
# 授予数据库权限
|
||||
PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d postgres <<EOF
|
||||
GRANT ALL PRIVILEGES ON DATABASE $DB_NAME TO $DB_USER;
|
||||
EOF
|
||||
|
||||
# 授予schema权限
|
||||
PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" <<EOF
|
||||
GRANT ALL ON SCHEMA public TO $DB_USER;
|
||||
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO $DB_USER;
|
||||
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO $DB_USER;
|
||||
GRANT ALL PRIVILEGES ON ALL FUNCTIONS IN SCHEMA public TO $DB_USER;
|
||||
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO $DB_USER;
|
||||
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO $DB_USER;
|
||||
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON FUNCTIONS TO $DB_USER;
|
||||
EOF
|
||||
|
||||
echo -e "${GREEN}✓ 权限授予成功${NC}"
|
||||
}
|
||||
|
||||
# 函数:运行数据库迁移
|
||||
run_migrations() {
|
||||
echo -e "${BLUE}运行数据库迁移...${NC}"
|
||||
|
||||
# 检查后端目录是否存在
|
||||
if [ ! -d "../../backend" ]; then
|
||||
echo -e "${YELLOW}警告: 后端目录不存在,跳过迁移${NC}"
|
||||
return
|
||||
fi
|
||||
|
||||
# 进入后端目录
|
||||
cd "../../backend"
|
||||
|
||||
# 检查Alembic配置
|
||||
if [ ! -f "alembic.ini" ]; then
|
||||
echo -e "${YELLOW}警告: alembic.ini 不存在,跳过迁移${NC}"
|
||||
cd - > /dev/null
|
||||
return
|
||||
fi
|
||||
|
||||
# 运行迁移
|
||||
if command -v alembic &> /dev/null; then
|
||||
# 设置数据库URL环境变量
|
||||
export DATABASE_URL
|
||||
|
||||
# 初始化迁移(如果第一次运行)
|
||||
if [ ! -d "alembic/versions" ]; then
|
||||
echo -e "${BLUE}初始化Alembic迁移...${NC}"
|
||||
alembic init alembic
|
||||
# 需要手动配置alembic.ini中的sqlalchemy.url
|
||||
fi
|
||||
|
||||
# 创建迁移版本
|
||||
echo -e "${BLUE}创建迁移版本...${NC}"
|
||||
alembic revision --autogenerate -m "初始迁移"
|
||||
|
||||
# 应用迁移
|
||||
echo -e "${BLUE}应用迁移...${NC}"
|
||||
alembic upgrade head
|
||||
|
||||
echo -e "${GREEN}✓ 数据库迁移完成${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}警告: alembic 命令未找到,跳过迁移${NC}"
|
||||
echo "请安装: pip install alembic"
|
||||
fi
|
||||
|
||||
# 返回原目录
|
||||
cd - > /dev/null
|
||||
}
|
||||
|
||||
# 函数:创建基础数据
|
||||
create_base_data() {
|
||||
echo -e "${BLUE}创建基础数据...${NC}"
|
||||
|
||||
# 检查数据库连接
|
||||
if ! check_database_exists; then
|
||||
echo -e "${RED}错误: 数据库不存在,无法创建基础数据${NC}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 创建基础数据SQL
|
||||
BASE_DATA_SQL=$(cat <<EOF
|
||||
-- 创建系统配置
|
||||
INSERT INTO system_config (config_key, config_value, description) VALUES
|
||||
('check_interval', '1800', '自动检查间隔(秒)'),
|
||||
('manual_cooldown', '300', '手动刷新冷却时间(秒)'),
|
||||
('history_retention', '90', '历史记录保留天数'),
|
||||
('ssh_timeout', '30', 'SSH连接超时时间'),
|
||||
('ssh_max_connections', '10', 'SSH最大连接数'),
|
||||
('batch_check_size', '100', '批量检查设备数量')
|
||||
ON CONFLICT (config_key) DO NOTHING;
|
||||
|
||||
-- 创建默认角色
|
||||
INSERT INTO roles (name, description, is_system) VALUES
|
||||
('super_admin', '超级管理员', true),
|
||||
('admin', '管理员', true),
|
||||
('area_admin', '区域管理员', true),
|
||||
('school_admin', '学校管理员', true),
|
||||
('operator', '操作员', true),
|
||||
('viewer', '查看员', true)
|
||||
ON CONFLICT (name) DO NOTHING;
|
||||
|
||||
-- 创建基础权限
|
||||
INSERT INTO permissions (name, code, module, description) VALUES
|
||||
-- 设备模块权限
|
||||
('设备查看', 'device_view', 'device', '查看设备信息'),
|
||||
('设备编辑', 'device_edit', 'device', '编辑设备信息'),
|
||||
('设备删除', 'device_delete', 'device', '删除设备'),
|
||||
('设备导入', 'device_import', 'device', '导入设备数据'),
|
||||
('设备检查', 'device_check', 'device', '检查设备状态'),
|
||||
-- 用户模块权限
|
||||
('用户查看', 'user_view', 'user', '查看用户信息'),
|
||||
('用户管理', 'user_manage', 'user', '管理用户'),
|
||||
('角色管理', 'role_manage', 'user', '管理角色'),
|
||||
('权限管理', 'permission_manage', 'user', '管理权限'),
|
||||
-- 系统模块权限
|
||||
('系统配置', 'system_config', 'system', '系统配置管理'),
|
||||
('OLT管理', 'olt_manage', 'system', 'OLT设备管理'),
|
||||
('任务管理', 'task_manage', 'system', '任务配置管理'),
|
||||
('审核管理', 'audit_manage', 'system', '审核流程管理'),
|
||||
-- 统计模块权限
|
||||
('统计查看', 'stats_view', 'stats', '查看统计信息'),
|
||||
('报表导出', 'report_export', 'stats', '导出报表')
|
||||
ON CONFLICT (code) DO NOTHING;
|
||||
|
||||
-- 为超级管理员角色分配所有权限
|
||||
INSERT INTO role_permissions (role_id, permission_id)
|
||||
SELECT r.id, p.id
|
||||
FROM roles r
|
||||
CROSS JOIN permissions p
|
||||
WHERE r.name = 'super_admin'
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- 为管理员角色分配大部分权限(除了用户管理相关)
|
||||
INSERT INTO role_permissions (role_id, permission_id)
|
||||
SELECT r.id, p.id
|
||||
FROM roles r
|
||||
CROSS JOIN permissions p
|
||||
WHERE r.name = 'admin'
|
||||
AND p.code NOT IN ('user_manage', 'role_manage', 'permission_manage')
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- 为区域管理员分配基础权限
|
||||
INSERT INTO role_permissions (role_id, permission_id)
|
||||
SELECT r.id, p.id
|
||||
FROM roles r
|
||||
CROSS JOIN permissions p
|
||||
WHERE r.name = 'area_admin'
|
||||
AND p.code IN ('device_view', 'device_edit', 'device_check', 'stats_view')
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- 为学校管理员分配基础权限
|
||||
INSERT INTO role_permissions (role_id, permission_id)
|
||||
SELECT r.id, p.id
|
||||
FROM roles r
|
||||
CROSS JOIN permissions p
|
||||
WHERE r.name = 'school_admin'
|
||||
AND p.code IN ('device_view', 'device_edit', 'device_check')
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- 为操作员分配基础权限
|
||||
INSERT INTO role_permissions (role_id, permission_id)
|
||||
SELECT r.id, p.id
|
||||
FROM roles r
|
||||
CROSS JOIN permissions p
|
||||
WHERE r.name = 'operator'
|
||||
AND p.code IN ('device_view', 'device_edit', 'device_check')
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- 为查看员分配只读权限
|
||||
INSERT INTO role_permissions (role_id, permission_id)
|
||||
SELECT r.id, p.id
|
||||
FROM roles r
|
||||
CROSS JOIN permissions p
|
||||
WHERE r.name = 'viewer'
|
||||
AND p.code IN ('device_view', 'stats_view')
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- 创建默认系统用户(通过Casdoor同步,这里只是占位)
|
||||
INSERT INTO users (casdoor_id, username, email, role, is_active) VALUES
|
||||
('system', '系统用户', 'system@example.com', 'super_admin', true)
|
||||
ON CONFLICT (casdoor_id) DO NOTHING;
|
||||
|
||||
EOF
|
||||
)
|
||||
|
||||
# 执行SQL
|
||||
echo "$BASE_DATA_SQL" | PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo -e "${GREEN}✓ 基础数据创建成功${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}警告: 基础数据创建可能失败,表可能不存在${NC}"
|
||||
echo "请先运行数据库迁移"
|
||||
fi
|
||||
}
|
||||
|
||||
# 函数:显示数据库信息
|
||||
show_database_info() {
|
||||
echo -e "\n${GREEN}========================================${NC}"
|
||||
echo -e "${GREEN} 数据库初始化完成${NC}"
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
echo ""
|
||||
echo -e "${BLUE}数据库连接信息:${NC}"
|
||||
echo " 主机: $DB_HOST"
|
||||
echo " 端口: $DB_PORT"
|
||||
echo " 数据库: $DB_NAME"
|
||||
echo " 用户: $DB_USER"
|
||||
echo ""
|
||||
echo -e "${BLUE}连接字符串:${NC}"
|
||||
echo " $DATABASE_URL"
|
||||
echo ""
|
||||
echo -e "${BLUE}下一步:${NC}"
|
||||
echo " 1. 启动后端服务运行数据库迁移"
|
||||
echo " 2. 配置Casdoor认证"
|
||||
echo " 3. 导入设备数据"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 主程序
|
||||
main() {
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
echo -e "${GREEN} H3C ONU设备管理系统 - 数据库初始化${NC}"
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
echo ""
|
||||
|
||||
# 检查PostgreSQL连接
|
||||
if ! check_postgres_connection; then
|
||||
echo -e "${RED}请检查PostgreSQL服务是否运行,以及连接信息是否正确${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 检查数据库是否存在
|
||||
if check_database_exists; then
|
||||
echo -e "${YELLOW}数据库已存在,是否重新初始化?${NC}"
|
||||
read -p "重新初始化将删除现有数据! (y/n): " -n 1 -r
|
||||
echo
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
echo -e "${YELLOW}删除数据库 '$DB_NAME'...${NC}"
|
||||
PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d postgres -c "DROP DATABASE IF EXISTS $DB_NAME;"
|
||||
create_database
|
||||
else
|
||||
echo -e "${YELLOW}跳过数据库创建${NC}"
|
||||
fi
|
||||
else
|
||||
create_database
|
||||
fi
|
||||
|
||||
# 创建用户和授予权限
|
||||
create_database_user
|
||||
grant_privileges
|
||||
|
||||
# 运行迁移(如果后端代码已存在)
|
||||
run_migrations
|
||||
|
||||
# 创建基础数据
|
||||
create_base_data
|
||||
|
||||
# 显示完成信息
|
||||
show_database_info
|
||||
}
|
||||
|
||||
# 执行主程序
|
||||
main "$@"
|
||||
Executable
+368
@@ -0,0 +1,368 @@
|
||||
#!/bin/bash
|
||||
|
||||
# H3C ONU设备管理系统 - 系统监控脚本
|
||||
# 用于监控系统状态、资源使用和性能指标
|
||||
|
||||
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
|
||||
|
||||
# 默认值
|
||||
INTERVAL=${1:-5} # 监控间隔(秒)
|
||||
DURATION=${2:-0} # 监控时长(0表示无限)
|
||||
COMPOSE_FILE="docker-compose.yml"
|
||||
|
||||
# 加载环境变量
|
||||
if [ -f ".env" ]; then
|
||||
set -a
|
||||
source .env
|
||||
set +a
|
||||
fi
|
||||
|
||||
# 函数:显示帮助信息
|
||||
show_help() {
|
||||
echo "H3C ONU设备管理系统 - 系统监控脚本"
|
||||
echo ""
|
||||
echo "使用方法: $0 [间隔] [时长]"
|
||||
echo ""
|
||||
echo "参数:"
|
||||
echo " 间隔 监控间隔(秒,默认: 5)"
|
||||
echo " 时长 监控总时长(秒,0表示无限,默认: 0)"
|
||||
echo ""
|
||||
echo "示例:"
|
||||
echo " $0 # 每5秒监控一次,无限时长"
|
||||
echo " $0 10 60 # 每10秒监控一次,持续60秒"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 函数:检查Docker Compose服务状态
|
||||
check_services_status() {
|
||||
echo -e "${BLUE}=== 服务状态 ===${NC}"
|
||||
|
||||
if ! command -v docker-compose &> /dev/null; then
|
||||
echo -e "${RED}错误: docker-compose 未安装${NC}"
|
||||
return
|
||||
fi
|
||||
|
||||
# 获取服务状态
|
||||
SERVICES=$(docker-compose -f "$COMPOSE_FILE" ps --services)
|
||||
|
||||
for service in $SERVICES; do
|
||||
STATUS=$(docker-compose -f "$COMPOSE_FILE" ps --filter "name=$service" --format "table {{.Status}}" | tail -1)
|
||||
CONTAINER_ID=$(docker-compose -f "$COMPOSE_FILE" ps -q "$service")
|
||||
|
||||
if [ -n "$CONTAINER_ID" ]; then
|
||||
# 检查容器是否运行
|
||||
if docker inspect --format='{{.State.Status}}' "$CONTAINER_ID" | grep -q "running"; then
|
||||
# 检查健康状态
|
||||
HEALTH=$(docker inspect --format='{{if .State.Health}}{{.State.Health.Status}}{{else}}no healthcheck{{end}}' "$CONTAINER_ID")
|
||||
|
||||
if [ "$HEALTH" = "healthy" ]; then
|
||||
echo -e "${GREEN}✓ $service: 运行中 (健康)${NC}"
|
||||
elif [ "$HEALTH" = "no healthcheck" ]; then
|
||||
echo -e "${GREEN}✓ $service: 运行中${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}⚠ $service: 运行中 ($HEALTH)${NC}"
|
||||
fi
|
||||
else
|
||||
echo -e "${RED}✗ $service: 未运行${NC}"
|
||||
fi
|
||||
else
|
||||
echo -e "${RED}✗ $service: 容器不存在${NC}"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# 函数:检查系统资源使用
|
||||
check_system_resources() {
|
||||
echo -e "\n${BLUE}=== 系统资源 ===${NC}"
|
||||
|
||||
# CPU使用率
|
||||
CPU_USAGE=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1)
|
||||
echo -e "CPU使用率: ${CYAN}${CPU_USAGE}%${NC}"
|
||||
|
||||
# 内存使用
|
||||
MEM_TOTAL=$(free -m | awk '/Mem:/ {print $2}')
|
||||
MEM_USED=$(free -m | awk '/Mem:/ {print $3}')
|
||||
MEM_PERCENT=$((MEM_USED * 100 / MEM_TOTAL))
|
||||
|
||||
if [ $MEM_PERCENT -lt 70 ]; then
|
||||
echo -e "内存使用: ${GREEN}${MEM_USED}MB/${MEM_TOTAL}MB (${MEM_PERCENT}%)${NC}"
|
||||
elif [ $MEM_PERCENT -lt 90 ]; then
|
||||
echo -e "内存使用: ${YELLOW}${MEM_USED}MB/${MEM_TOTAL}MB (${MEM_PERCENT}%)${NC}"
|
||||
else
|
||||
echo -e "内存使用: ${RED}${MEM_USED}MB/${MEM_TOTAL}MB (${MEM_PERCENT}%)${NC}"
|
||||
fi
|
||||
|
||||
# 磁盘使用
|
||||
DISK_USAGE=$(df -h / | awk 'NR==2 {print $5}' | cut -d'%' -f1)
|
||||
DISK_TOTAL=$(df -h / | awk 'NR==2 {print $2}')
|
||||
DISK_USED=$(df -h / | awk 'NR==2 {print $3}')
|
||||
|
||||
if [ $DISK_USAGE -lt 70 ]; then
|
||||
echo -e "磁盘使用: ${GREEN}${DISK_USED}/${DISK_TOTAL} (${DISK_USAGE}%)${NC}"
|
||||
elif [ $DISK_USAGE -lt 90 ]; then
|
||||
echo -e "磁盘使用: ${YELLOW}${DISK_USED}/${DISK_TOTAL} (${DISK_USAGE}%)${NC}"
|
||||
else
|
||||
echo -e "磁盘使用: ${RED}${DISK_USED}/${DISK_TOTAL} (${DISK_USAGE}%)${NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
# 函数:检查容器资源使用
|
||||
check_container_resources() {
|
||||
echo -e "\n${BLUE}=== 容器资源 ===${NC}"
|
||||
|
||||
if ! command -v docker &> /dev/null; then
|
||||
echo -e "${RED}错误: docker 未安装${NC}"
|
||||
return
|
||||
fi
|
||||
|
||||
# 获取所有运行中的容器
|
||||
CONTAINERS=$(docker ps --format "{{.Names}}")
|
||||
|
||||
for container in $CONTAINERS; do
|
||||
# 获取容器资源使用
|
||||
STATS=$(docker stats --no-stream --format "{{.CPUPerc}} {{.MemUsage}} {{.MemPerc}}" "$container" 2>/dev/null || echo "0% 0B/0B 0%")
|
||||
|
||||
CPU=$(echo "$STATS" | awk '{print $1}')
|
||||
MEM_USAGE=$(echo "$STATS" | awk '{print $2}')
|
||||
MEM_PERCENT=$(echo "$STATS" | awk '{print $3}' | cut -d'%' -f1)
|
||||
|
||||
# 检查是否属于本项目
|
||||
if echo "$container" | grep -q "h3c-onu-ms"; then
|
||||
COLOR="$CYAN"
|
||||
else
|
||||
COLOR="$PURPLE"
|
||||
fi
|
||||
|
||||
echo -e "${COLOR}$container${NC}: CPU=${CPU}, 内存=${MEM_USAGE} (${MEM_PERCENT}%)"
|
||||
done
|
||||
}
|
||||
|
||||
# 函数:检查应用健康状态
|
||||
check_application_health() {
|
||||
echo -e "\n${BLUE}=== 应用健康 ===${NC}"
|
||||
|
||||
# 检查后端API
|
||||
if curl -s -f http://localhost:8000/health > /dev/null 2>&1; then
|
||||
echo -e "${GREEN}✓ 后端API: 健康${NC}"
|
||||
|
||||
# 获取详细健康信息
|
||||
HEALTH_INFO=$(curl -s http://localhost:8000/health)
|
||||
echo " 详细信息: $HEALTH_INFO"
|
||||
else
|
||||
echo -e "${RED}✗ 后端API: 不可用${NC}"
|
||||
fi
|
||||
|
||||
# 检查前端
|
||||
if curl -s -f http://localhost:8080 > /dev/null 2>&1; then
|
||||
echo -e "${GREEN}✓ 前端应用: 可访问${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ 前端应用: 不可访问${NC}"
|
||||
fi
|
||||
|
||||
# 检查Redis
|
||||
REDIS_CONTAINER=$(docker-compose -f "$COMPOSE_FILE" ps -q redis 2>/dev/null || echo "")
|
||||
if [ -n "$REDIS_CONTAINER" ]; then
|
||||
if docker exec "$REDIS_CONTAINER" redis-cli ping > /dev/null 2>&1; then
|
||||
echo -e "${GREEN}✓ Redis: 运行正常${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ Redis: 连接失败${NC}"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 检查数据库连接
|
||||
if [ -n "$DATABASE_URL" ]; then
|
||||
# 简单检查数据库连接
|
||||
if command -v pg_isready &> /dev/null; then
|
||||
# 从DATABASE_URL解析主机和端口
|
||||
DB_HOST=$(echo "$DATABASE_URL" | sed -n 's/.*@\([^:]*\).*/\1/p')
|
||||
DB_PORT=$(echo "$DATABASE_URL" | sed -n 's/.*:\([0-9]*\)\/.*/\1/p')
|
||||
|
||||
if pg_isready -h "$DB_HOST" -p "$DB_PORT" > /dev/null 2>&1; then
|
||||
echo -e "${GREEN}✓ 数据库: 可连接${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ 数据库: 连接失败${NC}"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# 函数:检查网络连接
|
||||
check_network() {
|
||||
echo -e "\n${BLUE}=== 网络连接 ===${NC}"
|
||||
|
||||
# 检查互联网连接
|
||||
if ping -c 1 -W 2 8.8.8.8 > /dev/null 2>&1; then
|
||||
echo -e "${GREEN}✓ 互联网: 可连接${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}⚠ 互联网: 连接失败${NC}"
|
||||
fi
|
||||
|
||||
# 检查DNS
|
||||
if nslookup google.com > /dev/null 2>&1; then
|
||||
echo -e "${GREEN}✓ DNS: 工作正常${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}⚠ DNS: 解析失败${NC}"
|
||||
fi
|
||||
|
||||
# 检查端口监听
|
||||
echo -e "端口监听:"
|
||||
PORTS="80 443 8000 8080 5432 6379"
|
||||
for port in $PORTS; do
|
||||
if ss -tuln | grep -q ":$port "; then
|
||||
SERVICE=$(ss -tuln | grep ":$port " | awk '{print $5}' | cut -d':' -f2)
|
||||
echo -e " ${GREEN}✓ 端口 $port: 被监听 ($SERVICE)${NC}"
|
||||
else
|
||||
echo -e " ${YELLOW}⚠ 端口 $port: 未监听${NC}"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# 函数:检查日志错误
|
||||
check_log_errors() {
|
||||
echo -e "\n${BLUE}=== 日志检查 ===${NC}"
|
||||
|
||||
# 检查最近错误日志
|
||||
LOG_FILES="/var/log/syslog /var/log/messages /var/log/docker/*.log ../../backend/logs/*.log"
|
||||
|
||||
for log_file in $LOG_FILES; do
|
||||
if [ -f "$log_file" ]; then
|
||||
ERROR_COUNT=$(tail -100 "$log_file" | grep -i "error\|exception\|failed\|critical" | wc -l)
|
||||
if [ "$ERROR_COUNT" -gt 0 ]; then
|
||||
echo -e "${YELLOW}⚠ $log_file: 最近有 $ERROR_COUNT 个错误${NC}"
|
||||
|
||||
# 显示最近3个错误
|
||||
tail -100 "$log_file" | grep -i "error\|exception\|failed\|critical" | tail -3 | while read line; do
|
||||
echo " - $line"
|
||||
done
|
||||
fi
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# 函数:检查定时任务
|
||||
check_scheduled_tasks() {
|
||||
echo -e "\n${BLUE}=== 定时任务 ===${NC}"
|
||||
|
||||
# 检查Celery Beat
|
||||
BEAT_CONTAINER=$(docker-compose -f "$COMPOSE_FILE" ps -q celery-beat 2>/dev/null || echo "")
|
||||
if [ -n "$BEAT_CONTAINER" ]; then
|
||||
if docker logs --tail 10 "$BEAT_CONTAINER" 2>/dev/null | grep -q "beat"; then
|
||||
echo -e "${GREEN}✓ Celery Beat: 运行中${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}⚠ Celery Beat: 可能异常${NC}"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 检查Celery Worker
|
||||
WORKER_CONTAINER=$(docker-compose -f "$COMPOSE_FILE" ps -q celery-worker 2>/dev/null || echo "")
|
||||
if [ -n "$WORKER_CONTAINER" ]; then
|
||||
ACTIVE_TASKS=$(docker exec "$WORKER_CONTAINER" celery -A app.core.celery_app inspect active 2>/dev/null | grep -c "worker")
|
||||
if [ "$ACTIVE_TASKS" -gt 0 ]; then
|
||||
echo -e "${GREEN}✓ Celery Worker: 有 $ACTIVE_TASKS 个活跃任务${NC}"
|
||||
else
|
||||
echo -e "${CYAN}ℹ Celery Worker: 无活跃任务${NC}"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# 函数:生成监控报告
|
||||
generate_report() {
|
||||
local timestamp=$(date "+%Y-%m-%d %H:%M:%S")
|
||||
|
||||
echo -e "\n${PURPLE}========================================${NC}"
|
||||
echo -e "${PURPLE} 监控报告 - $timestamp${NC}"
|
||||
echo -e "${PURPLE}========================================${NC}"
|
||||
|
||||
check_services_status
|
||||
check_system_resources
|
||||
check_container_resources
|
||||
check_application_health
|
||||
check_network
|
||||
check_log_errors
|
||||
check_scheduled_tasks
|
||||
|
||||
echo -e "\n${PURPLE}========================================${NC}"
|
||||
}
|
||||
|
||||
# 函数:连续监控
|
||||
continuous_monitoring() {
|
||||
local start_time=$(date +%s)
|
||||
local iteration=1
|
||||
|
||||
echo -e "${GREEN}开始监控,间隔: ${INTERVAL}秒${NC}"
|
||||
if [ "$DURATION" -gt 0 ]; then
|
||||
echo -e "监控时长: ${DURATION}秒${NC}"
|
||||
else
|
||||
echo -e "监控时长: 无限${NC}"
|
||||
fi
|
||||
echo "按 Ctrl+C 停止监控"
|
||||
echo ""
|
||||
|
||||
while true; do
|
||||
# 检查是否达到监控时长
|
||||
if [ "$DURATION" -gt 0 ]; then
|
||||
local current_time=$(date +%s)
|
||||
local elapsed=$((current_time - start_time))
|
||||
|
||||
if [ $elapsed -ge $DURATION ]; then
|
||||
echo -e "\n${GREEN}监控时长达到,停止监控${NC}"
|
||||
break
|
||||
fi
|
||||
fi
|
||||
|
||||
# 清屏并显示报告
|
||||
clear
|
||||
echo -e "${BLUE}监控迭代 #$iteration - $(date "+%H:%M:%S")${NC}"
|
||||
generate_report
|
||||
|
||||
# 等待间隔
|
||||
sleep "$INTERVAL"
|
||||
iteration=$((iteration + 1))
|
||||
done
|
||||
}
|
||||
|
||||
# 函数:单次监控
|
||||
single_monitoring() {
|
||||
generate_report
|
||||
}
|
||||
|
||||
# 主程序
|
||||
main() {
|
||||
# 显示标题
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
echo -e "${GREEN} H3C ONU设备管理系统 - 系统监控${NC}"
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
echo ""
|
||||
|
||||
# 检查参数
|
||||
if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
|
||||
show_help
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 检查Docker Compose文件
|
||||
if [ ! -f "$COMPOSE_FILE" ]; then
|
||||
echo -e "${YELLOW}警告: $COMPOSE_FILE 不存在,使用默认配置${NC}"
|
||||
fi
|
||||
|
||||
# 选择监控模式
|
||||
if [ "$DURATION" -eq 0 ] && [ "$INTERVAL" -eq 5 ]; then
|
||||
# 默认单次监控
|
||||
single_monitoring
|
||||
else
|
||||
# 连续监控
|
||||
continuous_monitoring
|
||||
fi
|
||||
}
|
||||
|
||||
# 执行主程序
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user