Integrate dummy service pages - private services now link to demo login pages

This commit is contained in:
Roberth Rajala 2026-02-02 13:11:15 +01:00
parent 4fd5f593ea
commit 1554402d76
20 changed files with 9246 additions and 124 deletions

18
Dummy Sites/.dockerignore Normal file
View File

@ -0,0 +1,18 @@
# Git
.git
.gitignore
# Documentation
README.md
*.md
# IDE
.vscode
.idea
# OS files
.DS_Store
Thumbs.db
# Other
*.log

35
Dummy Sites/Dockerfile Normal file
View File

@ -0,0 +1,35 @@
# Use nginx alpine as base image for a lightweight container
FROM nginx:alpine
# Remove default nginx website
RUN rm -rf /usr/share/nginx/html/*
# Copy main dashboard
COPY index.html /usr/share/nginx/html/
# Copy all service pages
COPY services/ /usr/share/nginx/html/services/
# Create a custom nginx config
RUN echo 'server { \
listen 80; \
server_name localhost; \
root /usr/share/nginx/html; \
index index.html; \
\
location / { \
try_files $uri $uri/ =404; \
} \
\
# Enable gzip compression \
gzip on; \
gzip_vary on; \
gzip_min_length 1024; \
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; \
}' > /etc/nginx/conf.d/default.conf
# Expose port 80
EXPOSE 80
# Start nginx
CMD ["nginx", "-g", "daemon off;"]

181
Dummy Sites/README.md Normal file
View File

@ -0,0 +1,181 @@
# 🏠 Homelab Showcase Dashboard
A beautiful, realistic dashboard for showcasing your internal homelab services with **dummy login pages** that mimic the real interfaces. Perfect for demonstrations without exposing actual services.
> **Note:** This is a showcase/demo site. All login forms are non-functional and display a "demo page" message when submitted.
## ✨ Features
- 🎨 **Realistic Service Interfaces** - Each service has its own authentic-looking dummy page
- 🏠 **Main Dashboard** - Beautiful dark-themed landing page with all services
- 🔒 **Safe & Secure** - No real authentication, just visual demonstrations
- 📱 **Responsive Design** - Works on desktop, tablet, and mobile
- 🐳 **Docker Ready** - Easy deployment via Docker Compose on Portainer
## 📦 Included Services
### Storage & Cloud
| Service | Description | Dummy Page |
|---------|-------------|------------|
| **Nextcloud** | Self-hosted cloud storage | `services/nextcloud.html` |
### Media & Entertainment
| Service | Description | Dummy Page |
|---------|-------------|------------|
| **Plex** | Media streaming server | `services/plex.html` |
| **Jellyfin** | Open source media server | `services/jellyfin.html` |
### Infrastructure & Management
| Service | Description | Dummy Page |
|---------|-------------|------------|
| **Portainer** | Docker container management | `services/portainer.html` |
| **Proxmox** | Virtualization platform | `services/proxmox.html` |
| **Pi-hole** | Network ad blocker dashboard | `services/pihole.html` |
### Monitoring
| Service | Description | Dummy Page |
|---------|-------------|------------|
| **Netdata** | Real-time system monitoring | `services/netdata.html` |
## 🚀 Deployment
### Option 1: Portainer (Recommended)
1. **Upload files** to your Portainer VM:
```bash
scp -r * user@your-vm:/path/to/homelab-showcase/
```
2. **In Portainer:**
- Go to **Stacks** → **Add Stack**
- Name: `homelab-showcase`
- Copy contents of `docker-compose.yml`
- Deploy
3. **Access:**
- Dashboard: `http://your-vm-ip:8080`
- Or configure Traefik for `showcase.lemonlink.eu`
### Option 2: Docker Compose
```bash
docker-compose up -d
```
### Option 3: Manual Docker
```bash
docker build -t homelab-showcase .
docker run -d -p 8080:80 --name homelab-showcase --restart unless-stopped homelab-showcase
```
## 🗂️ File Structure
```
.
├── index.html # Main dashboard
├── Dockerfile # Container definition
├── docker-compose.yml # Compose configuration
├── README.md # This file
└── services/ # Dummy service pages
├── nextcloud.html # Nextcloud login page
├── portainer.html # Portainer login page
├── netdata.html # Netdata dashboard
├── plex.html # Plex media interface
├── jellyfin.html # Jellyfin login page
├── proxmox.html # Proxmox login page
└── pihole.html # Pi-hole dashboard
```
## 🎨 Service Pages Preview
### Nextcloud Login
- Authentic blue gradient background
- Official Nextcloud logo
- Username/password form
- "Stay logged in" checkbox
### Portainer Login
- Docker-themed dark interface
- OAuth option button
- Version info display
### Netdata Dashboard
- Real-time-looking charts (static)
- CPU, RAM, Disk, Network metrics
- Time range selector
- Node overview panel
### Plex Interface
- Dark media server theme
- Movie/TV show grid
- "Continue Watching" section
- Sidebar navigation
### Jellyfin Login
- Jellyfin blue theme
- Server name display
- Clean centered login box
### Proxmox Login
- Split-screen design
- Feature highlights
- Realm selection
### Pi-hole Dashboard
- Red-themed statistics
- Query graphs
- Top blocked domains list
- Client activity panel
## 🔧 Customization
### Adding More Services
1. Create a new HTML file in `services/` folder
2. Copy the demo banner from existing files:
```html
<div class="demo-banner">
🔒 This is a dummy page for showcase purposes only.
<a href="/">← Back to Dashboard</a>
</div>
```
3. Design the login/interface to match the real service
4. Add the service card to `index.html`
### Changing Domain Names
Edit the URLs in `index.html` service cards to match your actual domains:
```html
<span class="service-url">your-domain.com</span>
```
### Styling
Each service page is self-contained with inline CSS. Edit the `<style>` section in each file to customize.
## 📝 Notes
- All form submissions are intercepted with JavaScript and show an alert
- No data is collected or transmitted
- Charts are static SVG/ CSS representations
- All "login" buttons are non-functional
## 🔒 Security
This showcase is designed to be **completely safe**:
- No backend processing
- No authentication validation
- No data storage
- Static HTML/CSS/JS only
## 🛠️ Requirements
- Docker & Docker Compose (or Portainer)
- ~64MB RAM for the container
- Port 8080 available (configurable)
---
Made with ❤️ for the homelab community

View File

@ -0,0 +1,23 @@
version: '3.8'
services:
homelab-showcase:
build: .
container_name: homelab-showcase
restart: unless-stopped
ports:
- "8080:80"
# Optional: Add labels for Traefik reverse proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.homelab-showcase.rule=Host(`showcase.lemonlink.eu`)"
- "traefik.http.routers.homelab-showcase.entrypoints=websecure"
- "traefik.http.routers.homelab-showcase.tls.certresolver=letsencrypt"
- "traefik.http.services.homelab-showcase.loadbalancer.server.port=80"
# Optional: Resource limits
deploy:
resources:
limits:
memory: 64M
reservations:
memory: 32M

455
Dummy Sites/index.html Normal file
View File

@ -0,0 +1,455 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Homelab Dashboard</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
:root {
--bg-primary: #0f0f1a;
--bg-secondary: #16162a;
--bg-card: #1e1e3a;
--bg-card-hover: #252550;
--text-primary: #ffffff;
--text-secondary: #a0a0b8;
--accent: #6366f1;
--accent-hover: #818cf8;
--border: #2a2a4a;
--shadow: rgba(0, 0, 0, 0.4);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Inter', sans-serif;
background: var(--bg-primary);
color: var(--text-primary);
min-height: 100vh;
line-height: 1.6;
}
.container {
max-width: 1400px;
margin: 0 auto;
padding: 2rem;
}
header {
text-align: center;
padding: 3rem 0;
background: linear-gradient(135deg, var(--bg-secondary) 0%, var(--bg-primary) 100%);
border-bottom: 1px solid var(--border);
}
header h1 {
font-size: 3rem;
font-weight: 700;
background: linear-gradient(135deg, var(--text-primary) 0%, var(--accent) 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin-bottom: 0.5rem;
}
header p {
color: var(--text-secondary);
font-size: 1.1rem;
}
.status-bar {
display: flex;
justify-content: center;
gap: 2rem;
margin-top: 1.5rem;
flex-wrap: wrap;
}
.status-item {
display: flex;
align-items: center;
gap: 0.5rem;
color: var(--text-secondary);
font-size: 0.9rem;
}
.status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: #22c55e;
animation: pulse 2s infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.section {
margin: 3rem 0;
}
.section-title {
font-size: 1.5rem;
font-weight: 600;
margin-bottom: 1.5rem;
display: flex;
align-items: center;
gap: 0.75rem;
}
.section-title svg {
width: 24px;
height: 24px;
color: var(--accent);
}
.services-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 1.5rem;
}
.service-card {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: 16px;
padding: 1.5rem;
transition: all 0.3s ease;
cursor: pointer;
position: relative;
overflow: hidden;
text-decoration: none;
display: block;
}
.service-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 3px;
background: linear-gradient(90deg, var(--accent), var(--accent-hover));
transform: scaleX(0);
transition: transform 0.3s ease;
}
.service-card:hover {
transform: translateY(-4px);
background: var(--bg-card-hover);
box-shadow: 0 20px 40px var(--shadow);
}
.service-card:hover::before {
transform: scaleX(1);
}
.service-header {
display: flex;
align-items: center;
gap: 1rem;
margin-bottom: 1rem;
}
.service-icon {
width: 48px;
height: 48px;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5rem;
}
.service-icon.homarr { background: rgba(99, 102, 241, 0.2); }
.service-icon.netdata { background: rgba(34, 197, 94, 0.2); }
.service-icon.nextcloud { background: rgba(59, 130, 246, 0.2); }
.service-icon.portainer { background: rgba(20, 184, 166, 0.2); }
.service-icon.pihole { background: rgba(249, 115, 22, 0.2); }
.service-icon.plex { background: rgba(168, 85, 247, 0.2); }
.service-icon.jellyfin { background: rgba(99, 179, 237, 0.2); }
.service-icon.proxmox { background: rgba(239, 68, 68, 0.2); }
.service-icon.traefik { background: rgba(6, 182, 212, 0.2); }
.service-icon.homeassistant { background: rgba(251, 191, 36, 0.2); }
.service-icon.nas { background: rgba(107, 114, 128, 0.2); }
.service-icon.vaultwarden { background: rgba(236, 72, 153, 0.2); }
.service-info h3 {
font-size: 1.1rem;
font-weight: 600;
margin-bottom: 0.25rem;
color: var(--text-primary);
}
.service-status {
font-size: 0.75rem;
color: #22c55e;
display: flex;
align-items: center;
gap: 0.25rem;
}
.service-description {
color: var(--text-secondary);
font-size: 0.9rem;
line-height: 1.5;
}
.service-meta {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 1rem;
padding-top: 1rem;
border-top: 1px solid var(--border);
}
.service-url {
font-size: 0.8rem;
color: var(--accent);
font-family: monospace;
}
.arrow-icon {
width: 20px;
height: 20px;
color: var(--text-secondary);
transition: all 0.3s ease;
}
.service-card:hover .arrow-icon {
color: var(--accent);
transform: translateX(4px);
}
footer {
text-align: center;
padding: 3rem 0;
color: var(--text-secondary);
border-top: 1px solid var(--border);
margin-top: 3rem;
}
footer a {
color: var(--accent);
text-decoration: none;
}
footer a:hover {
text-decoration: underline;
}
@media (max-width: 768px) {
header h1 {
font-size: 2rem;
}
.services-grid {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<header>
<div class="container">
<h1>🏠 My Homelab</h1>
<p>Central dashboard for all internal services</p>
<div class="status-bar">
<div class="status-item">
<span class="status-dot"></span>
<span>All Systems Operational</span>
</div>
<div class="status-item">
<span>🖥️ 12 Services</span>
</div>
<div class="status-item">
<span>⏱️ Uptime: 45 days</span>
</div>
</div>
</div>
</header>
<main class="container">
<!-- Dashboards Section -->
<section class="section">
<h2 class="section-title">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="3" y="3" width="7" height="7" rx="1"/>
<rect x="14" y="3" width="7" height="7" rx="1"/>
<rect x="14" y="14" width="7" height="7" rx="1"/>
<rect x="3" y="14" width="7" height="7" rx="1"/>
</svg>
Dashboards & Monitoring
</h2>
<div class="services-grid">
<a href="services/netdata.html" class="service-card">
<div class="service-header">
<div class="service-icon netdata">📊</div>
<div class="service-info">
<h3>Netdata</h3>
<span class="service-status">● Running</span>
</div>
</div>
<p class="service-description">Real-time performance monitoring and health monitoring for systems.</p>
<div class="service-meta">
<span class="service-url">netdata.local:19999</span>
<svg class="arrow-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M5 12h14M12 5l7 7-7 7"/>
</svg>
</div>
</a>
</div>
</section>
<!-- Storage & Cloud Section -->
<section class="section">
<h2 class="section-title">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M18 10h-1.26A8 8 0 1 0 9 20h9a5 5 0 0 0 0-10z"/>
</svg>
Storage & Cloud
</h2>
<div class="services-grid">
<a href="services/nextcloud.html" class="service-card">
<div class="service-header">
<div class="service-icon nextcloud">☁️</div>
<div class="service-info">
<h3>Nextcloud</h3>
<span class="service-status">● Running</span>
</div>
</div>
<p class="service-description">Self-hosted file sync and share solution with office integration.</p>
<div class="service-meta">
<span class="service-url">cloud.lemonlink.eu</span>
<svg class="arrow-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M5 12h14M12 5l7 7-7 7"/>
</svg>
</div>
</a>
</div>
</section>
<!-- Media Section -->
<section class="section">
<h2 class="section-title">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polygon points="5 3 19 12 5 21 5 3"/>
</svg>
Media & Entertainment
</h2>
<div class="services-grid">
<a href="services/plex.html" class="service-card">
<div class="service-header">
<div class="service-icon plex">🎬</div>
<div class="service-info">
<h3>Plex</h3>
<span class="service-status">● Running</span>
</div>
</div>
<p class="service-description">Personal media server for movies, TV shows, music, and photos.</p>
<div class="service-meta">
<span class="service-url">plex.lemonlink.eu</span>
<svg class="arrow-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M5 12h14M12 5l7 7-7 7"/>
</svg>
</div>
</a>
<a href="services/jellyfin.html" class="service-card">
<div class="service-header">
<div class="service-icon jellyfin">🎭</div>
<div class="service-info">
<h3>Jellyfin</h3>
<span class="service-status">● Running</span>
</div>
</div>
<p class="service-description">Free Software Media System for managing and streaming media.</p>
<div class="service-meta">
<span class="service-url">jellyfin.lemonlink.eu</span>
<svg class="arrow-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M5 12h14M12 5l7 7-7 7"/>
</svg>
</div>
</a>
</div>
</section>
<!-- Infrastructure Section -->
<section class="section">
<h2 class="section-title">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"/>
</svg>
Infrastructure & Management
</h2>
<div class="services-grid">
<a href="services/portainer.html" class="service-card">
<div class="service-header">
<div class="service-icon portainer">🐳</div>
<div class="service-info">
<h3>Portainer</h3>
<span class="service-status">● Running</span>
</div>
</div>
<p class="service-description">Container management UI for Docker and Kubernetes environments.</p>
<div class="service-meta">
<span class="service-url">portainer.lemonlink.eu</span>
<svg class="arrow-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M5 12h14M12 5l7 7-7 7"/>
</svg>
</div>
</a>
<a href="services/proxmox.html" class="service-card">
<div class="service-header">
<div class="service-icon proxmox">🖥️</div>
<div class="service-info">
<h3>Proxmox VE</h3>
<span class="service-status">● Running</span>
</div>
</div>
<p class="service-description">Open-source server virtualization management platform.</p>
<div class="service-meta">
<span class="service-url">proxmox.lemonlink.eu:8006</span>
<svg class="arrow-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M5 12h14M12 5l7 7-7 7"/>
</svg>
</div>
</a>
<a href="services/pihole.html" class="service-card">
<div class="service-header">
<div class="service-icon pihole">🕳️</div>
<div class="service-info">
<h3>Pi-hole</h3>
<span class="service-status">● Running</span>
</div>
</div>
<p class="service-description">Network-wide ad blocker that acts as a DNS sinkhole.</p>
<div class="service-meta">
<span class="service-url">pihole.lemonlink.eu/admin</span>
<svg class="arrow-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M5 12h14M12 5l7 7-7 7"/>
</svg>
</div>
</a>
</div>
</section>
</main>
<footer>
<div class="container">
<p>🚀 Homelab Dashboard | Built with ❤️ | <a href="https://lemonlink.eu">lemonlink.eu</a></p>
</div>
</footer>
</body>
</html>

View File

@ -0,0 +1,202 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Jellyfin</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: #101010;
min-height: 100vh;
color: #fff;
}
.demo-banner {
background: #00a4dc;
color: white;
text-align: center;
padding: 10px;
font-size: 14px;
font-weight: 500;
z-index: 1000;
}
.demo-banner a {
color: #fff;
text-decoration: underline;
}
.login-container {
display: flex;
align-items: center;
justify-content: center;
min-height: calc(100vh - 40px);
padding: 40px 20px;
}
.login-box {
background: #1a1a1a;
border-radius: 16px;
padding: 48px;
width: 100%;
max-width: 440px;
text-align: center;
}
.logo {
margin-bottom: 32px;
}
.logo-icon {
width: 80px;
height: 80px;
background: linear-gradient(135deg, #00a4dc 0%, #0078a8 100%);
border-radius: 16px;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 16px;
font-size: 40px;
}
.logo h1 {
font-size: 28px;
font-weight: 600;
color: #00a4dc;
}
.server-name {
color: #888;
font-size: 14px;
margin-bottom: 32px;
}
.input-group {
margin-bottom: 20px;
text-align: left;
}
label {
display: block;
margin-bottom: 8px;
color: #aaa;
font-size: 14px;
}
input[type="text"],
input[type="password"] {
width: 100%;
padding: 14px 16px;
background: #252525;
border: 1px solid #333;
border-radius: 8px;
color: white;
font-size: 15px;
transition: border-color 0.3s;
}
input[type="text"]:focus,
input[type="password"]:focus {
outline: none;
border-color: #00a4dc;
}
.options {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
font-size: 13px;
}
.remember {
display: flex;
align-items: center;
gap: 8px;
color: #888;
}
button {
width: 100%;
padding: 14px;
background: #00a4dc;
color: white;
border: none;
border-radius: 8px;
font-size: 15px;
font-weight: 600;
cursor: pointer;
transition: background 0.3s;
}
button:hover {
background: #0088b8;
}
.manual-login {
margin-top: 20px;
color: #00a4dc;
font-size: 14px;
cursor: pointer;
}
.manual-login:hover {
text-decoration: underline;
}
.version {
margin-top: 30px;
color: #555;
font-size: 12px;
}
</style>
</head>
<body>
<div class="demo-banner">
🔒 This is a dummy login page for showcase purposes only. <a href="/">← Back to Dashboard</a>
</div>
<div class="login-container">
<div class="login-box">
<div class="logo">
<div class="logo-icon">🎬</div>
<h1>Jellyfin</h1>
</div>
<p class="server-name">media.lemonlink.eu</p>
<form onsubmit="event.preventDefault(); alert('🔒 This is a demo page. The real Jellyfin is not exposed.');">
<div class="input-group">
<label for="username">Username</label>
<input type="text" id="username" placeholder="Enter username" autocomplete="off">
</div>
<div class="input-group">
<label for="password">Password</label>
<input type="password" id="password" placeholder="••••••••">
</div>
<div class="options">
<label class="remember">
<input type="checkbox"> Remember me
</label>
</div>
<button type="submit">Sign In</button>
</form>
<p class="manual-login" onclick="alert('Manual server setup not available in demo')">
Change Server
</p>
<p class="version">Jellyfin Server 10.8.13</p>
</div>
</div>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,440 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Log in Nextcloud</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, sans-serif;
background: #0f172a;
min-height: 100vh;
color: #fff;
overflow-x: hidden;
}
/* Demo Banner */
.demo-banner {
background: rgba(0,0,0,0.9);
color: #fff;
text-align: center;
padding: 12px;
font-size: 13px;
position: relative;
z-index: 1000;
border-bottom: 1px solid #333;
}
.demo-banner a {
color: #60a5fa;
text-decoration: none;
}
/* Abstract Background Shapes */
.bg-shapes {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
overflow: hidden;
z-index: 0;
}
.shape {
position: absolute;
border-radius: 50%;
filter: blur(60px);
opacity: 0.6;
}
.shape-1 {
width: 600px;
height: 600px;
background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);
left: -200px;
top: -100px;
}
.shape-2 {
width: 500px;
height: 500px;
background: linear-gradient(135deg, #2563eb 0%, #1e40af 100%);
right: -150px;
bottom: -100px;
}
.shape-3 {
width: 300px;
height: 300px;
background: #60a5fa;
left: 60%;
top: 60%;
opacity: 0.4;
}
.shape-4 {
width: 200px;
height: 200px;
background: #3b82f6;
left: 30%;
top: 20%;
opacity: 0.3;
}
/* Main Container */
.container {
position: relative;
z-index: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
padding: 40px 20px;
}
/* Logo */
.logo {
margin-bottom: 30px;
}
.logo svg {
width: 120px;
height: 120px;
}
/* Login Card */
.login-card {
background: rgba(30, 41, 59, 0.95);
backdrop-filter: blur(20px);
border-radius: 16px;
padding: 32px;
width: 100%;
max-width: 360px;
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
}
.login-title {
font-size: 20px;
font-weight: 600;
text-align: center;
margin-bottom: 24px;
color: #fff;
}
/* Input Groups with Floating Labels */
.input-group {
position: relative;
margin-bottom: 20px;
}
.input-group input {
width: 100%;
height: 52px;
padding: 16px 44px 8px 16px;
background: #0f172a;
border: 1px solid #334155;
border-radius: 8px;
font-size: 15px;
color: #fff;
transition: border-color 0.2s, box-shadow 0.2s;
}
.input-group input:focus {
outline: none;
border-color: #3b82f6;
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2);
}
.input-group label {
position: absolute;
left: 16px;
top: 50%;
transform: translateY(-50%);
font-size: 15px;
color: #94a3b8;
pointer-events: none;
transition: all 0.2s;
background: transparent;
padding: 0 4px;
}
.input-group input:focus + label,
.input-group input:not(:placeholder-shown) + label {
top: 0;
transform: translateY(-50%) scale(0.85);
color: #60a5fa;
background: #0f172a;
}
.input-group input:focus + label {
color: #60a5fa;
}
/* Icons inside inputs */
.input-icon {
position: absolute;
right: 14px;
top: 50%;
transform: translateY(-50%);
color: #64748b;
cursor: pointer;
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
}
.input-icon:hover {
color: #94a3b8;
}
/* Remember Me */
.remember-row {
display: flex;
align-items: center;
margin-bottom: 20px;
}
.remember-checkbox {
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
font-size: 14px;
color: #cbd5e1;
}
.remember-checkbox input[type="checkbox"] {
width: 16px;
height: 16px;
accent-color: #3b82f6;
cursor: pointer;
}
/* Login Button */
.login-btn {
width: 100%;
height: 44px;
background: #3b82f6;
color: #fff;
border: none;
border-radius: 8px;
font-size: 15px;
font-weight: 500;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
transition: background 0.2s;
margin-bottom: 16px;
}
.login-btn:hover {
background: #2563eb;
}
.login-btn svg {
width: 16px;
height: 16px;
}
/* Alternative Login */
.alt-login {
text-align: center;
}
.alt-login-btn {
background: transparent;
border: none;
color: #cbd5e1;
font-size: 14px;
cursor: pointer;
padding: 8px 16px;
transition: color 0.2s;
}
.alt-login-btn:hover {
color: #fff;
text-decoration: underline;
}
/* Forgot Password */
.forgot-password {
text-align: center;
margin-top: 12px;
}
.forgot-password a {
color: #cbd5e1;
font-size: 14px;
text-decoration: none;
}
.forgot-password a:hover {
color: #fff;
text-decoration: underline;
}
/* Footer */
footer {
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
background: rgba(30, 41, 59, 0.9);
padding: 12px 24px;
border-radius: 24px;
font-size: 13px;
color: #94a3b8;
z-index: 10;
}
footer a {
color: #fff;
text-decoration: none;
font-weight: 500;
}
/* Loading State */
.login-btn.loading {
pointer-events: none;
}
.login-btn.loading::after {
content: '';
width: 16px;
height: 16px;
border: 2px solid rgba(255,255,255,0.3);
border-top-color: #fff;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
/* Responsive */
@media (max-width: 480px) {
.login-card {
padding: 24px;
}
.logo svg {
width: 100px;
height: 100px;
}
}
</style>
</head>
<body>
<div class="demo-banner">
🔒 This is a dummy login page for showcase purposes only. <a href="/">← Back to Dashboard</a>
</div>
<div class="bg-shapes">
<div class="shape shape-1"></div>
<div class="shape shape-2"></div>
<div class="shape shape-3"></div>
<div class="shape shape-4"></div>
</div>
<div class="container">
<div class="logo">
<svg viewBox="0 0 100 100" fill="none">
<circle cx="50" cy="50" r="45" fill="white"/>
<circle cx="35" cy="42" r="11" fill="#0f172a"/>
<circle cx="65" cy="42" r="11" fill="#0f172a"/>
<circle cx="50" cy="65" r="11" fill="#0f172a"/>
</svg>
</div>
<div class="login-card">
<h1 class="login-title">Log in to Nextcloud</h1>
<form onsubmit="handleLogin(event)">
<div class="input-group">
<input type="text" id="user" placeholder=" " autocomplete="off" required>
<label for="user">Account name or email</label>
</div>
<div class="input-group">
<input type="password" id="password" placeholder=" " required>
<label for="password">Password</label>
<span class="input-icon" onclick="togglePassword()" title="Show password">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/>
<circle cx="12" cy="12" r="3"/>
</svg>
</span>
</div>
<div class="remember-row">
<label class="remember-checkbox">
<input type="checkbox" checked>
<span>Remember me</span>
</label>
</div>
<button type="submit" class="login-btn" id="loginBtn">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M5 12h14M12 5l7 7-7 7"/>
</svg>
Log in
</button>
</form>
<div class="alt-login">
<button class="alt-login-btn" onclick="alert('Demo page - device login not available')">
Log in with a device
</button>
</div>
<div class="forgot-password">
<a href="#" onclick="alert('Demo page - password reset not available'); return false;">
Forgot password?
</a>
</div>
</div>
</div>
<footer>
<a href="https://nextcloud.com" target="_blank">Nextcloud</a> a safe home for all your data
</footer>
<script>
function handleLogin(e) {
e.preventDefault();
const btn = document.getElementById('loginBtn');
const originalContent = btn.innerHTML;
btn.classList.add('loading');
btn.innerHTML = 'Logging in…';
setTimeout(() => {
btn.classList.remove('loading');
btn.innerHTML = originalContent;
alert('🔒 This is a demo page. The real Nextcloud is not exposed.');
}, 1000);
}
function togglePassword() {
const pw = document.getElementById('password');
pw.type = pw.type === 'password' ? 'text' : 'password';
}
// Focus username on load
document.getElementById('user').focus();
</script>
</body>
</html>

View File

@ -0,0 +1,698 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Pi-hole Dashboard</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--bg-dark: #1a1a1a;
--bg-sidebar: #222;
--bg-card: #2a2a2a;
--border-color: #333;
--text-primary: #e0e0e0;
--text-secondary: #888;
--pihole-red: #96060c;
--pihole-green: #4caf50;
--pihole-blue: #3c8dbc;
--pihole-yellow: #f39c12;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: var(--bg-dark);
color: var(--text-primary);
font-size: 14px;
min-height: 100vh;
}
/* Demo Banner */
.demo-banner {
background: #1a1a1a;
color: var(--text-secondary);
text-align: center;
padding: 8px;
font-size: 12px;
border-bottom: 1px solid var(--border-color);
}
.demo-banner a {
color: var(--pihole-red);
text-decoration: none;
}
/* Layout */
.container {
display: flex;
min-height: calc(100vh - 33px);
}
/* Sidebar */
.sidebar {
width: 200px;
background: var(--bg-sidebar);
border-right: 1px solid var(--border-color);
}
.sidebar-header {
background: linear-gradient(180deg, #3c8dbc 0%, #357ca5 100%);
padding: 16px;
text-align: center;
}
.sidebar-header h1 {
font-size: 20px;
font-weight: 600;
color: white;
}
.status-box {
padding: 16px;
display: flex;
align-items: center;
gap: 12px;
}
.pihole-logo {
width: 50px;
height: 50px;
background: var(--pihole-red);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 28px;
position: relative;
}
.pihole-logo::before {
content: '';
position: absolute;
top: -5px;
left: 50%;
transform: translateX(-50%);
width: 20px;
height: 15px;
background: var(--pihole-green);
border-radius: 10px 10px 0 0;
}
.status-info {
flex: 1;
}
.status-title {
font-size: 12px;
color: var(--text-secondary);
margin-bottom: 4px;
}
.status-item {
display: flex;
align-items: center;
gap: 6px;
font-size: 11px;
color: var(--text-secondary);
margin-bottom: 2px;
}
.status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
}
.status-dot.active {
background: var(--pihole-green);
}
.nav-menu {
padding: 8px 0;
}
.nav-item {
display: flex;
align-items: center;
gap: 12px;
padding: 10px 16px;
color: var(--text-secondary);
cursor: pointer;
transition: all 0.2s;
font-size: 13px;
}
.nav-item:hover {
background: #2a2a2a;
color: var(--text-primary);
}
.nav-item.active {
background: #2a2a2a;
color: white;
border-left: 3px solid var(--pihole-red);
}
.nav-icon {
width: 18px;
text-align: center;
}
.nav-arrow {
margin-left: auto;
font-size: 10px;
}
/* Main Content */
.content {
flex: 1;
padding: 20px;
overflow-y: auto;
}
.top-bar {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.hostname {
color: var(--text-secondary);
font-size: 13px;
}
/* Stats Cards */
.stats-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 16px;
margin-bottom: 24px;
}
.stat-card {
background: var(--bg-card);
border-radius: 8px;
padding: 20px;
position: relative;
overflow: hidden;
min-height: 120px;
}
.stat-card.blue {
background: linear-gradient(135deg, #3c8dbc 0%, #2c6da0 100%);
}
.stat-card.red {
background: linear-gradient(135deg, #c0392b 0%, #962d22 100%);
}
.stat-card.yellow {
background: linear-gradient(135deg, #f39c12 0%, #c27a0e 100%);
}
.stat-card.green {
background: linear-gradient(135deg, #27ae60 0%, #1e8449 100%);
}
.stat-bg-icon {
position: absolute;
right: -10px;
bottom: -10px;
font-size: 80px;
opacity: 0.2;
}
.stat-label {
font-size: 13px;
color: rgba(255,255,255,0.9);
margin-bottom: 8px;
position: relative;
z-index: 1;
}
.stat-value {
font-size: 36px;
font-weight: 600;
color: white;
position: relative;
z-index: 1;
}
.stat-link {
position: absolute;
bottom: 12px;
left: 20px;
right: 20px;
font-size: 12px;
color: rgba(255,255,255,0.8);
display: flex;
align-items: center;
gap: 6px;
cursor: pointer;
z-index: 1;
}
.stat-link:hover {
color: white;
}
/* Charts */
.chart-card {
background: var(--bg-card);
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
}
.chart-title {
font-size: 15px;
margin-bottom: 16px;
color: var(--text-primary);
}
.chart-area {
height: 200px;
position: relative;
}
/* Bar Chart */
.bar-chart {
display: flex;
align-items: flex-end;
justify-content: space-between;
height: 160px;
padding: 0 4px;
position: relative;
}
.bar-chart::before {
content: '';
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
background:
linear-gradient(to bottom, transparent 19px, #333 20px),
linear-gradient(to right, transparent 19px, transparent 20px);
background-size: 100% 40px, 40px 100%;
opacity: 0.3;
}
.bar-group {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
gap: 2px;
height: 100%;
justify-content: flex-end;
position: relative;
}
.bar {
width: 6px;
border-radius: 2px 2px 0 0;
}
.bar.permitted {
background: #666;
}
.bar.blocked {
background: var(--pihole-green);
}
.bar.client {
width: 4px;
}
.chart-axis {
display: flex;
justify-content: space-between;
padding: 8px 4px 0;
font-size: 11px;
color: var(--text-secondary);
}
.chart-axis-y {
position: absolute;
left: 0;
top: 0;
bottom: 24px;
display: flex;
flex-direction: column;
justify-content: space-between;
font-size: 11px;
color: var(--text-secondary);
}
/* Pie Charts Grid */
.pie-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20px;
}
.pie-chart-container {
display: flex;
gap: 20px;
}
.pie-chart {
width: 150px;
height: 150px;
position: relative;
}
.pie-chart svg {
width: 100%;
height: 100%;
transform: rotate(-90deg);
}
.pie-legend {
flex: 1;
display: flex;
flex-direction: column;
gap: 8px;
justify-content: center;
}
.legend-item {
display: flex;
align-items: center;
gap: 8px;
font-size: 12px;
color: var(--text-secondary);
}
.legend-color {
width: 12px;
height: 12px;
border-radius: 2px;
}
.legend-checkbox {
width: 14px;
height: 14px;
accent-color: var(--pihole-blue);
}
</style>
</head>
<body>
<div class="demo-banner">
🔒 This is a dummy dashboard for showcase purposes only. <a href="/">← Back to Dashboard</a>
</div>
<div class="container">
<!-- Sidebar -->
<aside class="sidebar">
<div class="sidebar-header">
<h1>Pi-hole</h1>
</div>
<div class="status-box">
<div class="pihole-logo">🕳️</div>
<div class="status-info">
<div class="status-title">Status</div>
<div class="status-item">
<span class="status-dot active"></span>
<span>Active</span>
</div>
<div class="status-item">
<span class="status-dot active"></span>
<span>Load: 0.12 0.15 0.18</span>
</div>
<div class="status-item">
<span class="status-dot active"></span>
<span>Memory usage: 12.4%</span>
</div>
<div class="status-item">
<span>🌡️ Temp: 42.0 °C</span>
</div>
</div>
</div>
<nav class="nav-menu">
<div class="nav-item active">
<span class="nav-icon">🏠</span>
<span>Dashboard</span>
</div>
<div class="nav-item">
<span class="nav-icon">📋</span>
<span>Query Log</span>
</div>
<div class="nav-item">
<span class="nav-icon">📊</span>
<span>Long-term Data</span>
<span class="nav-arrow"></span>
</div>
<div class="nav-item">
<span class="nav-icon">👥</span>
<span>Groups</span>
</div>
<div class="nav-item">
<span class="nav-icon">🖥️</span>
<span>Clients</span>
</div>
<div class="nav-item">
<span class="nav-icon">🌐</span>
<span>Domains</span>
</div>
<div class="nav-item">
<span class="nav-icon">🛡️</span>
<span>Adlists</span>
</div>
<div class="nav-item">
<span class="nav-icon">⏸️</span>
<span>Disable Blocking</span>
<span class="nav-arrow"></span>
</div>
<div class="nav-item">
<span class="nav-icon">📝</span>
<span>Local DNS</span>
<span class="nav-arrow"></span>
</div>
<div class="nav-item">
<span class="nav-icon">🔧</span>
<span>Tools</span>
<span class="nav-arrow"></span>
</div>
<div class="nav-item">
<span class="nav-icon">⚙️</span>
<span>Settings</span>
</div>
<div class="nav-item">
<span class="nav-icon">❤️</span>
<span>Donate</span>
</div>
</nav>
</aside>
<!-- Main Content -->
<main class="content">
<div class="top-bar">
<button style="background: transparent; border: none; color: var(--text-secondary); font-size: 18px; cursor: pointer;"></button>
<span class="hostname">hostname: pihole</span>
<button style="background: transparent; border: none; color: var(--text-secondary); font-size: 18px; cursor: pointer;"></button>
</div>
<!-- Stats Cards -->
<div class="stats-grid">
<div class="stat-card blue">
<div class="stat-bg-icon">🌍</div>
<div class="stat-label">Total queries</div>
<div class="stat-value">73,770</div>
<div class="stat-link">
<span>23 active clients</span>
<span></span>
</div>
</div>
<div class="stat-card red">
<div class="stat-bg-icon">🛡️</div>
<div class="stat-label">Queries Blocked</div>
<div class="stat-value">17,304</div>
<div class="stat-link">
<span>List blocked queries</span>
<span></span>
</div>
</div>
<div class="stat-card yellow">
<div class="stat-bg-icon">📊</div>
<div class="stat-label">Percentage Blocked</div>
<div class="stat-value">23.5%</div>
<div class="stat-link">
<span>List all queries</span>
<span></span>
</div>
</div>
<div class="stat-card green">
<div class="stat-bg-icon">📋</div>
<div class="stat-label">Domains on Adlists</div>
<div class="stat-value">254,501</div>
<div class="stat-link">
<span>Manage adlists</span>
<span></span>
</div>
</div>
</div>
<!-- Total Queries Chart -->
<div class="chart-card">
<div class="chart-title">Total queries over last 24 hours</div>
<div class="chart-area">
<div class="chart-axis-y">
<span>1,200</span>
<span>1,000</span>
<span>800</span>
<span>600</span>
<span>400</span>
<span>200</span>
<span>0</span>
</div>
<div class="bar-chart" style="margin-left: 40px;">
<script>
// Generate bars
const hours = ['13:00','14:00','15:00','16:00','17:00','18:00','19:00','20:00','21:00','22:00','23:00','00:00','01:00','02:00','03:00','04:00','05:00','06:00','07:00','08:00','09:00','10:00','11:00','12:00'];
hours.forEach((hour, i) => {
const h1 = 30 + Math.random() * 50;
const h2 = 50 + Math.random() * 70;
document.write(`
<div class="bar-group">
<div class="bar permitted" style="height: ${h1}%;"></div>
<div class="bar blocked" style="height: ${h2}%;"></div>
</div>
`);
});
</script>
</div>
</div>
<div class="chart-axis">
<script>
hours.forEach(hour => document.write(`<span>${hour}</span>`));
</script>
</div>
</div>
<!-- Client Activity Chart -->
<div class="chart-card">
<div class="chart-title">Client activity over last 24 hours</div>
<div class="chart-area">
<div class="chart-axis-y">
<span>1,200</span>
<span>1,000</span>
<span>800</span>
<span>600</span>
<span>400</span>
<span>200</span>
<span>0</span>
</div>
<div class="bar-chart" style="margin-left: 40px;">
<script>
hours.forEach((hour, i) => {
const colors = ['#9c27b0', '#e91e63', '#00bcd4', '#ff9800', '#4caf50'];
document.write(`<div class="bar-group">`);
colors.forEach(color => {
const h = 20 + Math.random() * 60;
document.write(`<div class="bar client" style="height: ${h}%; background: ${color}; width: 2px;"></div>`);
});
document.write(`</div>`);
});
</script>
</div>
</div>
<div class="chart-axis">
<script>
hours.forEach(hour => document.write(`<span>${hour}</span>`));
</script>
</div>
</div>
<!-- Pie Charts -->
<div class="pie-grid">
<div class="chart-card">
<div class="chart-title">Query Types</div>
<div class="pie-chart-container">
<div class="pie-chart">
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="40" fill="none" stroke="#ff7043" stroke-width="20" stroke-dasharray="100 251"/>
<circle cx="50" cy="50" r="40" fill="none" stroke="#42a5f5" stroke-width="20" stroke-dasharray="60 251" stroke-dashoffset="-100"/>
<circle cx="50" cy="50" r="40" fill="none" stroke="#66bb6a" stroke-width="20" stroke-dasharray="40 251" stroke-dashoffset="-160"/>
<circle cx="50" cy="50" r="40" fill="none" stroke="#ab47bc" stroke-width="20" stroke-dasharray="30 251" stroke-dashoffset="-200"/>
<circle cx="50" cy="50" r="40" fill="none" stroke="#26c6da" stroke-width="20" stroke-dasharray="21 251" stroke-dashoffset="-230"/>
</svg>
</div>
<div class="pie-legend">
<label class="legend-item">
<input type="checkbox" class="legend-checkbox" checked>
<div class="legend-color" style="background: #ff7043;"></div>
<span>A (IPv4)</span>
</label>
<label class="legend-item">
<input type="checkbox" class="legend-checkbox" checked>
<div class="legend-color" style="background: #42a5f5;"></div>
<span>AAAA (IPv6)</span>
</label>
<label class="legend-item">
<input type="checkbox" class="legend-checkbox" checked>
<div class="legend-color" style="background: #66bb6a;"></div>
<span>SRV</span>
</label>
<label class="legend-item">
<input type="checkbox" class="legend-checkbox" checked>
<div class="legend-color" style="background: #ab47bc;"></div>
<span>SOA</span>
</label>
<label class="legend-item">
<input type="checkbox" class="legend-checkbox" checked>
<div class="legend-color" style="background: #26c6da;"></div>
<span>PTR</span>
</label>
</div>
</div>
</div>
<div class="chart-card">
<div class="chart-title">Upstream servers</div>
<div class="pie-chart-container">
<div class="pie-chart">
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="40" fill="none" stroke="#ff7043" stroke-width="20" stroke-dasharray="120 251"/>
<circle cx="50" cy="50" r="40" fill="none" stroke="#42a5f5" stroke-width="20" stroke-dasharray="80 251" stroke-dashoffset="-120"/>
<circle cx="50" cy="50" r="40" fill="none" stroke="#66bb6a" stroke-width="20" stroke-dasharray="51 251" stroke-dashoffset="-200"/>
</svg>
</div>
<div class="pie-legend">
<label class="legend-item">
<input type="checkbox" class="legend-checkbox" checked>
<div class="legend-color" style="background: #ff7043;"></div>
<span>blocked</span>
</label>
<label class="legend-item">
<input type="checkbox" class="legend-checkbox" checked>
<div class="legend-color" style="background: #42a5f5;"></div>
<span>cached</span>
</label>
<label class="legend-item">
<input type="checkbox" class="legend-checkbox" checked>
<div class="legend-color" style="background: #66bb6a;"></div>
<span>localhost#5335</span>
</label>
</div>
</div>
</div>
</div>
</main>
</div>
</body>
</html>

View File

@ -0,0 +1,431 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Plex</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: #1f1f1f;
min-height: 100vh;
color: #eee;
}
.demo-banner {
background: #e5a00d;
color: #000;
text-align: center;
padding: 10px;
font-size: 14px;
font-weight: 500;
z-index: 1000;
}
.demo-banner a {
color: #1f1f1f;
text-decoration: underline;
}
.sidebar {
position: fixed;
left: 0;
top: 40px;
bottom: 0;
width: 240px;
background: #1f1f1f;
border-right: 1px solid #333;
padding: 20px 0;
}
.logo {
padding: 0 20px 30px;
display: flex;
align-items: center;
gap: 10px;
}
.logo-icon {
width: 40px;
height: 40px;
background: #e5a00d;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
font-size: 22px;
}
.logo-text {
font-size: 24px;
font-weight: 700;
color: #e5a00d;
}
.nav-section {
margin-bottom: 24px;
}
.nav-title {
padding: 0 20px 8px;
font-size: 11px;
text-transform: uppercase;
color: #888;
letter-spacing: 1px;
}
.nav-item {
padding: 10px 20px;
display: flex;
align-items: center;
gap: 12px;
cursor: pointer;
transition: background 0.2s;
font-size: 14px;
}
.nav-item:hover, .nav-item.active {
background: rgba(255,255,255,0.1);
}
.nav-item.active {
border-left: 3px solid #e5a00d;
padding-left: 17px;
}
.nav-icon {
width: 20px;
text-align: center;
}
.main-content {
margin-left: 240px;
padding: 60px 30px 30px;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
}
.search-box {
background: rgba(255,255,255,0.1);
border: none;
border-radius: 4px;
padding: 10px 16px;
width: 300px;
color: #eee;
font-size: 14px;
}
.search-box::placeholder {
color: #888;
}
.user-menu {
display: flex;
align-items: center;
gap: 16px;
}
.user-avatar {
width: 36px;
height: 36px;
background: #e5a00d;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
color: #1f1f1f;
}
.section {
margin-bottom: 40px;
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
.section-title {
font-size: 20px;
font-weight: 600;
}
.see-all {
color: #e5a00d;
font-size: 14px;
cursor: pointer;
}
.movies-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
gap: 16px;
}
.movie-card {
cursor: pointer;
transition: transform 0.2s;
}
.movie-card:hover {
transform: scale(1.05);
}
.movie-poster {
aspect-ratio: 2/3;
border-radius: 8px;
background: linear-gradient(135deg, #333 0%, #555 100%);
display: flex;
align-items: center;
justify-content: center;
font-size: 48px;
margin-bottom: 8px;
position: relative;
overflow: hidden;
}
.movie-poster::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(180deg, transparent 50%, rgba(0,0,0,0.8) 100%);
}
.movie-title {
font-size: 14px;
font-weight: 500;
margin-bottom: 4px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.movie-meta {
font-size: 12px;
color: #888;
}
.hero-banner {
background: linear-gradient(135deg, #2a2a2a 0%, #1a1a1a 100%);
border-radius: 12px;
padding: 40px;
margin-bottom: 40px;
display: flex;
gap: 30px;
align-items: center;
}
.hero-poster {
width: 200px;
height: 300px;
background: linear-gradient(135deg, #444 0%, #666 100%);
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
font-size: 80px;
flex-shrink: 0;
}
.hero-info h2 {
font-size: 32px;
margin-bottom: 8px;
}
.hero-meta {
color: #aaa;
font-size: 14px;
margin-bottom: 16px;
}
.hero-desc {
color: #ccc;
line-height: 1.6;
margin-bottom: 20px;
max-width: 600px;
}
.hero-buttons {
display: flex;
gap: 12px;
}
.btn {
padding: 12px 24px;
border-radius: 4px;
font-size: 14px;
font-weight: 600;
cursor: pointer;
border: none;
display: flex;
align-items: center;
gap: 8px;
}
.btn-primary {
background: #e5a00d;
color: #1f1f1f;
}
.btn-secondary {
background: rgba(255,255,255,0.2);
color: #eee;
}
</style>
</head>
<body>
<div class="demo-banner">
🔒 This is a dummy media server for showcase purposes only. <a href="/">← Back to Dashboard</a>
</div>
<aside class="sidebar">
<div class="logo">
<div class="logo-icon">▶️</div>
<span class="logo-text">Plex</span>
</div>
<div class="nav-section">
<div class="nav-title">Discover</div>
<div class="nav-item active">
<span class="nav-icon">🏠</span>
<span>Home</span>
</div>
<div class="nav-item">
<span class="nav-icon">📺</span>
<span>Movies</span>
</div>
<div class="nav-item">
<span class="nav-icon">🎬</span>
<span>TV Shows</span>
</div>
<div class="nav-item">
<span class="nav-icon">🎵</span>
<span>Music</span>
</div>
</div>
<div class="nav-section">
<div class="nav-title">Library</div>
<div class="nav-item">
<span class="nav-icon">📁</span>
<span>All Media</span>
</div>
<div class="nav-item">
<span class="nav-icon">📊</span>
<span>Dashboard</span>
</div>
</div>
</aside>
<main class="main-content">
<div class="header">
<input type="text" class="search-box" placeholder="Search for movies, shows, music...">
<div class="user-menu">
<span>🔔</span>
<span>⚙️</span>
<div class="user-avatar">A</div>
</div>
</div>
<div class="hero-banner">
<div class="hero-poster">🎭</div>
<div class="hero-info">
<h2>Continue Watching</h2>
<div class="hero-meta">2023 • 2h 15m • Action, Sci-Fi</div>
<p class="hero-desc">
Pick up where you left off. Your personal media server is ready with all your favorite movies and TV shows.
</p>
<div class="hero-buttons">
<button class="btn btn-primary">▶ Play</button>
<button class="btn btn-secondary">+ My List</button>
</div>
</div>
</div>
<div class="section">
<div class="section-header">
<h3 class="section-title">Recently Added Movies</h3>
<span class="see-all">See all →</span>
</div>
<div class="movies-grid">
<div class="movie-card">
<div class="movie-poster">🎬</div>
<div class="movie-title">The Matrix Resurrections</div>
<div class="movie-meta">2021 • 148 min</div>
</div>
<div class="movie-card">
<div class="movie-poster">🚀</div>
<div class="movie-title">Interstellar</div>
<div class="movie-meta">2014 • 169 min</div>
</div>
<div class="movie-card">
<div class="movie-poster">🦸</div>
<div class="movie-title">The Dark Knight</div>
<div class="movie-meta">2008 • 152 min</div>
</div>
<div class="movie-card">
<div class="movie-poster">🤖</div>
<div class="movie-title">Blade Runner 2049</div>
<div class="movie-meta">2017 • 164 min</div>
</div>
<div class="movie-card">
<div class="movie-poster">🌌</div>
<div class="movie-title">Dune</div>
<div class="movie-meta">2021 • 155 min</div>
</div>
<div class="movie-card">
<div class="movie-poster">⚔️</div>
<div class="movie-title">Inception</div>
<div class="movie-meta">2010 • 148 min</div>
</div>
</div>
</div>
<div class="section">
<div class="section-header">
<h3 class="section-title">Continue Watching</h3>
<span class="see-all">See all →</span>
</div>
<div class="movies-grid">
<div class="movie-card">
<div class="movie-poster">📺</div>
<div class="movie-title">Breaking Bad S05E08</div>
<div class="movie-meta">45 min remaining</div>
</div>
<div class="movie-card">
<div class="movie-poster">🐉</div>
<div class="movie-title">House of Dragon S01E03</div>
<div class="movie-meta">32 min remaining</div>
</div>
<div class="movie-card">
<div class="movie-poster">⚔️</div>
<div class="movie-title">The Witcher S02E05</div>
<div class="movie-meta">28 min remaining</div>
</div>
<div class="movie-card">
<div class="movie-poster">🕵️</div>
<div class="movie-title">Sherlock S04E01</div>
<div class="movie-meta">52 min remaining</div>
</div>
</div>
</div>
</main>
</body>
</html>

View File

@ -0,0 +1,796 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Home - Portainer</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--bg-dark: #0f0f0f;
--bg-sidebar: #1a1a1a;
--bg-card: #1e1e1e;
--bg-hover: #252525;
--border-color: #2a2a2a;
--text-primary: #e0e0e0;
--text-secondary: #888;
--portainer-blue: #13bef9;
--portainer-teal: #00bfa5;
--success: #4caf50;
--danger: #f44336;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: var(--bg-dark);
color: var(--text-primary);
font-size: 13px;
height: 100vh;
overflow: hidden;
}
/* Demo Banner */
.demo-banner {
background: #141414;
color: var(--text-secondary);
text-align: center;
padding: 8px;
font-size: 12px;
border-bottom: 1px solid var(--border-color);
}
.demo-banner a {
color: var(--portainer-blue);
text-decoration: none;
}
/* Main Layout */
.container {
display: flex;
height: calc(100vh - 33px);
}
/* Left Sidebar */
.sidebar {
width: 240px;
background: var(--bg-sidebar);
border-right: 1px solid var(--border-color);
display: flex;
flex-direction: column;
}
.sidebar-header {
padding: 16px;
border-bottom: 1px solid var(--border-color);
}
.logo {
display: flex;
align-items: center;
gap: 8px;
}
.logo-icon {
font-size: 20px;
font-weight: 800;
color: white;
letter-spacing: -1px;
}
.logo-icon span {
color: var(--portainer-teal);
}
.logo-badge {
font-size: 9px;
color: var(--portainer-teal);
text-transform: uppercase;
letter-spacing: 1px;
margin-top: 2px;
}
.sidebar-nav {
flex: 1;
overflow-y: auto;
padding: 8px 0;
}
.nav-section {
margin-bottom: 8px;
}
.nav-header {
padding: 8px 16px;
color: var(--text-secondary);
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.nav-item {
display: flex;
align-items: center;
gap: 12px;
padding: 10px 16px;
color: var(--text-secondary);
cursor: pointer;
transition: all 0.2s;
}
.nav-item:hover {
background: var(--bg-hover);
color: var(--text-primary);
}
.nav-item.active {
background: var(--bg-hover);
color: var(--portainer-blue);
border-left: 3px solid var(--portainer-blue);
padding-left: 13px;
}
.nav-icon {
width: 18px;
text-align: center;
font-size: 14px;
}
.nav-arrow {
margin-left: auto;
font-size: 10px;
}
.sidebar-footer {
padding: 12px 16px;
border-top: 1px solid var(--border-color);
color: var(--text-secondary);
font-size: 11px;
}
/* Main Content */
.content {
flex: 1;
overflow-y: auto;
padding: 24px;
}
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
}
.page-title {
font-size: 24px;
font-weight: 500;
display: flex;
align-items: center;
gap: 8px;
}
.page-actions {
display: flex;
gap: 12px;
}
.icon-btn {
width: 36px;
height: 36px;
background: transparent;
border: 1px solid var(--border-color);
border-radius: 4px;
color: var(--text-secondary);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
.icon-btn:hover {
background: var(--bg-hover);
color: var(--text-primary);
}
.user-menu {
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
}
.user-avatar {
width: 32px;
height: 32px;
background: var(--portainer-blue);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
}
/* Environment Card */
.env-card {
background: var(--bg-card);
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 20px;
margin-bottom: 16px;
}
.env-header {
display: flex;
align-items: flex-start;
gap: 16px;
margin-bottom: 16px;
}
.env-icon {
width: 48px;
height: 48px;
background: #1da1f2;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
font-size: 28px;
}
.env-info {
flex: 1;
}
.env-name-row {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 6px;
}
.env-name {
font-size: 16px;
font-weight: 600;
}
.env-status {
display: flex;
align-items: center;
gap: 6px;
padding: 4px 10px;
background: rgba(76, 175, 80, 0.15);
color: var(--success);
border-radius: 4px;
font-size: 12px;
font-weight: 500;
}
.env-status::before {
content: '';
width: 8px;
height: 8px;
background: var(--success);
border-radius: 50%;
}
.env-meta {
display: flex;
align-items: center;
gap: 16px;
color: var(--text-secondary);
font-size: 12px;
flex-wrap: wrap;
}
.env-meta-item {
display: flex;
align-items: center;
gap: 6px;
}
.env-meta-item span {
color: var(--text-primary);
}
.env-path {
color: var(--text-secondary);
font-family: monospace;
font-size: 12px;
}
/* Stats Row */
.env-stats {
display: flex;
align-items: center;
gap: 24px;
padding-top: 16px;
border-top: 1px solid var(--border-color);
flex-wrap: wrap;
}
.stat-item {
display: flex;
align-items: center;
gap: 8px;
}
.stat-icon {
color: var(--text-secondary);
font-size: 14px;
}
.stat-value {
font-weight: 500;
}
.stat-label {
color: var(--text-secondary);
}
.container-status {
display: flex;
align-items: center;
gap: 8px;
}
.status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
}
.status-dot.running {
background: var(--success);
}
.status-dot.stopped {
background: var(--danger);
}
/* Environment Actions */
.env-actions {
display: flex;
align-items: center;
gap: 12px;
margin-left: auto;
}
.btn {
height: 36px;
padding: 0 16px;
border-radius: 4px;
font-size: 13px;
cursor: pointer;
display: flex;
align-items: center;
gap: 8px;
transition: all 0.2s;
}
.btn-primary {
background: white;
color: #000;
border: none;
}
.btn-primary:hover {
background: #e0e0e0;
}
.btn-icon {
width: 36px;
height: 36px;
background: transparent;
border: 1px solid var(--border-color);
border-radius: 4px;
color: var(--text-secondary);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
.btn-icon:hover {
background: var(--bg-hover);
color: var(--text-primary);
}
.connection-status {
display: flex;
align-items: center;
gap: 6px;
color: var(--text-secondary);
font-size: 12px;
}
.connection-status::before {
content: '✕';
color: var(--text-secondary);
}
/* Filters Bar */
.filters-bar {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 16px;
flex-wrap: wrap;
}
.filter-dropdown {
height: 36px;
padding: 0 12px;
background: var(--bg-card);
border: 1px solid var(--border-color);
border-radius: 4px;
color: var(--text-primary);
font-size: 13px;
cursor: pointer;
min-width: 140px;
}
.search-box {
flex: 1;
max-width: 400px;
height: 36px;
padding: 0 16px;
background: var(--bg-card);
border: 1px solid var(--border-color);
border-radius: 4px;
color: var(--text-primary);
font-size: 13px;
}
.search-box::placeholder {
color: var(--text-secondary);
}
.btn-refresh {
height: 36px;
padding: 0 16px;
background: var(--bg-card);
border: 1px solid var(--border-color);
border-radius: 4px;
color: var(--text-primary);
font-size: 13px;
cursor: pointer;
display: flex;
align-items: center;
gap: 8px;
}
.btn-refresh:hover {
background: var(--bg-hover);
}
/* Card Header */
.card-header {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 20px;
}
.card-title {
font-size: 16px;
font-weight: 600;
display: flex;
align-items: center;
gap: 8px;
}
.card-subtitle {
color: var(--text-secondary);
font-size: 12px;
}
/* Scrollbar */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: var(--bg-dark);
}
::-webkit-scrollbar-thumb {
background: #333;
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: #444;
}
</style>
</head>
<body>
<div class="demo-banner">
🔒 This is a dummy dashboard for showcase purposes only. <a href="/">← Back to Dashboard</a>
</div>
<div class="container">
<!-- Left Sidebar -->
<aside class="sidebar">
<div class="sidebar-header">
<div class="logo">
<div>
<div class="logo-icon">PORTAINER<span>.</span>IO</div>
<div class="logo-badge">Community Edition</div>
</div>
</div>
</div>
<nav class="sidebar-nav">
<div class="nav-section">
<div class="nav-item active">
<span class="nav-icon">🏠</span>
<span>Home</span>
</div>
</div>
<div class="nav-section">
<div class="nav-header">Environment: / None selected</div>
</div>
<div class="nav-section">
<div class="nav-header">Administration</div>
<div class="nav-item">
<span class="nav-icon">👤</span>
<span>User-related</span>
<span class="nav-arrow"></span>
</div>
<div class="nav-item">
<span class="nav-icon">🖥️</span>
<span>Environment-related</span>
<span class="nav-arrow"></span>
</div>
<div class="nav-item">
<span class="nav-icon">📦</span>
<span>Registries</span>
</div>
<div class="nav-item">
<span class="nav-icon">📋</span>
<span>Logs</span>
<span class="nav-arrow"></span>
</div>
<div class="nav-item">
<span class="nav-icon">🔔</span>
<span>Notifications</span>
</div>
<div class="nav-item">
<span class="nav-icon">⚙️</span>
<span>Settings</span>
<span class="nav-arrow"></span>
</div>
</div>
</nav>
<div class="sidebar-footer">
Community Edition 2.21.3 LTS
</div>
</aside>
<!-- Main Content -->
<main class="content">
<div class="page-header">
<h1 class="page-title">
Home
<span style="color: var(--portainer-blue); cursor: pointer;"></span>
</h1>
<div class="page-actions">
<button class="icon-btn">🔔</button>
<button class="icon-btn"></button>
<div class="user-menu">
<div class="user-avatar">A</div>
<span>admin</span>
<span></span>
</div>
</div>
</div>
<!-- Environments Section -->
<div class="env-card">
<div class="card-header">
<div class="card-title">
<span style="font-size: 20px;">🖥️</span>
Environments
</div>
<div style="margin-left: auto; display: flex; gap: 8px;">
<button class="btn-refresh">↻ Refresh</button>
<button class="btn-refresh">⬇ Kubectl</button>
</div>
</div>
<div class="card-subtitle" style="margin-bottom: 16px;">
Click on an environment to manage
</div>
<!-- Filters -->
<div class="filters-bar">
<select class="filter-dropdown">
<option>Platform</option>
</select>
<select class="filter-dropdown">
<option>Connection Type</option>
</select>
<select class="filter-dropdown">
<option>Status</option>
</select>
<select class="filter-dropdown">
<option>Tags</option>
</select>
<select class="filter-dropdown">
<option>Groups</option>
</select>
<select class="filter-dropdown">
<option>Agent Version</option>
</select>
<button style="background: transparent; border: none; color: var(--text-secondary); cursor: pointer;">
Clear all
</button>
<div style="margin-left: auto; display: flex; gap: 8px;">
<select class="filter-dropdown" style="min-width: 100px;">
<option>Sort By</option>
</select>
<button class="btn-icon"></button>
</div>
</div>
<!-- Environment 1 -->
<div class="env-card" style="margin-bottom: 12px;">
<div style="display: flex; justify-content: space-between; align-items: flex-start;">
<div class="env-header" style="flex: 1; margin-bottom: 0;">
<div class="env-icon">🐳</div>
<div class="env-info">
<div class="env-name-row">
<span class="env-name">local</span>
<span class="env-status">Up</span>
<span style="color: var(--text-secondary); font-size: 12px;">↻ 2026-02-02 12:42:53</span>
<span style="color: var(--text-secondary); font-size: 12px;">Standalone 2.21.3</span>
<span class="env-path">/var/run/docker.sock</span>
</div>
<div class="env-meta">
<span class="env-meta-item">Group: <span>Unassigned</span></span>
<span class="env-meta-item">🏷️ No tags</span>
<span class="env-meta-item">⚡ Local</span>
</div>
</div>
</div>
<div class="env-actions">
<button class="btn btn-primary">📡 Live connect</button>
<button class="btn-icon">✏️</button>
<div style="text-align: right;">
<div class="connection-status">Disconnected</div>
<button class="btn-icon" style="margin-top: 4px;">⚙️</button>
</div>
</div>
</div>
<div class="env-stats">
<div class="stat-item">
<span class="stat-icon">📚</span>
<span class="stat-value">12</span>
<span class="stat-label">stacks</span>
</div>
<div class="stat-item">
<span class="stat-icon">📦</span>
<span class="stat-value">24</span>
<span class="stat-label">containers</span>
</div>
<div class="container-status">
<span class="status-dot running"></span>
<span>18</span>
<span class="status-dot stopped"></span>
<span>2</span>
<span class="status-dot" style="background: #ff9800;"></span>
<span>0</span>
<span class="status-dot" style="background: #9c27b0;"></span>
<span>0</span>
</div>
<div class="stat-item">
<span class="stat-icon">💾</span>
<span class="stat-value">32</span>
<span class="stat-label">volumes</span>
</div>
<div class="stat-item">
<span class="stat-icon">🖼️</span>
<span class="stat-value">87</span>
<span class="stat-label">images</span>
</div>
<div class="stat-item">
<span class="stat-icon">🖥️</span>
<span class="stat-value">4</span>
<span class="stat-label">CPU</span>
</div>
<div class="stat-item">
<span class="stat-icon">💽</span>
<span class="stat-value">7.8</span>
<span class="stat-label">GB RAM</span>
</div>
</div>
</div>
<!-- Environment 2 -->
<div class="env-card">
<div style="display: flex; justify-content: space-between; align-items: flex-start;">
<div class="env-header" style="flex: 1; margin-bottom: 0;">
<div class="env-icon">🐳</div>
<div class="env-info">
<div class="env-name-row">
<span class="env-name">Compute-01</span>
<span class="env-status">Up</span>
<span style="color: var(--text-secondary); font-size: 12px;">↻ 2026-02-02 12:42:53</span>
<span style="color: var(--text-secondary); font-size: 12px;">Standalone 2.21.3</span>
<span class="env-path">agent.local:9001</span>
</div>
<div class="env-meta">
<span class="env-meta-item">Group: <span>Unassigned</span></span>
<span class="env-meta-item">🏷️ No tags</span>
<span class="env-meta-item">⚡ Agent 2.21.3</span>
</div>
</div>
</div>
<div class="env-actions">
<button class="btn btn-primary">📡 Live connect</button>
<button class="btn-icon">✏️</button>
<div style="text-align: right;">
<div class="connection-status">Disconnected</div>
<button class="btn-icon" style="margin-top: 4px;">⚙️</button>
</div>
</div>
</div>
<div class="env-stats">
<div class="stat-item">
<span class="stat-icon">📚</span>
<span class="stat-value">5</span>
<span class="stat-label">stacks</span>
</div>
<div class="stat-item">
<span class="stat-icon">📦</span>
<span class="stat-value">16</span>
<span class="stat-label">containers</span>
</div>
<div class="container-status">
<span class="status-dot running"></span>
<span>14</span>
<span class="status-dot stopped"></span>
<span>1</span>
<span class="status-dot" style="background: #ff9800;"></span>
<span>0</span>
<span class="status-dot" style="background: #9c27b0;"></span>
<span>0</span>
</div>
<div class="stat-item">
<span class="stat-icon">💾</span>
<span class="stat-value">10</span>
<span class="stat-label">volumes</span>
</div>
<div class="stat-item">
<span class="stat-icon">🖼️</span>
<span class="stat-value">24</span>
<span class="stat-label">images</span>
</div>
<div class="stat-item">
<span class="stat-icon">🖥️</span>
<span class="stat-value">6</span>
<span class="stat-label">CPU</span>
</div>
<div class="stat-item">
<span class="stat-icon">💽</span>
<span class="stat-value">8.2</span>
<span class="stat-label">GB RAM</span>
</div>
</div>
</div>
</div>
</main>
</div>
</body>
</html>

View File

@ -0,0 +1,624 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Proxmox Virtual Environment</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--bg-dark: #1e1e1e;
--bg-panel: #2d2d2d;
--bg-sidebar: #252525;
--bg-input: #3a3a3a;
--border-color: #404040;
--text-primary: #e0e0e0;
--text-secondary: #999;
--proxmox-orange: #e57000;
--proxmox-orange-light: #ff8c00;
--btn-blue: #3d85c6;
--btn-blue-hover: #2d6da3;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: var(--bg-dark);
color: var(--text-primary);
font-size: 13px;
height: 100vh;
overflow: hidden;
}
/* Demo Banner */
.demo-banner {
background: #1a1a1a;
color: var(--text-secondary);
text-align: center;
padding: 6px;
font-size: 12px;
border-bottom: 1px solid var(--border-color);
z-index: 1000;
position: relative;
}
.demo-banner a {
color: var(--proxmox-orange);
text-decoration: none;
}
/* Top Bar */
.top-bar {
background: linear-gradient(180deg, #3a3a3a 0%, #2d2d2d 100%);
border-bottom: 1px solid #1a1a1a;
height: 42px;
display: flex;
align-items: center;
padding: 0 8px;
gap: 12px;
}
.logo-area {
display: flex;
align-items: center;
gap: 8px;
padding: 0 8px;
}
.proxmox-logo {
display: flex;
align-items: center;
gap: 4px;
}
.logo-icon {
width: 28px;
height: 28px;
background: var(--proxmox-orange);
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
color: white;
font-size: 14px;
}
.logo-text {
font-size: 14px;
font-weight: 600;
color: var(--text-primary);
}
.logo-text span {
color: var(--proxmox-orange);
}
.search-box {
flex: 1;
max-width: 400px;
margin-left: 20px;
}
.search-box input {
width: 100%;
height: 28px;
background: #1e1e1e;
border: 1px solid #404040;
border-radius: 3px;
padding: 0 10px;
color: var(--text-secondary);
font-size: 12px;
}
.search-box input::placeholder {
color: #666;
}
.top-actions {
display: flex;
gap: 4px;
margin-left: auto;
}
.top-btn {
height: 28px;
padding: 0 12px;
background: linear-gradient(180deg, #4a4a4a 0%, #3a3a3a 100%);
border: 1px solid #505050;
border-radius: 3px;
color: var(--text-primary);
font-size: 12px;
cursor: pointer;
display: flex;
align-items: center;
gap: 6px;
}
.top-btn:hover {
background: linear-gradient(180deg, #5a5a5a 0%, #4a4a4a 100%);
}
.top-btn.primary {
background: var(--btn-blue);
border-color: #2d6da3;
}
.top-btn.primary:hover {
background: var(--btn-blue-hover);
}
.user-menu {
display: flex;
align-items: center;
gap: 4px;
padding: 0 8px;
}
/* Main Container */
.main-container {
display: flex;
height: calc(100vh - 90px);
}
/* Left Sidebar */
.sidebar {
width: 220px;
background: var(--bg-sidebar);
border-right: 1px solid var(--border-color);
display: flex;
flex-direction: column;
}
.sidebar-header {
display: flex;
align-items: center;
padding: 8px;
border-bottom: 1px solid var(--border-color);
gap: 8px;
}
.view-selector {
flex: 1;
height: 26px;
background: #3a3a3a;
border: 1px solid #505050;
border-radius: 3px;
color: var(--text-primary);
font-size: 12px;
padding: 0 8px;
}
.settings-btn {
width: 26px;
height: 26px;
background: #3a3a3a;
border: 1px solid #505050;
border-radius: 3px;
color: var(--text-primary);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
.sidebar-content {
flex: 1;
overflow-y: auto;
padding: 8px;
}
.tree-item {
display: flex;
align-items: center;
gap: 6px;
padding: 6px 8px;
color: var(--text-secondary);
cursor: pointer;
border-radius: 3px;
}
.tree-item:hover {
background: #333;
color: var(--text-primary);
}
.tree-icon {
width: 16px;
text-align: center;
}
/* Center Content - Login Area */
.content {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
background: var(--bg-dark);
position: relative;
}
/* Login Window */
.login-window {
background: #363636;
border: 1px solid #505050;
border-radius: 4px;
box-shadow: 0 4px 20px rgba(0,0,0,0.5);
width: 380px;
}
.login-header {
background: linear-gradient(180deg, #404040 0%, #363636 100%);
padding: 10px 15px;
border-bottom: 1px solid #505050;
font-size: 14px;
color: var(--btn-blue);
}
.login-body {
padding: 20px;
}
.form-row {
display: flex;
align-items: center;
margin-bottom: 12px;
gap: 10px;
}
.form-label {
width: 80px;
text-align: right;
color: var(--text-primary);
font-size: 13px;
}
.form-input-wrapper {
flex: 1;
position: relative;
}
.form-input {
width: 100%;
height: 30px;
background: var(--bg-input);
border: 1px solid #505050;
border-radius: 3px;
padding: 0 30px 0 10px;
color: var(--text-primary);
font-size: 13px;
}
.form-input:focus {
outline: none;
border-color: var(--btn-blue);
box-shadow: 0 0 0 1px var(--btn-blue);
}
.input-icon {
position: absolute;
right: 8px;
top: 50%;
transform: translateY(-50%);
color: var(--proxmox-orange);
font-size: 14px;
}
select.form-input {
cursor: pointer;
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23999' d='M6 8L1 3h10z'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 8px center;
padding-right: 28px;
}
.login-footer {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 12px;
padding-top: 8px;
border-top: 1px solid #505050;
margin-top: 8px;
}
.checkbox-label {
display: flex;
align-items: center;
gap: 6px;
color: var(--text-primary);
font-size: 12px;
cursor: pointer;
}
.checkbox-label input {
width: 14px;
height: 14px;
accent-color: var(--btn-blue);
}
.login-btn {
height: 28px;
padding: 0 20px;
background: var(--btn-blue);
border: 1px solid #2d6da3;
border-radius: 3px;
color: white;
font-size: 12px;
font-weight: 500;
cursor: pointer;
}
.login-btn:hover {
background: var(--btn-blue-hover);
}
/* Bottom Panel */
.bottom-panel {
height: 180px;
background: var(--bg-panel);
border-top: 1px solid var(--border-color);
display: flex;
flex-direction: column;
}
.panel-tabs {
display: flex;
background: #363636;
border-bottom: 1px solid var(--border-color);
}
.panel-tab {
padding: 8px 16px;
color: var(--text-secondary);
font-size: 12px;
cursor: pointer;
border-right: 1px solid var(--border-color);
}
.panel-tab:hover {
color: var(--text-primary);
}
.panel-tab.active {
background: var(--bg-panel);
color: var(--btn-blue);
border-bottom: 2px solid var(--btn-blue);
margin-bottom: -1px;
}
.panel-content {
flex: 1;
overflow: auto;
}
.data-table {
width: 100%;
border-collapse: collapse;
}
.data-table th {
text-align: left;
padding: 8px 12px;
color: var(--text-secondary);
font-size: 12px;
font-weight: normal;
border-bottom: 1px solid var(--border-color);
background: #2d2d2d;
}
.data-table th.sortable::after {
content: ' ↕';
font-size: 10px;
color: #666;
}
.data-table td {
padding: 8px 12px;
color: var(--text-secondary);
font-size: 12px;
border-bottom: 1px solid #333;
}
.data-table tr:hover td {
background: #333;
}
/* Scrollbar */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: var(--bg-dark);
}
::-webkit-scrollbar-thumb {
background: #505050;
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: #606060;
}
</style>
</head>
<body>
<div class="demo-banner">
🔒 This is a dummy login page for showcase purposes only. <a href="/">← Back to Dashboard</a>
</div>
<!-- Top Bar -->
<div class="top-bar">
<div class="logo-area">
<div class="proxmox-logo">
<div class="logo-icon">P</div>
<div class="logo-text"><span>PROX</span>MOX</div>
</div>
<div style="color: var(--text-secondary); font-size: 12px;">Virtual Environment</div>
</div>
<div class="search-box">
<input type="text" placeholder="Search">
</div>
<div class="top-actions">
<button class="top-btn">
<span>📄</span> Documentation
</button>
<button class="top-btn primary">
<span>🖥️</span> Create VM
</button>
<button class="top-btn primary">
<span>📦</span> Create CT
</button>
</div>
<div class="user-menu">
<button class="top-btn" style="width: 32px; padding: 0; justify-content: center;">
👤
</button>
</div>
</div>
<!-- Main Container -->
<div class="main-container">
<!-- Left Sidebar -->
<aside class="sidebar">
<div class="sidebar-header">
<select class="view-selector">
<option>Server View</option>
</select>
<button class="settings-btn">⚙️</button>
</div>
<div class="sidebar-content">
<div class="tree-item">
<span class="tree-icon"></span>
<span class="tree-icon">🏢</span>
<span>Datacenter</span>
</div>
</div>
</aside>
<!-- Center Content -->
<main class="content">
<!-- Login Window -->
<div class="login-window">
<div class="login-header">Proxmox VE Login</div>
<div class="login-body">
<form onsubmit="handleLogin(event)">
<div class="form-row">
<label class="form-label">User name:</label>
<div class="form-input-wrapper">
<input type="text" class="form-input" id="username" autocomplete="off">
<span class="input-icon">🔴</span>
</div>
</div>
<div class="form-row">
<label class="form-label">Password:</label>
<div class="form-input-wrapper">
<input type="password" class="form-input" id="password">
<span class="input-icon">🔴</span>
</div>
</div>
<div class="form-row">
<label class="form-label">Realm:</label>
<div class="form-input-wrapper">
<select class="form-input">
<option>Linux PAM standard authentication</option>
<option>Proxmox VE authentication server</option>
</select>
</div>
</div>
<div class="form-row">
<label class="form-label">Language:</label>
<div class="form-input-wrapper">
<select class="form-input">
<option>English - English</option>
<option>Deutsch - German</option>
<option>Français - French</option>
</select>
</div>
</div>
<div class="login-footer">
<label class="checkbox-label">
<input type="checkbox" checked>
Save User name:
</label>
<button type="submit" class="login-btn">Login</button>
</div>
</form>
</div>
</div>
</main>
</div>
<!-- Bottom Panel -->
<div class="bottom-panel">
<div class="panel-tabs">
<div class="panel-tab active">Tasks</div>
<div class="panel-tab">Cluster log</div>
</div>
<div class="panel-content">
<table class="data-table">
<thead>
<tr>
<th class="sortable">Start Time</th>
<th class="sortable">End Time</th>
<th>Node</th>
<th>User name</th>
<th>Description</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<tr>
<td>02/02/2026 12:21:42</td>
<td>-</td>
<td>pve</td>
<td>root@pam</td>
<td>Login</td>
<td>✓ OK</td>
</tr>
<tr>
<td>02/02/2026 12:20:15</td>
<td>02/02/2026 12:20:16</td>
<td>pve</td>
<td>root@pam</td>
<td>VM 100 - Start</td>
<td>✓ OK</td>
</tr>
<tr>
<td>02/02/2026 12:18:33</td>
<td>02/02/2026 12:18:34</td>
<td>pve</td>
<td>root@pam</td>
<td>VM 101 - Stop</td>
<td>✓ OK</td>
</tr>
</tbody>
</table>
</div>
</div>
<script>
function handleLogin(e) {
e.preventDefault();
alert('🔒 This is a demo page. The real Proxmox VE is not exposed.');
}
// Focus username on load
document.getElementById('username').focus();
</script>
</body>
</html>

View File

@ -322,46 +322,46 @@
Private Services
</h3>
<div class="services-grid">
<div class="service-card private-service" data-service="nextcloud">
<a href="./services/nextcloud.html" class="service-card private-service" target="_blank">
<div class="service-glow"></div>
<div class="service-icon" style="--icon-color: #0082c9; font-size: 28px;">☁️</div>
<h3 class="service-name">Nextcloud</h3>
<p class="service-desc">Private cloud storage, files, and collaboration</p>
<div class="service-status checking" data-status-url="https://cloud.lemonlink.eu/status.php">
<div class="service-status online">
<span class="status-dot"></span>
<span class="status-text">Checking...</span>
<span>Restricted</span>
</div>
<div class="service-arrow">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
<path d="M7 17L17 7M17 7H7M17 7V17"/>
</svg>
</div>
</div>
</a>
<div class="service-card private-service" data-service="netdata">
<a href="./services/netdata.html" class="service-card private-service" target="_blank">
<div class="service-glow"></div>
<div class="service-icon" style="--icon-color: #00ab44; font-size: 28px;">📈</div>
<h3 class="service-name">Netdata</h3>
<p class="service-desc">Real-time system monitoring and metrics</p>
<div class="service-status checking" data-status-url="https://stats.lemonlink.eu/api/v1/info">
<div class="service-status online">
<span class="status-dot"></span>
<span class="status-text">Checking...</span>
<span>Restricted</span>
</div>
<div class="service-arrow">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
<path d="M7 17L17 7M17 7H7M17 7V17"/>
</svg>
</div>
</div>
</a>
<div class="service-card private-service" data-service="immich">
<div class="service-glow"></div>
<div class="service-icon" style="--icon-color: #ad5c5c; font-size: 28px;">🖼️</div>
<h3 class="service-name">Immich</h3>
<p class="service-desc">Self-hosted photo and video backup solution</p>
<div class="service-status checking" data-status-url="https://photos.lemonlink.eu/api/server-info/ping">
<div class="service-status online">
<span class="status-dot"></span>
<span class="status-text">Checking...</span>
<span>Restricted</span>
</div>
<div class="service-arrow">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
@ -370,37 +370,37 @@
</div>
</div>
<div class="service-card private-service" data-service="homarr">
<a href="./services/portainer.html" class="service-card private-service" target="_blank">
<div class="service-glow"></div>
<div class="service-icon" style="--icon-color: #ff5c5c; font-size: 28px;">🎛️</div>
<h3 class="service-name">Homarr</h3>
<p class="service-desc">Customizable dashboard for all your services</p>
<div class="service-status checking" data-status-url="https://dash.lemonlink.eu">
<div class="service-icon" style="--icon-color: #13b6a6; font-size: 28px;">🐳</div>
<h3 class="service-name">Portainer</h3>
<p class="service-desc">Docker container management interface</p>
<div class="service-status online">
<span class="status-dot"></span>
<span class="status-text">Checking...</span>
<span>Restricted</span>
</div>
<div class="service-arrow">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
<path d="M7 17L17 7M17 7H7M17 7V17"/>
</svg>
</div>
</div>
</a>
<div class="service-card private-service" data-service="tailscale">
<a href="./services/pihole.html" class="service-card private-service" target="_blank">
<div class="service-glow"></div>
<div class="service-icon" style="--icon-color: #7b68ee; font-size: 28px;">🔒</div>
<h3 class="service-name">Tailscale</h3>
<p class="service-desc">Zero-config VPN for secure remote access</p>
<div class="service-status checking" data-status-url="https://login.tailscale.com">
<div class="service-icon" style="--icon-color: #f97316; font-size: 28px;">🛡️</div>
<h3 class="service-name">Pi-hole</h3>
<p class="service-desc">Network-wide ad blocking and DNS filtering</p>
<div class="service-status online">
<span class="status-dot"></span>
<span class="status-text">Checking...</span>
<span>Restricted</span>
</div>
<div class="service-arrow">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
<path d="M7 17L17 7M17 7H7M17 7V17"/>
</svg>
</div>
</div>
</a>
</div>
</div>
</div>
@ -687,102 +687,6 @@
</div>
</footer>
<!-- Service Info Modals - Hidden by default -->
<div id="modal-nextcloud" class="service-modal" style="display: none !important;">
<div class="modal-content">
<button class="modal-close">&times;</button>
<h3>☁️ Nextcloud</h3>
<p>Private cloud storage platform - your data stays under your control.</p>
<div class="modal-details">
<h4>Features:</h4>
<ul>
<li>File storage and sync</li>
<li>Calendar and contacts</li>
<li>Document collaboration</li>
<li>End-to-end encryption</li>
</ul>
<h4>Access:</h4>
<p>This is a <strong>private service</strong>. User account required.</p>
</div>
</div>
</div>
<div id="modal-netdata" class="service-modal" style="display: none !important;">
<div class="modal-content">
<button class="modal-close">&times;</button>
<h3>📊 Netdata Monitoring</h3>
<p>Real-time infrastructure monitoring with beautiful dashboards.</p>
<div class="modal-details">
<h4>Features:</h4>
<ul>
<li>Real-time metrics</li>
<li>System performance monitoring</li>
<li>Custom alerts</li>
<li>Historical data</li>
</ul>
<h4>Access:</h4>
<p>This is a <strong>private service</strong>. VPN or local network access required.</p>
</div>
</div>
</div>
<div id="modal-immich" class="service-modal" style="display: none !important;">
<div class="modal-content">
<button class="modal-close">&times;</button>
<h3>📸 Immich Photo Server</h3>
<p>Self-hosted photo and video backup solution - a Google Photos alternative.</p>
<div class="modal-details">
<h4>Features:</h4>
<ul>
<li>Automatic mobile photo backup</li>
<li>AI-powered face recognition</li>
<li>Albums and sharing</li>
<li>RAW file support</li>
</ul>
<h4>Access:</h4>
<p>This is a <strong>private service</strong>. Contact me for access.</p>
</div>
</div>
</div>
<div id="modal-homarr" class="service-modal" style="display: none !important;">
<div class="modal-content">
<button class="modal-close">&times;</button>
<h3>🎛️ Homarr Dashboard</h3>
<p>Personal dashboard aggregating all services in one place.</p>
<div class="modal-details">
<h4>Features:</h4>
<ul>
<li>Service status monitoring</li>
<li>Quick bookmarks</li>
<li>Weather widget</li>
<li>Custom integrations</li>
</ul>
<h4>Access:</h4>
<p>This is a <strong>private service</strong>. VPN access required.</p>
</div>
</div>
</div>
<div id="modal-tailscale" class="service-modal" style="display: none !important;">
<div class="modal-content">
<button class="modal-close">&times;</button>
<h3>🔒 Tailscale VPN</h3>
<p>Zero-configuration mesh VPN for secure remote access to the homelab.</p>
<div class="modal-details">
<h4>Features:</h4>
<ul>
<li>Point-to-point encrypted connections</li>
<li>No open ports required</li>
<li>Multi-device support</li>
<li>Exit nodes for secure browsing</li>
</ul>
<h4>Access:</h4>
<p>Tailscale network invitation required. Contact me to join the tailnet.</p>
</div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>

202
services/jellyfin.html Normal file
View File

@ -0,0 +1,202 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Jellyfin</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: #101010;
min-height: 100vh;
color: #fff;
}
.demo-banner {
background: #00a4dc;
color: white;
text-align: center;
padding: 10px;
font-size: 14px;
font-weight: 500;
z-index: 1000;
}
.demo-banner a {
color: #fff;
text-decoration: underline;
}
.login-container {
display: flex;
align-items: center;
justify-content: center;
min-height: calc(100vh - 40px);
padding: 40px 20px;
}
.login-box {
background: #1a1a1a;
border-radius: 16px;
padding: 48px;
width: 100%;
max-width: 440px;
text-align: center;
}
.logo {
margin-bottom: 32px;
}
.logo-icon {
width: 80px;
height: 80px;
background: linear-gradient(135deg, #00a4dc 0%, #0078a8 100%);
border-radius: 16px;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 16px;
font-size: 40px;
}
.logo h1 {
font-size: 28px;
font-weight: 600;
color: #00a4dc;
}
.server-name {
color: #888;
font-size: 14px;
margin-bottom: 32px;
}
.input-group {
margin-bottom: 20px;
text-align: left;
}
label {
display: block;
margin-bottom: 8px;
color: #aaa;
font-size: 14px;
}
input[type="text"],
input[type="password"] {
width: 100%;
padding: 14px 16px;
background: #252525;
border: 1px solid #333;
border-radius: 8px;
color: white;
font-size: 15px;
transition: border-color 0.3s;
}
input[type="text"]:focus,
input[type="password"]:focus {
outline: none;
border-color: #00a4dc;
}
.options {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
font-size: 13px;
}
.remember {
display: flex;
align-items: center;
gap: 8px;
color: #888;
}
button {
width: 100%;
padding: 14px;
background: #00a4dc;
color: white;
border: none;
border-radius: 8px;
font-size: 15px;
font-weight: 600;
cursor: pointer;
transition: background 0.3s;
}
button:hover {
background: #0088b8;
}
.manual-login {
margin-top: 20px;
color: #00a4dc;
font-size: 14px;
cursor: pointer;
}
.manual-login:hover {
text-decoration: underline;
}
.version {
margin-top: 30px;
color: #555;
font-size: 12px;
}
</style>
</head>
<body>
<div class="demo-banner">
🔒 This is a dummy login page for showcase purposes only. <a href="/">← Back to Dashboard</a>
</div>
<div class="login-container">
<div class="login-box">
<div class="logo">
<div class="logo-icon">🎬</div>
<h1>Jellyfin</h1>
</div>
<p class="server-name">media.lemonlink.eu</p>
<form onsubmit="event.preventDefault(); alert('🔒 This is a demo page. The real Jellyfin is not exposed.');">
<div class="input-group">
<label for="username">Username</label>
<input type="text" id="username" placeholder="Enter username" autocomplete="off">
</div>
<div class="input-group">
<label for="password">Password</label>
<input type="password" id="password" placeholder="••••••••">
</div>
<div class="options">
<label class="remember">
<input type="checkbox"> Remember me
</label>
</div>
<button type="submit">Sign In</button>
</form>
<p class="manual-login" onclick="alert('Manual server setup not available in demo')">
Change Server
</p>
<p class="version">Jellyfin Server 10.8.13</p>
</div>
</div>
</body>
</html>

1062
services/netdata.html Normal file

File diff suppressed because it is too large Load Diff

440
services/nextcloud.html Normal file
View File

@ -0,0 +1,440 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Log in Nextcloud</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, sans-serif;
background: #0f172a;
min-height: 100vh;
color: #fff;
overflow-x: hidden;
}
/* Demo Banner */
.demo-banner {
background: rgba(0,0,0,0.9);
color: #fff;
text-align: center;
padding: 12px;
font-size: 13px;
position: relative;
z-index: 1000;
border-bottom: 1px solid #333;
}
.demo-banner a {
color: #60a5fa;
text-decoration: none;
}
/* Abstract Background Shapes */
.bg-shapes {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
overflow: hidden;
z-index: 0;
}
.shape {
position: absolute;
border-radius: 50%;
filter: blur(60px);
opacity: 0.6;
}
.shape-1 {
width: 600px;
height: 600px;
background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);
left: -200px;
top: -100px;
}
.shape-2 {
width: 500px;
height: 500px;
background: linear-gradient(135deg, #2563eb 0%, #1e40af 100%);
right: -150px;
bottom: -100px;
}
.shape-3 {
width: 300px;
height: 300px;
background: #60a5fa;
left: 60%;
top: 60%;
opacity: 0.4;
}
.shape-4 {
width: 200px;
height: 200px;
background: #3b82f6;
left: 30%;
top: 20%;
opacity: 0.3;
}
/* Main Container */
.container {
position: relative;
z-index: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
padding: 40px 20px;
}
/* Logo */
.logo {
margin-bottom: 30px;
}
.logo svg {
width: 120px;
height: 120px;
}
/* Login Card */
.login-card {
background: rgba(30, 41, 59, 0.95);
backdrop-filter: blur(20px);
border-radius: 16px;
padding: 32px;
width: 100%;
max-width: 360px;
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
}
.login-title {
font-size: 20px;
font-weight: 600;
text-align: center;
margin-bottom: 24px;
color: #fff;
}
/* Input Groups with Floating Labels */
.input-group {
position: relative;
margin-bottom: 20px;
}
.input-group input {
width: 100%;
height: 52px;
padding: 16px 44px 8px 16px;
background: #0f172a;
border: 1px solid #334155;
border-radius: 8px;
font-size: 15px;
color: #fff;
transition: border-color 0.2s, box-shadow 0.2s;
}
.input-group input:focus {
outline: none;
border-color: #3b82f6;
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2);
}
.input-group label {
position: absolute;
left: 16px;
top: 50%;
transform: translateY(-50%);
font-size: 15px;
color: #94a3b8;
pointer-events: none;
transition: all 0.2s;
background: transparent;
padding: 0 4px;
}
.input-group input:focus + label,
.input-group input:not(:placeholder-shown) + label {
top: 0;
transform: translateY(-50%) scale(0.85);
color: #60a5fa;
background: #0f172a;
}
.input-group input:focus + label {
color: #60a5fa;
}
/* Icons inside inputs */
.input-icon {
position: absolute;
right: 14px;
top: 50%;
transform: translateY(-50%);
color: #64748b;
cursor: pointer;
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
}
.input-icon:hover {
color: #94a3b8;
}
/* Remember Me */
.remember-row {
display: flex;
align-items: center;
margin-bottom: 20px;
}
.remember-checkbox {
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
font-size: 14px;
color: #cbd5e1;
}
.remember-checkbox input[type="checkbox"] {
width: 16px;
height: 16px;
accent-color: #3b82f6;
cursor: pointer;
}
/* Login Button */
.login-btn {
width: 100%;
height: 44px;
background: #3b82f6;
color: #fff;
border: none;
border-radius: 8px;
font-size: 15px;
font-weight: 500;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
transition: background 0.2s;
margin-bottom: 16px;
}
.login-btn:hover {
background: #2563eb;
}
.login-btn svg {
width: 16px;
height: 16px;
}
/* Alternative Login */
.alt-login {
text-align: center;
}
.alt-login-btn {
background: transparent;
border: none;
color: #cbd5e1;
font-size: 14px;
cursor: pointer;
padding: 8px 16px;
transition: color 0.2s;
}
.alt-login-btn:hover {
color: #fff;
text-decoration: underline;
}
/* Forgot Password */
.forgot-password {
text-align: center;
margin-top: 12px;
}
.forgot-password a {
color: #cbd5e1;
font-size: 14px;
text-decoration: none;
}
.forgot-password a:hover {
color: #fff;
text-decoration: underline;
}
/* Footer */
footer {
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
background: rgba(30, 41, 59, 0.9);
padding: 12px 24px;
border-radius: 24px;
font-size: 13px;
color: #94a3b8;
z-index: 10;
}
footer a {
color: #fff;
text-decoration: none;
font-weight: 500;
}
/* Loading State */
.login-btn.loading {
pointer-events: none;
}
.login-btn.loading::after {
content: '';
width: 16px;
height: 16px;
border: 2px solid rgba(255,255,255,0.3);
border-top-color: #fff;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
/* Responsive */
@media (max-width: 480px) {
.login-card {
padding: 24px;
}
.logo svg {
width: 100px;
height: 100px;
}
}
</style>
</head>
<body>
<div class="demo-banner">
🔒 This is a demo login page. The real service is private. <a href="https://lemonlink.eu">← Back to LemonLink</a>
</div>
<div class="bg-shapes">
<div class="shape shape-1"></div>
<div class="shape shape-2"></div>
<div class="shape shape-3"></div>
<div class="shape shape-4"></div>
</div>
<div class="container">
<div class="logo">
<svg viewBox="0 0 100 100" fill="none">
<circle cx="50" cy="50" r="45" fill="white"/>
<circle cx="35" cy="42" r="11" fill="#0f172a"/>
<circle cx="65" cy="42" r="11" fill="#0f172a"/>
<circle cx="50" cy="65" r="11" fill="#0f172a"/>
</svg>
</div>
<div class="login-card">
<h1 class="login-title">Log in to Nextcloud</h1>
<form onsubmit="handleLogin(event)">
<div class="input-group">
<input type="text" id="user" placeholder=" " autocomplete="off" required>
<label for="user">Account name or email</label>
</div>
<div class="input-group">
<input type="password" id="password" placeholder=" " required>
<label for="password">Password</label>
<span class="input-icon" onclick="togglePassword()" title="Show password">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/>
<circle cx="12" cy="12" r="3"/>
</svg>
</span>
</div>
<div class="remember-row">
<label class="remember-checkbox">
<input type="checkbox" checked>
<span>Remember me</span>
</label>
</div>
<button type="submit" class="login-btn" id="loginBtn">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M5 12h14M12 5l7 7-7 7"/>
</svg>
Log in
</button>
</form>
<div class="alt-login">
<button class="alt-login-btn" onclick="alert('Demo page - device login not available')">
Log in with a device
</button>
</div>
<div class="forgot-password">
<a href="#" onclick="alert('Demo page - password reset not available'); return false;">
Forgot password?
</a>
</div>
</div>
</div>
<footer>
<a href="https://nextcloud.com" target="_blank">Nextcloud</a> a safe home for all your data
</footer>
<script>
function handleLogin(e) {
e.preventDefault();
const btn = document.getElementById('loginBtn');
const originalContent = btn.innerHTML;
btn.classList.add('loading');
btn.innerHTML = 'Logging in…';
setTimeout(() => {
btn.classList.remove('loading');
btn.innerHTML = originalContent;
alert('🔒 This is a demo page. The real Nextcloud is not exposed.');
}, 1000);
}
function togglePassword() {
const pw = document.getElementById('password');
pw.type = pw.type === 'password' ? 'text' : 'password';
}
// Focus username on load
document.getElementById('user').focus();
</script>
</body>
</html>

698
services/pihole.html Normal file
View File

@ -0,0 +1,698 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Pi-hole Dashboard</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--bg-dark: #1a1a1a;
--bg-sidebar: #222;
--bg-card: #2a2a2a;
--border-color: #333;
--text-primary: #e0e0e0;
--text-secondary: #888;
--pihole-red: #96060c;
--pihole-green: #4caf50;
--pihole-blue: #3c8dbc;
--pihole-yellow: #f39c12;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: var(--bg-dark);
color: var(--text-primary);
font-size: 14px;
min-height: 100vh;
}
/* Demo Banner */
.demo-banner {
background: #1a1a1a;
color: var(--text-secondary);
text-align: center;
padding: 8px;
font-size: 12px;
border-bottom: 1px solid var(--border-color);
}
.demo-banner a {
color: var(--pihole-red);
text-decoration: none;
}
/* Layout */
.container {
display: flex;
min-height: calc(100vh - 33px);
}
/* Sidebar */
.sidebar {
width: 200px;
background: var(--bg-sidebar);
border-right: 1px solid var(--border-color);
}
.sidebar-header {
background: linear-gradient(180deg, #3c8dbc 0%, #357ca5 100%);
padding: 16px;
text-align: center;
}
.sidebar-header h1 {
font-size: 20px;
font-weight: 600;
color: white;
}
.status-box {
padding: 16px;
display: flex;
align-items: center;
gap: 12px;
}
.pihole-logo {
width: 50px;
height: 50px;
background: var(--pihole-red);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 28px;
position: relative;
}
.pihole-logo::before {
content: '';
position: absolute;
top: -5px;
left: 50%;
transform: translateX(-50%);
width: 20px;
height: 15px;
background: var(--pihole-green);
border-radius: 10px 10px 0 0;
}
.status-info {
flex: 1;
}
.status-title {
font-size: 12px;
color: var(--text-secondary);
margin-bottom: 4px;
}
.status-item {
display: flex;
align-items: center;
gap: 6px;
font-size: 11px;
color: var(--text-secondary);
margin-bottom: 2px;
}
.status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
}
.status-dot.active {
background: var(--pihole-green);
}
.nav-menu {
padding: 8px 0;
}
.nav-item {
display: flex;
align-items: center;
gap: 12px;
padding: 10px 16px;
color: var(--text-secondary);
cursor: pointer;
transition: all 0.2s;
font-size: 13px;
}
.nav-item:hover {
background: #2a2a2a;
color: var(--text-primary);
}
.nav-item.active {
background: #2a2a2a;
color: white;
border-left: 3px solid var(--pihole-red);
}
.nav-icon {
width: 18px;
text-align: center;
}
.nav-arrow {
margin-left: auto;
font-size: 10px;
}
/* Main Content */
.content {
flex: 1;
padding: 20px;
overflow-y: auto;
}
.top-bar {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.hostname {
color: var(--text-secondary);
font-size: 13px;
}
/* Stats Cards */
.stats-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 16px;
margin-bottom: 24px;
}
.stat-card {
background: var(--bg-card);
border-radius: 8px;
padding: 20px;
position: relative;
overflow: hidden;
min-height: 120px;
}
.stat-card.blue {
background: linear-gradient(135deg, #3c8dbc 0%, #2c6da0 100%);
}
.stat-card.red {
background: linear-gradient(135deg, #c0392b 0%, #962d22 100%);
}
.stat-card.yellow {
background: linear-gradient(135deg, #f39c12 0%, #c27a0e 100%);
}
.stat-card.green {
background: linear-gradient(135deg, #27ae60 0%, #1e8449 100%);
}
.stat-bg-icon {
position: absolute;
right: -10px;
bottom: -10px;
font-size: 80px;
opacity: 0.2;
}
.stat-label {
font-size: 13px;
color: rgba(255,255,255,0.9);
margin-bottom: 8px;
position: relative;
z-index: 1;
}
.stat-value {
font-size: 36px;
font-weight: 600;
color: white;
position: relative;
z-index: 1;
}
.stat-link {
position: absolute;
bottom: 12px;
left: 20px;
right: 20px;
font-size: 12px;
color: rgba(255,255,255,0.8);
display: flex;
align-items: center;
gap: 6px;
cursor: pointer;
z-index: 1;
}
.stat-link:hover {
color: white;
}
/* Charts */
.chart-card {
background: var(--bg-card);
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
}
.chart-title {
font-size: 15px;
margin-bottom: 16px;
color: var(--text-primary);
}
.chart-area {
height: 200px;
position: relative;
}
/* Bar Chart */
.bar-chart {
display: flex;
align-items: flex-end;
justify-content: space-between;
height: 160px;
padding: 0 4px;
position: relative;
}
.bar-chart::before {
content: '';
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
background:
linear-gradient(to bottom, transparent 19px, #333 20px),
linear-gradient(to right, transparent 19px, transparent 20px);
background-size: 100% 40px, 40px 100%;
opacity: 0.3;
}
.bar-group {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
gap: 2px;
height: 100%;
justify-content: flex-end;
position: relative;
}
.bar {
width: 6px;
border-radius: 2px 2px 0 0;
}
.bar.permitted {
background: #666;
}
.bar.blocked {
background: var(--pihole-green);
}
.bar.client {
width: 4px;
}
.chart-axis {
display: flex;
justify-content: space-between;
padding: 8px 4px 0;
font-size: 11px;
color: var(--text-secondary);
}
.chart-axis-y {
position: absolute;
left: 0;
top: 0;
bottom: 24px;
display: flex;
flex-direction: column;
justify-content: space-between;
font-size: 11px;
color: var(--text-secondary);
}
/* Pie Charts Grid */
.pie-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20px;
}
.pie-chart-container {
display: flex;
gap: 20px;
}
.pie-chart {
width: 150px;
height: 150px;
position: relative;
}
.pie-chart svg {
width: 100%;
height: 100%;
transform: rotate(-90deg);
}
.pie-legend {
flex: 1;
display: flex;
flex-direction: column;
gap: 8px;
justify-content: center;
}
.legend-item {
display: flex;
align-items: center;
gap: 8px;
font-size: 12px;
color: var(--text-secondary);
}
.legend-color {
width: 12px;
height: 12px;
border-radius: 2px;
}
.legend-checkbox {
width: 14px;
height: 14px;
accent-color: var(--pihole-blue);
}
</style>
</head>
<body>
<div class="demo-banner">
🔒 This is a dummy dashboard for showcase purposes only. <a href="/">← Back to Dashboard</a>
</div>
<div class="container">
<!-- Sidebar -->
<aside class="sidebar">
<div class="sidebar-header">
<h1>Pi-hole</h1>
</div>
<div class="status-box">
<div class="pihole-logo">🕳️</div>
<div class="status-info">
<div class="status-title">Status</div>
<div class="status-item">
<span class="status-dot active"></span>
<span>Active</span>
</div>
<div class="status-item">
<span class="status-dot active"></span>
<span>Load: 0.12 0.15 0.18</span>
</div>
<div class="status-item">
<span class="status-dot active"></span>
<span>Memory usage: 12.4%</span>
</div>
<div class="status-item">
<span>🌡️ Temp: 42.0 °C</span>
</div>
</div>
</div>
<nav class="nav-menu">
<div class="nav-item active">
<span class="nav-icon">🏠</span>
<span>Dashboard</span>
</div>
<div class="nav-item">
<span class="nav-icon">📋</span>
<span>Query Log</span>
</div>
<div class="nav-item">
<span class="nav-icon">📊</span>
<span>Long-term Data</span>
<span class="nav-arrow"></span>
</div>
<div class="nav-item">
<span class="nav-icon">👥</span>
<span>Groups</span>
</div>
<div class="nav-item">
<span class="nav-icon">🖥️</span>
<span>Clients</span>
</div>
<div class="nav-item">
<span class="nav-icon">🌐</span>
<span>Domains</span>
</div>
<div class="nav-item">
<span class="nav-icon">🛡️</span>
<span>Adlists</span>
</div>
<div class="nav-item">
<span class="nav-icon">⏸️</span>
<span>Disable Blocking</span>
<span class="nav-arrow"></span>
</div>
<div class="nav-item">
<span class="nav-icon">📝</span>
<span>Local DNS</span>
<span class="nav-arrow"></span>
</div>
<div class="nav-item">
<span class="nav-icon">🔧</span>
<span>Tools</span>
<span class="nav-arrow"></span>
</div>
<div class="nav-item">
<span class="nav-icon">⚙️</span>
<span>Settings</span>
</div>
<div class="nav-item">
<span class="nav-icon">❤️</span>
<span>Donate</span>
</div>
</nav>
</aside>
<!-- Main Content -->
<main class="content">
<div class="top-bar">
<button style="background: transparent; border: none; color: var(--text-secondary); font-size: 18px; cursor: pointer;"></button>
<span class="hostname">hostname: pihole</span>
<button style="background: transparent; border: none; color: var(--text-secondary); font-size: 18px; cursor: pointer;"></button>
</div>
<!-- Stats Cards -->
<div class="stats-grid">
<div class="stat-card blue">
<div class="stat-bg-icon">🌍</div>
<div class="stat-label">Total queries</div>
<div class="stat-value">73,770</div>
<div class="stat-link">
<span>23 active clients</span>
<span></span>
</div>
</div>
<div class="stat-card red">
<div class="stat-bg-icon">🛡️</div>
<div class="stat-label">Queries Blocked</div>
<div class="stat-value">17,304</div>
<div class="stat-link">
<span>List blocked queries</span>
<span></span>
</div>
</div>
<div class="stat-card yellow">
<div class="stat-bg-icon">📊</div>
<div class="stat-label">Percentage Blocked</div>
<div class="stat-value">23.5%</div>
<div class="stat-link">
<span>List all queries</span>
<span></span>
</div>
</div>
<div class="stat-card green">
<div class="stat-bg-icon">📋</div>
<div class="stat-label">Domains on Adlists</div>
<div class="stat-value">254,501</div>
<div class="stat-link">
<span>Manage adlists</span>
<span></span>
</div>
</div>
</div>
<!-- Total Queries Chart -->
<div class="chart-card">
<div class="chart-title">Total queries over last 24 hours</div>
<div class="chart-area">
<div class="chart-axis-y">
<span>1,200</span>
<span>1,000</span>
<span>800</span>
<span>600</span>
<span>400</span>
<span>200</span>
<span>0</span>
</div>
<div class="bar-chart" style="margin-left: 40px;">
<script>
// Generate bars
const hours = ['13:00','14:00','15:00','16:00','17:00','18:00','19:00','20:00','21:00','22:00','23:00','00:00','01:00','02:00','03:00','04:00','05:00','06:00','07:00','08:00','09:00','10:00','11:00','12:00'];
hours.forEach((hour, i) => {
const h1 = 30 + Math.random() * 50;
const h2 = 50 + Math.random() * 70;
document.write(`
<div class="bar-group">
<div class="bar permitted" style="height: ${h1}%;"></div>
<div class="bar blocked" style="height: ${h2}%;"></div>
</div>
`);
});
</script>
</div>
</div>
<div class="chart-axis">
<script>
hours.forEach(hour => document.write(`<span>${hour}</span>`));
</script>
</div>
</div>
<!-- Client Activity Chart -->
<div class="chart-card">
<div class="chart-title">Client activity over last 24 hours</div>
<div class="chart-area">
<div class="chart-axis-y">
<span>1,200</span>
<span>1,000</span>
<span>800</span>
<span>600</span>
<span>400</span>
<span>200</span>
<span>0</span>
</div>
<div class="bar-chart" style="margin-left: 40px;">
<script>
hours.forEach((hour, i) => {
const colors = ['#9c27b0', '#e91e63', '#00bcd4', '#ff9800', '#4caf50'];
document.write(`<div class="bar-group">`);
colors.forEach(color => {
const h = 20 + Math.random() * 60;
document.write(`<div class="bar client" style="height: ${h}%; background: ${color}; width: 2px;"></div>`);
});
document.write(`</div>`);
});
</script>
</div>
</div>
<div class="chart-axis">
<script>
hours.forEach(hour => document.write(`<span>${hour}</span>`));
</script>
</div>
</div>
<!-- Pie Charts -->
<div class="pie-grid">
<div class="chart-card">
<div class="chart-title">Query Types</div>
<div class="pie-chart-container">
<div class="pie-chart">
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="40" fill="none" stroke="#ff7043" stroke-width="20" stroke-dasharray="100 251"/>
<circle cx="50" cy="50" r="40" fill="none" stroke="#42a5f5" stroke-width="20" stroke-dasharray="60 251" stroke-dashoffset="-100"/>
<circle cx="50" cy="50" r="40" fill="none" stroke="#66bb6a" stroke-width="20" stroke-dasharray="40 251" stroke-dashoffset="-160"/>
<circle cx="50" cy="50" r="40" fill="none" stroke="#ab47bc" stroke-width="20" stroke-dasharray="30 251" stroke-dashoffset="-200"/>
<circle cx="50" cy="50" r="40" fill="none" stroke="#26c6da" stroke-width="20" stroke-dasharray="21 251" stroke-dashoffset="-230"/>
</svg>
</div>
<div class="pie-legend">
<label class="legend-item">
<input type="checkbox" class="legend-checkbox" checked>
<div class="legend-color" style="background: #ff7043;"></div>
<span>A (IPv4)</span>
</label>
<label class="legend-item">
<input type="checkbox" class="legend-checkbox" checked>
<div class="legend-color" style="background: #42a5f5;"></div>
<span>AAAA (IPv6)</span>
</label>
<label class="legend-item">
<input type="checkbox" class="legend-checkbox" checked>
<div class="legend-color" style="background: #66bb6a;"></div>
<span>SRV</span>
</label>
<label class="legend-item">
<input type="checkbox" class="legend-checkbox" checked>
<div class="legend-color" style="background: #ab47bc;"></div>
<span>SOA</span>
</label>
<label class="legend-item">
<input type="checkbox" class="legend-checkbox" checked>
<div class="legend-color" style="background: #26c6da;"></div>
<span>PTR</span>
</label>
</div>
</div>
</div>
<div class="chart-card">
<div class="chart-title">Upstream servers</div>
<div class="pie-chart-container">
<div class="pie-chart">
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="40" fill="none" stroke="#ff7043" stroke-width="20" stroke-dasharray="120 251"/>
<circle cx="50" cy="50" r="40" fill="none" stroke="#42a5f5" stroke-width="20" stroke-dasharray="80 251" stroke-dashoffset="-120"/>
<circle cx="50" cy="50" r="40" fill="none" stroke="#66bb6a" stroke-width="20" stroke-dasharray="51 251" stroke-dashoffset="-200"/>
</svg>
</div>
<div class="pie-legend">
<label class="legend-item">
<input type="checkbox" class="legend-checkbox" checked>
<div class="legend-color" style="background: #ff7043;"></div>
<span>blocked</span>
</label>
<label class="legend-item">
<input type="checkbox" class="legend-checkbox" checked>
<div class="legend-color" style="background: #42a5f5;"></div>
<span>cached</span>
</label>
<label class="legend-item">
<input type="checkbox" class="legend-checkbox" checked>
<div class="legend-color" style="background: #66bb6a;"></div>
<span>localhost#5335</span>
</label>
</div>
</div>
</div>
</div>
</main>
</div>
</body>
</html>

431
services/plex.html Normal file
View File

@ -0,0 +1,431 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Plex</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: #1f1f1f;
min-height: 100vh;
color: #eee;
}
.demo-banner {
background: #e5a00d;
color: #000;
text-align: center;
padding: 10px;
font-size: 14px;
font-weight: 500;
z-index: 1000;
}
.demo-banner a {
color: #1f1f1f;
text-decoration: underline;
}
.sidebar {
position: fixed;
left: 0;
top: 40px;
bottom: 0;
width: 240px;
background: #1f1f1f;
border-right: 1px solid #333;
padding: 20px 0;
}
.logo {
padding: 0 20px 30px;
display: flex;
align-items: center;
gap: 10px;
}
.logo-icon {
width: 40px;
height: 40px;
background: #e5a00d;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
font-size: 22px;
}
.logo-text {
font-size: 24px;
font-weight: 700;
color: #e5a00d;
}
.nav-section {
margin-bottom: 24px;
}
.nav-title {
padding: 0 20px 8px;
font-size: 11px;
text-transform: uppercase;
color: #888;
letter-spacing: 1px;
}
.nav-item {
padding: 10px 20px;
display: flex;
align-items: center;
gap: 12px;
cursor: pointer;
transition: background 0.2s;
font-size: 14px;
}
.nav-item:hover, .nav-item.active {
background: rgba(255,255,255,0.1);
}
.nav-item.active {
border-left: 3px solid #e5a00d;
padding-left: 17px;
}
.nav-icon {
width: 20px;
text-align: center;
}
.main-content {
margin-left: 240px;
padding: 60px 30px 30px;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
}
.search-box {
background: rgba(255,255,255,0.1);
border: none;
border-radius: 4px;
padding: 10px 16px;
width: 300px;
color: #eee;
font-size: 14px;
}
.search-box::placeholder {
color: #888;
}
.user-menu {
display: flex;
align-items: center;
gap: 16px;
}
.user-avatar {
width: 36px;
height: 36px;
background: #e5a00d;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
color: #1f1f1f;
}
.section {
margin-bottom: 40px;
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
.section-title {
font-size: 20px;
font-weight: 600;
}
.see-all {
color: #e5a00d;
font-size: 14px;
cursor: pointer;
}
.movies-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
gap: 16px;
}
.movie-card {
cursor: pointer;
transition: transform 0.2s;
}
.movie-card:hover {
transform: scale(1.05);
}
.movie-poster {
aspect-ratio: 2/3;
border-radius: 8px;
background: linear-gradient(135deg, #333 0%, #555 100%);
display: flex;
align-items: center;
justify-content: center;
font-size: 48px;
margin-bottom: 8px;
position: relative;
overflow: hidden;
}
.movie-poster::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(180deg, transparent 50%, rgba(0,0,0,0.8) 100%);
}
.movie-title {
font-size: 14px;
font-weight: 500;
margin-bottom: 4px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.movie-meta {
font-size: 12px;
color: #888;
}
.hero-banner {
background: linear-gradient(135deg, #2a2a2a 0%, #1a1a1a 100%);
border-radius: 12px;
padding: 40px;
margin-bottom: 40px;
display: flex;
gap: 30px;
align-items: center;
}
.hero-poster {
width: 200px;
height: 300px;
background: linear-gradient(135deg, #444 0%, #666 100%);
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
font-size: 80px;
flex-shrink: 0;
}
.hero-info h2 {
font-size: 32px;
margin-bottom: 8px;
}
.hero-meta {
color: #aaa;
font-size: 14px;
margin-bottom: 16px;
}
.hero-desc {
color: #ccc;
line-height: 1.6;
margin-bottom: 20px;
max-width: 600px;
}
.hero-buttons {
display: flex;
gap: 12px;
}
.btn {
padding: 12px 24px;
border-radius: 4px;
font-size: 14px;
font-weight: 600;
cursor: pointer;
border: none;
display: flex;
align-items: center;
gap: 8px;
}
.btn-primary {
background: #e5a00d;
color: #1f1f1f;
}
.btn-secondary {
background: rgba(255,255,255,0.2);
color: #eee;
}
</style>
</head>
<body>
<div class="demo-banner">
🔒 This is a dummy media server for showcase purposes only. <a href="/">← Back to Dashboard</a>
</div>
<aside class="sidebar">
<div class="logo">
<div class="logo-icon">▶️</div>
<span class="logo-text">Plex</span>
</div>
<div class="nav-section">
<div class="nav-title">Discover</div>
<div class="nav-item active">
<span class="nav-icon">🏠</span>
<span>Home</span>
</div>
<div class="nav-item">
<span class="nav-icon">📺</span>
<span>Movies</span>
</div>
<div class="nav-item">
<span class="nav-icon">🎬</span>
<span>TV Shows</span>
</div>
<div class="nav-item">
<span class="nav-icon">🎵</span>
<span>Music</span>
</div>
</div>
<div class="nav-section">
<div class="nav-title">Library</div>
<div class="nav-item">
<span class="nav-icon">📁</span>
<span>All Media</span>
</div>
<div class="nav-item">
<span class="nav-icon">📊</span>
<span>Dashboard</span>
</div>
</div>
</aside>
<main class="main-content">
<div class="header">
<input type="text" class="search-box" placeholder="Search for movies, shows, music...">
<div class="user-menu">
<span>🔔</span>
<span>⚙️</span>
<div class="user-avatar">A</div>
</div>
</div>
<div class="hero-banner">
<div class="hero-poster">🎭</div>
<div class="hero-info">
<h2>Continue Watching</h2>
<div class="hero-meta">2023 • 2h 15m • Action, Sci-Fi</div>
<p class="hero-desc">
Pick up where you left off. Your personal media server is ready with all your favorite movies and TV shows.
</p>
<div class="hero-buttons">
<button class="btn btn-primary">▶ Play</button>
<button class="btn btn-secondary">+ My List</button>
</div>
</div>
</div>
<div class="section">
<div class="section-header">
<h3 class="section-title">Recently Added Movies</h3>
<span class="see-all">See all →</span>
</div>
<div class="movies-grid">
<div class="movie-card">
<div class="movie-poster">🎬</div>
<div class="movie-title">The Matrix Resurrections</div>
<div class="movie-meta">2021 • 148 min</div>
</div>
<div class="movie-card">
<div class="movie-poster">🚀</div>
<div class="movie-title">Interstellar</div>
<div class="movie-meta">2014 • 169 min</div>
</div>
<div class="movie-card">
<div class="movie-poster">🦸</div>
<div class="movie-title">The Dark Knight</div>
<div class="movie-meta">2008 • 152 min</div>
</div>
<div class="movie-card">
<div class="movie-poster">🤖</div>
<div class="movie-title">Blade Runner 2049</div>
<div class="movie-meta">2017 • 164 min</div>
</div>
<div class="movie-card">
<div class="movie-poster">🌌</div>
<div class="movie-title">Dune</div>
<div class="movie-meta">2021 • 155 min</div>
</div>
<div class="movie-card">
<div class="movie-poster">⚔️</div>
<div class="movie-title">Inception</div>
<div class="movie-meta">2010 • 148 min</div>
</div>
</div>
</div>
<div class="section">
<div class="section-header">
<h3 class="section-title">Continue Watching</h3>
<span class="see-all">See all →</span>
</div>
<div class="movies-grid">
<div class="movie-card">
<div class="movie-poster">📺</div>
<div class="movie-title">Breaking Bad S05E08</div>
<div class="movie-meta">45 min remaining</div>
</div>
<div class="movie-card">
<div class="movie-poster">🐉</div>
<div class="movie-title">House of Dragon S01E03</div>
<div class="movie-meta">32 min remaining</div>
</div>
<div class="movie-card">
<div class="movie-poster">⚔️</div>
<div class="movie-title">The Witcher S02E05</div>
<div class="movie-meta">28 min remaining</div>
</div>
<div class="movie-card">
<div class="movie-poster">🕵️</div>
<div class="movie-title">Sherlock S04E01</div>
<div class="movie-meta">52 min remaining</div>
</div>
</div>
</div>
</main>
</body>
</html>

796
services/portainer.html Normal file
View File

@ -0,0 +1,796 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Home - Portainer</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--bg-dark: #0f0f0f;
--bg-sidebar: #1a1a1a;
--bg-card: #1e1e1e;
--bg-hover: #252525;
--border-color: #2a2a2a;
--text-primary: #e0e0e0;
--text-secondary: #888;
--portainer-blue: #13bef9;
--portainer-teal: #00bfa5;
--success: #4caf50;
--danger: #f44336;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: var(--bg-dark);
color: var(--text-primary);
font-size: 13px;
height: 100vh;
overflow: hidden;
}
/* Demo Banner */
.demo-banner {
background: #141414;
color: var(--text-secondary);
text-align: center;
padding: 8px;
font-size: 12px;
border-bottom: 1px solid var(--border-color);
}
.demo-banner a {
color: var(--portainer-blue);
text-decoration: none;
}
/* Main Layout */
.container {
display: flex;
height: calc(100vh - 33px);
}
/* Left Sidebar */
.sidebar {
width: 240px;
background: var(--bg-sidebar);
border-right: 1px solid var(--border-color);
display: flex;
flex-direction: column;
}
.sidebar-header {
padding: 16px;
border-bottom: 1px solid var(--border-color);
}
.logo {
display: flex;
align-items: center;
gap: 8px;
}
.logo-icon {
font-size: 20px;
font-weight: 800;
color: white;
letter-spacing: -1px;
}
.logo-icon span {
color: var(--portainer-teal);
}
.logo-badge {
font-size: 9px;
color: var(--portainer-teal);
text-transform: uppercase;
letter-spacing: 1px;
margin-top: 2px;
}
.sidebar-nav {
flex: 1;
overflow-y: auto;
padding: 8px 0;
}
.nav-section {
margin-bottom: 8px;
}
.nav-header {
padding: 8px 16px;
color: var(--text-secondary);
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.nav-item {
display: flex;
align-items: center;
gap: 12px;
padding: 10px 16px;
color: var(--text-secondary);
cursor: pointer;
transition: all 0.2s;
}
.nav-item:hover {
background: var(--bg-hover);
color: var(--text-primary);
}
.nav-item.active {
background: var(--bg-hover);
color: var(--portainer-blue);
border-left: 3px solid var(--portainer-blue);
padding-left: 13px;
}
.nav-icon {
width: 18px;
text-align: center;
font-size: 14px;
}
.nav-arrow {
margin-left: auto;
font-size: 10px;
}
.sidebar-footer {
padding: 12px 16px;
border-top: 1px solid var(--border-color);
color: var(--text-secondary);
font-size: 11px;
}
/* Main Content */
.content {
flex: 1;
overflow-y: auto;
padding: 24px;
}
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
}
.page-title {
font-size: 24px;
font-weight: 500;
display: flex;
align-items: center;
gap: 8px;
}
.page-actions {
display: flex;
gap: 12px;
}
.icon-btn {
width: 36px;
height: 36px;
background: transparent;
border: 1px solid var(--border-color);
border-radius: 4px;
color: var(--text-secondary);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
.icon-btn:hover {
background: var(--bg-hover);
color: var(--text-primary);
}
.user-menu {
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
}
.user-avatar {
width: 32px;
height: 32px;
background: var(--portainer-blue);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
}
/* Environment Card */
.env-card {
background: var(--bg-card);
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 20px;
margin-bottom: 16px;
}
.env-header {
display: flex;
align-items: flex-start;
gap: 16px;
margin-bottom: 16px;
}
.env-icon {
width: 48px;
height: 48px;
background: #1da1f2;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
font-size: 28px;
}
.env-info {
flex: 1;
}
.env-name-row {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 6px;
}
.env-name {
font-size: 16px;
font-weight: 600;
}
.env-status {
display: flex;
align-items: center;
gap: 6px;
padding: 4px 10px;
background: rgba(76, 175, 80, 0.15);
color: var(--success);
border-radius: 4px;
font-size: 12px;
font-weight: 500;
}
.env-status::before {
content: '';
width: 8px;
height: 8px;
background: var(--success);
border-radius: 50%;
}
.env-meta {
display: flex;
align-items: center;
gap: 16px;
color: var(--text-secondary);
font-size: 12px;
flex-wrap: wrap;
}
.env-meta-item {
display: flex;
align-items: center;
gap: 6px;
}
.env-meta-item span {
color: var(--text-primary);
}
.env-path {
color: var(--text-secondary);
font-family: monospace;
font-size: 12px;
}
/* Stats Row */
.env-stats {
display: flex;
align-items: center;
gap: 24px;
padding-top: 16px;
border-top: 1px solid var(--border-color);
flex-wrap: wrap;
}
.stat-item {
display: flex;
align-items: center;
gap: 8px;
}
.stat-icon {
color: var(--text-secondary);
font-size: 14px;
}
.stat-value {
font-weight: 500;
}
.stat-label {
color: var(--text-secondary);
}
.container-status {
display: flex;
align-items: center;
gap: 8px;
}
.status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
}
.status-dot.running {
background: var(--success);
}
.status-dot.stopped {
background: var(--danger);
}
/* Environment Actions */
.env-actions {
display: flex;
align-items: center;
gap: 12px;
margin-left: auto;
}
.btn {
height: 36px;
padding: 0 16px;
border-radius: 4px;
font-size: 13px;
cursor: pointer;
display: flex;
align-items: center;
gap: 8px;
transition: all 0.2s;
}
.btn-primary {
background: white;
color: #000;
border: none;
}
.btn-primary:hover {
background: #e0e0e0;
}
.btn-icon {
width: 36px;
height: 36px;
background: transparent;
border: 1px solid var(--border-color);
border-radius: 4px;
color: var(--text-secondary);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
.btn-icon:hover {
background: var(--bg-hover);
color: var(--text-primary);
}
.connection-status {
display: flex;
align-items: center;
gap: 6px;
color: var(--text-secondary);
font-size: 12px;
}
.connection-status::before {
content: '✕';
color: var(--text-secondary);
}
/* Filters Bar */
.filters-bar {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 16px;
flex-wrap: wrap;
}
.filter-dropdown {
height: 36px;
padding: 0 12px;
background: var(--bg-card);
border: 1px solid var(--border-color);
border-radius: 4px;
color: var(--text-primary);
font-size: 13px;
cursor: pointer;
min-width: 140px;
}
.search-box {
flex: 1;
max-width: 400px;
height: 36px;
padding: 0 16px;
background: var(--bg-card);
border: 1px solid var(--border-color);
border-radius: 4px;
color: var(--text-primary);
font-size: 13px;
}
.search-box::placeholder {
color: var(--text-secondary);
}
.btn-refresh {
height: 36px;
padding: 0 16px;
background: var(--bg-card);
border: 1px solid var(--border-color);
border-radius: 4px;
color: var(--text-primary);
font-size: 13px;
cursor: pointer;
display: flex;
align-items: center;
gap: 8px;
}
.btn-refresh:hover {
background: var(--bg-hover);
}
/* Card Header */
.card-header {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 20px;
}
.card-title {
font-size: 16px;
font-weight: 600;
display: flex;
align-items: center;
gap: 8px;
}
.card-subtitle {
color: var(--text-secondary);
font-size: 12px;
}
/* Scrollbar */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: var(--bg-dark);
}
::-webkit-scrollbar-thumb {
background: #333;
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: #444;
}
</style>
</head>
<body>
<div class="demo-banner">
🔒 This is a dummy dashboard for showcase purposes only. <a href="/">← Back to Dashboard</a>
</div>
<div class="container">
<!-- Left Sidebar -->
<aside class="sidebar">
<div class="sidebar-header">
<div class="logo">
<div>
<div class="logo-icon">PORTAINER<span>.</span>IO</div>
<div class="logo-badge">Community Edition</div>
</div>
</div>
</div>
<nav class="sidebar-nav">
<div class="nav-section">
<div class="nav-item active">
<span class="nav-icon">🏠</span>
<span>Home</span>
</div>
</div>
<div class="nav-section">
<div class="nav-header">Environment: / None selected</div>
</div>
<div class="nav-section">
<div class="nav-header">Administration</div>
<div class="nav-item">
<span class="nav-icon">👤</span>
<span>User-related</span>
<span class="nav-arrow"></span>
</div>
<div class="nav-item">
<span class="nav-icon">🖥️</span>
<span>Environment-related</span>
<span class="nav-arrow"></span>
</div>
<div class="nav-item">
<span class="nav-icon">📦</span>
<span>Registries</span>
</div>
<div class="nav-item">
<span class="nav-icon">📋</span>
<span>Logs</span>
<span class="nav-arrow"></span>
</div>
<div class="nav-item">
<span class="nav-icon">🔔</span>
<span>Notifications</span>
</div>
<div class="nav-item">
<span class="nav-icon">⚙️</span>
<span>Settings</span>
<span class="nav-arrow"></span>
</div>
</div>
</nav>
<div class="sidebar-footer">
Community Edition 2.21.3 LTS
</div>
</aside>
<!-- Main Content -->
<main class="content">
<div class="page-header">
<h1 class="page-title">
Home
<span style="color: var(--portainer-blue); cursor: pointer;"></span>
</h1>
<div class="page-actions">
<button class="icon-btn">🔔</button>
<button class="icon-btn"></button>
<div class="user-menu">
<div class="user-avatar">A</div>
<span>admin</span>
<span></span>
</div>
</div>
</div>
<!-- Environments Section -->
<div class="env-card">
<div class="card-header">
<div class="card-title">
<span style="font-size: 20px;">🖥️</span>
Environments
</div>
<div style="margin-left: auto; display: flex; gap: 8px;">
<button class="btn-refresh">↻ Refresh</button>
<button class="btn-refresh">⬇ Kubectl</button>
</div>
</div>
<div class="card-subtitle" style="margin-bottom: 16px;">
Click on an environment to manage
</div>
<!-- Filters -->
<div class="filters-bar">
<select class="filter-dropdown">
<option>Platform</option>
</select>
<select class="filter-dropdown">
<option>Connection Type</option>
</select>
<select class="filter-dropdown">
<option>Status</option>
</select>
<select class="filter-dropdown">
<option>Tags</option>
</select>
<select class="filter-dropdown">
<option>Groups</option>
</select>
<select class="filter-dropdown">
<option>Agent Version</option>
</select>
<button style="background: transparent; border: none; color: var(--text-secondary); cursor: pointer;">
Clear all
</button>
<div style="margin-left: auto; display: flex; gap: 8px;">
<select class="filter-dropdown" style="min-width: 100px;">
<option>Sort By</option>
</select>
<button class="btn-icon"></button>
</div>
</div>
<!-- Environment 1 -->
<div class="env-card" style="margin-bottom: 12px;">
<div style="display: flex; justify-content: space-between; align-items: flex-start;">
<div class="env-header" style="flex: 1; margin-bottom: 0;">
<div class="env-icon">🐳</div>
<div class="env-info">
<div class="env-name-row">
<span class="env-name">local</span>
<span class="env-status">Up</span>
<span style="color: var(--text-secondary); font-size: 12px;">↻ 2026-02-02 12:42:53</span>
<span style="color: var(--text-secondary); font-size: 12px;">Standalone 2.21.3</span>
<span class="env-path">/var/run/docker.sock</span>
</div>
<div class="env-meta">
<span class="env-meta-item">Group: <span>Unassigned</span></span>
<span class="env-meta-item">🏷️ No tags</span>
<span class="env-meta-item">⚡ Local</span>
</div>
</div>
</div>
<div class="env-actions">
<button class="btn btn-primary">📡 Live connect</button>
<button class="btn-icon">✏️</button>
<div style="text-align: right;">
<div class="connection-status">Disconnected</div>
<button class="btn-icon" style="margin-top: 4px;">⚙️</button>
</div>
</div>
</div>
<div class="env-stats">
<div class="stat-item">
<span class="stat-icon">📚</span>
<span class="stat-value">12</span>
<span class="stat-label">stacks</span>
</div>
<div class="stat-item">
<span class="stat-icon">📦</span>
<span class="stat-value">24</span>
<span class="stat-label">containers</span>
</div>
<div class="container-status">
<span class="status-dot running"></span>
<span>18</span>
<span class="status-dot stopped"></span>
<span>2</span>
<span class="status-dot" style="background: #ff9800;"></span>
<span>0</span>
<span class="status-dot" style="background: #9c27b0;"></span>
<span>0</span>
</div>
<div class="stat-item">
<span class="stat-icon">💾</span>
<span class="stat-value">32</span>
<span class="stat-label">volumes</span>
</div>
<div class="stat-item">
<span class="stat-icon">🖼️</span>
<span class="stat-value">87</span>
<span class="stat-label">images</span>
</div>
<div class="stat-item">
<span class="stat-icon">🖥️</span>
<span class="stat-value">4</span>
<span class="stat-label">CPU</span>
</div>
<div class="stat-item">
<span class="stat-icon">💽</span>
<span class="stat-value">7.8</span>
<span class="stat-label">GB RAM</span>
</div>
</div>
</div>
<!-- Environment 2 -->
<div class="env-card">
<div style="display: flex; justify-content: space-between; align-items: flex-start;">
<div class="env-header" style="flex: 1; margin-bottom: 0;">
<div class="env-icon">🐳</div>
<div class="env-info">
<div class="env-name-row">
<span class="env-name">Compute-01</span>
<span class="env-status">Up</span>
<span style="color: var(--text-secondary); font-size: 12px;">↻ 2026-02-02 12:42:53</span>
<span style="color: var(--text-secondary); font-size: 12px;">Standalone 2.21.3</span>
<span class="env-path">agent.local:9001</span>
</div>
<div class="env-meta">
<span class="env-meta-item">Group: <span>Unassigned</span></span>
<span class="env-meta-item">🏷️ No tags</span>
<span class="env-meta-item">⚡ Agent 2.21.3</span>
</div>
</div>
</div>
<div class="env-actions">
<button class="btn btn-primary">📡 Live connect</button>
<button class="btn-icon">✏️</button>
<div style="text-align: right;">
<div class="connection-status">Disconnected</div>
<button class="btn-icon" style="margin-top: 4px;">⚙️</button>
</div>
</div>
</div>
<div class="env-stats">
<div class="stat-item">
<span class="stat-icon">📚</span>
<span class="stat-value">5</span>
<span class="stat-label">stacks</span>
</div>
<div class="stat-item">
<span class="stat-icon">📦</span>
<span class="stat-value">16</span>
<span class="stat-label">containers</span>
</div>
<div class="container-status">
<span class="status-dot running"></span>
<span>14</span>
<span class="status-dot stopped"></span>
<span>1</span>
<span class="status-dot" style="background: #ff9800;"></span>
<span>0</span>
<span class="status-dot" style="background: #9c27b0;"></span>
<span>0</span>
</div>
<div class="stat-item">
<span class="stat-icon">💾</span>
<span class="stat-value">10</span>
<span class="stat-label">volumes</span>
</div>
<div class="stat-item">
<span class="stat-icon">🖼️</span>
<span class="stat-value">24</span>
<span class="stat-label">images</span>
</div>
<div class="stat-item">
<span class="stat-icon">🖥️</span>
<span class="stat-value">6</span>
<span class="stat-label">CPU</span>
</div>
<div class="stat-item">
<span class="stat-icon">💽</span>
<span class="stat-value">8.2</span>
<span class="stat-label">GB RAM</span>
</div>
</div>
</div>
</div>
</main>
</div>
</body>
</html>

624
services/proxmox.html Normal file
View File

@ -0,0 +1,624 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Proxmox Virtual Environment</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--bg-dark: #1e1e1e;
--bg-panel: #2d2d2d;
--bg-sidebar: #252525;
--bg-input: #3a3a3a;
--border-color: #404040;
--text-primary: #e0e0e0;
--text-secondary: #999;
--proxmox-orange: #e57000;
--proxmox-orange-light: #ff8c00;
--btn-blue: #3d85c6;
--btn-blue-hover: #2d6da3;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: var(--bg-dark);
color: var(--text-primary);
font-size: 13px;
height: 100vh;
overflow: hidden;
}
/* Demo Banner */
.demo-banner {
background: #1a1a1a;
color: var(--text-secondary);
text-align: center;
padding: 6px;
font-size: 12px;
border-bottom: 1px solid var(--border-color);
z-index: 1000;
position: relative;
}
.demo-banner a {
color: var(--proxmox-orange);
text-decoration: none;
}
/* Top Bar */
.top-bar {
background: linear-gradient(180deg, #3a3a3a 0%, #2d2d2d 100%);
border-bottom: 1px solid #1a1a1a;
height: 42px;
display: flex;
align-items: center;
padding: 0 8px;
gap: 12px;
}
.logo-area {
display: flex;
align-items: center;
gap: 8px;
padding: 0 8px;
}
.proxmox-logo {
display: flex;
align-items: center;
gap: 4px;
}
.logo-icon {
width: 28px;
height: 28px;
background: var(--proxmox-orange);
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
color: white;
font-size: 14px;
}
.logo-text {
font-size: 14px;
font-weight: 600;
color: var(--text-primary);
}
.logo-text span {
color: var(--proxmox-orange);
}
.search-box {
flex: 1;
max-width: 400px;
margin-left: 20px;
}
.search-box input {
width: 100%;
height: 28px;
background: #1e1e1e;
border: 1px solid #404040;
border-radius: 3px;
padding: 0 10px;
color: var(--text-secondary);
font-size: 12px;
}
.search-box input::placeholder {
color: #666;
}
.top-actions {
display: flex;
gap: 4px;
margin-left: auto;
}
.top-btn {
height: 28px;
padding: 0 12px;
background: linear-gradient(180deg, #4a4a4a 0%, #3a3a3a 100%);
border: 1px solid #505050;
border-radius: 3px;
color: var(--text-primary);
font-size: 12px;
cursor: pointer;
display: flex;
align-items: center;
gap: 6px;
}
.top-btn:hover {
background: linear-gradient(180deg, #5a5a5a 0%, #4a4a4a 100%);
}
.top-btn.primary {
background: var(--btn-blue);
border-color: #2d6da3;
}
.top-btn.primary:hover {
background: var(--btn-blue-hover);
}
.user-menu {
display: flex;
align-items: center;
gap: 4px;
padding: 0 8px;
}
/* Main Container */
.main-container {
display: flex;
height: calc(100vh - 90px);
}
/* Left Sidebar */
.sidebar {
width: 220px;
background: var(--bg-sidebar);
border-right: 1px solid var(--border-color);
display: flex;
flex-direction: column;
}
.sidebar-header {
display: flex;
align-items: center;
padding: 8px;
border-bottom: 1px solid var(--border-color);
gap: 8px;
}
.view-selector {
flex: 1;
height: 26px;
background: #3a3a3a;
border: 1px solid #505050;
border-radius: 3px;
color: var(--text-primary);
font-size: 12px;
padding: 0 8px;
}
.settings-btn {
width: 26px;
height: 26px;
background: #3a3a3a;
border: 1px solid #505050;
border-radius: 3px;
color: var(--text-primary);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
.sidebar-content {
flex: 1;
overflow-y: auto;
padding: 8px;
}
.tree-item {
display: flex;
align-items: center;
gap: 6px;
padding: 6px 8px;
color: var(--text-secondary);
cursor: pointer;
border-radius: 3px;
}
.tree-item:hover {
background: #333;
color: var(--text-primary);
}
.tree-icon {
width: 16px;
text-align: center;
}
/* Center Content - Login Area */
.content {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
background: var(--bg-dark);
position: relative;
}
/* Login Window */
.login-window {
background: #363636;
border: 1px solid #505050;
border-radius: 4px;
box-shadow: 0 4px 20px rgba(0,0,0,0.5);
width: 380px;
}
.login-header {
background: linear-gradient(180deg, #404040 0%, #363636 100%);
padding: 10px 15px;
border-bottom: 1px solid #505050;
font-size: 14px;
color: var(--btn-blue);
}
.login-body {
padding: 20px;
}
.form-row {
display: flex;
align-items: center;
margin-bottom: 12px;
gap: 10px;
}
.form-label {
width: 80px;
text-align: right;
color: var(--text-primary);
font-size: 13px;
}
.form-input-wrapper {
flex: 1;
position: relative;
}
.form-input {
width: 100%;
height: 30px;
background: var(--bg-input);
border: 1px solid #505050;
border-radius: 3px;
padding: 0 30px 0 10px;
color: var(--text-primary);
font-size: 13px;
}
.form-input:focus {
outline: none;
border-color: var(--btn-blue);
box-shadow: 0 0 0 1px var(--btn-blue);
}
.input-icon {
position: absolute;
right: 8px;
top: 50%;
transform: translateY(-50%);
color: var(--proxmox-orange);
font-size: 14px;
}
select.form-input {
cursor: pointer;
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23999' d='M6 8L1 3h10z'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 8px center;
padding-right: 28px;
}
.login-footer {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 12px;
padding-top: 8px;
border-top: 1px solid #505050;
margin-top: 8px;
}
.checkbox-label {
display: flex;
align-items: center;
gap: 6px;
color: var(--text-primary);
font-size: 12px;
cursor: pointer;
}
.checkbox-label input {
width: 14px;
height: 14px;
accent-color: var(--btn-blue);
}
.login-btn {
height: 28px;
padding: 0 20px;
background: var(--btn-blue);
border: 1px solid #2d6da3;
border-radius: 3px;
color: white;
font-size: 12px;
font-weight: 500;
cursor: pointer;
}
.login-btn:hover {
background: var(--btn-blue-hover);
}
/* Bottom Panel */
.bottom-panel {
height: 180px;
background: var(--bg-panel);
border-top: 1px solid var(--border-color);
display: flex;
flex-direction: column;
}
.panel-tabs {
display: flex;
background: #363636;
border-bottom: 1px solid var(--border-color);
}
.panel-tab {
padding: 8px 16px;
color: var(--text-secondary);
font-size: 12px;
cursor: pointer;
border-right: 1px solid var(--border-color);
}
.panel-tab:hover {
color: var(--text-primary);
}
.panel-tab.active {
background: var(--bg-panel);
color: var(--btn-blue);
border-bottom: 2px solid var(--btn-blue);
margin-bottom: -1px;
}
.panel-content {
flex: 1;
overflow: auto;
}
.data-table {
width: 100%;
border-collapse: collapse;
}
.data-table th {
text-align: left;
padding: 8px 12px;
color: var(--text-secondary);
font-size: 12px;
font-weight: normal;
border-bottom: 1px solid var(--border-color);
background: #2d2d2d;
}
.data-table th.sortable::after {
content: ' ↕';
font-size: 10px;
color: #666;
}
.data-table td {
padding: 8px 12px;
color: var(--text-secondary);
font-size: 12px;
border-bottom: 1px solid #333;
}
.data-table tr:hover td {
background: #333;
}
/* Scrollbar */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: var(--bg-dark);
}
::-webkit-scrollbar-thumb {
background: #505050;
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: #606060;
}
</style>
</head>
<body>
<div class="demo-banner">
🔒 This is a dummy login page for showcase purposes only. <a href="/">← Back to Dashboard</a>
</div>
<!-- Top Bar -->
<div class="top-bar">
<div class="logo-area">
<div class="proxmox-logo">
<div class="logo-icon">P</div>
<div class="logo-text"><span>PROX</span>MOX</div>
</div>
<div style="color: var(--text-secondary); font-size: 12px;">Virtual Environment</div>
</div>
<div class="search-box">
<input type="text" placeholder="Search">
</div>
<div class="top-actions">
<button class="top-btn">
<span>📄</span> Documentation
</button>
<button class="top-btn primary">
<span>🖥️</span> Create VM
</button>
<button class="top-btn primary">
<span>📦</span> Create CT
</button>
</div>
<div class="user-menu">
<button class="top-btn" style="width: 32px; padding: 0; justify-content: center;">
👤
</button>
</div>
</div>
<!-- Main Container -->
<div class="main-container">
<!-- Left Sidebar -->
<aside class="sidebar">
<div class="sidebar-header">
<select class="view-selector">
<option>Server View</option>
</select>
<button class="settings-btn">⚙️</button>
</div>
<div class="sidebar-content">
<div class="tree-item">
<span class="tree-icon"></span>
<span class="tree-icon">🏢</span>
<span>Datacenter</span>
</div>
</div>
</aside>
<!-- Center Content -->
<main class="content">
<!-- Login Window -->
<div class="login-window">
<div class="login-header">Proxmox VE Login</div>
<div class="login-body">
<form onsubmit="handleLogin(event)">
<div class="form-row">
<label class="form-label">User name:</label>
<div class="form-input-wrapper">
<input type="text" class="form-input" id="username" autocomplete="off">
<span class="input-icon">🔴</span>
</div>
</div>
<div class="form-row">
<label class="form-label">Password:</label>
<div class="form-input-wrapper">
<input type="password" class="form-input" id="password">
<span class="input-icon">🔴</span>
</div>
</div>
<div class="form-row">
<label class="form-label">Realm:</label>
<div class="form-input-wrapper">
<select class="form-input">
<option>Linux PAM standard authentication</option>
<option>Proxmox VE authentication server</option>
</select>
</div>
</div>
<div class="form-row">
<label class="form-label">Language:</label>
<div class="form-input-wrapper">
<select class="form-input">
<option>English - English</option>
<option>Deutsch - German</option>
<option>Français - French</option>
</select>
</div>
</div>
<div class="login-footer">
<label class="checkbox-label">
<input type="checkbox" checked>
Save User name:
</label>
<button type="submit" class="login-btn">Login</button>
</div>
</form>
</div>
</div>
</main>
</div>
<!-- Bottom Panel -->
<div class="bottom-panel">
<div class="panel-tabs">
<div class="panel-tab active">Tasks</div>
<div class="panel-tab">Cluster log</div>
</div>
<div class="panel-content">
<table class="data-table">
<thead>
<tr>
<th class="sortable">Start Time</th>
<th class="sortable">End Time</th>
<th>Node</th>
<th>User name</th>
<th>Description</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<tr>
<td>02/02/2026 12:21:42</td>
<td>-</td>
<td>pve</td>
<td>root@pam</td>
<td>Login</td>
<td>✓ OK</td>
</tr>
<tr>
<td>02/02/2026 12:20:15</td>
<td>02/02/2026 12:20:16</td>
<td>pve</td>
<td>root@pam</td>
<td>VM 100 - Start</td>
<td>✓ OK</td>
</tr>
<tr>
<td>02/02/2026 12:18:33</td>
<td>02/02/2026 12:18:34</td>
<td>pve</td>
<td>root@pam</td>
<td>VM 101 - Stop</td>
<td>✓ OK</td>
</tr>
</tbody>
</table>
</div>
</div>
<script>
function handleLogin(e) {
e.preventDefault();
alert('🔒 This is a demo page. The real Proxmox VE is not exposed.');
}
// Focus username on load
document.getElementById('username').focus();
</script>
</body>
</html>