| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303 |
- {% extends "layout.html" %}
- {% block content %}
- <div class="flex justify-between items-end mb-10 border-b border-gray-800 pb-6">
- <div>
- <h2 class="text-4xl font-black text-white tracking-tighter italic">Settings</h2>
- <p class="text-gray-500 font-bold uppercase text-[10px] tracking-widest mt-2">BIND Engine & Data Retention</p>
- </div>
- <button onclick="saveAllSettings()" class="bg-blue-600 hover:bg-blue-500 px-10 py-3 rounded-xl font-black shadow-2xl transition transform hover:scale-[1.05] text-white uppercase tracking-widest text-xs">Save All Changes</button>
- </div>
- <form id="mainForm" action="/settings/save" method="POST" class="space-y-12">
- <div class="grid grid-cols-1 xl:grid-cols-3 gap-10">
-
- <div class="xl:col-span-1 space-y-6">
- <div class="bg-[#12161f] border border-gray-800 rounded-2xl p-6 shadow-xl">
- <h3 class="text-lg font-black text-blue-500 uppercase mb-6 tracking-tighter italic border-b border-gray-800 pb-2">Global Bind Engine</h3>
-
- <div class="space-y-4">
- <div class="bg-black/20 p-4 rounded-xl border border-gray-800">
- <div class="flex justify-between mb-2">
- <label class="text-[10px] font-bold text-gray-500 uppercase">Listen IPv4 Interface</label>
- <button type="button" onclick="addAcl('lv4Cont', 'listen_v4[]')" class="text-blue-400 font-bold text-[9px] bg-blue-400/10 px-2 py-0.5 rounded border border-blue-400/20">+ ADD</button>
- </div>
- <div id="lv4Cont" class="space-y-2">
- {% for item in b_opt.listen_v4 %}<div class="flex gap-2"><input type="text" name="listen_v4[]" value="{{ item }}" class="w-full bg-[#0b0e14] border border-gray-800 rounded-lg p-2 text-xs text-white font-mono outline-none"><button type="button" onclick="this.parentElement.remove()" class="text-red-500 font-black px-3">×</button></div>{% endfor %}
- </div>
- </div>
- <div class="bg-black/20 p-4 rounded-xl border border-gray-800">
- <div class="flex justify-between mb-2">
- <label class="text-[10px] font-bold text-gray-500 uppercase">Allow Query From</label>
- <button type="button" onclick="addAcl('aqCont', 'allow_query[]')" class="text-blue-400 font-bold text-[9px] bg-blue-400/10 px-2 py-0.5 rounded border border-blue-400/20">+ ADD</button>
- </div>
- <div id="aqCont" class="space-y-2">
- {% for item in b_opt.allow_query %}<div class="flex gap-2"><input type="text" name="allow_query[]" value="{{ item }}" class="w-full bg-[#0b0e14] border border-gray-800 rounded-lg p-2 text-xs text-white font-mono outline-none"><button type="button" onclick="this.parentElement.remove()" class="text-red-500 font-black px-3">×</button></div>{% endfor %}
- </div>
- </div>
- <div class="bg-black/20 p-4 rounded-xl border border-gray-800">
- <div class="flex justify-between mb-2">
- <label class="text-[10px] font-bold text-gray-500 uppercase">Allow Transfers From</label>
- <button type="button" onclick="addAcl('atCont', 'allow_transfer[]')" class="text-blue-400 font-bold text-[9px] bg-blue-400/10 px-2 py-0.5 rounded border border-blue-400/20">+ ADD</button>
- </div>
- <div id="atCont" class="space-y-2">
- {% for item in b_opt.allow_transfer %}<div class="flex gap-2"><input type="text" name="allow_transfer[]" value="{{ item }}" class="w-full bg-[#0b0e14] border border-gray-800 rounded-lg p-2 text-xs text-white font-mono outline-none"><button type="button" onclick="this.parentElement.remove()" class="text-red-500 font-black px-3">×</button></div>{% endfor %}
- </div>
- </div>
- <div class="bg-black/20 p-4 rounded-xl border border-gray-800">
- <div class="flex justify-between mb-2">
- <label class="text-[10px] font-bold text-gray-500 uppercase">Also Notify Slaves</label>
- <button type="button" onclick="addAcl('anCont', 'also_notify[]')" class="text-blue-400 font-bold text-[9px] bg-blue-400/10 px-2 py-0.5 rounded border border-blue-400/20">+ ADD</button>
- </div>
- <div id="anCont" class="space-y-2">
- {% for item in b_opt.also_notify %}<div class="flex gap-2"><input type="text" name="also_notify[]" value="{{ item }}" class="w-full bg-[#0b0e14] border border-gray-800 rounded-lg p-2 text-xs text-white font-mono outline-none"><button type="button" onclick="this.parentElement.remove()" class="text-red-500 font-black px-3">×</button></div>{% endfor %}
- </div>
- </div>
- <div class="bg-black/20 p-4 rounded-xl border border-gray-800">
- <label class="text-[10px] font-bold text-gray-500 uppercase mb-2 block">DNSSEC Validation</label>
- <select name="dnssec" class="w-full bg-[#0b0e14] border border-gray-800 rounded-lg p-2 text-sm text-white font-bold outline-none cursor-pointer">
- <option value="auto" {% if b_opt.dnssec == 'auto' %}selected{% endif %}>Auto</option>
- <option value="yes" {% if b_opt.dnssec == 'yes' %}selected{% endif %}>Yes</option>
- <option value="no" {% if b_opt.dnssec == 'no' %}selected{% endif %}>No</option>
- </select>
- </div>
- <div class="bg-black/20 p-4 rounded-xl border border-gray-800">
- <div class="bg-black/20 p-4 rounded-xl border border-gray-800">
- <label class="text-[10px] font-bold text-gray-500 uppercase mb-2 block">BIND Query Logging</label>
- <select name="query_log_enabled" class="w-full bg-[#0b0e14] border border-gray-800 rounded-lg p-2 text-sm text-white font-bold outline-none cursor-pointer">
- <option value="no" {% if p_data.get('query_log_enabled') == 'no' %}selected{% endif %}>Disabled</option>
- <option value="yes" {% if p_data.get('query_log_enabled') == 'yes' %}selected{% endif %}>Enabled (For Analytics)</option>
- </select>
- </div><br/>
- <div class="bg-black/20 p-4 rounded-xl border border-gray-800">
- <label class="text-[10px] font-bold text-gray-500 uppercase mb-2 block">Stats Retention (Days)</label>
- <input type="number" name="stats_retention" value="{{ p_data.get('stats_retention', 30) }}" min="1" max="365" class="w-full bg-[#0b0e14] border border-gray-800 rounded-lg p-2 text-sm text-white font-bold outline-none">
- </div>
- </div>
- </div>
- </div>
- </div>
- <div class="xl:col-span-2">
- <div class="bg-[#12161f] border border-gray-800 rounded-2xl overflow-hidden shadow-2xl">
- <div class="p-6 border-b border-gray-800 flex justify-between items-center bg-gray-900/30">
- <h3 class="text-lg font-black text-green-500 uppercase tracking-tighter italic">Zone Templates</h3>
- <button type="button" onclick="openAddTemplateModal()" class="bg-green-600/20 text-green-400 px-4 py-2 rounded-xl text-[10px] font-black uppercase border border-green-500/30 hover:bg-green-600/40 transition">+ New Template</button>
- </div>
-
- <table class="w-full text-left">
- <thead class="bg-black/20 text-[10px] font-black text-gray-500 uppercase tracking-widest">
- <tr>
- <th class="p-4 pl-6">Name</th>
- <th class="p-4">Default TTL</th>
- <th class="p-4">Refresh</th>
- <th class="p-4 text-right pr-6">Actions</th>
- </tr>
- </thead>
- <tbody class="divide-y divide-gray-800" id="templatesTableBody">
- {% for name, t in p_data.templates.items() %}
- {% set idx = loop.index0 %}
- <tr class="hover:bg-white/[0.02] transition" id="row-{{ idx }}">
- <td class="p-4 pl-6 text-green-400 font-black">{{ name }}</td>
- <td class="p-4 text-gray-400 font-mono text-xs">{{ t.ttl }}s</td>
- <td class="p-4 text-gray-400 font-mono text-xs">{{ t.refresh }}s</td>
- <td class="p-4 text-right pr-6 whitespace-nowrap">
- <button type="button"
- data-name="{{ name|e }}"
- data-idx="{{ idx }}"
- onclick="editTemplate(this.getAttribute('data-name'), this.getAttribute('data-idx'))"
- class="text-blue-400 text-[10px] font-black uppercase mr-4">Edit</button>
-
- <button type="button" onclick="this.closest('tr').remove()" class="text-red-500 text-[10px] font-black uppercase">Delete</button>
-
- <div class="hidden" id="data-container-{{ idx }}">
- <input type="hidden" name="t_name[]" value="{{ name }}">
- <input type="hidden" name="t_refresh[]" value="{{ t.refresh }}">
- <input type="hidden" name="t_retry[]" value="{{ t.retry }}">
- <input type="hidden" name="t_expire[]" value="{{ t.expire }}">
- <input type="hidden" name="t_ttl[]" value="{{ t.ttl }}">
- <textarea name="t_content[]" class="hidden">{{ t.content|e }}</textarea>
- <div id="vars-storage-{{ idx }}">
- {% for vk, vv in t.custom_vars.items() %}
- <input type="hidden" name="custom_key_{{ idx }}[]" value="{{ vk|e }}">
- <input type="hidden" name="custom_val_{{ idx }}[]" value="{{ vv|e }}">
- {% endfor %}
- </div>
- </div>
- </td>
- </tr>
- {% endfor %}
- </tbody>
- </table>
- </div>
- </div>
- </div>
- </form>
- <div id="templateModal" class="hidden fixed inset-0 bg-black/95 backdrop-blur-md flex items-center justify-center z-50 p-6">
- <div class="bg-[#12161f] border border-gray-800 w-full max-w-5xl rounded-3xl p-8 shadow-2xl overflow-y-auto max-h-[90vh]">
- <h3 id="modalTitle" class="text-2xl font-black text-white mb-8 uppercase tracking-tighter italic border-b border-gray-800 pb-4">Zone Template Editor</h3>
-
- <div class="grid grid-cols-1 lg:grid-cols-5 gap-6 mb-8">
- <div class="lg:col-span-2">
- <label class="text-[10px] font-black text-gray-500 uppercase mb-2 block ml-1">Template Label</label>
- <input type="text" id="modal_t_name" class="w-full bg-[#0b0e14] border border-gray-800 rounded-xl p-4 text-green-400 font-black outline-none">
- </div>
- <div><label class="text-[10px] font-black text-gray-500 uppercase block mb-2">Refresh</label><input type="number" id="modal_t_refresh" class="w-full bg-[#0b0e14] border border-gray-800 rounded-xl p-4 text-white outline-none"></div>
- <div><label class="text-[10px] font-black text-gray-500 uppercase block mb-2">Retry</label><input type="number" id="modal_t_retry" class="w-full bg-[#0b0e14] border border-gray-800 rounded-xl p-4 text-white outline-none"></div>
- <div><label class="text-[10px] font-black text-gray-500 uppercase block mb-2">TTL</label><input type="number" id="modal_t_ttl" class="w-full bg-[#0b0e14] border border-gray-800 rounded-xl p-4 text-white outline-none"></div>
- </div>
- <div class="grid grid-cols-1 lg:grid-cols-2 gap-8 mb-8">
- <div class="bg-black/30 p-6 rounded-2xl border border-gray-800">
- <div class="flex justify-between items-center mb-4">
- <span class="text-[10px] font-black text-gray-500 uppercase">Custom Variables</span>
- <button type="button" onclick="addModalVar('', '')" class="text-[9px] bg-gray-800 px-3 py-1 rounded-lg text-white font-bold hover:text-blue-400 transition">+ Add Var</button>
- </div>
- <div id="modal_vars_container" class="space-y-2"></div>
- </div>
- <textarea id="modal_t_content" class="w-full bg-[#0b0e14] border border-gray-800 rounded-2xl p-6 font-mono text-xs text-green-500 min-h-[300px] outline-none leading-relaxed"></textarea>
- </div>
- <div class="flex gap-4">
- <button type="button" onclick="closeModal()" class="flex-1 bg-gray-800 text-gray-400 font-bold py-4 rounded-xl uppercase text-xs hover:text-white transition">Cancel</button>
- <button type="button" onclick="applyToTable()" class="flex-1 bg-blue-600 text-white font-black py-4 rounded-xl uppercase text-xs hover:bg-blue-500 transition">Apply to Table</button>
- </div>
- </div>
- </div>
- <script>
- let currentEditIdx = -1;
- function addAcl(id, name) {
- const cont = document.getElementById(id);
- const div = document.createElement('div'); div.className = 'flex gap-2 mt-2';
- div.innerHTML = `<input type="text" name="${name}" class="w-full bg-[#0b0e14] border border-gray-800 rounded-lg p-2 text-xs text-white font-mono"><button type="button" onclick="this.parentElement.remove()" class="text-red-500 font-black px-3">×</button>`;
- cont.appendChild(div);
- }
- function openAddTemplateModal() {
- currentEditIdx = -1;
- document.getElementById('modal_t_name').value = "";
- document.getElementById('modal_t_refresh').value = "3600";
- document.getElementById('modal_t_retry').value = "1800";
- document.getElementById('modal_t_ttl').value = "86400";
- document.getElementById('modal_t_content').value = "$TTL {TTL}\n@ IN SOA ns1.{DOMAIN}. admin.{DOMAIN}. (\n {SERIAL}\n {REFRESH}\n {RETRY}\n 604800\n {TTL}\n)\n@ IN NS ns1.{DOMAIN}.";
- document.getElementById('modal_vars_container').innerHTML = '';
- document.getElementById('templateModal').classList.remove('hidden');
- }
- function editTemplate(name, idx) {
- currentEditIdx = idx;
- const cont = document.getElementById(`data-container-${idx}`);
- document.getElementById('modal_t_name').value = name;
- document.getElementById('modal_t_ttl').value = cont.querySelector('[name="t_ttl[]"]').value;
- document.getElementById('modal_t_refresh').value = cont.querySelector('[name="t_refresh[]"]').value;
- document.getElementById('modal_t_retry').value = cont.querySelector('[name="t_retry[]"]').value;
- document.getElementById('modal_t_content').value = cont.querySelector('textarea[name="t_content[]"]').value;
-
- document.getElementById('modal_vars_container').innerHTML = '';
- const keys = cont.querySelectorAll(`[name^="custom_key_"]`);
- const vals = cont.querySelectorAll(`[name^="custom_val_"]`);
- for(let i = 0; i < keys.length; i++) {
- addModalVar(keys[i].value, vals[i].value);
- }
- document.getElementById('templateModal').classList.remove('hidden');
- }
- function addModalVar(k, v) {
- const cont = document.getElementById('modal_vars_container');
- const div = document.createElement('div'); div.className = 'flex gap-2 mt-2';
- const safeK = k.replace(/"/g, '"');
- const safeV = v.replace(/"/g, '"');
-
- div.innerHTML = `<input type="text" placeholder="KEY" value="${safeK}" class="modal-var-key w-1/3 bg-[#0b0e14] border border-gray-800 rounded-lg p-2 text-xs text-white font-bold outline-none focus:border-blue-500"><input type="text" placeholder="Value" value="${safeV}" class="modal-var-val flex-1 bg-[#0b0e14] border border-gray-800 rounded-lg p-2 text-xs text-white outline-none focus:border-blue-500"><button type="button" onclick="this.parentElement.remove()" class="text-red-500 font-black px-3">×</button>`;
- cont.appendChild(div);
- }
- function closeModal() { document.getElementById('templateModal').classList.add('hidden'); }
- function applyToTable() {
- const tName = document.getElementById('modal_t_name').value.trim();
- if(!tName) return alert("Template name is required!");
-
- const tRefresh = document.getElementById('modal_t_refresh').value || "3600";
- const tRetry = document.getElementById('modal_t_retry').value || "1800";
- const tTtl = document.getElementById('modal_t_ttl').value || "86400";
- const tContent = document.getElementById('modal_t_content').value;
-
- let idx = currentEditIdx;
- if(idx === -1) {
- idx = 'new_' + Date.now();
- }
-
- let varInputs = '';
- const mKeys = document.querySelectorAll('.modal-var-key');
- const mVals = document.querySelectorAll('.modal-var-val');
- for(let i=0; i<mKeys.length; i++){
- if(mKeys[i].value.trim()){
- const safeKey = mKeys[i].value.replace(/"/g, '"');
- const safeVal = mVals[i].value.replace(/"/g, '"');
- varInputs += `<input type="hidden" name="custom_key_${idx}[]" value="${safeKey}">
- <input type="hidden" name="custom_val_${idx}[]" value="${safeVal}">`;
- }
- }
- // Η ΔΙΟΡΘΩΣΗ ΕΔΩ: Ασφαλής περασμένη μεταβλητή μέσω data-attributes και για τα νέα templates!
- const safeNameAttr = tName.replace(/"/g, '"');
-
- const trHtml = `
- <td class="p-4 pl-6 text-green-400 font-black">${tName}</td>
- <td class="p-4 text-gray-400 font-mono text-xs">${tTtl}s</td>
- <td class="p-4 text-gray-400 font-mono text-xs">${tRefresh}s</td>
- <td class="p-4 text-right pr-6 whitespace-nowrap">
- <button type="button" data-name="${safeNameAttr}" data-idx="${idx}" onclick="editTemplate(this.getAttribute('data-name'), this.getAttribute('data-idx'))" class="text-blue-400 text-[10px] font-black uppercase mr-4">Edit</button>
- <button type="button" onclick="this.closest('tr').remove()" class="text-red-500 text-[10px] font-black uppercase">Delete</button>
-
- <div class="hidden" id="data-container-${idx}">
- <input type="hidden" name="t_name[]" value="${tName}">
- <input type="hidden" name="t_refresh[]" value="${tRefresh}">
- <input type="hidden" name="t_retry[]" value="${tRetry}">
- <input type="hidden" name="t_expire[]" value="604800">
- <input type="hidden" name="t_ttl[]" value="${tTtl}">
- <textarea name="t_content[]" class="hidden"></textarea>
- <div id="vars-storage-${idx}">${varInputs}</div>
- </div>
- </td>
- `;
- let tr;
- if(currentEditIdx === -1) {
- tr = document.createElement('tr');
- tr.className = "hover:bg-white/[0.02] transition";
- tr.id = `row-${idx}`;
- tr.innerHTML = trHtml;
- document.getElementById('templatesTableBody').appendChild(tr);
- } else {
- tr = document.getElementById(`row-${idx}`);
- tr.innerHTML = trHtml;
- }
-
- // Ασφαλής εισαγωγή του περιεχομένου στο textarea
- tr.querySelector('textarea[name="t_content[]"]').value = tContent;
-
- closeModal();
- }
- function saveAllSettings() {
- const containers = document.querySelectorAll('tbody tr div[id^="data-container-"]');
- containers.forEach((cont, index) => {
- const keys = cont.querySelectorAll('input[name^="custom_key_"]');
- keys.forEach(k => k.name = `custom_key_${index}[]`);
- const vals = cont.querySelectorAll('input[name^="custom_val_"]');
- vals.forEach(v => v.name = `custom_val_${index}[]`);
- });
-
- document.getElementById('mainForm').submit();
- }
- </script>
- {% endblock %}
|