// Изменено: 2026-04-10 — добавлена Redis persistence // CreateQueueV1 — создаёт очередь для тенанта из request context. // Ключ в SyncQueues: "{tenantAccessKey}:{queueName}" для изоляции между тенантами. package gosqs import ( "net/http" "time" "shared-sqs/app/interfaces" "shared-sqs/app/models" "shared-sqs/app/persistence" "shared-sqs/app/utils" log "github.com/sirupsen/logrus" ) func CreateQueueV1(req *http.Request) (int, interfaces.AbstractResponseBody) { requestBody := models.NewCreateQueueRequest() ok := utils.REQUEST_TRANSFORMER(requestBody, req, false) if !ok { log.Error("Invalid Request - CreateQueueV1") return utils.CreateErrorResponseV1("InvalidParameterValue", true) } t := getTenantFromContext(req) if t == nil { return utils.CreateErrorResponseV1("InvalidClientTokenId", true) } // Валидация имени очереди по AWS правилам: макс 80 chars, [a-zA-Z0-9_-], опц. .fifo queueName := requestBody.QueueName if err := ValidateQueueName(queueName); err != nil { return utils.CreateErrorResponseV1("InvalidParameterValue", true) } key := tenantQueueKey(t.AccessKey, queueName) queueUrl := tenantQueueURL(t, queueName) queueArn := tenantQueueARN(t, queueName) models.SyncQueues.Lock() if _, exists := models.SyncQueues.Queues[key]; !exists { // Проверка лимита очередей тенанта if t.MaxQueues > 0 && countTenantQueues(t.AccessKey) >= t.MaxQueues { models.SyncQueues.Unlock() return utils.CreateErrorResponseV1("LimitExceeded", true) } log.Infof("Creating Queue: %s (tenant: %s)", queueName, t.ID) queue := &models.Queue{ Name: queueName, URL: queueUrl, Arn: queueArn, IsFIFO: utils.HasFIFOQueueName(queueName), EnableDuplicates: models.CurrentEnvironment.EnableDuplicates, Duplicates: make(map[string]time.Time), } if err := setQueueAttributesV1(queue, requestBody.Attributes); err != nil { models.SyncQueues.Unlock() return utils.CreateErrorResponseV1(err.Error(), true) } models.SyncQueues.Queues[key] = queue } // Сохраняем очередь в Redis пока держим Lock — консистентный снапшот persistence.SaveQueue(key, models.SyncQueues.Queues[key]) models.SyncQueues.Unlock() respStruct := models.CreateQueueResponse{ Xmlns: models.BaseXmlns, Result: models.CreateQueueResult{QueueUrl: queueUrl}, Metadata: models.BaseResponseMetadata, } return http.StatusOK, respStruct }