Update views
This commit is contained in:
@ -0,0 +1,23 @@
|
|||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>{% block title %}Sshecret Admin{% endblock %}</title>
|
||||||
|
|
||||||
|
{% block head %}
|
||||||
|
{% include 'base/partials/stylesheets.html.j2' %}
|
||||||
|
{% endblock %}
|
||||||
|
</head>
|
||||||
|
<body class="bg-gray-50 text-gray-900 min-h-screen flex flex-col">
|
||||||
|
<main id="content" class="flex-1 overflow-y-auto" hx-target="this" hx-swap="innerHTML">
|
||||||
|
<div class="" id="maincontent">
|
||||||
|
{% block content %}{% endblock %}
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
{% block scripts %}
|
||||||
|
{% include 'base/partials/scripts.html.j2' %}
|
||||||
|
{% endblock %}
|
||||||
|
{% block local_scripts %}
|
||||||
|
{% endblock %}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@ -2,7 +2,9 @@
|
|||||||
{% block title %}Client {{ client.name }}{% endblock %}
|
{% block title %}Client {{ client.name }}{% endblock %}
|
||||||
|
|
||||||
{% block master %}
|
{% block master %}
|
||||||
|
<div id="client-tree">
|
||||||
{% include '/clients/partials/tree.html.j2' %}
|
{% include '/clients/partials/tree.html.j2' %}
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,9 @@
|
|||||||
{% block title %}Clients{% endblock %}
|
{% block title %}Clients{% endblock %}
|
||||||
|
|
||||||
{% block master %}
|
{% block master %}
|
||||||
|
<div id="client-tree">
|
||||||
{% include '/clients/partials/tree.html.j2' %}
|
{% include '/clients/partials/tree.html.j2' %}
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -85,94 +85,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</sl-tab-panel>
|
</sl-tab-panel>
|
||||||
<sl-tab-panel name="events">
|
<sl-tab-panel name="events">
|
||||||
|
<div id="client-audit-events">
|
||||||
<table class="min-w-full lg:table-fixed divide-y divide-gray-200 dark:divide-gray-600" id="last-audit-events">
|
{% include '/clients/partials/client_events.html.j2' %}
|
||||||
<thead class="bg-gray-50 dark:bg-gray-700">
|
|
||||||
<tr>
|
|
||||||
<th scope="col" class="p-4 text-xs font-medium tracking-wider text-left text-gray-500 uppercase dark:text-white">Timestamp</th>
|
|
||||||
<th scope="col" class="p-4 text-xs font-medium tracking-wider text-left text-gray-500 uppercase dark:text-white">Subsystem</th>
|
|
||||||
<th scope="col" class="p-4 text-xs font-medium tracking-wider text-left text-gray-500 uppercase dark:text-white">Message</th>
|
|
||||||
<th scope="col" class="p-4 text-xs font-medium tracking-wider text-left text-gray-500 uppercase dark:text-white">Origin</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody class="bg-white dark:bg-gray-800">
|
|
||||||
{% for entry in events.results | list %}
|
|
||||||
<tr class="{{ loop.cycle('', 'bg-gray-50 dark:bg-gray-700 ') }}hover:bg-gray-100 dark:hover:bg-gray-700" id="login-entry-{{ entry.id }}">
|
|
||||||
<td class="p-4 text-sm font-normal text-gray-900 whitespace-nowrap dark:text-white">
|
|
||||||
<p>{{ entry.timestamp }}<button data-popover-target="popover-audit-entry-{{ entry.id }}" data-popover-placement="bottom-end" type="button"><svg class="w-4 h-4 ms-2 text-gray-400 hover:text-gray-500" aria-hidden="true" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-3a1 1 0 00-.867.5 1 1 0 11-1.731-1A3 3 0 0113 8a3.001 3.001 0 01-2 2.83V11a1 1 0 11-2 0v-1a1 1 0 011-1 1 1 0 100-2zm0 8a1 1 0 100-2 1 1 0 000 2z" clip-rule="evenodd"></path>
|
|
||||||
</svg><span class="sr-only">Show information</span></button></p>
|
|
||||||
|
|
||||||
<div data-popover id="popover-audit-entry-{{entry.id}}" role="tooltip" class="absolute z-10 invisible inline-block text-sm text-gray-500 transition-opacity duration-300 bg-white border border-gray-200 rounded-lg shadow-xs opacity-0 w-80 dark:bg-gray-800 dark:border-gray-600 dark:text-gray-400">
|
|
||||||
<dl class="max-w-md text-gray-900 divide-y divide-gray-200 dark:text-white dark:divide-gray-700 px-2 py-2">
|
|
||||||
<div class="flex flex-col pb-3">
|
|
||||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">ID</dt>
|
|
||||||
<dd class="text-xs font-semibold">{{ entry.id }}</dd>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col pb-3">
|
|
||||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Subsystem</dt>
|
|
||||||
<dd class="text-xs font-semibold">{{ entry.subsystem }}</dd>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex flex-col pb-3">
|
|
||||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Timestamp</dt>
|
|
||||||
<dd class="text-xs font-semibold">{{ entry.timestamp }}</dd>
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-col pb-3">
|
|
||||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Operation</dt>
|
|
||||||
<dd class="text-xs font-semibold">{{ entry.operation }}</dd>
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-col pb-3">
|
|
||||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Client ID</dt>
|
|
||||||
<dd class="text-xs font-semibold">{{ entry.client_id }}</dd>
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-col pb-3">
|
|
||||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Client Name</dt>
|
|
||||||
<dd class="text-xs font-semibold">{{ entry.client_name }}</dd>
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-col pb-3">
|
|
||||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Secret ID</dt>
|
|
||||||
<dd class="text-xs font-semibold">{{ entry.secret_id }}</dd>
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-col pb-3">
|
|
||||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Secret Name</dt>
|
|
||||||
<dd class="text-xs font-semibold">{{ entry.secret_name }}</dd>
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-col pb-3">
|
|
||||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Message</dt>
|
|
||||||
<dd class="text-xs font-semibold">{{ entry.message }}</dd>
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-col pb-3">
|
|
||||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Origin</dt>
|
|
||||||
<dd class="text-xs font-semibold">{{ entry.origin }}</dd>
|
|
||||||
</div>
|
|
||||||
{% if entry.data %}
|
|
||||||
{% for key, value in entry.data.items() %}
|
|
||||||
<div class="flex flex-col pb-3">
|
|
||||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">{{ key | capitalize }}</dt>
|
|
||||||
<dd class="text-xs font-semibold">{{ value }}</dd>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
</dl>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td class="p-4 text-sm font-normal text-gray-900 whitespace-nowrap dark:text-white">
|
|
||||||
{{ entry.subsystem }}
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td class="p-4 text-sm font-normal text-gray-900 whitespace-nowrap dark:text-white">
|
|
||||||
{{ entry.message }}
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td class="p-4 text-sm font-normal text-gray-900 whitespace-nowrap dark:text-white">
|
|
||||||
{{ entry.origin }}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</sl-tab-panel>
|
</sl-tab-panel>
|
||||||
</sl-tab-group>
|
</sl-tab-group>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -0,0 +1,157 @@
|
|||||||
|
<table class="min-w-full lg:table-fixed divide-y divide-gray-200 dark:divide-gray-600" id="last-audit-events">
|
||||||
|
<thead class="bg-gray-50 dark:bg-gray-700">
|
||||||
|
<tr>
|
||||||
|
<th scope="col" class="p-4 text-xs font-medium tracking-wider text-left text-gray-500 uppercase dark:text-white">Timestamp</th>
|
||||||
|
<th scope="col" class="p-4 text-xs font-medium tracking-wider text-left text-gray-500 uppercase dark:text-white">Subsystem</th>
|
||||||
|
<th scope="col" class="p-4 text-xs font-medium tracking-wider text-left text-gray-500 uppercase dark:text-white">Message</th>
|
||||||
|
<th scope="col" class="p-4 text-xs font-medium tracking-wider text-left text-gray-500 uppercase dark:text-white">Origin</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="bg-white dark:bg-gray-800" id="last-audit-events-body">
|
||||||
|
|
||||||
|
{% for entry in events.results | list %}
|
||||||
|
<tr class="{{ loop.cycle('', 'bg-gray-50 dark:bg-gray-700 ') }}hover:bg-gray-100 dark:hover:bg-gray-700" id="login-entry-{{ entry.id }}">
|
||||||
|
<td class="p-4 text-sm font-normal text-gray-900 whitespace-nowrap dark:text-white">
|
||||||
|
<p>{{ entry.timestamp }}<button data-popover-target="popover-audit-entry-{{ entry.id }}" data-popover-placement="bottom-end" type="button"><svg class="w-4 h-4 ms-2 text-gray-400 hover:text-gray-500" aria-hidden="true" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-3a1 1 0 00-.867.5 1 1 0 11-1.731-1A3 3 0 0113 8a3.001 3.001 0 01-2 2.83V11a1 1 0 11-2 0v-1a1 1 0 011-1 1 1 0 100-2zm0 8a1 1 0 100-2 1 1 0 000 2z" clip-rule="evenodd"></path>
|
||||||
|
</svg><span class="sr-only">Show information</span></button></p>
|
||||||
|
|
||||||
|
<div data-popover id="popover-audit-entry-{{entry.id}}" role="tooltip" class="absolute z-10 invisible inline-block text-sm text-gray-500 transition-opacity duration-300 bg-white border border-gray-200 rounded-lg shadow-xs opacity-0 w-80 dark:bg-gray-800 dark:border-gray-600 dark:text-gray-400">
|
||||||
|
<dl class="max-w-md text-gray-900 divide-y divide-gray-200 dark:text-white dark:divide-gray-700 px-2 py-2">
|
||||||
|
<div class="flex flex-col pb-3">
|
||||||
|
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">ID</dt>
|
||||||
|
<dd class="text-xs font-semibold">{{ entry.id }}</dd>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col pb-3">
|
||||||
|
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Subsystem</dt>
|
||||||
|
<dd class="text-xs font-semibold">{{ entry.subsystem }}</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-col pb-3">
|
||||||
|
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Timestamp</dt>
|
||||||
|
<dd class="text-xs font-semibold">{{ entry.timestamp }}</dd>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col pb-3">
|
||||||
|
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Operation</dt>
|
||||||
|
<dd class="text-xs font-semibold">{{ entry.operation }}</dd>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col pb-3">
|
||||||
|
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Client ID</dt>
|
||||||
|
<dd class="text-xs font-semibold">{{ entry.client_id }}</dd>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col pb-3">
|
||||||
|
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Client Name</dt>
|
||||||
|
<dd class="text-xs font-semibold">{{ entry.client_name }}</dd>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col pb-3">
|
||||||
|
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Secret ID</dt>
|
||||||
|
<dd class="text-xs font-semibold">{{ entry.secret_id }}</dd>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col pb-3">
|
||||||
|
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Secret Name</dt>
|
||||||
|
<dd class="text-xs font-semibold">{{ entry.secret_name }}</dd>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col pb-3">
|
||||||
|
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Message</dt>
|
||||||
|
<dd class="text-xs font-semibold">{{ entry.message }}</dd>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col pb-3">
|
||||||
|
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Origin</dt>
|
||||||
|
<dd class="text-xs font-semibold">{{ entry.origin }}</dd>
|
||||||
|
</div>
|
||||||
|
{% if entry.data %}
|
||||||
|
{% for key, value in entry.data.items() %}
|
||||||
|
<div class="flex flex-col pb-3">
|
||||||
|
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">{{ key | capitalize }}</dt>
|
||||||
|
<dd class="text-xs font-semibold">{{ value }}</dd>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class="p-4 text-sm font-normal text-gray-900 whitespace-nowrap dark:text-white">
|
||||||
|
{{ entry.subsystem }}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class="p-4 text-sm font-normal text-gray-900 whitespace-nowrap dark:text-white">
|
||||||
|
{{ entry.message }}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class="p-4 text-sm font-normal text-gray-900 whitespace-nowrap dark:text-white">
|
||||||
|
{{ entry.origin }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="sticky bottom-0 right-0 items-center w-full p-4 bg-white border-t border-gray-200 sm:flex sm:justify-between dark:bg-gray-800 dark:border-gray-700"
|
||||||
|
>
|
||||||
|
<div class="flex items-center mb-4 sm:mb-0">
|
||||||
|
|
||||||
|
<span class="text-sm font-normal text-gray-500 dark:text-gray-400"
|
||||||
|
>Showing
|
||||||
|
{% if events_paging.total < events_paging.last %}
|
||||||
|
<span class="font-semibold text-gray-900 dark:text-white">{{events_paging.first }}-{{ events_paging.total}}</span> of
|
||||||
|
{% else %}
|
||||||
|
<span class="font-semibold text-gray-900 dark:text-white">{{events_paging.first }}-{{ events_paging.last}}</span> of
|
||||||
|
{% endif %}
|
||||||
|
<span class="font-semibold text-gray-900 dark:text-white"
|
||||||
|
>{{ events_paging.total }}</span
|
||||||
|
></span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center space-x-3">
|
||||||
|
<div class="flex space-x-1">
|
||||||
|
|
||||||
|
<button
|
||||||
|
{% if events_paging.page == 1 %}
|
||||||
|
class="px-3 py-1 min-w-9 min-h-9 text-sm font-normal text-gray-900 bg-white border border-gray-300 rounded hover:bg-gray-100 focus:ring-4 focus:ring-primary-300 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700 transition duration-200 ease"
|
||||||
|
disabled=""
|
||||||
|
{% else %}
|
||||||
|
class="px-3 py-1 min-w-9 min-h-9 text-sm font-normal text-white bg-primary-700 hover:bg-primary-800 focus:ring-4 focus:ring-primary-300 dark:bg-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800 transition duration-200 ease"
|
||||||
|
hx-get="/clients/client/{{ client.id }}/events/{{ events_paging.page - 1 }}"
|
||||||
|
hx-target="#client-audit-events"
|
||||||
|
{% endif %}
|
||||||
|
>
|
||||||
|
Prev
|
||||||
|
</button>
|
||||||
|
{% for n in range(events_paging.total_pages) %}
|
||||||
|
{% set p = n + 1 %}
|
||||||
|
{% if p == events_paging.page %}
|
||||||
|
<button
|
||||||
|
class="px-3 py-1 min-w-9 min-h-9 text-sm font-normal text-white bg-primary-700 hover:bg-primary-800 focus:ring-4 focus:ring-primary-300 dark:bg-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800 transition duration-200 ease">
|
||||||
|
|
||||||
|
{{ p }}
|
||||||
|
</button>
|
||||||
|
{% else %}
|
||||||
|
<button
|
||||||
|
class="px-3 py-1 min-w-9 min-h-9 text-sm font-normal text-gray-900 bg-white border border-gray-300 rounded hover:bg-gray-100 focus:ring-4 focus:ring-primary-300 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700 transition duration-200 ease"
|
||||||
|
|
||||||
|
hx-get="/clients/client/{{client.id}}/events/{{ p }}"
|
||||||
|
hx-target="#client-audit-events"
|
||||||
|
>
|
||||||
|
{{ p }}
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
<button
|
||||||
|
{% if events_paging.page < events_paging.total_pages %}
|
||||||
|
|
||||||
|
class="px-3 py-1 min-w-9 min-h-9 text-sm font-normal text-white bg-primary-700 hover:bg-primary-800 focus:ring-4 focus:ring-primary-300 dark:bg-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800 transition duration-200 ease"
|
||||||
|
|
||||||
|
hx-get="/clients/client/{{ client.id }}/events/{{ events_paging.page + 1 }}"
|
||||||
|
hx-target="#client-audit-events"
|
||||||
|
{% else %}
|
||||||
|
class="px-3 py-1 min-w-9 min-h-9 text-sm font-normal text-gray-900 bg-white border border-gray-300 rounded hover:bg-gray-100 focus:ring-4 focus:ring-primary-300 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700 transition duration-200 ease"
|
||||||
|
disabled=""
|
||||||
|
{% endif %}
|
||||||
|
>
|
||||||
|
Next
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@ -86,7 +86,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</sl-details>
|
</sl-details>
|
||||||
{% if groups.groups %}
|
{% if flat_groups.groups %}
|
||||||
<sl-details summary="Group">
|
<sl-details summary="Group">
|
||||||
<form
|
<form
|
||||||
hx-put="/secrets/set-group/{{ secret.name }}"
|
hx-put="/secrets/set-group/{{ secret.name }}"
|
||||||
@ -98,7 +98,7 @@
|
|||||||
<div class="relative w-full">
|
<div class="relative w-full">
|
||||||
<select id="group_name" name="group_name" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
|
<select id="group_name" name="group_name" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
|
||||||
<option value="__ROOT">Ungrouped</option>
|
<option value="__ROOT">Ungrouped</option>
|
||||||
{% for group in groups.groups %}
|
{% for group in flat_groups.groups %}
|
||||||
<option value="{{ group.group_name }}" {% if group.name == secret.group -%}selected{% endif %}>{{ group.path }}</option>
|
<option value="{{ group.group_name }}" {% if group.name == secret.group -%}selected{% endif %}>{{ group.path }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
|
|||||||
@ -133,6 +133,10 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter:
|
|||||||
token_data: dict[str, str] = {"sub": user.username}
|
token_data: dict[str, str] = {"sub": user.username}
|
||||||
access_token = create_access_token(dependencies.settings, data=token_data)
|
access_token = create_access_token(dependencies.settings, data=token_data)
|
||||||
refresh_token = create_refresh_token(dependencies.settings, data=token_data)
|
refresh_token = create_refresh_token(dependencies.settings, data=token_data)
|
||||||
|
if next == "/refresh":
|
||||||
|
# Don't redirect from login to refresh. Send to dashboard instead.
|
||||||
|
next = "/"
|
||||||
|
|
||||||
response = RedirectResponse(url=next, status_code=status.HTTP_302_FOUND)
|
response = RedirectResponse(url=next, status_code=status.HTTP_302_FOUND)
|
||||||
response.set_cookie(
|
response.set_cookie(
|
||||||
"access_token",
|
"access_token",
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import logging
|
|||||||
import uuid
|
import uuid
|
||||||
from typing import Annotated
|
from typing import Annotated
|
||||||
from fastapi import APIRouter, Depends, Form, HTTPException, Query, Request, Response
|
from fastapi import APIRouter, Depends, Form, HTTPException, Query, Request, Response
|
||||||
|
from fastapi.responses import RedirectResponse
|
||||||
from pydantic import BaseModel, IPvAnyAddress, IPvAnyNetwork
|
from pydantic import BaseModel, IPvAnyAddress, IPvAnyNetwork
|
||||||
from sshecret_admin.frontend.views.common import PagingInfo
|
from sshecret_admin.frontend.views.common import PagingInfo
|
||||||
|
|
||||||
@ -20,6 +21,7 @@ from ..dependencies import FrontendDependencies
|
|||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
CLIENTS_PER_PAGE = 20
|
CLIENTS_PER_PAGE = 20
|
||||||
|
EVENTS_PER_PAGE = 20
|
||||||
|
|
||||||
|
|
||||||
class ClientUpdate(BaseModel):
|
class ClientUpdate(BaseModel):
|
||||||
@ -165,7 +167,7 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter:
|
|||||||
if not results:
|
if not results:
|
||||||
raise HTTPException(status_code=404, detail="Client not found.")
|
raise HTTPException(status_code=404, detail="Client not found.")
|
||||||
events = await admin.get_audit_log_detailed(
|
events = await admin.get_audit_log_detailed(
|
||||||
limit=10, client_name=results.client.name
|
limit=EVENTS_PER_PAGE, client_name=results.client.name
|
||||||
)
|
)
|
||||||
template = "clients/client.html.j2"
|
template = "clients/client.html.j2"
|
||||||
|
|
||||||
@ -179,6 +181,9 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter:
|
|||||||
headers["HX-Push-Url"] = request.url.path
|
headers["HX-Push-Url"] = request.url.path
|
||||||
template = "clients/partials/client_details.html.j2"
|
template = "clients/partials/client_details.html.j2"
|
||||||
|
|
||||||
|
events_paging = PagingInfo(
|
||||||
|
page=1, limit=EVENTS_PER_PAGE, total=events.total, offset=0
|
||||||
|
)
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
request,
|
request,
|
||||||
template,
|
template,
|
||||||
@ -191,10 +196,45 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter:
|
|||||||
"user": current_user,
|
"user": current_user,
|
||||||
"results": results.results,
|
"results": results.results,
|
||||||
"events": events,
|
"events": events,
|
||||||
|
"events_paging": events_paging,
|
||||||
},
|
},
|
||||||
headers=headers,
|
headers=headers,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@app.get("/clients/client/{id}/events/{page}")
|
||||||
|
async def get_client_events(
|
||||||
|
request: Request,
|
||||||
|
admin: Annotated[AdminBackend, Depends(dependencies.get_admin_backend)],
|
||||||
|
id: str,
|
||||||
|
page: int,
|
||||||
|
) -> Response:
|
||||||
|
"""Get more events for a client."""
|
||||||
|
if not "HX-Request" in request.headers:
|
||||||
|
return RedirectResponse(url=f"/clients/client/{id}")
|
||||||
|
|
||||||
|
client = await admin.get_client(("id", id))
|
||||||
|
if not client:
|
||||||
|
raise HTTPException(status_code=404, detail="Client not found.")
|
||||||
|
offset = 0
|
||||||
|
if page > 1:
|
||||||
|
offset = (page - 1) * EVENTS_PER_PAGE
|
||||||
|
events = await admin.get_audit_log_detailed(
|
||||||
|
limit=EVENTS_PER_PAGE, client_name=client.name, offset=offset
|
||||||
|
)
|
||||||
|
|
||||||
|
events_paging = PagingInfo(
|
||||||
|
page=page, limit=EVENTS_PER_PAGE, total=events.total, offset=offset
|
||||||
|
)
|
||||||
|
return templates.TemplateResponse(
|
||||||
|
request,
|
||||||
|
"clients/partials/client_events.html.j2",
|
||||||
|
{
|
||||||
|
"events": events,
|
||||||
|
"client": client,
|
||||||
|
"events_paging": events_paging,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
@app.put("/clients/{id}")
|
@app.put("/clients/{id}")
|
||||||
async def update_client(
|
async def update_client(
|
||||||
request: Request,
|
request: Request,
|
||||||
@ -228,7 +268,13 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter:
|
|||||||
|
|
||||||
final_client = await admin.update_client(updated_client)
|
final_client = await admin.update_client(updated_client)
|
||||||
|
|
||||||
events = await admin.get_audit_log_detailed(limit=10, client_name=client.name)
|
events = await admin.get_audit_log_detailed(
|
||||||
|
limit=EVENTS_PER_PAGE, client_name=client.name
|
||||||
|
)
|
||||||
|
|
||||||
|
events_paging = PagingInfo(
|
||||||
|
page=1, limit=EVENTS_PER_PAGE, total=events.total, offset=0
|
||||||
|
)
|
||||||
|
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
request,
|
request,
|
||||||
@ -236,6 +282,7 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter:
|
|||||||
{
|
{
|
||||||
"client": final_client,
|
"client": final_client,
|
||||||
"events": events,
|
"events": events,
|
||||||
|
"events_paging": events_paging,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -249,6 +296,12 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter:
|
|||||||
sources: list[str] | None = None
|
sources: list[str] | None = None
|
||||||
if client.sources:
|
if client.sources:
|
||||||
sources = [source.strip() for source in client.sources.split(",")]
|
sources = [source.strip() for source in client.sources.split(",")]
|
||||||
|
await admin.create_client(
|
||||||
|
name=client.name,
|
||||||
|
public_key=client.public_key,
|
||||||
|
description=client.description,
|
||||||
|
sources=sources,
|
||||||
|
)
|
||||||
|
|
||||||
headers = {"Hx-Refresh": "true"}
|
headers = {"Hx-Refresh": "true"}
|
||||||
return Response(
|
return Response(
|
||||||
|
|||||||
@ -173,6 +173,7 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter:
|
|||||||
"""Get secret detail."""
|
"""Get secret detail."""
|
||||||
secret = await admin.get_secret(name)
|
secret = await admin.get_secret(name)
|
||||||
groups = await admin.get_secret_groups()
|
groups = await admin.get_secret_groups()
|
||||||
|
flat_groups = await admin.get_secret_groups(flat=True)
|
||||||
events = await admin.get_audit_log_detailed(limit=10, secret_name=name)
|
events = await admin.get_audit_log_detailed(limit=10, secret_name=name)
|
||||||
|
|
||||||
if not secret:
|
if not secret:
|
||||||
@ -183,6 +184,7 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter:
|
|||||||
context: dict[str, Any] = {
|
context: dict[str, Any] = {
|
||||||
"secret": secret,
|
"secret": secret,
|
||||||
"groups": groups,
|
"groups": groups,
|
||||||
|
"flat_groups": flat_groups,
|
||||||
"events": events,
|
"events": events,
|
||||||
"secret_page": True,
|
"secret_page": True,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user