@if (!string.IsNullOrEmpty(cluster.LastError) && cluster.Status == RabbitMQClusterStatus.Failed)
{
}
@if (expanded)
{
@foreach (string tab in new[] { "vhosts", "queues", "exchanges", "routing", "users", "app-links", "backup", "credentials", "metrics" })
{
SetClusterTab(tab, cluster.Id)">
@TabLabel(tab)
}
@if (!string.IsNullOrEmpty(topologyError))
{
@topologyError
topologyError = null">
}
@* ── Vhosts ── *@
@if (clusterTab == "vhosts")
{
Virtual Hosts
showVhostForm = !showVhostForm">
Add Vhost
@if (showVhostForm)
{
CreateVhost(cluster)" disabled="@submitting">
@if (submitting) { }Create
showVhostForm = false">Cancel
}
@if (loadingTopology)
{
}
else if (vhosts.Count == 0)
{
No vhosts managed via topology operator yet.
}
else
{
Vhost K8s Name
@foreach (RabbitMQVhostInfo v in vhosts)
{
@v.VhostName
@v.K8sName
DeleteVhost(cluster, v.K8sName)">
}
}
}
@* ── Queues ── *@
@if (clusterTab == "queues")
{
Queues
showQueueForm = !showQueueForm">
Add Queue
@if (showQueueForm)
{
}
@if (loadingTopology) {
}
else if (queues.Count == 0) {
No queues yet.
}
else
{
Name Vhost Type Durable
@foreach (RabbitMQQueueInfo q in queues)
{
@q.QueueName
@q.Vhost
@q.QueueType
@(q.Durable ? "✓" : "—")
PrepareAppLink(cluster, q.QueueName, null, q.Vhost)">
Link to App
DeleteQueue(cluster, q.K8sName)">
}
}
}
@* ── Exchanges ── *@
@if (clusterTab == "exchanges")
{
Exchanges
showExchangeForm = !showExchangeForm">
Add Exchange
@if (showExchangeForm)
{
}
@if (loadingTopology) {
}
else if (exchanges.Count == 0) {
No exchanges yet.
}
else
{
Name Vhost Type Durable
@foreach (RabbitMQExchangeInfo ex in exchanges)
{
@ex.ExchangeName
@ex.Vhost
@ex.ExchangeType
@(ex.Durable ? "✓" : "—")
PrepareAppLink(cluster, null, ex.ExchangeName, ex.Vhost)">
Link to App
DeleteExchange(cluster, ex.K8sName)">
}
}
}
@* ── Routing Bindings ── *@
@if (clusterTab == "routing")
{
Routing Bindings
showRoutingForm = !showRoutingForm">
Add Binding
@if (showRoutingForm)
{
}
@if (loadingTopology) {
}
else if (routingBindings.Count == 0) {
No routing bindings yet.
}
else
{
Source → Destination Type Routing Key Vhost
@foreach (RabbitMQRoutingBindingInfo rb in routingBindings)
{
@rb.Source
→
@rb.Destination
@rb.DestinationType
@rb.RoutingKey
@rb.Vhost
DeleteRoutingBinding(cluster, rb.K8sName)">
}
}
}
@* ── Users ── *@
@if (clusterTab == "users")
{
Users
showUserForm = !showUserForm">
Add User
@if (showUserForm)
{
}
@if (loadingTopology) {
}
else if (users.Count == 0) {
No topology-managed users yet.
}
else
{
Username Tags K8s Name
@foreach (RabbitMQUserInfo u in users)
{
@u.Username
@foreach (string tag in u.Tags)
{
@tag
}
@u.K8sName
PreparePermission(u.Username)">
Permissions
DeleteUser(cluster, u.K8sName)">
}
}
@if (showPermissionForm && permissionTargetUser is not null)
{
}
}
@* ── App Links ── *@
@if (clusterTab == "app-links")
{
App Links
showAppLinkForm = !showAppLinkForm">
Link App
Each link provisions a dedicated RabbitMQ user for the app and syncs the connection details
(host, port, vhost, credentials, queue/exchange) into a Kubernetes Secret in the app's namespace.
@if (showAppLinkForm)
{
A dedicated RabbitMQ user with full access to the chosen vhost will be automatically
created and its credentials stored in the vault, then synced into the K8s secret below.
}
@if (loadingTopology) {
}
else if (messagingBindings.Count == 0) {
No app links yet.
}
else
{
App / Deployment Vhost Queue Exchange Secret Last Synced
@foreach (MessagingBinding mb in messagingBindings)
{
@mb.AppDeployment.App.Customer.Name / @mb.AppDeployment.App.Name
@mb.AppDeployment.Name (@mb.AppDeployment.Cluster.Name)
@mb.Vhost
@(mb.QueueName ?? "—")
@(mb.ExchangeName ?? "—")
@mb.KubernetesSecretName
@if (mb.LastSyncedAt.HasValue)
{
@mb.LastSyncedAt.Value.ToLocalTime().ToString("MM-dd HH:mm")
}
else
{
Not synced
}
SyncAppLink(mb)" disabled="@(syncingLinkId == mb.Id)">
@if (syncingLinkId == mb.Id)
{
}
else
{
}
DeleteAppLink(cluster, mb)">
}
}
}
@* ── Backup & Restore ── *@
@if (clusterTab == "backup")
{
@if (!cluster.StorageLinkId.HasValue)
{
No S3 storage link is attached to this cluster. Edit the cluster to add one before creating backups.
}
else
{
Definitions exported via rabbitmqctl export_definitions and stored as JSON in S3.
TriggerBackup(cluster)"
disabled="@(backingUp == cluster.Id)">
@if (backingUp == cluster.Id) { }
Export Now
}
@if (!string.IsNullOrEmpty(backupError))
{
@backupError
}
@if (showRestoreForm && restoreBackupId.HasValue)
{
Target Cluster
This cluster — @cluster.Name
@foreach (RabbitMQCluster other in clusters.Where(c => c.Id != cluster.Id))
{
@other.Name (@other.KubernetesCluster.Name)
}
@if (restoring) { }Restore
{ showRestoreForm = false; restoreBackupId = null; }">Cancel
}
@if (loadingBackups) {
}
else if (clusterBackups.Count == 0) {
No backups yet.
}
else
{
Created Size Status
@foreach (RabbitMQBackup backup in clusterBackups)
{
@backup.CreatedAt.ToLocalTime().ToString("yyyy-MM-dd HH:mm")
@FormatBytes(backup.SizeBytes)
@backup.Status
@if (backup.Status == RabbitMQBackupStatus.Ready)
{
PrepareRestore(backup)">
Restore
}
DeleteBackup(backup)">
}
}
}
@* ── Credentials ── *@
@if (clusterTab == "credentials")
{
Admin credentials generated by the Cluster Operator, stored encrypted in the tenant vault.
Source: K8s Secret @(cluster.Name)-default-user in @cluster.Namespace.
@if (loadingCredentials) {
}
else if (adminUsername is not null)
{
Stored in vault
}
else
{
Credentials not yet in vault. Click Sync from K8s once the cluster is running.
}
@if (!string.IsNullOrEmpty(credentialError))
{
@credentialError
}
SyncCredentials(cluster)" disabled="@syncingCredentials">
@if (syncingCredentials) { }
Sync from K8s
LoadCredentials(cluster)">
Refresh
ConfirmDeleteCluster(cluster)">
Delete Cluster
}
@if (clusterTab == "metrics")
{
}