Auto-generate HMAC-SHA256 webhook secret when enabling the Gaiia integration. Display webhook URL, secret with copy buttons, regenerate option, and setup instructions on the integrations page.
317 lines
14 KiB
Text
317 lines
14 KiB
Text
<Layouts.authenticated
|
|
flash={@flash}
|
|
current_scope={@current_scope}
|
|
>
|
|
<div class="border-b border-gray-200 pb-5 dark:border-white/5">
|
|
<div class="mb-4">
|
|
<.link
|
|
navigate={~p"/orgs/#{@organization.slug}/settings"}
|
|
class="inline-flex items-center gap-1 text-sm text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white"
|
|
>
|
|
<.icon name="hero-arrow-left" class="h-4 w-4" /> Back to Settings
|
|
</.link>
|
|
</div>
|
|
<h1 class="text-3xl font-semibold tracking-tight text-gray-900 dark:text-white">
|
|
Integrations
|
|
</h1>
|
|
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
|
|
Connect third-party services to enhance your monitoring capabilities.
|
|
</p>
|
|
</div>
|
|
|
|
<div class="mt-8 space-y-6">
|
|
<div
|
|
:for={provider <- @providers}
|
|
class="rounded-lg border border-gray-200 bg-white p-6 dark:border-white/10 dark:bg-white/5"
|
|
>
|
|
<div class="flex items-start justify-between">
|
|
<div class="flex items-start gap-4">
|
|
<div class="flex h-12 w-12 items-center justify-center rounded-lg bg-indigo-50 dark:bg-indigo-900/30">
|
|
<.icon name={provider.icon} class="h-6 w-6 text-indigo-600 dark:text-indigo-400" />
|
|
</div>
|
|
<div>
|
|
<h3 class="text-lg font-semibold text-gray-900 dark:text-white">
|
|
{provider.name}
|
|
</h3>
|
|
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
|
|
{provider.description}
|
|
</p>
|
|
|
|
<%= if integration = @integrations[provider.id] do %>
|
|
<div class="mt-3 flex items-center gap-4">
|
|
<span class={[
|
|
"inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium",
|
|
if(integration.enabled,
|
|
do: "bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400",
|
|
else: "bg-gray-100 text-gray-600 dark:bg-gray-800 dark:text-gray-400"
|
|
)
|
|
]}>
|
|
{if integration.enabled, do: "Enabled", else: "Disabled"}
|
|
</span>
|
|
|
|
<%= if integration.last_synced_at do %>
|
|
<span class="text-xs text-gray-500 dark:text-gray-400">
|
|
Last synced:
|
|
<.timestamp
|
|
datetime={integration.last_synced_at}
|
|
timezone={@timezone}
|
|
format="absolute"
|
|
/>
|
|
</span>
|
|
<% end %>
|
|
|
|
<%= if integration.last_sync_status && integration.last_sync_status != "never" do %>
|
|
<span class={[
|
|
"inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium",
|
|
case integration.last_sync_status do
|
|
"success" ->
|
|
"bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400"
|
|
|
|
"partial" ->
|
|
"bg-yellow-100 text-yellow-700 dark:bg-yellow-900/30 dark:text-yellow-400"
|
|
|
|
"failed" ->
|
|
"bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400"
|
|
|
|
_ ->
|
|
"bg-gray-100 text-gray-600 dark:bg-gray-800 dark:text-gray-400"
|
|
end
|
|
]}>
|
|
{String.capitalize(integration.last_sync_status)}
|
|
</span>
|
|
<% end %>
|
|
|
|
<%= if provider.id == "preseem" do %>
|
|
<.link
|
|
navigate={
|
|
~p"/orgs/#{@organization.slug}/settings/integrations/preseem/devices"
|
|
}
|
|
class="text-sm text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300"
|
|
>
|
|
Manage Devices →
|
|
</.link>
|
|
<.link
|
|
navigate={
|
|
~p"/orgs/#{@organization.slug}/settings/integrations/preseem/insights"
|
|
}
|
|
class="text-sm text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300"
|
|
>
|
|
Network Insights →
|
|
</.link>
|
|
<% end %>
|
|
<%= if provider.id == "gaiia" do %>
|
|
<.link
|
|
navigate={~p"/orgs/#{@organization.slug}/settings/integrations/gaiia/mapping"}
|
|
class="text-sm text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300"
|
|
>
|
|
Entity Mapping →
|
|
</.link>
|
|
<.link
|
|
navigate={
|
|
~p"/orgs/#{@organization.slug}/settings/integrations/gaiia/reconciliation"
|
|
}
|
|
class="text-sm text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300"
|
|
>
|
|
Reconciliation →
|
|
</.link>
|
|
<% end %>
|
|
</div>
|
|
<% end %>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex items-center gap-3">
|
|
<%= if integration = @integrations[provider.id] do %>
|
|
<button
|
|
type="button"
|
|
phx-click="toggle_enabled"
|
|
phx-value-provider={provider.id}
|
|
class={[
|
|
"relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2",
|
|
if(integration.enabled,
|
|
do: "bg-indigo-600",
|
|
else: "bg-gray-200 dark:bg-gray-700"
|
|
)
|
|
]}
|
|
>
|
|
<span class={[
|
|
"pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out",
|
|
if(integration.enabled, do: "translate-x-5", else: "translate-x-0")
|
|
]}>
|
|
</span>
|
|
</button>
|
|
<% end %>
|
|
|
|
<button
|
|
type="button"
|
|
id={"configure-#{provider.id}"}
|
|
phx-click="configure"
|
|
phx-value-provider={provider.id}
|
|
class="rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-xs ring-1 ring-inset ring-gray-300 hover:bg-gray-50 dark:bg-white/10 dark:text-white dark:ring-white/10 dark:hover:bg-white/20"
|
|
>
|
|
Configure
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<%= if @configuring == provider.id do %>
|
|
<div class="mt-6 border-t border-gray-200 pt-6 dark:border-white/10">
|
|
<.form
|
|
for={@form}
|
|
id={"#{provider.id}-form"}
|
|
phx-change="validate"
|
|
phx-submit="save"
|
|
>
|
|
<div class="space-y-4">
|
|
<.input
|
|
field={@form[:api_key]}
|
|
type="password"
|
|
label="API Key"
|
|
placeholder={"Enter your #{provider.name} API key"}
|
|
autocomplete="off"
|
|
value={get_credential(@integrations[provider.id], "api_key")}
|
|
/>
|
|
|
|
<%= if @test_result do %>
|
|
<div class={[
|
|
"rounded-md p-4",
|
|
case @test_result do
|
|
{:ok, _} -> "bg-green-50 dark:bg-green-900/20"
|
|
{:error, _} -> "bg-red-50 dark:bg-red-900/20"
|
|
end
|
|
]}>
|
|
<p class={[
|
|
"text-sm",
|
|
case @test_result do
|
|
{:ok, _} -> "text-green-800 dark:text-green-200"
|
|
{:error, _} -> "text-red-800 dark:text-red-200"
|
|
end
|
|
]}>
|
|
{elem(@test_result, 1)}
|
|
</p>
|
|
</div>
|
|
<% end %>
|
|
|
|
<div class="flex items-center justify-between pt-4">
|
|
<button
|
|
type="button"
|
|
id="test-connection"
|
|
phx-click="test_connection"
|
|
class="rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-xs ring-1 ring-inset ring-gray-300 hover:bg-gray-50 dark:bg-white/10 dark:text-white dark:ring-white/10 dark:hover:bg-white/20"
|
|
>
|
|
Test Connection
|
|
</button>
|
|
|
|
<div class="flex items-center gap-3">
|
|
<button
|
|
type="button"
|
|
phx-click="close_config"
|
|
class="text-sm font-semibold text-gray-900 dark:text-white"
|
|
>
|
|
Cancel
|
|
</button>
|
|
<button
|
|
type="submit"
|
|
phx-disable-with="Saving..."
|
|
class="rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-xs hover:bg-indigo-500 dark:bg-indigo-500 dark:hover:bg-indigo-400"
|
|
>
|
|
Save
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</.form>
|
|
</div>
|
|
<% end %>
|
|
|
|
<%= if provider.id == "gaiia" && @integrations["gaiia"] do %>
|
|
<div class="mt-6 border-t border-gray-200 pt-6 dark:border-white/10">
|
|
<h4 class="text-sm font-semibold text-gray-900 dark:text-white">
|
|
Webhook Configuration
|
|
</h4>
|
|
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
|
Receive real-time updates from Gaiia when accounts, subscriptions, or inventory items change.
|
|
</p>
|
|
|
|
<div class="mt-4 space-y-4">
|
|
<div>
|
|
<label class="block text-xs font-medium text-gray-700 dark:text-gray-300">
|
|
Webhook URL
|
|
</label>
|
|
<div class="mt-1 flex items-center gap-2">
|
|
<input
|
|
id="gaiia-webhook-url"
|
|
type="text"
|
|
readonly
|
|
value={webhook_url(@organization.id)}
|
|
class="block w-full rounded-md border-gray-300 bg-gray-50 py-1.5 text-sm text-gray-900 shadow-xs sm:leading-6 dark:border-white/10 dark:bg-white/5 dark:text-white"
|
|
/>
|
|
<button
|
|
type="button"
|
|
phx-hook="CopyToClipboard"
|
|
data-target="#gaiia-webhook-url"
|
|
id="copy-webhook-url"
|
|
class="inline-flex items-center gap-1 rounded-md bg-white px-2.5 py-1.5 text-xs font-medium text-gray-700 shadow-xs ring-1 ring-inset ring-gray-300 hover:bg-gray-50 dark:bg-white/10 dark:text-gray-300 dark:ring-white/10 dark:hover:bg-white/20"
|
|
>
|
|
<.icon name="hero-clipboard" class="h-3.5 w-3.5" /> Copy
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<label class="block text-xs font-medium text-gray-700 dark:text-gray-300">
|
|
Webhook Secret
|
|
</label>
|
|
<div class="mt-1 flex items-center gap-2">
|
|
<input
|
|
id="gaiia-webhook-secret"
|
|
type="text"
|
|
readonly
|
|
value={get_credential(@integrations["gaiia"], "webhook_secret")}
|
|
class="block w-full rounded-md border-gray-300 bg-gray-50 py-1.5 font-mono text-sm text-gray-900 shadow-xs sm:leading-6 dark:border-white/10 dark:bg-white/5 dark:text-white"
|
|
/>
|
|
<button
|
|
type="button"
|
|
phx-hook="CopyToClipboard"
|
|
data-target="#gaiia-webhook-secret"
|
|
id="copy-webhook-secret"
|
|
class="inline-flex items-center gap-1 rounded-md bg-white px-2.5 py-1.5 text-xs font-medium text-gray-700 shadow-xs ring-1 ring-inset ring-gray-300 hover:bg-gray-50 dark:bg-white/10 dark:text-gray-300 dark:ring-white/10 dark:hover:bg-white/20"
|
|
>
|
|
<.icon name="hero-clipboard" class="h-3.5 w-3.5" /> Copy
|
|
</button>
|
|
</div>
|
|
<div class="mt-2">
|
|
<button
|
|
type="button"
|
|
id="regenerate-webhook-secret"
|
|
phx-click="regenerate_webhook_secret"
|
|
data-confirm="Are you sure? Any existing Gaiia webhook using the current secret will stop working."
|
|
class="text-xs text-red-600 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300"
|
|
>
|
|
Regenerate Secret
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="rounded-md bg-blue-50 p-3 dark:bg-blue-900/20">
|
|
<h5 class="text-xs font-medium text-blue-800 dark:text-blue-300">
|
|
Setup Instructions
|
|
</h5>
|
|
<ol class="mt-2 list-inside list-decimal space-y-1 text-xs text-blue-700 dark:text-blue-400">
|
|
<li>In your Gaiia admin panel, go to Settings → Webhooks</li>
|
|
<li>Click "Add Webhook"</li>
|
|
<li>Paste the Webhook URL above</li>
|
|
<li>Paste the Webhook Secret above into the "Secret" field</li>
|
|
<li>Select events: Account, Billing Subscription, Inventory Item</li>
|
|
<li>Save the webhook</li>
|
|
</ol>
|
|
<p class="mt-2 text-xs text-blue-600 dark:text-blue-400">
|
|
Once configured, Towerops will receive real-time updates when accounts, subscriptions, or inventory items change in Gaiia.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<% end %>
|
|
</div>
|
|
</div>
|
|
</Layouts.authenticated>
|