Files
FastapiAdmin/backend/run_linux.sh
T

379 lines
12 KiB
Bash
Executable File
Raw 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
set -e
# 固定脚本所在目录
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
# ========== 彩色输出定义 ==========
if [[ -t 1 ]]; then
tty_red="\033[0;31m"
tty_green="\033[0;32m"
tty_yellow="\033[0;33m"
tty_blue="\033[0;34m"
tty_cyan="\033[0;36m"
tty_purple="\033[0;35m"
tty_bold="\033[1m"
tty_reset="\033[0m"
else
tty_red="" tty_green="" tty_yellow="" tty_blue="" tty_cyan="" tty_purple="" tty_bold="" tty_reset=""
fi
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[38;5;226m[!]'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
LIGHT_GRAY='\033[0;37m'
PURPLE='\033[0;35m'
BOLD='\033[1m'
RESET='\033[0m'
# ========== 彩色输出函数 ==========
function info() {
echo -e "${tty_green}$1${tty_reset}"
}
function warn() {
echo -e "${tty_yellow}⚠️ $1${tty_reset}"
}
function error() {
echo -e "${tty_red}$1${tty_reset}"
}
function pause() {
read -n1 -r -p "按任意键继续..." key
}
# 判断执行是否成功
JudgeSuccess() {
if [ $? -ne 0 ]; then
error "步骤失败: $1"
exit 1
else
info "步骤成功: $1"
fi
}
# 分割线输出函数
print_separator() {
printf "${LIGHT_GRAY}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}\n"
}
show_banner() {
echo ""
echo -e "╭──────────────────────────────────────────────╮"
echo -e "│ \033[1;34m👋 欢迎使用 FastAPI 初始化脚本\033[0m │"
echo -e "╰──────────────────────────────────────────────╯"
echo "版本: 3.0.0"
echo "作者:coderxslee"
echo ""
}
# ========== 从 .env.dev 文件中读取数据库配置 ==========
function load_db_config() {
local env_file="$SCRIPT_DIR/env/.env.dev"
if [ ! -f "$env_file" ]; then
error "未找到 $env_file 文件"
return 1
fi
# 读取数据库配置(忽略注释和空行)
# 使用 sed 去掉注释部分,然后提取值
DATABASE_HOST=$(grep "^DATABASE_HOST" "$env_file" | cut -d'=' -f2 | sed 's/#.*//' | xargs)
DATABASE_PORT=$(grep "^DATABASE_PORT" "$env_file" | cut -d'=' -f2 | sed 's/#.*//' | xargs)
DATABASE_USER=$(grep "^DATABASE_USER" "$env_file" | cut -d'=' -f2 | sed 's/#.*//' | xargs)
DATABASE_PASSWORD=$(grep "^DATABASE_PASSWORD" "$env_file" | cut -d'=' -f2 | sed 's/#.*//' | xargs)
DATABASE_NAME=$(grep "^DATABASE_NAME" "$env_file" | cut -d'=' -f2 | sed 's/#.*//' | xargs)
DATABASE_TYPE=$(grep "^DATABASE_TYPE" "$env_file" | cut -d'=' -f2 | sed 's/#.*//' | xargs)
# 验证必要的配置
if [[ -z "$DATABASE_HOST" ]] || [[ -z "$DATABASE_PORT" ]] || [[ -z "$DATABASE_USER" ]] || [[ -z "$DATABASE_NAME" ]]; then
error "数据库配置不完整,请检查 $env_file 文件"
return 1
fi
return 0
}
# ========== FastAPI 功能函数 ==========
# 1. 启动开发服务器
function start_dev_server() {
print_separator
echo -e "${tty_cyan}🚀 启动(uv run dev...${tty_reset}"
load_db_config || return 1
echo -e "${tty_blue}🗄️ 检查并创建数据库...${tty_reset}"
# 检查数据库是否存在
DB_EXISTS=$(PGPASSWORD="$DATABASE_PASSWORD" psql -h "$DATABASE_HOST" -p "$DATABASE_PORT" -U "$DATABASE_USER" -d "postgres" -tAc "SELECT 1 FROM pg_database WHERE datname='$DATABASE_NAME';" 2>/dev/null || echo "")
if [ "$DB_EXISTS" != "1" ]; then
warn "数据库 '$DATABASE_NAME' 不存在,正在创建..."
CREATE_DB_OUTPUT=$(PGPASSWORD="$DATABASE_PASSWORD" psql -h "$DATABASE_HOST" -p "$DATABASE_PORT" -U "$DATABASE_USER" -d "postgres" -c "CREATE DATABASE \"$DATABASE_NAME\" WITH ENCODING 'UTF8' LC_COLLATE 'C.UTF-8' LC_CTYPE 'C.UTF-8' TEMPLATE template0 OWNER $DATABASE_USER;" 2>&1)
if [ $? -ne 0 ]; then
error "数据库创建失败"
echo -e "${tty_red}错误详情:${tty_reset}"
echo -e "${tty_red}$CREATE_DB_OUTPUT${tty_reset}"
return 1
fi
info "数据库 '$DATABASE_NAME' 创建成功 (UTF8 编码, C.UTF-8 排序规则)"
else
info "数据库 '$DATABASE_NAME' 已存在"
fi
print_separator
cd "$SCRIPT_DIR"
uv run main.py run --env=dev
JudgeSuccess "开发服务器启动"
echo -e "${tty_green}🎉 开发服务器启动完成!${tty_reset}"
print_separator
}
# 2. 生成迁移文件
function create_migration() {
print_separator
echo -e "${tty_cyan}📝 生成迁移文件(模型变更后)...${tty_reset}"
cd "$SCRIPT_DIR"
uv run main.py revision --env=dev
JudgeSuccess "迁移文件生成"
echo -e "${tty_green}🎉 迁移文件生成完成!${tty_reset}"
print_separator
}
# 3. 应用迁移
function apply_migration() {
print_separator
echo -e "${tty_cyan}⚡ 应用迁移...${tty_reset}"
cd "$SCRIPT_DIR"
uv run main.py upgrade --env=dev
JudgeSuccess "迁移应用"
echo -e "${tty_green}🎉 迁移应用完成!${tty_reset}"
print_separator
}
# 4. 重置数据库中的迁移记录
function reset_migration_records() {
print_separator
echo -e "${tty_cyan}🔄 重置数据库中的迁移记录...${tty_reset}"
echo -e "${tty_yellow}⚠️ 警告:此操作将重置数据库中的迁移记录!${tty_reset}"
read -p "确认继续吗?(y/N): " confirm
if [[ $confirm != [yY] ]]; then
echo -e "${tty_yellow}操作已取消${tty_reset}"
return
fi
load_db_config || return 1
cd "$SCRIPT_DIR"
echo -e "${tty_blue}🔄 正在重置迁移记录...${tty_reset}"
# 使用 psql 连接到数据库并重置迁移记录
PGPASSWORD="$DATABASE_PASSWORD" psql -h "$DATABASE_HOST" -p "$DATABASE_PORT" -U "$DATABASE_USER" -d "$DATABASE_NAME" -c "DELETE FROM alembic_version;" 2>/dev/null
JudgeSuccess "迁移记录重置"
echo -e "${tty_green}🎉 迁移记录重置完成!${tty_reset}"
print_separator
}
# 5. 清理数据库(删除所有表)
function clean_database() {
print_separator
echo -e "${tty_cyan}🗄️ 清理数据库(删除所有表)...${tty_reset}"
echo -e "${tty_yellow}⚠️ 警告:此操作将删除数据库中的所有数据!${tty_reset}"
read -p "确认继续吗?(y/N): " confirm
if [[ $confirm != [yY] ]]; then
echo -e "${tty_yellow}操作已取消${tty_reset}"
return
fi
load_db_config || return 1
cd "$SCRIPT_DIR"
echo -e "${tty_blue}🗄️ 清理数据库,删除所有现有的表...${tty_reset}"
# 使用 psql 连接到数据库并删除所有表
PGPASSWORD="$DATABASE_PASSWORD" psql -h "$DATABASE_HOST" -p "$DATABASE_PORT" -U "$DATABASE_USER" -d "$DATABASE_NAME" -c "DROP SCHEMA public CASCADE; CREATE SCHEMA public;" 2>/dev/null
JudgeSuccess "数据库清理"
echo -e "${tty_green}🎉 数据库清理完成!${tty_reset}"
print_separator
}
# 6. 删除数据库
function drop_database() {
print_separator
echo -e "${tty_cyan}🗑️ 删除数据库...${tty_reset}"
# 获取数据库名称
echo -e "${tty_blue}📝 请输入要删除的数据库名称:${tty_reset}"
read -p "数据库名称: " db_name
# 验证数据库名称不为空
if [[ -z "$db_name" ]]; then
error "数据库名称不能为空!"
return
fi
# 显示警告信息
echo -e "${tty_red}⚠️ 警告:此操作将永久删除数据库 '$db_name' 及其所有数据!${tty_reset}"
echo -e "${tty_yellow}此操作不可撤销!${tty_reset}"
# 第一次确认
read -p "确认要删除数据库 '$db_name' 吗?(y/N): " confirm1
if [[ $confirm1 != [yY] ]]; then
echo -e "${tty_yellow}操作已取消${tty_reset}"
return
fi
# 第二次确认
echo -e "${tty_red}⚠️ 最后确认:您真的要删除数据库 '$db_name' 吗?${tty_reset}"
read -p "请输入 'DELETE' 来确认删除: " confirm2
if [[ $confirm2 != "DELETE" && $confirm2 != "delete" ]]; then
echo -e "${tty_yellow}操作已取消${tty_reset}"
return
fi
load_db_config || return 1
cd "$SCRIPT_DIR"
echo -e "${tty_blue}🗑️ 正在删除数据库 '$db_name'...${tty_reset}"
# 先断开所有连接到该数据库的连接
PGPASSWORD="$DATABASE_PASSWORD" psql -h "$DATABASE_HOST" -p "$DATABASE_PORT" -U "$DATABASE_USER" -d "postgres" -c "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '$db_name' AND pid <> pg_backend_pid();" 2>/dev/null
# 删除数据库
PGPASSWORD="$DATABASE_PASSWORD" psql -h "$DATABASE_HOST" -p "$DATABASE_PORT" -U "$DATABASE_USER" -d "postgres" -c "DROP DATABASE IF EXISTS \"$db_name\";" 2>/dev/null
JudgeSuccess "数据库删除"
echo -e "${tty_green}🎉 数据库 '$db_name' 删除完成!${tty_reset}"
print_separator
}
# 7. 初始化数据(执行 sql 目录下脚本)
function init_sql_data() {
print_separator
echo -e "${tty_cyan}🧰 初始化数据...${tty_reset}"
local sql_dir
sql_dir="$REPO_ROOT/backend/sql/postgres/init_data"
if [ ! -d "$sql_dir" ]; then
error "未找到 SQL 目录: $sql_dir"
return 1
fi
local sql_files=()
while IFS= read -r file; do
sql_files+=("$file")
done < <(find "$sql_dir" -maxdepth 1 -type f -name "*.sql" 2>/dev/null | sort)
if [ ${#sql_files[@]} -eq 0 ]; then
warn "SQL 目录下没有可执行的 .sql 文件: $sql_dir"
return 0
fi
echo -e "${tty_purple}可用 SQL 文件:${tty_reset}"
echo -e "${tty_yellow}0. 执行全部 SQL 文件${tty_reset}"
local i
for i in "${!sql_files[@]}"; do
echo "$((i + 1)). $(basename "${sql_files[$i]}")"
done
local choice
read -p "请选择要初始化的 SQL(输入序号): " choice
if ! [[ "$choice" =~ ^[0-9]+$ ]]; then
error "输入无效,请输入数字序号"
return 1
fi
load_db_config || return 1
if [ "$choice" -eq 0 ]; then
echo -e "${tty_blue}🚀 开始执行全部 SQL 文件...${tty_reset}"
local file
for file in "${sql_files[@]}"; do
echo -e "${tty_blue}执行: $(basename "$file")${tty_reset}"
if PGPASSWORD="$DATABASE_PASSWORD" psql -h "$DATABASE_HOST" -p "$DATABASE_PORT" -U "$DATABASE_USER" -d "$DATABASE_NAME" -f "$file"; then
info "执行成功: $(basename "$file")"
else
error "执行失败: $(basename "$file")"
return 1
fi
done
info "全部 SQL 文件执行完成"
else
local index=$((choice - 1))
if [ "$index" -lt 0 ] || [ "$index" -ge ${#sql_files[@]} ]; then
error "序号超出范围"
return 1
fi
local selected_file="${sql_files[$index]}"
echo -e "${tty_blue}执行: $(basename "$selected_file")${tty_reset}"
if PGPASSWORD="$DATABASE_PASSWORD" psql -h "$DATABASE_HOST" -p "$DATABASE_PORT" -U "$DATABASE_USER" -d "$DATABASE_NAME" -f "$selected_file"; then
info "执行成功: $(basename "$selected_file")"
info "SQL 初始化完成"
else
error "执行失败: $(basename "$selected_file")"
return 1
fi
fi
print_separator
}
# 主菜单
function main_menu() {
clear
show_banner
echo -e "\033[1;34m📦 请选择要执行的操作:\033[0m"
echo ""
echo -e "\033[1;33m1. 🚀 启动(uv run dev\033[0m"
echo -e "\033[1;32m2. 📝 生成迁移文件(模型变更后)\033[0m"
echo -e "\033[1;36m3. ⚡ 应用迁移\033[0m"
echo -e "\033[1;36m4. 🔄 重置数据库中的迁移记录\033[0m"
echo -e "\033[1;31m5. 🗄️ 清理数据库(删除所有表)\033[0m"
echo -e "\033[1;31m6. 🗑️ 删除数据库\033[0m"
echo -e "\033[1;36m7. 🧰 初始化数据(执行 sql 脚本)\033[0m"
echo -e "\033[1;31m0. ❌ 退出\033[0m"
echo ""
read -p "请选择你要执行的操作: " option
case $option in
1) start_dev_server && pause ;;
2) create_migration && pause ;;
3) apply_migration && pause ;;
4) reset_migration_records && pause ;;
5) clean_database && pause ;;
6) drop_database && pause ;;
7) init_sql_data && pause ;;
0) exit 0 ;;
*) error "未知选项: $option" && pause ;;
esac
}
# 非交互模式入口
if [[ "$1" == "--start-dev" ]]; then
start_dev_server
exit $?
fi
# 默认进入交互菜单
main_menu