仓库初始化
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""GUI logging setup."""
|
||||
|
||||
import logging
|
||||
from datetime import datetime
|
||||
|
||||
def setup_logging():
|
||||
"""配置日志系统。日志属于软件配置,与软件一起走(存于 AppData/ServerManager/logs)。"""
|
||||
# 创建根日志器
|
||||
logger = logging.getLogger('server_manager')
|
||||
logger.setLevel(logging.DEBUG)
|
||||
logger.propagate = False
|
||||
if logger.handlers:
|
||||
return logger
|
||||
|
||||
log_format = '%(asctime)s [%(levelname)s] %(name)s: %(message)s'
|
||||
date_format = '%Y-%m-%d %H:%M:%S'
|
||||
|
||||
# 控制台处理器 - 只记录警告及以上
|
||||
console_handler = logging.StreamHandler()
|
||||
console_handler.setLevel(logging.WARNING)
|
||||
console_handler.setFormatter(logging.Formatter(log_format, date_format))
|
||||
logger.addHandler(console_handler)
|
||||
|
||||
try:
|
||||
from models.app_config import get_app_log_dir
|
||||
log_dir = get_app_log_dir()
|
||||
log_file = log_dir / f"server_manager_{datetime.now().strftime('%Y%m%d')}.log"
|
||||
|
||||
file_handler = logging.FileHandler(log_file, encoding='utf-8')
|
||||
file_handler.setLevel(logging.DEBUG)
|
||||
file_handler.setFormatter(logging.Formatter(log_format, date_format))
|
||||
logger.addHandler(file_handler)
|
||||
|
||||
# 清理旧日志(保留最近7天)
|
||||
try:
|
||||
for old_log in log_dir.glob('server_manager_*.log'):
|
||||
if old_log != log_file:
|
||||
file_time = datetime.fromtimestamp(old_log.stat().st_mtime)
|
||||
if (datetime.now() - file_time).days > 7:
|
||||
old_log.unlink()
|
||||
except Exception:
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return logger
|
||||
|
||||
|
||||
logger = setup_logging()
|
||||
@@ -0,0 +1,15 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Recent project persistence helpers."""
|
||||
|
||||
from typing import List
|
||||
|
||||
def load_recent_projects() -> List[str]:
|
||||
"""加载最近打开的项目路径列表(来自软件配置)"""
|
||||
from models.app_config import get_recent_projects
|
||||
return get_recent_projects()
|
||||
|
||||
|
||||
def save_recent_project(project_path: str):
|
||||
"""将项目路径加入软件配置的最近列表并保存"""
|
||||
from models.app_config import save_recent_project as _save
|
||||
_save(project_path)
|
||||
@@ -0,0 +1,109 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Shared imports and runtime dependencies for the GUI package."""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import json
|
||||
import subprocess
|
||||
import threading
|
||||
import logging
|
||||
import tempfile
|
||||
import time
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
|
||||
SRC_DIR = Path(__file__).resolve().parents[1]
|
||||
|
||||
from services.commands import (
|
||||
IS_WINDOWS,
|
||||
ensure_and_get_server_manager_config_dir,
|
||||
get_local_ip,
|
||||
get_server_manager_config_dir,
|
||||
get_ebin_paths,
|
||||
get_erl_cmd,
|
||||
get_escript_cmd,
|
||||
get_rebar3_cmd,
|
||||
run_cmd_in_new_window,
|
||||
register_launched_window,
|
||||
get_launched_windows,
|
||||
close_launched_windows,
|
||||
build_start_command,
|
||||
build_start_command_by_config,
|
||||
build_stop_command,
|
||||
build_remsh_command,
|
||||
subprocess_args_to_display,
|
||||
read_config_file,
|
||||
read_merged_config,
|
||||
write_config_file,
|
||||
replace_placeholders,
|
||||
get_template_file,
|
||||
build_replace_map,
|
||||
generate_start_config,
|
||||
run_startup_migrations,
|
||||
get_config_list,
|
||||
get_server_list,
|
||||
query_remote_servers_from_login_rpc,
|
||||
get_login_db_name,
|
||||
get_node_status_cache,
|
||||
check_nodes_status,
|
||||
check_node_status,
|
||||
REBAR3_CMD,
|
||||
)
|
||||
|
||||
from services.server_creator import (
|
||||
get_existing_server_ids,
|
||||
create_server,
|
||||
get_id_range_from_config,
|
||||
get_default_port,
|
||||
get_default_server_name,
|
||||
build_confirmation_message,
|
||||
generate_server_dir_name,
|
||||
get_server_type_name,
|
||||
get_allowed_config_keys,
|
||||
get_required_keys,
|
||||
get_template_config_keys,
|
||||
SERVER_TYPE_MAP,
|
||||
)
|
||||
|
||||
from services.hot_update import (
|
||||
get_current_version,
|
||||
get_version_info,
|
||||
fetch_update_manifest,
|
||||
version_less,
|
||||
get_delta_for_current,
|
||||
download_file as updater_download_file,
|
||||
run_installer_and_exit,
|
||||
apply_delta_patch,
|
||||
clean_up_old_version,
|
||||
)
|
||||
|
||||
from views.logs.log_viewer_tab import LogViewerTab
|
||||
|
||||
from PyQt6.QtWidgets import (
|
||||
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
|
||||
QTabWidget, QLabel, QLineEdit, QPushButton, QTextEdit, QComboBox,
|
||||
QGroupBox, QFormLayout, QSpinBox, QMessageBox, QFileDialog,
|
||||
QSplitter, QFrame, QScrollArea, QCheckBox, QGridLayout,
|
||||
QListWidget, QListWidgetItem, QDialog, QDialogButtonBox,
|
||||
QProgressBar, QRadioButton, QButtonGroup, QDateTimeEdit,
|
||||
QTableWidget, QTableWidgetItem, QHeaderView, QStackedWidget,
|
||||
QProgressDialog, QAbstractItemView, QSizePolicy,
|
||||
)
|
||||
from PyQt6.QtCore import Qt, QThread, pyqtSignal, QTimer, QDateTime, QSize, QPointF, QByteArray, QEvent
|
||||
from PyQt6.QtGui import (
|
||||
QFont, QColor, QPalette, QIcon, QAction, QCursor,
|
||||
QPainter, QPen, QBrush, QPixmap, QPolygonF,
|
||||
)
|
||||
from PyQt6.QtSvg import QSvgRenderer
|
||||
|
||||
from .logging import logger
|
||||
|
||||
|
||||
def get_terminal_launch_options(config) -> Dict[str, str]:
|
||||
"""Return the currently configured terminal launch options for new windows."""
|
||||
return {
|
||||
"terminal_mode": config.get("terminal", "launch_mode", default="auto"),
|
||||
"terminal_exe_path": config.get("terminal", "exe_path", default=""),
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Small GUI formatting helpers."""
|
||||
|
||||
import re
|
||||
|
||||
def node_host_only(value: str) -> str:
|
||||
"""Return the host part for Erlang node strings like name@ip."""
|
||||
text = str(value or '').strip().strip("'\"")
|
||||
if '@' in text:
|
||||
return text.rsplit('@', 1)[1].strip()
|
||||
return text
|
||||
|
||||
|
||||
def _server_id_sort_key(server_id) -> tuple:
|
||||
"""服务器 ID 的稳定排序键,优先按数值升序。"""
|
||||
text = str(server_id or "").strip()
|
||||
try:
|
||||
return (0, int(text), text)
|
||||
except ValueError:
|
||||
match = re.search(r"\d+", text)
|
||||
if match:
|
||||
return (0, int(match.group(0)), text)
|
||||
return (1, 0, text)
|
||||
Reference in New Issue
Block a user