index.twig 5.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. {% extends 'layout.twig' %}
  2. {% block title %}Allowlist — IRDB{% endblock %}
  3. {% block content %}
  4. <div class="mx-auto max-w-5xl">
  5. <div class="flex items-center justify-between">
  6. <h1 class="text-2xl font-semibold tracking-tight">Allowlist</h1>
  7. <span class="text-sm text-slate-500 dark:text-slate-400">{{ list.total|default(0) }} total</span>
  8. </div>
  9. {% set kind_links = [
  10. { label: 'All', value: '' },
  11. { label: 'IPs', value: 'ip' },
  12. { label: 'Subnets', value: 'subnet' },
  13. ] %}
  14. <div class="mt-4 flex gap-2 text-sm">
  15. {% for k in kind_links %}
  16. {% set is_active = (kind|default('') == k.value) or (kind == null and k.value == '') %}
  17. <a href="/app/allowlist{% if k.value %}?kind={{ k.value }}{% endif %}"
  18. class="rounded-full px-3 py-1 {% if is_active %}bg-emerald-100 text-emerald-800 dark:bg-emerald-900 dark:text-emerald-100{% else %}border border-slate-300 text-slate-600 hover:bg-slate-50 dark:border-slate-700 dark:text-slate-300 dark:hover:bg-slate-800{% endif %}">{{ k.label }}</a>
  19. {% endfor %}
  20. </div>
  21. {% if can_write %}
  22. <section class="mt-6 rounded-2xl border border-slate-200 bg-white p-5 shadow-sm dark:border-slate-800 dark:bg-slate-900">
  23. <h2 class="text-sm font-semibold uppercase tracking-wider text-slate-500 dark:text-slate-400">Add allowlist entry</h2>
  24. <form method="post" action="/app/allowlist" class="mt-3 grid grid-cols-1 gap-3 md:grid-cols-3 text-sm" x-data="{ kind: 'ip' }">
  25. <input type="hidden" name="csrf_token" value="{{ csrf_token }}">
  26. <div>
  27. <label for="al-kind" class="block text-xs font-medium text-slate-600 dark:text-slate-400">Kind</label>
  28. <select id="al-kind" name="kind" x-model="kind"
  29. class="mt-1 w-full rounded-md border border-slate-300 bg-white px-2 py-1.5 dark:border-slate-700 dark:bg-slate-950">
  30. <option value="ip">Single IP</option>
  31. <option value="subnet">Subnet (CIDR)</option>
  32. </select>
  33. </div>
  34. <div x-show="kind == 'ip'">
  35. <label for="al-ip" class="block text-xs font-medium text-slate-600 dark:text-slate-400">IP</label>
  36. <input type="text" id="al-ip" name="ip" placeholder="203.0.113.5"
  37. class="mt-1 w-full rounded-md border border-slate-300 bg-white px-2 py-1.5 font-mono dark:border-slate-700 dark:bg-slate-950">
  38. </div>
  39. <div x-show="kind == 'subnet'">
  40. <label for="al-cidr" class="block text-xs font-medium text-slate-600 dark:text-slate-400">CIDR</label>
  41. <input type="text" id="al-cidr" name="cidr" placeholder="10.0.0.0/8"
  42. class="mt-1 w-full rounded-md border border-slate-300 bg-white px-2 py-1.5 font-mono dark:border-slate-700 dark:bg-slate-950">
  43. </div>
  44. <div>
  45. <label for="al-reason" class="block text-xs font-medium text-slate-600 dark:text-slate-400">Reason</label>
  46. <input type="text" id="al-reason" name="reason"
  47. class="mt-1 w-full rounded-md border border-slate-300 bg-white px-2 py-1.5 dark:border-slate-700 dark:bg-slate-950">
  48. </div>
  49. <div class="md:col-span-3 flex justify-end">
  50. <button type="submit" class="rounded-md bg-emerald-600 px-3 py-1.5 text-sm font-medium text-white hover:bg-emerald-500">Add entry</button>
  51. </div>
  52. </form>
  53. </section>
  54. {% endif %}
  55. <section class="mt-6 overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm dark:border-slate-800 dark:bg-slate-900">
  56. <table class="w-full text-sm">
  57. <thead class="border-b border-slate-200 bg-slate-50 text-left text-xs uppercase tracking-wider text-slate-500 dark:border-slate-800 dark:bg-slate-950 dark:text-slate-400">
  58. <tr>
  59. <th class="px-4 py-2 font-medium">Kind</th>
  60. <th class="px-4 py-2 font-medium">Target</th>
  61. <th class="px-4 py-2 font-medium">Reason</th>
  62. <th class="px-4 py-2 font-medium">Created</th>
  63. {% if can_write %}<th class="px-4 py-2 text-right font-medium">Actions</th>{% endif %}
  64. </tr>
  65. </thead>
  66. <tbody class="divide-y divide-slate-100 dark:divide-slate-800">
  67. {% for item in list.items|default([]) %}
  68. <tr>
  69. <td class="px-4 py-2 font-mono text-xs uppercase">{{ item.kind }}</td>
  70. <td class="px-4 py-2 font-mono">{{ item.kind == 'ip' ? item.ip : item.cidr }}</td>
  71. <td class="px-4 py-2 text-slate-600 dark:text-slate-300">{{ item.reason|default('—') }}</td>
  72. <td class="px-4 py-2 text-slate-500 dark:text-slate-400">{{ item.created_at }}</td>
  73. {% if can_write %}
  74. <td class="px-4 py-2 text-right">
  75. {% include 'partials/confirm_form.twig' with {
  76. action: '/app/allowlist/' ~ item.id ~ '/delete',
  77. label: 'Remove',
  78. description: 'This removes the allowlist entry immediately.',
  79. } only %}
  80. </td>
  81. {% endif %}
  82. </tr>
  83. {% else %}
  84. <tr><td colspan="5" class="px-4 py-6 text-center text-slate-400">No allowlist entries.</td></tr>
  85. {% endfor %}
  86. </tbody>
  87. </table>
  88. </section>
  89. </div>
  90. {% endblock %}