general_config.html 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. {% extends "layout.html" %}
  2. {% block content %}
  3. <div class="flex justify-between items-end mb-10 border-b border-gray-800 pb-6">
  4. <div>
  5. <h2 class="text-4xl font-black text-white tracking-tighter italic">Settings</h2>
  6. <p class="text-gray-500 font-bold uppercase text-[10px] tracking-widest mt-2">BIND Engine & Data Retention</p>
  7. </div>
  8. <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>
  9. </div>
  10. <form id="mainForm" action="/settings/save" method="POST" class="space-y-12">
  11. <div class="grid grid-cols-1 xl:grid-cols-3 gap-10">
  12. <div class="xl:col-span-1 space-y-6">
  13. <div class="bg-[#12161f] border border-gray-800 rounded-2xl p-6 shadow-xl">
  14. <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>
  15. <div class="space-y-4">
  16. <div class="bg-black/20 p-4 rounded-xl border border-gray-800">
  17. <div class="flex justify-between mb-2">
  18. <label class="text-[10px] font-bold text-gray-500 uppercase">Listen IPv4 Interface</label>
  19. <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>
  20. </div>
  21. <div id="lv4Cont" class="space-y-2">
  22. {% 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 %}
  23. </div>
  24. </div>
  25. <div class="bg-black/20 p-4 rounded-xl border border-gray-800">
  26. <div class="flex justify-between mb-2">
  27. <label class="text-[10px] font-bold text-gray-500 uppercase">Allow Query From</label>
  28. <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>
  29. </div>
  30. <div id="aqCont" class="space-y-2">
  31. {% 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 %}
  32. </div>
  33. </div>
  34. <div class="bg-black/20 p-4 rounded-xl border border-gray-800">
  35. <div class="flex justify-between mb-2">
  36. <label class="text-[10px] font-bold text-gray-500 uppercase">Allow Transfers From</label>
  37. <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>
  38. </div>
  39. <div id="atCont" class="space-y-2">
  40. {% 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 %}
  41. </div>
  42. </div>
  43. <div class="bg-black/20 p-4 rounded-xl border border-gray-800">
  44. <div class="flex justify-between mb-2">
  45. <label class="text-[10px] font-bold text-gray-500 uppercase">Also Notify Slaves</label>
  46. <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>
  47. </div>
  48. <div id="anCont" class="space-y-2">
  49. {% 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 %}
  50. </div>
  51. </div>
  52. <div class="bg-black/20 p-4 rounded-xl border border-gray-800">
  53. <label class="text-[10px] font-bold text-gray-500 uppercase mb-2 block">DNSSEC Validation</label>
  54. <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">
  55. <option value="auto" {% if b_opt.dnssec == 'auto' %}selected{% endif %}>Auto</option>
  56. <option value="yes" {% if b_opt.dnssec == 'yes' %}selected{% endif %}>Yes</option>
  57. <option value="no" {% if b_opt.dnssec == 'no' %}selected{% endif %}>No</option>
  58. </select>
  59. </div>
  60. <div class="bg-black/20 p-4 rounded-xl border border-gray-800">
  61. <div class="bg-black/20 p-4 rounded-xl border border-gray-800">
  62. <label class="text-[10px] font-bold text-gray-500 uppercase mb-2 block">BIND Query Logging</label>
  63. <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">
  64. <option value="no" {% if p_data.get('query_log_enabled') == 'no' %}selected{% endif %}>Disabled</option>
  65. <option value="yes" {% if p_data.get('query_log_enabled') == 'yes' %}selected{% endif %}>Enabled (For Analytics)</option>
  66. </select>
  67. </div><br/>
  68. <div class="bg-black/20 p-4 rounded-xl border border-gray-800">
  69. <label class="text-[10px] font-bold text-gray-500 uppercase mb-2 block">Stats Retention (Days)</label>
  70. <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">
  71. </div>
  72. </div>
  73. </div>
  74. </div>
  75. </div>
  76. <div class="xl:col-span-2">
  77. <div class="bg-[#12161f] border border-gray-800 rounded-2xl overflow-hidden shadow-2xl">
  78. <div class="p-6 border-b border-gray-800 flex justify-between items-center bg-gray-900/30">
  79. <h3 class="text-lg font-black text-green-500 uppercase tracking-tighter italic">Zone Templates</h3>
  80. <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>
  81. </div>
  82. <table class="w-full text-left">
  83. <thead class="bg-black/20 text-[10px] font-black text-gray-500 uppercase tracking-widest">
  84. <tr>
  85. <th class="p-4 pl-6">Name</th>
  86. <th class="p-4">Default TTL</th>
  87. <th class="p-4">Refresh</th>
  88. <th class="p-4 text-right pr-6">Actions</th>
  89. </tr>
  90. </thead>
  91. <tbody class="divide-y divide-gray-800" id="templatesTableBody">
  92. {% for name, t in p_data.templates.items() %}
  93. {% set idx = loop.index0 %}
  94. <tr class="hover:bg-white/[0.02] transition" id="row-{{ idx }}">
  95. <td class="p-4 pl-6 text-green-400 font-black">{{ name }}</td>
  96. <td class="p-4 text-gray-400 font-mono text-xs">{{ t.ttl }}s</td>
  97. <td class="p-4 text-gray-400 font-mono text-xs">{{ t.refresh }}s</td>
  98. <td class="p-4 text-right pr-6 whitespace-nowrap">
  99. <button type="button"
  100. data-name="{{ name|e }}"
  101. data-idx="{{ idx }}"
  102. onclick="editTemplate(this.getAttribute('data-name'), this.getAttribute('data-idx'))"
  103. class="text-blue-400 text-[10px] font-black uppercase mr-4">Edit</button>
  104. <button type="button" onclick="this.closest('tr').remove()" class="text-red-500 text-[10px] font-black uppercase">Delete</button>
  105. <div class="hidden" id="data-container-{{ idx }}">
  106. <input type="hidden" name="t_name[]" value="{{ name }}">
  107. <input type="hidden" name="t_refresh[]" value="{{ t.refresh }}">
  108. <input type="hidden" name="t_retry[]" value="{{ t.retry }}">
  109. <input type="hidden" name="t_expire[]" value="{{ t.expire }}">
  110. <input type="hidden" name="t_ttl[]" value="{{ t.ttl }}">
  111. <textarea name="t_content[]" class="hidden">{{ t.content|e }}</textarea>
  112. <div id="vars-storage-{{ idx }}">
  113. {% for vk, vv in t.custom_vars.items() %}
  114. <input type="hidden" name="custom_key_{{ idx }}[]" value="{{ vk|e }}">
  115. <input type="hidden" name="custom_val_{{ idx }}[]" value="{{ vv|e }}">
  116. {% endfor %}
  117. </div>
  118. </div>
  119. </td>
  120. </tr>
  121. {% endfor %}
  122. </tbody>
  123. </table>
  124. </div>
  125. </div>
  126. </div>
  127. </form>
  128. <div id="templateModal" class="hidden fixed inset-0 bg-black/95 backdrop-blur-md flex items-center justify-center z-50 p-6">
  129. <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]">
  130. <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>
  131. <div class="grid grid-cols-1 lg:grid-cols-5 gap-6 mb-8">
  132. <div class="lg:col-span-2">
  133. <label class="text-[10px] font-black text-gray-500 uppercase mb-2 block ml-1">Template Label</label>
  134. <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">
  135. </div>
  136. <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>
  137. <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>
  138. <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>
  139. </div>
  140. <div class="grid grid-cols-1 lg:grid-cols-2 gap-8 mb-8">
  141. <div class="bg-black/30 p-6 rounded-2xl border border-gray-800">
  142. <div class="flex justify-between items-center mb-4">
  143. <span class="text-[10px] font-black text-gray-500 uppercase">Custom Variables</span>
  144. <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>
  145. </div>
  146. <div id="modal_vars_container" class="space-y-2"></div>
  147. </div>
  148. <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>
  149. </div>
  150. <div class="flex gap-4">
  151. <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>
  152. <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>
  153. </div>
  154. </div>
  155. </div>
  156. <script>
  157. let currentEditIdx = -1;
  158. function addAcl(id, name) {
  159. const cont = document.getElementById(id);
  160. const div = document.createElement('div'); div.className = 'flex gap-2 mt-2';
  161. 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>`;
  162. cont.appendChild(div);
  163. }
  164. function openAddTemplateModal() {
  165. currentEditIdx = -1;
  166. document.getElementById('modal_t_name').value = "";
  167. document.getElementById('modal_t_refresh').value = "3600";
  168. document.getElementById('modal_t_retry').value = "1800";
  169. document.getElementById('modal_t_ttl').value = "86400";
  170. 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}.";
  171. document.getElementById('modal_vars_container').innerHTML = '';
  172. document.getElementById('templateModal').classList.remove('hidden');
  173. }
  174. function editTemplate(name, idx) {
  175. currentEditIdx = idx;
  176. const cont = document.getElementById(`data-container-${idx}`);
  177. document.getElementById('modal_t_name').value = name;
  178. document.getElementById('modal_t_ttl').value = cont.querySelector('[name="t_ttl[]"]').value;
  179. document.getElementById('modal_t_refresh').value = cont.querySelector('[name="t_refresh[]"]').value;
  180. document.getElementById('modal_t_retry').value = cont.querySelector('[name="t_retry[]"]').value;
  181. document.getElementById('modal_t_content').value = cont.querySelector('textarea[name="t_content[]"]').value;
  182. document.getElementById('modal_vars_container').innerHTML = '';
  183. const keys = cont.querySelectorAll(`[name^="custom_key_"]`);
  184. const vals = cont.querySelectorAll(`[name^="custom_val_"]`);
  185. for(let i = 0; i < keys.length; i++) {
  186. addModalVar(keys[i].value, vals[i].value);
  187. }
  188. document.getElementById('templateModal').classList.remove('hidden');
  189. }
  190. function addModalVar(k, v) {
  191. const cont = document.getElementById('modal_vars_container');
  192. const div = document.createElement('div'); div.className = 'flex gap-2 mt-2';
  193. const safeK = k.replace(/"/g, '&quot;');
  194. const safeV = v.replace(/"/g, '&quot;');
  195. 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>`;
  196. cont.appendChild(div);
  197. }
  198. function closeModal() { document.getElementById('templateModal').classList.add('hidden'); }
  199. function applyToTable() {
  200. const tName = document.getElementById('modal_t_name').value.trim();
  201. if(!tName) return alert("Template name is required!");
  202. const tRefresh = document.getElementById('modal_t_refresh').value || "3600";
  203. const tRetry = document.getElementById('modal_t_retry').value || "1800";
  204. const tTtl = document.getElementById('modal_t_ttl').value || "86400";
  205. const tContent = document.getElementById('modal_t_content').value;
  206. let idx = currentEditIdx;
  207. if(idx === -1) {
  208. idx = 'new_' + Date.now();
  209. }
  210. let varInputs = '';
  211. const mKeys = document.querySelectorAll('.modal-var-key');
  212. const mVals = document.querySelectorAll('.modal-var-val');
  213. for(let i=0; i<mKeys.length; i++){
  214. if(mKeys[i].value.trim()){
  215. const safeKey = mKeys[i].value.replace(/"/g, '&quot;');
  216. const safeVal = mVals[i].value.replace(/"/g, '&quot;');
  217. varInputs += `<input type="hidden" name="custom_key_${idx}[]" value="${safeKey}">
  218. <input type="hidden" name="custom_val_${idx}[]" value="${safeVal}">`;
  219. }
  220. }
  221. // Η ΔΙΟΡΘΩΣΗ ΕΔΩ: Ασφαλής περασμένη μεταβλητή μέσω data-attributes και για τα νέα templates!
  222. const safeNameAttr = tName.replace(/"/g, '&quot;');
  223. const trHtml = `
  224. <td class="p-4 pl-6 text-green-400 font-black">${tName}</td>
  225. <td class="p-4 text-gray-400 font-mono text-xs">${tTtl}s</td>
  226. <td class="p-4 text-gray-400 font-mono text-xs">${tRefresh}s</td>
  227. <td class="p-4 text-right pr-6 whitespace-nowrap">
  228. <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>
  229. <button type="button" onclick="this.closest('tr').remove()" class="text-red-500 text-[10px] font-black uppercase">Delete</button>
  230. <div class="hidden" id="data-container-${idx}">
  231. <input type="hidden" name="t_name[]" value="${tName}">
  232. <input type="hidden" name="t_refresh[]" value="${tRefresh}">
  233. <input type="hidden" name="t_retry[]" value="${tRetry}">
  234. <input type="hidden" name="t_expire[]" value="604800">
  235. <input type="hidden" name="t_ttl[]" value="${tTtl}">
  236. <textarea name="t_content[]" class="hidden"></textarea>
  237. <div id="vars-storage-${idx}">${varInputs}</div>
  238. </div>
  239. </td>
  240. `;
  241. let tr;
  242. if(currentEditIdx === -1) {
  243. tr = document.createElement('tr');
  244. tr.className = "hover:bg-white/[0.02] transition";
  245. tr.id = `row-${idx}`;
  246. tr.innerHTML = trHtml;
  247. document.getElementById('templatesTableBody').appendChild(tr);
  248. } else {
  249. tr = document.getElementById(`row-${idx}`);
  250. tr.innerHTML = trHtml;
  251. }
  252. // Ασφαλής εισαγωγή του περιεχομένου στο textarea
  253. tr.querySelector('textarea[name="t_content[]"]').value = tContent;
  254. closeModal();
  255. }
  256. function saveAllSettings() {
  257. const containers = document.querySelectorAll('tbody tr div[id^="data-container-"]');
  258. containers.forEach((cont, index) => {
  259. const keys = cont.querySelectorAll('input[name^="custom_key_"]');
  260. keys.forEach(k => k.name = `custom_key_${index}[]`);
  261. const vals = cont.querySelectorAll('input[name^="custom_val_"]');
  262. vals.forEach(v => v.name = `custom_val_${index}[]`);
  263. });
  264. document.getElementById('mainForm').submit();
  265. }
  266. </script>
  267. {% endblock %}