adblock.html 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. {% extends "layout.html" %}
  2. {% block content %}
  3. <div class="max-w-6xl mx-auto p-4 md:p-8">
  4. <div class="flex flex-col md:flex-row justify-between items-start md:items-center mb-10 border-b border-gray-800 pb-6 gap-4">
  5. <div>
  6. <h2 class="text-3xl font-black text-white tracking-tighter italic flex items-center gap-3">
  7. <svg class="w-8 h-8 text-red-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"></path></svg>
  8. AdBlock Engine (RPZ)
  9. </h2>
  10. <p class="text-gray-500 font-bold mt-2 text-[10px] md:text-xs uppercase tracking-widest">Network-Wide Security & Filtering</p>
  11. </div>
  12. <div class="flex flex-wrap gap-3 w-full md:w-auto">
  13. {% if rpz_enabled %}
  14. <button onclick="disableAdBlock()" id="btnDisable" class="flex-1 md:flex-none bg-red-600/10 text-red-500 hover:bg-red-600 hover:text-white px-6 py-3 rounded-xl font-black uppercase text-[10px] tracking-widest transition border border-red-500/20 flex items-center justify-center gap-2">
  15. <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg> Disable
  16. </button>
  17. {% endif %}
  18. <button onclick="updateAdBlock()" id="btnUpdate" class="flex-1 md:flex-none bg-blue-600 hover:bg-blue-500 text-white px-8 py-3 rounded-xl font-black uppercase text-[10px] tracking-widest transition shadow-xl shadow-blue-900/20 flex items-center justify-center gap-2">
  19. <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path></svg> Sync & Apply
  20. </button>
  21. <button onclick="syncClusterAdblock()" id="btnClusterSync" class="flex-1 md:flex-none bg-purple-600/20 text-purple-400 hover:bg-purple-600 hover:text-white px-6 py-3 rounded-xl font-black uppercase text-[10px] tracking-widest transition border border-purple-500/30 flex items-center justify-center gap-2">
  22. <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01"></path></svg> Cluster Sync
  23. </button>
  24. </div>
  25. </div>
  26. <div class="grid grid-cols-1 md:grid-cols-2 gap-8 mb-10">
  27. <div class="bg-[#12161f] border-2 {% if rpz_enabled %}border-green-500/30{% else %}border-gray-800{% endif %} rounded-3xl p-8 flex flex-col items-center justify-center text-center transition-colors relative overflow-hidden group shadow-2xl">
  28. {% if rpz_enabled %}
  29. <div class="absolute inset-0 bg-green-500/5 opacity-0 group-hover:opacity-100 transition duration-500"></div>
  30. <div class="w-20 h-20 bg-green-500/20 text-green-500 rounded-full flex items-center justify-center mb-6 shadow-[0_0_30px_rgba(34,197,94,0.3)] animate-pulse relative z-10">
  31. <svg class="w-10 h-10" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m5.618-4.016A11.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.016z"></path></svg>
  32. </div>
  33. <h3 class="text-3xl font-black text-green-400 uppercase tracking-widest mb-2 relative z-10">Protection Active</h3>
  34. <p class="text-gray-400 text-sm relative z-10">BIND RPZ is actively intercepting bad domains.</p>
  35. {% else %}
  36. <div class="w-20 h-20 bg-gray-800 text-gray-500 rounded-full flex items-center justify-center mb-6 relative z-10">
  37. <svg class="w-10 h-10" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"></path></svg>
  38. </div>
  39. <h3 class="text-3xl font-black text-gray-500 uppercase tracking-widest mb-2 relative z-10">Protection Offline</h3>
  40. <p class="text-gray-400 text-sm relative z-10">Toggle your preferred lists below and click Apply.</p>
  41. {% endif %}
  42. </div>
  43. <div class="bg-[#12161f] border border-gray-800 rounded-3xl p-8 flex flex-col justify-center shadow-2xl relative overflow-hidden">
  44. <div class="mb-8 relative z-10">
  45. <p class="text-xs font-black text-gray-500 uppercase tracking-widest mb-2">Domains in Blocklist</p>
  46. <p class="text-6xl font-black text-white font-mono tracking-tighter" id="domainsCount">{{ "{:,}".format(settings.adblock_domains_count) }}</p>
  47. </div>
  48. <div class="relative z-10">
  49. <p class="text-xs font-black text-gray-500 uppercase tracking-widest mb-2">Last Sync Time</p>
  50. <p class="text-base font-bold text-blue-400" id="lastUpdate">{{ settings.adblock_last_update }}</p>
  51. </div>
  52. </div>
  53. </div>
  54. <div class="flex gap-6 border-b border-gray-800 mb-8 overflow-x-auto whitespace-nowrap scrollbar-hide">
  55. <button onclick="switchAdTab('lists')" id="adTabBtn_lists" class="pb-4 text-[11px] font-black uppercase tracking-widest text-blue-500 border-b-2 border-blue-500 transition focus:outline-none">Filter Lists</button>
  56. <button onclick="switchAdTab('custom')" id="adTabBtn_custom" class="pb-4 text-[11px] font-black uppercase tracking-widest text-gray-500 border-b-2 border-transparent hover:text-gray-300 transition focus:outline-none">Custom Rules (Allow/Deny)</button>
  57. </div>
  58. <div id="adSection_lists" class="ad-section">
  59. <div class="bg-[#12161f] border border-gray-800 rounded-3xl p-8 shadow-2xl">
  60. <div class="flex justify-between items-center mb-8 border-b border-gray-800 pb-4">
  61. <h3 class="text-2xl font-black text-white uppercase italic tracking-tighter">Blocklist Sources</h3>
  62. <button onclick="document.getElementById('addCustomModal').classList.remove('hidden')" class="text-[10px] bg-gray-800 hover:bg-gray-700 text-white px-4 py-2 rounded-lg font-black uppercase tracking-widest transition flex items-center gap-2">
  63. <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"></path></svg> Custom List
  64. </button>
  65. </div>
  66. {% set categories = ['General', 'Security', 'Other', 'Regional', 'Custom'] %}
  67. {% for cat in categories %}
  68. {% set cat_lists = adblock_lists | selectattr('category', 'equalto', cat) | list %}
  69. {% if cat_lists %}
  70. <div class="mb-10 last:mb-0">
  71. <h4 class="text-xs font-black {% if cat == 'Security' %}text-red-500{% elif cat == 'General' %}text-blue-500{% elif cat == 'Regional' %}text-yellow-500{% else %}text-purple-500{% endif %} uppercase tracking-widest mb-4 flex items-center gap-2">
  72. {{ cat }} Filters
  73. </h4>
  74. <div class="grid grid-cols-1 xl:grid-cols-2 gap-4">
  75. {% for lst in cat_lists %}
  76. <div class="flex items-center justify-between bg-[#0b0e14] p-5 rounded-2xl border border-gray-800/60 hover:border-gray-600 transition group relative overflow-hidden">
  77. <div class="flex-1 pr-4 z-10">
  78. <h5 class="text-base font-bold text-gray-200 group-hover:text-white transition">{{ lst.name }}</h5>
  79. <p class="text-[11px] text-gray-500 mt-1 mb-3 line-clamp-2 leading-relaxed">{{ lst.desc | default('Custom user list.') }}</p>
  80. <div class="flex gap-3">
  81. <a href="{{ lst.url }}" target="_blank" class="text-[9px] text-blue-500/70 hover:text-blue-400 font-mono tracking-widest truncate max-w-[200px] inline-block flex items-center gap-1"><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="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"></path></svg> List source</a>
  82. {% if lst.info %}
  83. <a href="{{ lst.info }}" target="_blank" class="text-[9px] text-gray-500 hover:text-gray-300 font-mono tracking-widest flex items-center gap-1"><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="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg> Info</a>
  84. {% endif %}
  85. </div>
  86. </div>
  87. <div class="flex flex-col items-end justify-between h-full z-10 gap-3">
  88. <label class="relative inline-flex items-center cursor-pointer">
  89. <input type="checkbox" onchange="toggleList('{{ lst.id }}', this.checked)" class="sr-only peer" {% if lst.enabled %}checked{% endif %}>
  90. <div class="w-11 h-6 bg-gray-800 border border-gray-700 peer-focus:outline-none rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-gray-400 peer-checked:after:bg-white after:border-gray-500 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-green-500 peer-checked:border-green-500"></div>
  91. </label>
  92. {% if lst.is_custom %}
  93. <div class="flex gap-3 mt-2">
  94. <button onclick="openEditCustomModal('{{ lst.id }}', '{{ lst.name|escape }}', '{{ lst.desc|escape }}', '{{ lst.url|escape }}')" class="text-blue-500 hover:text-blue-400 text-[10px] uppercase font-black transition flex items-center gap-1">
  95. <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="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"></path></svg> Edit
  96. </button>
  97. <a href="/api/adblock/delete_list/{{ lst.id }}" onclick="return confirm('Delete this custom list?')" class="text-red-500 hover:text-red-400 text-[10px] uppercase font-black transition flex items-center gap-1">
  98. <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="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path></svg> Del
  99. </a>
  100. </div>
  101. {% endif %}
  102. </div>
  103. </div>
  104. {% endfor %}
  105. </div>
  106. </div>
  107. {% endif %}
  108. {% endfor %}
  109. </div>
  110. </div>
  111. <div id="adSection_custom" class="ad-section hidden">
  112. <div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
  113. <div class="bg-[#12161f] p-8 rounded-3xl border border-gray-800 shadow-2xl">
  114. <h3 class="text-xl font-black text-green-500 uppercase italic mb-6 flex items-center gap-2">
  115. <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg> Allowed Domains (Whitelist)
  116. </h3>
  117. <form onsubmit="addRule(event, 'allowed')" class="flex gap-2 mb-6">
  118. <input type="text" id="input_allowed" required placeholder="domain.tld or *.domain.tld" class="flex-1 bg-[#0b0e14] border border-gray-800 rounded-xl px-4 py-3 text-white font-mono text-xs outline-none focus:border-green-500 transition">
  119. <button type="submit" class="bg-green-600 hover:bg-green-500 text-white px-6 rounded-xl font-black uppercase text-[10px] shadow-lg transition flex items-center justify-center min-w-[80px]">Add</button>
  120. </form>
  121. <div class="bg-gray-900/30 p-4 rounded-xl mb-6 text-[10px] text-gray-400 border border-gray-800">
  122. <p class="font-black text-gray-500 mb-1">EXAMPLES:</p>
  123. <p class="font-mono">google.com <span class="mx-2 opacity-30 text-white">|</span> *.youtube.com <span class="mx-2 opacity-30 text-white">|</span> api.github.com</p>
  124. </div>
  125. <div class="space-y-2 h-[350px] overflow-y-auto custom-scroll pr-2">
  126. {% if custom_rules.allowed %}
  127. {% for d in custom_rules.allowed %}
  128. <div class="flex justify-between items-center bg-[#0b0e14] border border-gray-800 p-3 rounded-xl">
  129. <span class="text-green-400 font-mono text-[11px]">{{ d }}</span>
  130. <div class="flex gap-4">
  131. <button onclick="editRule('{{ d }}', 'allowed')" class="text-blue-500 hover:text-blue-400 transition text-[10px] font-black uppercase flex items-center gap-1" title="Edit">
  132. <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="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"></path></svg> Edit
  133. </button>
  134. <button onclick="deleteRule('{{ d }}', 'allowed')" class="text-red-500 hover:text-red-400 transition text-[10px] font-black uppercase flex items-center gap-1" title="Delete">
  135. <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="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path></svg> Delete
  136. </button>
  137. </div>
  138. </div>
  139. {% endfor %}
  140. {% else %}
  141. <div class="text-center p-10 border-2 border-dashed border-gray-800 rounded-xl text-gray-600 font-bold text-[10px] uppercase tracking-widest mt-4">No allowed domains.</div>
  142. {% endif %}
  143. </div>
  144. </div>
  145. <div class="bg-[#12161f] p-8 rounded-3xl border border-gray-800 shadow-2xl">
  146. <h3 class="text-xl font-black text-red-500 uppercase italic mb-6 flex items-center gap-2">
  147. <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728A9 9 0 015.636 5.636m12.728 12.728L5.636 5.636"></path></svg> Deny Domains (Blacklist)
  148. </h3>
  149. <form onsubmit="addRule(event, 'deny')" class="flex gap-2 mb-6">
  150. <input type="text" id="input_deny" required placeholder="domain.tld or *.domain.tld" class="flex-1 bg-[#0b0e14] border border-gray-800 rounded-xl px-4 py-3 text-white font-mono text-xs outline-none focus:border-red-500 transition">
  151. <button type="submit" class="bg-red-600 hover:bg-red-500 text-white px-6 rounded-xl font-black uppercase text-[10px] shadow-lg transition flex items-center justify-center min-w-[80px]">Block</button>
  152. </form>
  153. <div class="bg-gray-900/30 p-4 rounded-xl mb-6 text-[10px] text-gray-400 border border-gray-800">
  154. <p class="font-black text-gray-500 mb-1">EXAMPLES:</p>
  155. <p class="font-mono">tiktok.com <span class="mx-2 opacity-30 text-white">|</span> *.analytics.io <span class="mx-2 opacity-30 text-white">|</span> tracking.net</p>
  156. </div>
  157. <div class="space-y-2 h-[350px] overflow-y-auto custom-scroll pr-2">
  158. {% if custom_rules.deny %}
  159. {% for d in custom_rules.deny %}
  160. <div class="flex justify-between items-center bg-[#0b0e14] border border-gray-800 p-3 rounded-xl">
  161. <span class="text-red-400 font-mono text-[11px]">{{ d }}</span>
  162. <div class="flex gap-4">
  163. <button onclick="editRule('{{ d }}', 'deny')" class="text-blue-500 hover:text-blue-400 transition text-[10px] font-black uppercase flex items-center gap-1" title="Edit">
  164. <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="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"></path></svg> Edit
  165. </button>
  166. <button onclick="deleteRule('{{ d }}', 'deny')" class="text-red-500 hover:text-red-400 transition text-[10px] font-black uppercase flex items-center gap-1" title="Delete">
  167. <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="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path></svg> Delete
  168. </button>
  169. </div>
  170. </div>
  171. {% endfor %}
  172. {% else %}
  173. <div class="text-center p-10 border-2 border-dashed border-gray-800 rounded-xl text-gray-600 font-bold text-[10px] uppercase tracking-widest mt-4">No blocked domains.</div>
  174. {% endif %}
  175. </div>
  176. </div>
  177. </div>
  178. </div>
  179. </div>
  180. <div id="addCustomModal" class="hidden fixed inset-0 bg-black/90 backdrop-blur-md z-50 flex items-center justify-center p-4">
  181. <div class="bg-[#12161f] border border-gray-800 w-full max-w-lg rounded-3xl p-8 shadow-2xl">
  182. <h3 class="text-2xl font-black text-white mb-6 uppercase border-b border-gray-800 pb-4">Add Custom URL</h3>
  183. <form action="/api/adblock/add_custom" method="POST" class="space-y-5">
  184. <div>
  185. <label class="block text-[10px] font-black text-gray-500 uppercase mb-2">List Name</label>
  186. <input type="text" name="name" required placeholder="e.g. My Anti-Malware List" class="w-full bg-[#0b0e14] border border-gray-800 rounded-xl p-4 text-white outline-none focus:border-blue-500 transition font-bold">
  187. </div>
  188. <div>
  189. <label class="block text-[10px] font-black text-gray-500 uppercase mb-2">Description (Optional)</label>
  190. <input type="text" name="desc" placeholder="A short description of this list..." class="w-full bg-[#0b0e14] border border-gray-800 rounded-xl p-4 text-white outline-none focus:border-blue-500 transition text-sm">
  191. </div>
  192. <div>
  193. <label class="block text-[10px] font-black text-gray-500 uppercase mb-2">Direct URL (.txt)</label>
  194. <input type="url" name="url" required placeholder="https://example.com/list.txt" class="w-full bg-[#0b0e14] border border-gray-800 rounded-xl p-4 text-white font-mono text-sm outline-none focus:border-blue-500 transition">
  195. <p class="text-[9px] text-gray-500 font-bold mt-2 uppercase tracking-widest">Supports Hosts or Adblock format.</p>
  196. </div>
  197. <div class="flex gap-4 pt-4 border-t border-gray-800">
  198. <button type="button" onclick="document.getElementById('addCustomModal').classList.add('hidden')" class="flex-1 bg-gray-800 text-gray-400 hover:text-white transition font-bold py-4 rounded-xl uppercase text-xs">Cancel</button>
  199. <button type="submit" class="flex-1 bg-blue-600 hover:bg-blue-500 transition text-white font-black py-4 rounded-xl uppercase text-xs shadow-xl shadow-blue-900/20">Add List</button>
  200. </div>
  201. </form>
  202. </div>
  203. </div>
  204. <div id="editCustomModal" class="hidden fixed inset-0 bg-black/90 backdrop-blur-md z-50 flex items-center justify-center p-4">
  205. <div class="bg-[#12161f] border border-gray-800 w-full max-w-lg rounded-3xl p-8 shadow-2xl">
  206. <h3 class="text-2xl font-black text-blue-400 mb-6 uppercase border-b border-gray-800 pb-4">Edit Custom List</h3>
  207. <form action="/api/adblock/edit_custom" method="POST" class="space-y-5">
  208. <input type="hidden" name="list_id" id="editListId">
  209. <div>
  210. <label class="block text-[10px] font-black text-gray-500 uppercase mb-2">List Name</label>
  211. <input type="text" name="name" id="editListName" required class="w-full bg-[#0b0e14] border border-gray-800 rounded-xl p-4 text-white outline-none focus:border-blue-500 transition font-bold">
  212. </div>
  213. <div>
  214. <label class="block text-[10px] font-black text-gray-500 uppercase mb-2">Description</label>
  215. <input type="text" name="desc" id="editListDesc" class="w-full bg-[#0b0e14] border border-gray-800 rounded-xl p-4 text-white outline-none focus:border-blue-500 transition text-sm">
  216. </div>
  217. <div>
  218. <label class="block text-[10px] font-black text-gray-500 uppercase mb-2">Direct URL (.txt)</label>
  219. <input type="url" name="url" id="editListUrl" required class="w-full bg-[#0b0e14] border border-gray-800 rounded-xl p-4 text-white font-mono text-sm outline-none focus:border-blue-500 transition">
  220. </div>
  221. <div class="flex gap-4 pt-4 border-t border-gray-800">
  222. <button type="button" onclick="document.getElementById('editCustomModal').classList.add('hidden')" class="flex-1 bg-gray-800 text-gray-400 hover:text-white transition font-bold py-4 rounded-xl uppercase text-xs">Cancel</button>
  223. <button type="submit" class="flex-1 bg-blue-600 hover:bg-blue-500 transition text-white font-black py-4 rounded-xl uppercase text-xs shadow-xl shadow-blue-900/20">Save</button>
  224. </div>
  225. </form>
  226. </div>
  227. </div>
  228. <script>
  229. // --- TAB SWITCHER ---
  230. function switchAdTab(tab) {
  231. document.querySelectorAll('.ad-section').forEach(el => el.classList.add('hidden'));
  232. document.querySelectorAll('[id^="adTabBtn_"]').forEach(btn => {
  233. btn.classList.remove('text-blue-500', 'border-blue-500');
  234. btn.classList.add('text-gray-500', 'border-transparent');
  235. });
  236. const activeSection = document.getElementById('adSection_' + tab);
  237. if (activeSection) activeSection.classList.remove('hidden');
  238. const activeBtn = document.getElementById('adTabBtn_' + tab);
  239. if (activeBtn) {
  240. activeBtn.classList.remove('text-gray-500', 'border-transparent');
  241. activeBtn.classList.add('text-blue-500', 'border-blue-500');
  242. }
  243. }
  244. // Προεπιλογή: Φόρτωσε το tab των Lists
  245. document.addEventListener("DOMContentLoaded", () => {
  246. switchAdTab('lists');
  247. });
  248. // Ασφαλής κλήση για το AppToast
  249. const showToast = (opts) => {
  250. if (typeof AppToast !== 'undefined') AppToast.fire(opts);
  251. else console.log(opts.title);
  252. };
  253. // --- AJAX DOM REFRESHER ---
  254. // Αυτή η συνάρτηση διαβάζει αθόρυβα τα νέα δεδομένα από τον server
  255. // και ανανεώνει ΜΟΝΟ το Custom Tab, χωρίς να γίνει reload η σελίδα!
  256. function refreshCustomRulesAJAX() {
  257. return fetch('/adblock')
  258. .then(res => res.text())
  259. .then(html => {
  260. const parser = new DOMParser();
  261. const doc = parser.parseFromString(html, 'text/html');
  262. const newCustomSection = doc.getElementById('adSection_custom').innerHTML;
  263. document.getElementById('adSection_custom').innerHTML = newCustomSection;
  264. // Ενημερώνουμε και τον γενικό μετρητή των domains αν υπάρχει
  265. const newDomainsCount = doc.getElementById('domainsCount');
  266. if (newDomainsCount && document.getElementById('domainsCount')) {
  267. document.getElementById('domainsCount').innerText = newDomainsCount.innerText;
  268. }
  269. });
  270. }
  271. // --- ADBLOCK LOGIC ---
  272. function toggleList(id, state) {
  273. fetch('/api/adblock/toggle_list', {
  274. method: 'POST',
  275. headers: {'Content-Type': 'application/json'},
  276. body: JSON.stringify({id: id, state: state})
  277. });
  278. }
  279. function openEditCustomModal(id, name, desc, url) {
  280. document.getElementById('editListId').value = id;
  281. document.getElementById('editListName').value = name;
  282. document.getElementById('editListDesc').value = desc;
  283. document.getElementById('editListUrl').value = url;
  284. document.getElementById('editCustomModal').classList.remove('hidden');
  285. }
  286. function updateAdBlock() {
  287. const btn = document.getElementById('btnUpdate');
  288. const originalText = btn.innerHTML;
  289. btn.innerHTML = '<svg class="w-4 h-4 animate-spin" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path></svg> Compiling...';
  290. btn.disabled = true;
  291. btn.classList.add('opacity-50', 'cursor-not-allowed');
  292. fetch('/api/adblock/update', { method: 'POST' })
  293. .then(r => r.json())
  294. .then(data => {
  295. if(data.status === 'success') {
  296. showToast({ icon: 'success', title: `Synced ${data.count} unique domains!` });
  297. setTimeout(() => window.location.reload(), 1500); // Εδώ το reload έχει νόημα γιατί ανανεώνει όλο το panel
  298. } else {
  299. showToast({ icon: 'error', title: 'Error', text: data.message });
  300. btn.innerHTML = originalText;
  301. btn.disabled = false;
  302. btn.classList.remove('opacity-50', 'cursor-not-allowed');
  303. }
  304. }).catch(err => {
  305. showToast({ icon: 'error', title: 'Connection Error' });
  306. btn.innerHTML = originalText;
  307. btn.disabled = false;
  308. btn.classList.remove('opacity-50', 'cursor-not-allowed');
  309. });
  310. }
  311. function disableAdBlock() {
  312. if(!confirm("Disable AdBlock Engine? Your network will no longer be protected from ads/trackers.")) return;
  313. const btn = document.getElementById('btnDisable');
  314. btn.innerHTML = '<svg class="w-4 h-4 animate-spin" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path></svg> Disabling...';
  315. btn.disabled = true;
  316. fetch('/api/adblock/disable', { method: 'POST' })
  317. .then(r => r.json())
  318. .then(data => {
  319. if(data.status === 'success') {
  320. showToast({ icon: 'success', title: 'AdBlock Disabled' });
  321. setTimeout(() => window.location.reload(), 1000);
  322. }
  323. });
  324. }
  325. function syncClusterAdblock() {
  326. const btn = document.getElementById('btnClusterSync');
  327. const originalText = btn.innerHTML;
  328. btn.innerHTML = '<svg class="w-4 h-4 animate-spin" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path></svg> Syncing...';
  329. btn.disabled = true;
  330. fetch('/api/adblock/sync_cluster', { method: 'POST' })
  331. .then(r => r.json())
  332. .then(data => {
  333. showToast({ icon: 'success', title: data.message });
  334. btn.innerHTML = originalText;
  335. btn.disabled = false;
  336. }).catch(err => {
  337. showToast({ icon: 'error', title: 'Cluster Sync Failed' });
  338. btn.innerHTML = originalText;
  339. btn.disabled = false;
  340. });
  341. }
  342. // --- PURE AJAX CRUD FOR CUSTOM RULES ---
  343. function addRule(e, type) {
  344. e.preventDefault();
  345. const input = document.getElementById('input_' + type);
  346. const domain = input.value.trim();
  347. if(!domain) return;
  348. const btn = e.target.querySelector('button');
  349. const originalText = btn.innerText;
  350. btn.innerHTML = '...';
  351. btn.disabled = true;
  352. const fd = new FormData();
  353. fd.append('type', type);
  354. fd.append('domain', domain);
  355. fetch('/api/adblock/rules/add', { method: 'POST', body: fd })
  356. .then(() => fetch('/api/adblock/update', { method: 'POST' }))
  357. .then(() => refreshCustomRulesAJAX())
  358. .then(() => {
  359. showToast({ icon: 'success', title: `Domain added to ${type} list` });
  360. // Το input καθαρίζει μόνο του επειδή γίνεται αντικατάσταση του DOM!
  361. });
  362. }
  363. function editRule(oldDomain, type) {
  364. let newDomain = prompt("Edit domain:", oldDomain);
  365. if(newDomain !== null && newDomain.trim() !== "" && newDomain.trim() !== oldDomain) {
  366. const fd = new FormData();
  367. fd.append('type', type);
  368. fd.append('old_domain', oldDomain);
  369. fd.append('new_domain', newDomain.trim());
  370. showToast({ title: 'Updating & Syncing...', icon: 'info', timer: 2000, showConfirmButton: false });
  371. fetch('/api/adblock/rules/edit', { method: 'POST', body: fd })
  372. .then(() => fetch('/api/adblock/update', { method: 'POST' }))
  373. .then(() => refreshCustomRulesAJAX())
  374. .then(() => {
  375. showToast({ icon: 'success', title: 'Rule updated' });
  376. });
  377. }
  378. }
  379. function deleteRule(domain, type) {
  380. if(!confirm(`Remove ${domain} from ${type} list?`)) return;
  381. const fd = new FormData();
  382. fd.append('type', type);
  383. fd.append('domain', domain);
  384. showToast({ title: 'Removing & Syncing...', icon: 'info', timer: 2000, showConfirmButton: false });
  385. fetch('/api/adblock/rules/delete', { method: 'POST', body: fd })
  386. .then(() => fetch('/api/adblock/update', { method: 'POST' }))
  387. .then(() => refreshCustomRulesAJAX())
  388. .then(() => {
  389. showToast({ icon: 'success', title: 'Rule removed' });
  390. });
  391. }
  392. </script>
  393. {% endblock %}