From 3adc0d83232dcf264e14f61baf3d581226876f59 Mon Sep 17 00:00:00 2001 From: Naeel Date: Thu, 9 Apr 2026 06:17:21 +0300 Subject: [PATCH] sqs-operator v0.1.12: ingress /queues/* -> elasticmq-ui (Next.js basePath fixes) --- .../controller/queueservice_controller.go | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/sqs-operator/internal/controller/queueservice_controller.go b/sqs-operator/internal/controller/queueservice_controller.go index a7289ce..d722061 100644 --- a/sqs-operator/internal/controller/queueservice_controller.go +++ b/sqs-operator/internal/controller/queueservice_controller.go @@ -170,6 +170,12 @@ func (r *QueueServiceReconciler) provision(ctx context.Context, qs *sqsv1alpha1. if err := r.ensureIngressUIAssets(ctx, qs, tenantNS, assetsIngName, svcName); err != nil { return r.setFailed(ctx, qs, fmt.Sprintf("ensure ingress-ui-assets: %v", err)) } + // Ingress для внутренних маршрутов Next.js (/queues/[name]). + // Next.js собран с basePath="" — browser navigates /queues/xxx напрямую без /sqs-ui/tenantID/ префикса. + queuesIngName := "sqs-ing-ui-queues-" + tenantID + if err := r.ensureIngressUIQueues(ctx, qs, tenantNS, queuesIngName, svcName); err != nil { + return r.setFailed(ctx, qs, fmt.Sprintf("ensure ingress-ui-queues: %v", err)) + } } // Все ресурсы созданы — переходим в Provisioning, ждём готовности pod @@ -245,6 +251,10 @@ func (r *QueueServiceReconciler) ensureHealthy(ctx context.Context, qs *sqsv1alp name string obj client.Object }{"sqs-ing-ui-assets-" + tenantID, &netv1.Ingress{}}) + checkResources = append(checkResources, struct { + name string + obj client.Object + }{"sqs-ing-ui-queues-" + tenantID, &netv1.Ingress{}}) } for _, res := range checkResources { if err := r.Get(ctx, client.ObjectKey{Namespace: tenantNS, Name: res.name}, res.obj); err != nil { @@ -311,6 +321,12 @@ func (r *QueueServiceReconciler) handleDeletion(ctx context.Context, qs *sqsv1al deleteOpts := []client.DeleteOption{client.PropagationPolicy(metav1.DeletePropagationForeground)} + // Удаляем Ingress UI queues (/queues/*) если был создан + uiQueuesIng := &netv1.Ingress{} + if err := r.Get(ctx, client.ObjectKey{Namespace: tenantNS, Name: "sqs-ing-ui-queues-" + tenantID}, uiQueuesIng); err == nil { + _ = r.Delete(ctx, uiQueuesIng, deleteOpts...) + } + // Удаляем Ingress UI assets (/_next/) если был создан uiAssetsIng := &netv1.Ingress{} if err := r.Get(ctx, client.ObjectKey{Namespace: tenantNS, Name: "sqs-ing-ui-assets-" + tenantID}, uiAssetsIng); err == nil { @@ -952,3 +968,62 @@ func (r *QueueServiceReconciler) ensureIngressUIAssets(ctx context.Context, qs * } return r.Create(ctx, ing) } + +// ensureIngressUIQueues создаёт Ingress для внутренних маршрутов Next.js (/queues/*). +// Next.js собран с basePath="" — при навигации браузер обращается к /queues/[name] напрямую. +// Без rewrite-target: nginx передаёт /queues/... как есть в UI-контейнер (port 3000). +func (r *QueueServiceReconciler) ensureIngressUIQueues(ctx context.Context, qs *sqsv1alpha1.QueueService, ns, name, svcName string) error { + ing := &netv1.Ingress{} + if err := r.Get(ctx, client.ObjectKey{Namespace: ns, Name: name}, ing); err == nil { + return nil + } else if !errors.IsNotFound(err) { + return fmt.Errorf("get ui-queues ingress: %w", err) + } + + pathType := netv1.PathTypePrefix + + ing = &netv1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ns, + Labels: sqsLabels(qs.Spec.TenantID), + Annotations: map[string]string{ + "nginx.ingress.kubernetes.io/proxy-read-timeout": "30", + "cert-manager.io/cluster-issuer": "letsencrypt-prod", + }, + }, + Spec: netv1.IngressSpec{ + IngressClassName: strPtr("nginx"), + TLS: []netv1.IngressTLS{ + { + Hosts: []string{r.SQSExternalHost}, + SecretName: "sqs-tls-" + qs.Spec.TenantID, + }, + }, + Rules: []netv1.IngressRule{ + { + Host: r.SQSExternalHost, + IngressRuleValue: netv1.IngressRuleValue{ + HTTP: &netv1.HTTPIngressRuleValue{ + Paths: []netv1.HTTPIngressPath{ + { + Path: "/queues", + PathType: &pathType, + Backend: netv1.IngressBackend{ + Service: &netv1.IngressServiceBackend{ + Name: svcName, + Port: netv1.ServiceBackendPort{ + Number: int32(elasticMQUIPort), + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + return r.Create(ctx, ing) +}