Files
ubicloud/views/postgres/show.erb
2025-01-21 16:19:12 +05:30

384 lines
18 KiB
Plaintext

<% @page_title = @pg[:name]
edit_perm = has_permission?("Postgres:edit", @pg[:id])
delete_perm = has_permission?("Postgres:delete", @pg[:id]) %>
<%== render(
"components/page_header",
locals: {
breadcrumbs: [
%w[Projects /project],
[@project_data[:name], @project_data[:path]],
["PostgreSQL Databases", "#{@project_data[:path]}/postgres"],
[@pg[:name], "#"]
],
right_items: [render("components/pg_state_label", locals: { state: @pg[:state], extra_class: "text-md" })]
}
) %>
<div class="grid gap-6">
<!-- Detail Card -->
<% data = [
["ID", @pg[:id]],
["Name", @pg[:name]],
["Location", @pg[:location]],
["Compute", @pg[:vm_size]],
["Storage", "#{@pg[:storage_size_gib]} GB"],
["Version", "Postgres #{@pg[:version]}"],
["High Availability", PG_HA_DATA[@pg[:ha_type]]]
]
if @pg[:connection_string]
data.push(["Connection String", @pg[:connection_string], { copyable: true, revealable: true }])
else
data.push(["Connection String", "Waiting for host to be ready..."])
end
if @pg[:ca_certificates]
data.push(
[
"CA Certificates",
render("components/download_button", locals: { link: "#{request.path}/ca-certificates" }),
{ escape: false }
]
)
else
data.push(["CA Certificates", "Waiting for host to be ready..."])
end %>
<div class="grid grid-cols-1 gap-6 md:grid-cols-6">
<div class="<%= (@pg[:flavor] == PostgresResource::Flavor::STANDARD) ? "col-span-6" : "md:col-span-3" %>">
<%== render("components/kv_data_card", locals: { data: data }) %>
</div>
<% if @pg[:flavor] == PostgresResource::Flavor::PARADEDB %>
<div class="md:col-span-3">
<div class="overflow-hidden rounded-lg shadow ring-1 ring-black ring-opacity-5 bg-white divide-y divide-gray-200">
<div class="px-4 py-5 sm:p-6">
<div class="space-y-2">
<img src="/logo-paradedb.png" class="h-6 object-contain mb-4"/>
<p class="text-sm text-gray-500 leading-6">
ParadeDB is an Elasticsearch alternative built on Postgres. ParadeDB instances are managed by the
ParadeDB team and are optimal for search and analytics workloads.
</p>
<div class="text-sm text-gray-500 leading-6">
<span class="font-semibold">Support:</span>
<ul class="list-disc list-inside ml-2">
<li>Via email at
<a href="mailto:support@paradedb.com" class="text-orange-600 font-semibold">support@paradedb.com</a></li>
<li>Via Slack at
<a
href="https://join.slack.com/t/paradedbcommunity/shared_invite/zt-2lkzdsetw-OiIgbyFeiibd1DG~6wFgTQ"
target="_blank"
class="text-orange-600 font-semibold"
>ParadeDB Community Slack</a></li>
</ul>
</div>
<div class="text-sm text-gray-500 leading-6">
<span class="font-semibold">Documentation:</span>
<ul class="list-disc list-inside ml-2">
<li>To start writing queries:
<a href="https://docs.paradedb.com/welcome/quickstart" class="text-orange-600 font-semibold">https://docs.paradedb.com/welcome/quickstart</a></li>
<li>To ingest data from existing database(s) or data lake(s):
<a href="https://docs.paradedb.com/ingest/quickstart" class="text-orange-600 font-semibold">https://docs.paradedb.com/ingest/quickstart</a></li>
</ul>
</div>
<p class="text-sm text-gray-500 leading-6">
* Note that ingesting into Ubicloud ParadeDB PostgreSQL via logical replication is not yet supported.
All other ParadeDB ingestion schemes are supported.
</p>
</div>
</div>
</div>
</div>
<% elsif @pg[:flavor] == PostgresResource::Flavor::LANTERN %>
<div class="md:col-span-3">
<div class="overflow-hidden rounded-lg shadow ring-1 ring-black ring-opacity-5 bg-white divide-y divide-gray-200">
<div class="px-4 py-5 sm:p-6">
<div class="space-y-2">
<img src="/logo-lantern.png" class="h-6 object-contain mb-4"/>
<p class="text-sm text-gray-500 leading-6">
Lantern is a PostgreSQL-based vector database designed specifically for building AI applications.
Lantern instances are managed by the Lantern team and are optimal for AI workloads.
</p>
<div class="text-sm text-gray-500 leading-6">
You can reach to Lantern team for support at
<a href="mailto:support@lantern.dev" class="text-orange-600 font-semibold">support@lantern.dev</a>
</div>
<div class="text-sm text-gray-500 leading-6">
Check out
<a href="https://lantern.dev/docs" class="text-orange-600 font-semibold">Lantern Documentation</a>
to get more information about Lantern.
</div>
</div>
</div>
</div>
</div>
<% end %>
</div>
<!-- Fork database -->
<% if @pg[:earliest_restore_time] && @pg[:latest_restore_time] > @pg[:earliest_restore_time] %>
<div class="overflow-hidden rounded-lg shadow ring-1 ring-black ring-opacity-5 bg-white divide-y divide-gray-200">
<div class="px-4 py-5 sm:p-6">
<form action="<%= "#{@project_data[:path]}#{@pg[:path]}/restore" %>" role="form" method="POST">
<%== csrf_tag("#{@project_data[:path]}#{@pg[:path]}/restore") %>
<div class="space-y-4">
<div>
<h2 class="text-lg font-medium leading-6 text-gray-900">Fork PostgreSQL database</h2>
<p class="mt-1 text-sm text-gray-500">
When you fork your existing PostgreSQL database, a new server will be provisioned.
</p>
</div>
<div class="grid grid-cols-12 gap-6">
<div class="col-span-12 sm:col-span-5">
<%== render("components/form/text", locals: { label: "New server name", name: "name", attributes: { required: true } }) %>
</div>
<div class="col-span-12 sm:col-span-5">
<%== render(
"components/form/datepicker",
locals: {
label: "Target Time (UTC)",
name: "restore_target",
default_date: @pg[:latest_restore_time],
max_date: @pg[:latest_restore_time],
min_date: @pg[:earliest_restore_time]
}
) %>
</div>
<div class="col-span-12 sm:col-span-2 flex justify-end items-end">
<%== render("components/form/submit_button", locals: { text: "Fork" }) %>
</div>
</div>
</div>
</form>
</div>
</div>
<% end %>
<!-- Firewall Rules Card -->
<div class="md:flex md:items-center md:justify-between pb-2 lg:pb-4">
<div class="min-w-0 flex-1">
<h3 class="text-2xl font-bold leading-7 text-gray-900 sm:truncate sm:text-2xl sm:tracking-tight">
Firewall Rules
</h3>
</div>
</div>
<div class="overflow-hidden rounded-lg shadow ring-1 ring-black ring-opacity-5 bg-white divide-y divide-gray-200">
<table class="min-w-full divide-y divide-gray-300">
<thead class="bg-gray-50">
<tr>
<th scope="col" class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">CIDR</th>
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">Port Range</th>
<% if edit_perm %>
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900"></th>
<% end %>
</tr>
</thead>
<tbody class="divide-y divide-gray-200 bg-white">
<% @pg[:firewall_rules].each do |fwr| %>
<tr>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-6" scope="row"><%= fwr[:cidr] %></td>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-6" scope="row">5432</td>
<% if edit_perm %>
<td
id="fwr-delete-<%=fwr[:id]%>"
class="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-6"
>
<%== render("components/delete_button", locals: { url: "#{request.path}/firewall-rule/#{fwr[:id]}", text: "" }) %>
</td>
<% end %>
</tr>
<% end %>
<% if edit_perm %>
<tr>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-6" scope="row">
<%== render(
"components/form/text",
locals: {
name: "cidr",
type: "cidr",
attributes: {
placeholder: "0.0.0.0/0",
required: true,
form: "form-pg-fwr-create"
}
}
) %>
</td>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-6" scope="row">
5432
</td>
<td class="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-6">
<form action="<%= "#{request.path}/firewall-rule" %>" role="form" method="POST" id="form-pg-fwr-create">
<%== csrf_tag("#{request.path}/firewall-rule") %>
<%== render("components/form/submit_button", locals: { text: "Create", extra_class: "firewall-rule-create-button" }) %>
</form>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
<!-- Metric Destination Card -->
<% if edit_perm %>
<div class="md:flex md:items-center md:justify-between pb-2 lg:pb-4">
<div class="min-w-0 flex-1">
<h3 class="text-2xl font-bold leading-7 text-gray-900 sm:truncate sm:text-2xl sm:tracking-tight">
Metric Destinations
</h3>
</div>
</div>
<div class="overflow-hidden rounded-lg shadow ring-1 ring-black ring-opacity-5 bg-white divide-y divide-gray-200">
<table class="min-w-full divide-y divide-gray-300">
<thead class="bg-gray-50">
<tr>
<th scope="col" class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">URL</th>
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">Username</th>
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">Password</th>
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900"></th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200 bg-white">
<% @pg[:metric_destinations].each do |md| %>
<tr>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-6" scope="row"><%= md[:url] %></td>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-6" scope="row"><%= md[:username] %></td>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-6" scope="row">●●●●●●</td>
<td
id="md-delete-<%=md[:id]%>"
class="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-6"
>
<%== render("components/delete_button", locals: { url: "#{request.path}/metric-destination/#{md[:id]}", text: "" }) %>
</td>
</tr>
<% end %>
<tr>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-6" scope="row">
<%== render("components/form/text", locals: { name: "url", attributes: { form: "form-pg-md-create", required: true } }) %>
</td>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-6" scope="row">
<%== render("components/form/text", locals: { name: "username", attributes: { form: "form-pg-md-create", required: true } }) %>
</td>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-6" scope="row">
<%== render(
"components/form/text",
locals: {
name: "metric-destination-password",
type: "password",
attributes: {
required: true,
form: "form-pg-md-create",
autocomplete: "new-password"
},
extra_class: "metric-destination-password"
}
) %>
</td>
<td class="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-6">
<form action="<%= "#{request.path}/metric-destination" %>" role="form" method="POST" id="form-pg-md-create">
<%== csrf_tag("#{request.path}/metric-destination") %>
<%== render("components/form/submit_button", locals: { text: "Create", extra_class: "metric-destination-create-button" }) %>
</form>
</td>
</tr>
</tbody>
</table>
</div>
<% end %>
<!-- Danger Zone -->
<% if edit_perm || delete_perm || @pg[:primary] %>
<div>
<div class="md:flex md:items-center md:justify-between pb-2 lg:pb-4">
<div class="min-w-0 flex-1">
<h3 class="text-2xl font-bold leading-7 text-gray-900 sm:truncate sm:text-2xl sm:tracking-tight">
Danger Zone
</h3>
</div>
</div>
<div class="overflow-hidden rounded-lg shadow ring-1 ring-black ring-opacity-5 bg-white divide-y divide-gray-200">
<!-- Reset password -->
<% if @pg[:primary] %>
<div class="px-4 py-5 sm:p-6">
<form action="<%= "#{@project_data[:path]}#{@pg[:path]}/reset-superuser-password" %>" role="form" method="POST">
<%== csrf_tag("#{@project_data[:path]}#{@pg[:path]}/reset-superuser-password") %>
<div class="space-y-4">
<div>
<h3 class="text-base font-semibold leading-6 text-gray-900">Reset superuser password</h3>
</div>
<div class="grid grid-cols-12 gap-6">
<div class="col-span-12 sm:col-span-5">
<%== render(
"components/form/text",
locals: {
label: "New password",
name: "password",
type: "password",
attributes: {
required: true
},
extra_class: "reset-superuser-password-new-password"
}
) %>
</div>
<div class="col-span-12 sm:col-span-5">
<%== render(
"components/form/text",
locals: {
label: "New password (repeat)",
name: "repeat_password",
type: "password",
attributes: {
required: true
},
extra_class: "reset-superuser-password-new-password-repeat"
}
) %>
</div>
<div class="col-span-12 sm:col-span-2 flex justify-end items-end">
<%== render("components/form/submit_button", locals: { text: "Reset" }) %>
</div>
</div>
</div>
</form>
</div>
<% end %>
<!-- Restart Card -->
<% if edit_perm %>
<div class="px-4 py-5 sm:p-6">
<form action="<%= "#{@project_data[:path]}#{@pg[:path]}/restart" %>" role="form" method="POST">
<%== csrf_tag("#{@project_data[:path]}#{@pg[:path]}/restart") %>
<div class="sm:flex sm:items-center sm:justify-between">
<div>
<h3 class="text-base font-semibold leading-6 text-gray-900">Restart PostgreSQL database</h3>
<div class="mt-2 text-sm text-gray-500">
<p>This action will restart the PostgreSQL database. The database will be offline momentarily, and
all connections will be dropped.</p>
</div>
</div>
<div id="postgres-restart-<%=@pg[:id]%>" class="mt-5 sm:ml-6 sm:mt-0 sm:flex sm:flex-shrink-0 sm:items-center">
<div class="col-span-12 sm:col-span-2 flex justify-end items-end">
<%== render("components/form/submit_button", locals: { text: "Restart", extra_class: "restart-btn" }) %>
</div>
</div>
</div>
</form>
</div>
<% end %>
<!-- Delete Card -->
<% if delete_perm %>
<div class="px-4 py-5 sm:p-6">
<div class="sm:flex sm:items-center sm:justify-between">
<div>
<h3 class="text-base font-semibold leading-6 text-gray-900">Delete PostgreSQL database</h3>
<div class="mt-2 text-sm text-gray-500">
<p>This action will permanently delete this PostgreSQL database.</p>
</div>
</div>
<div id="postgres-delete-<%=@pg[:id]%>" class="mt-5 sm:ml-6 sm:mt-0 sm:flex sm:flex-shrink-0 sm:items-center">
<%== render("components/delete_button", locals: { confirmation: @pg[:name], redirect: "#{@project_data[:path]}/postgres" }) %>
</div>
</div>
</div>
<% end %>
</div>
</div>
<% end %>
</div>