This commit is contained in:
Federico Luzzi 2024-07-26 15:54:03 -03:00
parent 341a794ffb
commit 973cd977ba
7 changed files with 6135 additions and 20 deletions

21
app.py
View File

@ -11,6 +11,7 @@ DEFAULT_GROUP_PRIORITY = 100
DEFAULT_GROUP = 'Applications' DEFAULT_GROUP = 'Applications'
DEFAULT_GROUP_ICON = 'fas fa-box' DEFAULT_GROUP_ICON = 'fas fa-box'
DEFAULT_TITLE = 'Traefik Routers' DEFAULT_TITLE = 'Traefik Routers'
DEFAULT_ICON = 'fas fa-bars'
client = docker.from_env() client = docker.from_env()
@ -30,6 +31,7 @@ def filter_routers(routers, containers):
if not is_router_hidden(router['name'], containers): if not is_router_hidden(router['name'], containers):
router['description'] = get_router_description(router['name'], containers) router['description'] = get_router_description(router['name'], containers)
router['display_name'] = get_router_display_name(router['name'], containers) router['display_name'] = get_router_display_name(router['name'], containers)
router['icon'] = get_router_icon(router['name'], containers)
router['group'] = get_router_group(router['name'], containers) router['group'] = get_router_group(router['name'], containers)
filtered_routers.append(router) filtered_routers.append(router)
break break
@ -71,16 +73,17 @@ def get_router_group(router_name, containers):
return labels[group_label] return labels[group_label]
return DEFAULT_GROUP return DEFAULT_GROUP
def get_groups(containers): def get_router_icon(router_name, containers):
groups = { service_name = router_name.split('@')[0]
DEFAULT_GROUP: { for container in containers:
'priority': DEFAULT_GROUP_PRIORITY, labels = container.attrs.get('Config', {}).get('Labels', {})
'collapsed': False, icon_label = f'traefik-frontend.http.routers.{service_name}.icon'
'routers': [], if icon_label in labels:
'icon': DEFAULT_GROUP_ICON return labels[icon_label]
} return DEFAULT_ICON
}
def get_groups(containers):
groups = {DEFAULT_GROUP: {'priority': DEFAULT_GROUP_PRIORITY, 'collapsed': False, 'routers': [], 'icon': DEFAULT_GROUP_ICON}}
for container in containers: for container in containers:
labels = container.attrs.get('Config', {}).get('Labels', {}) labels = container.attrs.get('Config', {}).get('Labels', {})
for label, value in labels.items(): for label, value in labels.items():

View File

@ -30,6 +30,7 @@ services:
- "traefik-frontend.http.routers.connpy.group=Development" - "traefik-frontend.http.routers.connpy.group=Development"
- "traefik-frontend.http.routers.nginx.hidden=true" - "traefik-frontend.http.routers.nginx.hidden=true"
- "traefik-frontend.groups.Management.priority=1" - "traefik-frontend.groups.Management.priority=1"
- "traefik-frontend.groups.Management.collapsed=false"
- "traefik-frontend.groups.Management.icon=fas fa-tools" - "traefik-frontend.groups.Management.icon=fas fa-tools"
- "traefik-frontend.groups.Networking.priority=2" - "traefik-frontend.groups.Networking.priority=2"
- "traefik-frontend.groups.Networking.icon=fas fa-network-wired" - "traefik-frontend.groups.Networking.icon=fas fa-network-wired"

View File

@ -1,5 +1,5 @@
:root { :root {
--light-background: #f9f9f9; --light-background: #f0f0f0;
--dark-background: #121212; --dark-background: #121212;
--light-title-text: #e0e0e0; --light-title-text: #e0e0e0;
--light-icons: #333; --light-icons: #333;
@ -112,4 +112,11 @@ body {
.dark-mode .settings-menu .fa { .dark-mode .settings-menu .fa {
color: var(--dark-icons); color: var(--dark-icons);
} }
.router svg {
font-size: 26px;
color: var(--light-router-title);
}
.dark-mode .router svg {
color: var(--dark-router-title);
}

View File

@ -41,30 +41,75 @@
} }
.group { .group {
margin-bottom: 40px; margin-bottom: 20px; /* Reduced margin between groups */
} }
.group-title { .group-title {
display: inline-block; display: inline-block;
cursor: pointer; cursor: pointer;
margin-top: 10px; /* Reduced margin above group title */
} }
.group-icon { .group-icon {
margin-right: 8px; margin-right: 8px;
} }
.router-container {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
}
.router {
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
padding: 20px 15px; /* Added padding top and bottom */
margin: 15px; /* Added margin between boxes */
flex: 1 1 calc(33.333% - 40px); /* Adjusted for new margin */
max-width: calc(33.333% - 40px); /* Adjusted for new margin */
text-align: left;
cursor: pointer;
transition: box-shadow 0.3s ease;
box-sizing: border-box;
display: flex;
align-items: center;
}
.router svg {
font-size: 32px; /* Made the icon bigger */
margin-right: 15px; /* Added padding between icon and text */
}
.router:hover {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
.router h2 {
font-size: 18px;
margin: 0;
}
.router p {
font-size: 14px;
margin: 5px 0 0;
}
.content {
padding: 20px; /* Added padding to the left and right of the webpage */
}
/* Responsive Design */ /* Responsive Design */
@media (max-width: 1024px) { @media (max-width: 1024px) {
.router { .router {
flex: 1 1 calc(50% - 20px); flex: 1 1 calc(50% - 40px); /* Adjusted for new margin */
max-width: calc(50% - 20px); max-width: calc(50% - 40px); /* Adjusted for new margin */
} }
} }
@media (max-width: 768px) { @media (max-width: 768px) {
.router { .router {
flex: 1 1 calc(50% - 20px); flex: 1 1 calc(50% - 40px); /* Adjusted for new margin */
max-width: calc(50% - 20px); max-width: calc(50% - 40px); /* Adjusted for new margin */
} }
} }

6044
static/js/fontawesome.js Normal file

File diff suppressed because one or more lines are too long

View File

@ -8,6 +8,16 @@ document.addEventListener("DOMContentLoaded", function() {
document.getElementById('theme-icon').classList.add('fa-moon'); document.getElementById('theme-icon').classList.add('fa-moon');
} }
document.querySelectorAll('.group').forEach(group => {
const groupName = group.id;
const collapsed = group.dataset.collapsed === 'true';
if (collapsed) {
group.classList.add('collapsed');
} else if (localStorage.getItem(`collapsed-${groupName}`) === 'true') {
group.classList.add('collapsed');
}
});
document.addEventListener("click", function(event) { document.addEventListener("click", function(event) {
const settingsMenu = document.getElementById('settings-menu'); const settingsMenu = document.getElementById('settings-menu');
if (!settingsMenu.contains(event.target) && !event.target.closest('.icon-button')) { if (!settingsMenu.contains(event.target) && !event.target.closest('.icon-button')) {
@ -33,6 +43,8 @@ function toggleDarkMode() {
function toggleGroup(group) { function toggleGroup(group) {
const groupElement = document.getElementById(group); const groupElement = document.getElementById(group);
groupElement.classList.toggle("collapsed"); groupElement.classList.toggle("collapsed");
const isCollapsed = groupElement.classList.contains("collapsed");
localStorage.setItem(`collapsed-${group}`, isCollapsed);
} }
function toggleSettings() { function toggleSettings() {

View File

@ -4,10 +4,10 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ title }}</title> <title>{{ title }}</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
<link rel="stylesheet" href="{{ url_for('static', filename='css/colors.css') }}"> <link rel="stylesheet" href="{{ url_for('static', filename='css/colors.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}"> <link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/components.css') }}"> <link rel="stylesheet" href="{{ url_for('static', filename='css/components.css') }}">
<script src="{{ url_for('static', filename='js/fontawesome.js') }}" defer></script>
<script src="{{ url_for('static', filename='js/scripts.js') }}" defer></script> <script src="{{ url_for('static', filename='js/scripts.js') }}" defer></script>
</head> </head>
<body> <body>
@ -23,13 +23,16 @@
</div> </div>
<div class="content"> <div class="content">
{% for group, data in groups.items() %} {% for group, data in groups.items() %}
<div class="group {% if data.collapsed %}collapsed{% endif %}" id="{{ group }}"> <div class="group {% if data.collapsed %}collapsed{% endif %}" id="{{ group }}" data-collapsed="{{ data.collapsed }}">
<h2 class="group-title" onclick="toggleGroup('{{ group }}')"><i class="{{ data.icon }}"></i> {{ group }}</h2> <h2 class="group-title" onclick="toggleGroup('{{ group }}')"><i class="{{ data.icon }}"></i> {{ group }}</h2>
<div class="router-container"> <div class="router-container">
{% for router in data.routers %} {% for router in data.routers %}
<div class="router" onclick="location.href='http://{{ router['rule'].split('`')[1] }}'"> <div class="router" onclick="window.open('http://{{ router['rule'].split('`')[1] }}', '_blank')">
<h2>{{ router['display_name'] }}</h2> <i class="{{ router['icon'] }}"></i>
<p>{{ router['description'] }}</p> <div>
<h2>{{ router['display_name'] }}</h2>
<p>{{ router['description'] }}</p>
</div>
</div> </div>
{% endfor %} {% endfor %}
</div> </div>