root il y a 6 mois
Parent
commit
9f82eebead
1 fichiers modifiés avec 112 ajouts et 129 suppressions
  1. 112 129
      templates/layout.html

+ 112 - 129
templates/layout.html

@@ -1,135 +1,118 @@
-{% block content %}
-<div class="flex justify-between items-center mb-10">
-    <div>
-        <h2 class="text-4xl font-black text-white tracking-tighter italic">User Management</h2>
-        <p class="text-gray-500 font-bold uppercase text-[10px] tracking-widest mt-2">Manage access and RBAC roles</p>
-    </div>
-    <button onclick="openAddUser()" class="bg-blue-600 hover:bg-blue-500 px-8 py-3 rounded-xl font-black shadow-2xl transition transform hover:scale-105 text-white uppercase text-xs tracking-widest">+ Create User</button>
-</div>
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>BIND9 Pro Command Center</title>
+    <script src="https://cdn.tailwindcss.com"></script>
+    <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
+    <style>
+        body { background-color: #0b0e14; color: #ffffff; }
+        .custom-scroll::-webkit-scrollbar { width: 6px; height: 6px; }
+        .custom-scroll::-webkit-scrollbar-track { background: transparent; }
+        .custom-scroll::-webkit-scrollbar-thumb { background: #374151; border-radius: 10px; }
+        .custom-scroll::-webkit-scrollbar-thumb:hover { background: #4b5563; }
+        /* Αποτρέπει το text selection στα διπλά κλικ */
+        .no-select { user-select: none; }
+    </style>
+</head>
+<body class="flex h-screen overflow-hidden font-sans">
 
-<div class="bg-[#12161f] border border-gray-800 rounded-3xl overflow-hidden shadow-2xl">
-    <table class="w-full text-left">
-        <thead class="bg-black/20 text-[10px] font-black text-gray-500 uppercase tracking-widest border-b border-gray-800">
-            <tr>
-                <th class="p-6">Username</th>
-                <th class="p-6">Role</th>
-                <th class="p-6">Templates</th>
-                <th class="p-6 text-right">Actions</th>
-            </tr>
-        </thead>
-        <tbody class="divide-y divide-gray-800">
-            {% for u, data in users.items() %}
-            <tr class="hover:bg-white/[0.02] transition">
-                <td class="p-6 font-bold text-white">{{ u }}</td>
-                <td class="p-6">
-                    <span class="px-3 py-1 rounded-full text-[9px] font-black uppercase border {% if data.role=='admin' %}border-red-500/30 text-red-400 bg-red-500/10{% else %}border-blue-500/30 text-blue-400 bg-blue-500/10{% endif %}">
-                        {{ data.role }}
-                    </span>
-                </td>
-                <td class="p-6 text-xs text-gray-500">{{ data.assigned_templates|join(', ') or 'None' }}</td>
-                <td class="p-6 text-right">
-                    
-                    <button type="button" 
-                            data-user="{{ u }}" 
-                            data-role="{{ data.role }}" 
-                            data-templates="{{ data.assigned_templates|join(',') if data.assigned_templates else '' }}"
-                            onclick="editUser(this)" 
-                            class="text-blue-500 hover:text-blue-400 text-[10px] font-black uppercase mr-4">
-                        Edit
-                    </button>
-
-                    {% if u != 'admin' %}
-                    <a href="/users/delete/{{ u|urlencode }}" onclick="return confirm('Are you sure you want to delete this user?')" class="text-red-500 hover:text-red-400 text-[10px] font-black uppercase">Delete</a>
-                    {% endif %}
-                    
-                </td>
-            </tr>
-            {% endfor %}
-        </tbody>
-    </table>
-</div>
-
-<div id="userModal" class="hidden fixed inset-0 bg-black/90 backdrop-blur-md flex items-center justify-center z-50 p-4">
-    <div class="bg-[#12161f] border border-gray-800 w-full max-w-md rounded-3xl p-8 shadow-2xl">
-        <h3 id="modalTitle" class="text-xl font-black text-white mb-6 uppercase">Add User</h3>
-        <form action="/users/save" method="POST" class="space-y-4">
-            <input type="hidden" name="action" id="userAction" value="add">
-            <input type="hidden" name="old_username" id="oldUsername">
-            
-            <input type="text" name="username" id="userName" placeholder="Username" required class="w-full bg-[#0b0e14] border border-gray-800 rounded-xl p-3 text-sm font-bold text-white outline-none">
-            <input type="password" name="password" id="userPass" placeholder="Password (Leave blank to keep current)" class="w-full bg-[#0b0e14] border border-gray-800 rounded-xl p-3 text-sm font-bold text-white outline-none">
-            
-            <select name="role" id="userRole" class="w-full bg-[#0b0e14] border border-gray-800 rounded-xl p-3 text-sm font-bold text-white outline-none">
-                <option value="user">User (Restricted)</option>
-                <option value="admin">Administrator</option>
-            </select>
-
-            <div class="bg-black/20 p-4 rounded-xl border border-gray-800">
-                <label class="text-[10px] font-black text-gray-500 uppercase block mb-3">Assigned Templates</label>
-                <div class="grid grid-cols-2 gap-2 max-h-48 overflow-y-auto pr-2">
-                    {% for t in all_templates %}
-                    <label class="flex items-center gap-2 text-xs text-gray-400 cursor-pointer hover:text-white">
-                        <input type="checkbox" name="assigned_templates[]" value="{{ t }}" class="template-check rounded border-gray-700 bg-gray-900 text-blue-600">
-                        {{ t }}
-                    </label>
-                    {% endfor %}
+    <aside class="w-64 bg-[#12161f] border-r border-gray-800 flex flex-col shadow-2xl z-20">
+        <div class="p-6 border-b border-gray-800 flex items-center justify-center">
+            <h1 class="text-3xl font-black text-blue-500 italic tracking-tighter">BIND<span class="text-white">PRO</span></h1>
+        </div>
+        <div class="mt-auto pt-6 border-t border-gray-800">
+			<div class="flex items-center justify-center gap-2 text-gray-500 opacity-50 hover:opacity-100 transition-opacity">
+				<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path></svg>
+				<span class="text-[9px] font-mono font-black uppercase tracking-widest">{{ app_version }}</span>
+			</div>
+		</div>
+        <nav class="flex-1 p-4 space-y-2 overflow-y-auto custom-scroll mt-4">
+		
+            <p class="px-4 text-[10px] font-black text-gray-600 uppercase tracking-widest mb-2">Main Menu</p>
+            <a href="/" class="flex items-center gap-3 px-4 py-3 text-sm font-bold text-gray-400 rounded-xl hover:bg-blue-600 hover:text-white transition group">
+                <svg class="w-5 h-5 group-hover:scale-110 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z"></path></svg>
+                Dashboard
+            </a>
+            {% if session.get('role') == 'admin' %}
+            <a href="/analytics" class="flex items-center gap-3 px-4 py-3 text-sm font-bold text-gray-400 rounded-xl hover:bg-purple-600 hover:text-white transition group">
+                <svg class="w-5 h-5 group-hover:scale-110 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path></svg>
+                Analytics
+            </a>
+            <a href="/settings" class="flex items-center gap-3 px-4 py-3 text-sm font-bold text-gray-400 rounded-xl hover:bg-gray-700 hover:text-white transition group">
+                <svg class="w-5 h-5 group-hover:rotate-90 transition-transform duration-300" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"></path><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path></svg>
+                Settings
+            </a>
+            <a href="/users" class="flex items-center gap-3 px-4 py-3 text-sm font-bold text-gray-400 rounded-xl hover:bg-gray-700 hover:text-white transition group">
+                <svg class="w-5 h-5 group-hover:scale-110 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z"></path></svg>
+                Users
+            </a>
+			<a href="/adblock" class="flex items-center gap-3 px-4 py-3 text-sm font-bold text-gray-400 rounded-xl hover:bg-gray-700 hover:text-white transition group">
+				<svg class="w-5 h-5 group-hover:scale-110 transition-transform group-hover:text-red-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
+					<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.618 5.984A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016zM12 9v2m0 4h.01"></path>
+				</svg>
+				AdBlock Engine
+			</a>
+			<a href="/adanalytics" class="flex items-center gap-3 px-4 py-3 text-sm font-bold text-gray-400 rounded-xl hover:bg-gray-700 hover:text-white transition group">
+				<svg class="w-5 h-5 group-hover:scale-110 transition-transform group-hover:text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
+					<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path>
+				</svg>
+				AdBlock Analytics
+			</a>
+			<a href="/cluster" class="flex items-center gap-3 px-4 py-3 text-sm font-bold text-gray-400 rounded-xl hover:bg-blue-600 hover:text-white transition group">
+                <svg class="w-5 h-5 group-hover:scale-110 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"></path></svg>
+                Cluster Nodes
+            </a>
+			<a href="/profile" class="flex items-center gap-3 px-4 py-2 text-gray-400 hover:text-white transition">
+			<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"></path></svg>
+			<span class="text-xs font-black uppercase tracking-widest">My API</span>
+		</a>
+            {% endif %}
+        </nav>
+        
+        <div class="p-6 border-t border-gray-800 bg-gray-900/30">
+            <div class="flex items-center justify-between">
+                <div>
+                    <p class="text-[10px] text-gray-500 uppercase font-black tracking-widest">Logged in as</p>
+                    <p class="text-sm font-bold text-white">{{ session.get('username') }}</p>
                 </div>
+                <a href="/logout" title="Logout" class="bg-red-500/10 text-red-500 hover:bg-red-500 hover:text-white p-2 rounded-xl transition">
+                    <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"></path></svg>
+                </a>
             </div>
+        </div>
+    </aside>
 
-            <div class="flex gap-4 pt-4">
-                <button type="button" onclick="closeUserModal()" class="flex-1 bg-gray-800 text-gray-400 font-bold py-3 rounded-xl uppercase text-xs hover:text-white transition">Cancel</button>
-                <button type="submit" class="flex-1 bg-blue-600 text-white font-black py-3 rounded-xl uppercase text-xs hover:bg-blue-500 transition">Save</button>
-            </div>
-        </form>
-    </div>
-</div>
-
-<script>
-function openAddUser() {
-    document.getElementById('userModal').classList.remove('hidden');
-    document.getElementById('userAction').value = 'add';
-    document.getElementById('modalTitle').innerText = 'Add User';
-    document.getElementById('userName').value = '';
-    document.getElementById('userPass').value = '';
-    document.getElementById('userPass').placeholder = 'Password (Required)';
-    document.getElementById('userPass').required = true;
-    
-    var checkboxes = document.querySelectorAll('.template-check');
-    for (var i = 0; i < checkboxes.length; i++) {
-        checkboxes[i].checked = false;
-    }
-}
-
-function editUser(btn) {
-    // Διαβάζουμε τα δεδομένα με ασφάλεια από το κουμπί
-    var u = btn.getAttribute('data-user');
-    var role = btn.getAttribute('data-role');
-    var rawTemplates = btn.getAttribute('data-templates');
-    
-    // Σπάμε το "AWMN,LOCAL" σε λίστα
-    var templates = rawTemplates ? rawTemplates.split(',') : [];
-
-    // Εμφάνιση Modal και γέμισμα πεδίων
-    document.getElementById('userModal').classList.remove('hidden');
-    document.getElementById('userAction').value = 'edit';
-    document.getElementById('oldUsername').value = u;
-    document.getElementById('userName').value = u;
-    document.getElementById('userRole').value = role;
-    document.getElementById('modalTitle').innerText = 'Edit User';
-    
-    document.getElementById('userPass').value = '';
-    document.getElementById('userPass').placeholder = 'Password (Leave blank to keep current)';
-    document.getElementById('userPass').required = false; 
+    <main class="flex-1 overflow-y-auto custom-scroll relative bg-[#0b0e14]">
+        <div class="p-10 max-w-[1600px] mx-auto">
+            
+            {% with messages = get_flashed_messages(with_categories=true) %}
+              {% if messages %}
+                <script>
+                  document.addEventListener('DOMContentLoaded', function() {
+                    const Toast = Swal.mixin({ toast: true, position: 'bottom-end', showConfirmButton: false, timer: 4000, timerProgressBar: true, background: '#1f2937', color: '#fff' });
+                    {% for category, message in messages %}
+                        Toast.fire({ icon: '{{ "success" if category == "success" else "error" }}', title: '{{ message }}' });
+                    {% endfor %}
+                  });
+                </script>
+              {% endif %}
+            {% endwith %}
 
-    // Τσεκάρισμα των σωστών Templates
-    var checkboxes = document.querySelectorAll('.template-check');
-    for (var i = 0; i < checkboxes.length; i++) {
-        checkboxes[i].checked = templates.includes(checkboxes[i].value);
-    }
-}
+            {% block content %}{% endblock %}
+        </div>
+    </main>
 
-function closeUserModal() { 
-    document.getElementById('userModal').classList.add('hidden'); 
-}
-</script>
-{% endblock %}
+    <script>
+        const AppToast = Swal.mixin({
+            toast: true, position: 'bottom-end', showConfirmButton: false, 
+            timer: 3000, timerProgressBar: true, background: '#12161f', color: '#fff',
+            customClass: { popup: 'border border-gray-700 rounded-xl shadow-2xl' }
+        });
+        
+        // Loader Spinner SVG
+        const spinnerSvg = `<svg class="animate-spin -ml-1 mr-2 h-4 w-4 text-white inline" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>`;
+    </script>
+</body>
+</html>