Dieser Commit ist enthalten in:
Claude Project Manager
2025-08-01 23:50:28 +02:00
Commit 04585e95b6
290 geänderte Dateien mit 64086 neuen und 0 gelöschten Zeilen

Datei anzeigen

@ -0,0 +1,229 @@
"""Service for applying browser protection during automation."""
from typing import Optional
from playwright.sync_api import Page
from domain.value_objects.browser_protection_style import BrowserProtectionStyle, ProtectionLevel
class BrowserProtectionService:
"""Handles browser protection during automation to prevent user interference."""
SHIELD_ELEMENT_ID = "accountforge-shield"
PROTECTION_STYLE_ID = "accountforge-protection-styles"
def protect_browser(self, page: Page, style: BrowserProtectionStyle) -> None:
"""Apply protection to the browser page based on the configured style."""
if style.level == ProtectionLevel.NONE:
return
# Generate and inject protection script
script = self._generate_protection_script(style)
page.evaluate(script)
# Speichere Script für Wiederanwendung
escaped_script = script.replace('`', '\\`')
page.evaluate(f"""
window.__accountforge_protection = `{escaped_script}`;
""")
def remove_protection(self, page: Page) -> None:
"""Remove all protection from the browser page."""
page.evaluate(f"""
// Remove shield element
const shield = document.getElementById('{self.SHIELD_ELEMENT_ID}');
if (shield) shield.remove();
// Remove style element
const styles = document.getElementById('{self.PROTECTION_STYLE_ID}');
if (styles) styles.remove();
// Note: Event listeners will be removed on page navigation
""")
def _generate_protection_script(self, style: BrowserProtectionStyle) -> str:
"""Generate JavaScript code for protection based on style configuration."""
script_parts = []
# Create shield overlay
if style.level in [ProtectionLevel.MEDIUM, ProtectionLevel.STRONG]:
script_parts.append(self._create_shield_script(style))
# Add visual effects
if style.show_border or (style.level == ProtectionLevel.LIGHT):
script_parts.append(self._create_visual_effects_script(style))
# Block interactions
if style.level in [ProtectionLevel.MEDIUM, ProtectionLevel.STRONG]:
script_parts.append(self._create_interaction_blocker_script())
return "\n".join(script_parts)
def _create_shield_script(self, style: BrowserProtectionStyle) -> str:
"""Create the main shield overlay element."""
badge_positions = {
"top-left": "top: 20px; left: 20px;",
"top-right": "top: 20px; right: 20px;",
"bottom-left": "bottom: 20px; left: 20px;",
"bottom-right": "bottom: 20px; right: 20px;"
}
badge_position_css = badge_positions.get(style.badge_position, badge_positions["top-right"])
blur_css = "backdrop-filter: blur(2px);" if style.blur_effect else ""
return f"""
// Create shield overlay
const shield = document.createElement('div');
shield.id = '{self.SHIELD_ELEMENT_ID}';
shield.style.cssText = `
position: fixed;
top: 0; left: 0;
width: 100vw; height: 100vh;
background: {style.get_overlay_color()};
{blur_css}
z-index: 2147483647;
cursor: not-allowed;
user-select: none;
pointer-events: all;
`;
// Add info badge if configured
if ({str(style.show_badge).lower()}) {{
const badge = document.createElement('div');
badge.style.cssText = `
position: absolute;
{badge_position_css}
background: rgba(220, 38, 38, 0.95);
color: white;
padding: 12px 24px;
border-radius: 8px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
font-size: 14px;
font-weight: 500;
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
animation: fadeIn 0.3s ease-in;
`;
badge.textContent = '{style.badge_text}';
shield.appendChild(badge);
}}
document.documentElement.appendChild(shield);
"""
def _create_visual_effects_script(self, style: BrowserProtectionStyle) -> str:
"""Create visual effects like animated border."""
return f"""
// Add styles for visual effects
const styleElement = document.createElement('style');
styleElement.id = '{self.PROTECTION_STYLE_ID}';
styleElement.textContent = `
@keyframes pulse {{
0% {{ opacity: 0.4; }}
50% {{ opacity: 0.8; }}
100% {{ opacity: 0.4; }}
}}
@keyframes fadeIn {{
from {{ opacity: 0; transform: translateY(-10px); }}
to {{ opacity: 1; transform: translateY(0); }}
}}
{self._get_border_css(style) if style.show_border else ''}
`;
document.head.appendChild(styleElement);
{self._get_border_element_script(style) if style.show_border else ''}
"""
def _get_border_css(self, style: BrowserProtectionStyle) -> str:
"""Get CSS for animated border."""
return f"""
#accountforge-border {{
position: fixed;
top: 0; left: 0;
width: 100%; height: 100%;
border: 3px solid {style.border_color};
box-shadow: inset 0 0 30px rgba(255, 0, 0, 0.2);
pointer-events: none;
animation: pulse 2s infinite;
z-index: 2147483646;
}}
"""
def _get_border_element_script(self, style: BrowserProtectionStyle) -> str:
"""Get script to create border element."""
return f"""
// Create animated border
const border = document.createElement('div');
border.id = 'accountforge-border';
document.body.appendChild(border);
"""
def _create_interaction_blocker_script(self) -> str:
"""Create script to block all user interactions."""
return f"""
// Verhindere das Shield selbst entfernt zu werden
(function() {{
// Prüfe ob Shield schon existiert
if (document.getElementById('{self.SHIELD_ELEMENT_ID}')) {{
return;
}}
// Block all interaction events
const blockedEvents = [
'click', 'dblclick', 'mousedown', 'mouseup', 'mousemove',
'keydown', 'keypress', 'keyup',
'touchstart', 'touchend', 'touchmove',
'contextmenu', 'wheel', 'scroll', 'input', 'change', 'focus'
];
const eventBlocker = function(e) {{
// Prüfe ob das Event vom Shield selbst kommt
const shield = document.getElementById('{self.SHIELD_ELEMENT_ID}');
if (shield && (e.target === shield || shield.contains(e.target))) {{
return;
}}
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
return false;
}};
// Add event listeners with capture phase
blockedEvents.forEach(eventType => {{
document.addEventListener(eventType, eventBlocker, true);
window.addEventListener(eventType, eventBlocker, true);
}});
// Disable text selection
document.body.style.userSelect = 'none';
document.body.style.webkitUserSelect = 'none';
document.body.style.mozUserSelect = 'none';
document.body.style.msUserSelect = 'none';
// Disable all input fields
const disableInputs = () => {{
document.querySelectorAll('input, textarea, select, button').forEach(el => {{
el.style.pointerEvents = 'none';
el.setAttribute('disabled', 'true');
el.setAttribute('readonly', 'true');
}});
}};
// Initial disable
disableInputs();
// Re-disable on DOM changes
const inputObserver = new MutationObserver(disableInputs);
inputObserver.observe(document.body, {{ childList: true, subtree: true }});
// Disable drag
document.ondragstart = function() {{ return false; }};
document.onselectstart = function() {{ return false; }};
// Prevent focus on any element
document.addEventListener('focusin', function(e) {{
e.target.blur();
}}, true);
}})();
"""