| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435 |
- {% extends "layout.html" %}
- {% block content %}
- <div class="max-w-6xl mx-auto p-4 md:p-8">
- <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">
- <div>
- <h2 class="text-3xl font-black text-white tracking-tighter italic flex items-center gap-3">
- <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>
- AdBlock Engine (RPZ)
- </h2>
- <p class="text-gray-500 font-bold mt-2 text-[10px] md:text-xs uppercase tracking-widest">Network-Wide Security & Filtering</p>
- </div>
- <div class="flex flex-wrap gap-3 w-full md:w-auto">
- {% if rpz_enabled %}
- <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">
- <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
- </button>
- {% endif %}
- <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">
- <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
- </button>
- <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">
- <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
- </button>
- </div>
- </div>
- <div class="grid grid-cols-1 md:grid-cols-2 gap-8 mb-10">
- <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">
- {% if rpz_enabled %}
- <div class="absolute inset-0 bg-green-500/5 opacity-0 group-hover:opacity-100 transition duration-500"></div>
- <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">
- <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>
- </div>
- <h3 class="text-3xl font-black text-green-400 uppercase tracking-widest mb-2 relative z-10">Protection Active</h3>
- <p class="text-gray-400 text-sm relative z-10">BIND RPZ is actively intercepting bad domains.</p>
- {% else %}
- <div class="w-20 h-20 bg-gray-800 text-gray-500 rounded-full flex items-center justify-center mb-6 relative z-10">
- <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>
- </div>
- <h3 class="text-3xl font-black text-gray-500 uppercase tracking-widest mb-2 relative z-10">Protection Offline</h3>
- <p class="text-gray-400 text-sm relative z-10">Toggle your preferred lists below and click Apply.</p>
- {% endif %}
- </div>
- <div class="bg-[#12161f] border border-gray-800 rounded-3xl p-8 flex flex-col justify-center shadow-2xl relative overflow-hidden">
- <div class="mb-8 relative z-10">
- <p class="text-xs font-black text-gray-500 uppercase tracking-widest mb-2">Domains in Blocklist</p>
- <p class="text-6xl font-black text-white font-mono tracking-tighter" id="domainsCount">{{ "{:,}".format(settings.adblock_domains_count) }}</p>
- </div>
- <div class="relative z-10">
- <p class="text-xs font-black text-gray-500 uppercase tracking-widest mb-2">Last Sync Time</p>
- <p class="text-base font-bold text-blue-400" id="lastUpdate">{{ settings.adblock_last_update }}</p>
- </div>
- </div>
- </div>
- <div class="flex gap-6 border-b border-gray-800 mb-8 overflow-x-auto whitespace-nowrap scrollbar-hide">
- <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>
- <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>
- </div>
- <div id="adSection_lists" class="ad-section">
- <div class="bg-[#12161f] border border-gray-800 rounded-3xl p-8 shadow-2xl">
- <div class="flex justify-between items-center mb-8 border-b border-gray-800 pb-4">
- <h3 class="text-2xl font-black text-white uppercase italic tracking-tighter">Blocklist Sources</h3>
- <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">
- <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
- </button>
- </div>
- {% set categories = ['General', 'Security', 'Other', 'Regional', 'Custom'] %}
-
- {% for cat in categories %}
- {% set cat_lists = adblock_lists | selectattr('category', 'equalto', cat) | list %}
- {% if cat_lists %}
- <div class="mb-10 last:mb-0">
- <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">
- {{ cat }} Filters
- </h4>
-
- <div class="grid grid-cols-1 xl:grid-cols-2 gap-4">
- {% for lst in cat_lists %}
- <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">
- <div class="flex-1 pr-4 z-10">
- <h5 class="text-base font-bold text-gray-200 group-hover:text-white transition">{{ lst.name }}</h5>
- <p class="text-[11px] text-gray-500 mt-1 mb-3 line-clamp-2 leading-relaxed">{{ lst.desc | default('Custom user list.') }}</p>
- <div class="flex gap-3">
- <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>
- {% if lst.info %}
- <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>
- {% endif %}
- </div>
- </div>
-
- <div class="flex flex-col items-end justify-between h-full z-10 gap-3">
- <label class="relative inline-flex items-center cursor-pointer">
- <input type="checkbox" onchange="toggleList('{{ lst.id }}', this.checked)" class="sr-only peer" {% if lst.enabled %}checked{% endif %}>
- <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>
- </label>
-
- {% if lst.is_custom %}
- <div class="flex gap-3 mt-2">
- <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">
- <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
- </button>
- <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">
- <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
- </a>
- </div>
- {% endif %}
- </div>
- </div>
- {% endfor %}
- </div>
- </div>
- {% endif %}
- {% endfor %}
- </div>
- </div>
- <div id="adSection_custom" class="ad-section hidden">
- <div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
- <div class="bg-[#12161f] p-8 rounded-3xl border border-gray-800 shadow-2xl">
- <h3 class="text-xl font-black text-green-500 uppercase italic mb-6 flex items-center gap-2">
- <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)
- </h3>
- <form onsubmit="addRule(event, 'allowed')" class="flex gap-2 mb-6">
- <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">
- <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>
- </form>
- <div class="bg-gray-900/30 p-4 rounded-xl mb-6 text-[10px] text-gray-400 border border-gray-800">
- <p class="font-black text-gray-500 mb-1">EXAMPLES:</p>
- <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>
- </div>
- <div class="space-y-2 h-[350px] overflow-y-auto custom-scroll pr-2">
- {% if custom_rules.allowed %}
- {% for d in custom_rules.allowed %}
- <div class="flex justify-between items-center bg-[#0b0e14] border border-gray-800 p-3 rounded-xl">
- <span class="text-green-400 font-mono text-[11px]">{{ d }}</span>
- <div class="flex gap-4">
- <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">
- <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
- </button>
- <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">
- <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
- </button>
- </div>
- </div>
- {% endfor %}
- {% else %}
- <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>
- {% endif %}
- </div>
- </div>
- <div class="bg-[#12161f] p-8 rounded-3xl border border-gray-800 shadow-2xl">
- <h3 class="text-xl font-black text-red-500 uppercase italic mb-6 flex items-center gap-2">
- <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)
- </h3>
- <form onsubmit="addRule(event, 'deny')" class="flex gap-2 mb-6">
- <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">
- <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>
- </form>
- <div class="bg-gray-900/30 p-4 rounded-xl mb-6 text-[10px] text-gray-400 border border-gray-800">
- <p class="font-black text-gray-500 mb-1">EXAMPLES:</p>
- <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>
- </div>
- <div class="space-y-2 h-[350px] overflow-y-auto custom-scroll pr-2">
- {% if custom_rules.deny %}
- {% for d in custom_rules.deny %}
- <div class="flex justify-between items-center bg-[#0b0e14] border border-gray-800 p-3 rounded-xl">
- <span class="text-red-400 font-mono text-[11px]">{{ d }}</span>
- <div class="flex gap-4">
- <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">
- <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
- </button>
- <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">
- <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
- </button>
- </div>
- </div>
- {% endfor %}
- {% else %}
- <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>
- {% endif %}
- </div>
- </div>
- </div>
- </div>
- </div>
- <div id="addCustomModal" class="hidden fixed inset-0 bg-black/90 backdrop-blur-md z-50 flex items-center justify-center p-4">
- <div class="bg-[#12161f] border border-gray-800 w-full max-w-lg rounded-3xl p-8 shadow-2xl">
- <h3 class="text-2xl font-black text-white mb-6 uppercase border-b border-gray-800 pb-4">Add Custom URL</h3>
- <form action="/api/adblock/add_custom" method="POST" class="space-y-5">
- <div>
- <label class="block text-[10px] font-black text-gray-500 uppercase mb-2">List Name</label>
- <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">
- </div>
- <div>
- <label class="block text-[10px] font-black text-gray-500 uppercase mb-2">Description (Optional)</label>
- <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">
- </div>
- <div>
- <label class="block text-[10px] font-black text-gray-500 uppercase mb-2">Direct URL (.txt)</label>
- <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">
- <p class="text-[9px] text-gray-500 font-bold mt-2 uppercase tracking-widest">Supports Hosts or Adblock format.</p>
- </div>
- <div class="flex gap-4 pt-4 border-t border-gray-800">
- <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>
- <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>
- </div>
- </form>
- </div>
- </div>
- <div id="editCustomModal" class="hidden fixed inset-0 bg-black/90 backdrop-blur-md z-50 flex items-center justify-center p-4">
- <div class="bg-[#12161f] border border-gray-800 w-full max-w-lg rounded-3xl p-8 shadow-2xl">
- <h3 class="text-2xl font-black text-blue-400 mb-6 uppercase border-b border-gray-800 pb-4">Edit Custom List</h3>
- <form action="/api/adblock/edit_custom" method="POST" class="space-y-5">
- <input type="hidden" name="list_id" id="editListId">
- <div>
- <label class="block text-[10px] font-black text-gray-500 uppercase mb-2">List Name</label>
- <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">
- </div>
- <div>
- <label class="block text-[10px] font-black text-gray-500 uppercase mb-2">Description</label>
- <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">
- </div>
- <div>
- <label class="block text-[10px] font-black text-gray-500 uppercase mb-2">Direct URL (.txt)</label>
- <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">
- </div>
- <div class="flex gap-4 pt-4 border-t border-gray-800">
- <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>
- <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>
- </div>
- </form>
- </div>
- </div>
- <script>
- // --- TAB SWITCHER ---
- function switchAdTab(tab) {
- document.querySelectorAll('.ad-section').forEach(el => el.classList.add('hidden'));
-
- document.querySelectorAll('[id^="adTabBtn_"]').forEach(btn => {
- btn.classList.remove('text-blue-500', 'border-blue-500');
- btn.classList.add('text-gray-500', 'border-transparent');
- });
-
- const activeSection = document.getElementById('adSection_' + tab);
- if (activeSection) activeSection.classList.remove('hidden');
-
- const activeBtn = document.getElementById('adTabBtn_' + tab);
- if (activeBtn) {
- activeBtn.classList.remove('text-gray-500', 'border-transparent');
- activeBtn.classList.add('text-blue-500', 'border-blue-500');
- }
- }
- // Προεπιλογή: Φόρτωσε το tab των Lists
- document.addEventListener("DOMContentLoaded", () => {
- switchAdTab('lists');
- });
- // Ασφαλής κλήση για το AppToast
- const showToast = (opts) => {
- if (typeof AppToast !== 'undefined') AppToast.fire(opts);
- else console.log(opts.title);
- };
- // --- AJAX DOM REFRESHER ---
- // Αυτή η συνάρτηση διαβάζει αθόρυβα τα νέα δεδομένα από τον server
- // και ανανεώνει ΜΟΝΟ το Custom Tab, χωρίς να γίνει reload η σελίδα!
- function refreshCustomRulesAJAX() {
- return fetch('/adblock')
- .then(res => res.text())
- .then(html => {
- const parser = new DOMParser();
- const doc = parser.parseFromString(html, 'text/html');
- const newCustomSection = doc.getElementById('adSection_custom').innerHTML;
- document.getElementById('adSection_custom').innerHTML = newCustomSection;
-
- // Ενημερώνουμε και τον γενικό μετρητή των domains αν υπάρχει
- const newDomainsCount = doc.getElementById('domainsCount');
- if (newDomainsCount && document.getElementById('domainsCount')) {
- document.getElementById('domainsCount').innerText = newDomainsCount.innerText;
- }
- });
- }
- // --- ADBLOCK LOGIC ---
- function toggleList(id, state) {
- fetch('/api/adblock/toggle_list', {
- method: 'POST',
- headers: {'Content-Type': 'application/json'},
- body: JSON.stringify({id: id, state: state})
- });
- }
- function openEditCustomModal(id, name, desc, url) {
- document.getElementById('editListId').value = id;
- document.getElementById('editListName').value = name;
- document.getElementById('editListDesc').value = desc;
- document.getElementById('editListUrl').value = url;
- document.getElementById('editCustomModal').classList.remove('hidden');
- }
- function updateAdBlock() {
- const btn = document.getElementById('btnUpdate');
- const originalText = btn.innerHTML;
- 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...';
- btn.disabled = true;
- btn.classList.add('opacity-50', 'cursor-not-allowed');
- fetch('/api/adblock/update', { method: 'POST' })
- .then(r => r.json())
- .then(data => {
- if(data.status === 'success') {
- showToast({ icon: 'success', title: `Synced ${data.count} unique domains!` });
- setTimeout(() => window.location.reload(), 1500); // Εδώ το reload έχει νόημα γιατί ανανεώνει όλο το panel
- } else {
- showToast({ icon: 'error', title: 'Error', text: data.message });
- btn.innerHTML = originalText;
- btn.disabled = false;
- btn.classList.remove('opacity-50', 'cursor-not-allowed');
- }
- }).catch(err => {
- showToast({ icon: 'error', title: 'Connection Error' });
- btn.innerHTML = originalText;
- btn.disabled = false;
- btn.classList.remove('opacity-50', 'cursor-not-allowed');
- });
- }
- function disableAdBlock() {
- if(!confirm("Disable AdBlock Engine? Your network will no longer be protected from ads/trackers.")) return;
- const btn = document.getElementById('btnDisable');
- 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...';
- btn.disabled = true;
- fetch('/api/adblock/disable', { method: 'POST' })
- .then(r => r.json())
- .then(data => {
- if(data.status === 'success') {
- showToast({ icon: 'success', title: 'AdBlock Disabled' });
- setTimeout(() => window.location.reload(), 1000);
- }
- });
- }
- function syncClusterAdblock() {
- const btn = document.getElementById('btnClusterSync');
- const originalText = btn.innerHTML;
- 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...';
- btn.disabled = true;
-
- fetch('/api/adblock/sync_cluster', { method: 'POST' })
- .then(r => r.json())
- .then(data => {
- showToast({ icon: 'success', title: data.message });
- btn.innerHTML = originalText;
- btn.disabled = false;
- }).catch(err => {
- showToast({ icon: 'error', title: 'Cluster Sync Failed' });
- btn.innerHTML = originalText;
- btn.disabled = false;
- });
- }
- // --- PURE AJAX CRUD FOR CUSTOM RULES ---
- function addRule(e, type) {
- e.preventDefault();
- const input = document.getElementById('input_' + type);
- const domain = input.value.trim();
- if(!domain) return;
-
- const btn = e.target.querySelector('button');
- const originalText = btn.innerText;
- btn.innerHTML = '...';
- btn.disabled = true;
-
- const fd = new FormData();
- fd.append('type', type);
- fd.append('domain', domain);
-
- fetch('/api/adblock/rules/add', { method: 'POST', body: fd })
- .then(() => fetch('/api/adblock/update', { method: 'POST' }))
- .then(() => refreshCustomRulesAJAX())
- .then(() => {
- showToast({ icon: 'success', title: `Domain added to ${type} list` });
- // Το input καθαρίζει μόνο του επειδή γίνεται αντικατάσταση του DOM!
- });
- }
- function editRule(oldDomain, type) {
- let newDomain = prompt("Edit domain:", oldDomain);
- if(newDomain !== null && newDomain.trim() !== "" && newDomain.trim() !== oldDomain) {
- const fd = new FormData();
- fd.append('type', type);
- fd.append('old_domain', oldDomain);
- fd.append('new_domain', newDomain.trim());
-
- showToast({ title: 'Updating & Syncing...', icon: 'info', timer: 2000, showConfirmButton: false });
-
- fetch('/api/adblock/rules/edit', { method: 'POST', body: fd })
- .then(() => fetch('/api/adblock/update', { method: 'POST' }))
- .then(() => refreshCustomRulesAJAX())
- .then(() => {
- showToast({ icon: 'success', title: 'Rule updated' });
- });
- }
- }
- function deleteRule(domain, type) {
- if(!confirm(`Remove ${domain} from ${type} list?`)) return;
-
- const fd = new FormData();
- fd.append('type', type);
- fd.append('domain', domain);
-
- showToast({ title: 'Removing & Syncing...', icon: 'info', timer: 2000, showConfirmButton: false });
-
- fetch('/api/adblock/rules/delete', { method: 'POST', body: fd })
- .then(() => fetch('/api/adblock/update', { method: 'POST' }))
- .then(() => refreshCustomRulesAJAX())
- .then(() => {
- showToast({ icon: 'success', title: 'Rule removed' });
- });
- }
- </script>
- {% endblock %}
|