1
0

index.twig 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. {% extends 'layout.twig' %}
  2. {% block title %}Settings — IRDB{% endblock %}
  3. {% block content %}
  4. <div class="mx-auto max-w-5xl space-y-6">
  5. <div class="flex items-center justify-between">
  6. <h1 class="text-2xl font-semibold tracking-tight">Settings</h1>
  7. <span class="text-xs text-slate-500 dark:text-slate-400">Admin only · read-only · masked secrets</span>
  8. </div>
  9. {% if error %}
  10. <div class="rounded-md border border-red-300 bg-red-50 px-4 py-2 text-sm text-red-800 dark:border-red-800 dark:bg-red-950 dark:text-red-300">{{ error }}</div>
  11. {% endif %}
  12. {# ------------------------- Configuration ------------------------- #}
  13. {% if config and config.sections %}
  14. <section class="rounded-2xl border border-slate-200 bg-white p-5 shadow-sm dark:border-slate-800 dark:bg-slate-900">
  15. <h2 class="text-sm font-semibold uppercase tracking-wider text-slate-500 dark:text-slate-400">Configuration</h2>
  16. <p class="mt-1 text-xs text-slate-500 dark:text-slate-400">Effective values from the api's environment. Secrets are masked (<code>***</code>) or previewed (first 8 chars + …).</p>
  17. <div class="mt-4 grid gap-5 md:grid-cols-2">
  18. {% for section_name, items in config.sections %}
  19. <div class="rounded-lg border border-slate-100 dark:border-slate-800">
  20. <div class="border-b border-slate-100 bg-slate-50 px-4 py-2 text-xs font-semibold uppercase tracking-wider text-slate-500 dark:border-slate-800 dark:bg-slate-950 dark:text-slate-400">{{ section_name }}</div>
  21. <dl class="divide-y divide-slate-100 dark:divide-slate-800 text-sm">
  22. {% for key, value in items %}
  23. <div class="grid grid-cols-2 gap-2 px-4 py-2">
  24. <dt class="font-mono text-xs text-slate-500 dark:text-slate-400">{{ key }}</dt>
  25. <dd class="break-all font-mono text-xs text-slate-700 dark:text-slate-200">
  26. {%- if value is null -%}<span class="text-slate-400">—</span>
  27. {%- elseif value is same as(true) -%}true
  28. {%- elseif value is same as(false) -%}false
  29. {%- else -%}{{ value }}{%- endif -%}
  30. </dd>
  31. </div>
  32. {% endfor %}
  33. </dl>
  34. </div>
  35. {% endfor %}
  36. </div>
  37. </section>
  38. {% endif %}
  39. {# ------------------------------ Jobs ----------------------------- #}
  40. {% if jobs and jobs.jobs %}
  41. <section class="rounded-2xl border border-slate-200 bg-white p-5 shadow-sm dark:border-slate-800 dark:bg-slate-900">
  42. <h2 class="text-sm font-semibold uppercase tracking-wider text-slate-500 dark:text-slate-400">Jobs</h2>
  43. <p class="mt-1 text-xs text-slate-500 dark:text-slate-400">Latest run, lock state, and manual-trigger buttons. Manual triggers run synchronously — wait for the response.</p>
  44. <div class="mt-4 overflow-hidden rounded-lg border border-slate-100 dark:border-slate-800">
  45. <table class="w-full text-sm">
  46. <thead class="border-b border-slate-100 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">
  47. <tr>
  48. <th class="px-4 py-2 font-medium">Name</th>
  49. <th class="px-4 py-2 font-medium">Last status</th>
  50. <th class="px-4 py-2 font-medium">Last finished</th>
  51. <th class="px-4 py-2 font-medium">Items</th>
  52. <th class="px-4 py-2 text-right font-medium">Trigger</th>
  53. </tr>
  54. </thead>
  55. <tbody class="divide-y divide-slate-100 dark:divide-slate-800">
  56. {% for name, info in jobs.jobs %}
  57. <tr>
  58. <td class="px-4 py-2 align-top font-mono text-xs">
  59. {{ name }}
  60. {% if info.overdue %}
  61. <span class="ml-1 rounded bg-red-100 px-1.5 py-0.5 text-[0.65rem] font-mono uppercase text-red-800 dark:bg-red-950 dark:text-red-300">overdue</span>
  62. {% endif %}
  63. </td>
  64. <td class="px-4 py-2 align-top">
  65. {% if info.last_run %}
  66. {% set s = info.last_run.status %}
  67. {% set classes = {
  68. 'success': 'bg-emerald-100 text-emerald-900 dark:bg-emerald-900 dark:text-emerald-100',
  69. 'failure': 'bg-red-100 text-red-900 dark:bg-red-900 dark:text-red-100',
  70. 'skipped_locked': 'bg-amber-100 text-amber-900 dark:bg-amber-900 dark:text-amber-100',
  71. 'running': 'bg-blue-100 text-blue-900 dark:bg-blue-900 dark:text-blue-100',
  72. } %}
  73. <span class="rounded px-2 py-0.5 font-mono text-[0.65rem] uppercase {{ classes[s]|default('bg-slate-100 text-slate-700 dark:bg-slate-800 dark:text-slate-300') }}">{{ s }}</span>
  74. {% else %}
  75. <span class="text-xs text-slate-400">never run</span>
  76. {% endif %}
  77. </td>
  78. <td class="px-4 py-2 align-top font-mono text-xs text-slate-500">
  79. {{ info.last_run.finished_at|default('—') }}
  80. </td>
  81. <td class="px-4 py-2 align-top font-mono text-xs text-slate-500">
  82. {{ info.last_run.items_processed|default('—') }}
  83. </td>
  84. <td class="px-4 py-2 align-top text-right">
  85. {% if name != 'tick' %}
  86. <form method="post" action="/app/settings/jobs/trigger/{{ name }}" class="inline" x-data="{ submitting: false }" x-on:submit="submitting = true">
  87. <input type="hidden" name="csrf_token" value="{{ csrf_token }}">
  88. <button type="submit" x-bind:disabled="submitting"
  89. class="rounded-md border border-slate-300 px-2 py-1 text-xs hover:bg-slate-50 disabled:opacity-50 dark:border-slate-700 dark:hover:bg-slate-800">
  90. <span x-show="!submitting">Run now</span>
  91. <span x-show="submitting" x-cloak>Running…</span>
  92. </button>
  93. </form>
  94. {% else %}
  95. <span class="text-xs text-slate-400">scheduled</span>
  96. {% endif %}
  97. </td>
  98. </tr>
  99. {% endfor %}
  100. </tbody>
  101. </table>
  102. </div>
  103. </section>
  104. {% endif %}
  105. {# ------------------------------ GeoIP ----------------------------- #}
  106. {% if config and config.sections.geoip %}
  107. <section class="rounded-2xl border border-slate-200 bg-white p-5 shadow-sm dark:border-slate-800 dark:bg-slate-900">
  108. <h2 class="text-sm font-semibold uppercase tracking-wider text-slate-500 dark:text-slate-400">GeoIP</h2>
  109. <p class="mt-1 text-xs text-slate-500 dark:text-slate-400">Provider, on-disk paths, and credential state. DB freshness comes from healthz; the trigger button on <code>refresh-geoip</code> is in the Jobs section above.</p>
  110. <dl class="mt-3 grid grid-cols-3 gap-2 text-sm">
  111. <dt class="text-slate-500 dark:text-slate-400">Provider</dt>
  112. <dd class="col-span-2 font-mono text-xs">{{ config.sections.geoip.GEOIP_PROVIDER|default('—') }}</dd>
  113. <dt class="text-slate-500 dark:text-slate-400">Country DB</dt>
  114. <dd class="col-span-2 font-mono text-xs">{{ config.sections.geoip.GEOIP_COUNTRY_DB|default('—') }}</dd>
  115. <dt class="text-slate-500 dark:text-slate-400">ASN DB</dt>
  116. <dd class="col-span-2 font-mono text-xs">{{ config.sections.geoip.GEOIP_ASN_DB|default('—') }}</dd>
  117. <dt class="text-slate-500 dark:text-slate-400">MaxMind key</dt>
  118. <dd class="col-span-2 font-mono text-xs">{{ config.sections.geoip.MAXMIND_LICENSE_KEY ? config.sections.geoip.MAXMIND_LICENSE_KEY : '(unset)' }}</dd>
  119. <dt class="text-slate-500 dark:text-slate-400">IPinfo token</dt>
  120. <dd class="col-span-2 font-mono text-xs">{{ config.sections.geoip.IPINFO_TOKEN ? config.sections.geoip.IPINFO_TOKEN : '(unset)' }}</dd>
  121. </dl>
  122. </section>
  123. {% endif %}
  124. </div>
  125. {% endblock %}