wip
This commit is contained in:
parent
b834040b4e
commit
6e9cea9ba4
2 changed files with 106 additions and 152 deletions
24
Dockerfile
24
Dockerfile
|
@ -1,16 +1,30 @@
|
|||
FROM python:3.12-alpine
|
||||
|
||||
# Create app directory
|
||||
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
|
||||
|
||||
COPY . /app/
|
||||
# Copy the rest of the application
|
||||
COPY . .
|
||||
|
||||
# Make entrypoint script executable
|
||||
RUN chmod +x /app/entrypoint.sh
|
||||
# Make the entrypoint script executable
|
||||
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"]
|
||||
CMD ["server"]
|
||||
|
|
|
@ -7,116 +7,48 @@
|
|||
<title>Caddy Dashboard</title>
|
||||
<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">
|
||||
<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>
|
||||
.bg-mesh {
|
||||
background-color: #121212;
|
||||
body {
|
||||
background-color: #0f0f0f;
|
||||
background-image:
|
||||
radial-gradient(at 40% 20%, rgba(61, 65, 154, 0.1) 0px, transparent 50%),
|
||||
radial-gradient(at 80% 0%, rgba(105, 29, 209, 0.1) 0px, 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%;
|
||||
}
|
||||
radial-gradient(circle at 25% 25%, rgba(40, 40, 40, 0.05) 0%, transparent 50%),
|
||||
radial-gradient(circle at 75% 75%, rgba(40, 40, 40, 0.05) 0%, transparent 50%);
|
||||
}
|
||||
|
||||
/* Subtle grid pattern */
|
||||
.bg-grid {
|
||||
background-size: 40px 40px;
|
||||
background-size: 50px 50px;
|
||||
background-image:
|
||||
linear-gradient(to right, rgba(55, 65, 81, 0.05) 1px, transparent 1px),
|
||||
linear-gradient(to bottom, 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(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>
|
||||
<script>
|
||||
function toggleSearch() {
|
||||
let searchBar = document.getElementById("search-box");
|
||||
searchBar.classList.toggle("hidden");
|
||||
if (!searchBar.classList.contains("hidden")) {
|
||||
searchBar.focus();
|
||||
let searchContainer = document.getElementById("search-container");
|
||||
searchContainer.classList.toggle("active");
|
||||
if (searchContainer.classList.contains("active")) {
|
||||
document.getElementById("search-box").focus();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -138,7 +70,7 @@
|
|||
}).then(response => {
|
||||
if (response.ok) {
|
||||
const serverBox = document.getElementById("server-box-" + serverName);
|
||||
serverBox.classList.add('opacity-0', 'scale-95');
|
||||
serverBox.classList.add('opacity-0');
|
||||
setTimeout(() => {
|
||||
serverBox.remove();
|
||||
}, 300);
|
||||
|
@ -152,71 +84,79 @@
|
|||
event.preventDefault();
|
||||
toggleSearch();
|
||||
}
|
||||
if (event.key === "Escape") {
|
||||
let searchContainer = document.getElementById("search-container");
|
||||
if (searchContainer.classList.contains("active")) {
|
||||
toggleSearch();
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body class="bg-mesh text-gray-100 min-h-screen">
|
||||
<div
|
||||
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="container mx-auto px-4 py-6 text-center">
|
||||
<h1 class="text-4xl font-bold tracking-tight gradient-text">Caddy Dashboard</h1>
|
||||
<p class="text-cyan-300 text-lg mt-2 opacity-80">Übersicht über alle aktiven Proxy-Server</p>
|
||||
<div class="mt-6 flex justify-center">
|
||||
<button onclick="toggleSearch()" class="bg-gradient-to-r from-neon-blue to-neon-purple text-white px-8 py-3 rounded-full
|
||||
shadow-lg shadow-indigo-500/20 hover:shadow-indigo-500/40 transition-all duration-300
|
||||
flex items-center space-x-2">
|
||||
<i class="fas fa-search"></i>
|
||||
<span>Suche</span>
|
||||
<body class="text-gray-200 min-h-screen bg-grid">
|
||||
<!-- Minimal header -->
|
||||
<header class="py-4 px-6 bg-black/50 backdrop-blur-sm border-b border-gray-800/50">
|
||||
<div class="container mx-auto flex justify-between items-center">
|
||||
<h1 class="text-xl font-medium tracking-tight text-white">Caddy Dashboard</h1>
|
||||
<button onclick="toggleSearch()" class="text-sm px-3 py-1.5 bg-zinc-800 hover:bg-zinc-700 border border-zinc-700 rounded-md
|
||||
transition-colors flex items-center gap-2">
|
||||
<i class="fas fa-search text-xs"></i>
|
||||
<span class="hidden sm:inline">/</span>
|
||||
</button>
|
||||
</div>
|
||||
</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>
|
||||
</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 class="container mx-auto p-4 md:p-6 bg-grid">
|
||||
<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>
|
||||
|
||||
<div class="container mx-auto p-4 md:p-6">
|
||||
{% 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
|
||||
border border-gray-700/30 transition-all duration-300 transform">
|
||||
<div class="flex flex-col md:flex-row justify-between items-start md:items-center">
|
||||
<div id="server-box-{{ server }}" class="mb-8 opacity-100 transition-opacity duration-300">
|
||||
<div class="flex justify-between items-center mb-4 px-1">
|
||||
<div>
|
||||
<h2 class="text-2xl font-bold gradient-text">{{ server }}</h2>
|
||||
<p class="text-sm text-gray-400 flex items-center mt-1">
|
||||
<i class="far fa-clock mr-2"></i> Zuletzt aktualisiert: {{ timestamps[server] }}
|
||||
</p>
|
||||
<h2 class="text-lg font-medium text-white">{{ server }}</h2>
|
||||
<p class="text-xs text-gray-500 mt-0.5">Zuletzt aktualisiert: {{ timestamps[server] }}</p>
|
||||
</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
|
||||
text-white px-6 py-2.5 rounded-full text-sm font-medium transition-all duration-300 transform
|
||||
hover:scale-105 flex items-center">
|
||||
<i class="fas fa-trash-alt mr-2"></i> Löschen
|
||||
<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
|
||||
transition-colors">
|
||||
<i class="fas fa-trash-alt mr-1.5"></i>Löschen
|
||||
</button>
|
||||
</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() %}
|
||||
<div class="domain-card card-gradient bg-obsidian p-5 rounded-xl backdrop-blur-sm shadow-lg
|
||||
border border-gray-800/50 flex flex-col space-y-4 transition-all duration-300 transform hover:translate-y-[-2px]"
|
||||
<div class="domain-card card bg-zinc-900 border border-zinc-800 rounded-lg overflow-hidden"
|
||||
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"
|
||||
class="text-neon-blue hover:text-neon-cyan font-medium text-lg break-words transition-colors duration-300 flex items-center">
|
||||
<i class="fas fa-globe mr-2 opacity-80"></i>
|
||||
<span>{{ domain }}</span>
|
||||
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 text-xs text-gray-500"></i>
|
||||
<span class="truncate">{{ domain }}</span>
|
||||
</a>
|
||||
<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 class="pt-2 border-t border-gray-800/50">
|
||||
<p class="text-gray-300 break-words text-sm font-mono bg-midnight/50 p-2 rounded-lg">
|
||||
<i class="fas fa-network-wired mr-2 text-gray-500"></i>{{ target }}
|
||||
</p>
|
||||
<div class="px-4 py-2.5">
|
||||
<p class="text-gray-400 text-xs font-mono truncate">{{ target }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
@ -225,9 +165,9 @@
|
|||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<footer class="bg-obsidian/60 backdrop-blur-md border-t border-gray-700/30 py-4 mt-12">
|
||||
<div class="container mx-auto px-4 text-center text-gray-500 text-sm">
|
||||
Caddy Dashboard • Powered by Flask & Tailwind CSS
|
||||
<footer class="py-4 mt-12 border-t border-zinc-900">
|
||||
<div class="container mx-auto px-4 text-center text-xs text-gray-600">
|
||||
Caddy Dashboard
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue