Refactor to Flowbite for UI

This commit is contained in:
2026-02-03 09:54:49 +00:00
parent 962ba27679
commit bebaaf1367
16 changed files with 706 additions and 337 deletions

View File

@@ -1,42 +1,113 @@
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
<div class="flex items-center gap-4">
<div class="flex h-12 w-12 items-center justify-center rounded-xl bg-purple-600 text-white text-xl font-semibold">
{{ server.initial }}
<div class="space-y-4">
<nav class="flex" aria-label="Breadcrumb">
<ol class="inline-flex items-center space-x-1 text-sm text-gray-500">
<li class="inline-flex items-center">
<a href="{% url 'servers:dashboard' %}" class="inline-flex items-center gap-1 font-medium text-gray-600 hover:text-blue-700">
<svg class="h-4 w-4" aria-hidden="true" fill="currentColor" viewBox="0 0 20 20">
<path d="M10 3.172 2 10v7a1 1 0 0 0 1 1h5v-5h4v5h5a1 1 0 0 0 1-1v-7l-8-6.828Z"></path>
</svg>
Servers
</a>
</li>
<li class="inline-flex items-center">
<svg class="h-4 w-4 text-gray-400" aria-hidden="true" fill="currentColor" viewBox="0 0 20 20">
<path d="M7.05 4.55a1 1 0 0 1 1.4-1.42l6 5.9a1 1 0 0 1 0 1.42l-6 5.9a1 1 0 1 1-1.4-1.42L12.5 10 7.05 4.55Z"></path>
</svg>
<span class="ml-1 font-medium text-gray-700">{{ server.display_name }}</span>
</li>
</ol>
</nav>
<div class="flex flex-col gap-4 rounded-2xl border border-gray-200 bg-white p-5 shadow-sm sm:flex-row sm:items-center sm:justify-between">
<div class="flex items-center gap-4">
<div class="flex h-12 w-12 items-center justify-center rounded-2xl bg-blue-700 text-lg font-semibold text-white shadow-sm">
{{ server.initial }}
</div>
<div>
<h1 class="text-2xl font-semibold tracking-tight text-gray-900">{{ server.display_name }}</h1>
<p class="text-sm text-gray-500">
{{ server.hostname|default:server.ipv4|default:server.ipv6|default:"Unassigned" }}
</p>
</div>
</div>
<div>
<h1 class="text-2xl font-semibold tracking-tight text-gray-900">{{ server.display_name }}</h1>
<p class="text-sm text-gray-600">
{{ server.hostname|default:server.ipv4|default:server.ipv6|default:"Unassigned" }}
</p>
<div class="flex items-center gap-2">
<div class="relative">
<button
type="button"
data-tooltip-target="server-header-status-{{ server.id }}"
class="{% if server_status.is_active %}inline-flex items-center rounded-full bg-emerald-50 px-2.5 py-1 text-xs font-semibold text-emerald-700{% else %}inline-flex items-center rounded-full bg-rose-50 px-2.5 py-1 text-xs font-semibold text-rose-700{% endif %}"
>
{{ server_status.label }}: {{ server_status.detail }}
</button>
<div
id="server-header-status-{{ server.id }}"
role="tooltip"
class="invisible absolute z-10 inline-block w-64 rounded-lg border border-gray-200 bg-white p-3 text-xs text-gray-700 shadow-sm opacity-0 transition-opacity"
>
<div class="space-y-1">
<div class="flex items-center justify-between">
<span class="font-semibold text-gray-500">Status</span>
<span class="font-medium text-gray-900">{{ server_status.label }}: {{ server_status.detail }}</span>
</div>
<div class="flex items-center justify-between">
<span class="font-semibold text-gray-500">Ping</span>
<span class="font-medium text-gray-900">
{% if server_status.ping_ms is not None %}{{ server_status.ping_ms }}ms{% else %}—{% endif %}
</span>
</div>
<div class="flex items-center justify-between">
<span class="font-semibold text-gray-500">Hostname</span>
<span class="font-medium text-gray-900">{{ server.hostname|default:"—" }}</span>
</div>
<div class="flex items-center justify-between">
<span class="font-semibold text-gray-500">IPv4</span>
<span class="font-medium text-gray-900">{{ server.ipv4|default:"—" }}</span>
</div>
<div class="flex items-center justify-between">
<span class="font-semibold text-gray-500">IPv6</span>
<span class="font-medium text-gray-900">{{ server.ipv6|default:"—" }}</span>
</div>
<div class="flex items-center justify-between">
<span class="font-semibold text-gray-500">Last heartbeat</span>
<span class="font-medium text-gray-900">
{% if server_status.heartbeat_at %}{{ server_status.heartbeat_at|date:"M j, Y H:i" }}{% else %}—{% endif %}
</span>
</div>
</div>
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
</div>
<a href="{% url 'servers:dashboard' %}" class="inline-flex items-center rounded-lg border border-gray-200 bg-white px-3 py-2 text-xs font-semibold text-gray-700 hover:bg-gray-50">
Back to servers
</a>
</div>
</div>
<a href="{% url 'servers:dashboard' %}" class="text-sm font-semibold text-purple-700 hover:text-purple-800">Back to servers</a>
</div>
<nav class="flex flex-wrap gap-2 border-b border-gray-200 pb-2 text-sm font-semibold text-gray-500">
<nav class="mt-4 flex flex-wrap gap-2 border-b border-gray-200 pb-3 text-sm font-medium text-gray-500">
<a
href="{% url 'servers:detail' server.id %}"
class="{% if active_tab == 'details' %}rounded-full bg-purple-50 px-3 py-1 text-purple-700{% else %}rounded-full bg-gray-100 px-3 py-1 text-gray-500 hover:text-gray-700{% endif %}"
class="{% if active_tab == 'details' %}rounded-full bg-blue-50 px-4 py-1.5 text-blue-700 ring-1 ring-blue-100{% else %}rounded-full bg-gray-100 px-4 py-1.5 text-gray-600 hover:bg-white hover:text-gray-900{% endif %}"
>
Details
</a>
<a
href="{% url 'servers:audit' server.id %}"
class="{% if active_tab == 'audit' %}rounded-full bg-purple-50 px-3 py-1 text-purple-700{% else %}rounded-full bg-gray-100 px-3 py-1 text-gray-500 hover:text-gray-700{% endif %}"
class="{% if active_tab == 'audit' %}rounded-full bg-blue-50 px-4 py-1.5 text-blue-700 ring-1 ring-blue-100{% else %}rounded-full bg-gray-100 px-4 py-1.5 text-gray-600 hover:bg-white hover:text-gray-900{% endif %}"
>
Audit
</a>
{% if can_shell %}
<a
href="{% url 'servers:shell' server.id %}"
class="{% if active_tab == 'shell' %}rounded-full bg-purple-50 px-3 py-1 text-purple-700{% else %}rounded-full bg-gray-100 px-3 py-1 text-gray-500 hover:text-gray-700{% endif %}"
class="{% if active_tab == 'shell' %}rounded-full bg-blue-50 px-4 py-1.5 text-blue-700 ring-1 ring-blue-100{% else %}rounded-full bg-gray-100 px-4 py-1.5 text-gray-600 hover:bg-white hover:text-gray-900{% endif %}"
>
Shell
</a>
{% endif %}
<a
href="{% url 'servers:settings' server.id %}"
class="{% if active_tab == 'settings' %}rounded-full bg-purple-50 px-3 py-1 text-purple-700{% else %}rounded-full bg-gray-100 px-3 py-1 text-gray-500 hover:text-gray-700{% endif %}"
class="{% if active_tab == 'settings' %}rounded-full bg-blue-50 px-4 py-1.5 text-blue-700 ring-1 ring-blue-100{% else %}rounded-full bg-gray-100 px-4 py-1.5 text-gray-600 hover:bg-white hover:text-gray-900{% endif %}"
>
Settings
</a>

View File

@@ -3,26 +3,43 @@
{% block title %}Audit • {{ server.display_name }} • Keywarden{% endblock %}
{% block content %}
<div class="space-y-8">
<div class="space-y-6">
{% include "servers/_header.html" %}
<section class="rounded-2xl border border-gray-200 bg-white p-5 shadow-sm">
<div class="flex items-center justify-between">
<h2 class="text-lg font-semibold text-gray-900">Audit logs</h2>
<span class="text-xs font-semibold text-gray-500">Placeholder</span>
<section class="rounded-2xl border border-gray-200 bg-white p-6 shadow-sm">
<div class="flex flex-wrap items-start justify-between gap-4">
<div>
<h2 class="text-lg font-semibold text-gray-900">Audit logs</h2>
<p class="mt-1 text-sm text-gray-500">Track certificate issuance and access events.</p>
</div>
<span class="inline-flex items-center rounded-full bg-gray-100 px-2.5 py-1 text-xs font-semibold text-gray-700">Placeholder</span>
</div>
<div class="mt-4 rounded-xl border border-dashed border-gray-200 bg-gray-50 p-6 text-center text-sm text-gray-600">
Logs will appear here once collection is enabled for this server.
<div class="mt-5 rounded-xl border border-dashed border-gray-200 bg-gray-50 p-6 text-center">
<div class="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-blue-50 text-blue-700">
<svg class="h-6 w-6" aria-hidden="true" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12 6v6l4 2" />
<circle cx="12" cy="12" r="9" stroke-width="1.5"></circle>
</svg>
</div>
<p class="mt-3 text-sm text-gray-600">Logs will appear here once collection is enabled for this server.</p>
</div>
</section>
<section class="rounded-2xl border border-gray-200 bg-white p-5 shadow-sm">
<div class="flex items-center justify-between">
<h2 class="text-lg font-semibold text-gray-900">Metrics</h2>
<span class="text-xs font-semibold text-gray-500">Placeholder</span>
<section class="rounded-2xl border border-gray-200 bg-white p-6 shadow-sm">
<div class="flex flex-wrap items-start justify-between gap-4">
<div>
<h2 class="text-lg font-semibold text-gray-900">Metrics</h2>
<p class="mt-1 text-sm text-gray-500">Monitor CPU, memory, and session activity.</p>
</div>
<span class="inline-flex items-center rounded-full bg-gray-100 px-2.5 py-1 text-xs font-semibold text-gray-700">Placeholder</span>
</div>
<div class="mt-4 rounded-xl border border-dashed border-gray-200 bg-gray-50 p-6 text-center text-sm text-gray-600">
Metrics will appear here once collection is enabled for this server.
<div class="mt-5 rounded-xl border border-dashed border-gray-200 bg-gray-50 p-6 text-center">
<div class="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-blue-50 text-blue-700">
<svg class="h-6 w-6" aria-hidden="true" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M3 12h18M7 8v8M17 8v8" />
</svg>
</div>
<p class="mt-3 text-sm text-gray-600">Metrics will appear here once collection is enabled for this server.</p>
</div>
</section>
</div>

View File

@@ -3,15 +3,24 @@
{% block title %}Servers • Keywarden{% endblock %}
{% block content %}
<div class="space-y-8">
<div class="space-y-6">
<div class="flex flex-wrap items-center justify-between gap-4">
<div>
<h1 class="text-2xl font-semibold tracking-tight text-gray-900">Servers</h1>
<p class="mt-1 text-sm text-gray-500">Review the servers you can access and their certificate status.</p>
</div>
<span class="inline-flex items-center rounded-full bg-blue-50 px-3 py-1 text-xs font-semibold text-blue-700">
{{ servers|length }} total
</span>
</div>
{% if servers %}
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
<div class="grid gap-5 sm:grid-cols-2 lg:grid-cols-3">
{% for item in servers %}
<article class="group relative overflow-hidden rounded-2xl border border-gray-200 bg-white p-5 shadow-sm transition hover:-translate-y-0.5 hover:shadow-md">
<article class="flex h-full flex-col rounded-2xl border border-gray-200 bg-white p-5 shadow-sm transition hover:-translate-y-0.5 hover:shadow-md">
<div class="flex items-start justify-between">
<div class="flex items-center gap-3">
<div class="flex h-10 w-10 items-center justify-center rounded-lg bg-purple-600 text-white font-semibold">
<div class="flex h-10 w-10 items-center justify-center rounded-xl bg-blue-700 text-sm font-semibold text-white">
{{ item.server.initial }}
</div>
<div>
@@ -21,11 +30,56 @@
</p>
</div>
</div>
<span class="inline-flex items-center rounded-full bg-emerald-50 px-2.5 py-1 text-xs font-semibold text-emerald-700">Active</span>
<div class="relative">
<button
type="button"
data-tooltip-target="server-status-{{ item.server.id }}"
class="{% if item.status.is_active %}inline-flex items-center rounded-full bg-emerald-50 px-2.5 py-1 text-xs font-semibold text-emerald-700{% else %}inline-flex items-center rounded-full bg-rose-50 px-2.5 py-1 text-xs font-semibold text-rose-700{% endif %}"
>
{{ item.status.label }}: {{ item.status.detail }}
</button>
<div
id="server-status-{{ item.server.id }}"
role="tooltip"
class="invisible absolute z-10 inline-block w-64 rounded-lg border border-gray-200 bg-white p-3 text-xs text-gray-700 shadow-sm opacity-0 transition-opacity"
>
<div class="space-y-1">
<div class="flex items-center justify-between">
<span class="font-semibold text-gray-500">Status</span>
<span class="font-medium text-gray-900">{{ item.status.label }}: {{ item.status.detail }}</span>
</div>
<div class="flex items-center justify-between">
<span class="font-semibold text-gray-500">Ping</span>
<span class="font-medium text-gray-900">
{% if item.status.ping_ms is not None %}{{ item.status.ping_ms }}ms{% else %}—{% endif %}
</span>
</div>
<div class="flex items-center justify-between">
<span class="font-semibold text-gray-500">Hostname</span>
<span class="font-medium text-gray-900">{{ item.server.hostname|default:"—" }}</span>
</div>
<div class="flex items-center justify-between">
<span class="font-semibold text-gray-500">IPv4</span>
<span class="font-medium text-gray-900">{{ item.server.ipv4|default:"—" }}</span>
</div>
<div class="flex items-center justify-between">
<span class="font-semibold text-gray-500">IPv6</span>
<span class="font-medium text-gray-900">{{ item.server.ipv6|default:"—" }}</span>
</div>
<div class="flex items-center justify-between">
<span class="font-semibold text-gray-500">Last heartbeat</span>
<span class="font-medium text-gray-900">
{% if item.status.heartbeat_at %}{{ item.status.heartbeat_at|date:"M j, Y H:i" }}{% else %}—{% endif %}
</span>
</div>
</div>
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
</div>
</div>
<dl class="mt-4 space-y-2 text-sm text-gray-600">
<div class="flex items-center justify-between">
<dl class="mt-5 divide-y divide-gray-100 text-sm text-gray-600">
<div class="flex items-center justify-between py-2">
<dt>Access until</dt>
<dd class="font-medium text-gray-900">
{% if item.expires_at %}
@@ -35,7 +89,7 @@
{% endif %}
</dd>
</div>
<div class="flex items-center justify-between">
<div class="flex items-center justify-between py-2">
<dt>Last accessed</dt>
<dd class="font-medium text-gray-900">
{% if item.last_accessed %}
@@ -47,16 +101,23 @@
</div>
</dl>
<div class="mt-4 border-t border-gray-100 pt-3 text-xs text-gray-500">
<a href="{% url 'servers:detail' item.server.id %}" class="font-semibold text-purple-700 hover:text-purple-800">View details and logs</a>
<div class="mt-5 flex items-center justify-between border-t border-gray-100 pt-4 text-xs text-gray-500">
<span>Certificates and access</span>
<a href="{% url 'servers:detail' item.server.id %}" class="font-semibold text-blue-700 hover:underline">View details</a>
</div>
</article>
{% endfor %}
</div>
{% else %}
<div class="rounded-xl border border-dashed border-gray-300 bg-white p-10 text-center">
<h2 class="text-lg font-semibold text-gray-900">No server access yet</h2>
<p class="mt-2 text-sm text-gray-600">Request access to a server to see it here.</p>
<div class="rounded-2xl border border-dashed border-gray-200 bg-white p-10 text-center">
<div class="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-blue-50 text-blue-700">
<svg class="h-6 w-6" aria-hidden="true" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12 6v6l4 2" />
<circle cx="12" cy="12" r="9" stroke-width="1.5"></circle>
</svg>
</div>
<h2 class="mt-4 text-lg font-semibold text-gray-900">No server access yet</h2>
<p class="mt-2 text-sm text-gray-600">Request access to a server to see it listed here.</p>
</div>
{% endif %}
</div>

View File

@@ -3,32 +3,39 @@
{% block title %}{{ server.display_name }} • Keywarden{% endblock %}
{% block content %}
<div class="space-y-8">
<div class="space-y-6">
{% include "servers/_header.html" %}
<section class="grid gap-4 lg:grid-cols-3">
<div class="rounded-2xl border border-gray-200 bg-white p-5 shadow-sm">
<div class="rounded-2xl border border-gray-200 bg-white p-6 shadow-sm">
<h2 class="text-lg font-semibold text-gray-900">Server details</h2>
<dl class="mt-4 space-y-3 text-sm text-gray-600">
<div class="flex items-center justify-between">
<dl class="mt-4 divide-y divide-gray-100 text-sm text-gray-600">
<div class="flex items-center justify-between py-2">
<dt>Hostname</dt>
<dd class="font-medium text-gray-900">{{ server.hostname|default:"—" }}</dd>
</div>
<div class="flex items-center justify-between">
<div class="flex items-center justify-between py-2">
<dt>IPv4</dt>
<dd class="font-medium text-gray-900">{{ server.ipv4|default:"—" }}</dd>
</div>
<div class="flex items-center justify-between">
<div class="flex items-center justify-between py-2">
<dt>IPv6</dt>
<dd class="font-medium text-gray-900">{{ server.ipv6|default:"—" }}</dd>
</div>
</dl>
</div>
<div class="rounded-2xl border border-gray-200 bg-white p-5 shadow-sm lg:col-span-2">
<h2 class="text-lg font-semibold text-gray-900">Account & certificate</h2>
<dl class="mt-4 space-y-3 text-sm text-gray-600">
<div class="flex items-center justify-between">
<div class="rounded-2xl border border-gray-200 bg-white p-6 shadow-sm lg:col-span-2">
<div class="flex flex-wrap items-start justify-between gap-4">
<div>
<h2 class="text-lg font-semibold text-gray-900">Account & certificate</h2>
<p class="mt-1 text-sm text-gray-500">Credentials and certificate download options.</p>
</div>
<span class="inline-flex items-center rounded-full bg-blue-50 px-2.5 py-1 text-xs font-semibold text-blue-700">Access</span>
</div>
<dl class="mt-4 divide-y divide-gray-100 text-sm text-gray-600">
<div class="flex items-center justify-between py-2">
<dt>Account name</dt>
<dd class="font-medium text-gray-900">
{% if system_username %}
@@ -38,7 +45,7 @@
{% endif %}
</dd>
</div>
<div class="flex items-center justify-between">
<div class="flex items-center justify-between py-2">
<dt>Account status</dt>
<dd class="font-medium text-gray-900">
{% if account_present is None %}
@@ -50,22 +57,22 @@
{% endif %}
</dd>
</div>
<div class="flex items-center justify-between">
<div class="flex flex-col gap-3 py-2 sm:flex-row sm:items-center sm:justify-between">
<dt>Certificate</dt>
<dd class="font-medium text-gray-900">
{% if certificate_key_id %}
<div class="flex items-center gap-2">
<div class="inline-flex overflow-hidden rounded-md shadow-sm">
<div class="flex flex-wrap items-center gap-2">
<div class="inline-flex rounded-lg shadow-sm" role="group">
<button
type="button"
class="inline-flex items-center rounded-l-md bg-purple-600 px-3 py-1.5 text-xs font-semibold text-white hover:bg-purple-700"
class="inline-flex items-center rounded-l-lg bg-blue-700 px-3 py-1.5 text-xs font-semibold text-white hover:bg-blue-800 focus:outline-none focus:ring-2 focus:ring-blue-300"
data-download-url="/api/v1/keys/{{ certificate_key_id }}/certificate"
>
Download
</button>
<button
type="button"
class="inline-flex items-center rounded-r-md border border-l-0 border-gray-200 bg-gray-100 px-3 py-1.5 text-xs font-semibold text-gray-700 hover:bg-gray-200"
class="inline-flex items-center rounded-r-lg border border-gray-200 bg-white px-3 py-1.5 text-xs font-semibold text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-300"
data-download-url="/api/v1/keys/{{ certificate_key_id }}/certificate.sha256"
>
Hash
@@ -73,7 +80,7 @@
</div>
<button
type="button"
class="inline-flex items-center rounded-md bg-red-600 px-3 py-1.5 text-xs font-semibold text-white hover:bg-red-700 js-regenerate-cert"
class="inline-flex items-center rounded-lg bg-rose-600 px-3 py-1.5 text-xs font-semibold text-white hover:bg-rose-700 focus:outline-none focus:ring-2 focus:ring-rose-300 js-regenerate-cert"
data-key-id="{{ certificate_key_id }}"
>
Regenerate
@@ -87,10 +94,16 @@
</dl>
</div>
<div class="rounded-2xl border border-gray-200 bg-white p-5 shadow-sm lg:col-span-3">
<h2 class="text-lg font-semibold text-gray-900">Access</h2>
<dl class="mt-4 space-y-3 text-sm text-gray-600">
<div class="flex items-center justify-between">
<div class="rounded-2xl border border-gray-200 bg-white p-6 shadow-sm lg:col-span-3">
<div class="flex flex-wrap items-start justify-between gap-4">
<div>
<h2 class="text-lg font-semibold text-gray-900">Access</h2>
<p class="mt-1 text-sm text-gray-500">Review access windows and last usage.</p>
</div>
<span class="inline-flex items-center rounded-full bg-gray-100 px-2.5 py-1 text-xs font-semibold text-gray-700">Usage</span>
</div>
<dl class="mt-4 divide-y divide-gray-100 text-sm text-gray-600">
<div class="flex items-center justify-between py-2">
<dt>Access until</dt>
<dd class="font-medium text-gray-900">
{% if expires_at %}
@@ -100,7 +113,7 @@
{% endif %}
</dd>
</div>
<div class="flex items-center justify-between">
<div class="flex items-center justify-between py-2">
<dt>Last accessed</dt>
<dd class="font-medium text-gray-900">
{% if last_accessed %}

View File

@@ -3,16 +3,25 @@
{% block title %}Settings • {{ server.display_name }} • Keywarden{% endblock %}
{% block content %}
<div class="space-y-8">
<div class="space-y-6">
{% include "servers/_header.html" %}
<section class="rounded-2xl border border-gray-200 bg-white p-5 shadow-sm">
<div class="flex items-center justify-between">
<h2 class="text-lg font-semibold text-gray-900">Settings</h2>
<span class="text-xs font-semibold text-gray-500">Placeholder</span>
<section class="rounded-2xl border border-gray-200 bg-white p-6 shadow-sm">
<div class="flex flex-wrap items-start justify-between gap-4">
<div>
<h2 class="text-lg font-semibold text-gray-900">Settings</h2>
<p class="mt-1 text-sm text-gray-500">Manage server-level access policies and metadata.</p>
</div>
<span class="inline-flex items-center rounded-full bg-gray-100 px-2.5 py-1 text-xs font-semibold text-gray-700">Placeholder</span>
</div>
<div class="mt-4 rounded-xl border border-dashed border-gray-200 bg-gray-50 p-6 text-center text-sm text-gray-600">
Settings will appear here as server options are added.
<div class="mt-5 rounded-xl border border-dashed border-gray-200 bg-gray-50 p-6 text-center">
<div class="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-blue-50 text-blue-700">
<svg class="h-6 w-6" aria-hidden="true" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12 6v6l4 2" />
<circle cx="12" cy="12" r="9" stroke-width="1.5"></circle>
</svg>
</div>
<p class="mt-3 text-sm text-gray-600">Settings will appear here as server options are added.</p>
</div>
</section>
</div>

View File

@@ -18,25 +18,25 @@
{% block content %}
{% if is_popout %}
<div class="w-screen">
<div id="shell-popout-shell" class="w-full border border-gray-200 bg-gray-900 shadow-sm">
<div id="shell-terminal" class="h-full w-full p-2"></div>
<div id="shell-popout-shell" class="w-full rounded-2xl border border-gray-200 bg-slate-950 shadow-sm">
<div id="shell-terminal" class="h-full w-full p-3"></div>
</div>
</div>
{% else %}
<div class="space-y-8">
<div class="space-y-6">
{% include "servers/_header.html" %}
<section class="rounded-2xl border border-gray-200 bg-white p-5 shadow-sm">
<section class="rounded-2xl border border-gray-200 bg-white p-6 shadow-sm">
<div class="flex flex-wrap items-center justify-between gap-3">
<div>
<h2 class="text-lg font-semibold text-gray-900">Shell access</h2>
<p class="mt-1 text-sm text-gray-600">
<p class="mt-1 text-sm text-gray-500">
Connect with your private key and the signed certificate for this server.
</p>
</div>
<button
type="button"
class="inline-flex items-center rounded-md border border-gray-200 bg-white px-3 py-1.5 text-xs font-semibold text-gray-700 hover:bg-gray-50"
class="inline-flex items-center rounded-lg border border-gray-200 bg-white px-3 py-2 text-xs font-semibold text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-300"
id="shell-popout"
data-popout-url="{% url 'servers:shell' server.id %}?popout=1"
>
@@ -44,7 +44,7 @@
</button>
</div>
<div class="mt-4 space-y-4 text-sm text-gray-600">
<div class="mt-5 grid gap-4 text-sm text-gray-600 lg:grid-cols-2">
<dl class="space-y-4">
<div class="flex items-center justify-between">
<dt>Account name</dt>
@@ -69,46 +69,46 @@
</dl>
{% if shell_command %}
<div class="rounded-xl border border-dashed border-gray-200 bg-gray-50 p-4">
<div class="rounded-xl border border-gray-200 bg-gray-50 p-4">
<div class="flex flex-wrap items-center justify-between gap-2">
<span class="text-xs font-semibold text-gray-500">SSH command</span>
<span class="text-xs font-semibold uppercase tracking-wide text-gray-500">SSH command</span>
<button
type="button"
class="text-xs font-semibold text-purple-700 hover:text-purple-800"
class="text-xs font-semibold text-blue-700 hover:underline"
data-copy-target="shell-command"
>
Copy command
</button>
</div>
<code class="mt-2 block break-all text-xs text-gray-800" id="shell-command">{{ shell_command }}</code>
</div>
<div class="flex flex-wrap items-center gap-2">
{% if certificate_key_id %}
<div class="inline-flex overflow-hidden rounded-md shadow-sm">
<code class="mt-3 block break-all rounded-lg bg-white p-3 text-xs text-gray-800" id="shell-command">{{ shell_command }}</code>
<div class="mt-4 flex flex-wrap items-center gap-2">
{% if certificate_key_id %}
<div class="inline-flex rounded-lg shadow-sm" role="group">
<button
type="button"
class="inline-flex items-center rounded-l-lg bg-blue-700 px-3 py-1.5 text-xs font-semibold text-white hover:bg-blue-800 focus:outline-none focus:ring-2 focus:ring-blue-300"
data-download-url="/api/v1/keys/{{ certificate_key_id }}/certificate"
>
Download
</button>
<button
type="button"
class="inline-flex items-center rounded-r-lg border border-gray-200 bg-white px-3 py-1.5 text-xs font-semibold text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-300"
data-download-url="/api/v1/keys/{{ certificate_key_id }}/certificate.sha256"
>
Hash
</button>
</div>
<button
type="button"
class="inline-flex items-center rounded-l-md bg-purple-600 px-3 py-1.5 text-xs font-semibold text-white hover:bg-purple-700"
data-download-url="/api/v1/keys/{{ certificate_key_id }}/certificate"
class="inline-flex items-center rounded-lg bg-rose-600 px-3 py-1.5 text-xs font-semibold text-white hover:bg-rose-700 focus:outline-none focus:ring-2 focus:ring-rose-300 js-regenerate-cert"
data-key-id="{{ certificate_key_id }}"
>
Download
Regenerate
</button>
<button
type="button"
class="inline-flex items-center rounded-r-md border border-l-0 border-gray-200 bg-gray-100 px-3 py-1.5 text-xs font-semibold text-gray-700 hover:bg-gray-200"
data-download-url="/api/v1/keys/{{ certificate_key_id }}/certificate.sha256"
>
Hash
</button>
</div>
<button
type="button"
class="inline-flex items-center rounded-md bg-red-600 px-3 py-1.5 text-xs font-semibold text-white hover:bg-red-700 js-regenerate-cert"
data-key-id="{{ certificate_key_id }}"
>
Regenerate
</button>
{% endif %}
<span class="text-xs text-gray-500">Use the command above for local SSH.</span>
{% endif %}
<span class="text-xs text-gray-500">Use the command above for local SSH.</span>
</div>
</div>
{% else %}
<p class="text-sm text-gray-600">Upload a key to enable downloads and a local SSH command.</p>
@@ -116,26 +116,26 @@
</div>
</section>
<section class="rounded-2xl border border-gray-200 bg-white p-5 shadow-sm">
<section class="rounded-2xl border border-gray-200 bg-white p-6 shadow-sm">
<div class="flex flex-wrap items-center justify-between gap-3">
<div>
<h2 class="text-lg font-semibold text-gray-900">Browser terminal</h2>
<p class="mt-1 text-sm text-gray-600">
<p class="mt-1 text-sm text-gray-500">
Launch a proxied terminal session to the target host in your browser.
</p>
</div>
<div class="flex items-center gap-2">
<span class="text-xs font-semibold text-gray-500">Beta</span>
<span class="inline-flex items-center rounded-full bg-amber-100 px-2.5 py-1 text-xs font-semibold text-amber-800">Beta</span>
<button
type="button"
class="inline-flex items-center rounded-md bg-purple-600 px-3 py-1.5 text-xs font-semibold text-white hover:bg-purple-700"
class="inline-flex items-center rounded-lg bg-blue-700 px-3 py-2 text-xs font-semibold text-white hover:bg-blue-800 focus:outline-none focus:ring-4 focus:ring-blue-300"
id="shell-start"
>
Start terminal
</button>
</div>
</div>
<div class="mt-4 rounded-xl border border-gray-200 bg-gray-900 p-2">
<div class="mt-4 rounded-xl border border-gray-200 bg-slate-950 p-2">
<div id="shell-terminal" class="h-96"></div>
</div>
<p class="mt-3 text-xs text-gray-500">
@@ -279,8 +279,8 @@
startButton.textContent = isRunning ? "Stop terminal" : "Start terminal";
startButton.classList.toggle("bg-red-600", isRunning);
startButton.classList.toggle("hover:bg-red-700", isRunning);
startButton.classList.toggle("bg-purple-600", !isRunning);
startButton.classList.toggle("hover:bg-purple-700", !isRunning);
startButton.classList.toggle("bg-blue-700", !isRunning);
startButton.classList.toggle("hover:bg-blue-800", !isRunning);
}
function stopTerminal() {
@@ -313,9 +313,9 @@
fontFamily: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace",
fontSize: 13,
theme: {
background: "#0b0f12",
foreground: "#d1d5db",
cursor: "#a855f7"
background: "#0b1120",
foreground: "#e2e8f0",
cursor: "#38bdf8"
}
});
term.open(termContainer);