diff --git a/install.sh b/install.sh index 16a4db5..c67c5d2 100644 --- a/install.sh +++ b/install.sh @@ -1,7 +1,9 @@ #!/bin/bash # # FunMC 一键部署脚本 -# 用法: curl -fsSL https://fc.funmc.cn/install.sh | bash +# 用法: bash install.sh [ -force ] +# - 无参数: 更新安装,保留数据库与现有配置(server.env / relay.env / credentials.txt) +# - -force: 强制覆盖安装,清空数据库并重写所有配置 # set -e @@ -30,6 +32,12 @@ echo "║ 魔幻方开发 ║" echo "║ ║" echo "╚═══════════════════════════════════════════════════════════╝" echo -e "${NC}" +if [ "$FORCE_INSTALL" -eq 1 ]; then + echo -e "${YELLOW}运行模式: 强制覆盖安装(将清空数据库并重写配置)${NC}" +else + echo -e "${GREEN}运行模式: 更新安装(保留数据库与现有配置)${NC}" +fi +echo "" # 检查 root 权限 if [ "$EUID" -ne 0 ]; then @@ -38,6 +46,15 @@ if [ "$EUID" -ne 0 ]; then exit 1 fi +# 解析参数:-force 或 --force 为强制覆盖安装,否则为更新(不覆盖数据) +FORCE_INSTALL=0 +for arg in "$@"; do + if [ "$arg" = "-force" ] || [ "$arg" = "--force" ]; then + FORCE_INSTALL=1 + break + fi +done + # 检测系统 detect_os() { if [ -f /etc/os-release ]; then @@ -99,32 +116,37 @@ install_nodejs() { fi } -# 配置数据库(固定密码 12345678,强制删除旧库与用户并重建) +# 配置数据库(-force 时强制删除并重建,否则仅确保库存在且不覆盖数据) setup_database() { echo -e "${YELLOW}[4/7] 配置数据库...${NC}" systemctl enable postgresql systemctl start postgresql - # 强制断开对 funmc 库的所有连接,再删除库和用户(避免 role already exists / 无法删除库) - sudo -u postgres psql -d postgres -c "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'funmc' AND pid <> pg_backend_pid();" 2>/dev/null || true - sudo -u postgres psql -d postgres -c "DROP DATABASE IF EXISTS funmc;" 2>/dev/null || true - sudo -u postgres psql -d postgres -c "DROP USER IF EXISTS funmc;" 2>/dev/null || true + if [ "$FORCE_INSTALL" -eq 1 ]; then + # 强制断开对 funmc 库的所有连接,再删除库和用户 + sudo -u postgres psql -d postgres -c "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'funmc' AND pid <> pg_backend_pid();" 2>/dev/null || true + sudo -u postgres psql -d postgres -c "DROP DATABASE IF EXISTS funmc;" 2>/dev/null || true + sudo -u postgres psql -d postgres -c "DROP USER IF EXISTS funmc;" 2>/dev/null || true + sudo -u postgres psql -d postgres -c "CREATE USER funmc WITH PASSWORD '12345678';" + sudo -u postgres psql -d postgres -c "CREATE DATABASE funmc OWNER funmc;" + sudo -u postgres psql -d postgres -c "GRANT ALL PRIVILEGES ON DATABASE funmc TO funmc;" + echo -e "${GREEN}✓ 数据库已强制重建(密码 12345678)${NC}" + else + # 更新模式:不删除,仅确保用户和库存在(若已存在则跳过) + sudo -u postgres psql -d postgres -c "CREATE USER funmc WITH PASSWORD '12345678';" 2>/dev/null || true + sudo -u postgres psql -d postgres -c "CREATE DATABASE funmc OWNER funmc;" 2>/dev/null || true + sudo -u postgres psql -d postgres -c "GRANT ALL PRIVILEGES ON DATABASE funmc TO funmc;" 2>/dev/null || true + echo -e "${GREEN}✓ 数据库检查完成(未覆盖现有数据)${NC}" + fi - # 创建用户和数据库 - sudo -u postgres psql -d postgres -c "CREATE USER funmc WITH PASSWORD '12345678';" - sudo -u postgres psql -d postgres -c "CREATE DATABASE funmc OWNER funmc;" - sudo -u postgres psql -d postgres -c "GRANT ALL PRIVILEGES ON DATABASE funmc TO funmc;" - - # 配置 pg_hba.conf + # 配置 pg_hba.conf(仅追加缺失项) PG_HBA=$(sudo -u postgres psql -t -c "SHOW hba_file;" | xargs) if ! grep -q "funmc" "$PG_HBA"; then echo "local funmc funmc md5" >> "$PG_HBA" echo "host funmc funmc 127.0.0.1/32 md5" >> "$PG_HBA" systemctl reload postgresql fi - - echo -e "${GREEN}✓ 数据库配置完成(密码 12345678,已强制覆盖旧数据)${NC}" } # 下载并编译 FunMC @@ -160,19 +182,20 @@ build_funmc() { echo -e "${GREEN}✓ FunMC 编译完成${NC}" } -# 配置服务 +# 配置服务(-force 时重写配置;否则若已有配置则保留仅做迁移与重启) configure_services() { echo -e "${YELLOW}[6/7] 配置服务...${NC}" - DB_PASSWORD="12345678" - JWT_SECRET=$(openssl rand -base64 48 | tr -dc 'a-zA-Z0-9' | head -c 48) - ADMIN_PASSWORD=$(openssl rand -base64 16 | tr -dc 'a-zA-Z0-9' | head -c 12) - - # 获取服务器 IP - SERVER_IP=$(curl -s ifconfig.me || curl -s ipinfo.io/ip || hostname -I | awk '{print $1}') - - # 创建主配置文件 - cat > $CONFIG_DIR/server.env << EOF + WROTE_CONFIG=0 + if [ "$FORCE_INSTALL" -eq 1 ] || [ ! -f "$CONFIG_DIR/server.env" ]; then + # 强制安装或首次安装:生成新配置 + WROTE_CONFIG=1 + DB_PASSWORD="12345678" + JWT_SECRET=$(openssl rand -base64 48 | tr -dc 'a-zA-Z0-9' | head -c 48) + ADMIN_PASSWORD=$(openssl rand -base64 16 | tr -dc 'a-zA-Z0-9' | head -c 12) + SERVER_IP=$(curl -s ifconfig.me || curl -s ipinfo.io/ip || hostname -I | awk '{print $1}') + + cat > $CONFIG_DIR/server.env << EOF # FunMC 服务端配置 DATABASE_URL=postgres://funmc:${DB_PASSWORD}@localhost/funmc JWT_SECRET=${JWT_SECRET} @@ -196,14 +219,21 @@ CLIENT_VERSION=${FUNMC_VERSION} DOWNLOADS_DIR=$INSTALL_DIR/downloads EOF - # 创建中继配置 - cat > $CONFIG_DIR/relay.env << EOF + cat > $CONFIG_DIR/relay.env << EOF RELAY_PORT=7900 JWT_SECRET=${JWT_SECRET} RUST_LOG=info EOF + else + # 更新模式:保留现有配置,仅确保 DB_PASSWORD 等变量存在供后续迁移使用 + DB_PASSWORD=$(grep DATABASE_URL "$CONFIG_DIR/server.env" 2>/dev/null | sed -n 's/.*:\/\/funmc:\([^@]*\)@.*/\1/p') + if [ -z "$DB_PASSWORD" ]; then + DB_PASSWORD="12345678" + fi + echo -e "${GREEN}✓ 保留现有配置(未覆盖 server.env / relay.env)${NC}" + fi - # 创建 systemd 服务文件 + # 创建 systemd 服务文件(始终更新以便安装路径等变更生效) cat > /etc/systemd/system/funmc-server.service << EOF [Unit] Description=FunMC API Server @@ -256,8 +286,9 @@ EOF echo -e "${GREEN}✓ 服务配置完成${NC}" - # 保存凭据 - cat > $CONFIG_DIR/credentials.txt << EOF + # 仅强制/首次安装时写入凭据文件,更新模式不覆盖 + if [ "$WROTE_CONFIG" -eq 1 ]; then + cat > $CONFIG_DIR/credentials.txt << EOF ====================================== FunMC 服务端安装信息 ====================================== @@ -276,7 +307,8 @@ JWT 密钥: ${JWT_SECRET} 请妥善保管此文件! ====================================== EOF - chmod 600 $CONFIG_DIR/credentials.txt + chmod 600 $CONFIG_DIR/credentials.txt + fi } # 配置防火墙 diff --git a/server/src/api/download.rs b/server/src/api/download.rs index b1ff959..a9d89bf 100644 --- a/server/src/api/download.rs +++ b/server/src/api/download.rs @@ -32,6 +32,24 @@ pub struct ClientBuild { pub status: String, } +/// 列出 downloads 目录下可用的文件名(供下载页仅对存在的文件显示「下载」) +pub async fn list_download_files() -> Json> { + let downloads_dir = std::env::var("DOWNLOADS_DIR").unwrap_or_else(|_| "./downloads".to_string()); + let mut names = Vec::new(); + if let Ok(mut rd) = tokio::fs::read_dir(&downloads_dir).await { + while let Ok(Some(entry)) = rd.next_entry().await { + if let Ok(meta) = entry.metadata().await { + if meta.is_file() { + if let Ok(name) = entry.file_name().into_string() { + names.push(name); + } + } + } + } + } + Json(names) +} + pub async fn get_client_config(State(state): State>) -> Json { let config = state.server_config.read().unwrap(); @@ -103,8 +121,8 @@ pub async fn download_page(State(state): State>) -> Html {
🪟

Windows

Windows 10/11

- + 下载 .exe @@ -117,12 +135,12 @@ pub async fn download_page(State(state): State>) -> Html {

macOS

macOS 11+

@@ -135,8 +153,8 @@ pub async fn download_page(State(state): State>) -> Html {
🐧

Linux

Ubuntu/Debian/Fedora

- + 下载 AppImage @@ -151,8 +169,8 @@ pub async fn download_page(State(state): State>) -> Html {
🤖

Android

- + 下载 APK
@@ -186,6 +204,20 @@ pub async fn download_page(State(state): State>) -> Html { 魔幻方开发 · FunMC + "##, server_name = config.server_name, diff --git a/server/src/api/mod.rs b/server/src/api/mod.rs index 92983b0..da73469 100644 --- a/server/src/api/mod.rs +++ b/server/src/api/mod.rs @@ -59,6 +59,7 @@ pub fn router(_state: Arc) -> Router> { .route("/admin/builds/trigger", post(download::trigger_build)) // Download .route("/client-config", get(download::get_client_config)) + .route("/download/list", get(download::list_download_files)) .route("/download/:filename", get(download::download_file)) // WebSocket signaling .route("/ws", get(ws_handler))