This commit is contained in:
pika 2025-03-24 19:30:00 +01:00
parent b834040b4e
commit 6e9cea9ba4
2 changed files with 106 additions and 152 deletions

View file

@ -1,16 +1,30 @@
FROM python:3.12-alpine FROM python:3.12-alpine
# Create app directory
WORKDIR /app WORKDIR /app
COPY requirements.txt /app/ # Set environment variables
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PIP_NO_CACHE_DIR=1 \
PIP_DISABLE_PIP_VERSION_CHECK=1
# Install dependencies first (for better caching)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt RUN pip install --no-cache-dir -r requirements.txt
COPY . /app/ # Copy the rest of the application
COPY . .
# Make entrypoint script executable # Make the entrypoint script executable
RUN chmod +x /app/entrypoint.sh RUN chmod +x entrypoint.sh
EXPOSE 5000 # Set correct permissions
RUN chown -R nobody:nobody /app
# Switch to non-root user
USER nobody
# Set proper signal handling for faster shutdown
ENTRYPOINT ["/app/entrypoint.sh"] ENTRYPOINT ["/app/entrypoint.sh"]
CMD ["server"] CMD ["server"]

View file

@ -7,116 +7,48 @@
<title>Caddy Dashboard</title> <title>Caddy Dashboard</title>
<script src="https://cdn.tailwindcss.com"></script> <script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<script>
tailwind.config = {
theme: {
extend: {
colors: {
midnight: '#121212',
obsidian: '#0a0a0a',
charcoal: '#1e1e1e',
slate: {
850: '#172032',
},
neon: {
blue: '#4dadff',
purple: '#a259ff',
pink: '#ff59f8',
cyan: '#38ffdd',
green: '#3cf7a2',
},
},
backgroundImage: {
'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
},
animation: {
'gradient-shift': 'gradient-shift 10s ease infinite',
},
keyframes: {
'gradient-shift': {
'0%, 100%': { backgroundPosition: '0% 50%' },
'50%': { backgroundPosition: '100% 50%' },
}
}
}
}
}
</script>
<style> <style>
.bg-mesh { body {
background-color: #121212; background-color: #0f0f0f;
background-image: background-image:
radial-gradient(at 40% 20%, rgba(61, 65, 154, 0.1) 0px, transparent 50%), radial-gradient(circle at 25% 25%, rgba(40, 40, 40, 0.05) 0%, transparent 50%),
radial-gradient(at 80% 0%, rgba(105, 29, 209, 0.1) 0px, transparent 50%), radial-gradient(circle at 75% 75%, rgba(40, 40, 40, 0.05) 0%, transparent 50%);
radial-gradient(at 0% 50%, rgba(46, 49, 146, 0.1) 0px, transparent 50%),
radial-gradient(at 80% 50%, rgba(11, 103, 159, 0.1) 0px, transparent 50%),
radial-gradient(at 0% 100%, rgba(23, 86, 118, 0.1) 0px, transparent 50%),
radial-gradient(at 80% 100%, rgba(61, 111, 154, 0.1) 0px, transparent 50%),
radial-gradient(at 0% 0%, rgba(42, 9, 68, 0.1) 0px, transparent 50%);
}
.gradient-text {
background: linear-gradient(90deg, #4dadff, #a259ff, #38ffdd);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
background-size: 300% 100%;
animation: gradient-shift 8s ease infinite;
}
.card-gradient {
position: relative;
border-radius: 16px;
overflow: hidden;
z-index: 1;
}
.card-gradient::before {
content: "";
position: absolute;
top: -2px;
left: -2px;
right: -2px;
bottom: -2px;
background: linear-gradient(45deg, #4dadff, #a259ff, #38ffdd);
z-index: -1;
border-radius: 18px;
opacity: 0;
transition: opacity 0.3s ease;
}
.card-gradient:hover::before {
opacity: 0.5;
}
@keyframes gradient-shift {
0% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
100% {
background-position: 0% 50%;
}
} }
/* Subtle grid pattern */ /* Subtle grid pattern */
.bg-grid { .bg-grid {
background-size: 40px 40px; background-size: 50px 50px;
background-image: background-image:
linear-gradient(to right, rgba(55, 65, 81, 0.05) 1px, transparent 1px), linear-gradient(to right, rgba(40, 40, 40, 0.05) 1px, transparent 1px),
linear-gradient(to bottom, rgba(55, 65, 81, 0.05) 1px, transparent 1px); linear-gradient(to bottom, rgba(40, 40, 40, 0.05) 1px, transparent 1px);
}
.card {
transition: all 0.2s ease;
}
.card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
}
.search-popup {
opacity: 0;
visibility: hidden;
transition: opacity 0.15s ease, visibility 0.15s;
}
.search-popup.active {
opacity: 1;
visibility: visible;
} }
</style> </style>
<script> <script>
function toggleSearch() { function toggleSearch() {
let searchBar = document.getElementById("search-box"); let searchContainer = document.getElementById("search-container");
searchBar.classList.toggle("hidden"); searchContainer.classList.toggle("active");
if (!searchBar.classList.contains("hidden")) { if (searchContainer.classList.contains("active")) {
searchBar.focus(); document.getElementById("search-box").focus();
} }
} }
@ -138,7 +70,7 @@
}).then(response => { }).then(response => {
if (response.ok) { if (response.ok) {
const serverBox = document.getElementById("server-box-" + serverName); const serverBox = document.getElementById("server-box-" + serverName);
serverBox.classList.add('opacity-0', 'scale-95'); serverBox.classList.add('opacity-0');
setTimeout(() => { setTimeout(() => {
serverBox.remove(); serverBox.remove();
}, 300); }, 300);
@ -152,71 +84,79 @@
event.preventDefault(); event.preventDefault();
toggleSearch(); toggleSearch();
} }
if (event.key === "Escape") {
let searchContainer = document.getElementById("search-container");
if (searchContainer.classList.contains("active")) {
toggleSearch();
}
}
}); });
</script> </script>
</head> </head>
<body class="bg-mesh text-gray-100 min-h-screen"> <body class="text-gray-200 min-h-screen bg-grid">
<div <!-- Minimal header -->
class="bg-gradient-to-r from-slate-850/60 to-charcoal/60 backdrop-blur-md border-b border-gray-700/30 sticky top-0 z-10"> <header class="py-4 px-6 bg-black/50 backdrop-blur-sm border-b border-gray-800/50">
<header class="container mx-auto px-4 py-6 text-center"> <div class="container mx-auto flex justify-between items-center">
<h1 class="text-4xl font-bold tracking-tight gradient-text">Caddy Dashboard</h1> <h1 class="text-xl font-medium tracking-tight text-white">Caddy Dashboard</h1>
<p class="text-cyan-300 text-lg mt-2 opacity-80">Übersicht über alle aktiven Proxy-Server</p> <button onclick="toggleSearch()" class="text-sm px-3 py-1.5 bg-zinc-800 hover:bg-zinc-700 border border-zinc-700 rounded-md
<div class="mt-6 flex justify-center"> transition-colors flex items-center gap-2">
<button onclick="toggleSearch()" class="bg-gradient-to-r from-neon-blue to-neon-purple text-white px-8 py-3 rounded-full <i class="fas fa-search text-xs"></i>
shadow-lg shadow-indigo-500/20 hover:shadow-indigo-500/40 transition-all duration-300 <span class="hidden sm:inline">/</span>
flex items-center space-x-2"> </button>
<i class="fas fa-search"></i> </div>
<span>Suche</span> </header>
<!-- Search popup -->
<div id="search-container"
class="search-popup fixed inset-0 z-50 flex items-start justify-center pt-20 px-4 bg-black/50 backdrop-blur-sm">
<div class="w-full max-w-xl bg-zinc-900 border border-zinc-800 rounded-lg overflow-hidden shadow-2xl">
<div class="flex items-center px-4 border-b border-zinc-800">
<i class="fas fa-search text-gray-500 mr-3"></i>
<input type="text" id="search-box"
class="w-full py-3 bg-transparent text-white border-none focus:outline-none"
placeholder="Domain suchen..." onkeyup="filterEntries()">
<button onclick="toggleSearch()" class="text-gray-500 hover:text-white">
<i class="fas fa-times"></i>
</button> </button>
</div> </div>
</header> <div class="p-2 text-xs text-gray-500">
Drücke <kbd class="px-1.5 py-0.5 bg-zinc-800 rounded border border-zinc-700">/</kbd> zum Öffnen, <kbd
class="px-1.5 py-0.5 bg-zinc-800 rounded border border-zinc-700">ESC</kbd> zum Schließen
</div>
</div>
</div> </div>
<div class="container mx-auto p-4 md:p-6 bg-grid"> <div class="container mx-auto p-4 md:p-6">
<div class="transition-all duration-300 transform">
<input type="text" id="search-box" class="hidden w-full p-4 mb-6 text-gray-100 text-lg rounded-xl bg-charcoal/80 backdrop-blur-md
border border-gray-700/50 focus:border-neon-blue focus:outline-none focus:ring-2
focus:ring-neon-blue/30 shadow-xl transition-all duration-300 transform"
placeholder="Nach Subdomains suchen... (Drücke '/' für schnellen Zugriff)" onkeyup="filterEntries()">
</div>
{% for server, entries in proxies.items() %} {% for server, entries in proxies.items() %}
<div id="server-box-{{ server }}" class="bg-gradient-to-br from-charcoal/80 to-slate-850/80 backdrop-blur-md p-7 rounded-2xl shadow-xl mb-8 <div id="server-box-{{ server }}" class="mb-8 opacity-100 transition-opacity duration-300">
border border-gray-700/30 transition-all duration-300 transform"> <div class="flex justify-between items-center mb-4 px-1">
<div class="flex flex-col md:flex-row justify-between items-start md:items-center">
<div> <div>
<h2 class="text-2xl font-bold gradient-text">{{ server }}</h2> <h2 class="text-lg font-medium text-white">{{ server }}</h2>
<p class="text-sm text-gray-400 flex items-center mt-1"> <p class="text-xs text-gray-500 mt-0.5">Zuletzt aktualisiert: {{ timestamps[server] }}</p>
<i class="far fa-clock mr-2"></i> Zuletzt aktualisiert: {{ timestamps[server] }}
</p>
</div> </div>
<button onclick="deleteServer('{{ server }}')" class="mt-4 md:mt-0 bg-gradient-to-r from-red-500 to-rose-600 hover:from-red-600 hover:to-rose-700 <button onclick="deleteServer('{{ server }}')" class="text-xs px-3 py-1.5 bg-zinc-900 hover:bg-red-900/70 border border-zinc-800 hover:border-red-800/50 text-gray-400 hover:text-white rounded
text-white px-6 py-2.5 rounded-full text-sm font-medium transition-all duration-300 transform transition-colors">
hover:scale-105 flex items-center"> <i class="fas fa-trash-alt mr-1.5"></i>Löschen
<i class="fas fa-trash-alt mr-2"></i> Löschen
</button> </button>
</div> </div>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-5 mt-6"> <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
{% for domain, target in entries.items() %} {% for domain, target in entries.items() %}
<div class="domain-card card-gradient bg-obsidian p-5 rounded-xl backdrop-blur-sm shadow-lg <div class="domain-card card bg-zinc-900 border border-zinc-800 rounded-lg overflow-hidden"
border border-gray-800/50 flex flex-col space-y-4 transition-all duration-300 transform hover:translate-y-[-2px]"
data-domain="{{ domain }}"> data-domain="{{ domain }}">
<div class="flex justify-between items-start"> <div class="px-4 py-3 flex justify-between items-center border-b border-zinc-800">
<a href="https://{{ domain }}" target="_blank" <a href="https://{{ domain }}" target="_blank"
class="text-neon-blue hover:text-neon-cyan font-medium text-lg break-words transition-colors duration-300 flex items-center"> class="text-white hover:text-blue-400 text-sm font-medium truncate transition-colors flex items-center gap-2 max-w-[calc(100%-24px)]">
<i class="fas fa-globe mr-2 opacity-80"></i> <i class="fas fa-globe text-xs text-gray-500"></i>
<span>{{ domain }}</span> <span class="truncate">{{ domain }}</span>
</a> </a>
<div class="ml-2 flex-shrink-0"> <div class="ml-2 flex-shrink-0">
<div class="w-2 h-2 bg-green-500 rounded-full shadow-sm shadow-green-400/50"></div> <div class="w-2 h-2 bg-green-500 rounded-full"></div>
</div> </div>
</div> </div>
<div class="pt-2 border-t border-gray-800/50"> <div class="px-4 py-2.5">
<p class="text-gray-300 break-words text-sm font-mono bg-midnight/50 p-2 rounded-lg"> <p class="text-gray-400 text-xs font-mono truncate">{{ target }}</p>
<i class="fas fa-network-wired mr-2 text-gray-500"></i>{{ target }}
</p>
</div> </div>
</div> </div>
{% endfor %} {% endfor %}
@ -225,9 +165,9 @@
{% endfor %} {% endfor %}
</div> </div>
<footer class="bg-obsidian/60 backdrop-blur-md border-t border-gray-700/30 py-4 mt-12"> <footer class="py-4 mt-12 border-t border-zinc-900">
<div class="container mx-auto px-4 text-center text-gray-500 text-sm"> <div class="container mx-auto px-4 text-center text-xs text-gray-600">
Caddy Dashboard • Powered by Flask & Tailwind CSS Caddy Dashboard
</div> </div>
</footer> </footer>
</body> </body>