Dieser Commit ist enthalten in:
Claude Project Manager
2025-07-08 13:13:46 +02:00
Ursprung d1667f9e0d
Commit 5f32daf3a4
15 geänderte Dateien mit 1139 neuen und 136 gelöschten Zeilen

Datei anzeigen

@ -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,

Datei anzeigen

@ -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

Datei anzeigen

@ -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"""

Datei anzeigen

@ -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