diff --git a/api/doc/openapi.json b/api/doc/openapi.json index 1e3f9650a..cb2a8a041 100644 --- a/api/doc/openapi.json +++ b/api/doc/openapi.json @@ -1701,6 +1701,62 @@ }, "type": "object" }, + "CreateHealthCheckRequest": { + "description": "CreateHealthCheckRequest schema", + "properties": { + "application_id": { + "type": "string" + }, + "body": { + "nullable": true, + "type": "string" + }, + "endpoint": { + "type": "string" + }, + "expected_status_codes": { + "items": { + "type": "integer" + }, + "nullable": true, + "type": "array" + }, + "failure_threshold": { + "nullable": true, + "type": "integer" + }, + "headers": { + "additionalProperties": { + "type": "string" + }, + "nullable": true, + "type": "object" + }, + "interval_seconds": { + "nullable": true, + "type": "integer" + }, + "method": { + "type": "string" + }, + "retention_days": { + "nullable": true, + "type": "integer" + }, + "success_threshold": { + "nullable": true, + "type": "integer" + }, + "timeout_seconds": { + "nullable": true, + "type": "integer" + } + }, + "required": [ + "application_id" + ], + "type": "object" + }, "CreateOrganizationRequest": { "description": "CreateOrganizationRequest schema", "properties": { @@ -8100,6 +8156,26 @@ }, "type": "object" }, + "Response": { + "description": "Response schema", + "properties": { + "data": { + "nullable": true + }, + "error": { + "nullable": true, + "type": "string" + }, + "message": { + "nullable": true, + "type": "string" + }, + "status": { + "type": "string" + } + }, + "type": "object" + }, "RestartDeploymentRequest": { "description": "RestartDeploymentRequest schema", "properties": { @@ -8174,6 +8250,21 @@ }, "type": "object" }, + "ToggleHealthCheckRequest": { + "description": "ToggleHealthCheckRequest schema", + "properties": { + "application_id": { + "type": "string" + }, + "enabled": { + "type": "boolean" + } + }, + "required": [ + "application_id" + ], + "type": "object" + }, "TwoFactorLoginRequest": { "description": "TwoFactorLoginRequest schema", "properties": { @@ -8425,6 +8516,64 @@ }, "type": "object" }, + "UpdateHealthCheckRequest": { + "description": "UpdateHealthCheckRequest schema", + "properties": { + "application_id": { + "type": "string" + }, + "body": { + "nullable": true, + "type": "string" + }, + "endpoint": { + "nullable": true, + "type": "string" + }, + "expected_status_codes": { + "items": { + "type": "integer" + }, + "nullable": true, + "type": "array" + }, + "failure_threshold": { + "nullable": true, + "type": "integer" + }, + "headers": { + "additionalProperties": { + "type": "string" + }, + "nullable": true, + "type": "object" + }, + "interval_seconds": { + "nullable": true, + "type": "integer" + }, + "method": { + "nullable": true, + "type": "string" + }, + "retention_days": { + "nullable": true, + "type": "integer" + }, + "success_threshold": { + "nullable": true, + "type": "integer" + }, + "timeout_seconds": { + "nullable": true, + "type": "integer" + } + }, + "required": [ + "application_id" + ], + "type": "object" + }, "UpdateLabelsRequest": { "description": "UpdateLabelsRequest schema", "properties": { @@ -15138,10 +15287,10 @@ ] } }, - "/api/v1/notification/preferences": { - "get": { - "description": "#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/notification/controller.(*NotificationController).GetPreferences`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).SetupRoutes.(*Router).setupAuthentication.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func2`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func3`\n\n---\n\n", - "operationId": "GET_/api/v1/notification/preferences", + "/api/v1/healthcheck": { + "delete": { + "description": "#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/healthcheck/controller.(*HealthCheckController).DeleteHealthCheck`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).SetupRoutes.(*Router).setupAuthentication.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func2`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func3`\n\n---\n\n", + "operationId": "DELETE_/api/v1/healthcheck", "parameters": [ { "in": "header", @@ -15156,12 +15305,12 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PreferencesResponse" + "$ref": "#/components/schemas/Response" } }, "application/xml": { "schema": { - "$ref": "#/components/schemas/PreferencesResponse" + "$ref": "#/components/schemas/Response" } } }, @@ -15201,15 +15350,14 @@ "description": "" } }, - "summary": "get preferences", + "summary": "delete health check", "tags": [ - "api/v1/notification", - "preferences" + "api/v1/healthcheck" ] }, - "post": { - "description": "#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/notification/controller.(*NotificationController).UpdatePreference`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).SetupRoutes.(*Router).setupAuthentication.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func2`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func3`\n\n---\n\n", - "operationId": "POST_/api/v1/notification/preferences", + "get": { + "description": "#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/healthcheck/controller.(*HealthCheckController).GetHealthCheck`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).SetupRoutes.(*Router).setupAuthentication.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func2`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func3`\n\n---\n\n", + "operationId": "GET_/api/v1/healthcheck", "parameters": [ { "in": "header", @@ -15219,28 +15367,17 @@ } } ], - "requestBody": { - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/UpdatePreferenceRequest" - } - } - }, - "description": "Request body for notification.UpdatePreferenceRequest", - "required": true - }, "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MessageResponse" + "$ref": "#/components/schemas/Response" } }, "application/xml": { "schema": { - "$ref": "#/components/schemas/MessageResponse" + "$ref": "#/components/schemas/Response" } } }, @@ -15280,17 +15417,14 @@ "description": "" } }, - "summary": "update preference", + "summary": "get health check", "tags": [ - "api/v1/notification", - "preferences" + "api/v1/healthcheck" ] - } - }, - "/api/v1/notification/smtp": { - "delete": { - "description": "#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/notification/controller.(*NotificationController).DeleteSmtp`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).SetupRoutes.(*Router).setupAuthentication.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func2`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func3`\n\n---\n\n", - "operationId": "DELETE_/api/v1/notification/smtp", + }, + "post": { + "description": "#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/healthcheck/controller.(*HealthCheckController).CreateHealthCheck`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).SetupRoutes.(*Router).setupAuthentication.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func2`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func3`\n\n---\n\n", + "operationId": "POST_/api/v1/healthcheck", "parameters": [ { "in": "header", @@ -15304,11 +15438,11 @@ "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/DeleteSMTPConfigRequest" + "$ref": "#/components/schemas/CreateHealthCheckRequest" } } }, - "description": "Request body for notification.DeleteSMTPConfigRequest", + "description": "Request body for types.CreateHealthCheckRequest", "required": true }, "responses": { @@ -15316,12 +15450,12 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MessageResponse" + "$ref": "#/components/schemas/Response" } }, "application/xml": { "schema": { - "$ref": "#/components/schemas/MessageResponse" + "$ref": "#/components/schemas/Response" } } }, @@ -15361,15 +15495,14 @@ "description": "" } }, - "summary": "delete smtp", + "summary": "create health check", "tags": [ - "api/v1/notification", - "smtp" + "api/v1/healthcheck" ] }, - "get": { - "description": "#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/notification/controller.(*NotificationController).GetSmtp`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).SetupRoutes.(*Router).setupAuthentication.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func2`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func3`\n\n---\n\n", - "operationId": "GET_/api/v1/notification/smtp", + "put": { + "description": "#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/healthcheck/controller.(*HealthCheckController).UpdateHealthCheck`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).SetupRoutes.(*Router).setupAuthentication.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func2`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func3`\n\n---\n\n", + "operationId": "PUT_/api/v1/healthcheck", "parameters": [ { "in": "header", @@ -15379,17 +15512,28 @@ } } ], + "requestBody": { + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/UpdateHealthCheckRequest" + } + } + }, + "description": "Request body for types.UpdateHealthCheckRequest", + "required": true + }, "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SMTPConfigResponse" + "$ref": "#/components/schemas/Response" } }, "application/xml": { "schema": { - "$ref": "#/components/schemas/SMTPConfigResponse" + "$ref": "#/components/schemas/Response" } } }, @@ -15429,15 +15573,16 @@ "description": "" } }, - "summary": "get smtp", + "summary": "update health check", "tags": [ - "api/v1/notification", - "smtp" + "api/v1/healthcheck" ] - }, - "post": { - "description": "#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/notification/controller.(*NotificationController).AddSmtp`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).SetupRoutes.(*Router).setupAuthentication.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func2`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func3`\n\n---\n\n", - "operationId": "POST_/api/v1/notification/smtp", + } + }, + "/api/v1/healthcheck/results": { + "get": { + "description": "#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/healthcheck/controller.(*HealthCheckController).GetHealthCheckResults`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).SetupRoutes.(*Router).setupAuthentication.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func2`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func3`\n\n---\n\n", + "operationId": "GET_/api/v1/healthcheck/results", "parameters": [ { "in": "header", @@ -15447,28 +15592,17 @@ } } ], - "requestBody": { - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/CreateSMTPConfigRequest" - } - } - }, - "description": "Request body for notification.CreateSMTPConfigRequest", - "required": true - }, "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MessageResponse" + "$ref": "#/components/schemas/Response" } }, "application/xml": { "schema": { - "$ref": "#/components/schemas/MessageResponse" + "$ref": "#/components/schemas/Response" } } }, @@ -15508,15 +15642,16 @@ "description": "" } }, - "summary": "add smtp", + "summary": "get health check results", "tags": [ - "api/v1/notification", - "smtp" + "api/v1/healthcheck" ] - }, - "put": { - "description": "#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/notification/controller.(*NotificationController).UpdateSmtp`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).SetupRoutes.(*Router).setupAuthentication.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func2`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func3`\n\n---\n\n", - "operationId": "PUT_/api/v1/notification/smtp", + } + }, + "/api/v1/healthcheck/stats": { + "get": { + "description": "#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/healthcheck/controller.(*HealthCheckController).GetHealthCheckStats`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).SetupRoutes.(*Router).setupAuthentication.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func2`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func3`\n\n---\n\n", + "operationId": "GET_/api/v1/healthcheck/stats", "parameters": [ { "in": "header", @@ -15526,28 +15661,17 @@ } } ], - "requestBody": { - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/UpdateSMTPConfigRequest" - } - } - }, - "description": "Request body for notification.UpdateSMTPConfigRequest", - "required": true - }, "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MessageResponse" + "$ref": "#/components/schemas/Response" } }, "application/xml": { "schema": { - "$ref": "#/components/schemas/MessageResponse" + "$ref": "#/components/schemas/Response" } } }, @@ -15587,17 +15711,16 @@ "description": "" } }, - "summary": "update smtp", + "summary": "get health check stats", "tags": [ - "api/v1/notification", - "smtp" + "api/v1/healthcheck" ] } }, - "/api/v1/notification/webhook": { - "delete": { - "description": "#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/notification/controller.(*NotificationController).DeleteWebhookConfig`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).SetupRoutes.(*Router).setupAuthentication.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func2`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func3`\n\n---\n\n", - "operationId": "DELETE_/api/v1/notification/webhook", + "/api/v1/healthcheck/toggle": { + "patch": { + "description": "#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/healthcheck/controller.(*HealthCheckController).ToggleHealthCheck`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).SetupRoutes.(*Router).setupAuthentication.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func2`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func3`\n\n---\n\n", + "operationId": "PATCH_/api/v1/healthcheck/toggle", "parameters": [ { "in": "header", @@ -15611,11 +15734,11 @@ "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/DeleteWebhookConfigRequest" + "$ref": "#/components/schemas/ToggleHealthCheckRequest" } } }, - "description": "Request body for notification.DeleteWebhookConfigRequest", + "description": "Request body for types.ToggleHealthCheckRequest", "required": true }, "responses": { @@ -15623,12 +15746,12 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MessageResponse" + "$ref": "#/components/schemas/Response" } }, "application/xml": { "schema": { - "$ref": "#/components/schemas/MessageResponse" + "$ref": "#/components/schemas/Response" } } }, @@ -15668,15 +15791,16 @@ "description": "" } }, - "summary": "delete webhook config", + "summary": "toggle health check", "tags": [ - "api/v1/notification", - "webhook" + "api/v1/healthcheck" ] - }, - "post": { - "description": "#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/notification/controller.(*NotificationController).CreateWebhookConfig`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).SetupRoutes.(*Router).setupAuthentication.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func2`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func3`\n\n---\n\n", - "operationId": "POST_/api/v1/notification/webhook", + } + }, + "/api/v1/notification/preferences": { + "get": { + "description": "#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/notification/controller.(*NotificationController).GetPreferences`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).SetupRoutes.(*Router).setupAuthentication.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func2`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func3`\n\n---\n\n", + "operationId": "GET_/api/v1/notification/preferences", "parameters": [ { "in": "header", @@ -15686,23 +15810,558 @@ } } ], - "requestBody": { - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/CreateWebhookConfigRequest" - } - } - }, - "description": "Request body for notification.CreateWebhookConfigRequest", - "required": true - }, "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/WebhookConfigResponse" + "$ref": "#/components/schemas/PreferencesResponse" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/PreferencesResponse" + } + } + }, + "description": "OK" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPError" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/HTTPError" + } + } + }, + "description": "Bad Request _(validation or deserialization error)_" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPError" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/HTTPError" + } + } + }, + "description": "Internal Server Error _(panics)_" + }, + "default": { + "description": "" + } + }, + "summary": "get preferences", + "tags": [ + "api/v1/notification", + "preferences" + ] + }, + "post": { + "description": "#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/notification/controller.(*NotificationController).UpdatePreference`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).SetupRoutes.(*Router).setupAuthentication.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func2`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func3`\n\n---\n\n", + "operationId": "POST_/api/v1/notification/preferences", + "parameters": [ + { + "in": "header", + "name": "Accept", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/UpdatePreferenceRequest" + } + } + }, + "description": "Request body for notification.UpdatePreferenceRequest", + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MessageResponse" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/MessageResponse" + } + } + }, + "description": "OK" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPError" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/HTTPError" + } + } + }, + "description": "Bad Request _(validation or deserialization error)_" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPError" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/HTTPError" + } + } + }, + "description": "Internal Server Error _(panics)_" + }, + "default": { + "description": "" + } + }, + "summary": "update preference", + "tags": [ + "api/v1/notification", + "preferences" + ] + } + }, + "/api/v1/notification/smtp": { + "delete": { + "description": "#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/notification/controller.(*NotificationController).DeleteSmtp`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).SetupRoutes.(*Router).setupAuthentication.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func2`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func3`\n\n---\n\n", + "operationId": "DELETE_/api/v1/notification/smtp", + "parameters": [ + { + "in": "header", + "name": "Accept", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/DeleteSMTPConfigRequest" + } + } + }, + "description": "Request body for notification.DeleteSMTPConfigRequest", + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MessageResponse" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/MessageResponse" + } + } + }, + "description": "OK" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPError" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/HTTPError" + } + } + }, + "description": "Bad Request _(validation or deserialization error)_" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPError" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/HTTPError" + } + } + }, + "description": "Internal Server Error _(panics)_" + }, + "default": { + "description": "" + } + }, + "summary": "delete smtp", + "tags": [ + "api/v1/notification", + "smtp" + ] + }, + "get": { + "description": "#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/notification/controller.(*NotificationController).GetSmtp`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).SetupRoutes.(*Router).setupAuthentication.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func2`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func3`\n\n---\n\n", + "operationId": "GET_/api/v1/notification/smtp", + "parameters": [ + { + "in": "header", + "name": "Accept", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SMTPConfigResponse" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/SMTPConfigResponse" + } + } + }, + "description": "OK" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPError" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/HTTPError" + } + } + }, + "description": "Bad Request _(validation or deserialization error)_" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPError" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/HTTPError" + } + } + }, + "description": "Internal Server Error _(panics)_" + }, + "default": { + "description": "" + } + }, + "summary": "get smtp", + "tags": [ + "api/v1/notification", + "smtp" + ] + }, + "post": { + "description": "#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/notification/controller.(*NotificationController).AddSmtp`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).SetupRoutes.(*Router).setupAuthentication.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func2`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func3`\n\n---\n\n", + "operationId": "POST_/api/v1/notification/smtp", + "parameters": [ + { + "in": "header", + "name": "Accept", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/CreateSMTPConfigRequest" + } + } + }, + "description": "Request body for notification.CreateSMTPConfigRequest", + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MessageResponse" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/MessageResponse" + } + } + }, + "description": "OK" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPError" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/HTTPError" + } + } + }, + "description": "Bad Request _(validation or deserialization error)_" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPError" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/HTTPError" + } + } + }, + "description": "Internal Server Error _(panics)_" + }, + "default": { + "description": "" + } + }, + "summary": "add smtp", + "tags": [ + "api/v1/notification", + "smtp" + ] + }, + "put": { + "description": "#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/notification/controller.(*NotificationController).UpdateSmtp`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).SetupRoutes.(*Router).setupAuthentication.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func2`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func3`\n\n---\n\n", + "operationId": "PUT_/api/v1/notification/smtp", + "parameters": [ + { + "in": "header", + "name": "Accept", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/UpdateSMTPConfigRequest" + } + } + }, + "description": "Request body for notification.UpdateSMTPConfigRequest", + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MessageResponse" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/MessageResponse" + } + } + }, + "description": "OK" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPError" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/HTTPError" + } + } + }, + "description": "Bad Request _(validation or deserialization error)_" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPError" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/HTTPError" + } + } + }, + "description": "Internal Server Error _(panics)_" + }, + "default": { + "description": "" + } + }, + "summary": "update smtp", + "tags": [ + "api/v1/notification", + "smtp" + ] + } + }, + "/api/v1/notification/webhook": { + "delete": { + "description": "#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/notification/controller.(*NotificationController).DeleteWebhookConfig`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).SetupRoutes.(*Router).setupAuthentication.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func2`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func3`\n\n---\n\n", + "operationId": "DELETE_/api/v1/notification/webhook", + "parameters": [ + { + "in": "header", + "name": "Accept", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/DeleteWebhookConfigRequest" + } + } + }, + "description": "Request body for notification.DeleteWebhookConfigRequest", + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MessageResponse" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/MessageResponse" + } + } + }, + "description": "OK" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPError" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/HTTPError" + } + } + }, + "description": "Bad Request _(validation or deserialization error)_" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPError" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/HTTPError" + } + } + }, + "description": "Internal Server Error _(panics)_" + }, + "default": { + "description": "" + } + }, + "summary": "delete webhook config", + "tags": [ + "api/v1/notification", + "webhook" + ] + }, + "post": { + "description": "#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/notification/controller.(*NotificationController).CreateWebhookConfig`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).SetupRoutes.(*Router).setupAuthentication.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func1`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func2`\n- `github.com/raghavyuva/nixopus-api/internal/routes.(*Router).applyMiddleware.func3`\n\n---\n\n", + "operationId": "POST_/api/v1/notification/webhook", + "parameters": [ + { + "in": "header", + "name": "Accept", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/CreateWebhookConfigRequest" + } + } + }, + "description": "Request body for notification.CreateWebhookConfigRequest", + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WebhookConfigResponse" } }, "application/xml": { @@ -17989,6 +18648,9 @@ { "name": "api/v1/health" }, + { + "name": "api/v1/healthcheck" + }, { "name": "api/v1/notification" }, diff --git a/api/internal/features/deploy/tasks/init.go b/api/internal/features/deploy/tasks/init.go index ed2a0db6d..0b7d38f10 100644 --- a/api/internal/features/deploy/tasks/init.go +++ b/api/internal/features/deploy/tasks/init.go @@ -65,7 +65,7 @@ func (t *TaskService) SetupCreateDeploymentQueue() { TaskCreateDeployment = taskq.RegisterTask(&taskq.TaskOptions{ Name: TASK_CREATE_DEPLOYMENT, - RetryLimit: 0, + RetryLimit: 1, Handler: func(ctx context.Context, data shared_types.TaskPayload) error { fmt.Printf("[%s] start: correlation_id=%s\n", TASK_CREATE_DEPLOYMENT, data.CorrelationID) err := t.BuildPack(ctx, data) @@ -91,7 +91,7 @@ func (t *TaskService) SetupCreateDeploymentQueue() { TaskUpdateDeployment = taskq.RegisterTask(&taskq.TaskOptions{ Name: TASK_UPDATE_DEPLOYMENT, - RetryLimit: 0, + RetryLimit: 1, Handler: func(ctx context.Context, data shared_types.TaskPayload) error { fmt.Println("Updating deployment") err := t.HandleUpdateDeployment(ctx, data) @@ -116,7 +116,7 @@ func (t *TaskService) SetupCreateDeploymentQueue() { TaskReDeploy = taskq.RegisterTask(&taskq.TaskOptions{ Name: TASK_REDEPLOYMENT, - RetryLimit: 0, + RetryLimit: 1, Handler: func(ctx context.Context, data shared_types.TaskPayload) error { fmt.Println("Redeploying application") err := t.HandleReDeploy(ctx, data) @@ -141,7 +141,7 @@ func (t *TaskService) SetupCreateDeploymentQueue() { TaskRollback = taskq.RegisterTask(&taskq.TaskOptions{ Name: TASK_ROLLBACK, - RetryLimit: 0, + RetryLimit: 1, Handler: func(ctx context.Context, data shared_types.TaskPayload) error { fmt.Println("Rolling back deployment") err := t.HandleRollback(ctx, data) @@ -166,7 +166,7 @@ func (t *TaskService) SetupCreateDeploymentQueue() { TaskRestart = taskq.RegisterTask(&taskq.TaskOptions{ Name: TASK_RESTART, - RetryLimit: 0, + RetryLimit: 1, Handler: func(ctx context.Context, data shared_types.TaskPayload) error { fmt.Println("Restarting deployment") err := t.HandleRestart(ctx, data) diff --git a/api/internal/queue/init.go b/api/internal/queue/init.go index e1f29820f..5d8664687 100644 --- a/api/internal/queue/init.go +++ b/api/internal/queue/init.go @@ -2,6 +2,10 @@ package queue import ( "context" + "fmt" + "log" + "sync" + "time" "github.com/go-redis/redis/v8" @@ -10,14 +14,19 @@ import ( ) var ( - redisClient *redis.Client - factory taskq.Factory + redisClient *redis.Client + factory taskq.Factory + onceConsumers sync.Once + consumersStarted bool + registeredQueues []string + queuesMutex sync.RWMutex ) // Init initializes the queue factory with a shared Redis v8 client. func Init(client *redis.Client) { redisClient = client factory = redisq.NewFactory() + registeredQueues = make([]string, 0) } // RegisterQueue registers a new queue with the shared redis client. @@ -25,13 +34,117 @@ func RegisterQueue(opts *taskq.QueueOptions) taskq.Queue { if opts.Redis == nil { opts.Redis = redisClient } - return factory.RegisterQueue(opts) + queue := factory.RegisterQueue(opts) + + // Track registered queue names for cleanup + queuesMutex.Lock() + registeredQueues = append(registeredQueues, opts.Name) + queuesMutex.Unlock() + + log.Printf("Registered queue: %s (MinNumWorker: %d, MaxNumWorker: %d)", opts.Name, opts.MinNumWorker, opts.MaxNumWorker) + return queue } +// cleanupDeadConsumers removes dead consumers from Redis consumer groups. +// Dead consumers are those that haven't been active for more than ConsumerIdleTimeout. +// This helps prevent accumulation of dead consumer entries after restarts. +func cleanupDeadConsumers(ctx context.Context) error { + if redisClient == nil { + return nil + } + + queuesMutex.RLock() + queueNames := make([]string, len(registeredQueues)) + copy(queueNames, registeredQueues) + queuesMutex.RUnlock() + + if len(queueNames) == 0 { + return nil + } + + log.Println("Cleaning up dead consumers from Redis consumer groups...") + cleanedCount := 0 + + // taskq uses "taskq" as the default consumer group name + groupName := "taskq" + + for _, queueName := range queueNames { + // Get stream key for this queue (taskq format: taskq:{queueName}) + streamKey := fmt.Sprintf("taskq:{%s}", queueName) + + // Get consumer information + cmd := redisClient.XInfoConsumers(ctx, streamKey, groupName) + if cmd.Err() != nil { + // Consumer group might not exist yet, skip + continue + } + + consumers, err := cmd.Result() + if err != nil { + log.Printf("Warning: Failed to get consumers for queue %s: %v", queueName, err) + continue + } + + // Check each consumer and remove if idle for too long + for _, consumer := range consumers { + idleTime := time.Duration(consumer.Idle) * time.Millisecond + // Consider consumers idle for more than 15 minutes as dead + // (longer than ConsumerIdleTimeout to account for processing time) + // Only remove if they have no pending messages + if idleTime > 15*time.Minute && consumer.Pending == 0 { + delCmd := redisClient.XGroupDelConsumer(ctx, streamKey, groupName, consumer.Name) + if delCmd.Err() == nil { + log.Printf("Removed dead consumer '%s' from queue '%s' (idle for %v)", consumer.Name, queueName, idleTime) + cleanedCount++ + } else { + log.Printf("Warning: Failed to remove dead consumer '%s' from queue '%s': %v", consumer.Name, queueName, delCmd.Err()) + } + } else if consumer.Pending > 0 { + log.Printf("Skipping consumer '%s' from queue '%s' (has %d pending messages, idle for %v)", consumer.Name, queueName, consumer.Pending, idleTime) + } + } + } + + if cleanedCount > 0 { + log.Printf("Cleaned up %d dead consumer(s)", cleanedCount) + } else { + log.Println("No dead consumers found to clean up") + } + + return nil +} + +// StartConsumers starts consumers for all registered queues. This function is idempotent +// and will only start consumers once, even if called multiple times. +// It also cleans up dead consumers from previous restarts before starting new ones. func StartConsumers(ctx context.Context) error { - return factory.StartConsumers(ctx) + var err error + onceConsumers.Do(func() { + // Clean up dead consumers before starting new ones + if cleanupErr := cleanupDeadConsumers(ctx); cleanupErr != nil { + log.Printf("Warning: Failed to cleanup dead consumers: %v", cleanupErr) + } + + log.Println("Starting task queue consumers...") + err = factory.StartConsumers(ctx) + if err != nil { + log.Printf("Error starting consumers: %v", err) + } else { + consumersStarted = true + log.Println("Task queue consumers started successfully") + } + }) + return err +} + +// IsConsumersStarted returns whether consumers have been started +func IsConsumersStarted() bool { + return consumersStarted } +// Close gracefully closes all consumers and cleans up resources func Close() error { + log.Println("Closing task queue consumers...") + consumersStarted = false return factory.Close() } diff --git a/api/main.go b/api/main.go index ee6ba4a11..9b403400b 100644 --- a/api/main.go +++ b/api/main.go @@ -86,6 +86,7 @@ func main() { signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) <-sigChan log.Println("Shutting down...") + queue.Close() schedulers.Main.Stop() schedulers.HealthCheck.Stop() os.Exit(0) diff --git a/view/app/extensions/page.tsx b/view/app/extensions/page.tsx index 35fa12a52..c649a0311 100644 --- a/view/app/extensions/page.tsx +++ b/view/app/extensions/page.tsx @@ -63,6 +63,7 @@ export default function ExtensionsPage() { onCategoryChange(cat.id)} className={cn( - 'w-full flex items-center gap-3 px-3 py-2 rounded-md text-sm transition-colors', + 'w-full flex items-center gap-3 px-3 py-2 rounded-md text-sm transition-colors overflow-hidden text-left', activeCategory === cat.id ? 'bg-muted font-medium' : 'hover:bg-muted/50' )} > - - {cat.label} + + {cat.label} ); }; diff --git a/view/packages/hooks/shared/use-app-sidebar.ts b/view/packages/hooks/shared/use-app-sidebar.ts index ab33faa21..9759635ad 100644 --- a/view/packages/hooks/shared/use-app-sidebar.ts +++ b/view/packages/hooks/shared/use-app-sidebar.ts @@ -1,4 +1,4 @@ -import { useRouter } from 'next/navigation'; +import { useRouter, usePathname } from 'next/navigation'; import { useAppSelector, useAppDispatch } from '@/redux/hooks'; import { useGetUserOrganizationsQuery } from '@/redux/services/users/userApi'; import { useNavigationState } from '@/packages/hooks/shared/use_navigation_state'; @@ -58,6 +58,7 @@ export function useAppSidebar() { const dispatch = useAppDispatch(); const { canAccessResource } = useRBAC(); const router = useRouter(); + const pathname = usePathname(); const [showLogoutDialog, setShowLogoutDialog] = useState(false); const { closeSettings } = useSettingsModal(); @@ -262,6 +263,21 @@ Add any other context about the problem here.`; } }, [activeOrg?.id, refetch]); + // Sync activeNav with current pathname to prevent multiple active menu items + useEffect(() => { + if (pathname) { + // Find the matching navigation item URL for the current pathname + const matchingNavItem = data.navMain.find( + (item) => pathname === item.url || pathname.startsWith(item.url + '/') + ); + + // Only update activeNav if we found a match and it's different from current activeNav + if (matchingNavItem && matchingNavItem.url !== activeNav) { + setActiveNav(matchingNavItem.url); + } + } + }, [pathname, activeNav, setActiveNav]); + return { user, isLoading,