Status LED
Dieser Commit ist enthalten in:
@ -12,6 +12,7 @@ from typing import Optional, Callable, List, Dict
|
||||
from gui.styles import COLORS, FONTS
|
||||
from src.gitea.repository_manager import RepositoryManager
|
||||
from src.gitea.gitea_client import GiteaClient
|
||||
import json
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -156,6 +157,22 @@ class GiteaExplorer(ctk.CTkFrame):
|
||||
text_color=COLORS['text_secondary']
|
||||
)
|
||||
self.status_label.pack(pady=5)
|
||||
|
||||
def _get_clone_directory(self) -> Path:
|
||||
"""Get clone directory from settings or use default"""
|
||||
try:
|
||||
settings_file = Path.home() / ".claude_project_manager" / "ui_settings.json"
|
||||
if settings_file.exists():
|
||||
with open(settings_file, 'r') as f:
|
||||
settings = json.load(f)
|
||||
clone_dir = settings.get("gitea_clone_directory")
|
||||
if clone_dir:
|
||||
return Path(clone_dir)
|
||||
except Exception as e:
|
||||
logger.warning(f"Could not load clone directory from settings: {e}")
|
||||
|
||||
# Return default if not found in settings
|
||||
return Path.home() / "GiteaRepos"
|
||||
|
||||
def refresh_repositories(self):
|
||||
"""Refresh repository list from Gitea - only IntelSight"""
|
||||
@ -276,7 +293,8 @@ class GiteaExplorer(ctk.CTkFrame):
|
||||
name_label.pack(side="left", fill="x", expand=True)
|
||||
|
||||
# Clone/Status indicator
|
||||
local_path = Path.home() / "GiteaRepos" / repo['name']
|
||||
clone_dir = self._get_clone_directory()
|
||||
local_path = clone_dir / repo['name']
|
||||
if local_path.exists():
|
||||
status_label = ctk.CTkLabel(
|
||||
top_row,
|
||||
|
||||
@ -96,6 +96,9 @@ class MainWindow:
|
||||
# Start periodic status check after a delay
|
||||
self.root.after(30000, self.check_process_status) # Start after 30 seconds
|
||||
|
||||
# Check service status immediately on start
|
||||
self.check_service_status()
|
||||
|
||||
# Initialize activity sync
|
||||
self.init_activity_sync()
|
||||
|
||||
@ -460,6 +463,9 @@ class MainWindow:
|
||||
elif project.id == "vps-docker-permanent":
|
||||
# Handle VPS Docker
|
||||
self.open_vps_docker()
|
||||
elif project.id == "activity-server-permanent":
|
||||
# Handle Activity Server
|
||||
self.open_activity_server()
|
||||
else:
|
||||
# Check if already running
|
||||
if self.process_tracker.is_running(project.id):
|
||||
@ -641,6 +647,51 @@ class MainWindow:
|
||||
|
||||
# Monitor the process
|
||||
self.monitor_process("vps-docker-permanent", process)
|
||||
|
||||
def open_activity_server(self):
|
||||
"""Open Activity Server connection"""
|
||||
# Check if already running
|
||||
if self.process_tracker.is_running("activity-server-permanent"):
|
||||
self.update_status("Activity Server connection already active", error=True)
|
||||
return
|
||||
|
||||
# Create connection script for Activity Server
|
||||
script = self.vps_connection.create_activity_server_script()
|
||||
import tempfile
|
||||
with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f:
|
||||
f.write(script)
|
||||
script_path = f.name
|
||||
|
||||
# Launch terminal
|
||||
import subprocess
|
||||
if os.path.exists(r"C:\Windows\System32\wt.exe"):
|
||||
process = subprocess.Popen([
|
||||
"wt.exe", "-w", "0", "new-tab",
|
||||
"--title", "CPM Activity Server",
|
||||
"--", "cmd", "/c", script_path
|
||||
])
|
||||
else:
|
||||
process = subprocess.Popen(['cmd', '/c', 'start', 'CPM Activity Server', script_path])
|
||||
|
||||
# Track the process
|
||||
self.process_manager.processes["activity-server-permanent"] = process
|
||||
self.process_manager.save_process_data()
|
||||
self.process_tracker.set_running("activity-server-permanent")
|
||||
|
||||
# Update tile status immediately
|
||||
if "activity-server-permanent" in self.project_tiles:
|
||||
self.project_tiles["activity-server-permanent"].update_status(True)
|
||||
|
||||
self.update_status("Connected to Activity Server")
|
||||
|
||||
# Update Activity Server project
|
||||
activity_project = self.project_manager.get_project("activity-server-permanent")
|
||||
if activity_project:
|
||||
activity_project.update_last_accessed()
|
||||
self.project_manager.save_projects()
|
||||
|
||||
# Monitor the process
|
||||
self.monitor_process("activity-server-permanent", process)
|
||||
|
||||
def open_readme(self, project: Project):
|
||||
"""Open project README"""
|
||||
@ -1200,6 +1251,54 @@ class MainWindow:
|
||||
# Schedule next check in 30 seconds (less frequent)
|
||||
self.root.after(30000, self.check_process_status)
|
||||
|
||||
# Also check service status
|
||||
self.check_service_status()
|
||||
|
||||
def check_service_status(self):
|
||||
"""Check status of Activity Server and Admin Panel"""
|
||||
import threading
|
||||
|
||||
def check_services():
|
||||
# Check Activity Server
|
||||
activity_status = self.check_activity_server_status()
|
||||
if "activity-server-permanent" in self.project_tiles:
|
||||
self.root.after(0, lambda: self.project_tiles["activity-server-permanent"].update_service_status(activity_status))
|
||||
|
||||
# Check Admin Panel
|
||||
admin_status = self.check_admin_panel_status()
|
||||
if "admin-panel-permanent" in self.project_tiles:
|
||||
self.root.after(0, lambda: self.project_tiles["admin-panel-permanent"].update_service_status(admin_status))
|
||||
|
||||
# Run in background thread
|
||||
threading.Thread(target=check_services, daemon=True).start()
|
||||
|
||||
# Schedule next check in 60 seconds
|
||||
self.root.after(60000, self.check_service_status)
|
||||
|
||||
def check_activity_server_status(self) -> bool:
|
||||
"""Check if Activity Server is running"""
|
||||
import requests
|
||||
try:
|
||||
# Activity Server runs on port 3001
|
||||
# Check /api/activities endpoint - returns 401 when running (needs auth)
|
||||
response = requests.get("http://91.99.192.14:3001/api/activities", timeout=5)
|
||||
# 401 means server is running but needs authentication
|
||||
return response.status_code in [200, 401, 403]
|
||||
except:
|
||||
pass
|
||||
return False
|
||||
|
||||
def check_admin_panel_status(self) -> bool:
|
||||
"""Check if Admin Panel is accessible"""
|
||||
import requests
|
||||
try:
|
||||
# Admin Panel runs on port 80 (not 8082)
|
||||
response = requests.get("http://91.99.192.14:80/", timeout=5)
|
||||
return response.status_code in [200, 301, 302] # OK or redirects
|
||||
except:
|
||||
pass
|
||||
return False
|
||||
|
||||
def open_gitea_window(self):
|
||||
"""Open Gitea integration window"""
|
||||
# Create Gitea window if not already open
|
||||
|
||||
@ -68,6 +68,19 @@ class ProjectTile(ctk.CTkFrame):
|
||||
title_row = ctk.CTkFrame(container, fg_color="transparent")
|
||||
title_row.pack(fill="x", pady=(0, 5))
|
||||
|
||||
# Service status indicator for Admin Panel and Activity Server
|
||||
if self.project.id in ["admin-panel-permanent", "activity-server-permanent"]:
|
||||
self.service_status_indicator = ctk.CTkLabel(
|
||||
title_row,
|
||||
text="⚫", # Gray circle by default
|
||||
font=('Segoe UI', 10),
|
||||
text_color=COLORS['text_dim'],
|
||||
width=20
|
||||
)
|
||||
self.service_status_indicator.pack(side="left", padx=(0, 5))
|
||||
# Initialize tooltip
|
||||
self._update_service_tooltip("Wird geprüft...")
|
||||
|
||||
# Activity indicator (initially hidden)
|
||||
self.activity_indicator = ctk.CTkLabel(
|
||||
title_row,
|
||||
@ -209,8 +222,8 @@ class ProjectTile(ctk.CTkFrame):
|
||||
)
|
||||
self.open_button.pack(side="left", padx=(0, 5))
|
||||
|
||||
# CMD button only for main VPS Server tile (not Admin Panel or Docker)
|
||||
if self.is_vps and self.project.id == "vps-permanent":
|
||||
# CMD button for VPS Server and Activity Server
|
||||
if self.project.id in ["vps-permanent", "activity-server-permanent"]:
|
||||
self.cmd_button = ctk.CTkButton(
|
||||
button_frame,
|
||||
text="CMD",
|
||||
@ -220,8 +233,8 @@ class ProjectTile(ctk.CTkFrame):
|
||||
)
|
||||
self.cmd_button.pack(side="left", padx=(0, 5))
|
||||
|
||||
# Gitea button (not for VPS)
|
||||
if not self.is_vps:
|
||||
# Gitea button and Activity button (not for VPS and not for Activity Server)
|
||||
if not self.is_vps and self.project.id != "activity-server-permanent":
|
||||
self.gitea_button = ctk.CTkButton(
|
||||
button_frame,
|
||||
text="Gitea ▼",
|
||||
@ -246,8 +259,8 @@ class ProjectTile(ctk.CTkFrame):
|
||||
if not activity_service.is_configured() or not activity_service.connected:
|
||||
self.activity_button.configure(state="normal", text_color=COLORS['text_dim'])
|
||||
|
||||
# Delete button (not for VPS) - keep it in first row
|
||||
if not self.is_vps and self.on_delete:
|
||||
# Delete button (not for VPS and not for Activity Server) - keep it in first row
|
||||
if not self.is_vps and self.on_delete and self.project.id != "activity-server-permanent":
|
||||
self.delete_button = ctk.CTkButton(
|
||||
button_frame,
|
||||
text="✕",
|
||||
@ -259,8 +272,8 @@ class ProjectTile(ctk.CTkFrame):
|
||||
)
|
||||
self.delete_button.pack(side="right")
|
||||
|
||||
# Second row - Open Explorer button (only for non-VPS tiles)
|
||||
if not self.is_vps:
|
||||
# Second row - Open Explorer button (only for non-VPS tiles and not for Activity Server)
|
||||
if not self.is_vps and self.project.id != "activity-server-permanent":
|
||||
explorer_frame = ctk.CTkFrame(container, fg_color="transparent")
|
||||
explorer_frame.pack(fill="x", pady=(5, 0))
|
||||
|
||||
@ -416,7 +429,41 @@ class ProjectTile(ctk.CTkFrame):
|
||||
if platform.system() == 'Windows':
|
||||
# Create a temporary batch file for Windows
|
||||
with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f:
|
||||
batch_content = f'''@echo off
|
||||
# Different content based on which tile's CMD button was pressed
|
||||
if self.project.id == "activity-server-permanent":
|
||||
batch_content = f'''@echo off
|
||||
echo Connecting to CPM Activity Server...
|
||||
echo.
|
||||
echo Target Directory: /home/claude-dev/cpm-activity-server
|
||||
echo.
|
||||
|
||||
REM Try using plink if available (PuTTY command line)
|
||||
where plink >nul 2>&1
|
||||
if %ERRORLEVEL% EQU 0 (
|
||||
echo Using PuTTY plink for connection...
|
||||
echo.
|
||||
echo After connection, please run:
|
||||
echo cd /home/claude-dev/cpm-activity-server
|
||||
echo.
|
||||
plink -ssh -l {ssh_user} -pw "{ssh_pass}" {ssh_host}
|
||||
) else (
|
||||
echo PuTTY plink not found. Trying ssh with manual password entry...
|
||||
echo.
|
||||
echo Password: {ssh_pass}
|
||||
echo.
|
||||
echo Please copy the password above and paste it when prompted.
|
||||
echo.
|
||||
echo After login, run: cd /home/claude-dev/cpm-activity-server
|
||||
echo.
|
||||
pause
|
||||
ssh {ssh_user}@{ssh_host}
|
||||
)
|
||||
|
||||
pause
|
||||
'''
|
||||
else:
|
||||
# Regular VPS Server connection
|
||||
batch_content = f'''@echo off
|
||||
echo Connecting to VPS Server...
|
||||
echo.
|
||||
echo Please wait while establishing connection...
|
||||
@ -715,6 +762,71 @@ pause
|
||||
# Update activity button if exists
|
||||
if hasattr(self, 'activity_button'):
|
||||
self.activity_button.configure(text="▶")
|
||||
|
||||
def update_service_status(self, is_online: bool):
|
||||
"""Update service status indicator"""
|
||||
if hasattr(self, 'service_status_indicator'):
|
||||
if is_online:
|
||||
self.service_status_indicator.configure(
|
||||
text="🟢",
|
||||
text_color=COLORS['accent_success']
|
||||
)
|
||||
self._update_service_tooltip("Online")
|
||||
else:
|
||||
self.service_status_indicator.configure(
|
||||
text="🔴",
|
||||
text_color=COLORS['accent_error']
|
||||
)
|
||||
self._update_service_tooltip("Offline")
|
||||
|
||||
def _update_service_tooltip(self, status: str):
|
||||
"""Update tooltip for service status"""
|
||||
if hasattr(self, 'service_status_indicator'):
|
||||
# Store status for tooltip
|
||||
self.service_status = status
|
||||
|
||||
# Bind hover events if not already bound
|
||||
if not hasattr(self, '_service_tooltip_bound'):
|
||||
self.service_status_indicator.bind("<Enter>", self._show_service_tooltip)
|
||||
self.service_status_indicator.bind("<Leave>", self._hide_service_tooltip)
|
||||
self._service_tooltip_bound = True
|
||||
|
||||
def _show_service_tooltip(self, event):
|
||||
"""Show service status tooltip"""
|
||||
if hasattr(self, 'service_tooltip'):
|
||||
self.service_tooltip.destroy()
|
||||
|
||||
self.service_tooltip = ctk.CTkToplevel(self)
|
||||
self.service_tooltip.wm_overrideredirect(True)
|
||||
self.service_tooltip.configure(fg_color=COLORS['bg_secondary'])
|
||||
|
||||
# Get status text
|
||||
status_text = getattr(self, 'service_status', 'Wird geprüft...')
|
||||
if status_text == "Online":
|
||||
display_text = "✓ Service ist online"
|
||||
elif status_text == "Offline":
|
||||
display_text = "✗ Service ist offline"
|
||||
else:
|
||||
display_text = "⟳ Status wird geprüft..."
|
||||
|
||||
label = ctk.CTkLabel(
|
||||
self.service_tooltip,
|
||||
text=display_text,
|
||||
font=FONTS['small'],
|
||||
text_color=COLORS['text_primary'],
|
||||
fg_color=COLORS['bg_secondary']
|
||||
)
|
||||
label.pack(padx=8, pady=5)
|
||||
|
||||
# Position tooltip
|
||||
x = self.service_status_indicator.winfo_rootx()
|
||||
y = self.service_status_indicator.winfo_rooty() + 25
|
||||
self.service_tooltip.geometry(f"+{x}+{y}")
|
||||
|
||||
def _hide_service_tooltip(self, event):
|
||||
"""Hide service status tooltip"""
|
||||
if hasattr(self, 'service_tooltip'):
|
||||
self.service_tooltip.destroy()
|
||||
|
||||
def _show_activity_tooltip(self, user_name: str):
|
||||
"""Show tooltip with active user name"""
|
||||
|
||||
@ -8,6 +8,8 @@ from gui.styles import COLORS, FONTS
|
||||
from utils.logger import logger
|
||||
import json
|
||||
from pathlib import Path
|
||||
from tkinter import filedialog
|
||||
import os
|
||||
|
||||
|
||||
class SettingsDialog(ctk.CTkToplevel):
|
||||
@ -19,7 +21,7 @@ class SettingsDialog(ctk.CTkToplevel):
|
||||
|
||||
# Window setup
|
||||
self.title("Einstellungen")
|
||||
self.minsize(500, 450)
|
||||
self.minsize(650, 550)
|
||||
self.resizable(True, True)
|
||||
|
||||
# Make modal
|
||||
@ -56,96 +58,31 @@ class SettingsDialog(ctk.CTkToplevel):
|
||||
)
|
||||
title_label.pack(pady=(0, 20))
|
||||
|
||||
# Activity Server Section
|
||||
activity_section = ctk.CTkFrame(main_frame, fg_color=COLORS['bg_secondary'])
|
||||
activity_section.pack(fill="x", pady=(0, 15))
|
||||
|
||||
activity_header = ctk.CTkLabel(
|
||||
activity_section,
|
||||
text="👥 Team-Aktivität Server",
|
||||
font=FONTS['body'],
|
||||
text_color=COLORS['text_primary']
|
||||
)
|
||||
activity_header.pack(anchor="w", padx=15, pady=(10, 5))
|
||||
|
||||
# Server URL
|
||||
url_label = ctk.CTkLabel(
|
||||
activity_section,
|
||||
text="Server URL:",
|
||||
font=FONTS['small'],
|
||||
text_color=COLORS['text_secondary']
|
||||
)
|
||||
url_label.pack(anchor="w", padx=30, pady=(5, 0))
|
||||
|
||||
self.server_url_var = ctk.StringVar(value=self.settings.get("activity_server_url", "http://91.99.192.14:3001"))
|
||||
self.server_url_entry = ctk.CTkEntry(
|
||||
activity_section,
|
||||
textvariable=self.server_url_var,
|
||||
width=300,
|
||||
fg_color=COLORS['bg_primary']
|
||||
)
|
||||
self.server_url_entry.pack(padx=30, pady=(0, 5))
|
||||
|
||||
# API Key
|
||||
api_label = ctk.CTkLabel(
|
||||
activity_section,
|
||||
text="API Key:",
|
||||
font=FONTS['small'],
|
||||
text_color=COLORS['text_secondary']
|
||||
)
|
||||
api_label.pack(anchor="w", padx=30, pady=(5, 0))
|
||||
|
||||
self.api_key_var = ctk.StringVar(value=self.settings.get("activity_api_key", ""))
|
||||
self.api_key_entry = ctk.CTkEntry(
|
||||
activity_section,
|
||||
textvariable=self.api_key_var,
|
||||
width=300,
|
||||
fg_color=COLORS['bg_primary'],
|
||||
show="*"
|
||||
)
|
||||
self.api_key_entry.pack(padx=30, pady=(0, 5))
|
||||
|
||||
# User Name
|
||||
user_label = ctk.CTkLabel(
|
||||
activity_section,
|
||||
text="Benutzername:",
|
||||
font=FONTS['small'],
|
||||
text_color=COLORS['text_secondary']
|
||||
)
|
||||
user_label.pack(anchor="w", padx=30, pady=(5, 0))
|
||||
|
||||
self.user_name_var = ctk.StringVar(value=self.settings.get("activity_user_name", ""))
|
||||
self.user_name_entry = ctk.CTkEntry(
|
||||
activity_section,
|
||||
textvariable=self.user_name_var,
|
||||
width=300,
|
||||
fg_color=COLORS['bg_primary']
|
||||
)
|
||||
self.user_name_entry.pack(padx=30, pady=(0, 5))
|
||||
|
||||
# Test connection button
|
||||
test_btn = ctk.CTkButton(
|
||||
activity_section,
|
||||
text="Verbindung testen",
|
||||
command=self.test_activity_connection,
|
||||
fg_color=COLORS['bg_tile'],
|
||||
hover_color=COLORS['bg_tile_hover'],
|
||||
# Tab view
|
||||
self.tabview = ctk.CTkTabview(
|
||||
main_frame,
|
||||
fg_color=COLORS['bg_secondary'],
|
||||
segmented_button_fg_color=COLORS['bg_tile'],
|
||||
segmented_button_selected_color=COLORS['accent_primary'],
|
||||
segmented_button_selected_hover_color=COLORS['accent_hover'],
|
||||
segmented_button_unselected_color=COLORS['bg_tile'],
|
||||
segmented_button_unselected_hover_color=COLORS['bg_tile_hover'],
|
||||
text_color=COLORS['text_primary'],
|
||||
width=150
|
||||
text_color_disabled=COLORS['text_dim']
|
||||
)
|
||||
test_btn.pack(pady=(5, 10))
|
||||
self.tabview.pack(fill="both", expand=True)
|
||||
|
||||
# Status label
|
||||
self.connection_status_label = ctk.CTkLabel(
|
||||
activity_section,
|
||||
text="",
|
||||
font=FONTS['small'],
|
||||
text_color=COLORS['text_secondary'],
|
||||
height=20
|
||||
)
|
||||
self.connection_status_label.pack(pady=(0, 10))
|
||||
# Add tabs
|
||||
self.tabview.add("Allgemein")
|
||||
self.tabview.add("Gitea")
|
||||
self.tabview.add("Team-Aktivität")
|
||||
|
||||
# Buttons
|
||||
# Setup each tab
|
||||
self.setup_general_tab()
|
||||
self.setup_gitea_tab()
|
||||
self.setup_activity_tab()
|
||||
|
||||
# Buttons (at bottom of main frame)
|
||||
button_frame = ctk.CTkFrame(main_frame, fg_color="transparent")
|
||||
button_frame.pack(fill="x", pady=(20, 0))
|
||||
|
||||
@ -184,6 +121,264 @@ class SettingsDialog(ctk.CTkToplevel):
|
||||
)
|
||||
save_btn.pack(side="right", padx=(5, 0))
|
||||
|
||||
def setup_general_tab(self):
|
||||
"""Setup general settings tab"""
|
||||
tab = self.tabview.tab("Allgemein")
|
||||
|
||||
# Container with padding
|
||||
container = ctk.CTkFrame(tab, fg_color="transparent")
|
||||
container.pack(fill="both", expand=True, padx=20, pady=20)
|
||||
|
||||
# Placeholder for future general settings
|
||||
info_label = ctk.CTkLabel(
|
||||
container,
|
||||
text="Allgemeine Einstellungen werden hier angezeigt.\n(Aktuell keine verfügbar)",
|
||||
font=FONTS['body'],
|
||||
text_color=COLORS['text_dim']
|
||||
)
|
||||
info_label.pack(pady=50)
|
||||
|
||||
def setup_gitea_tab(self):
|
||||
"""Setup Gitea settings tab"""
|
||||
tab = self.tabview.tab("Gitea")
|
||||
|
||||
# Scrollable container
|
||||
container = ctk.CTkScrollableFrame(tab, fg_color="transparent")
|
||||
container.pack(fill="both", expand=True, padx=20, pady=20)
|
||||
|
||||
# Clone Directory Section
|
||||
clone_section = ctk.CTkFrame(container, fg_color=COLORS['bg_tile'])
|
||||
clone_section.pack(fill="x", pady=(0, 15))
|
||||
|
||||
clone_header = ctk.CTkLabel(
|
||||
clone_section,
|
||||
text="📁 Clone-Verzeichnis",
|
||||
font=FONTS['body'],
|
||||
text_color=COLORS['text_primary']
|
||||
)
|
||||
clone_header.pack(anchor="w", padx=15, pady=(10, 5))
|
||||
|
||||
# Clone directory path
|
||||
clone_path_frame = ctk.CTkFrame(clone_section, fg_color="transparent")
|
||||
clone_path_frame.pack(fill="x", padx=30, pady=(5, 10))
|
||||
|
||||
default_clone_dir = str(Path.home() / "GiteaRepos")
|
||||
self.clone_dir_var = ctk.StringVar(value=self.settings.get("gitea_clone_directory", default_clone_dir))
|
||||
|
||||
self.clone_dir_entry = ctk.CTkEntry(
|
||||
clone_path_frame,
|
||||
textvariable=self.clone_dir_var,
|
||||
width=350,
|
||||
fg_color=COLORS['bg_primary']
|
||||
)
|
||||
self.clone_dir_entry.pack(side="left", padx=(0, 10))
|
||||
|
||||
browse_btn = ctk.CTkButton(
|
||||
clone_path_frame,
|
||||
text="Durchsuchen...",
|
||||
command=self.browse_clone_directory,
|
||||
fg_color=COLORS['bg_secondary'],
|
||||
hover_color=COLORS['bg_tile_hover'],
|
||||
text_color=COLORS['text_primary'],
|
||||
width=100
|
||||
)
|
||||
browse_btn.pack(side="left")
|
||||
|
||||
# Info label
|
||||
info_label = ctk.CTkLabel(
|
||||
clone_section,
|
||||
text="Standardverzeichnis für geklonte Repositories",
|
||||
font=FONTS['small'],
|
||||
text_color=COLORS['text_dim']
|
||||
)
|
||||
info_label.pack(anchor="w", padx=30, pady=(0, 10))
|
||||
|
||||
# Server Configuration Section
|
||||
server_section = ctk.CTkFrame(container, fg_color=COLORS['bg_tile'])
|
||||
server_section.pack(fill="x", pady=(0, 15))
|
||||
|
||||
server_header = ctk.CTkLabel(
|
||||
server_section,
|
||||
text="🌐 Gitea Server",
|
||||
font=FONTS['body'],
|
||||
text_color=COLORS['text_primary']
|
||||
)
|
||||
server_header.pack(anchor="w", padx=15, pady=(10, 5))
|
||||
|
||||
# Server URL
|
||||
url_label = ctk.CTkLabel(
|
||||
server_section,
|
||||
text="Server URL:",
|
||||
font=FONTS['small'],
|
||||
text_color=COLORS['text_secondary']
|
||||
)
|
||||
url_label.pack(anchor="w", padx=30, pady=(5, 0))
|
||||
|
||||
self.gitea_url_var = ctk.StringVar(value=self.settings.get("gitea_server_url", "https://gitea-undso.intelsight.de"))
|
||||
self.gitea_url_entry = ctk.CTkEntry(
|
||||
server_section,
|
||||
textvariable=self.gitea_url_var,
|
||||
width=350,
|
||||
fg_color=COLORS['bg_primary']
|
||||
)
|
||||
self.gitea_url_entry.pack(padx=30, pady=(0, 5))
|
||||
|
||||
# API Token
|
||||
token_label = ctk.CTkLabel(
|
||||
server_section,
|
||||
text="API Token:",
|
||||
font=FONTS['small'],
|
||||
text_color=COLORS['text_secondary']
|
||||
)
|
||||
token_label.pack(anchor="w", padx=30, pady=(5, 0))
|
||||
|
||||
self.gitea_token_var = ctk.StringVar(value=self.settings.get("gitea_api_token", ""))
|
||||
self.gitea_token_entry = ctk.CTkEntry(
|
||||
server_section,
|
||||
textvariable=self.gitea_token_var,
|
||||
width=350,
|
||||
fg_color=COLORS['bg_primary'],
|
||||
show="*"
|
||||
)
|
||||
self.gitea_token_entry.pack(padx=30, pady=(0, 5))
|
||||
|
||||
# Username
|
||||
user_label = ctk.CTkLabel(
|
||||
server_section,
|
||||
text="Benutzername:",
|
||||
font=FONTS['small'],
|
||||
text_color=COLORS['text_secondary']
|
||||
)
|
||||
user_label.pack(anchor="w", padx=30, pady=(5, 0))
|
||||
|
||||
self.gitea_user_var = ctk.StringVar(value=self.settings.get("gitea_username", ""))
|
||||
self.gitea_user_entry = ctk.CTkEntry(
|
||||
server_section,
|
||||
textvariable=self.gitea_user_var,
|
||||
width=350,
|
||||
fg_color=COLORS['bg_primary']
|
||||
)
|
||||
self.gitea_user_entry.pack(padx=30, pady=(0, 10))
|
||||
|
||||
# Test connection button
|
||||
test_btn = ctk.CTkButton(
|
||||
server_section,
|
||||
text="Verbindung testen",
|
||||
command=self.test_gitea_connection,
|
||||
fg_color=COLORS['bg_secondary'],
|
||||
hover_color=COLORS['bg_tile_hover'],
|
||||
text_color=COLORS['text_primary'],
|
||||
width=150
|
||||
)
|
||||
test_btn.pack(pady=(5, 10))
|
||||
|
||||
# Status label
|
||||
self.gitea_status_label = ctk.CTkLabel(
|
||||
server_section,
|
||||
text="",
|
||||
font=FONTS['small'],
|
||||
text_color=COLORS['text_secondary'],
|
||||
height=20
|
||||
)
|
||||
self.gitea_status_label.pack(pady=(0, 10))
|
||||
|
||||
def setup_activity_tab(self):
|
||||
"""Setup activity server settings tab"""
|
||||
tab = self.tabview.tab("Team-Aktivität")
|
||||
|
||||
# Container with padding
|
||||
container = ctk.CTkFrame(tab, fg_color="transparent")
|
||||
container.pack(fill="both", expand=True, padx=20, pady=20)
|
||||
|
||||
# Activity Server Section
|
||||
activity_section = ctk.CTkFrame(container, fg_color=COLORS['bg_tile'])
|
||||
activity_section.pack(fill="x", pady=(0, 15))
|
||||
|
||||
activity_header = ctk.CTkLabel(
|
||||
activity_section,
|
||||
text="👥 Team-Aktivität Server",
|
||||
font=FONTS['body'],
|
||||
text_color=COLORS['text_primary']
|
||||
)
|
||||
activity_header.pack(anchor="w", padx=15, pady=(10, 5))
|
||||
|
||||
# Server URL
|
||||
url_label = ctk.CTkLabel(
|
||||
activity_section,
|
||||
text="Server URL:",
|
||||
font=FONTS['small'],
|
||||
text_color=COLORS['text_secondary']
|
||||
)
|
||||
url_label.pack(anchor="w", padx=30, pady=(5, 0))
|
||||
|
||||
self.server_url_var = ctk.StringVar(value=self.settings.get("activity_server_url", "http://91.99.192.14:3001"))
|
||||
self.server_url_entry = ctk.CTkEntry(
|
||||
activity_section,
|
||||
textvariable=self.server_url_var,
|
||||
width=350,
|
||||
fg_color=COLORS['bg_primary']
|
||||
)
|
||||
self.server_url_entry.pack(padx=30, pady=(0, 5))
|
||||
|
||||
# API Key
|
||||
api_label = ctk.CTkLabel(
|
||||
activity_section,
|
||||
text="API Key:",
|
||||
font=FONTS['small'],
|
||||
text_color=COLORS['text_secondary']
|
||||
)
|
||||
api_label.pack(anchor="w", padx=30, pady=(5, 0))
|
||||
|
||||
self.api_key_var = ctk.StringVar(value=self.settings.get("activity_api_key", ""))
|
||||
self.api_key_entry = ctk.CTkEntry(
|
||||
activity_section,
|
||||
textvariable=self.api_key_var,
|
||||
width=350,
|
||||
fg_color=COLORS['bg_primary'],
|
||||
show="*"
|
||||
)
|
||||
self.api_key_entry.pack(padx=30, pady=(0, 5))
|
||||
|
||||
# User Name
|
||||
user_label = ctk.CTkLabel(
|
||||
activity_section,
|
||||
text="Benutzername:",
|
||||
font=FONTS['small'],
|
||||
text_color=COLORS['text_secondary']
|
||||
)
|
||||
user_label.pack(anchor="w", padx=30, pady=(5, 0))
|
||||
|
||||
self.user_name_var = ctk.StringVar(value=self.settings.get("activity_user_name", ""))
|
||||
self.user_name_entry = ctk.CTkEntry(
|
||||
activity_section,
|
||||
textvariable=self.user_name_var,
|
||||
width=350,
|
||||
fg_color=COLORS['bg_primary']
|
||||
)
|
||||
self.user_name_entry.pack(padx=30, pady=(0, 5))
|
||||
|
||||
# Test connection button
|
||||
test_btn = ctk.CTkButton(
|
||||
activity_section,
|
||||
text="Verbindung testen",
|
||||
command=self.test_activity_connection,
|
||||
fg_color=COLORS['bg_secondary'],
|
||||
hover_color=COLORS['bg_tile_hover'],
|
||||
text_color=COLORS['text_primary'],
|
||||
width=150
|
||||
)
|
||||
test_btn.pack(pady=(5, 10))
|
||||
|
||||
# Status label
|
||||
self.connection_status_label = ctk.CTkLabel(
|
||||
activity_section,
|
||||
text="",
|
||||
font=FONTS['small'],
|
||||
text_color=COLORS['text_secondary'],
|
||||
height=20
|
||||
)
|
||||
self.connection_status_label.pack(pady=(0, 10))
|
||||
|
||||
def load_settings(self):
|
||||
"""Load settings from file"""
|
||||
try:
|
||||
@ -205,49 +400,141 @@ class SettingsDialog(ctk.CTkToplevel):
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to save UI settings: {e}")
|
||||
|
||||
def browse_clone_directory(self):
|
||||
"""Browse for clone directory"""
|
||||
current_dir = self.clone_dir_var.get()
|
||||
if not os.path.exists(current_dir):
|
||||
current_dir = str(Path.home())
|
||||
|
||||
directory = filedialog.askdirectory(
|
||||
title="Clone-Verzeichnis auswählen",
|
||||
initialdir=current_dir,
|
||||
parent=self
|
||||
)
|
||||
|
||||
if directory:
|
||||
self.clone_dir_var.set(directory)
|
||||
logger.info(f"Selected clone directory: {directory}")
|
||||
|
||||
def test_gitea_connection(self):
|
||||
"""Test connection to Gitea server"""
|
||||
import requests
|
||||
|
||||
server_url = self.gitea_url_var.get().strip()
|
||||
api_token = self.gitea_token_var.get().strip()
|
||||
|
||||
if not server_url:
|
||||
self.gitea_status_label.configure(
|
||||
text="⚠️ Bitte Server URL eingeben",
|
||||
text_color=COLORS['accent_warning']
|
||||
)
|
||||
return
|
||||
|
||||
self.gitea_status_label.configure(
|
||||
text="🔄 Teste Verbindung...",
|
||||
text_color=COLORS['text_secondary']
|
||||
)
|
||||
self.update()
|
||||
|
||||
try:
|
||||
# Try to connect to the Gitea API
|
||||
headers = {}
|
||||
if api_token:
|
||||
headers["Authorization"] = f"token {api_token}"
|
||||
|
||||
response = requests.get(
|
||||
f"{server_url}/api/v1/version",
|
||||
timeout=5,
|
||||
headers=headers
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
version = response.json().get('version', 'Unknown')
|
||||
self.gitea_status_label.configure(
|
||||
text=f"✅ Verbindung erfolgreich! (Gitea {version})",
|
||||
text_color=COLORS['accent_success']
|
||||
)
|
||||
logger.info(f"Gitea connection successful: {server_url}, version: {version}")
|
||||
else:
|
||||
self.gitea_status_label.configure(
|
||||
text=f"❌ Server antwortet mit Status {response.status_code}",
|
||||
text_color=COLORS['accent_error']
|
||||
)
|
||||
logger.warning(f"Gitea server returned status {response.status_code}")
|
||||
|
||||
except requests.exceptions.ConnectionError:
|
||||
self.gitea_status_label.configure(
|
||||
text="❌ Server nicht erreichbar",
|
||||
text_color=COLORS['accent_error']
|
||||
)
|
||||
logger.error(f"Gitea server not reachable: {server_url}")
|
||||
except requests.exceptions.Timeout:
|
||||
self.gitea_status_label.configure(
|
||||
text="❌ Verbindung Timeout",
|
||||
text_color=COLORS['accent_error']
|
||||
)
|
||||
logger.error(f"Gitea server connection timeout: {server_url}")
|
||||
except Exception as e:
|
||||
self.gitea_status_label.configure(
|
||||
text=f"❌ Fehler: {str(e)}",
|
||||
text_color=COLORS['accent_error']
|
||||
)
|
||||
logger.error(f"Gitea server connection error: {e}")
|
||||
|
||||
def save_settings_only(self):
|
||||
"""Save settings without applying to service or closing dialog"""
|
||||
# Get values
|
||||
# Get activity values
|
||||
server_url = self.server_url_var.get().strip()
|
||||
api_key = self.api_key_var.get().strip()
|
||||
user_name = self.user_name_var.get().strip()
|
||||
|
||||
# Get Gitea values
|
||||
gitea_url = self.gitea_url_var.get().strip()
|
||||
gitea_token = self.gitea_token_var.get().strip()
|
||||
gitea_user = self.gitea_user_var.get().strip()
|
||||
clone_dir = self.clone_dir_var.get().strip()
|
||||
|
||||
# Update settings
|
||||
self.settings["activity_server_url"] = server_url
|
||||
self.settings["activity_api_key"] = api_key
|
||||
self.settings["activity_user_name"] = user_name
|
||||
|
||||
self.settings["gitea_server_url"] = gitea_url
|
||||
self.settings["gitea_api_token"] = gitea_token
|
||||
self.settings["gitea_username"] = gitea_user
|
||||
self.settings["gitea_clone_directory"] = clone_dir
|
||||
|
||||
self.save_settings()
|
||||
|
||||
# Show confirmation
|
||||
self.connection_status_label.configure(
|
||||
text="✅ Einstellungen gespeichert!",
|
||||
text_color=COLORS['accent_success']
|
||||
)
|
||||
logger.info("Settings saved successfully")
|
||||
# Show confirmation on the active tab
|
||||
current_tab = self.tabview.get()
|
||||
if current_tab == "Team-Aktivität":
|
||||
self.connection_status_label.configure(
|
||||
text="✅ Einstellungen gespeichert!",
|
||||
text_color=COLORS['accent_success']
|
||||
)
|
||||
self.after(2000, lambda: self.connection_status_label.configure(text=""))
|
||||
elif current_tab == "Gitea":
|
||||
self.gitea_status_label.configure(
|
||||
text="✅ Einstellungen gespeichert!",
|
||||
text_color=COLORS['accent_success']
|
||||
)
|
||||
self.after(2000, lambda: self.gitea_status_label.configure(text=""))
|
||||
|
||||
# Clear status after 2 seconds
|
||||
self.after(2000, lambda: self.connection_status_label.configure(text=""))
|
||||
logger.info("Settings saved successfully")
|
||||
|
||||
def apply_settings(self):
|
||||
"""Apply the selected settings"""
|
||||
import uuid
|
||||
from services.activity_sync import activity_service
|
||||
|
||||
# Get values
|
||||
server_url = self.server_url_var.get().strip()
|
||||
api_key = self.api_key_var.get().strip()
|
||||
user_name = self.user_name_var.get().strip()
|
||||
# Save all settings first
|
||||
self.save_settings_only()
|
||||
|
||||
# Update settings
|
||||
self.settings["activity_server_url"] = server_url
|
||||
self.settings["activity_api_key"] = api_key
|
||||
self.settings["activity_user_name"] = user_name
|
||||
self.save_settings()
|
||||
|
||||
# Update activity service
|
||||
activity_service.server_url = server_url
|
||||
activity_service.api_key = api_key
|
||||
activity_service.user_name = user_name
|
||||
# Apply activity service settings
|
||||
activity_service.server_url = self.settings.get("activity_server_url", "")
|
||||
activity_service.api_key = self.settings.get("activity_api_key", "")
|
||||
activity_service.user_name = self.settings.get("activity_user_name", "")
|
||||
activity_service.user_id = self.settings.get("activity_user_id", str(uuid.uuid4()))
|
||||
|
||||
# Save user ID if newly generated
|
||||
@ -267,9 +554,65 @@ class SettingsDialog(ctk.CTkToplevel):
|
||||
|
||||
logger.info("Activity server settings applied")
|
||||
|
||||
# Apply Gitea settings to config
|
||||
self.update_gitea_config()
|
||||
|
||||
# Close dialog
|
||||
self.destroy()
|
||||
|
||||
def update_gitea_config(self):
|
||||
"""Update Gitea configuration in the application"""
|
||||
try:
|
||||
# Import here to avoid circular imports
|
||||
from src.gitea.gitea_client import gitea_config
|
||||
from src.gitea.git_operations import git_ops
|
||||
|
||||
# Update Gitea config
|
||||
if hasattr(gitea_config, 'base_url'):
|
||||
gitea_config.base_url = self.settings.get("gitea_server_url", gitea_config.base_url)
|
||||
if hasattr(gitea_config, 'api_token'):
|
||||
gitea_config.api_token = self.settings.get("gitea_api_token", gitea_config.api_token)
|
||||
if hasattr(gitea_config, 'username'):
|
||||
gitea_config.username = self.settings.get("gitea_username", gitea_config.username)
|
||||
|
||||
# Update clone directory
|
||||
clone_dir = self.settings.get("gitea_clone_directory")
|
||||
if clone_dir:
|
||||
# Re-initialize git_ops to use new settings
|
||||
from src.gitea.git_operations import init_git_ops
|
||||
init_git_ops()
|
||||
|
||||
# Now update the clone directory
|
||||
if hasattr(git_ops, 'default_clone_dir'):
|
||||
git_ops.default_clone_dir = Path(clone_dir)
|
||||
logger.info(f"Updated git_ops clone directory to: {clone_dir}")
|
||||
|
||||
# Update all existing RepositoryManager instances
|
||||
# This ensures that any git_ops instances in the application get the new directory
|
||||
try:
|
||||
# Update in main window if it exists
|
||||
parent = self.master
|
||||
if hasattr(parent, 'gitea_handler') and hasattr(parent.gitea_handler, 'repo_manager'):
|
||||
if hasattr(parent.gitea_handler.repo_manager, 'git_ops'):
|
||||
parent.gitea_handler.repo_manager.git_ops.default_clone_dir = Path(clone_dir)
|
||||
logger.info("Updated main window's git_ops clone directory")
|
||||
|
||||
# Update in sidebar gitea explorer if it exists
|
||||
if hasattr(parent, 'sidebar') and hasattr(parent.sidebar, 'gitea_explorer'):
|
||||
if hasattr(parent.sidebar.gitea_explorer, 'repo_manager'):
|
||||
if hasattr(parent.sidebar.gitea_explorer.repo_manager, 'git_ops'):
|
||||
parent.sidebar.gitea_explorer.repo_manager.git_ops.default_clone_dir = Path(clone_dir)
|
||||
logger.info("Updated sidebar's git_ops clone directory")
|
||||
except Exception as e:
|
||||
logger.warning(f"Could not update all git_ops instances: {e}")
|
||||
|
||||
logger.info("Gitea configuration updated")
|
||||
|
||||
except ImportError as e:
|
||||
logger.warning(f"Could not update Gitea config: {e}")
|
||||
except Exception as e:
|
||||
logger.error(f"Error updating Gitea config: {e}")
|
||||
|
||||
def test_activity_connection(self):
|
||||
"""Test connection to activity server"""
|
||||
import requests
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren